Allow to customize notification message (#415)

Co-authored-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax
2021-06-25 23:24:02 +02:00
committed by GitHub
parent 066eafc8a9
commit 5d583809a0
43 changed files with 573 additions and 276 deletions

View File

@@ -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:

View File

@@ -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 -->

View File

@@ -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:

View File

@@ -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!

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -14,21 +14,26 @@ 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 |
| `insecureSkipVerify` | `false` | Controls whether a client verifies the server's certificate chain and hostname | | `insecureSkipVerify` | `false` | Controls whether a client verifies the server's certificate chain and hostname |
| `localName` | `localhost` | Hostname sent to the SMTP server with the HELO command | | `localName` | `localhost` | Hostname sent to the SMTP server with the HELO command |
| `username` | | SMTP username | | `username` | | SMTP username |
| `usernameFile` | | Use content of secret file as SMTP username if `username` not defined | | `usernameFile` | | Use content of secret file as SMTP username if `username` not defined |
| `password` | | SMTP password | | `password` | | SMTP password |
| `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

View File

@@ -13,17 +13,20 @@ 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 |
| `password` | | Password for authentication | | `password` | | Password for authentication |
| `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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
![](../assets/notif/telegram.png) ![](../assets/notif/telegram.png)

View File

@@ -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

View File

@@ -76,13 +76,17 @@ func TestLoadFile(t *testing.T) {
"<@125>", "<@125>",
"<@&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",
@@ -111,15 +124,19 @@ func TestLoadFile(t *testing.T) {
QoS: 0, QoS: 0,
}, },
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",
Channel: "#general", Channel: "#general",
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",
@@ -128,14 +145,17 @@ 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",
@@ -291,8 +311,9 @@ func TestLoadEnv(t *testing.T) {
Watch: (&model.Watch{}).GetDefaults(), Watch: (&model.Watch{}).GetDefaults(),
Notif: &model.Notif{ Notif: &model.Notif{
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,

View File

@@ -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: {}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -8,9 +8,11 @@ import (
// NotifDiscord holds Discord notification configuration details // NotifDiscord holds Discord notification configuration details
type NotifDiscord struct { 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
} }

View File

@@ -8,11 +8,13 @@ import (
// NotifGotify holds gotify notification configuration details // NotifGotify holds gotify notification configuration details
type NotifGotify struct { type NotifGotify struct {
Endpoint string `yaml:"endpoint,omitempty" json:"endpoint,omitempty" validate:"required"` Endpoint string `yaml:"endpoint,omitempty" json:"endpoint,omitempty" validate:"required"`
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"`
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
} }

View File

@@ -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
} }

View File

@@ -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
} }

View File

@@ -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
} }

View File

@@ -6,14 +6,19 @@ 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"`
Channel string `yaml:"channel,omitempty" json:"channel,omitempty" validate:"required"` Channel string `yaml:"channel,omitempty" json:"channel,omitempty" validate:"required"`
UserID string `yaml:"userID,omitempty" json:"userID,omitempty" validate:"required"` UserID string `yaml:"userID,omitempty" json:"userID,omitempty" validate:"required"`
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
} }

View File

@@ -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
} }

View File

@@ -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
} }

View File

@@ -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
} }

View File

@@ -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"
) )
@@ -21,15 +22,13 @@ type Client struct {
// Options holds msg client object options // Options holds msg client object options
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
} }

View File

@@ -45,14 +45,16 @@ 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,

View File

@@ -50,26 +50,28 @@ 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)

View File

@@ -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{
@@ -63,14 +53,16 @@ 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)
} }

View File

@@ -72,8 +72,9 @@ 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

View File

@@ -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
} }
@@ -25,9 +22,8 @@ type Client struct {
func New(config *model.NotifMqtt, meta model.Meta) notifier.Notifier { func New(config *model.NotifMqtt, meta model.Meta) notifier.Notifier {
return notifier.Notifier{ return notifier.Notifier{
Handler: &Client{ Handler: &Client{
cfg: config, cfg: config,
meta: meta, meta: meta,
logger: log.With().Str("notif", "mqtt").Logger(),
}, },
} }
} }

View File

@@ -46,21 +46,23 @@ 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,

View File

@@ -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{
@@ -54,14 +52,16 @@ 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,
}, },

View File

@@ -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{
@@ -39,14 +37,15 @@ func (c *Client) Name() string {
// Send creates and sends a slack notification with an entry // Send creates and sends a slack notification with an entry
func (c *Client) Send(entry model.NotifEntry) error { 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)),

View File

@@ -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{
@@ -56,14 +53,15 @@ 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
} }

View File

@@ -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{
@@ -63,9 +59,10 @@ 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,
}) })

View File

@@ -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: