Zum Inhalt

Read it Later - Sende Artikel aus Newsboat direkt an Wallabag

Papierschiff mit roter Fahne fährt auf dem Wasser

Newsboat kann über die API Artikel direkt an Wallabag senden. Dort können die Beiträge bequem und übersichtlich verwaltet und mit den entsprechenden Apps auf allen Endgeräten gelesen werden.

In Newsboat werden wie gewohnt alle Feeds abgerufen und entsprechend den eigenen Vorlieben verwaltet. Bei der Speicherung als Bookmark 🔖 wird ein Shell Skript verwendet, dass die Weiterleitung der Artikel-URL an Wallabag übernimmt. Dabei werden die Zugangsdaten mit dem eigenen GPG-Schlüssel ver- und entschlüssel, welcher vorzugweise auf einem YubiKey oder Nitrokey hinterlegt ist und anschließend über OAuth2 an Wallabag gesendet wird.

Der Prozess sieht kurz zusammengefasst so aus:

Newsboat ----> & / Nitrokey ----> OAuth2 ----> Wallabag



Wie funktionierts?

  1. Newsboat ist der RSS Reader der die Beiträge abruft und auf der CLI darstellt. Es können für unterschiedliche Themengebiete mehrere Newsboat-Instanzen genutzt werden.
  2. In Newsboat werden Lesezeichen mit Strg + b gespeichert (👉 zum Beitrag )
  3. Lesezeichen werden fortlaufend in eine Markdown-Datei geschrieben.
  4. Lesezeichen werden fortlaufenden in eine txt-Datei geschrieben.
  5. Lesezeichen werden an Wallabag geschickt und dort als ungelesener Artikel mit Schlagwort angezeigt.

Das ganze hört sich simpel an, erfordert jedoch ein wenig Vorbereitung und Konfiguration, da eine Anbindung über OAuth2 erfolgt.


Was ist OAuth und was macht das?

OAuth21 stellt zwei unterschiedliche Tokens zur Verfügung

  • access_token, dieser erlaubt einen Zugriff auf Wallabag. Er hat jedoch ein Ablaufdatum von 3600 Sekunden, dann verliert er seine Gültigkeit. Eine regelmäßige Aktualisierung (= refresh) ist notwendig.
  • referesh_token, mit diesem Token kann man den access_token wieder aktivieren und einen Zugriff auf Wallabag erhalten.

Zusätzlich zu den Token werden

  • client-id
  • client-secret

benötigt.

Login Credentials und Tokens mit GnuPG schützen

Die Login Credentials und die Token sind wichtige Daten, die nicht ungeschützt abgespeichert werden sollten.

Deshalb werden diese Informationen in einder json-Datei abgespeichert und mit dem eigenen GPG-Schlüssel verschlüsselt.


Wallabag

Wallabag nutzt OAuth2 um Anwendungen, also dem Skript zur Lesezeichenspeicherung, eine sichere Möglichkeit der Anmeldung zu bieten.

Wallabag-Backup anlegen

Ein Backup im JASON-Format aller Inhalte sollte für den Notfall erstellt und heruntergeladen werden.


Client hinzufügen

Wallabag bietet die Möglichkeit über OAuth2 2 einen Token abzurufen. Mit diesem lässt sich die API steuern.

Damit man die API nutzen kann, ist ein Client anzulegen. Dadurch erhält man die beiden Credentials Client-ID und Client-Secret die für die nachfolgenden Befehle benötigt werden.

Client anlegen

Client anlegen

Client-ID und Client-Secret werden angezeigt

Token - Anfordern - 1. Versuch

Zum Anfordern des Tokens ist ein Befehl über ein Terminal an die Wallabag-API zu senden. Die folgenden Zeilen sind anzupassen.

  • wallabag.example.com = durch die eigene URL ersetzen
  • client_id = Client-ID
  • client_secret = Client-Secret
  • username = Wallabag-Benutzer
  • password = Passwort des Wallabag-Benutzers

In einem Terminal gibt man den angepassten Befehl ein. Auf meinem Apple-Rechner verwende ich dafür iTerm2.

Token anfordern 1
http POST https://wallabag.example.com/oauth/v2/token \
    grant_type=password \
    client_id=11_xxxxxxxxxxxxx \
    client_secret=lyyyyyyyyyyyy \
    username=wallabag \
    password=passwordpassword
zsh: command not found: http

Der Fehler zsh: command not found: http ist nicht wirklich aussagekräftig. Es besagt aber, dass zsh keine Verbindung zur eigenen Wallabag-Installation herstellen kann, um den Token abzurufen.

httpie installieren

Abhilfe schafft die Installation des Tools httpie, das mithilfe von Homebrew geholt werden kann:3

brew install httpie

Token - Anfordern - 2. Versuch

Token anfordern 2
http POST https://wallabag.example.com/oauth/v2/token \
    grant_type=password \
    client_id=11_xxxxxxxxxxxxx \
    client_secret=lyyyyyyyyyyyy \
    username=wallabag \
    password=passwordpassword

Die Verbindung zu Wallabag war erfolgreich. Neben einigen Metadaten werden auch die Tokens angezeigt.

Token
HTTP/1.1 200 OK
Cache-Control: no-store, private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: application/json
Date: Thu, 09 Oct 2025 19:01:29 GMT
Keep-Alive: timeout=20
Pragma: no-cache
Server: nginx
Transfer-Encoding: chunked
Vary: Accept-Encoding

{
    "access_token": "accesstoken-accesstoken-accesstoken-accesstoken-accesstoken-accesstoken",
    "expires_in": 3600,
    "refresh_token": "refresh-token-refresh-token-refresh-token-refresh-token-refresh-token",
    "scope": null,
    "token_type": "bearer"
}

Artikel an Wallabag senden mit cURL

Eine URL kann man damit direkt über die CLI mit cURL an Wallabag senden. Es wird dafür der access_token benötigt. Die Wallabag API-Dokumentation gibt Aufschluss über die verfügbaren Endpoints.

cURL
curl -X POST https://wallabag.example.com/api/entries.json \
  -H "Authorization: Bearer accesstoken-accesstoken-accesstoken-accesstoken-accesstoken-accesstoken" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://strobelstefan.de/blog/2025/04/13/the_newsboat_rss_feedreader_-_rss-reader_f%C3%BCr_die_cli.html?h=newsboat"}'

Möchte man mehr Informationen mitgeben, dann lässt sich der cURL-Befehl erweitern:

cURL
curl -X POST https://wallabag.example.com/api/entries.json \
  -H "Authorization: Bearer accesstoken-accesstoken-accesstoken-accesstoken-accesstoken-accesstoken" \
  -H "Content-Type: application/json" \
  -d '{
        "url": "https://strobelstefan.de/blog/2025/04/13/the_newsboat_rss_feedreader_-_rss-reader_f%C3%BCr_die_cli.html?h=newsboat",
        "tags": "RSS,Newsboat,Software",
        "starred": 1,
        "archive": 0
      }'

Der Artikel erscheint sofort in Wallabag.

Artikel in Wallabag


Artikel aus Newsboat an Wallabag

Das Senden des Artikels von der CLI an Wallabag funktioniert. Nun kann der Artikel direkt aus Newsboat heraus gesendet werden.

Dafür wird das Shell Script bookmark.sh aus dem Artikel 👉 The Newsboat RSS Feedreader -RSS-Reader für die CLI verwendet und ein wenig erweitert.

Die gekennzeichneten Zeilen sind anzupassen.

Artikel aus Newsboat an Wallabag
#!/usr/bin/env bash

url="$1"
title="$2"
description="$3"
feed_title="$4"
date_time=$(date "+%Y-%m-%d")  # Get only the current date

# Bookmark files
bookmarks_file_md="$HOME/newsboat-bookmarks/bookmarks-finance.md"

# bookmarks.txt - relevant if you wanna convert article to PDF file
bookmarks_file_txt="$HOME/newsboat-bookmarks/bookmarks-finance.txt"

# Check if the last entry in the file already has today's date
last_date=$(tail -n 1 $bookmarks_file_md | cut -d' ' -f2)

# If the last date in the file is different from today's date, add today's date as a header
if [[ "$last_date" != "$date_time" ]]; then
  echo "## Date: ${date_time}" >> $bookmarks_file_md
fi

# Add the feed details
echo "- ***${feed_title}***; [${title}](${url}); ${date_time}; 📝 ${description}" >> $bookmarks_file_md

# If the last date in the file is different from today's date, add today's date as a header
if [[ "$last_date" != "$date_time" ]]; then
  echo "Date: ${date_time}" >> $bookmarks_file_txt
fi

# Add the feed details
echo "- ${feed_title}; ${title}; ${url}; 📝 ${description}; ${date_time}" >> $bookmarks_file_txt

#url title description feed_title

# Send article from Newsboat to Wallabag

curl -X POST https://wallabag.example.com/api/entries.json \
  -H "Authorization: Bearer accesstoken-accesstoken-accesstoken-accesstoken-accesstoken-accesstoken" \
  -H "Content-Type: application/json" \
  -d '{
        "url": "'"${url}"'",
        "tags": "Newsboat",
        "starred": 1,
        "archive": 0
      }'

exit 0

Die Datei enthält den Access Token, der einen Zugriff auf Wallabag ermöglicht.

Das Problem 🚨, der access_token Token hat nur eine Gültigkeit von 3600 Sekunden. Danach muss es wieder mit dem Refresh Token erneuert werden.


wallabag_token.json.gpg

Benutzername, Passwort und die Tokens sind besonders sensibel und werden geschützt. Die Datei wallabag_token.json wird mit dem eigenen GPG Default Key verschlüsselt.

gpg --encrypt --default-recipient-self --output ~/.newsboat/wallabag_token.json.gpg

# oder mit der direkten Nennung des GPG-Schlüssel
gpg --encrypt --recipient <Schlüssel-ID> ~/.newsboat/wallabag_token.json

# oder mit der direkten Nennung der E-Mail-Adresse
gpg --encrypt --recipient mail@example.com ~/.newsboat/wallabag_token.json

Es wird die Datei wallabag_token.json.gpg erstellt und alle sensiblen Informationen sind ab sofort mit dem GPG-Schlüssel abgesperrt.

Die Verwendung eines Hardwaretoken, wie dem YubiKey oder Nitrokey, macht den ganzen Prozess der Ver- und Entschlüsselung sehr einfach und komfortable. Hier im Blog gib es umfangreiche Anleitungen zum Einrichten und Konfigurieren von YubiKeys und Nitrokeys.


GPG Default Key

Der Standardschlüssel wird in der Datei ~/.gnupg.gpg.conf in der Zeile default-key global festgelegt.

Die Abfrage des Standardschlüssels erfolgt mit.

echo "test" | gpg --sign --verbose

Im Skript wird der Schlüssel expliziet angegeben. Soll immer der globale Standardschlüssel verwendet werden, dann sind die enstprechenden Zeilen aus dem Code zu entfernen.


Gib mir gerne einen Kaffee ☕ aus 😀

Gib mir gerne einen Kaffee ☕ aus !

Wenn dir meine Beiträge gefallen und geholfen haben, dann kannst du mir gerne einen Kaffee ☕️ ausgeben.

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address:

bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj


Das fertige Skript

Es werden zwei Skripte angelegt:

  • bookmark.sh, das bereits verwendet wird. Im Skript sind ein paar kleine Anpassungen vorzunehmen. ( 👉 The Newsboat RSS Feedreader - RSS-Reader für die CLI)
  • wallabag_token.json.gpg = verschlüsselte Datei mit client_id, client_secret, access_token und referesh_token.

Alle Dateien werden direkt im Verzeichnis ~/.newsboat/ abgespeichert.

In der Newsboat-Konfigurationsdatei ist die Zeile bookmark-cmd einzufügen. Der Pfad muss auf die Datei bookmark.sh verweisen.

Newsboat - Konfigurationsdatei
bookmark-cmd "$HOME/.newsboat/bookmark.sh"

bookmark.sh

#!/usr/bin/env bash

# Created by https://www.strobelstefan.de
# Hosted on https://codeberg.org/sst/

# If you like this script, feel free to buy me a coffee.
# PayPal https://www.paypal.com/donate?hosted_button_id=BNV5XKAAXK6TJ
# Bitcoin - bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj


#####################
# Newsboat variables
#####################
# The values for the variables get populated directly from Newsboat.
# Therfor it is necessary to call this script from the Newsboat config file.
url="$1"
title="$2"
description="$3"
feed_title="$4"
date_time=$(date "+%Y-%m-%d")  # Get only the current date


#####################
# Bookmarks variables
#####################

# Bookmark Markdown file
bookmarks_file_md="$HOME/newsboat-bookmarks/bookmarks.md"

# Bookmark TXT file - relevnat if you wanna convert article to PDF file
bookmarks_file_txt="$HOME/newsboat-bookmarks/bookmarks.txt"


#####################
# Wallabag variables
#####################
# Wallabag URL
wallabag_url="https://wallabag.example.com"

# Standard tag for Wallabag
tags="it,tag2,tag3"


#####################
# GPG variables
#####################
# Your GPG key ID (replace with your actual key ID)
gpg_key_id="<Schlüssel-ID>"

# Encrypt your file
# gpg --encrypt --default-recipient-self --output ~/.newsboat/wallabag_token.json
# gpg --encrypt --recipient mail@example ~/.newsboat/wallabag_token.json
# gpg --encrypt --recipient key-id ~/.newsboat/wallabag_token.json

# Encrypted file containing the tokens (path to your encrypted token file)
encrypted_file="$HOME/.newsboat/wallabag_token.json.gpg"

#Log File
response_body_txt="$HOME/.newsboat/response_body.txt"


###################################
# Newsboat Bookmarking
###################################
# See Newsboat documentation for more details
# https://newsboat.org/releases/2.41/docs/newsboat.html#_bookmarking

# Check if the last entry in the file already has today's date
last_date=$(tail -n 1 $bookmarks_file_md | cut -d' ' -f2)

# Markdown file
# If the last date in the file is different from today's date, add today's date as a header
if [[ "$last_date" != "$date_time" ]]; then
  echo "## Date: ${date_time}" >> $bookmarks_file_md
fi

# Add feed details
echo "- ***${feed_title}***; [${title}](${url}); ${date_time}; 📝 ${description}" >> $bookmarks_file_md


# TXT file
# If the last date in the file is different from today's date, add today's date as a header
if [[ "$last_date" != "$date_time" ]]; then
  echo "Date: ${date_time}" >> $bookmarks_file_txt
fi

# Add the feed details
echo "- ${feed_title}; ${title}; ${url}; 📝 ${description}; ${date_time}" >> $bookmarks_file_txt



###################################
# Send article from Newsboat to Wallabag
###################################

set -euo pipefail

#####################
# Variables
#####################
backup_file="wallabag_token.gpg.bak"
response_body_txt=$(mktemp)

# Backup original encrypted token
cp "$encrypted_file" "$backup_file"
echo "Backup created: $backup_file"

# Decrypt token file
decrypted_data=$(gpg --decrypt --recipient "$gpg_key_id" "$encrypted_file" 2>/dev/null)


if [ -z "$decrypted_data" ]; then
  echo "Error: Failed to decrypt the token file."
  exit 1
fi

# Extract tokens and credentials
access_token=$(echo "$decrypted_data" | jq -r .access_token)
refresh_token=$(echo "$decrypted_data" | jq -r .refresh_token)
client_id=$(echo "$decrypted_data" | jq -r .client_id)
client_secret=$(echo "$decrypted_data" | jq -r .client_secret)
wallabag_username=$(echo "$decrypted_data" | jq -r .username)
wallabag_password=$(echo "$decrypted_data" | jq -r .password)

# Check access token validity
echo "Checking access token..."
response=$(curl -s -w "%{http_code}" -o "$response_body_txt" \
  -H "Authorization: Bearer $access_token" "$wallabag_url/api/user")
http_status="${response: -3}"

echo "DEBUG: HTTP status $http_status"

if [ "$http_status" -eq 401 ]; then
  echo "Access token invalid, attempting to refresh..."

  # Refresh token
  response=$(curl -s -X POST "$wallabag_url/oauth/v2/token" \
    -d "grant_type=refresh_token" \
    -d "client_id=$client_id" \
    -d "client_secret=$client_secret" \
    -d "refresh_token=$refresh_token")

  echo "DEBUG: Refresh response: $response"

  # If refresh worked
  if echo "$response" | jq -e '.access_token' >/dev/null 2>&1 && \
     [ "$(echo "$response" | jq -r .access_token)" != "null" ]; then

    access_token=$(echo "$response" | jq -r .access_token)
    refresh_token=$(echo "$response" | jq -r .refresh_token)

    echo "Token refreshed successfully."

  else
    # Fallback: full reauthentication
    echo "Refresh token invalid. Performing full reauthentication..."

    response=$(curl -s -X POST "$wallabag_url/oauth/v2/token" \
      -d "grant_type=password" \
      -d "client_id=$client_id" \
      -d "client_secret=$client_secret" \
      -d "username=$wallabag_username" \
      -d "password=$wallabag_password")

    echo "DEBUG: Reauth response: $response"

    if echo "$response" | jq -e '.access_token' >/dev/null 2>&1 && \
       [ "$(echo "$response" | jq -r .access_token)" != "null" ]; then
      access_token=$(echo "$response" | jq -r .access_token)
      refresh_token=$(echo "$response" | jq -r .refresh_token)
      echo "Reauthentication successful."
    else
      echo "Error: Failed to reauthenticate. Response:"
      echo "$response"
      exit 1
    fi
  fi

  # Update and re-encrypt token file
  updated_data=$(echo "$decrypted_data" | jq \
    --arg access_token "$access_token" \
    --arg refresh_token "$refresh_token" \
    '.access_token = $access_token | .refresh_token = $refresh_token')

  echo "$updated_data" | gpg --batch --yes --encrypt --recipient "$gpg_key_id" > "$encrypted_file"
  echo "Encrypted file updated with new tokens."
fi

# Send article to Wallabag
echo "Sending article to Wallabag..."
curl -s -X POST "$wallabag_url/api/entries.json" \
  -H "Authorization: Bearer $access_token" \
  -H "Content-Type: application/json" \
  -d '{
        "url": "'"${url}"'",
        "tags": "'"${tags}"'",
        "starred": 1,
        "archive": 0
      }' >/dev/null

# Cleanup
trap 'rm -f "$response_body_txt"' EXIT

echo "Article sent successfully."

exit 0

wallabag_token.json

Die Zugangsdaten werden in der Datei wallabag_token.json gespeichert. Es kann dafür einfach der Block aus Token - Anfordern - 2. Versuch in die Datei kopiert und angepasst werden.

~/.newsboat/wallabag_token.json

Dort den Abschnitt einfügen.

{
  "client_id": "...",
  "client_secret": "...",
  "username": "...",
  "password": "...",
  "access_token": "...",
  "refresh_token": "..."
}

Mehrere Newsboat Instanzen

Es ist möglich mehrere Newsboat für unterschiedliche Themen zu nutzen. Dabei ist darauf zu achten, dass

entweder

alle Instanzen die gleiche wallabag_token.json.gpg verwenden

oder

für jede Instanz ein eigener Wallabag Client (eigenes Client-ID und eigene Client-Secret) mit eigener wallabag_token.json.gpg angelegt wird.

Beispiel

In diesem Beispiel werden zwei Newsboat-Instanzen verwendet.

  • ~/.newsboat = 1. Instanz
  • ~/.newsboat-music/newsboat/ = 2. Instanz

Die Dateistruktur je Instanz

1. Newsboat-Instanz

~/.newsboat/
|
|- bookmark.sh
|
|- config
|
|- styles.css
|
|- urls
|
|- wallabag_token.gpg.bak
|
|- wallabag_token.json.gpg

2. Newsboat-Instanz

~/.newsboat-music/newsboat/
|
|- bookmark.sh
|
|- config
|
|- styles.css
|
|- urls

Beide Instanzen teilen sich die Datei wallabag_token.json.gpg.

Jede Instanz hat eine eigene bookmark.sh. Es sind in der Datei lediglich die folgenden Zeilen angepasst worden:

...

# Bookmark Markdown file
bookmarks_file_md="$HOME/newsboat-bookmarks/xxx.md"

# Bookmark TXT file - relevnat if you wanna convert article to PDF file
bookmarks_file_txt="$HOME/newsboat-bookmarks/xxx.txt"

...

# Standard tag for Wallabag
tags="xxx"

...

Verbesserungen und Optimierungen

Das Skript ist auf Codeberg.org verfügbar. Jeder ist herzlich eingeladen, zur Weiterentwicklung beizutragen. Falls du Verbesserungsvorschläge hast oder das Skript optimieren möchtest, kannst du gerne Pull Requests einreichen oder offene Issues melden. Dein Feedback ist wertvoll und trägt dazu bei, das Projekt kontinuierlich zu verbessern.

Gib mir gerne einen Kaffee ☕ aus 😀

Gib mir gerne einen Kaffee ☕ aus !

Wenn dir meine Beiträge gefallen und geholfen haben, dann kannst du mir gerne einen Kaffee ☕️ ausgeben.

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address:

bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj

Source
  • Photo by Artak Petrosyan on Unsplash