Support sending notifications to ntfy.sh

This commit is contained in:
Jan-Lukas Else 2021-12-29 07:09:42 +01:00
parent 4099f6c56c
commit e994e5400d
8 changed files with 124 additions and 13 deletions

View File

@ -258,9 +258,15 @@ type configActivityPub struct {
}
type configNotifications struct {
Ntfy *configNtfy `mapstructure:"ntfy"`
Telegram *configTelegram `mapstructure:"telegram"`
}
type configNtfy struct {
Enabled bool `mapstructure:"enabled"`
Topic string `mapstructure:"topic"`
}
type configTelegram struct {
Enabled bool `mapstructure:"enabled"`
ChatID string `mapstructure:"chatId"`

View File

@ -35,3 +35,9 @@ It is possible to configure multiple compression providers. If one fails, the ne
GoBlog features a button on each post that allows you to read the post's content aloud. By default, that uses an API from the browser to generate the speech. But it's not available on all browsers and on some operating systems it sounds horrible.
There's also the possibility to configure GoBlog to use Google Cloud's Text-to-Speech API. For that take a look at the `example-config.yml` file. If configured and enabled, after publishing a post, GoBlog will automatically generate an audio file, save it to the configured media storage (local file storage by default) and safe the audio file URL to the post's `tts` parameter. After updating a post, you can manually regenerate the audio file by using the button on the post. When deleting a post or regenerating the audio, GoBlog tries to delete the old audio file as well.
## Notifications
On receiving a webmention, a new comment or a contact form submission, GoBlog will create a new notification. Notifications are displayed on `/notifications` and can be deleted by the user.
If configured, GoBlog will also send a notification using a Telegram Bot or [Ntfy.sh](https://ntfy.sh/). See the `example-config.yml` file for how to configure the notification providers.

View File

@ -114,8 +114,11 @@ micropub:
# Notifications
notifications:
ntfy: # Receive notifications using Ntfy.sh
enabled: true # Enable it
topic: ntfy.sh/mynotificationstopic # The topic for the notifications
telegram: # Receive notifications via Telegram
enabled: true
enabled: true # Enable it
chatId: 123456 # Telegram chat ID (usually the user id on Telegram)
botToken: BOT-TOKEN # Telegram bot token

View File

@ -30,10 +30,12 @@ func (a *goBlog) sendNotification(text string) {
if err := a.db.saveNotification(n); err != nil {
log.Println("Failed to save notification:", err.Error())
}
if an := a.cfg.Notifications; an != nil {
_, _, err := a.send(an.Telegram, n.Text, "")
if err != nil {
log.Println("Failed to send Telegram notification:", err.Error())
if cfg := a.cfg.Notifications; cfg != nil {
if err := a.sendNtfy(cfg.Ntfy, n.Text); err != nil {
log.Println("Failed to send notification to Ntfy:", err.Error())
}
if _, _, err := a.sendTelegram(cfg.Telegram, n.Text, ""); err != nil {
log.Println("Failed to send notification to Telegram:", err.Error())
}
}
}

28
ntfy.go Normal file
View File

@ -0,0 +1,28 @@
package main
import (
"context"
"strings"
"github.com/carlmjohnson/requests"
)
func (ntfy *configNtfy) enabled() bool {
if ntfy == nil || !ntfy.Enabled || ntfy.Topic == "" {
return false
}
return true
}
func (a *goBlog) sendNtfy(cfg *configNtfy, msg string) error {
if !cfg.enabled() {
return nil
}
return requests.
URL(cfg.Topic).
Client(a.httpClient).
UserAgent(appUserAgent).
Post().
BodyReader(strings.NewReader(msg)).
Fetch(context.Background())
}

66
ntfy_test.go Normal file
View File

@ -0,0 +1,66 @@
package main
import (
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_ntfySending(t *testing.T) {
fakeClient := newFakeHttpClient()
fakeClient.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {}))
app := &goBlog{
cfg: createDefaultTestConfig(t),
httpClient: fakeClient.Client,
}
app.cfg.Notifications = &configNotifications{
Ntfy: &configNtfy{
Enabled: true,
Topic: "example.com/topic",
},
}
_ = app.initConfig()
_ = app.initDatabase(false)
app.initComponents(true)
app.sendNotification("Test notification")
req := fakeClient.req
require.NotNil(t, req)
assert.Equal(t, http.MethodPost, req.Method)
assert.Equal(t, "https://example.com/topic", req.URL.String())
reqBody, _ := req.GetBody()
reqBodyByte, _ := io.ReadAll(reqBody)
assert.Equal(t, "Test notification", string(reqBodyByte))
res := fakeClient.res
require.NotNil(t, res)
assert.Equal(t, http.StatusOK, res.StatusCode)
}
func Test_ntfyConfig(t *testing.T) {
var cfg *configNtfy
assert.False(t, cfg.enabled())
cfg = &configNtfy{}
assert.False(t, cfg.enabled())
cfg.Enabled = true
assert.False(t, cfg.enabled())
cfg.Topic = "example.com/topic"
assert.True(t, cfg.enabled())
}

View File

@ -25,7 +25,7 @@ func (a *goBlog) initTelegram() {
return
}
// Send message
chatId, msgId, err := a.send(tg, html, tgbotapi.ModeHTML)
chatId, msgId, err := a.sendTelegram(tg, html, tgbotapi.ModeHTML)
if err != nil {
log.Printf("Failed to send post to Telegram: %v", err)
return
@ -71,7 +71,7 @@ func (a *goBlog) initTelegram() {
return
}
// Send update
err = a.update(tg, chatId, messageId, html, "HTML")
err = a.updateTelegram(tg, chatId, messageId, html, "HTML")
if err != nil {
log.Printf("Failed to send update to Telegram: %v", err)
}
@ -98,7 +98,7 @@ func (a *goBlog) initTelegram() {
return
}
// Delete message
err = a.delete(tg, chatId, messageId)
err = a.deleteTelegram(tg, chatId, messageId)
if err != nil {
log.Printf("Failed to delete Telegram message: %v", err)
}
@ -134,7 +134,7 @@ func (tg *configTelegram) generateHTML(title, fullURL, shortURL string) string {
return message.String()
}
func (a *goBlog) send(tg *configTelegram, message, mode string) (int64, int, error) {
func (a *goBlog) sendTelegram(tg *configTelegram, message, mode string) (int64, int, error) {
if !tg.enabled() {
return 0, 0, nil
}
@ -156,7 +156,7 @@ func (a *goBlog) send(tg *configTelegram, message, mode string) (int64, int, err
return res.Chat.ID, res.MessageID, nil
}
func (a *goBlog) update(tg *configTelegram, chatId int64, messageId int, message, mode string) error {
func (a *goBlog) updateTelegram(tg *configTelegram, chatId int64, messageId int, message, mode string) error {
if !tg.enabled() {
return nil
}
@ -189,7 +189,7 @@ func (a *goBlog) update(tg *configTelegram, chatId int64, messageId int, message
return err
}
func (a *goBlog) delete(tg *configTelegram, chatId int64, messageId int) error {
func (a *goBlog) deleteTelegram(tg *configTelegram, chatId int64, messageId int) error {
if !tg.enabled() {
return nil
}

View File

@ -67,7 +67,7 @@ func Test_configTelegram_send(t *testing.T) {
httpClient: fakeClient.Client,
}
chatId, msgId, err := app.send(tg, "Message", "HTML")
chatId, msgId, err := app.sendTelegram(tg, "Message", "HTML")
require.Nil(t, err)
assert.Equal(t, 123, msgId)