Add Apprise integration

This commit is contained in:
Jean Khawand 2023-08-01 05:55:17 +02:00 committed by GitHub
parent da0198cc0d
commit bf4823bdbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 193 additions and 4 deletions

View File

@ -23,5 +23,9 @@ services:
POSTGRES_HOST_AUTH_METHOD: trust
ports:
- 5432:5432
apprise:
image: caronc/apprise:latest
restart: unless-stopped
hostname: apprise
volumes:
postgres-data: null

View File

@ -724,4 +724,13 @@ var migrations = []func(tx *sql.Tx) error{
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN apprise_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN apprise_url text default '';
ALTER TABLE integrations ADD COLUMN apprise_services_url text default '';
`
_, err = tx.Exec(sql)
return err
},
}

View File

@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package apprise
import (
"fmt"
"net"
"strings"
"time"
"miniflux.app/http/client"
"miniflux.app/model"
)
// Client represents a Apprise client.
type Client struct {
servicesURL string
baseURL string
}
// NewClient returns a new Apprise client.
func NewClient(serviceURL, baseURL string) *Client {
return &Client{serviceURL, baseURL}
}
// PushEntry pushes entry to apprise
func (c *Client) PushEntry(entry *model.Entry) error {
if c.baseURL == "" || c.servicesURL == "" {
return fmt.Errorf("apprise: missing credentials")
}
timeout := time.Duration(1 * time.Second)
_, err := net.DialTimeout("tcp", c.baseURL, timeout)
if err != nil {
clt := client.New(c.baseURL + "/notify")
message := "[" + entry.Title + "]" + "(" + entry.URL + ")" + "\n\n"
data := &Data{
Urls: c.servicesURL,
Body: message,
}
response, error := clt.PostJSON(data)
if error != nil {
return fmt.Errorf("apprise: ending message failed: %v", error)
}
if response.HasServerFailure() {
return fmt.Errorf("apprise: request failed, status=%d", response.StatusCode)
}
} else {
return fmt.Errorf("%s %s %s", c.baseURL, "responding on port:", strings.Split(c.baseURL, ":")[1])
}
return nil
}

View File

@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package apprise
type Data struct {
Urls string `json:"urls"`
Body string `json:"body"`
}

View File

@ -5,6 +5,7 @@ package integration // import "miniflux.app/integration"
import (
"miniflux.app/config"
"miniflux.app/integration/apprise"
"miniflux.app/integration/espial"
"miniflux.app/integration/instapaper"
"miniflux.app/integration/linkding"
@ -160,4 +161,16 @@ func PushEntry(entry *model.Entry, integration *model.Integration) {
logger.Error("[Integration] push entry to telegram bot failed: %v", err)
}
}
if integration.AppriseEnabled {
logger.Debug("[Integration] Sending Entry %q for User #%d to apprise", entry.URL, integration.UserID)
client := apprise.NewClient(
integration.AppriseServicesURL,
integration.AppriseURL,
)
err := client.PushEntry(entry)
if err != nil {
logger.Error("[Integration] push entry to apprise failed: %v", err)
}
}
}

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"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",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"form.integration.nunux_keeper_activate": "Αποθήκευση άρθρων στο Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Τελικό σημείο Nunux Keeper API",
"form.integration.nunux_keeper_api_key": "Κλειδί API Nunux Keeper",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise Services URLs (seperated by comma)",
"form.integration.nunux_keeper_activate": "Save entries to Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API key",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"form.integration.nunux_keeper_activate": "Enviar artículos a Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Acceso API de Nunux Keeper",
"form.integration.nunux_keeper_api_key": "Clave de API de Nunux Keeper",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"form.integration.nunux_keeper_activate": "Tallenna artikkelit Nunux Keeperiin",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API-päätepiste",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API-avain",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Sauvegarder les articles vers Notion",
"form.integration.notion_page_id": "l'identifiant de la page Notion",
"form.integration.notion_token": "Jeton d'accès de l'API de Notion",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"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",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"form.integration.nunux_keeper_activate": "विषय-वस्तु को ननक्स कीपर में सहेजें",
"form.integration.nunux_keeper_endpoint": "ननक्स कीपर एपीआई समापन बिंदु",
"form.integration.nunux_keeper_api_key": "ननक्स कीपर एपीआई कुंजी",

View File

@ -351,6 +351,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"form.integration.nunux_keeper_activate": "Simpan artikel ke Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Titik URL API Nunux Keeper",
"form.integration.nunux_keeper_api_key": "Kunci API Nunux Keeper",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"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",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"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",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"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",

View File

@ -356,6 +356,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"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",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"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",

View File

@ -356,6 +356,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"form.integration.nunux_keeper_activate": "Сохранять статьи в Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Конечная точка Nunux Keeper API",
"form.integration.nunux_keeper_api_key": "API-ключ Nunux Keeper",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"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ı",

View File

@ -353,6 +353,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"form.integration.nunux_keeper_activate": "Зберігати статті до Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint",
"form.integration.nunux_keeper_api_key": "Ключ API Nunux Keeper",

View File

@ -352,6 +352,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"form.integration.nunux_keeper_activate": "保存文章到 Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API 端点",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API 密钥",

View File

@ -354,6 +354,9 @@
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Apprise services urls seperated by comma",
"form.integration.nunux_keeper_activate": "儲存文章到 Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API 端點",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API 金鑰",

View File

@ -54,4 +54,7 @@ type Integration struct {
MatrixBotPassword string
MatrixBotURL string
MatrixBotChatID string
AppriseEnabled bool
AppriseURL string
AppriseServicesURL string
}

View File

@ -157,7 +157,10 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
matrix_bot_user,
matrix_bot_password,
matrix_bot_url,
matrix_bot_chat_id
matrix_bot_chat_id,
apprise_enabled,
apprise_url,
apprise_services_url
FROM
integrations
WHERE
@ -214,6 +217,9 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
&integration.MatrixBotPassword,
&integration.MatrixBotURL,
&integration.MatrixBotChatID,
&integration.AppriseEnabled,
&integration.AppriseURL,
&integration.AppriseServicesURL,
)
switch {
case err == sql.ErrNoRows:
@ -278,9 +284,12 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
notion_token=$45,
notion_page_id=$46,
readwise_enabled=$47,
readwise_api_key=$48
readwise_api_key=$48,
apprise_enabled=$49,
apprise_url=$50,
apprise_services_url=$51
WHERE
user_id=$49
user_id=$52
`
_, err := s.db.Exec(
query,
@ -332,6 +341,9 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
integration.NotionPageID,
integration.ReadwiseEnabled,
integration.ReadwiseAPIKey,
integration.AppriseEnabled,
integration.AppriseURL,
integration.AppriseServicesURL,
integration.UserID,
)
@ -352,7 +364,7 @@ func (s *Storage) HasSaveEntry(userID int64) (result bool) {
WHERE
user_id=$1
AND
(pinboard_enabled='t' OR instapaper_enabled='t' OR wallabag_enabled='t' OR notion_enabled='t' OR nunux_keeper_enabled='t' OR espial_enabled='t' OR readwise_enabled='t' OR pocket_enabled='t' OR linkding_enabled='t')
(pinboard_enabled='t' OR instapaper_enabled='t' OR wallabag_enabled='t' OR notion_enabled='t' OR nunux_keeper_enabled='t' OR espial_enabled='t' OR readwise_enabled='t' OR pocket_enabled='t' OR linkding_enabled='t' OR apprise_enabled='t')
`
if err := s.db.QueryRow(query, userID).Scan(&result); err != nil {
result = false

View File

@ -234,7 +234,26 @@
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
<h3>Apprise</h3>
<div class="form-section">
<label>
<input type="checkbox" name="apprise_enabled" value="1" {{ if .form.AppriseEnabled }}checked{{ end }}> {{ t "form.integration.apprise_activate" }}
</label>
<label for="form-apprise-url">{{ t "form.integration.apprise_url" }}</label>
<input type="text" name="apprise_url" id="form-apprise-url" value="{{ .form.AppriseURL }}" placeholder="http://apprise:8080" spellcheck="false">
<label for="form-apprise-services-url">{{ t "form.integration.apprise_services_url" }}
<a href="https://github.com/caronc/apprise/wiki" target="_blank">
{{ icon "external-link" }}
</a>
</label>
<input type="text" name="apprise_services_url" id="form-apprise-services-urls" value="{{ .form.AppriseServicesURL }}" placeholder="tgram://<token>/<chat_id>/,matrix://" spellcheck="false">
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
<h3>Telegram Bot</h3>
<div class="form-section">
<label>

View File

@ -59,6 +59,9 @@ type IntegrationForm struct {
MatrixBotPassword string
MatrixBotURL string
MatrixBotChatID string
AppriseEnabled bool
AppriseURL string
AppriseServicesURL string
}
// Merge copy form values to the model.
@ -109,6 +112,9 @@ func (i IntegrationForm) Merge(integration *model.Integration) {
integration.MatrixBotPassword = i.MatrixBotPassword
integration.MatrixBotURL = i.MatrixBotURL
integration.MatrixBotChatID = i.MatrixBotChatID
integration.AppriseEnabled = i.AppriseEnabled
integration.AppriseServicesURL = i.AppriseServicesURL
integration.AppriseURL = i.AppriseURL
}
// NewIntegrationForm returns a new IntegrationForm.
@ -162,5 +168,8 @@ func NewIntegrationForm(r *http.Request) *IntegrationForm {
MatrixBotPassword: r.FormValue("matrix_bot_password"),
MatrixBotURL: r.FormValue("matrix_bot_url"),
MatrixBotChatID: r.FormValue("matrix_bot_chat_id"),
AppriseEnabled: r.FormValue("apprise_enabled") == "1",
AppriseURL: r.FormValue("apprise_url"),
AppriseServicesURL: r.FormValue("apprise_services_url"),
}
}

View File

@ -74,6 +74,9 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) {
MatrixBotPassword: integration.MatrixBotPassword,
MatrixBotURL: integration.MatrixBotURL,
MatrixBotChatID: integration.MatrixBotChatID,
AppriseEnabled: integration.AppriseEnabled,
AppriseURL: integration.AppriseURL,
AppriseServicesURL: integration.AppriseServicesURL,
}
sess := session.New(h.store, request.SessionID(r))