diff --git a/database/migrations.go b/database/migrations.go index 18da44ed..8b207c29 100644 --- a/database/migrations.go +++ b/database/migrations.go @@ -546,4 +546,13 @@ var migrations = []func(tx *sql.Tx) error{ `) return err }, + func(tx *sql.Tx) (err error) { + sql := ` + ALTER TABLE integrations ADD COLUMN telegram_bot_enabled bool default 'f'; + ALTER TABLE integrations ADD COLUMN telegram_bot_token text default ''; + ALTER TABLE integrations ADD COLUMN telegram_bot_chat_id text default ''; + ` + _, err = tx.Exec(sql) + return err + }, } diff --git a/go.mod b/go.mod index 4801c53e..e620412a 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/PuerkitoBio/goquery v1.7.1 github.com/coreos/go-oidc v2.2.1+incompatible github.com/felixge/httpsnoop v1.0.1 // indirect + github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect github.com/golang/gddo v0.0.0-20200831202555-721e228c7686 // indirect github.com/gorilla/mux v1.8.0 github.com/lib/pq v1.10.2 @@ -15,6 +16,7 @@ require ( github.com/rylans/getlang v0.0.0-20200505200108-4c3188ff8a2d github.com/stretchr/testify v1.6.1 // indirect github.com/tdewolff/minify/v2 v2.9.21 + github.com/technoweenie/multipartstreamer v1.0.1 // indirect golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/net v0.0.0-20210614182718-04defd469f4e golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d diff --git a/go.sum b/go.sum index f7b2b3f1..96c33d90 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU= +github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/gddo v0.0.0-20180823221919-9d8ff1c67be5/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= @@ -227,6 +229,8 @@ github.com/tdewolff/parse/v2 v2.5.19 h1:Kjaj3KQOx/4elIxlBSglus4E2oMfdROphvbq2b+O github.com/tdewolff/parse/v2 v2.5.19/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= +github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= diff --git a/integration/integration.go b/integration/integration.go index 211ed5f1..2d5d38d9 100644 --- a/integration/integration.go +++ b/integration/integration.go @@ -10,6 +10,7 @@ import ( "miniflux.app/integration/nunuxkeeper" "miniflux.app/integration/pinboard" "miniflux.app/integration/pocket" + "miniflux.app/integration/telegrambot" "miniflux.app/integration/wallabag" "miniflux.app/logger" "miniflux.app/model" @@ -70,3 +71,15 @@ func SendEntry(entry *model.Entry, integration *model.Integration) { } } } + +// PushEntry pushes new entry to the activated providers. +// This function should be wrapped in a goroutine to avoid block of program execution. +func PushEntry(entry *model.Entry, integration *model.Integration) { + if integration.TelegramBotEnabled { + logger.Debug("[Integration] Sending Entry #%d for User #%d to telegram", entry.ID, integration.UserID) + err := telegrambot.PushEntry(entry, integration.TelegramBotToken, integration.TelegramBotChatID) + if err != nil { + logger.Error("[Integration] push entry to telegram bot failed: %v", err) + } + } +} diff --git a/integration/telegrambot/doc.go b/integration/telegrambot/doc.go new file mode 100644 index 00000000..c3605fa9 --- /dev/null +++ b/integration/telegrambot/doc.go @@ -0,0 +1,2 @@ +// Package telegrambot provides a simple entry-to-telegram push +package telegrambot diff --git a/integration/telegrambot/telegrambot.go b/integration/telegrambot/telegrambot.go new file mode 100644 index 00000000..e864e306 --- /dev/null +++ b/integration/telegrambot/telegrambot.go @@ -0,0 +1,41 @@ +package telegrambot + +import ( + "bytes" + "fmt" + "html/template" + "strconv" + + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" + "miniflux.app/model" +) + +// PushEntry pushes entry to telegram chat using integration settings provided +func PushEntry(entry *model.Entry, botToken, chatID string) error { + bot, err := tgbotapi.NewBotAPI(botToken) + if err != nil { + return fmt.Errorf("telegrambot: create bot failed: %w", err) + } + + t, err := template.New("message").Parse("{{ .Title }}\n{{ .URL }}") + if err != nil { + return fmt.Errorf("telegrambot: parse template failed: %w", err) + } + + var result bytes.Buffer + + err = t.Execute(&result, entry) + if err != nil { + return fmt.Errorf("telegrambot: execute template failed: %w", err) + } + + chatId, _ := strconv.ParseInt(chatID, 10, 64) + msg := tgbotapi.NewMessage(chatId, result.String()) + msg.ParseMode = tgbotapi.ModeHTML + msg.DisableWebPagePreview = false + if _, err := bot.Send(msg); err != nil { + return fmt.Errorf("telegrambot: send message failed: %w", err) + } + + return nil +} diff --git a/locale/translations/de_DE.json b/locale/translations/de_DE.json index d2910973..868893cd 100644 --- a/locale/translations/de_DE.json +++ b/locale/translations/de_DE.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Artikel in Nunux Keeper speichern", "form.integration.nunux_keeper_endpoint": "Nunux Keeper API-Endpunkt", "form.integration.nunux_keeper_api_key": "Nunux Keeper API-Schlüssel", + "form.integration.telegram_bot_activate": "Pushen Sie neue Artikel in den Telegram-Chat", + "form.integration.telegram_bot_token": "Bot token", + "form.integration.telegram_chat_id": "Chat ID", "form.api_key.label.description": "API-Schlüsselbezeichnung", "form.submit.loading": "Lade...", "form.submit.saving": "Speichern...", diff --git a/locale/translations/el_EL.json b/locale/translations/el_EL.json index aee5c10b..06df3494 100644 --- a/locale/translations/el_EL.json +++ b/locale/translations/el_EL.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Αποθήκευση άρθρων στο Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Τελικό σημείο Nunux Keeper API", "form.integration.nunux_keeper_api_key": "Κλειδί API Nunux Keeper", + "form.integration.telegram_bot_activate": "Προωθήστε νέα άρθρα στη συνομιλία Telegram", + "form.integration.telegram_bot_token": "Διακριτικό bot", + "form.integration.telegram_chat_id": "Αναγνωριστικό συνομιλίας", "form.api_key.label.description": "Ετικέτα κλειδιού API", "form.submit.loading": "Φόρτωση...", "form.submit.saving": "Αποθήκευση...", diff --git a/locale/translations/en_US.json b/locale/translations/en_US.json index 7ed64552..3e6e3e48 100644 --- a/locale/translations/en_US.json +++ b/locale/translations/en_US.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Save articles to Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint", "form.integration.nunux_keeper_api_key": "Nunux Keeper API key", + "form.integration.telegram_bot_activate": "Push new articles to Telegram chat", + "form.integration.telegram_bot_token": "Bot token", + "form.integration.telegram_chat_id": "Chat ID", "form.api_key.label.description": "API Key Label", "form.submit.loading": "Loading...", "form.submit.saving": "Saving...", diff --git a/locale/translations/es_ES.json b/locale/translations/es_ES.json index 4a0172b1..dbef106b 100644 --- a/locale/translations/es_ES.json +++ b/locale/translations/es_ES.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Guardar artículos a Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Extremo de API de Nunux Keeper", "form.integration.nunux_keeper_api_key": "Clave de API de Nunux Keeper", + "form.integration.telegram_bot_activate": "Envíe nuevos artículos al chat de Telegram", + "form.integration.telegram_bot_token": "Token de bot", + "form.integration.telegram_chat_id": "ID de chat", "form.api_key.label.description": "Etiqueta de clave API", "form.submit.loading": "Cargando...", "form.submit.saving": "Guardando...", diff --git a/locale/translations/fr_FR.json b/locale/translations/fr_FR.json index 441617dc..e45ee9bb 100644 --- a/locale/translations/fr_FR.json +++ b/locale/translations/fr_FR.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Sauvegarder les articles vers Nunux Keeper", "form.integration.nunux_keeper_endpoint": "URL de l'API de Nunux Keeper", "form.integration.nunux_keeper_api_key": "Clé d'API de Nunux Keeper", + "form.integration.telegram_bot_activate": "Envoyer les nouveaux articles vers Telegram", + "form.integration.telegram_bot_token": "Jeton de sécurité de l'API du Bot Telegram", + "form.integration.telegram_chat_id": "Identifiant de discussion", "form.api_key.label.description": "Libellé de la clé d'API", "form.submit.loading": "Chargement...", "form.submit.saving": "Sauvegarde en cours...", diff --git a/locale/translations/it_IT.json b/locale/translations/it_IT.json index a1cb5010..b50590ca 100644 --- a/locale/translations/it_IT.json +++ b/locale/translations/it_IT.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Salva gli articoli su Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Endpoint dell'API di Nunux Keeper", "form.integration.nunux_keeper_api_key": "API key dell'account Nunux Keeper", + "form.integration.telegram_bot_activate": "Invia nuovi articoli alla chat di Telegram", + "form.integration.telegram_bot_token": "Token bot", + "form.integration.telegram_chat_id": "ID chat", "form.api_key.label.description": "Etichetta chiave API", "form.submit.loading": "Caricamento in corso...", "form.submit.saving": "Salvataggio in corso...", diff --git a/locale/translations/ja_JP.json b/locale/translations/ja_JP.json index 8d03d038..4d75a62a 100644 --- a/locale/translations/ja_JP.json +++ b/locale/translations/ja_JP.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Nunux Keeper に記事を保存する", "form.integration.nunux_keeper_endpoint": "Nunux Keeper の API Endpoint", "form.integration.nunux_keeper_api_key": "Nunux Keeper の API key", + "form.integration.telegram_bot_activate": "新しい記事をTelegramチャットにプッシュする", + "form.integration.telegram_bot_token": "ボットトークン", + "form.integration.telegram_chat_id": "チャットID", "form.api_key.label.description": "APIキーラベル", "form.submit.loading": "読み込み中…", "form.submit.saving": "保存中…", diff --git a/locale/translations/nl_NL.json b/locale/translations/nl_NL.json index 287afbf4..18a59238 100644 --- a/locale/translations/nl_NL.json +++ b/locale/translations/nl_NL.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Opslaan naar Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Nunux Keeper URL", "form.integration.nunux_keeper_api_key": "Nunux Keeper API-sleutel", + "form.integration.telegram_bot_activate": "Push nieuwe artikelen naar Telegram-chat", + "form.integration.telegram_bot_token": "Bot token", + "form.integration.telegram_chat_id": "Chat ID", "form.api_key.label.description": "API-sleutellabel", "form.submit.loading": "Laden...", "form.submit.saving": "Opslaag...", diff --git a/locale/translations/pl_PL.json b/locale/translations/pl_PL.json index c50f043a..e51275c5 100644 --- a/locale/translations/pl_PL.json +++ b/locale/translations/pl_PL.json @@ -327,6 +327,9 @@ "form.integration.nunux_keeper_activate": "Zapisz artykuly do Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Nunux Keeper URL", "form.integration.nunux_keeper_api_key": "Nunux Keeper API key", + "form.integration.telegram_bot_activate": "Przesyłaj nowe artykuły do czatu Telegram", + "form.integration.telegram_bot_token": "Token bota", + "form.integration.telegram_chat_id": "Identyfikator czatu", "form.api_key.label.description": "Etykieta klucza API", "form.submit.loading": "Ładowanie...", "form.submit.saving": "Zapisywanie...", diff --git a/locale/translations/pt_BR.json b/locale/translations/pt_BR.json index 3b163659..9c58d48c 100644 --- a/locale/translations/pt_BR.json +++ b/locale/translations/pt_BR.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Salvar itens no Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Endpoint de API do Nunux Keeper", "form.integration.nunux_keeper_api_key": "Chave de API do Nunux Keeper", + "form.integration.telegram_bot_activate": "Envie novos artigos para o chat do Telegram", + "form.integration.telegram_bot_token": "Token de bot", + "form.integration.telegram_chat_id": "ID de bate-papo", "form.api_key.label.description": "Etiqueta da chave de API", "form.submit.loading": "Carregando...", "form.submit.saving": "Salvando...", diff --git a/locale/translations/ru_RU.json b/locale/translations/ru_RU.json index 39055382..2095f986 100644 --- a/locale/translations/ru_RU.json +++ b/locale/translations/ru_RU.json @@ -327,6 +327,9 @@ "form.integration.nunux_keeper_activate": "Сохранять статьи в Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Конечная точка Nunux Keeper API", "form.integration.nunux_keeper_api_key": "Nunux Keeper API Key", + "form.integration.telegram_bot_activate": "Публикуйте новые статьи в Telegram-чате", + "form.integration.telegram_bot_token": "Токен бота", + "form.integration.telegram_chat_id": "ID чата", "form.api_key.label.description": "Описание API-ключа", "form.submit.loading": "Загрузка…", "form.submit.saving": "Сохранение…", diff --git a/locale/translations/tr_TR.json b/locale/translations/tr_TR.json index 1acf4141..18d1a538 100644 --- a/locale/translations/tr_TR.json +++ b/locale/translations/tr_TR.json @@ -325,6 +325,9 @@ "form.integration.nunux_keeper_activate": "Makaleleri Nunux Keeper'a kaydet", "form.integration.nunux_keeper_endpoint": "Nunux Keeper API Uç Noktası", "form.integration.nunux_keeper_api_key": "Nunux Keeper API anahtarı", + "form.integration.telegram_bot_activate": "Yeni makaleleri Telegram sohbetine gönderin", + "form.integration.telegram_bot_token": "Bot jetonu", + "form.integration.telegram_chat_id": "Sohbet kimliği", "form.api_key.label.description": "API Anahtar Etiketi", "form.submit.loading": "Yükleniyor...", "form.submit.saving": "Kaydediliyor...", diff --git a/locale/translations/zh_CN.json b/locale/translations/zh_CN.json index 768bcd45..9548fd86 100644 --- a/locale/translations/zh_CN.json +++ b/locale/translations/zh_CN.json @@ -323,7 +323,10 @@ "form.integration.nunux_keeper_activate": "保存文章到 Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Nunux Keeper API 端点", "form.integration.nunux_keeper_api_key": "Nunux Keeper API 密钥", - "form.api_key.label.description": "API 密钥标签", + "form.integration.telegram_bot_activate": "将新文章推送到 Telegram", + "form.integration.telegram_bot_token": "机器人令牌", + "form.integration.telegram_chat_id": "聊天ID", + "form.api_key.label.description": "API密钥标签", "form.submit.loading": "载入中…", "form.submit.saving": "保存中…", "time_elapsed.not_yet": "未来", diff --git a/model/integration.go b/model/integration.go index 528859f5..f60827b1 100644 --- a/model/integration.go +++ b/model/integration.go @@ -29,4 +29,7 @@ type Integration struct { PocketEnabled bool PocketAccessToken string PocketConsumerKey string + TelegramBotEnabled bool + TelegramBotToken string + TelegramBotChatID string } diff --git a/reader/processor/processor.go b/reader/processor/processor.go index c36a2046..a2165f51 100644 --- a/reader/processor/processor.go +++ b/reader/processor/processor.go @@ -14,6 +14,8 @@ import ( "time" "unicode/utf8" + "miniflux.app/integration" + "miniflux.app/config" "miniflux.app/http/client" "miniflux.app/logger" @@ -80,6 +82,18 @@ func ProcessFeedEntries(store *storage.Storage, feed *model.Feed) { // The sanitizer should always run at the end of the process to make sure unsafe HTML is filtered. entry.Content = sanitizer.Sanitize(entry.URL, entry.Content) + if entryIsNew { + intg, err := store.Integration(feed.UserID) + if err != nil { + logger.Error("[Processor] Get integrations for user %d failed: %v; the refresh process will go on, but no integrations will run this time.", feed.UserID, err) + } else if intg != nil { + localEntry := entry + go func() { + integration.PushEntry(localEntry, intg) + }() + } + } + updateEntryReadingTime(store, feed, entry, entryIsNew) filteredEntries = append(filteredEntries, entry) } diff --git a/storage/integration.go b/storage/integration.go index 48a6fdeb..a93df344 100644 --- a/storage/integration.go +++ b/storage/integration.go @@ -68,7 +68,10 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) { nunux_keeper_api_key, pocket_enabled, pocket_access_token, - pocket_consumer_key + pocket_consumer_key, + telegram_bot_enabled, + telegram_bot_token, + telegram_bot_chat_id FROM integrations WHERE @@ -99,6 +102,9 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) { &integration.PocketEnabled, &integration.PocketAccessToken, &integration.PocketConsumerKey, + &integration.TelegramBotEnabled, + &integration.TelegramBotToken, + &integration.TelegramBotChatID, ) switch { case err == sql.ErrNoRows: @@ -137,9 +143,12 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error { nunux_keeper_api_key=$19, pocket_enabled=$20, pocket_access_token=$21, - pocket_consumer_key=$22 + pocket_consumer_key=$22, + telegram_bot_enabled=$23, + telegram_bot_token=$24, + telegram_bot_chat_id=$25 WHERE - user_id=$23 + user_id=$26 ` _, err := s.db.Exec( query, @@ -165,6 +174,9 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error { integration.PocketEnabled, integration.PocketAccessToken, integration.PocketConsumerKey, + integration.TelegramBotEnabled, + integration.TelegramBotToken, + integration.TelegramBotChatID, integration.UserID, ) diff --git a/template/templates/views/integrations.html b/template/templates/views/integrations.html index 8c6c6fa4..8b9ecbb9 100644 --- a/template/templates/views/integrations.html +++ b/template/templates/views/integrations.html @@ -136,6 +136,24 @@ +