Skip to content

Commit

Permalink
Add Shiori integration
Browse files Browse the repository at this point in the history
  • Loading branch information
fguillot committed Aug 13, 2023
1 parent 13d9d86 commit 28df0b1
Show file tree
Hide file tree
Showing 26 changed files with 312 additions and 13 deletions.
26 changes: 18 additions & 8 deletions internal/database/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,26 +709,36 @@ var migrations = []func(tx *sql.Tx) error{
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN notion_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN notion_token text default '';
ALTER TABLE integrations ADD COLUMN notion_page_id text default '';
ALTER TABLE integrations ADD COLUMN notion_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN notion_token text default '';
ALTER TABLE integrations ADD COLUMN notion_page_id text default '';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN readwise_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN readwise_api_key text default '';
ALTER TABLE integrations ADD COLUMN readwise_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN readwise_api_key text default '';
`
_, 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 '';
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
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN shiori_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN shiori_url text default '';
ALTER TABLE integrations ADD COLUMN shiori_username text default '';
ALTER TABLE integrations ADD COLUMN shiori_password text default '';
`
_, err = tx.Exec(sql)
return err
Expand Down
15 changes: 15 additions & 0 deletions internal/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"miniflux.app/v2/internal/integration/pinboard"
"miniflux.app/v2/internal/integration/pocket"
"miniflux.app/v2/internal/integration/readwise"
"miniflux.app/v2/internal/integration/shiori"
"miniflux.app/v2/internal/integration/telegrambot"
"miniflux.app/v2/internal/integration/wallabag"
"miniflux.app/v2/internal/logger"
Expand Down Expand Up @@ -137,6 +138,20 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
logger.Error("[Integration] UserID #%d: %v", integration.UserID, err)
}
}

if integration.ShioriEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Shiori", entry.ID, entry.URL, integration.UserID)

client := shiori.NewClient(
integration.ShioriURL,
integration.ShioriUsername,
integration.ShioriPassword,
)

if err := client.AddBookmark(entry.URL, entry.Title); err != nil {
logger.Error("[Integration] Unable to send entry #%d to Shiori for user #%d: %v", entry.ID, integration.UserID, err)
}
}
}

// PushEntries pushes an entry array to third-party providers during feed refreshes.
Expand Down
132 changes: 132 additions & 0 deletions internal/integration/shiori/shiori.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package shiori // import "miniflux.app/v2/internal/integration/shiori"

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"

"miniflux.app/v2/internal/url"
"miniflux.app/v2/internal/version"
)

const defaultClientTimeout = 10 * time.Second

type Client struct {
baseURL string
username string
password string
}

func NewClient(baseURL, username, password string) *Client {
return &Client{baseURL: baseURL, username: username, password: password}
}

func (c *Client) AddBookmark(entryURL, entryTitle string) error {
if c.baseURL == "" || c.username == "" || c.password == "" {
return fmt.Errorf("shiori: missing base URL, username or password")
}

sessionID, err := c.authenticate()
if err != nil {
return fmt.Errorf("shiori: unable to authenticate: %v", err)
}

apiEndpoint, err := url.JoinBaseURLAndPath(c.baseURL, "/api/bookmarks")
if err != nil {
return fmt.Errorf("shiori: invalid API endpoint: %v", err)
}

requestBody, err := json.Marshal(&addBookmarkRequest{
URL: entryURL,
Title: entryTitle,
CreateArchive: true,
})

if err != nil {
return fmt.Errorf("shiori: unable to encode request body: %v", err)
}

request, err := http.NewRequest("POST", apiEndpoint, bytes.NewReader(requestBody))
if err != nil {
return fmt.Errorf("shiori: unable to create request: %v", err)
}

request.Header.Set("Content-Type", "application/json")
request.Header.Set("Accept", "application/json")
request.Header.Set("User-Agent", "Miniflux/"+version.Version)
request.Header.Set("X-Session-Id", sessionID)

httpClient := &http.Client{Timeout: defaultClientTimeout}

response, err := httpClient.Do(request)
if err != nil {
return fmt.Errorf("shiori: unable to send request: %v", err)
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
return fmt.Errorf("shiori: unable to create bookmark: url=%s status=%d", apiEndpoint, response.StatusCode)
}

return nil
}

func (c *Client) authenticate() (sessionID string, err error) {
apiEndpoint, err := url.JoinBaseURLAndPath(c.baseURL, "/api/login")
if err != nil {
return "", fmt.Errorf("shiori: invalid API endpoint: %v", err)
}

requestBody, err := json.Marshal(&authRequest{Username: c.username, Password: c.password})
if err != nil {
return "", fmt.Errorf("shiori: unable to encode request body: %v", err)
}

request, err := http.NewRequest("POST", apiEndpoint, bytes.NewReader(requestBody))
if err != nil {
return "", fmt.Errorf("shiori: unable to create request: %v", err)
}

request.Header.Set("Content-Type", "application/json")
request.Header.Set("Accept", "application/json")
request.Header.Set("User-Agent", "Miniflux/"+version.Version)

httpClient := &http.Client{Timeout: defaultClientTimeout}

response, err := httpClient.Do(request)
if err != nil {
return "", fmt.Errorf("shiori: unable to send request: %v", err)
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
return "", fmt.Errorf("shiori: unable to authenticate: url=%s status=%d", apiEndpoint, response.StatusCode)
}

var authResponse authResponse
if err := json.NewDecoder(response.Body).Decode(&authResponse); err != nil {
return "", fmt.Errorf("shiori: unable to decode response: %v", err)
}

return authResponse.SessionID, nil
}

type authRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}

type authResponse struct {
SessionID string `json:"session"`
}

type addBookmarkRequest struct {
URL string `json:"url"`
Title string `json:"title"`
CreateArchive bool `json:"createArchive"`
}
4 changes: 4 additions & 0 deletions internal/locale/translations/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"form.integration.matrix_bot_password": "Passwort für Matrix-Benutzer",
"form.integration.matrix_bot_url": "URL des Matrix-Servers",
"form.integration.matrix_bot_chat_id": "ID des Matrix-Raums",
"form.integration.shiori_activate": "Artikel in Shiori",
"form.integration.shiori_endpoint": "Shiori API-Endpunkt",
"form.integration.shiori_username": "Shiori Benutzername",
"form.integration.shiori_password": "Shiori Passwort",
"form.api_key.label.description": "API-Schlüsselbezeichnung",
"form.submit.loading": "Lade...",
"form.submit.saving": "Speichern...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/el_EL.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"form.integration.matrix_bot_password": "Κωδικός πρόσβασης για τον χρήστη Matrix",
"form.integration.matrix_bot_url": "URL διακομιστή Matrix",
"form.integration.matrix_bot_chat_id": "Αναγνωριστικό της αίθουσας Matrix",
"form.integration.shiori_activate": "Αποθήκευση άρθρων στο Shiori",
"form.integration.shiori_endpoint": "Τελικό σημείο Shiori",
"form.integration.shiori_username": "Όνομα Χρήστη Shiori",
"form.integration.shiori_password": "Κωδικός Πρόσβασης Shiori",
"form.api_key.label.description": "Ετικέτα κλειδιού API",
"form.submit.loading": "Φόρτωση...",
"form.submit.saving": "Αποθήκευση...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"form.integration.matrix_bot_password": "Password for Matrix user",
"form.integration.matrix_bot_url": "Matrix server URL",
"form.integration.matrix_bot_chat_id": "ID of Matrix Room",
"form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password",
"form.api_key.label.description": "API Key Label",
"form.submit.loading": "Loading…",
"form.submit.saving": "Saving…",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/es_ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"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_chat_id": "ID de la sala de Matrix",
"form.integration.shiori_activate": "Guardar artículos a Shiori",
"form.integration.shiori_endpoint": "Extremo de API de Shiori",
"form.integration.shiori_username": "Nombre de usuario de Shiori",
"form.integration.shiori_password": "Contraseña de Shiori",
"form.api_key.label.description": "Etiqueta de clave API",
"form.submit.loading": "Cargando...",
"form.submit.saving": "Guardando...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/fi_FI.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"form.integration.matrix_bot_password": "Matrix-käyttäjän salasana",
"form.integration.matrix_bot_url": "Matrix-palvelimen URL-osoite",
"form.integration.matrix_bot_chat_id": "Matrix-huoneen tunnus",
"form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password",
"form.api_key.label.description": "API Key Label",
"form.submit.loading": "Ladataan...",
"form.submit.saving": "Tallennetaan...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"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_chat_id": "Identifiant de la salle Matrix",
"form.integration.shiori_activate": "Sauvegarder les articles vers Shiori",
"form.integration.shiori_endpoint": "URL de l'API de Shiori",
"form.integration.shiori_username": "Nom d'utilisateur de Shiori",
"form.integration.shiori_password": "Mot de passe de Shiori",
"form.api_key.label.description": "Libellé de la clé d'API",
"form.submit.loading": "Chargement...",
"form.submit.saving": "Sauvegarde en cours...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/hi_IN.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"form.integration.matrix_bot_password": "मैट्रिक्स उपयोगकर्ता के लिए पासवर्ड",
"form.integration.matrix_bot_url": "मैट्रिक्स सर्वर URL",
"form.integration.matrix_bot_chat_id": "मैट्रिक्स रूम की आईडी",
"form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password",
"form.api_key.label.description": "एपीआई कुंजी लेबल",
"form.submit.loading": "लोड हो रहा है...",
"form.submit.saving": "सहेजा जा रहा है...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/id_ID.json
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,10 @@
"form.integration.matrix_bot_password": "Kata Sandi Matrix",
"form.integration.matrix_bot_url": "URL Peladen Matrix",
"form.integration.matrix_bot_chat_id": "ID Ruang Matrix",
"form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password",
"form.api_key.label.description": "Label Kunci API",
"form.submit.loading": "Memuat...",
"form.submit.saving": "Menyimpan...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/it_IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"form.integration.matrix_bot_password": "Password per l'utente Matrix",
"form.integration.matrix_bot_url": "URL del server Matrix",
"form.integration.matrix_bot_chat_id": "ID della stanza Matrix",
"form.integration.shiori_activate": "Salva gli articoli su Shiori",
"form.integration.shiori_endpoint": "Endpoint dell'API di Shiori",
"form.integration.shiori_username": "Nome utente dell'account Shiori",
"form.integration.shiori_password": "Password dell'account Shiori",
"form.api_key.label.description": "Etichetta chiave API",
"form.submit.loading": "Caricamento in corso...",
"form.submit.saving": "Salvataggio in corso...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/ja_JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"form.integration.matrix_bot_password": "Matrixユーザ用パスワード",
"form.integration.matrix_bot_url": "MatrixサーバーのURL",
"form.integration.matrix_bot_chat_id": "MatrixルームのID",
"form.integration.shiori_activate": "Shiori に記事を保存する",
"form.integration.shiori_endpoint": "Shiori の API Endpoint",
"form.integration.shiori_username": "Shiori の ユーザー名",
"form.integration.shiori_password": "Shiori の パスワード",
"form.api_key.label.description": "API キーラベル",
"form.submit.loading": "読み込み中…",
"form.submit.saving": "保存中…",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/nl_NL.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"form.integration.matrix_bot_password": "Wachtwoord voor Matrix-gebruiker",
"form.integration.matrix_bot_url": "URL van de Matrix-server",
"form.integration.matrix_bot_chat_id": "ID van Matrix-kamer",
"form.integration.shiori_activate": "Opslaan naar Shiori",
"form.integration.shiori_endpoint": "Shiori URL",
"form.integration.shiori_username": "Shiori gebruikersnaam",
"form.integration.shiori_password": "Shiori wachtwoord",
"form.api_key.label.description": "API-sleutellabel",
"form.submit.loading": "Laden...",
"form.submit.saving": "Opslaag...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/pl_PL.json
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@
"form.integration.matrix_bot_password": "Hasło dla użytkownika Matrix",
"form.integration.matrix_bot_url": "URL serwera Matrix",
"form.integration.matrix_bot_chat_id": "Identyfikator pokoju Matrix",
"form.integration.shiori_activate": "Zapisz artykuły do Shiori",
"form.integration.shiori_endpoint": "Shiori URL",
"form.integration.shiori_username": "Login do Shiori",
"form.integration.shiori_password": "Hasło do Shiori",
"form.api_key.label.description": "Etykieta klucza API",
"form.submit.loading": "Ładowanie...",
"form.submit.saving": "Zapisywanie...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/pt_BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"form.integration.matrix_bot_password": "Palavra-passe para utilizador da Matrix",
"form.integration.matrix_bot_url": "URL do servidor Matrix",
"form.integration.matrix_bot_chat_id": "Identificação da sala Matrix",
"form.integration.shiori_activate": "Salvar itens no Shiori",
"form.integration.shiori_endpoint": "Endpoint da API do Shiori",
"form.integration.shiori_username": "Nome de usuário do Shiori",
"form.integration.shiori_password": "Senha do Shiori",
"form.api_key.label.description": "Etiqueta da chave de API",
"form.submit.loading": "Carregando...",
"form.submit.saving": "Salvando...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/ru_RU.json
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@
"form.integration.matrix_bot_password": "Пароль пользователя Matrix",
"form.integration.matrix_bot_url": "Ссылка на сервер Matrix",
"form.integration.matrix_bot_chat_id": "ID комнаты Matrix",
"form.integration.shiori_activate": "Сохранять статьи в Shiori",
"form.integration.shiori_endpoint": "Конечная точка Shiori API",
"form.integration.shiori_username": "Имя пользователя Shiori",
"form.integration.shiori_password": "Пароль Shiori",
"form.api_key.label.description": "Описание API-ключа",
"form.submit.loading": "Загрузка…",
"form.submit.saving": "Сохранение…",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/tr_TR.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@
"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_chat_id": "Matris odasının kimliği",
"form.integration.shiori_activate": "Makaleleri Shiori'e kaydet",
"form.integration.shiori_endpoint": "Shiori API Uç Noktası",
"form.integration.shiori_username": "Shiori Kullanıcı Adı",
"form.integration.shiori_password": "Shiori Parolası",
"form.api_key.label.description": "API Anahtar Etiketi",
"form.submit.loading": "Yükleniyor...",
"form.submit.saving": "Kaydediliyor...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/uk_UA.json
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@
"form.integration.matrix_bot_password": "Пароль для користувача Matrix",
"form.integration.matrix_bot_url": "URL-адреса сервера Матриці",
"form.integration.matrix_bot_chat_id": "Ідентифікатор кімнати Матриці",
"form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password",
"form.api_key.label.description": "Назва ключа API",
"form.submit.loading": "Завантаження...",
"form.submit.saving": "Зберігаю...",
Expand Down
4 changes: 4 additions & 0 deletions internal/locale/translations/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,10 @@
"form.integration.matrix_bot_password": "矩阵用户密码",
"form.integration.matrix_bot_url": "矩阵服务器 URL",
"form.integration.matrix_bot_chat_id": "Matrix房间ID",
"form.integration.shiori_activate": "保存文章到 Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori 用户名",
"form.integration.shiori_password": "Shiori 密码",
"form.api_key.label.description": "API密钥标签",
"form.submit.loading": "载入中…",
"form.submit.saving": "保存中…",
Expand Down
Loading

0 comments on commit 28df0b1

Please sign in to comment.