ntfy2vikunja ist eine kleine, robuste Bridge, die Nachrichten aus einem ntfy-Topic per WebSocket empfängt und als Tasks in Nextcloud Deck oder Vikunja anlegt. Die Verarbeitung läuft über eine persistente SQLite-Queue mit Retry/Backoff, optionalem Feedback-Post an ntfy und systemd/Forgejo-Deploy für den stabilen Linux-Betrieb. https://tasks.home.schumbi.de
Find a file
Ralf Warmuth d5c4e75dad
All checks were successful
Deploy ntfy2vikunja / test (push) Successful in 14s
Deploy ntfy2vikunja / deploy (push) Successful in 7s
README: Vikunja als obsolet markieren, Deck als einziges Backend
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-23 11:50:17 +01:00
.forgejo/workflows Backend wählbar: Deck (Standard) oder Vikunja 2026-02-23 09:59:14 +01:00
deploy chore: Sudoers um restart und enable --now (beide) ergaenzt 2026-02-13 23:09:21 +01:00
tests Backend wählbar: Deck (Standard) oder Vikunja 2026-02-23 09:59:14 +01:00
.env.example DUE_TIME konfigurierbar, Default 10:00 UTC 2026-02-23 10:22:04 +01:00
.gitignore Add unit tests, CI test job, and security scans (pip-audit, bandit) 2026-02-13 11:02:21 +01:00
bridge_ingestor.py Harden ntfy message mapping for Android client behavior. 2026-02-11 00:10:52 +01:00
bridge_run.py Initial commit for ntfy-to-Vikunja bridge. 2026-02-10 18:10:23 +01:00
bridge_worker.py Backend wählbar: Deck (Standard) oder Vikunja 2026-02-23 09:59:14 +01:00
create_task.py DUE_TIME konfigurierbar, Default 10:00 UTC 2026-02-23 10:22:04 +01:00
deck_client.py DUE_TIME konfigurierbar, Default 10:00 UTC 2026-02-23 10:22:04 +01:00
DEPLOY.md fix: Cache-Normalisierung, is_bang bei expliziter Prio, Bucket nur ohne due: 2026-02-13 10:45:02 +01:00
due_time.py DUE_TIME konfigurierbar, Default 10:00 UTC 2026-02-23 10:22:04 +01:00
healthcheck.py Improve bridge reliability with richer message parsing and runtime checks. 2026-02-10 23:16:48 +01:00
ntfy_subscribe.py Initial commit for ntfy-to-Vikunja bridge. 2026-02-10 18:10:23 +01:00
OPERATIONS.md fix: Cache-Normalisierung, is_bang bei expliziter Prio, Bucket nur ohne due: 2026-02-13 10:45:02 +01:00
pyproject.toml Add unit tests, CI test job, and security scans (pip-audit, bandit) 2026-02-13 11:02:21 +01:00
queue_store.py Initial commit for ntfy-to-Vikunja bridge. 2026-02-10 18:10:23 +01:00
README.md README: Vikunja als obsolet markieren, Deck als einziges Backend 2026-02-23 11:50:17 +01:00
RELEASE_NOTES.md Backend wählbar: Deck (Standard) oder Vikunja 2026-02-23 09:59:14 +01:00
requirements-dev.txt Pin urllib3 and wheel to fix pip-audit CVEs 2026-02-13 11:26:46 +01:00
requirements.txt Initial commit for ntfy-to-Vikunja bridge. 2026-02-10 18:10:23 +01:00
vikunja_lookup.py fix: VIKUNJA_BUCKET_HIGH_PRIO konfiguriert -> Fehler bei fehlgeschlagenem Lookup 2026-02-13 11:48:55 +01:00

ntfy2vikunja

Vikunja ist obsolet. Das Projekt wurde eingestellt; es wird nur noch Nextcloud Deck als Backend unterstützt. Die Vikunja-Option bleibt aus Abwärtskompatibilität im Code, wird aber nicht mehr gepflegt.

Bridge von ntfy nach Nextcloud Deck (früher auch Vikunja):

  • Nachrichten werden per WebSocket von NTFY_TOPIC gelesen
  • in einer lokalen SQLite-Queue zwischengespeichert
  • vom Worker zuverlässig als Tasks (Vikunja) oder Karten (Deck) angelegt

Verwende BACKEND=deck (Standard). BACKEND=vikunja ist obsolet und wird nicht mehr gepflegt.

Features

  • WebSocket-Subscriber (ntfy)
  • Persistente Queue (SQLite)
  • Retry mit Exponential Backoff
  • Dead-letter Zustand (dead) nach zu vielen Fehlversuchen
  • Entkoppelte Architektur (Ingestor/Worker) oder kombinierter Runner

Tests

Vor Aenderungen oder neuen Features: Tests ausfuehren, damit bestehende Funktionalitaet erhalten bleibt.

pip install -r requirements-dev.txt
pytest tests/ -v --cov=queue_store --cov=bridge_worker --cov=bridge_ingestor --cov=vikunja_lookup --cov=create_task --cov=deck_client

Die CI laeuft Tests vor jedem Deploy; der Deploy wird blockiert, wenn Tests fehlschlagen.

Security-Checks: pip-audit und bandit -r . -x ./tests

Projektstruktur

  • ntfy_subscribe.py WebSocket-Client für ntfy
  • create_task.py Vikunja Task-Erstellung (bei BACKEND=vikunja)
  • vikunja_lookup.py Projekt- und Bucket-Namen in IDs auflösen
  • deck_client.py Nextcloud Deck API (Karten anlegen, Board/Stack auflösen; bei BACKEND=deck)
  • queue_store.py SQLite Queue-Layer
  • bridge_ingestor.py ntfy -> Queue
  • bridge_worker.py Queue -> Vikunja oder Deck (je nach BACKEND)
  • bridge_run.py Ingestor + Worker in einem Prozess (lokal/dev)
  • healthcheck.py Queue-Status als JSON (Monitoring)
  • deploy/systemd/*.service Linux Services
  • .forgejo/workflows/deploy.yml Forgejo CI/CD Deploy Workflow

Voraussetzungen

  • Python 3.11+ (3.10 sollte ebenfalls funktionieren)
  • Abhängigkeiten aus requirements.txt

Installation:

pip install -r requirements.txt

Konfiguration

Als Vorlage:

cp .env.example .env

Relevante ENV-Variablen:

  • BACKEND deck (Standard; Vikunja ist obsolet)
  • NTFY_BASE_URL, NTFY_TOPIC, NTFY_TOKEN (oder NTFY_USER/NTFY_PASSWORD)
  • NTFY_FEEDBACK_TOPIC (optional: sendet "Task erstellt" als ntfy-Message)
  • NTFY_FEEDBACK_BASE_URL / NTFY_FEEDBACK_TOKEN (optional)
  • DUE_TIME (optional, Standard 10:00) Tageszeit für Fälligkeitsdatum, wenn nur Datum angegeben wird (HH:MM oder HH:MM:SS, UTC)
  • QUEUE_DB_PATH (z. B. ./state/queue.db oder /opt/ntfy2vikunja/state/queue.db)

Bei BACKEND=deck (Standard):

  • NEXTCLOUD_URL (z. B. https://nextcloud.example.com)
  • NEXTCLOUD_USER, NEXTCLOUD_PASSWORD (oder App-Passwort)
  • DECK_BOARD Board-ID oder Name
  • DECK_STACK Stack-ID oder Name (Spalte, z. B. ToDo)
  • DECK_STACK_HIGH_PRIO (optional: Stack fuer Nachrichten mit fuehrendem ! ohne due:)

Bei BACKEND=vikunja (obsolet, nicht mehr gepflegt):

  • VIKUNJA_URL, VIKUNJA_TOKEN, VIKUNJA_PROJECT, VIKUNJA_BUCKET_HIGH_PRIO

Lokal starten

Option A: Alles in einem Prozess

python bridge_run.py

Option B: Getrennt starten

Terminal 1:

python bridge_ingestor.py

Terminal 2:

python bridge_worker.py

Task-Mapping (aktuell)

  • title: erste Zeile von message, sonst ntfy title
  • priority: Parsing aus prio:/priority: oder fuehrendem ! in Title/Body
  • due_date: optional aus Title oder Body via Pattern due:YYYY-MM-DD
  • Android-Leer-Events wie triggered (ohne Inhalt) werden ignoriert

Beispiel:

Server reboot prio:high due:2026-03-01

Beispiele Nachricht -> Task

  • Backup pruefen -> Task ohne due_date, ohne priority
  • Backup pruefen due:2026-03-10 -> Task mit due_date=2026-03-10
  • Backup pruefen prio:5 -> Task mit priority=5
  • Backup pruefen prio:high due:2026-03-10 -> Task mit priority=4, due_date=2026-03-10
  • ! Backup sofort pruefen due:2026-03-10 -> Task mit priority=5, due_date=2026-03-10
  • ! Backup sofort (ohne due:) -> Task mit priority=5, due_date = Erstellungstag (heute); führendes ! wird aus dem Titel entfernt

Optional: Mit DECK_STACK_HIGH_PRIO landen Tasks mit fuehrendem ! (ohne due:) im angegebenen Stack (z. B. „Heute“). Normale Tasks landen in DECK_STACK (z. B. Inbox). Tasks mit Fälligkeitsdatum landen stets in der normalen Spalte.

Prioritaetsreihenfolge:

  1. prio: / priority: Marker
  2. fuehrender ! Marker (setzt priority=5)

Healthcheck

python healthcheck.py

Exit-Codes:

  • 0: OK
  • 1: Queue erreichbar, aber Schwellwert überschritten (z. B. zu viele dead)
  • 2: Queue DB nicht gefunden

Queue-Zustände

  • pending neu
  • processing gerade in Bearbeitung
  • failed fehlgeschlagen, wird später erneut versucht
  • done erfolgreich in Deck erstellt
  • dead maximale Retry-Anzahl erreicht

Linux / systemd / Forgejo

Siehe:

  • DEPLOY.md
  • OPERATIONS.md
  • deploy/systemd/ntfy2vikunja-ingestor.service
  • deploy/systemd/ntfy2vikunja-worker.service
  • deploy/systemd/ntfy2vikunja-bridge-run.service

Hinweise

  • Für Produktion sind zwei getrennte Services (Ingestor + Worker) empfohlen.
  • bridge_run.service ist praktisch, aber weniger isoliert.