Add Readeck integration

This commit is contained in:
MSTCL 2024-02-22 03:57:34 +00:00 committed by GitHub
parent 2f8d3a7958
commit cfdb890eae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 366 additions and 20 deletions

View File

@ -855,4 +855,15 @@ var migrations = []func(tx *sql.Tx) error{
_, err = tx.Exec(sql) _, err = tx.Exec(sql)
return err return err
}, },
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN readeck_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN readeck_only_url bool default 'f';
ALTER TABLE integrations ADD COLUMN readeck_url text default '';
ALTER TABLE integrations ADD COLUMN readeck_api_key text default '';
ALTER TABLE integrations ADD COLUMN readeck_labels text default '';
`
_, err = tx.Exec(sql)
return err
},
} }

View File

@ -19,6 +19,7 @@ import (
"miniflux.app/v2/internal/integration/omnivore" "miniflux.app/v2/internal/integration/omnivore"
"miniflux.app/v2/internal/integration/pinboard" "miniflux.app/v2/internal/integration/pinboard"
"miniflux.app/v2/internal/integration/pocket" "miniflux.app/v2/internal/integration/pocket"
"miniflux.app/v2/internal/integration/readeck"
"miniflux.app/v2/internal/integration/readwise" "miniflux.app/v2/internal/integration/readwise"
"miniflux.app/v2/internal/integration/shaarli" "miniflux.app/v2/internal/integration/shaarli"
"miniflux.app/v2/internal/integration/shiori" "miniflux.app/v2/internal/integration/shiori"
@ -250,6 +251,29 @@ func SendEntry(entry *model.Entry, userIntegrations *model.Integration) {
} }
} }
if userIntegrations.ReadeckEnabled {
slog.Debug("Sending entry to Readeck",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
)
client := readeck.NewClient(
userIntegrations.ReadeckURL,
userIntegrations.ReadeckAPIKey,
userIntegrations.ReadeckLabels,
userIntegrations.ReadeckOnlyURL,
)
if err := client.CreateBookmark(entry.URL, entry.Title, entry.Content); err != nil {
slog.Error("Unable to send entry to Readeck",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
slog.Any("error", err),
)
}
}
if userIntegrations.ReadwiseEnabled { if userIntegrations.ReadwiseEnabled {
slog.Debug("Sending entry to Readwise", slog.Debug("Sending entry to Readwise",
slog.Int64("user_id", userIntegrations.UserID), slog.Int64("user_id", userIntegrations.UserID),

View File

@ -0,0 +1,149 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package readeck // import "miniflux.app/v2/internal/integration/readeck"
import (
"bytes"
"encoding/json"
"fmt"
"mime/multipart"
"net/http"
"strings"
"time"
"miniflux.app/v2/internal/urllib"
"miniflux.app/v2/internal/version"
)
const defaultClientTimeout = 10 * time.Second
type Client struct {
baseURL string
apiKey string
labels string
onlyURL bool
}
func NewClient(baseURL, apiKey, labels string, onlyURL bool) *Client {
return &Client{baseURL: baseURL, apiKey: apiKey, labels: labels, onlyURL: onlyURL}
}
func (c *Client) CreateBookmark(entryURL, entryTitle string, entryContent string) error {
if c.baseURL == "" || c.apiKey == "" {
return fmt.Errorf("readeck: missing base URL or API key")
}
apiEndpoint, err := urllib.JoinBaseURLAndPath(c.baseURL, "/api/bookmarks/")
if err != nil {
return fmt.Errorf(`readeck: invalid API endpoint: %v`, err)
}
labelsSplitFn := func(c rune) bool {
return c == ',' || c == ' '
}
labelsSplit := strings.FieldsFunc(c.labels, labelsSplitFn)
var request *http.Request
if c.onlyURL {
requestBodyJson, err := json.Marshal(&readeckBookmark{
Url: entryURL,
Title: entryTitle,
Labels: labelsSplit,
})
if err != nil {
return fmt.Errorf("readeck: unable to encode request body: %v", err)
}
request, err = http.NewRequest(http.MethodPost, apiEndpoint, bytes.NewReader(requestBodyJson))
if err != nil {
return fmt.Errorf("readeck: unable to create request: %v", err)
}
request.Header.Set("Content-Type", "application/json")
} else {
requestBody := new(bytes.Buffer)
multipartWriter := multipart.NewWriter(requestBody)
urlPart, err := multipartWriter.CreateFormField("url")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry url): %v", err)
}
urlPart.Write([]byte(entryURL))
titlePart, err := multipartWriter.CreateFormField("title")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry title): %v", err)
}
titlePart.Write([]byte(entryTitle))
featurePart, err := multipartWriter.CreateFormField("feature_find_main")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (feature_find_main flag): %v", err)
}
featurePart.Write([]byte("false")) // false to disable readability
for _, label := range labelsSplit {
labelPart, err := multipartWriter.CreateFormField("labels")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry labels): %v", err)
}
labelPart.Write([]byte(label))
}
contentBodyHeader, err := json.Marshal(&partContentHeader{
Url: entryURL,
ContentHeader: contentHeader{ContentType: "text/html"},
})
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry content header): %v", err)
}
contentPart, err := multipartWriter.CreateFormFile("resource", "blob")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry content): %v", err)
}
contentPart.Write(contentBodyHeader)
contentPart.Write([]byte("\n"))
contentPart.Write([]byte(entryContent))
err = multipartWriter.Close()
if err != nil {
return fmt.Errorf("readeck: unable to encode request body: %v", err)
}
request, err = http.NewRequest(http.MethodPost, apiEndpoint, requestBody)
if err != nil {
return fmt.Errorf("readeck: unable to create request: %v", err)
}
request.Header.Set("Content-Type", multipartWriter.FormDataContentType())
}
request.Header.Set("User-Agent", "Miniflux/"+version.Version)
request.Header.Set("Authorization", "Bearer "+c.apiKey)
httpClient := &http.Client{Timeout: defaultClientTimeout}
response, err := httpClient.Do(request)
if err != nil {
return fmt.Errorf("readeck: unable to send request: %v", err)
}
defer response.Body.Close()
if response.StatusCode >= 400 {
return fmt.Errorf("readeck: unable to create bookmark: url=%s status=%d", apiEndpoint, response.StatusCode)
}
return nil
}
type readeckBookmark struct {
Url string `json:"url"`
Title string `json:"title"`
Labels []string `json:"labels,omitempty"`
}
type contentHeader struct {
ContentType string `json:"content-type"`
}
type partContentHeader struct {
Url string `json:"url"`
ContentHeader contentHeader `json:"headers"`
}

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Passwort für Matrix-Benutzer", "form.integration.matrix_bot_password": "Passwort für Matrix-Benutzer",
"form.integration.matrix_bot_url": "URL des Matrix-Servers", "form.integration.matrix_bot_url": "URL des Matrix-Servers",
"form.integration.matrix_bot_chat_id": "ID des Matrix-Raums", "form.integration.matrix_bot_chat_id": "ID des Matrix-Raums",
"form.integration.readeck_activate": "Artikel in Readeck speichern",
"form.integration.readeck_endpoint": "Readeck API-Endpunkt",
"form.integration.readeck_api_key": "Readeck API-Schlüssel",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Nur URL senden (anstelle des vollständigen Inhalts)",
"form.integration.shiori_activate": "Artikel in Shiori speichern", "form.integration.shiori_activate": "Artikel in Shiori speichern",
"form.integration.shiori_endpoint": "Shiori API-Endpunkt", "form.integration.shiori_endpoint": "Shiori API-Endpunkt",
"form.integration.shiori_username": "Shiori Benutzername", "form.integration.shiori_username": "Shiori Benutzername",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Κωδικός πρόσβασης για τον χρήστη Matrix", "form.integration.matrix_bot_password": "Κωδικός πρόσβασης για τον χρήστη Matrix",
"form.integration.matrix_bot_url": "URL διακομιστή Matrix", "form.integration.matrix_bot_url": "URL διακομιστή Matrix",
"form.integration.matrix_bot_chat_id": "Αναγνωριστικό της αίθουσας Matrix", "form.integration.matrix_bot_chat_id": "Αναγνωριστικό της αίθουσας Matrix",
"form.integration.readeck_activate": "Αποθήκευση άρθρων στο Readeck",
"form.integration.readeck_endpoint": "Τελικό σημείο Readeck API",
"form.integration.readeck_api_key": "Κλειδί API Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Αποστολή μόνο URL (αντί για πλήρες περιεχόμενο)",
"form.integration.shiori_activate": "Αποθήκευση άρθρων στο Shiori", "form.integration.shiori_activate": "Αποθήκευση άρθρων στο Shiori",
"form.integration.shiori_endpoint": "Τελικό σημείο Shiori", "form.integration.shiori_endpoint": "Τελικό σημείο Shiori",
"form.integration.shiori_username": "Όνομα Χρήστη Shiori", "form.integration.shiori_username": "Όνομα Χρήστη Shiori",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Password for Matrix user", "form.integration.matrix_bot_password": "Password for Matrix user",
"form.integration.matrix_bot_url": "Matrix server URL", "form.integration.matrix_bot_url": "Matrix server URL",
"form.integration.matrix_bot_chat_id": "ID of Matrix Room", "form.integration.matrix_bot_chat_id": "ID of Matrix Room",
"form.integration.readeck_activate": "Save entries to readeck",
"form.integration.readeck_endpoint": "Readeck API Endpoint",
"form.integration.readeck_api_key": "Readeck API key",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Send only URL (instead of full content)",
"form.integration.shiori_activate": "Save articles to Shiori", "form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Contraseña para el usuario de Matrix", "form.integration.matrix_bot_password": "Contraseña para el usuario de Matrix",
"form.integration.matrix_bot_url": "URL del servidor de Matrix", "form.integration.matrix_bot_url": "URL del servidor de Matrix",
"form.integration.matrix_bot_chat_id": "ID de la sala de Matrix", "form.integration.matrix_bot_chat_id": "ID de la sala de Matrix",
"form.integration.readeck_activate": "Enviar artículos a Readeck",
"form.integration.readeck_endpoint": "Acceso API de Readeck",
"form.integration.readeck_api_key": "Clave de API de Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Enviar solo URL (en lugar de contenido completo)",
"form.integration.shiori_activate": "Guardar artículos a Shiori", "form.integration.shiori_activate": "Guardar artículos a Shiori",
"form.integration.shiori_endpoint": "Extremo de API de Shiori", "form.integration.shiori_endpoint": "Extremo de API de Shiori",
"form.integration.shiori_username": "Nombre de usuario de Shiori", "form.integration.shiori_username": "Nombre de usuario de Shiori",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Matrix-käyttäjän salasana", "form.integration.matrix_bot_password": "Matrix-käyttäjän salasana",
"form.integration.matrix_bot_url": "Matrix-palvelimen URL-osoite", "form.integration.matrix_bot_url": "Matrix-palvelimen URL-osoite",
"form.integration.matrix_bot_chat_id": "Matrix-huoneen tunnus", "form.integration.matrix_bot_chat_id": "Matrix-huoneen tunnus",
"form.integration.readeck_activate": "Tallenna artikkelit Readeckiin",
"form.integration.readeck_endpoint": "Readeck API-päätepiste",
"form.integration.readeck_api_key": "Readeck API-avain",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Lähetä vain URL-osoite (koko sisällön sijaan)",
"form.integration.shiori_activate": "Save articles to Shiori", "form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Mot de passe de l'utilisateur Matrix", "form.integration.matrix_bot_password": "Mot de passe de l'utilisateur Matrix",
"form.integration.matrix_bot_url": "URL du serveur Matrix", "form.integration.matrix_bot_url": "URL du serveur Matrix",
"form.integration.matrix_bot_chat_id": "Identifiant de la salle Matrix", "form.integration.matrix_bot_chat_id": "Identifiant de la salle Matrix",
"form.integration.readeck_activate": "Sauvegarder les articles vers Readeck",
"form.integration.readeck_endpoint": "URL de l'API de Readeck",
"form.integration.readeck_api_key": "Clé d'API de Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Envoyer uniquement l'URL (au lieu du contenu complet)",
"form.integration.shiori_activate": "Sauvegarder les articles vers Shiori", "form.integration.shiori_activate": "Sauvegarder les articles vers Shiori",
"form.integration.shiori_endpoint": "URL de l'API de Shiori", "form.integration.shiori_endpoint": "URL de l'API de Shiori",
"form.integration.shiori_username": "Nom d'utilisateur de Shiori", "form.integration.shiori_username": "Nom d'utilisateur de Shiori",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "मैट्रिक्स उपयोगकर्ता के लिए पासवर्ड", "form.integration.matrix_bot_password": "मैट्रिक्स उपयोगकर्ता के लिए पासवर्ड",
"form.integration.matrix_bot_url": "मैट्रिक्स सर्वर URL", "form.integration.matrix_bot_url": "मैट्रिक्स सर्वर URL",
"form.integration.matrix_bot_chat_id": "मैट्रिक्स रूम की आईडी", "form.integration.matrix_bot_chat_id": "मैट्रिक्स रूम की आईडी",
"form.integration.readeck_activate": "Readeck में विषयवस्तु सहेजें",
"form.integration.readeck_endpoint": "Readeck·एपीआई·समापन·बिंदु",
"form.integration.readeck_api_key": "Readeck एपीआई कुंजी",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "केवल URL भेजें (पूर्ण सामग्री के बजाय)",
"form.integration.shiori_activate": "Save articles to Shiori", "form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",
@ -518,4 +523,4 @@
"error.feed_not_found": "This feed does not exist or does not belong to this user.", "error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.", "error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v." "error.feed_format_not_detected": "Unable to detect feed format: %v."
} }

View File

@ -444,6 +444,11 @@
"form.integration.matrix_bot_password": "Kata Sandi Matrix", "form.integration.matrix_bot_password": "Kata Sandi Matrix",
"form.integration.matrix_bot_url": "URL Peladen Matrix", "form.integration.matrix_bot_url": "URL Peladen Matrix",
"form.integration.matrix_bot_chat_id": "ID Ruang Matrix", "form.integration.matrix_bot_chat_id": "ID Ruang Matrix",
"form.integration.readeck_activate": "Simpan artikel ke Readeck",
"form.integration.readeck_endpoint": "Titik URL API Readeck",
"form.integration.readeck_api_key": "Kunci API Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Kirim hanya URL (alih-alih konten penuh)",
"form.integration.shiori_activate": "Save articles to Shiori", "form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Password per l'utente Matrix", "form.integration.matrix_bot_password": "Password per l'utente Matrix",
"form.integration.matrix_bot_url": "URL del server Matrix", "form.integration.matrix_bot_url": "URL del server Matrix",
"form.integration.matrix_bot_chat_id": "ID della stanza Matrix", "form.integration.matrix_bot_chat_id": "ID della stanza Matrix",
"form.integration.readeck_activate": "Salva gli articoli su Readeck",
"form.integration.readeck_endpoint": "Endpoint dell'API di Readeck",
"form.integration.readeck_api_key": "API key dell'account Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Invia solo URL (invece del contenuto completo)",
"form.integration.shiori_activate": "Salva gli articoli su Shiori", "form.integration.shiori_activate": "Salva gli articoli su Shiori",
"form.integration.shiori_endpoint": "Endpoint dell'API di Shiori", "form.integration.shiori_endpoint": "Endpoint dell'API di Shiori",
"form.integration.shiori_username": "Nome utente dell'account Shiori", "form.integration.shiori_username": "Nome utente dell'account Shiori",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Matrixユーザ用パスワード", "form.integration.matrix_bot_password": "Matrixユーザ用パスワード",
"form.integration.matrix_bot_url": "MatrixサーバーのURL", "form.integration.matrix_bot_url": "MatrixサーバーのURL",
"form.integration.matrix_bot_chat_id": "MatrixルームのID", "form.integration.matrix_bot_chat_id": "MatrixルームのID",
"form.integration.readeck_activate": "Readeck に記事を保存する",
"form.integration.readeck_endpoint": "Readeck の API Endpoint",
"form.integration.readeck_api_key": "Readeck の API key",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "URL のみを送信 (完全なコンテンツではなく)",
"form.integration.shiori_activate": "Shiori に記事を保存する", "form.integration.shiori_activate": "Shiori に記事を保存する",
"form.integration.shiori_endpoint": "Shiori の API Endpoint", "form.integration.shiori_endpoint": "Shiori の API Endpoint",
"form.integration.shiori_username": "Shiori の ユーザー名", "form.integration.shiori_username": "Shiori の ユーザー名",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Wachtwoord voor Matrix-gebruiker", "form.integration.matrix_bot_password": "Wachtwoord voor Matrix-gebruiker",
"form.integration.matrix_bot_url": "URL van de Matrix-server", "form.integration.matrix_bot_url": "URL van de Matrix-server",
"form.integration.matrix_bot_chat_id": "ID van Matrix-kamer", "form.integration.matrix_bot_chat_id": "ID van Matrix-kamer",
"form.integration.readeck_activate": "Opslaan naar Readeck",
"form.integration.readeck_endpoint": "Readeck URL",
"form.integration.readeck_api_key": "Readeck API-sleutel",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Alleen URL verzenden (in plaats van volledige inhoud)",
"form.integration.shiori_activate": "Opslaan naar Shiori", "form.integration.shiori_activate": "Opslaan naar Shiori",
"form.integration.shiori_endpoint": "Shiori URL", "form.integration.shiori_endpoint": "Shiori URL",
"form.integration.shiori_username": "Shiori gebruikersnaam", "form.integration.shiori_username": "Shiori gebruikersnaam",

View File

@ -450,6 +450,11 @@
"form.integration.matrix_bot_password": "Hasło dla użytkownika Matrix", "form.integration.matrix_bot_password": "Hasło dla użytkownika Matrix",
"form.integration.matrix_bot_url": "URL serwera Matrix", "form.integration.matrix_bot_url": "URL serwera Matrix",
"form.integration.matrix_bot_chat_id": "Identyfikator pokoju Matrix", "form.integration.matrix_bot_chat_id": "Identyfikator pokoju Matrix",
"form.integration.readeck_activate": "Zapisz artykuły do Readeck",
"form.integration.readeck_endpoint": "Readeck URL",
"form.integration.readeck_api_key": "Readeck API key",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Wyślij tylko adres URL (zamiast pełnej treści)",
"form.integration.shiori_activate": "Zapisz artykuły do Shiori", "form.integration.shiori_activate": "Zapisz artykuły do Shiori",
"form.integration.shiori_endpoint": "Shiori URL", "form.integration.shiori_endpoint": "Shiori URL",
"form.integration.shiori_username": "Login do Shiori", "form.integration.shiori_username": "Login do Shiori",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Palavra-passe para utilizador da Matrix", "form.integration.matrix_bot_password": "Palavra-passe para utilizador da Matrix",
"form.integration.matrix_bot_url": "URL do servidor Matrix", "form.integration.matrix_bot_url": "URL do servidor Matrix",
"form.integration.matrix_bot_chat_id": "Identificação da sala Matrix", "form.integration.matrix_bot_chat_id": "Identificação da sala Matrix",
"form.integration.readeck_activate": "Salvar itens no Readeck",
"form.integration.readeck_endpoint": "Endpoint de API do Readeck",
"form.integration.readeck_api_key": "Chave de API do Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Enviar apenas URL (em vez de conteúdo completo)",
"form.integration.shiori_activate": "Salvar itens no Shiori", "form.integration.shiori_activate": "Salvar itens no Shiori",
"form.integration.shiori_endpoint": "Endpoint da API do Shiori", "form.integration.shiori_endpoint": "Endpoint da API do Shiori",
"form.integration.shiori_username": "Nome de usuário do Shiori", "form.integration.shiori_username": "Nome de usuário do Shiori",

View File

@ -450,6 +450,11 @@
"form.integration.matrix_bot_password": "Пароль пользователя Matrix", "form.integration.matrix_bot_password": "Пароль пользователя Matrix",
"form.integration.matrix_bot_url": "Ссылка на сервер Matrix", "form.integration.matrix_bot_url": "Ссылка на сервер Matrix",
"form.integration.matrix_bot_chat_id": "ID комнаты Matrix", "form.integration.matrix_bot_chat_id": "ID комнаты Matrix",
"form.integration.readeck_activate": "Сохранять статьи в Readeck",
"form.integration.readeck_endpoint": "Конечная точка Readeck API",
"form.integration.readeck_api_key": "API-ключ Readeck",
"form.integration.readeck_labels": "Теги Readeck",
"form.integration.readeck_only_url": "Отправлять только ссылку (без содержимого)",
"form.integration.shiori_activate": "Сохранять статьи в Shiori", "form.integration.shiori_activate": "Сохранять статьи в Shiori",
"form.integration.shiori_endpoint": "Конечная точка Shiori API", "form.integration.shiori_endpoint": "Конечная точка Shiori API",
"form.integration.shiori_username": "Имя пользователя Shiori", "form.integration.shiori_username": "Имя пользователя Shiori",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Matrix kullanıcısı için şifre", "form.integration.matrix_bot_password": "Matrix kullanıcısı için şifre",
"form.integration.matrix_bot_url": "Matris sunucusu URL'si", "form.integration.matrix_bot_url": "Matris sunucusu URL'si",
"form.integration.matrix_bot_chat_id": "Matris odasının kimliği", "form.integration.matrix_bot_chat_id": "Matris odasının kimliği",
"form.integration.readeck_activate": "Makaleleri Readeck'e kaydet",
"form.integration.readeck_endpoint": "Readeck API Uç Noktası",
"form.integration.readeck_api_key": "Readeck API Anahtarı",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Yalnızca URL gönder (tam içerik yerine)",
"form.integration.shiori_activate": "Makaleleri Shiori'e kaydet", "form.integration.shiori_activate": "Makaleleri Shiori'e kaydet",
"form.integration.shiori_endpoint": "Shiori API Uç Noktası", "form.integration.shiori_endpoint": "Shiori API Uç Noktası",
"form.integration.shiori_username": "Shiori Kullanıcı Adı", "form.integration.shiori_username": "Shiori Kullanıcı Adı",

View File

@ -451,6 +451,11 @@
"form.integration.matrix_bot_password": "Пароль для користувача Matrix", "form.integration.matrix_bot_password": "Пароль для користувача Matrix",
"form.integration.matrix_bot_url": "URL-адреса сервера Матриці", "form.integration.matrix_bot_url": "URL-адреса сервера Матриці",
"form.integration.matrix_bot_chat_id": "Ідентифікатор кімнати Матриці", "form.integration.matrix_bot_chat_id": "Ідентифікатор кімнати Матриці",
"form.integration.readeck_activate": "Зберігати статті до Readeck",
"form.integration.readeck_endpoint": "Readeck API Endpoint",
"form.integration.readeck_api_key": "Ключ API Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Надіслати лише URL (замість повного вмісту)",
"form.integration.shiori_activate": "Save articles to Shiori", "form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",

View File

@ -445,6 +445,11 @@
"form.integration.matrix_bot_password": "Matrix Bot 密码", "form.integration.matrix_bot_password": "Matrix Bot 密码",
"form.integration.matrix_bot_url": "Matrix 服务器 URL", "form.integration.matrix_bot_url": "Matrix 服务器 URL",
"form.integration.matrix_bot_chat_id": "Matrix 聊天 ID", "form.integration.matrix_bot_chat_id": "Matrix 聊天 ID",
"form.integration.readeck_activate": "保存文章到 Readeck",
"form.integration.readeck_endpoint": "Readeck API 端点",
"form.integration.readeck_api_key": "Readeck API 密钥",
"form.integration.readeck_labels": "Readeck 默认标签",
"form.integration.readeck_only_url": "仅发送 URL而不是完整内容",
"form.integration.shiori_activate": "保存文章到 Shiori", "form.integration.shiori_activate": "保存文章到 Shiori",
"form.integration.shiori_endpoint": "Shiori API 端点", "form.integration.shiori_endpoint": "Shiori API 端点",
"form.integration.shiori_username": "Shiori 用户名", "form.integration.shiori_username": "Shiori 用户名",

View File

@ -447,6 +447,11 @@
"form.integration.matrix_bot_password": "Matrix 的密碼", "form.integration.matrix_bot_password": "Matrix 的密碼",
"form.integration.matrix_bot_url": "Matrix 伺服器的 URL", "form.integration.matrix_bot_url": "Matrix 伺服器的 URL",
"form.integration.matrix_bot_chat_id": "Matrix 房間 ID", "form.integration.matrix_bot_chat_id": "Matrix 房間 ID",
"form.integration.readeck_activate": "儲存文章到 Readeck",
"form.integration.readeck_endpoint": "Readeck API 端點",
"form.integration.readeck_api_key": "Readeck API 金鑰",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "仅发送 URL而不是完整内容",
"form.integration.shiori_activate": "儲存文章到 Shiori", "form.integration.shiori_activate": "儲存文章到 Shiori",
"form.integration.shiori_endpoint": "Shiori API 端點", "form.integration.shiori_endpoint": "Shiori API 端點",
"form.integration.shiori_username": "Shiori 使用者名稱", "form.integration.shiori_username": "Shiori 使用者名稱",

View File

@ -70,6 +70,11 @@ type Integration struct {
AppriseEnabled bool AppriseEnabled bool
AppriseURL string AppriseURL string
AppriseServicesURL string AppriseServicesURL string
ReadeckEnabled bool
ReadeckURL string
ReadeckAPIKey string
ReadeckLabels string
ReadeckOnlyURL bool
ShioriEnabled bool ShioriEnabled bool
ShioriURL string ShioriURL string
ShioriUsername string ShioriUsername string

View File

@ -174,6 +174,11 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
apprise_enabled, apprise_enabled,
apprise_url, apprise_url,
apprise_services_url, apprise_services_url,
readeck_enabled,
readeck_url,
readeck_api_key,
readeck_labels,
readeck_only_url,
shiori_enabled, shiori_enabled,
shiori_url, shiori_url,
shiori_username, shiori_username,
@ -261,6 +266,11 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
&integration.AppriseEnabled, &integration.AppriseEnabled,
&integration.AppriseURL, &integration.AppriseURL,
&integration.AppriseServicesURL, &integration.AppriseServicesURL,
&integration.ReadeckEnabled,
&integration.ReadeckURL,
&integration.ReadeckAPIKey,
&integration.ReadeckLabels,
&integration.ReadeckOnlyURL,
&integration.ShioriEnabled, &integration.ShioriEnabled,
&integration.ShioriURL, &integration.ShioriURL,
&integration.ShioriUsername, &integration.ShioriUsername,
@ -354,26 +364,31 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
apprise_enabled=$59, apprise_enabled=$59,
apprise_url=$60, apprise_url=$60,
apprise_services_url=$61, apprise_services_url=$61,
shiori_enabled=$62, readeck_enabled=$62,
shiori_url=$63, readeck_url=$63,
shiori_username=$64, readeck_api_key=$64,
shiori_password=$65, readeck_labels=$65,
shaarli_enabled=$66, readeck_only_url=$66,
shaarli_url=$67, shiori_enabled=$67,
shaarli_api_secret=$68, shiori_url=$68,
webhook_enabled=$69, shiori_username=$69,
webhook_url=$70, shiori_password=$70,
webhook_secret=$71, shaarli_enabled=$71,
rssbridge_enabled=$72, shaarli_url=$72,
rssbridge_url=$73, shaarli_api_secret=$73,
omnivore_enabled=$74, webhook_enabled=$74,
omnivore_api_key=$75, webhook_url=$75,
omnivore_url=$76, webhook_secret=$76,
linkwarden_enabled=$77, rssbridge_enabled=$77,
linkwarden_url=$78, rssbridge_url=$78,
linkwarden_api_key=$79 omnivore_enabled=$79,
omnivore_api_key=$80,
omnivore_url=$81,
linkwarden_enabled=$82,
linkwarden_url=$83,
linkwarden_api_key=$84
WHERE WHERE
user_id=$80 user_id=$85
` `
_, err := s.db.Exec( _, err := s.db.Exec(
query, query,
@ -438,6 +453,11 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
integration.AppriseEnabled, integration.AppriseEnabled,
integration.AppriseURL, integration.AppriseURL,
integration.AppriseServicesURL, integration.AppriseServicesURL,
integration.ReadeckEnabled,
integration.ReadeckURL,
integration.ReadeckAPIKey,
integration.ReadeckLabels,
integration.ReadeckOnlyURL,
integration.ShioriEnabled, integration.ShioriEnabled,
integration.ShioriURL, integration.ShioriURL,
integration.ShioriUsername, integration.ShioriUsername,
@ -490,6 +510,7 @@ func (s *Storage) HasSaveEntry(userID int64) (result bool) {
linkwarden_enabled='t' OR linkwarden_enabled='t' OR
apprise_enabled='t' OR apprise_enabled='t' OR
shiori_enabled='t' OR shiori_enabled='t' OR
readeck_enabled='t' OR
shaarli_enabled='t' OR shaarli_enabled='t' OR
webhook_enabled='t' OR webhook_enabled='t' OR
omnivore_enabled='t' omnivore_enabled='t'

View File

@ -363,6 +363,32 @@
</div> </div>
</details> </details>
<details {{ if .form.ReadeckEnabled }}open{{ end }}>
<summary>Readeck</summary>
<div class="form-section">
<label>
<input type="checkbox" name="readeck_enabled" value="1" {{ if .form.ReadeckEnabled }}checked{{ end }}> {{ t "form.integration.readeck_activate" }}
</label>
<label>
<input type="checkbox" name="readeck_only_url" value="1" {{ if .form.ReadeckOnlyURL }}checked{{ end }}> {{ t "form.integration.readeck_only_url" }}
</label>
<label for="form-readeck-url">{{ t "form.integration.readeck_endpoint" }}</label>
<input type="url" name="readeck_url" id="form-readeck-url" value="{{ .form.ReadeckURL }}" placeholder="https://readeck.com" spellcheck="false">
<label for="form-readeck-api-key">{{ t "form.integration.readeck_api_key" }}</label>
<input type="text" name="readeck_api_key" id="form-readeck-api-key" value="{{ .form.ReadeckAPIKey }}" spellcheck="false">
<label for="form-readeck-labels">{{ t "form.integration.readeck_labels" }}</label>
<input type="text" name="readeck_labels" id="form-readeck-labels" value="{{ .form.ReadeckLabels }}" 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>
</details>
<details {{ if .form.ShioriEnabled }}open{{ end }}> <details {{ if .form.ShioriEnabled }}open{{ end }}>
<summary>Shiori</summary> <summary>Shiori</summary>
<div class="form-section"> <div class="form-section">

View File

@ -76,6 +76,11 @@ type IntegrationForm struct {
AppriseEnabled bool AppriseEnabled bool
AppriseURL string AppriseURL string
AppriseServicesURL string AppriseServicesURL string
ReadeckEnabled bool
ReadeckURL string
ReadeckAPIKey string
ReadeckLabels string
ReadeckOnlyURL bool
ShioriEnabled bool ShioriEnabled bool
ShioriURL string ShioriURL string
ShioriUsername string ShioriUsername string
@ -157,6 +162,11 @@ func (i IntegrationForm) Merge(integration *model.Integration) {
integration.AppriseEnabled = i.AppriseEnabled integration.AppriseEnabled = i.AppriseEnabled
integration.AppriseServicesURL = i.AppriseServicesURL integration.AppriseServicesURL = i.AppriseServicesURL
integration.AppriseURL = i.AppriseURL integration.AppriseURL = i.AppriseURL
integration.ReadeckEnabled = i.ReadeckEnabled
integration.ReadeckURL = i.ReadeckURL
integration.ReadeckAPIKey = i.ReadeckAPIKey
integration.ReadeckLabels = i.ReadeckLabels
integration.ReadeckOnlyURL = i.ReadeckOnlyURL
integration.ShioriEnabled = i.ShioriEnabled integration.ShioriEnabled = i.ShioriEnabled
integration.ShioriURL = i.ShioriURL integration.ShioriURL = i.ShioriURL
integration.ShioriUsername = i.ShioriUsername integration.ShioriUsername = i.ShioriUsername
@ -240,6 +250,11 @@ func NewIntegrationForm(r *http.Request) *IntegrationForm {
AppriseEnabled: r.FormValue("apprise_enabled") == "1", AppriseEnabled: r.FormValue("apprise_enabled") == "1",
AppriseURL: r.FormValue("apprise_url"), AppriseURL: r.FormValue("apprise_url"),
AppriseServicesURL: r.FormValue("apprise_services_url"), AppriseServicesURL: r.FormValue("apprise_services_url"),
ReadeckEnabled: r.FormValue("readeck_enabled") == "1",
ReadeckURL: r.FormValue("readeck_url"),
ReadeckAPIKey: r.FormValue("readeck_api_key"),
ReadeckLabels: r.FormValue("readeck_labels"),
ReadeckOnlyURL: r.FormValue("readeck_only_url") == "1",
ShioriEnabled: r.FormValue("shiori_enabled") == "1", ShioriEnabled: r.FormValue("shiori_enabled") == "1",
ShioriURL: r.FormValue("shiori_url"), ShioriURL: r.FormValue("shiori_url"),
ShioriUsername: r.FormValue("shiori_username"), ShioriUsername: r.FormValue("shiori_username"),

View File

@ -90,6 +90,11 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) {
AppriseEnabled: integration.AppriseEnabled, AppriseEnabled: integration.AppriseEnabled,
AppriseURL: integration.AppriseURL, AppriseURL: integration.AppriseURL,
AppriseServicesURL: integration.AppriseServicesURL, AppriseServicesURL: integration.AppriseServicesURL,
ReadeckEnabled: integration.ReadeckEnabled,
ReadeckURL: integration.ReadeckURL,
ReadeckAPIKey: integration.ReadeckAPIKey,
ReadeckLabels: integration.ReadeckLabels,
ReadeckOnlyURL: integration.ReadeckOnlyURL,
ShioriEnabled: integration.ShioriEnabled, ShioriEnabled: integration.ShioriEnabled,
ShioriURL: integration.ShioriURL, ShioriURL: integration.ShioriURL,
ShioriUsername: integration.ShioriUsername, ShioriUsername: integration.ShioriUsername,