From 759ef889a3773af6f0c32133cb4915f4633cc8b2 Mon Sep 17 00:00:00 2001 From: Shaolong Chen Date: Fri, 18 Oct 2024 13:18:17 +0800 Subject: [PATCH] feat(integration): add cubox integration Signed-off-by: Shaolong Chen --- internal/database/migrations.go | 8 +++ internal/integration/cubox/cubox.go | 70 +++++++++++++++++++ internal/integration/integration.go | 20 ++++++ internal/locale/translations/de_DE.json | 2 + internal/locale/translations/el_EL.json | 2 + internal/locale/translations/en_US.json | 2 + internal/locale/translations/es_ES.json | 2 + internal/locale/translations/fi_FI.json | 2 + internal/locale/translations/fr_FR.json | 2 + internal/locale/translations/hi_IN.json | 2 + internal/locale/translations/id_ID.json | 2 + internal/locale/translations/it_IT.json | 2 + internal/locale/translations/ja_JP.json | 2 + internal/locale/translations/nl_NL.json | 2 + internal/locale/translations/pl_PL.json | 2 + internal/locale/translations/pt_BR.json | 2 + internal/locale/translations/ru_RU.json | 2 + internal/locale/translations/tr_TR.json | 2 + internal/locale/translations/uk_UA.json | 2 + internal/locale/translations/zh_CN.json | 2 + internal/locale/translations/zh_TW.json | 2 + internal/model/integration.go | 2 + internal/storage/integration.go | 17 +++-- .../templates/views/integrations.html | 16 +++++ internal/ui/form/integration.go | 6 ++ internal/ui/integration_show.go | 2 + 26 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 internal/integration/cubox/cubox.go diff --git a/internal/database/migrations.go b/internal/database/migrations.go index 2c7ea8b2838..8285038efa6 100644 --- a/internal/database/migrations.go +++ b/internal/database/migrations.go @@ -952,4 +952,12 @@ 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 cubox_enabled bool default 'f'; + ALTER TABLE integrations ADD COLUMN cubox_api_link text default ''; + ` + _, err = tx.Exec(sql) + return err + }, } diff --git a/internal/integration/cubox/cubox.go b/internal/integration/cubox/cubox.go new file mode 100644 index 00000000000..ed24b28b6cd --- /dev/null +++ b/internal/integration/cubox/cubox.go @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Cubox API documentation: https://help.cubox.cc/save/api/ + +package cubox // import "miniflux.app/v2/internal/integration/cubox" + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "time" + + "miniflux.app/v2/internal/version" +) + +const defaultClientTimeout = 10 * time.Second + +type Client struct { + apiLink string +} + +func NewClient(apiLink string) *Client { + return &Client{apiLink: apiLink} +} + +func (c *Client) SaveLink(entryURL string) error { + if c.apiLink == "" { + return errors.New("cubox: missing API link") + } + + requestBody, err := json.Marshal(&card{ + Type: "url", + Content: entryURL, + }) + if err != nil { + return fmt.Errorf("cubox: unable to encode request body: %w", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), defaultClientTimeout) + defer cancel() + + request, err := http.NewRequestWithContext(ctx, http.MethodPost, c.apiLink, bytes.NewReader(requestBody)) + if err != nil { + return fmt.Errorf("cubox: unable to create request: %w", err) + } + + request.Header.Set("Content-Type", "application/json") + request.Header.Set("User-Agent", "Miniflux/"+version.Version) + + response, err := http.DefaultClient.Do(request) + if err != nil { + return fmt.Errorf("cubox: unable to send request: %w", err) + } + defer response.Body.Close() + + if response.StatusCode != 200 { + return fmt.Errorf("cubox: unable to save link: status=%d", response.StatusCode) + } + + return nil +} + +type card struct { + Type string `json:"type"` + Content string `json:"content"` +} diff --git a/internal/integration/integration.go b/internal/integration/integration.go index 042867762af..850811c9d50 100644 --- a/internal/integration/integration.go +++ b/internal/integration/integration.go @@ -9,6 +9,7 @@ import ( "miniflux.app/v2/internal/config" "miniflux.app/v2/internal/integration/apprise" "miniflux.app/v2/internal/integration/betula" + "miniflux.app/v2/internal/integration/cubox" "miniflux.app/v2/internal/integration/espial" "miniflux.app/v2/internal/integration/instapaper" "miniflux.app/v2/internal/integration/linkace" @@ -322,6 +323,25 @@ func SendEntry(entry *model.Entry, userIntegrations *model.Integration) { } } + if userIntegrations.CuboxEnabled { + slog.Debug("Sending entry to Cubox", + slog.Int64("user_id", userIntegrations.UserID), + slog.Int64("entry_id", entry.ID), + slog.String("entry_url", entry.URL), + ) + + client := cubox.NewClient(userIntegrations.CuboxAPILink) + + if err := client.SaveLink(entry.URL); err != nil { + slog.Error("Unable to send entry to Cubox", + slog.Int64("user_id", userIntegrations.UserID), + slog.Int64("entry_id", entry.ID), + slog.String("entry_url", entry.URL), + slog.Any("error", err), + ) + } + } + if userIntegrations.ShioriEnabled { slog.Debug("Sending entry to Shiori", slog.Int64("user_id", userIntegrations.UserID), diff --git a/internal/locale/translations/de_DE.json b/internal/locale/translations/de_DE.json index 370a9f3bfca..bc01c5191a9 100644 --- a/internal/locale/translations/de_DE.json +++ b/internal/locale/translations/de_DE.json @@ -355,6 +355,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "Allgemein", "form.feed.fieldset.rules": "Regeln", "form.feed.fieldset.network_settings": "Netzwerkeinstellungen", diff --git a/internal/locale/translations/el_EL.json b/internal/locale/translations/el_EL.json index 1585128cdf1..6c97936c843 100644 --- a/internal/locale/translations/el_EL.json +++ b/internal/locale/translations/el_EL.json @@ -359,6 +359,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.category.label.title": "Τίτλος", "form.category.hide_globally": "Απόκρυψη καταχωρήσεων σε γενική λίστα μη αναγνωσμένων", "form.user.label.username": "Χρήστης", diff --git a/internal/locale/translations/en_US.json b/internal/locale/translations/en_US.json index eaf4d1e89d1..937678da51c 100644 --- a/internal/locale/translations/en_US.json +++ b/internal/locale/translations/en_US.json @@ -510,6 +510,8 @@ "form.integration.ntfy_username": "Ntfy Username (optional)", "form.integration.ntfy_password": "Ntfy Password (optional)", "form.integration.ntfy_icon_url": "Ntfy Icon URL (optional)", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.api_key.label.description": "API Key Label", "form.submit.loading": "Loading…", "form.submit.saving": "Saving…", diff --git a/internal/locale/translations/es_ES.json b/internal/locale/translations/es_ES.json index 51cd0f230aa..62ab959bede 100644 --- a/internal/locale/translations/es_ES.json +++ b/internal/locale/translations/es_ES.json @@ -355,6 +355,8 @@ "form.feed.label.ntfy_default_priority": "Prioridad predeterminada a Ntfy", "form.feed.label.ntfy_low_priority": "Prioridad baja a Ntfy", "form.feed.label.ntfy_min_priority": "Prioridad mínima a Ntfy", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "General", "form.feed.fieldset.rules": "Reglas", "form.feed.fieldset.network_settings": "Ajustes de red", diff --git a/internal/locale/translations/fi_FI.json b/internal/locale/translations/fi_FI.json index 05945de8690..04dde43e884 100644 --- a/internal/locale/translations/fi_FI.json +++ b/internal/locale/translations/fi_FI.json @@ -355,6 +355,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "General", "form.feed.fieldset.rules": "Rules", "form.feed.fieldset.network_settings": "Network Settings", diff --git a/internal/locale/translations/fr_FR.json b/internal/locale/translations/fr_FR.json index 8fdf4f2e9ab..cee8fa6d5f5 100644 --- a/internal/locale/translations/fr_FR.json +++ b/internal/locale/translations/fr_FR.json @@ -355,6 +355,8 @@ "form.feed.label.ntfy_default_priority": "Priorité par défaut de notification", "form.feed.label.ntfy_low_priority": "Priorité basse de notification", "form.feed.label.ntfy_min_priority": "Priorité minimale de notification", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "Général", "form.feed.fieldset.rules": "Règles", "form.feed.fieldset.network_settings": "Paramètres réseau", diff --git a/internal/locale/translations/hi_IN.json b/internal/locale/translations/hi_IN.json index 746eed9be5f..aa1669e3cfb 100644 --- a/internal/locale/translations/hi_IN.json +++ b/internal/locale/translations/hi_IN.json @@ -355,6 +355,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "General", "form.feed.fieldset.rules": "Rules", "form.feed.fieldset.network_settings": "Network Settings", diff --git a/internal/locale/translations/id_ID.json b/internal/locale/translations/id_ID.json index e92516e05e0..e61e31e71d8 100644 --- a/internal/locale/translations/id_ID.json +++ b/internal/locale/translations/id_ID.json @@ -345,6 +345,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "General", "form.feed.fieldset.rules": "Rules", "form.feed.fieldset.network_settings": "Network Settings", diff --git a/internal/locale/translations/it_IT.json b/internal/locale/translations/it_IT.json index d754f5e50d3..7a4997434bb 100644 --- a/internal/locale/translations/it_IT.json +++ b/internal/locale/translations/it_IT.json @@ -355,6 +355,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "General", "form.feed.fieldset.rules": "Rules", "form.feed.fieldset.network_settings": "Network Settings", diff --git a/internal/locale/translations/ja_JP.json b/internal/locale/translations/ja_JP.json index 0b0a8f5f9c0..2a3b6a6efb6 100644 --- a/internal/locale/translations/ja_JP.json +++ b/internal/locale/translations/ja_JP.json @@ -345,6 +345,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "General", "form.feed.fieldset.rules": "Rules", "form.feed.fieldset.network_settings": "Network Settings", diff --git a/internal/locale/translations/nl_NL.json b/internal/locale/translations/nl_NL.json index fc7ef956aef..72ace2a5f99 100644 --- a/internal/locale/translations/nl_NL.json +++ b/internal/locale/translations/nl_NL.json @@ -355,6 +355,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy standaard prioriteit", "form.feed.label.ntfy_low_priority": "Ntfy lage prioriteit", "form.feed.label.ntfy_min_priority": "Ntfy minimale prioriteit", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "Algemeen", "form.feed.fieldset.rules": "Regels", "form.feed.fieldset.network_settings": "Netwerk Instellingen", diff --git a/internal/locale/translations/pl_PL.json b/internal/locale/translations/pl_PL.json index 1d7ca5ad133..fdae50b5899 100644 --- a/internal/locale/translations/pl_PL.json +++ b/internal/locale/translations/pl_PL.json @@ -365,6 +365,8 @@ "form.feed.label.ntfy_default_priority": "Domyślny priorytet ntfy", "form.feed.label.ntfy_low_priority": "Niski priorytet ntfy", "form.feed.label.ntfy_min_priority": "Minimalny priorytet ntfy", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "Ogólne", "form.feed.fieldset.rules": "Reguły", "form.feed.fieldset.network_settings": "Ustawienia sieci", diff --git a/internal/locale/translations/pt_BR.json b/internal/locale/translations/pt_BR.json index a1dc2b12467..c2461f5dfc1 100644 --- a/internal/locale/translations/pt_BR.json +++ b/internal/locale/translations/pt_BR.json @@ -355,6 +355,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "General", "form.feed.fieldset.rules": "Rules", "form.feed.fieldset.network_settings": "Network Settings", diff --git a/internal/locale/translations/ru_RU.json b/internal/locale/translations/ru_RU.json index 4c6b26bc7de..df031ccd232 100644 --- a/internal/locale/translations/ru_RU.json +++ b/internal/locale/translations/ru_RU.json @@ -365,6 +365,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "General", "form.feed.fieldset.rules": "Rules", "form.feed.fieldset.network_settings": "Network Settings", diff --git a/internal/locale/translations/tr_TR.json b/internal/locale/translations/tr_TR.json index 91ac11682be..7ae251b54d8 100644 --- a/internal/locale/translations/tr_TR.json +++ b/internal/locale/translations/tr_TR.json @@ -286,6 +286,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.prefs.fieldset.application_settings": "Uygulama Ayarları", "form.prefs.fieldset.authentication_settings": "Kimlik Doğrulama Ayarları", "form.prefs.fieldset.reader_settings": "Okuyucu Ayarları", diff --git a/internal/locale/translations/uk_UA.json b/internal/locale/translations/uk_UA.json index 7ca464184c6..214d6bd8bc5 100644 --- a/internal/locale/translations/uk_UA.json +++ b/internal/locale/translations/uk_UA.json @@ -365,6 +365,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.category.label.title": "Назва", "form.category.hide_globally": "Приховати записи в глобальному списку непрочитаного", "form.feed.fieldset.general": "General", diff --git a/internal/locale/translations/zh_CN.json b/internal/locale/translations/zh_CN.json index 7ec1eed430a..38adcf31971 100644 --- a/internal/locale/translations/zh_CN.json +++ b/internal/locale/translations/zh_CN.json @@ -345,6 +345,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy默认优先级", "form.feed.label.ntfy_low_priority": "Ntfy低优先级", "form.feed.label.ntfy_min_priority": "Ntfy最低优先级", + "form.integration.cubox_activate": "保存文章到 Cubox", + "form.integration.cubox_api_link": "Cubox API 链接", "form.feed.fieldset.general": "通用", "form.feed.fieldset.rules": "规则", "form.feed.fieldset.network_settings": "网络设置", diff --git a/internal/locale/translations/zh_TW.json b/internal/locale/translations/zh_TW.json index 3c7c46004d0..c70ea1c8fb5 100644 --- a/internal/locale/translations/zh_TW.json +++ b/internal/locale/translations/zh_TW.json @@ -345,6 +345,8 @@ "form.feed.label.ntfy_default_priority": "Ntfy default priority", "form.feed.label.ntfy_low_priority": "Ntfy low priority", "form.feed.label.ntfy_min_priority": "Ntfy min priority", + "form.integration.cubox_activate": "Save entries to Cubox", + "form.integration.cubox_api_link": "Cubox API link", "form.feed.fieldset.general": "通用", "form.feed.fieldset.rules": "規則", "form.feed.fieldset.network_settings": "網路設定", diff --git a/internal/model/integration.go b/internal/model/integration.go index 23fef60e16c..5b8d70abcba 100644 --- a/internal/model/integration.go +++ b/internal/model/integration.go @@ -104,4 +104,6 @@ type Integration struct { NtfyUsername string NtfyPassword string NtfyIconURL string + CuboxEnabled bool + CuboxAPILink string } diff --git a/internal/storage/integration.go b/internal/storage/integration.go index 90c46326e0a..dea3369d1bd 100644 --- a/internal/storage/integration.go +++ b/internal/storage/integration.go @@ -207,7 +207,9 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) { ntfy_api_token, ntfy_username, ntfy_password, - ntfy_icon_url + ntfy_icon_url, + cubox_enabled, + cubox_api_link FROM integrations WHERE @@ -314,6 +316,8 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) { &integration.NtfyUsername, &integration.NtfyPassword, &integration.NtfyIconURL, + &integration.CuboxEnabled, + &integration.CuboxAPILink, ) switch { case err == sql.ErrNoRows: @@ -428,9 +432,11 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error { ntfy_api_token=$95, ntfy_username=$96, ntfy_password=$97, - ntfy_icon_url=$98 + ntfy_icon_url=$98, + cubox_enabled=$99, + cubox_api_link=$100 WHERE - user_id=$99 + user_id=$101 ` _, err := s.db.Exec( query, @@ -532,6 +538,8 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error { integration.NtfyUsername, integration.NtfyPassword, integration.NtfyIconURL, + integration.CuboxEnabled, + integration.CuboxAPILink, integration.UserID, ) @@ -571,7 +579,8 @@ func (s *Storage) HasSaveEntry(userID int64) (result bool) { webhook_enabled='t' OR omnivore_enabled='t' OR raindrop_enabled='t' OR - betula_enabled='t' + betula_enabled='t' OR + cubox_enabled='t' ) ` if err := s.db.QueryRow(query, userID).Scan(&result); err != nil { diff --git a/internal/template/templates/views/integrations.html b/internal/template/templates/views/integrations.html index bd03f0506cf..469abcd5e87 100644 --- a/internal/template/templates/views/integrations.html +++ b/internal/template/templates/views/integrations.html @@ -57,6 +57,22 @@

{{ t "page.integrations.title" }}

+
+ Cubox +
+ + + + + +
+ +
+
+
+
Espial
diff --git a/internal/ui/form/integration.go b/internal/ui/form/integration.go index a60e9100255..0852123be6a 100644 --- a/internal/ui/form/integration.go +++ b/internal/ui/form/integration.go @@ -110,6 +110,8 @@ type IntegrationForm struct { NtfyUsername string NtfyPassword string NtfyIconURL string + CuboxEnabled bool + CuboxAPILink string } // Merge copy form values to the model. @@ -209,6 +211,8 @@ func (i IntegrationForm) Merge(integration *model.Integration) { integration.NtfyUsername = i.NtfyUsername integration.NtfyPassword = i.NtfyPassword integration.NtfyIconURL = i.NtfyIconURL + integration.CuboxEnabled = i.CuboxEnabled + integration.CuboxAPILink = i.CuboxAPILink } // NewIntegrationForm returns a new IntegrationForm. @@ -311,6 +315,8 @@ func NewIntegrationForm(r *http.Request) *IntegrationForm { NtfyUsername: r.FormValue("ntfy_username"), NtfyPassword: r.FormValue("ntfy_password"), NtfyIconURL: r.FormValue("ntfy_icon_url"), + CuboxEnabled: r.FormValue("cubox_enabled") == "1", + CuboxAPILink: r.FormValue("cubox_api_link"), } } diff --git a/internal/ui/integration_show.go b/internal/ui/integration_show.go index 8cc3bfe6add..1395fab263c 100644 --- a/internal/ui/integration_show.go +++ b/internal/ui/integration_show.go @@ -124,6 +124,8 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) { NtfyUsername: integration.NtfyUsername, NtfyPassword: integration.NtfyPassword, NtfyIconURL: integration.NtfyIconURL, + CuboxEnabled: integration.CuboxEnabled, + CuboxAPILink: integration.CuboxAPILink, } sess := session.New(h.store, request.SessionID(r))