mirror of
https://github.com/crazy-max/diun.git
synced 2025-12-21 13:23:09 +01:00
Allow to customize notification message (#415)
Co-authored-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
12
README.md
12
README.md
@@ -27,12 +27,14 @@ a Docker registry.
|
|||||||
|
|
||||||
Documentation can be found on https://crazymax.dev/diun/
|
Documentation can be found on https://crazymax.dev/diun/
|
||||||
|
|
||||||
## How can I help?
|
## Contributing
|
||||||
|
|
||||||
All kinds of contributions are welcome :raised_hands:! The most basic way to show your support is to star :star2: the
|
Want to contribute? Awesome! The most basic way to show your support is to star :star2: the project,
|
||||||
project, or to raise issues :speech_balloon: You can also support this project by
|
or to raise issues :speech_balloon:. If you want to open a pull request, please read the
|
||||||
[**becoming a sponsor on GitHub**](https://github.com/sponsors/crazy-max) :clap: or by making a
|
[contributing guidelines](.github/CONTRIBUTING.md).
|
||||||
[Paypal donation](https://www.paypal.me/crazyws) to ensure this journey continues indefinitely! :rocket:
|
|
||||||
|
You can also support this project by [**becoming a sponsor on GitHub**](https://github.com/sponsors/crazy-max) or by
|
||||||
|
making a [Paypal donation](https://www.paypal.me/crazyws) to ensure this journey continues indefinitely!
|
||||||
|
|
||||||
Thanks again for your support, it is much appreciated! :pray:
|
Thanks again for your support, it is much appreciated! :pray:
|
||||||
|
|
||||||
|
|||||||
@@ -10,48 +10,48 @@
|
|||||||
{% set assets = config.site_url ~ 'assets' %}
|
{% set assets = config.site_url ~ 'assets' %}
|
||||||
|
|
||||||
{% block extrahead %}
|
{% block extrahead %}
|
||||||
<link rel="preconnect" href="https://{{ config.extra.plausible.server }}">
|
<link rel="preconnect" href="https://[[ config.extra.plausible.server ]]">
|
||||||
<script defer data-domain="{{ config.extra.plausible.domain }}" src="https://{{ config.extra.plausible.server }}/js/plausible.outbound-links.js" ></script>
|
<script defer data-domain="[[ config.extra.plausible.domain ]]" src="https://[[ config.extra.plausible.server ]]/js/plausible.outbound-links.js" ></script>
|
||||||
<script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script>
|
<script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script>
|
||||||
|
|
||||||
<meta name="application-name" content="{{ config.site_name }}">
|
<meta name="application-name" content="[[ config.site_name ]]">
|
||||||
<meta name="msapplication-TileColor" content="#FFFFFF">
|
<meta name="msapplication-TileColor" content="#FFFFFF">
|
||||||
<meta name="msapplication-TileImage" content="{{ assets }}/meta/mstile-144x144.png">
|
<meta name="msapplication-TileImage" content="[[ assets ]]/meta/mstile-144x144.png">
|
||||||
<meta name="msapplication-square70x70logo" content="{{ assets }}/meta/mstile-70x70.png">
|
<meta name="msapplication-square70x70logo" content="[[ assets ]]/meta/mstile-70x70.png">
|
||||||
<meta name="msapplication-square150x150logo" content="{{ assets }}/meta/mstile-150x150.png">
|
<meta name="msapplication-square150x150logo" content="[[ assets ]]/meta/mstile-150x150.png">
|
||||||
<meta name="msapplication-wide310x150logo" content="{{ assets }}/meta/mstile-310x150.png">
|
<meta name="msapplication-wide310x150logo" content="[[ assets ]]/meta/mstile-310x150.png">
|
||||||
<meta name="msapplication-square310x310logo" content="{{ assets }}/meta/mstile-310x310.png">
|
<meta name="msapplication-square310x310logo" content="[[ assets ]]/meta/mstile-310x310.png">
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
<meta property="og:title" content="{{ title }}">
|
<meta property="og:title" content="[[ title ]]">
|
||||||
<meta property="og:description" content="{{ config.site_description }}">
|
<meta property="og:description" content="[[ config.site_description ]]">
|
||||||
<meta property="og:url" content="{{ page.canonical_url }}">
|
<meta property="og:url" content="[[ page.canonical_url ]]">
|
||||||
<meta property="og:image" content="{{ assets }}/meta/card.png">
|
<meta property="og:image" content="[[ assets ]]/meta/card.png">
|
||||||
<meta property="og:image:alt" content="{{ config.site_name }}">
|
<meta property="og:image:alt" content="[[ config.site_name ]]">
|
||||||
<meta property="og:image:type" content="image/png">
|
<meta property="og:image:type" content="image/png">
|
||||||
<meta property="og:image:width" content="1280">
|
<meta property="og:image:width" content="1280">
|
||||||
<meta property="og:image:height" content="640">
|
<meta property="og:image:height" content="640">
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
<meta name="twitter:site" content="@crazyws">
|
<meta name="twitter:site" content="@crazyws">
|
||||||
<meta name="twitter:creator" content="@crazyws">
|
<meta name="twitter:creator" content="@crazyws">
|
||||||
<meta name="twitter:title" content="{{ title }}">
|
<meta name="twitter:title" content="[[ title ]]">
|
||||||
<meta name="twitter:description" content="{{ config.site_description }}">
|
<meta name="twitter:description" content="[[ config.site_description ]]">
|
||||||
<meta name="twitter:image" content="{{ assets }}/meta/card.png">
|
<meta name="twitter:image" content="[[ assets ]]/meta/card.png">
|
||||||
<meta name="google-site-verification" content="{{ config.extra.seo.google }}">
|
<meta name="google-site-verification" content="[[ config.extra.seo.google ]]">
|
||||||
<meta name="msvalidate.01" content="{{ config.extra.seo.bing }}">
|
<meta name="msvalidate.01" content="[[ config.extra.seo.bing ]]">
|
||||||
|
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="{{ assets }}/meta/apple-touch-icon-57x57.png">
|
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="[[ assets ]]/meta/apple-touch-icon-57x57.png">
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="{{ assets }}/meta/apple-touch-icon-114x114.png">
|
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="[[ assets ]]/meta/apple-touch-icon-114x114.png">
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="{{ assets }}/meta/apple-touch-icon-72x72.png">
|
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="[[ assets ]]/meta/apple-touch-icon-72x72.png">
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="{{ assets }}/meta/apple-touch-icon-144x144.png">
|
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="[[ assets ]]/meta/apple-touch-icon-144x144.png">
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="60x60" href="{{ assets }}/meta/apple-touch-icon-60x60.png">
|
<link rel="apple-touch-icon-precomposed" sizes="60x60" href="[[ assets ]]/meta/apple-touch-icon-60x60.png">
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="{{ assets }}/meta/apple-touch-icon-120x120.png">
|
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="[[ assets ]]/meta/apple-touch-icon-120x120.png">
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="{{ assets }}/meta/apple-touch-icon-76x76.png">
|
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="[[ assets ]]/meta/apple-touch-icon-76x76.png">
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ assets }}/meta/apple-touch-icon-152x152.png">
|
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="[[ assets ]]/meta/apple-touch-icon-152x152.png">
|
||||||
<link rel="icon" type="image/png" href="{{ assets }}/meta/favicon-196x196.png" sizes="196x196">
|
<link rel="icon" type="image/png" href="[[ assets ]]/meta/favicon-196x196.png" sizes="196x196">
|
||||||
<link rel="icon" type="image/png" href="{{ assets }}/meta/favicon-96x96.png" sizes="96x96">
|
<link rel="icon" type="image/png" href="[[ assets ]]/meta/favicon-96x96.png" sizes="96x96">
|
||||||
<link rel="icon" type="image/png" href="{{ assets }}/meta/favicon-32x32.png" sizes="32x32">
|
<link rel="icon" type="image/png" href="[[ assets ]]/meta/favicon-32x32.png" sizes="32x32">
|
||||||
<link rel="icon" type="image/png" href="{{ assets }}/meta/favicon-16x16.png" sizes="16x16">
|
<link rel="icon" type="image/png" href="[[ assets ]]/meta/favicon-16x16.png" sizes="16x16">
|
||||||
<link rel="icon" type="image/png" href="{{ assets }}/meta/favicon-128.png" sizes="128x128">
|
<link rel="icon" type="image/png" href="[[ assets ]]/meta/favicon-128.png" sizes="128x128">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<!-- Announcement bar -->
|
<!-- Announcement bar -->
|
||||||
|
|||||||
@@ -3,18 +3,18 @@
|
|||||||
Hi there! I'm thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
|
Hi there! I'm thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
|
||||||
|
|
||||||
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license)
|
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license)
|
||||||
to the public under the [project's open source license]({{ config.repo_url }}/blob/master/LICENSE).
|
to the public under the [project's open source license]([[ config.repo_url ]]/blob/master/LICENSE).
|
||||||
|
|
||||||
## Submitting a pull request
|
## Submitting a pull request
|
||||||
|
|
||||||
1. [Fork]({{ config.repo_url }}fork) and clone the repository
|
1. [Fork]([[ config.repo_url ]]fork) and clone the repository
|
||||||
2. Configure and install the dependencies: `go mod download`
|
2. Configure and install the dependencies: `go mod download`
|
||||||
3. Create a new branch: `git checkout -b my-branch-name`
|
3. Create a new branch: `git checkout -b my-branch-name`
|
||||||
4. Make your changes
|
4. Make your changes
|
||||||
5. Validate: `docker buildx bake validate`
|
5. Validate: `docker buildx bake validate`
|
||||||
6. Test your code: `docker buildx bake test`
|
6. Test your code: `docker buildx bake test`
|
||||||
7. Build the project: `docker buildx bake artifact-all image-all`
|
7. Build the project: `docker buildx bake artifact-all image-all`
|
||||||
8. Push to your fork and [submit a pull request]({{ config.repo_url }}compare)
|
8. Push to your fork and [submit a pull request]([[ config.repo_url ]]compare)
|
||||||
9. Pat your self on the back and wait for your pull request to be reviewed and merged.
|
9. Pat your self on the back and wait for your pull request to be reviewed and merged.
|
||||||
|
|
||||||
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
|
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
**Diun** :bell: is free and open source and always will be.
|
**Diun** :bell: is free and open source and always will be.
|
||||||
|
|
||||||
All kinds of contributions are welcome! The most basic way to show your support is to
|
All kinds of contributions are welcome! The most basic way to show your support is to
|
||||||
[star the project]({{ config.repo_url }}), or to raise issues.
|
[star the project]([[ config.repo_url ]]), or to raise issues.
|
||||||
|
|
||||||
You can also support this project by [**becoming a sponsor on GitHub**](https://github.com/sponsors/crazy-max) or
|
You can also support this project by [**becoming a sponsor on GitHub**](https://github.com/sponsors/crazy-max) or
|
||||||
by making a [Paypal donation](https://www.paypal.me/crazyws) to ensure this journey continues indefinitely!
|
by making a [Paypal donation](https://www.paypal.me/crazyws) to ensure this journey continues indefinitely!
|
||||||
|
|||||||
37
docs/faq.md
37
docs/faq.md
@@ -26,6 +26,43 @@ Or within a container:
|
|||||||
docker-compose exec diun diun notif test
|
docker-compose exec diun diun notif test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Notification template
|
||||||
|
|
||||||
|
The title and body of a notification message can be customized for each notifier through `templateTitle` and
|
||||||
|
`templateBody` fields except for those rendering _JSON_ or _Env_ like [Amqp](notif/amqp.md),
|
||||||
|
[MQTT](notif/mqtt.md), [Script](notif/script.md) and [Webhook](notif/webhook.md).
|
||||||
|
|
||||||
|
Templating is supported with the following fields:
|
||||||
|
|
||||||
|
| Key | Description |
|
||||||
|
|----------------------------------|-------------|
|
||||||
|
| `.Meta.ID` | App ID: `diun` |
|
||||||
|
| `.Meta.Name` | App Name: `Diun` |
|
||||||
|
| `.Meta.Desc` | App description: `Docker image update notifier` |
|
||||||
|
| `.Meta.URL` | App repo URL: `https://github.com/crazy-max/diun` |
|
||||||
|
| `.Meta.Logo` | App logo URL: `https://raw.githubusercontent.com/crazy-max/diun/master/.res/diun.png` |
|
||||||
|
| `.Meta.Author` | App author: `CrazyMax` |
|
||||||
|
| `.Meta.Version` | App version: `v4.19.0` |
|
||||||
|
| `.Meta.UserAgent` | App user-agent used to talk with registries: `diun/4.19.0 go/1.16 Linux` |
|
||||||
|
| `.Meta.Hostname` | Hostname |
|
||||||
|
| `.Entry.Status` | Entry status. Can be `new`, `update`, `unchange`, `skip` or `error` |
|
||||||
|
| `.Entry.Provider` | [Provider](config/providers.md) used |
|
||||||
|
| `.Entry.Image` | Docker image name. e.g. `docker.io/crazymax/diun:latest` |
|
||||||
|
| `.Entry.Image.Domain` | Docker image domain. e.g. `docker.io` |
|
||||||
|
| `.Entry.Image.Path` | Docker image path. e.g. `crazymax/diun` |
|
||||||
|
| `.Entry.Image.Tag` | Docker image tag. e.g. `latest` |
|
||||||
|
| `.Entry.Image.Digest` | Docker image digest |
|
||||||
|
| `.Entry.Image.HubLink` | Docker image hub link (if available). e.g. `https://hub.docker.com/r/crazymax/diun` |
|
||||||
|
| `.Entry.Manifest.Name` | Manifest name. e.g. `docker.io/crazymax/diun` |
|
||||||
|
| `.Entry.Manifest.Tag` | Manifest tag. e.g. `latest` |
|
||||||
|
| `.Entry.Manifest.MIMEType` | Manifest MIME type. e.g. `application/vnd.docker.distribution.manifest.list.v2+json` |
|
||||||
|
| `.Entry.Manifest.Digest` | Manifest digest |
|
||||||
|
| `.Entry.Manifest.Created` | Manifest created date. e.g. `2021-06-20T12:23:56Z` |
|
||||||
|
| `.Entry.Manifest.DockerVersion` | Version of Docker that was used to build the image. e.g. `20.10.7` |
|
||||||
|
| `.Entry.Manifest.Labels` | Image labels |
|
||||||
|
| `.Entry.Manifest.Layers` | Image layers |
|
||||||
|
| `.Entry.Manifest.Platform` | Platform that the image is runs on. e.g. `linux/amd64` |
|
||||||
|
|
||||||
## Authentication against the registry
|
## Authentication against the registry
|
||||||
|
|
||||||
You can authenticate against the registry through the [`regopts` settings](config/regopts.md) or you can mount
|
You can authenticate against the registry through the [`regopts` settings](config/regopts.md) or you can mount
|
||||||
|
|||||||
@@ -2,27 +2,27 @@
|
|||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
Diun binaries are available on [releases]({{ config.repo_url }}releases) page.
|
Diun binaries are available on [releases]([[ config.repo_url ]]releases) page.
|
||||||
|
|
||||||
Choose the archive matching the destination platform:
|
Choose the archive matching the destination platform:
|
||||||
|
|
||||||
* [diun_{{ git.tag | trim('v') }}_darwin_arm64.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_darwin_arm64.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_darwin_arm64.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_darwin_arm64.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_darwin_x86_64.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_darwin_x86_64.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_darwin_x86_64.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_darwin_x86_64.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_linux_arm64.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_arm64.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_linux_arm64.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_linux_arm64.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_linux_armv5.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_armv5.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_linux_armv5.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_linux_armv5.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_linux_armv6.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_armv6.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_linux_armv6.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_linux_armv6.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_linux_armv7.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_armv7.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_linux_armv7.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_linux_armv7.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_linux_i386.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_i386.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_linux_i386.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_linux_i386.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_linux_ppc64le.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_ppc64le.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_linux_ppc64le.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_linux_ppc64le.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_linux_s390x.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_s390x.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_linux_s390x.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_linux_s390x.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_linux_x86_64.tar.gz]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_x86_64.tar.gz)
|
* [diun_[[ git.tag | trim('v') ]]_linux_x86_64.tar.gz]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_linux_x86_64.tar.gz)
|
||||||
* [diun_{{ git.tag | trim('v') }}_windows_i386.zip]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_windows_i386.zip)
|
* [diun_[[ git.tag | trim('v') ]]_windows_i386.zip]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_windows_i386.zip)
|
||||||
* [diun_{{ git.tag | trim('v') }}_windows_x86_64.zip]({{ config.repo_url }}/releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_windows_x86_64.zip)
|
* [diun_[[ git.tag | trim('v') ]]_windows_x86_64.zip]([[ config.repo_url ]]/releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_windows_x86_64.zip)
|
||||||
|
|
||||||
And extract diun:
|
And extract diun:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
wget -qO- {{ config.repo_url }}releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_x86_64.tar.gz | tar -zxvf - diun
|
wget -qO- [[ config.repo_url ]]releases/download/v[[ git.tag | trim('v') ]]/diun_[[ git.tag | trim('v') ]]_linux_x86_64.tar.gz | tar -zxvf - diun
|
||||||
```
|
```
|
||||||
|
|
||||||
After getting the binary, it can be tested with [`./diun --help`](../usage/command-line.md#global-options) command
|
After getting the binary, it can be tested with [`./diun --help`](../usage/command-line.md#global-options) command
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ To create a new service, paste this content in `/etc/systemd/system/diun.service
|
|||||||
```
|
```
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Diun
|
Description=Diun
|
||||||
Documentation={{ config.site_url }}
|
Documentation=[[ config.site_url ]]
|
||||||
After=syslog.target
|
After=syslog.target
|
||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
|
|||||||
@@ -16,18 +16,37 @@ Allow to send notifications to your Discord channel.
|
|||||||
- "<@125>"
|
- "<@125>"
|
||||||
- "<@&200>"
|
- "<@&200>"
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
|
templateTitle: "{{ .Entry.Image }} released"
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|--------------------|---------------|---------------|
|
|---------------------|---------------------------------------|---------------|
|
||||||
| `webhookURL`[^1] | | Discord [incoming webhook URL](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) |
|
| `webhookURL`[^1] | | Discord [incoming webhook URL](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) |
|
||||||
| `mentions` | | List of users or roles to notify |
|
| `mentions` | | List of users or roles to notify |
|
||||||
| `timeout` | `10s` | Timeout specifies a time limit for the request to be made |
|
| `timeout` | `10s` | Timeout specifies a time limit for the request to be made |
|
||||||
|
| `templateTitle`[^1] | See [below](#default-templatetitle) | [Notification template](../faq.md#notification-template) for message title |
|
||||||
|
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||||
|
|
||||||
!!! abstract "Environment variables"
|
!!! abstract "Environment variables"
|
||||||
* `DIUN_NOTIF_DISCORD_WEBHOOKURL`
|
* `DIUN_NOTIF_DISCORD_WEBHOOKURL`
|
||||||
* `DIUN_NOTIF_DISCORD_MENTIONS` (comma separated)
|
* `DIUN_NOTIF_DISCORD_MENTIONS` (comma separated)
|
||||||
* `DIUN_NOTIF_DISCORD_TIMEOUT`
|
* `DIUN_NOTIF_DISCORD_TIMEOUT`
|
||||||
|
* `DIUN_NOTIF_DISCORD_TEMPLATETITLE`
|
||||||
|
* `DIUN_NOTIF_DISCORD_TEMPLATEBODY`
|
||||||
|
|
||||||
|
### Default `templateTitle`
|
||||||
|
|
||||||
|
```
|
||||||
|
[[ config.extra.template.defaultTitle ]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default `templateBody`
|
||||||
|
|
||||||
|
```
|
||||||
|
[[ config.extra.template.defaultBody ]]
|
||||||
|
```
|
||||||
|
|
||||||
## Sample
|
## Sample
|
||||||
|
|
||||||
|
|||||||
@@ -12,15 +12,20 @@ Notifications can be sent using a [Gotify](https://gotify.net/) instance.
|
|||||||
token: Token123456
|
token: Token123456
|
||||||
priority: 1
|
priority: 1
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
|
templateTitle: "{{ .Entry.Image }} released"
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|--------------------|---------------|---------------|
|
|---------------------|---------------------------------------|---------------|
|
||||||
| `endpoint`[^1] | | Gotify base URL |
|
| `endpoint`[^1] | | Gotify base URL |
|
||||||
| `token` | | Application token |
|
| `token` | | Application token |
|
||||||
| `tokenFile` | | Use content of secret file as application token if `token` not defined |
|
| `tokenFile` | | Use content of secret file as application token if `token` not defined |
|
||||||
| `priority` | `1` | The priority of the message |
|
| `priority` | `1` | The priority of the message |
|
||||||
| `timeout` | `10s` | Timeout specifies a time limit for the request to be made |
|
| `timeout` | `10s` | Timeout specifies a time limit for the request to be made |
|
||||||
|
| `templateTitle`[^1] | See [below](#default-templatetitle) | [Notification template](../faq.md#notification-template) for message title |
|
||||||
|
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||||
|
|
||||||
!!! abstract "Environment variables"
|
!!! abstract "Environment variables"
|
||||||
* `DIUN_NOTIF_GOTIFY_ENDPOINT`
|
* `DIUN_NOTIF_GOTIFY_ENDPOINT`
|
||||||
@@ -28,6 +33,20 @@ Notifications can be sent using a [Gotify](https://gotify.net/) instance.
|
|||||||
* `DIUN_NOTIF_GOTIFY_TOKENFILE`
|
* `DIUN_NOTIF_GOTIFY_TOKENFILE`
|
||||||
* `DIUN_NOTIF_GOTIFY_PRIORITY`
|
* `DIUN_NOTIF_GOTIFY_PRIORITY`
|
||||||
* `DIUN_NOTIF_GOTIFY_TIMEOUT`
|
* `DIUN_NOTIF_GOTIFY_TIMEOUT`
|
||||||
|
* `DIUN_NOTIF_GOTIFY_TEMPLATETITLE`
|
||||||
|
* `DIUN_NOTIF_GOTIFY_TEMPLATEBODY`
|
||||||
|
|
||||||
|
### Default `templateTitle`
|
||||||
|
|
||||||
|
```
|
||||||
|
[[ config.extra.template.defaultTitle ]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default `templateBody`
|
||||||
|
|
||||||
|
```
|
||||||
|
[[ config.extra.template.defaultBody ]]
|
||||||
|
```
|
||||||
|
|
||||||
## Sample
|
## Sample
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,13 @@ Notifications can be sent through SMTP.
|
|||||||
insecureSkipVerify: false
|
insecureSkipVerify: false
|
||||||
from: diun@example.com
|
from: diun@example.com
|
||||||
to: webmaster@example.com
|
to: webmaster@example.com
|
||||||
|
templateTitle: "{{ .Entry.Image }} released"
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|-----------------------|---------------|---------------|
|
|-----------------------|--------------------------------------------|---------------|
|
||||||
| `host`[^1] | `localhost` | SMTP server host |
|
| `host`[^1] | `localhost` | SMTP server host |
|
||||||
| `port`[^1] | `25` | SMTP server port |
|
| `port`[^1] | `25` | SMTP server port |
|
||||||
| `ssl` | `false` | SSL defines whether an SSL connection is used. Should be false in most cases since the auth mechanism should use STARTTLS |
|
| `ssl` | `false` | SSL defines whether an SSL connection is used. Should be false in most cases since the auth mechanism should use STARTTLS |
|
||||||
@@ -29,6 +32,8 @@ Notifications can be sent through SMTP.
|
|||||||
| `passwordFile` | | Use content of secret file as SMTP password if `password` not defined |
|
| `passwordFile` | | Use content of secret file as SMTP password if `password` not defined |
|
||||||
| `from`[^1] | | Sender email address |
|
| `from`[^1] | | Sender email address |
|
||||||
| `to`[^1] | | Recipient email address |
|
| `to`[^1] | | Recipient email address |
|
||||||
|
| `templateTitle`[^1] | See [below](#default-templatetitle) | [Notification template](../faq.md#notification-template) for message title |
|
||||||
|
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||||
|
|
||||||
!!! abstract "Environment variables"
|
!!! abstract "Environment variables"
|
||||||
* `DIUN_NOTIF_MAIL_HOST`
|
* `DIUN_NOTIF_MAIL_HOST`
|
||||||
@@ -42,6 +47,28 @@ Notifications can be sent through SMTP.
|
|||||||
* `DIUN_NOTIF_MAIL_PASSWORDFILE`
|
* `DIUN_NOTIF_MAIL_PASSWORDFILE`
|
||||||
* `DIUN_NOTIF_MAIL_FROM`
|
* `DIUN_NOTIF_MAIL_FROM`
|
||||||
* `DIUN_NOTIF_MAIL_TO`
|
* `DIUN_NOTIF_MAIL_TO`
|
||||||
|
* `DIUN_NOTIF_MAIL_TEMPLATETITLE`
|
||||||
|
* `DIUN_NOTIF_MAIL_TEMPLATEBODY`
|
||||||
|
|
||||||
|
### Default `templateTitle`
|
||||||
|
|
||||||
|
```
|
||||||
|
[[ config.extra.template.defaultTitle ]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default `templateBody`
|
||||||
|
|
||||||
|
```
|
||||||
|
Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
||||||
|
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}
|
||||||
|
on {{ .Meta.Hostname }}.
|
||||||
|
|
||||||
|
This image has been {{ if (eq .Entry.Status "new") }}created{{ else }}updated{{ end }} at
|
||||||
|
<code>{{ .Entry.Manifest.Created.Format "Jan 02, 2006 15:04:05 UTC" }}</code> with digest <code>{{ .Entry.Manifest.Digest }}</code>
|
||||||
|
for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
||||||
|
|
||||||
|
Need help, or have questions? Go to {{ .Meta.URL }} and leave an issue.
|
||||||
|
```
|
||||||
|
|
||||||
## Sample
|
## Sample
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,12 @@ Allow to send notifications to your Matrix server.
|
|||||||
password: bar
|
password: bar
|
||||||
roomID: "!abcdefGHIjklmno:matrix.org"
|
roomID: "!abcdefGHIjklmno:matrix.org"
|
||||||
msgType: notice
|
msgType: notice
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|-----------------------|------------------------|-------------------|
|
|-----------------------|--------------------------------------------|-------------------|
|
||||||
| `homeserverURL` | `https://matrix.org` | Matrix server URL |
|
| `homeserverURL` | `https://matrix.org` | Matrix server URL |
|
||||||
| `user` | | Username for authentication |
|
| `user` | | Username for authentication |
|
||||||
| `userFile` | | Use content of secret file as username authentication if `username` not defined |
|
| `userFile` | | Use content of secret file as username authentication if `username` not defined |
|
||||||
@@ -24,6 +26,7 @@ Allow to send notifications to your Matrix server.
|
|||||||
| `passwordFile` | | Use content of secret file as password authentication if `password` not defined |
|
| `passwordFile` | | Use content of secret file as password authentication if `password` not defined |
|
||||||
| `roomID` | | Room ID to send messages |
|
| `roomID` | | Room ID to send messages |
|
||||||
| `msgType` | `notice` | Type of message being sent. Can be `notice` or `text` |
|
| `msgType` | `notice` | Type of message being sent. Can be `notice` or `text` |
|
||||||
|
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||||
|
|
||||||
!!! abstract "Environment variables"
|
!!! abstract "Environment variables"
|
||||||
* `DIUN_NOTIF_MATRIX_HOMESERVERURL`
|
* `DIUN_NOTIF_MATRIX_HOMESERVERURL`
|
||||||
@@ -33,6 +36,13 @@ Allow to send notifications to your Matrix server.
|
|||||||
* `DIUN_NOTIF_MATRIX_PASSWORDFILE`
|
* `DIUN_NOTIF_MATRIX_PASSWORDFILE`
|
||||||
* `DIUN_NOTIF_MATRIX_ROOMID`
|
* `DIUN_NOTIF_MATRIX_ROOMID`
|
||||||
* `DIUN_NOTIF_MATRIX_MSGTYPE`
|
* `DIUN_NOTIF_MATRIX_MSGTYPE`
|
||||||
|
* `DIUN_NOTIF_MATRIX_TEMPLATEBODY`
|
||||||
|
|
||||||
|
### Default `templateBody`
|
||||||
|
|
||||||
|
```
|
||||||
|
[[ config.extra.template.defaultBody ]]
|
||||||
|
```
|
||||||
|
|
||||||
## Sample
|
## Sample
|
||||||
|
|
||||||
|
|||||||
@@ -10,20 +10,39 @@ You can send notifications using [Pushover](https://pushover.net/).
|
|||||||
pushover:
|
pushover:
|
||||||
token: uQiRzpo4DXghDmr9QzzfQu27cmVRsG
|
token: uQiRzpo4DXghDmr9QzzfQu27cmVRsG
|
||||||
recipient: gznej3rKEVAvPUxu9vvNnqpmZpokzF
|
recipient: gznej3rKEVAvPUxu9vvNnqpmZpokzF
|
||||||
|
templateTitle: "{{ .Entry.Image }} released"
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|--------------------|---------------|---------------|
|
|---------------------|--------------------------------------------|---------------|
|
||||||
| `token` | | Pushover [application/API token](https://pushover.net/api#registration) |
|
| `token` | | Pushover [application/API token](https://pushover.net/api#registration) |
|
||||||
| `tokenFile` | | Use content of secret file as Pushover application/API token if `token` not defined |
|
| `tokenFile` | | Use content of secret file as Pushover application/API token if `token` not defined |
|
||||||
| `recipient` | | User key to send notification to |
|
| `recipient` | | User key to send notification to |
|
||||||
| `recipientFile` | | Use content of secret file as User key if `recipient` not defined |
|
| `recipientFile` | | Use content of secret file as User key if `recipient` not defined |
|
||||||
|
| `templateTitle`[^1] | See [below](#default-templatetitle) | [Notification template](../faq.md#notification-template) for message title |
|
||||||
|
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||||
|
|
||||||
!!! abstract "Environment variables"
|
!!! abstract "Environment variables"
|
||||||
* `DIUN_NOTIF_PUSHOVER_TOKEN`
|
* `DIUN_NOTIF_PUSHOVER_TOKEN`
|
||||||
* `DIUN_NOTIF_PUSHOVER_TOKENFILE`
|
* `DIUN_NOTIF_PUSHOVER_TOKENFILE`
|
||||||
* `DIUN_NOTIF_PUSHOVER_RECIPIENT`
|
* `DIUN_NOTIF_PUSHOVER_RECIPIENT`
|
||||||
* `DIUN_NOTIF_PUSHOVER_RECIPIENTFILE`
|
* `DIUN_NOTIF_PUSHOVER_RECIPIENTFILE`
|
||||||
|
* `DIUN_NOTIF_PUSHOVER_TEMPLATETITLE`
|
||||||
|
* `DIUN_NOTIF_PUSHOVER_TEMPLATEBODY`
|
||||||
|
|
||||||
|
### Default `templateTitle`
|
||||||
|
|
||||||
|
```
|
||||||
|
[[ config.extra.template.defaultTitle ]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default `templateBody`
|
||||||
|
|
||||||
|
```
|
||||||
|
[[ config.extra.template.defaultBody ]]
|
||||||
|
```
|
||||||
|
|
||||||
## Sample
|
## Sample
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,21 @@ Allow to send notifications to your Rocket.Chat channel.
|
|||||||
userID: abcdEFGH012345678
|
userID: abcdEFGH012345678
|
||||||
token: Token123456
|
token: Token123456
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
|
templateTitle: "{{ .Entry.Image }} released"
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|--------------------|---------------|---------------|
|
|---------------------|--------------------------------------------|---------------|
|
||||||
| `endpoint`[^1] | | Rocket.Chat base URL |
|
| `endpoint`[^1] | | Rocket.Chat base URL |
|
||||||
| `channel`[^1] | | Channel name with the prefix in front of it |
|
| `channel`[^1] | | Channel name with the prefix in front of it |
|
||||||
| `userID`[^1] | | User ID |
|
| `userID`[^1] | | User ID |
|
||||||
| `token` | | Authentication token |
|
| `token` | | Authentication token |
|
||||||
| `tokenFile` | | Use content of secret file as authentication token if `token` not defined |
|
| `tokenFile` | | Use content of secret file as authentication token if `token` not defined |
|
||||||
| `timeout` | `10s` | Timeout specifies a time limit for the request to be made |
|
| `timeout` | `10s` | Timeout specifies a time limit for the request to be made |
|
||||||
|
| `templateTitle`[^1] | See [below](#default-templatetitle) | [Notification template](../faq.md#notification-template) for message title |
|
||||||
|
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
You must first create a _Personal Access Token_ through your account settings on your Rocket.Chat instance.
|
You must first create a _Personal Access Token_ through your account settings on your Rocket.Chat instance.
|
||||||
@@ -34,6 +39,20 @@ Allow to send notifications to your Rocket.Chat channel.
|
|||||||
* `DIUN_NOTIF_ROCKETCHAT_TOKEN`
|
* `DIUN_NOTIF_ROCKETCHAT_TOKEN`
|
||||||
* `DIUN_NOTIF_ROCKETCHAT_TOKENFILE`
|
* `DIUN_NOTIF_ROCKETCHAT_TOKENFILE`
|
||||||
* `DIUN_NOTIF_ROCKETCHAT_TIMEOUT`
|
* `DIUN_NOTIF_ROCKETCHAT_TIMEOUT`
|
||||||
|
* `DIUN_NOTIF_ROCKETCHAT_TEMPLATETITLE`
|
||||||
|
* `DIUN_NOTIF_ROCKETCHAT_TEMPLATEBODY`
|
||||||
|
|
||||||
|
### Default `templateTitle`
|
||||||
|
|
||||||
|
```
|
||||||
|
[[ config.extra.template.defaultTitle ]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default `templateBody`
|
||||||
|
|
||||||
|
```
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }} on {{ .Meta.Hostname }}.
|
||||||
|
```
|
||||||
|
|
||||||
## Sample
|
## Sample
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,24 @@ You can send notifications to your Slack channel using an [incoming webhook URL]
|
|||||||
notif:
|
notif:
|
||||||
slack:
|
slack:
|
||||||
webhookURL: https://hooks.slack.com/services/ABCD12EFG/HIJK34LMN/01234567890abcdefghij
|
webhookURL: https://hooks.slack.com/services/ABCD12EFG/HIJK34LMN/01234567890abcdefghij
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|--------------------|---------------|---------------|
|
|--------------------|--------------------------------------------|---------------|
|
||||||
| `webhookURL`[^1] | | Slack [incoming webhook URL](https://api.slack.com/messaging/webhooks) |
|
| `webhookURL`[^1] | | Slack [incoming webhook URL](https://api.slack.com/messaging/webhooks) |
|
||||||
|
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||||
|
|
||||||
!!! abstract "Environment variables"
|
!!! abstract "Environment variables"
|
||||||
* `DIUN_NOTIF_SLACK_WEBHOOKURL`
|
* `DIUN_NOTIF_SLACK_WEBHOOKURL`
|
||||||
|
* `DIUN_NOTIF_SLACK_TEMPLATEBODY`
|
||||||
|
|
||||||
|
### Default `templateBody`
|
||||||
|
|
||||||
|
```
|
||||||
|
<!channel> Docker tag `{{ .Entry.Image }}` {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}.
|
||||||
|
```
|
||||||
|
|
||||||
## Sample
|
## Sample
|
||||||
|
|
||||||
|
|||||||
@@ -9,14 +9,24 @@ You can send notifications to your Teams team-channel using an [incoming webhook
|
|||||||
notif:
|
notif:
|
||||||
teams:
|
teams:
|
||||||
webhookURL: https://outlook.office.com/webhook/ABCD12EFG/HIJK34LMN/01234567890abcdefghij
|
webhookURL: https://outlook.office.com/webhook/ABCD12EFG/HIJK34LMN/01234567890abcdefghij
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|--------------------|---------------|---------------|
|
|--------------------|--------------------------------------------|---------------|
|
||||||
| `webhookURL`[^1] | | Teams [incoming webhook URL](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/what-are-webhooks-and-connectors) |
|
| `webhookURL`[^1] | | Teams [incoming webhook URL](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/what-are-webhooks-and-connectors) |
|
||||||
|
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||||
|
|
||||||
!!! abstract "Environment variables"
|
!!! abstract "Environment variables"
|
||||||
* `DIUN_NOTIF_TEAMS_WEBHOOKURL`
|
* `DIUN_NOTIF_TEAMS_WEBHOOKURL`
|
||||||
|
* `DIUN_NOTIF_TEAMS_TEMPLATEBODY`
|
||||||
|
|
||||||
|
### Default `templateBody`
|
||||||
|
|
||||||
|
```
|
||||||
|
Docker tag {{ if .Entry.Image.HubLink }}[`{{ .Entry.Image }}`]({{ .Entry.Image.HubLink }}){{ else }}`{{ .Entry.Image }}`{{ end }}{{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}.
|
||||||
|
```
|
||||||
|
|
||||||
## Sample
|
## Sample
|
||||||
|
|
||||||
|
|||||||
@@ -17,24 +17,37 @@ Multiple chat IDs can be provided in order to deliver notifications to multiple
|
|||||||
chatIDs:
|
chatIDs:
|
||||||
- 123456789
|
- 123456789
|
||||||
- 987654321
|
- 987654321
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|--------------------|---------------|---------------|
|
|--------------------|--------------------------------------------|---------------|
|
||||||
| `token` | | Telegram bot token |
|
| `token` | | Telegram bot token |
|
||||||
| `tokenFile` | | Use content of secret file as Telegram bot token if `token` not defined |
|
| `tokenFile` | | Use content of secret file as Telegram bot token if `token` not defined |
|
||||||
| `chatIDs` | | List of chat IDs to send notifications to |
|
| `chatIDs` | | List of chat IDs to send notifications to |
|
||||||
| `chatIDsFile` | | Use content of secret file as chat IDs if `chatIDs` not defined |
|
| `chatIDsFile` | | Use content of secret file as chat IDs if `chatIDs` not defined |
|
||||||
|
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||||
|
|
||||||
!!! abstract "Environment variables"
|
!!! abstract "Environment variables"
|
||||||
* `DIUN_NOTIF_TELEGRAM_TOKEN`
|
* `DIUN_NOTIF_TELEGRAM_TOKEN`
|
||||||
* `DIUN_NOTIF_TELEGRAM_TOKENFILE`
|
* `DIUN_NOTIF_TELEGRAM_TOKENFILE`
|
||||||
* `DIUN_NOTIF_TELEGRAM_CHATIDS` (comma separated)
|
* `DIUN_NOTIF_TELEGRAM_CHATIDS` (comma separated)
|
||||||
* `DIUN_NOTIF_TELEGRAM_CHATIDSFILE`
|
* `DIUN_NOTIF_TELEGRAM_CHATIDSFILE`
|
||||||
|
* `DIUN_NOTIF_TELEGRAM_TEMPLATEBODY`
|
||||||
|
|
||||||
!!! example "chat IDs secret file"
|
!!! example "chat IDs secret file"
|
||||||
Chat IDs secret file must be a valid JSON array like: `[123456789,987654321]`
|
Chat IDs secret file must be a valid JSON array like: `[123456789,987654321]`
|
||||||
|
|
||||||
|
### Default `templateBody`
|
||||||
|
|
||||||
|
```
|
||||||
|
Docker tag {{ if .Entry.Image.HubLink }}[{{ .Entry.Image }}]({{ .Entry.Image.HubLink }}){{ else }}{{ .Entry.Image }}{{ end }} which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }} on {{ escapeMarkdown .Meta.Hostname }}.
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
`escapeMarkdown` is a special function to deal with a [makrdown strict parsing issue](https://github.com/crazy-max/diun/issues/162#issuecomment-683095898) on Telegram server.
|
||||||
|
|
||||||
## Sample
|
## Sample
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
First, [be a good guy](https://github.com/kossnocorp/etiquette/blob/master/README.md).
|
First, [be a good guy](https://github.com/kossnocorp/etiquette/blob/master/README.md).
|
||||||
|
|
||||||
Please do a search in [open issues]({{ config.repo_url }}issues?utf8=%E2%9C%93&q=) to see if the issue or feature request has already been filed and read the [FAQ](faq.md) page first.
|
Please do a search in [open issues]([[ config.repo_url ]]issues?utf8=%E2%9C%93&q=) to see if the issue or feature request has already been filed and read the [FAQ](faq.md) page first.
|
||||||
|
|
||||||
If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment.
|
If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment.
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ File a single issue per problem and feature request.
|
|||||||
|
|
||||||
The more information you can provide, the more likely someone will be successful reproducing the issue and finding a fix.
|
The more information you can provide, the more likely someone will be successful reproducing the issue and finding a fix.
|
||||||
|
|
||||||
You are now ready to [create a new issue]({{ config.repo_url }}issues/new/choose)!
|
You are now ready to [create a new issue]([[ config.repo_url ]]issues/new/choose)!
|
||||||
|
|
||||||
## Closure policy
|
## Closure policy
|
||||||
|
|
||||||
|
|||||||
@@ -77,12 +77,16 @@ func TestLoadFile(t *testing.T) {
|
|||||||
"<@&200>",
|
"<@&200>",
|
||||||
},
|
},
|
||||||
Timeout: utl.NewDuration(10 * time.Second),
|
Timeout: utl.NewDuration(10 * time.Second),
|
||||||
|
TemplateTitle: model.NotifDefaultTemplateTitle,
|
||||||
|
TemplateBody: model.NotifDefaultTemplateBody,
|
||||||
},
|
},
|
||||||
Gotify: &model.NotifGotify{
|
Gotify: &model.NotifGotify{
|
||||||
Endpoint: "http://gotify.foo.com",
|
Endpoint: "http://gotify.foo.com",
|
||||||
Token: "Token123456",
|
Token: "Token123456",
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
Timeout: utl.NewDuration(10 * time.Second),
|
Timeout: utl.NewDuration(10 * time.Second),
|
||||||
|
TemplateTitle: model.NotifDefaultTemplateTitle,
|
||||||
|
TemplateBody: model.NotifDefaultTemplateBody,
|
||||||
},
|
},
|
||||||
Mail: &model.NotifMail{
|
Mail: &model.NotifMail{
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
@@ -92,6 +96,14 @@ func TestLoadFile(t *testing.T) {
|
|||||||
LocalName: "localhost",
|
LocalName: "localhost",
|
||||||
From: "diun@example.com",
|
From: "diun@example.com",
|
||||||
To: "webmaster@example.com",
|
To: "webmaster@example.com",
|
||||||
|
TemplateTitle: model.NotifDefaultTemplateTitle,
|
||||||
|
TemplateBody: `Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
||||||
|
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}.
|
||||||
|
|
||||||
|
This image has been {{ if (eq .Entry.Status "new") }}created{{ else }}updated{{ end }} at
|
||||||
|
<code>{{ .Entry.Manifest.Created.Format "Jan 02, 2006 15:04:05 UTC" }}</code> with digest <code>{{ .Entry.Manifest.Digest }}</code>
|
||||||
|
for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
Matrix: &model.NotifMatrix{
|
Matrix: &model.NotifMatrix{
|
||||||
HomeserverURL: "https://matrix.org",
|
HomeserverURL: "https://matrix.org",
|
||||||
@@ -99,6 +111,7 @@ func TestLoadFile(t *testing.T) {
|
|||||||
Password: "bar",
|
Password: "bar",
|
||||||
RoomID: "!abcdefGHIjklmno:matrix.org",
|
RoomID: "!abcdefGHIjklmno:matrix.org",
|
||||||
MsgType: model.NotifMatrixMsgTypeNotice,
|
MsgType: model.NotifMatrixMsgTypeNotice,
|
||||||
|
TemplateBody: model.NotifDefaultTemplateBody,
|
||||||
},
|
},
|
||||||
Mqtt: &model.NotifMqtt{
|
Mqtt: &model.NotifMqtt{
|
||||||
Scheme: "mqtt",
|
Scheme: "mqtt",
|
||||||
@@ -113,6 +126,8 @@ func TestLoadFile(t *testing.T) {
|
|||||||
Pushover: &model.NotifPushover{
|
Pushover: &model.NotifPushover{
|
||||||
Token: "uQiRzpo4DXghDmr9QzzfQu27cmVRsG",
|
Token: "uQiRzpo4DXghDmr9QzzfQu27cmVRsG",
|
||||||
Recipient: "gznej3rKEVAvPUxu9vvNnqpmZpokzF",
|
Recipient: "gznej3rKEVAvPUxu9vvNnqpmZpokzF",
|
||||||
|
TemplateTitle: model.NotifDefaultTemplateTitle,
|
||||||
|
TemplateBody: model.NotifDefaultTemplateBody,
|
||||||
},
|
},
|
||||||
RocketChat: &model.NotifRocketChat{
|
RocketChat: &model.NotifRocketChat{
|
||||||
Endpoint: "http://rocket.foo.com:3000",
|
Endpoint: "http://rocket.foo.com:3000",
|
||||||
@@ -120,6 +135,8 @@ func TestLoadFile(t *testing.T) {
|
|||||||
UserID: "abcdEFGH012345678",
|
UserID: "abcdEFGH012345678",
|
||||||
Token: "Token123456",
|
Token: "Token123456",
|
||||||
Timeout: utl.NewDuration(10 * time.Second),
|
Timeout: utl.NewDuration(10 * time.Second),
|
||||||
|
TemplateTitle: model.NotifDefaultTemplateTitle,
|
||||||
|
TemplateBody: model.NotifRocketChatDefaultTemplateBody,
|
||||||
},
|
},
|
||||||
Script: &model.NotifScript{
|
Script: &model.NotifScript{
|
||||||
Cmd: "uname",
|
Cmd: "uname",
|
||||||
@@ -129,13 +146,16 @@ func TestLoadFile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Slack: &model.NotifSlack{
|
Slack: &model.NotifSlack{
|
||||||
WebhookURL: "https://hooks.slack.com/services/ABCD12EFG/HIJK34LMN/01234567890abcdefghij",
|
WebhookURL: "https://hooks.slack.com/services/ABCD12EFG/HIJK34LMN/01234567890abcdefghij",
|
||||||
|
TemplateBody: model.NotifSlackDefaultTemplateBody,
|
||||||
},
|
},
|
||||||
Teams: &model.NotifTeams{
|
Teams: &model.NotifTeams{
|
||||||
WebhookURL: "https://outlook.office.com/webhook/ABCD12EFG/HIJK34LMN/01234567890abcdefghij",
|
WebhookURL: "https://outlook.office.com/webhook/ABCD12EFG/HIJK34LMN/01234567890abcdefghij",
|
||||||
|
TemplateBody: model.NotifTeamsDefaultTemplateBody,
|
||||||
},
|
},
|
||||||
Telegram: &model.NotifTelegram{
|
Telegram: &model.NotifTelegram{
|
||||||
Token: "abcdef123456",
|
Token: "abcdef123456",
|
||||||
ChatIDs: []int64{8547439, 1234567},
|
ChatIDs: []int64{8547439, 1234567},
|
||||||
|
TemplateBody: model.NotifTelegramDefaultTemplateBody,
|
||||||
},
|
},
|
||||||
Webhook: &model.NotifWebhook{
|
Webhook: &model.NotifWebhook{
|
||||||
Endpoint: "http://webhook.foo.com/sd54qad89azd5a",
|
Endpoint: "http://webhook.foo.com/sd54qad89azd5a",
|
||||||
@@ -293,6 +313,7 @@ func TestLoadEnv(t *testing.T) {
|
|||||||
Telegram: &model.NotifTelegram{
|
Telegram: &model.NotifTelegram{
|
||||||
Token: "abcdef123456",
|
Token: "abcdef123456",
|
||||||
ChatIDs: []int64{8547439, 1234567},
|
ChatIDs: []int64{8547439, 1234567},
|
||||||
|
TemplateBody: model.NotifTelegramDefaultTemplateBody,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Providers: &model.Providers{
|
Providers: &model.Providers{
|
||||||
@@ -398,6 +419,14 @@ func TestLoadMixed(t *testing.T) {
|
|||||||
LocalName: "foo.com",
|
LocalName: "foo.com",
|
||||||
From: "diun@foo.com",
|
From: "diun@foo.com",
|
||||||
To: "webmaster@foo.com",
|
To: "webmaster@foo.com",
|
||||||
|
TemplateTitle: model.NotifDefaultTemplateTitle,
|
||||||
|
TemplateBody: `Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
||||||
|
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}.
|
||||||
|
|
||||||
|
This image has been {{ if (eq .Entry.Status "new") }}created{{ else }}updated{{ end }} at
|
||||||
|
<code>{{ .Entry.Manifest.Created.Format "Jan 02, 2006 15:04:05 UTC" }}</code> with digest <code>{{ .Entry.Manifest.Digest }}</code>
|
||||||
|
for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RegOpts: nil,
|
RegOpts: nil,
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ notif:
|
|||||||
insecureSkipVerify: false
|
insecureSkipVerify: false
|
||||||
from: diun@example.com
|
from: diun@example.com
|
||||||
to: webmaster@example.com
|
to: webmaster@example.com
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
||||||
|
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}.
|
||||||
|
|
||||||
|
This image has been {{ if (eq .Entry.Status "new") }}created{{ else }}updated{{ end }} at
|
||||||
|
<code>{{ .Entry.Manifest.Created.Format "Jan 02, 2006 15:04:05 UTC" }}</code> with digest <code>{{ .Entry.Manifest.Digest }}</code>
|
||||||
|
for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
||||||
|
|
||||||
providers:
|
providers:
|
||||||
docker: {}
|
docker: {}
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ notif:
|
|||||||
insecureSkipVerify: false
|
insecureSkipVerify: false
|
||||||
from: diun@example.com
|
from: diun@example.com
|
||||||
to: webmaster@example.com
|
to: webmaster@example.com
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
||||||
|
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}.
|
||||||
|
|
||||||
|
This image has been {{ if (eq .Entry.Status "new") }}created{{ else }}updated{{ end }} at
|
||||||
|
<code>{{ .Entry.Manifest.Created.Format "Jan 02, 2006 15:04:05 UTC" }}</code> with digest <code>{{ .Entry.Manifest.Digest }}</code>
|
||||||
|
for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
||||||
matrix:
|
matrix:
|
||||||
homeserverURL: https://matrix.org
|
homeserverURL: https://matrix.org
|
||||||
user: "@foo:matrix.org"
|
user: "@foo:matrix.org"
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ notif:
|
|||||||
insecureSkipVerify: false
|
insecureSkipVerify: false
|
||||||
from: diun@example.com
|
from: diun@example.com
|
||||||
to: webmaster@example.com
|
to: webmaster@example.com
|
||||||
|
templateBody: |
|
||||||
|
Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
||||||
|
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}.
|
||||||
|
|
||||||
|
This image has been {{ if (eq .Entry.Status "new") }}created{{ else }}updated{{ end }} at
|
||||||
|
<code>{{ .Entry.Manifest.Created.Format "Jan 02, 2006 15:04:05 UTC" }}</code> with digest <code>{{ .Entry.Manifest.Digest }}</code>
|
||||||
|
for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
||||||
matrix:
|
matrix:
|
||||||
homeserverURL: https://matrix.org
|
homeserverURL: https://matrix.org
|
||||||
user: "@foo:matrix.org"
|
user: "@foo:matrix.org"
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ import (
|
|||||||
"github.com/crazy-max/diun/v4/pkg/registry"
|
"github.com/crazy-max/diun/v4/pkg/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Defaults used for notification template
|
||||||
|
const (
|
||||||
|
NotifDefaultTemplateTitle = `{{ if (eq .Entry.Status "new") }}New image {{ .Entry.Image }} has been added{{ else }}Image update for {{ .Entry.Image }}{{ end }}`
|
||||||
|
NotifDefaultTemplateBody = `Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }} which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }} on {{ .Meta.Hostname }}.`
|
||||||
|
)
|
||||||
|
|
||||||
// NotifEntries represents a list of notification entries
|
// NotifEntries represents a list of notification entries
|
||||||
type NotifEntries struct {
|
type NotifEntries struct {
|
||||||
Entries []NotifEntry
|
Entries []NotifEntry
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ type NotifDiscord struct {
|
|||||||
WebhookURL string `yaml:"webhookURL,omitempty" json:"webhookURL,omitempty" validate:"required"`
|
WebhookURL string `yaml:"webhookURL,omitempty" json:"webhookURL,omitempty" validate:"required"`
|
||||||
Mentions []string `yaml:"mentions,omitempty" json:"mentions,omitempty"`
|
Mentions []string `yaml:"mentions,omitempty" json:"mentions,omitempty"`
|
||||||
Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty" validate:"required"`
|
Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty" validate:"required"`
|
||||||
|
TemplateTitle string `yaml:"templateTitle,omitempty" json:"templateTitle,omitempty" validate:"required"`
|
||||||
|
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaults gets the default values
|
// GetDefaults gets the default values
|
||||||
@@ -23,4 +25,6 @@ func (s *NotifDiscord) GetDefaults() *NotifDiscord {
|
|||||||
// SetDefaults sets the default values
|
// SetDefaults sets the default values
|
||||||
func (s *NotifDiscord) SetDefaults() {
|
func (s *NotifDiscord) SetDefaults() {
|
||||||
s.Timeout = utl.NewDuration(10 * time.Second)
|
s.Timeout = utl.NewDuration(10 * time.Second)
|
||||||
|
s.TemplateTitle = NotifDefaultTemplateTitle
|
||||||
|
s.TemplateBody = NotifDefaultTemplateBody
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ type NotifGotify struct {
|
|||||||
TokenFile string `yaml:"tokenFile,omitempty" json:"tokenFile,omitempty" validate:"omitempty,file"`
|
TokenFile string `yaml:"tokenFile,omitempty" json:"tokenFile,omitempty" validate:"omitempty,file"`
|
||||||
Priority int `yaml:"priority,omitempty" json:"priority,omitempty" validate:"omitempty,min=0"`
|
Priority int `yaml:"priority,omitempty" json:"priority,omitempty" validate:"omitempty,min=0"`
|
||||||
Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty" validate:"required"`
|
Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty" validate:"required"`
|
||||||
|
TemplateTitle string `yaml:"templateTitle,omitempty" json:"templateTitle,omitempty" validate:"required"`
|
||||||
|
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaults gets the default values
|
// GetDefaults gets the default values
|
||||||
@@ -26,4 +28,6 @@ func (s *NotifGotify) GetDefaults() *NotifGotify {
|
|||||||
func (s *NotifGotify) SetDefaults() {
|
func (s *NotifGotify) SetDefaults() {
|
||||||
s.Priority = 1
|
s.Priority = 1
|
||||||
s.Timeout = utl.NewDuration(10 * time.Second)
|
s.Timeout = utl.NewDuration(10 * time.Second)
|
||||||
|
s.TemplateTitle = NotifDefaultTemplateTitle
|
||||||
|
s.TemplateBody = NotifDefaultTemplateBody
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,17 @@ import (
|
|||||||
"github.com/crazy-max/diun/v4/pkg/utl"
|
"github.com/crazy-max/diun/v4/pkg/utl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NotifMailDefaultTemplateBody ...
|
||||||
|
const NotifMailDefaultTemplateBody = `Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
||||||
|
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}
|
||||||
|
on {{ .Meta.Hostname }}.
|
||||||
|
|
||||||
|
This image has been {{ if (eq .Entry.Status "new") }}created{{ else }}updated{{ end }} at
|
||||||
|
<code>{{ .Entry.Manifest.Created.Format "Jan 02, 2006 15:04:05 UTC" }}</code> with digest <code>{{ .Entry.Manifest.Digest }}</code>
|
||||||
|
for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
||||||
|
|
||||||
|
Need help, or have questions? Go to {{ .Meta.URL }} and leave an issue.`
|
||||||
|
|
||||||
// NotifMail holds mail notification configuration details
|
// NotifMail holds mail notification configuration details
|
||||||
type NotifMail struct {
|
type NotifMail struct {
|
||||||
Host string `yaml:"host,omitempty" json:"host,omitempty" validate:"required"`
|
Host string `yaml:"host,omitempty" json:"host,omitempty" validate:"required"`
|
||||||
@@ -17,6 +28,8 @@ type NotifMail struct {
|
|||||||
PasswordFile string `yaml:"passwordFile,omitempty" json:"passwordFile,omitempty" validate:"omitempty,file"`
|
PasswordFile string `yaml:"passwordFile,omitempty" json:"passwordFile,omitempty" validate:"omitempty,file"`
|
||||||
From string `yaml:"from,omitempty" json:"from,omitempty" validate:"required,email"`
|
From string `yaml:"from,omitempty" json:"from,omitempty" validate:"required,email"`
|
||||||
To string `yaml:"to,omitempty" json:"to,omitempty" validate:"required,email"`
|
To string `yaml:"to,omitempty" json:"to,omitempty" validate:"required,email"`
|
||||||
|
TemplateTitle string `yaml:"templateTitle,omitempty" json:"templateTitle,omitempty" validate:"required"`
|
||||||
|
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaults gets the default values
|
// GetDefaults gets the default values
|
||||||
@@ -33,4 +46,6 @@ func (s *NotifMail) SetDefaults() {
|
|||||||
s.SSL = utl.NewFalse()
|
s.SSL = utl.NewFalse()
|
||||||
s.InsecureSkipVerify = utl.NewFalse()
|
s.InsecureSkipVerify = utl.NewFalse()
|
||||||
s.LocalName = "localhost"
|
s.LocalName = "localhost"
|
||||||
|
s.TemplateTitle = NotifDefaultTemplateTitle
|
||||||
|
s.TemplateBody = NotifMailDefaultTemplateBody
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ type NotifMatrix struct {
|
|||||||
PasswordFile string `yaml:"passwordFile,omitempty" json:"passwordFile,omitempty" validate:"omitempty,file"`
|
PasswordFile string `yaml:"passwordFile,omitempty" json:"passwordFile,omitempty" validate:"omitempty,file"`
|
||||||
RoomID string `yaml:"roomID,omitempty" json:"roomID,omitempty" validate:"required"`
|
RoomID string `yaml:"roomID,omitempty" json:"roomID,omitempty" validate:"required"`
|
||||||
MsgType NotifMatrixMsgType `yaml:"msgType,omitempty" json:"msgType,omitempty" validate:"required,oneof=notice text"`
|
MsgType NotifMatrixMsgType `yaml:"msgType,omitempty" json:"msgType,omitempty" validate:"required,oneof=notice text"`
|
||||||
|
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifMatrix message type constants
|
// NotifMatrix message type constants
|
||||||
@@ -31,4 +32,5 @@ func (s *NotifMatrix) GetDefaults() *NotifMatrix {
|
|||||||
func (s *NotifMatrix) SetDefaults() {
|
func (s *NotifMatrix) SetDefaults() {
|
||||||
s.HomeserverURL = "https://matrix.org"
|
s.HomeserverURL = "https://matrix.org"
|
||||||
s.MsgType = NotifMatrixMsgTypeNotice
|
s.MsgType = NotifMatrixMsgTypeNotice
|
||||||
|
s.TemplateBody = NotifDefaultTemplateBody
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,19 @@ type NotifPushover struct {
|
|||||||
Recipient string `yaml:"recipient,omitempty" json:"recipient,omitempty" validate:"omitempty"`
|
Recipient string `yaml:"recipient,omitempty" json:"recipient,omitempty" validate:"omitempty"`
|
||||||
RecipientFile string `yaml:"recipientFile,omitempty" json:"recipientFile,omitempty" validate:"omitempty,file"`
|
RecipientFile string `yaml:"recipientFile,omitempty" json:"recipientFile,omitempty" validate:"omitempty,file"`
|
||||||
Priority int `yaml:"priority,omitempty" json:"priority,omitempty" validate:"omitempty,min=-2,max=2"`
|
Priority int `yaml:"priority,omitempty" json:"priority,omitempty" validate:"omitempty,min=-2,max=2"`
|
||||||
|
TemplateTitle string `yaml:"templateTitle,omitempty" json:"templateTitle,omitempty" validate:"required"`
|
||||||
|
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaults gets the default values
|
// GetDefaults gets the default values
|
||||||
func (s *NotifPushover) GetDefaults() *NotifPushover {
|
func (s *NotifPushover) GetDefaults() *NotifPushover {
|
||||||
return nil
|
n := &NotifPushover{}
|
||||||
|
n.SetDefaults()
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults sets the default values
|
// SetDefaults sets the default values
|
||||||
func (s *NotifPushover) SetDefaults() {
|
func (s *NotifPushover) SetDefaults() {
|
||||||
// noop
|
s.TemplateTitle = NotifDefaultTemplateTitle
|
||||||
|
s.TemplateBody = NotifDefaultTemplateBody
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import (
|
|||||||
"github.com/crazy-max/diun/v4/pkg/utl"
|
"github.com/crazy-max/diun/v4/pkg/utl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NotifRocketChatDefaultTemplateBody ...
|
||||||
|
const NotifRocketChatDefaultTemplateBody = `Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }} on {{ .Meta.Hostname }}.`
|
||||||
|
|
||||||
// NotifRocketChat holds Rocket.Chat notification configuration details
|
// NotifRocketChat holds Rocket.Chat notification configuration details
|
||||||
type NotifRocketChat struct {
|
type NotifRocketChat struct {
|
||||||
Endpoint string `yaml:"endpoint,omitempty" json:"endpoint,omitempty" validate:"required"`
|
Endpoint string `yaml:"endpoint,omitempty" json:"endpoint,omitempty" validate:"required"`
|
||||||
@@ -14,6 +17,8 @@ type NotifRocketChat struct {
|
|||||||
Token string `yaml:"token,omitempty" json:"token,omitempty" validate:"omitempty"`
|
Token string `yaml:"token,omitempty" json:"token,omitempty" validate:"omitempty"`
|
||||||
TokenFile string `yaml:"tokenFile,omitempty" json:"tokenFile,omitempty" validate:"omitempty,file"`
|
TokenFile string `yaml:"tokenFile,omitempty" json:"tokenFile,omitempty" validate:"omitempty,file"`
|
||||||
Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty" validate:"required"`
|
Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty" validate:"required"`
|
||||||
|
TemplateTitle string `yaml:"templateTitle,omitempty" json:"templateTitle,omitempty" validate:"required"`
|
||||||
|
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaults gets the default values
|
// GetDefaults gets the default values
|
||||||
@@ -26,4 +31,6 @@ func (s *NotifRocketChat) GetDefaults() *NotifRocketChat {
|
|||||||
// SetDefaults sets the default values
|
// SetDefaults sets the default values
|
||||||
func (s *NotifRocketChat) SetDefaults() {
|
func (s *NotifRocketChat) SetDefaults() {
|
||||||
s.Timeout = utl.NewDuration(10 * time.Second)
|
s.Timeout = utl.NewDuration(10 * time.Second)
|
||||||
|
s.TemplateTitle = NotifDefaultTemplateTitle
|
||||||
|
s.TemplateBody = NotifRocketChatDefaultTemplateBody
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
// NotifSlackDefaultTemplateBody ...
|
||||||
|
const NotifSlackDefaultTemplateBody = "<!channel> Docker tag `{{ .Entry.Image }}` {{ if (eq .Entry.Status \"new\") }}newly added{{ else }}updated{{ end }}."
|
||||||
|
|
||||||
// NotifSlack holds slack notification configuration details
|
// NotifSlack holds slack notification configuration details
|
||||||
type NotifSlack struct {
|
type NotifSlack struct {
|
||||||
WebhookURL string `yaml:"webhookURL,omitempty" json:"webhookURL,omitempty" validate:"required"`
|
WebhookURL string `yaml:"webhookURL,omitempty" json:"webhookURL,omitempty" validate:"required"`
|
||||||
|
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaults gets the default values
|
// GetDefaults gets the default values
|
||||||
func (s *NotifSlack) GetDefaults() *NotifSlack {
|
func (s *NotifSlack) GetDefaults() *NotifSlack {
|
||||||
return nil
|
n := &NotifSlack{}
|
||||||
|
n.SetDefaults()
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults sets the default values
|
// SetDefaults sets the default values
|
||||||
func (s *NotifSlack) SetDefaults() {
|
func (s *NotifSlack) SetDefaults() {
|
||||||
// noop
|
s.TemplateBody = NotifSlackDefaultTemplateBody
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
// NotifTeamsDefaultTemplateBody ...
|
||||||
|
const NotifTeamsDefaultTemplateBody = "Docker tag {{ if .Entry.Image.HubLink }}[`{{ .Entry.Image }}`]({{ .Entry.Image.HubLink }}){{ else }}`{{ .Entry.Image }}`{{ end }}{{ if (eq .Entry.Status \"new\") }}newly added{{ else }}updated{{ end }}."
|
||||||
|
|
||||||
// NotifTeams holds Teams notification configuration details
|
// NotifTeams holds Teams notification configuration details
|
||||||
type NotifTeams struct {
|
type NotifTeams struct {
|
||||||
WebhookURL string `yaml:"webhookURL,omitempty" json:"webhookURL,omitempty" validate:"required"`
|
WebhookURL string `yaml:"webhookURL,omitempty" json:"webhookURL,omitempty" validate:"required"`
|
||||||
|
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaults gets the default values
|
// GetDefaults gets the default values
|
||||||
func (s *NotifTeams) GetDefaults() *NotifTeams {
|
func (s *NotifTeams) GetDefaults() *NotifTeams {
|
||||||
return nil
|
n := &NotifTeams{}
|
||||||
|
n.SetDefaults()
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults sets the default values
|
// SetDefaults sets the default values
|
||||||
func (s *NotifTeams) SetDefaults() {
|
func (s *NotifTeams) SetDefaults() {
|
||||||
// noop
|
s.TemplateBody = NotifTeamsDefaultTemplateBody
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
// NotifTelegramDefaultTemplateBody ...
|
||||||
|
const NotifTelegramDefaultTemplateBody = `Docker tag {{ if .Entry.Image.HubLink }}[{{ .Entry.Image }}]({{ .Entry.Image.HubLink }}){{ else }}{{ .Entry.Image }}{{ end }} which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }} on {{ escapeMarkdown .Meta.Hostname }}.`
|
||||||
|
|
||||||
// NotifTelegram holds Telegram notification configuration details
|
// NotifTelegram holds Telegram notification configuration details
|
||||||
type NotifTelegram struct {
|
type NotifTelegram struct {
|
||||||
Token string `yaml:"token,omitempty" json:"token,omitempty" validate:"omitempty"`
|
Token string `yaml:"token,omitempty" json:"token,omitempty" validate:"omitempty"`
|
||||||
TokenFile string `yaml:"tokenFile,omitempty" json:"tokenFile,omitempty" validate:"omitempty,file"`
|
TokenFile string `yaml:"tokenFile,omitempty" json:"tokenFile,omitempty" validate:"omitempty,file"`
|
||||||
ChatIDs []int64 `yaml:"chatIDs,omitempty" json:"chatIDs,omitempty" validate:"omitempty"`
|
ChatIDs []int64 `yaml:"chatIDs,omitempty" json:"chatIDs,omitempty" validate:"omitempty"`
|
||||||
ChatIDsFile string `yaml:"chatIDsFile,omitempty" json:"chatIDsFile,omitempty" validate:"omitempty,file"`
|
ChatIDsFile string `yaml:"chatIDsFile,omitempty" json:"chatIDsFile,omitempty" validate:"omitempty,file"`
|
||||||
|
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaults gets the default values
|
// GetDefaults gets the default values
|
||||||
func (s *NotifTelegram) GetDefaults() *NotifTelegram {
|
func (s *NotifTelegram) GetDefaults() *NotifTelegram {
|
||||||
return nil
|
n := &NotifTelegram{}
|
||||||
|
n.SetDefaults()
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults sets the default values
|
// SetDefaults sets the default values
|
||||||
func (s *NotifTelegram) SetDefaults() {
|
func (s *NotifTelegram) SetDefaults() {
|
||||||
// noop
|
s.TemplateBody = NotifTelegramDefaultTemplateBody
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/crazy-max/diun/v4/internal/model"
|
"github.com/crazy-max/diun/v4/internal/model"
|
||||||
"github.com/microcosm-cc/bluemonday"
|
"github.com/microcosm-cc/bluemonday"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/russross/blackfriday/v2"
|
"github.com/russross/blackfriday/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,13 +24,11 @@ type Client struct {
|
|||||||
type Options struct {
|
type Options struct {
|
||||||
Meta model.Meta
|
Meta model.Meta
|
||||||
Entry model.NotifEntry
|
Entry model.NotifEntry
|
||||||
TplFuncs template.FuncMap
|
TemplateTitle string
|
||||||
|
TemplateBody string
|
||||||
|
TemplateFuncs template.FuncMap
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultTpl = `Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
|
||||||
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}
|
|
||||||
on {{ .Meta.Hostname }}.`
|
|
||||||
|
|
||||||
// New initializes a new msg client
|
// New initializes a new msg client
|
||||||
func New(opts Options) (*Client, error) {
|
func New(opts Options) (*Client, error) {
|
||||||
return &Client{
|
return &Client{
|
||||||
@@ -38,47 +37,49 @@ func New(opts Options) (*Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RenderMarkdown returns a notification message as markdown
|
// RenderMarkdown returns a notification message as markdown
|
||||||
func (c *Client) RenderMarkdown() (title string, text []byte, err error) {
|
func (c *Client) RenderMarkdown() (title []byte, body []byte, err error) {
|
||||||
return c.RenderMarkdownTemplate(strings.ReplaceAll(defaultTpl, "\n", " "))
|
var titleBuf bytes.Buffer
|
||||||
}
|
titleTpl := template.Must(template.New("title").Funcs(c.opts.TemplateFuncs).Parse(strings.TrimSuffix(strings.TrimSpace(c.opts.TemplateTitle), "\n")))
|
||||||
|
err = titleTpl.Execute(&titleBuf, struct {
|
||||||
// RenderMarkdownTemplate returns a notification message as markdown with a custom template
|
|
||||||
func (c *Client) RenderMarkdownTemplate(tpl string) (title string, text []byte, err error) {
|
|
||||||
title = fmt.Sprintf("Image update for %s", c.opts.Entry.Image.String())
|
|
||||||
if c.opts.Entry.Status == model.ImageStatusNew {
|
|
||||||
title = fmt.Sprintf("New image %s has been added", c.opts.Entry.Image.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
var msgBuf bytes.Buffer
|
|
||||||
msgTpl := template.Must(template.New("notif").Funcs(c.opts.TplFuncs).Parse(tpl))
|
|
||||||
err = msgTpl.Execute(&msgBuf, struct {
|
|
||||||
Meta model.Meta
|
Meta model.Meta
|
||||||
Entry model.NotifEntry
|
Entry model.NotifEntry
|
||||||
}{
|
}{
|
||||||
Meta: c.opts.Meta,
|
Meta: c.opts.Meta,
|
||||||
Entry: c.opts.Entry,
|
Entry: c.opts.Entry,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return title, body, errors.Wrap(err, "Cannot render notif title")
|
||||||
|
}
|
||||||
|
title = titleBuf.Bytes()
|
||||||
|
|
||||||
|
var bodyBuf bytes.Buffer
|
||||||
|
bodyTpl := template.Must(template.New("body").Funcs(c.opts.TemplateFuncs).Parse(strings.TrimSuffix(strings.TrimSpace(c.opts.TemplateBody), "\n")))
|
||||||
|
err = bodyTpl.Execute(&bodyBuf, struct {
|
||||||
|
Meta model.Meta
|
||||||
|
Entry model.NotifEntry
|
||||||
|
}{
|
||||||
|
Meta: c.opts.Meta,
|
||||||
|
Entry: c.opts.Entry,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return title, body, errors.Wrap(err, "Cannot render notif body")
|
||||||
|
}
|
||||||
|
body = bodyBuf.Bytes()
|
||||||
|
|
||||||
text = msgBuf.Bytes()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderHTML returns a notification message as html
|
// RenderHTML returns a notification message as html
|
||||||
func (c *Client) RenderHTML() (title string, text []byte, err error) {
|
func (c *Client) RenderHTML() (title []byte, body []byte, err error) {
|
||||||
return c.RenderHTMLTemplate(strings.ReplaceAll(defaultTpl, "\n", " "))
|
title, body, err = c.RenderMarkdown()
|
||||||
}
|
|
||||||
|
|
||||||
// RenderHTMLTemplate returns a notification message as html with a custom template
|
|
||||||
func (c *Client) RenderHTMLTemplate(tpl string) (title string, text []byte, err error) {
|
|
||||||
title, text, err = c.RenderMarkdownTemplate(tpl)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return title, body, err
|
||||||
}
|
}
|
||||||
|
|
||||||
text = []byte(bluemonday.UGCPolicy().Sanitize(
|
body = []byte(bluemonday.UGCPolicy().Sanitize(
|
||||||
// Dirty way to remove wrapped <p></p> and newline
|
// Dirty way to remove wrapped <p></p> and newline
|
||||||
// https://github.com/russross/blackfriday/issues/237
|
// https://github.com/russross/blackfriday/issues/237
|
||||||
strings.TrimRight(strings.TrimLeft(strings.TrimSpace(string(blackfriday.Run(text))), "<p>"), "</p>"),
|
strings.TrimRight(strings.TrimLeft(strings.TrimSpace(string(blackfriday.Run(body))), "<p>"), "</p>"),
|
||||||
))
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,12 +47,14 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
message, err := msg.New(msg.Options{
|
message, err := msg.New(msg.Options{
|
||||||
Meta: c.meta,
|
Meta: c.meta,
|
||||||
Entry: entry,
|
Entry: entry,
|
||||||
|
TemplateTitle: c.cfg.TemplateTitle,
|
||||||
|
TemplateBody: c.cfg.TemplateBody,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
title, text, err := message.RenderMarkdown()
|
title, body, err := message.RenderMarkdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -62,7 +64,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
content.WriteString(fmt.Sprintf("%s ", mention))
|
content.WriteString(fmt.Sprintf("%s ", mention))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content.WriteString(title)
|
content.WriteString(string(title))
|
||||||
|
|
||||||
fields := []EmbedField{
|
fields := []EmbedField{
|
||||||
{
|
{
|
||||||
@@ -100,7 +102,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
AvatarURL: c.meta.Logo,
|
AvatarURL: c.meta.Logo,
|
||||||
Embeds: []Embed{
|
Embeds: []Embed{
|
||||||
{
|
{
|
||||||
Description: string(text),
|
Description: string(body),
|
||||||
Footer: EmbedFooter{
|
Footer: EmbedFooter{
|
||||||
Text: fmt.Sprintf("%s © %d %s %s", c.meta.Author, time.Now().Year(), c.meta.Name, c.meta.Version),
|
Text: fmt.Sprintf("%s © %d %s %s", c.meta.Author, time.Now().Year(), c.meta.Name, c.meta.Version),
|
||||||
IconURL: c.meta.Logo,
|
IconURL: c.meta.Logo,
|
||||||
|
|||||||
@@ -52,24 +52,26 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
message, err := msg.New(msg.Options{
|
message, err := msg.New(msg.Options{
|
||||||
Meta: c.meta,
|
Meta: c.meta,
|
||||||
Entry: entry,
|
Entry: entry,
|
||||||
|
TemplateTitle: c.cfg.TemplateTitle,
|
||||||
|
TemplateBody: c.cfg.TemplateBody,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
title, text, err := message.RenderMarkdown()
|
title, body, err := message.RenderMarkdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := json.Marshal(struct {
|
jsonBody, err := json.Marshal(struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
Extras map[string]interface{} `json:"extras"`
|
Extras map[string]interface{} `json:"extras"`
|
||||||
}{
|
}{
|
||||||
Message: string(text),
|
Message: string(body),
|
||||||
Title: title,
|
Title: string(title),
|
||||||
Priority: c.cfg.Priority,
|
Priority: c.cfg.Priority,
|
||||||
Extras: map[string]interface{}{
|
Extras: map[string]interface{}{
|
||||||
"client::display": map[string]string{
|
"client::display": map[string]string{
|
||||||
@@ -91,13 +93,13 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
q.Set("token", token)
|
q.Set("token", token)
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", u.String(), bytes.NewBuffer(body))
|
req, err := http.NewRequest("POST", u.String(), bytes.NewBuffer(jsonBody))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Add("Content-Length", strconv.Itoa(len(string(body))))
|
req.Header.Add("Content-Length", strconv.Itoa(len(string(jsonBody))))
|
||||||
req.Header.Set("User-Agent", c.meta.UserAgent)
|
req.Header.Set("User-Agent", c.meta.UserAgent)
|
||||||
|
|
||||||
resp, err := hc.Do(req)
|
resp, err := hc.Do(req)
|
||||||
|
|||||||
@@ -21,16 +21,6 @@ type Client struct {
|
|||||||
meta model.Meta
|
meta model.Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
const customTpl = `Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
|
||||||
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}
|
|
||||||
on {{ .Meta.Hostname }}.
|
|
||||||
|
|
||||||
This image has been {{ if (eq .Entry.Status "new") }}created{{ else }}updated{{ end }} at
|
|
||||||
<code>{{ .Entry.Manifest.Created.Format "Jan 02, 2006 15:04:05 UTC" }}</code> with digest <code>{{ .Entry.Manifest.Digest }}</code>
|
|
||||||
for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
|
||||||
|
|
||||||
Need help, or have questions? Go to {{ .Meta.URL }} and leave an issue.`
|
|
||||||
|
|
||||||
// New creates a new mail notification instance
|
// New creates a new mail notification instance
|
||||||
func New(config *model.NotifMail, meta model.Meta) notifier.Notifier {
|
func New(config *model.NotifMail, meta model.Meta) notifier.Notifier {
|
||||||
return notifier.Notifier{
|
return notifier.Notifier{
|
||||||
@@ -65,12 +55,14 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
message, err := msg.New(msg.Options{
|
message, err := msg.New(msg.Options{
|
||||||
Meta: c.meta,
|
Meta: c.meta,
|
||||||
Entry: entry,
|
Entry: entry,
|
||||||
|
TemplateTitle: c.cfg.TemplateTitle,
|
||||||
|
TemplateBody: c.cfg.TemplateBody,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
title, text, err := message.RenderMarkdownTemplate(customTpl)
|
title, body, err := message.RenderMarkdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -78,7 +70,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
email := hermes.Email{
|
email := hermes.Email{
|
||||||
Body: hermes.Body{
|
Body: hermes.Body{
|
||||||
Title: fmt.Sprintf("%s 🔔 notification", c.meta.Name),
|
Title: fmt.Sprintf("%s 🔔 notification", c.meta.Name),
|
||||||
FreeMarkdown: hermes.Markdown(text),
|
FreeMarkdown: hermes.Markdown(body),
|
||||||
Signature: "Thanks for your support!",
|
Signature: "Thanks for your support!",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -95,12 +87,12 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
return fmt.Errorf("hermes: %v", err)
|
return fmt.Errorf("hermes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := gomail.NewMessage()
|
mailMessage := gomail.NewMessage()
|
||||||
msg.SetHeader("From", fmt.Sprintf("%s <%s>", c.meta.Name, c.cfg.From))
|
mailMessage.SetHeader("From", fmt.Sprintf("%s <%s>", c.meta.Name, c.cfg.From))
|
||||||
msg.SetHeader("To", c.cfg.To)
|
mailMessage.SetHeader("To", c.cfg.To)
|
||||||
msg.SetHeader("Subject", title)
|
mailMessage.SetHeader("Subject", string(title))
|
||||||
msg.SetBody("text/plain", textpart)
|
mailMessage.SetBody("text/plain", textpart)
|
||||||
msg.AddAlternative("text/html", htmlpart)
|
mailMessage.AddAlternative("text/html", htmlpart)
|
||||||
|
|
||||||
var tlsConfig *tls.Config
|
var tlsConfig *tls.Config
|
||||||
if *c.cfg.InsecureSkipVerify {
|
if *c.cfg.InsecureSkipVerify {
|
||||||
@@ -128,5 +120,5 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
LocalName: c.cfg.LocalName,
|
LocalName: c.cfg.LocalName,
|
||||||
}
|
}
|
||||||
|
|
||||||
return dialer.DialAndSend(msg)
|
return dialer.DialAndSend(mailMessage)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
message, err := msg.New(msg.Options{
|
message, err := msg.New(msg.Options{
|
||||||
Meta: c.meta,
|
Meta: c.meta,
|
||||||
Entry: entry,
|
Entry: entry,
|
||||||
|
TemplateBody: c.cfg.TemplateBody,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import (
|
|||||||
"github.com/crazy-max/diun/v4/internal/notif/notifier"
|
"github.com/crazy-max/diun/v4/internal/notif/notifier"
|
||||||
"github.com/crazy-max/diun/v4/pkg/utl"
|
"github.com/crazy-max/diun/v4/pkg/utl"
|
||||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client represents an active mqtt notification object
|
// Client represents an active mqtt notification object
|
||||||
@@ -17,7 +15,6 @@ type Client struct {
|
|||||||
*notifier.Notifier
|
*notifier.Notifier
|
||||||
cfg *model.NotifMqtt
|
cfg *model.NotifMqtt
|
||||||
meta model.Meta
|
meta model.Meta
|
||||||
logger zerolog.Logger
|
|
||||||
mqttClient MQTT.Client
|
mqttClient MQTT.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +24,6 @@ func New(config *model.NotifMqtt, meta model.Meta) notifier.Notifier {
|
|||||||
Handler: &Client{
|
Handler: &Client{
|
||||||
cfg: config,
|
cfg: config,
|
||||||
meta: meta,
|
meta: meta,
|
||||||
logger: log.With().Str("notif", "mqtt").Logger(),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,19 +48,21 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
message, err := msg.New(msg.Options{
|
message, err := msg.New(msg.Options{
|
||||||
Meta: c.meta,
|
Meta: c.meta,
|
||||||
Entry: entry,
|
Entry: entry,
|
||||||
|
TemplateTitle: c.cfg.TemplateTitle,
|
||||||
|
TemplateBody: c.cfg.TemplateBody,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
title, text, err := message.RenderHTML()
|
title, body, err := message.RenderHTML()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = pushover.New(token).SendMessage(&pushover.Message{
|
_, err = pushover.New(token).SendMessage(&pushover.Message{
|
||||||
Message: string(text),
|
Title: string(title),
|
||||||
Title: title,
|
Message: string(body),
|
||||||
Priority: c.cfg.Priority,
|
Priority: c.cfg.Priority,
|
||||||
URL: c.meta.URL,
|
URL: c.meta.URL,
|
||||||
URLTitle: c.meta.Name,
|
URLTitle: c.meta.Name,
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ type Client struct {
|
|||||||
meta model.Meta
|
meta model.Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
const customTpl = `Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }} on {{ .Meta.Hostname }}.`
|
|
||||||
|
|
||||||
// New creates a new rocketchat notification instance
|
// New creates a new rocketchat notification instance
|
||||||
func New(config *model.NotifRocketChat, meta model.Meta) notifier.Notifier {
|
func New(config *model.NotifRocketChat, meta model.Meta) notifier.Notifier {
|
||||||
return notifier.Notifier{
|
return notifier.Notifier{
|
||||||
@@ -56,12 +54,14 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
message, err := msg.New(msg.Options{
|
message, err := msg.New(msg.Options{
|
||||||
Meta: c.meta,
|
Meta: c.meta,
|
||||||
Entry: entry,
|
Entry: entry,
|
||||||
|
TemplateTitle: c.cfg.TemplateTitle,
|
||||||
|
TemplateBody: c.cfg.TemplateBody,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
title, text, err := message.RenderMarkdownTemplate(customTpl)
|
title, body, err := message.RenderMarkdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -106,10 +106,10 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
Alias: c.meta.Name,
|
Alias: c.meta.Name,
|
||||||
Avatar: c.meta.Logo,
|
Avatar: c.meta.Logo,
|
||||||
Channel: c.cfg.Channel,
|
Channel: c.cfg.Channel,
|
||||||
Text: title,
|
Text: string(title),
|
||||||
Attachments: []Attachment{
|
Attachments: []Attachment{
|
||||||
{
|
{
|
||||||
Text: string(text),
|
Text: string(body),
|
||||||
Ts: json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
|
Ts: json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ type Client struct {
|
|||||||
meta model.Meta
|
meta model.Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
const customTpl = "<!channel> Docker tag `{{ .Entry.Image }}` {{ if (eq .Entry.Status \"new\") }}newly added{{ else }}updated{{ end }}."
|
|
||||||
|
|
||||||
// New creates a new slack notification instance
|
// New creates a new slack notification instance
|
||||||
func New(config *model.NotifSlack, meta model.Meta) notifier.Notifier {
|
func New(config *model.NotifSlack, meta model.Meta) notifier.Notifier {
|
||||||
return notifier.Notifier{
|
return notifier.Notifier{
|
||||||
@@ -41,12 +39,13 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
message, err := msg.New(msg.Options{
|
message, err := msg.New(msg.Options{
|
||||||
Meta: c.meta,
|
Meta: c.meta,
|
||||||
Entry: entry,
|
Entry: entry,
|
||||||
|
TemplateBody: c.cfg.TemplateBody,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, text, err := message.RenderMarkdownTemplate(customTpl)
|
_, body, err := message.RenderMarkdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -99,7 +98,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
AuthorSubname: "github.com/crazy-max/diun",
|
AuthorSubname: "github.com/crazy-max/diun",
|
||||||
AuthorLink: c.meta.URL,
|
AuthorLink: c.meta.URL,
|
||||||
AuthorIcon: c.meta.Logo,
|
AuthorIcon: c.meta.Logo,
|
||||||
Text: string(text),
|
Text: string(body),
|
||||||
Footer: fmt.Sprintf("%s © %d %s %s", c.meta.Author, time.Now().Year(), c.meta.Name, c.meta.Version),
|
Footer: fmt.Sprintf("%s © %d %s %s", c.meta.Author, time.Now().Year(), c.meta.Name, c.meta.Version),
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
Ts: json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
|
Ts: json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
|
||||||
|
|||||||
@@ -18,9 +18,6 @@ type Client struct {
|
|||||||
meta model.Meta
|
meta model.Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
const customTpl = "Docker tag {{ if .Entry.Image.HubLink }}[`{{ .Entry.Image }}`]({{ .Entry.Image.HubLink }}){{ else }}`{{ .Entry.Image }}`{{ end }}" +
|
|
||||||
"{{ if (eq .Entry.Status \"new\") }}newly added{{ else }}updated{{ end }}."
|
|
||||||
|
|
||||||
// New creates a new webhook notification instance
|
// New creates a new webhook notification instance
|
||||||
func New(config *model.NotifTeams, meta model.Meta) notifier.Notifier {
|
func New(config *model.NotifTeams, meta model.Meta) notifier.Notifier {
|
||||||
return notifier.Notifier{
|
return notifier.Notifier{
|
||||||
@@ -58,12 +55,13 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
message, err := msg.New(msg.Options{
|
message, err := msg.New(msg.Options{
|
||||||
Meta: c.meta,
|
Meta: c.meta,
|
||||||
Entry: entry,
|
Entry: entry,
|
||||||
|
TemplateBody: c.cfg.TemplateBody,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, text, err := message.RenderMarkdownTemplate(customTpl)
|
_, body, err := message.RenderMarkdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -73,7 +71,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
themeColor = "0076D7"
|
themeColor = "0076D7"
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := json.Marshal(struct {
|
jsonBody, err := json.Marshal(struct {
|
||||||
Type string `json:"@type"`
|
Type string `json:"@type"`
|
||||||
Context string `json:"@context"`
|
Context string `json:"@context"`
|
||||||
ThemeColor string `json:"themeColor"`
|
ThemeColor string `json:"themeColor"`
|
||||||
@@ -81,11 +79,11 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
Sections []Sections `json:"sections"`
|
Sections []Sections `json:"sections"`
|
||||||
}{
|
}{
|
||||||
Type: "MessageCard",
|
Type: "MessageCard",
|
||||||
Context: "http://schema.org/extensions",
|
Context: "https://schema.org/extensions",
|
||||||
ThemeColor: themeColor,
|
ThemeColor: themeColor,
|
||||||
Summary: string(text),
|
Summary: string(body),
|
||||||
Sections: []Sections{{
|
Sections: []Sections{{
|
||||||
ActivityTitle: string(text),
|
ActivityTitle: string(body),
|
||||||
ActivitySubtitle: "Provider: " + entry.Provider,
|
ActivitySubtitle: "Provider: " + entry.Provider,
|
||||||
Facts: []Fact{
|
Facts: []Fact{
|
||||||
{"Hostname", c.meta.Hostname},
|
{"Hostname", c.meta.Hostname},
|
||||||
@@ -99,7 +97,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", c.cfg.WebhookURL, bytes.NewBuffer(body))
|
req, err := http.NewRequest("POST", c.cfg.WebhookURL, bytes.NewBuffer(jsonBody))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ type Client struct {
|
|||||||
meta model.Meta
|
meta model.Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
const customTpl = `Docker tag {{ if .Entry.Image.HubLink }}[{{ .Entry.Image }}]({{ .Entry.Image.HubLink }}){{ else }}{{ .Entry.Image }}{{ end }}
|
|
||||||
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}
|
|
||||||
on {{ escapeMarkdown .Meta.Hostname }}.`
|
|
||||||
|
|
||||||
// New creates a new Telegram notification instance
|
// New creates a new Telegram notification instance
|
||||||
func New(config *model.NotifTelegram, meta model.Meta) notifier.Notifier {
|
func New(config *model.NotifTelegram, meta model.Meta) notifier.Notifier {
|
||||||
return notifier.Notifier{
|
return notifier.Notifier{
|
||||||
@@ -65,7 +61,8 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
message, err := msg.New(msg.Options{
|
message, err := msg.New(msg.Options{
|
||||||
Meta: c.meta,
|
Meta: c.meta,
|
||||||
Entry: entry,
|
Entry: entry,
|
||||||
TplFuncs: template.FuncMap{
|
TemplateBody: c.cfg.TemplateBody,
|
||||||
|
TemplateFuncs: template.FuncMap{
|
||||||
"escapeMarkdown": func(text string) string {
|
"escapeMarkdown": func(text string) string {
|
||||||
text = strings.ReplaceAll(text, "_", "\\_")
|
text = strings.ReplaceAll(text, "_", "\\_")
|
||||||
text = strings.ReplaceAll(text, "*", "\\*")
|
text = strings.ReplaceAll(text, "*", "\\*")
|
||||||
@@ -79,7 +76,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, text, err := message.RenderMarkdownTemplate(strings.ReplaceAll(customTpl, "\n", " "))
|
_, body, err := message.RenderMarkdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -89,7 +86,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
|||||||
BaseChat: tgbotapi.BaseChat{
|
BaseChat: tgbotapi.BaseChat{
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
},
|
},
|
||||||
Text: string(text),
|
Text: string(body),
|
||||||
ParseMode: "markdown",
|
ParseMode: "markdown",
|
||||||
DisableWebPagePreview: true,
|
DisableWebPagePreview: true,
|
||||||
})
|
})
|
||||||
|
|||||||
13
mkdocs.yml
13
mkdocs.yml
@@ -22,6 +22,13 @@ extra:
|
|||||||
plausible:
|
plausible:
|
||||||
server: plausible.re4per.com
|
server: plausible.re4per.com
|
||||||
domain: crazymax.dev/diun
|
domain: crazymax.dev/diun
|
||||||
|
template:
|
||||||
|
defaultTitle: |
|
||||||
|
{{ if (eq .Entry.Status "new") }}New image {{ .Entry.Image }} has been added{{ else }}Image update for {{ .Entry.Image }}{{ end }}
|
||||||
|
defaultBody: |
|
||||||
|
Docker tag {{ if .Entry.Image.HubLink }}[**{{ .Entry.Image }}**]({{ .Entry.Image.HubLink }}){{ else }}**{{ .Entry.Image }}**{{ end }}
|
||||||
|
which you subscribed to through {{ .Entry.Provider }} provider has been {{ if (eq .Entry.Status "new") }}newly added{{ else }}updated{{ end }}
|
||||||
|
on {{ .Meta.Hostname }}.
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
name: material
|
name: material
|
||||||
@@ -76,7 +83,11 @@ plugins:
|
|||||||
- git-revision-date-localized:
|
- git-revision-date-localized:
|
||||||
type: iso_datetime
|
type: iso_datetime
|
||||||
enable_creation_date: true
|
enable_creation_date: true
|
||||||
- macros
|
- macros:
|
||||||
|
j2_block_start_string: '[[%'
|
||||||
|
j2_block_end_string: '%]]'
|
||||||
|
j2_variable_start_string: '[['
|
||||||
|
j2_variable_end_string: ']]'
|
||||||
- search:
|
- search:
|
||||||
prebuild_index: python
|
prebuild_index: python
|
||||||
lang:
|
lang:
|
||||||
|
|||||||
Reference in New Issue
Block a user