Zwei kleine Bots, die Inhalte automatisch nach Bluesky weiterleiten — entstanden für ein Fanprojekt, das Vereinsnachrichten dort verfügbar machen wollte, wo sie sonst fehlen. Sport ist dabei kein Muss: Der Ticker lässt sich ebenso für Veranstaltungen, Vereine oder andere Kanäle einrichten, dann ohne Spielplan-Anbindung.
Das Ziel ist bewusst fest auf Bluesky gelegt. Die Quelle ist austauschbar: Aktuell gibt es einen WhatsApp-Kanal- und einen Instagram-Anschluss.
Inoffiziell. Dieses Projekt steht in keiner Verbindung zu Meta, Bluesky oder irgendeinem Verein. Es greift auf inoffizielle Schnittstellen zu — bitte lies den Abschnitt Risiken & Rechtliches, bevor du es einsetzt.
| Programm | Betriebsart | Quelle (aktuell) |
|---|---|---|
skyrelay-matchday.py |
Spieltagsgebunden: läuft nur an Spieltagen, lauscht auf Live-Ereignisse und postet sofort | WhatsApp-Kanal |
skyrelay-feed.py |
Dauerbetrieb: prüft regelmäßig auf neue Beiträge | Instagram-Profil |
Benannt sind sie nach der Betriebsart, nicht nach der Quelle — damit ein Quellenwechsel später keine Umbenennung erzwingt.
Die Quelle ist derzeit fest mit dem jeweiligen Programm verbunden. Ob eine Kombination überhaupt sinnvoll ist, entscheidet eine einzige Frage: Meldet die Quelle neue Beiträge von sich aus (Push), oder muss man sie regelmäßig abfragen?
| Quelle | Ticker (ereignisgetrieben, Zeitfenster) | Feed (Dauerbetrieb, Abfrage) |
|---|---|---|
| WhatsApp-Kanal | ✅ umgesetzt — Beiträge erscheinen binnen Sekunden | ⚙️ möglich: Ticker ohne Spielplan mit weitem Zeitfenster (siehe unten) |
| ❌ nicht sinnvoll — Instagram kennt kein Push; für Ticker-Tempo müsste man im Minutentakt abfragen, was zuverlässig zur Kontosperre führt | ✅ umgesetzt | |
| weitere Quellen | nur mit Push-Unterstützung | wenn regelmäßiges Abfragen erlaubt ist |
WhatsApp im Dauerbetrieb braucht keinen Umbau: In der Konfiguration den
Spielplan leer lassen ([team] openligadb_filter =), day_end auf 23:59
setzen und den Cron-Eintrag täglich statt nur an Spieltagen starten. Einzige
Einschränkung: Über Mitternacht entsteht eine kurze Lücke, weil sich das
Programm beendet und nach dem Neustart nur Beiträge des laufenden Tages annimmt.
Neue Quellen sind willkommen, aber vor dem Programmieren lohnt die Prüfung: Gibt es eine brauchbare Bibliothek? Erlaubt der Dienst automatisierten Zugriff? Und vor allem — kann er Ereignisse melden, oder verträgt er zumindest häufiges Abfragen ohne Sperre? Erst wenn das geklärt ist, lohnt sich die Trennung von Quelle und Ablaufsteuerung. Sprich uns über ein Issue an.
- Spieltagserkennung über OpenLigaDB: An spielfreien Tagen beendet sich das Programm sofort und baut gar keine Verbindung auf. Nicht auf Fußball beschränkt — gut gepflegt sind dort Fußball (1.–3. Liga, DFB-Pokal, Frauen-Bundesliga) und Eishockey (DEL, DEL2). Für Sportarten ohne Daten (etwa Basketball oder der Handball-Ligabetrieb) lässt sich die Erkennung abschalten; der Ticker läuft dann an jedem Tag, an dem er gestartet wird.
- Spiel-Hashtag wird automatisch erzeugt (
#KSCDSCheim/auswärts korrekt herum), inklusive DFB-Pokal-Runden, und an jeden Beitrag gehängt. - Profil-Statuszeile: Die erste Zeile der Bluesky-Biografie schaltet zwischen „Bot ist an – 1. Spieltag …" und „Bot ist aus – nächstes Spiel …" um.
- Bearbeitungen im Kanal werden erkannt: Der alte Bluesky-Beitrag wird gelöscht und durch die korrigierte Fassung ersetzt.
- Überträgt Text, Bilder, Videos und erzeugt Link-Vorschaukarten.
Beide Programme laufen produktiv und sind vollständig über skyrelay.conf
konfigurierbar — Verein, Kanäle, Konten, Texte und Dateinamen stecken alle dort,
im Code steht nichts Vereinsspezifisches mehr.
- Linux, 64 Bit (
x86_64oderaarch64). Ein Raspberry Pi 3B+ genügt, aber das System muss 64-Bit sein — für 32-Bit (armv7l) gibt es keine passenden Pakete. - Python 3.10 oder neuer, dazu
python3-venvundtzdata. - Ein Bluesky-Konto für den Bot samt App-Passwort (Einstellungen → Datenschutz und Sicherheit → App-Passwörter).
- Für
skyrelay-matchday.py: eine separate WhatsApp-Nummer, deren Verlust verschmerzbar wäre (siehe Risiken). Das Konto muss den Kanal abonniert haben. - Für
skyrelay-feed.py: ein Instagram-Zweitkonto für den Abruf.
git clone https://github.com/Vijabei/SkyRelay.git
cd SkyRelay
./install.shDas Skript prüft System, Architektur und Python-Version, legt ein virtuelles
Umfeld unter venv/ an und installiert die Abhängigkeiten. Es ändert nichts am
System: Fehlende Systempakete werden nur gemeldet, nicht automatisch nachinstalliert.
Am einfachsten mit dem Einrichtungsassistenten:
venv/bin/python skyrelay-setup.pyEr fragt zuerst nach dem Einsatzzweck und führt dann unterschiedlich weiter:
| Zweck | Ablauf |
|---|---|
| Sport mit Spielplan | Liga und Verein werden live bei OpenLigaDB gesucht (kein Nachschlagen von Team-Nummern), die Kürzeltabelle für die Hashtags wird vorbefüllt — für die Fußball-Ligen 1–3 mit den gebräuchlichen Kürzeln, sonst mit klar als Vorschlag markierten Ableitungen (?). Neben Fußball stehen die weiteren gepflegten Ligen zur Wahl, etwa die DEL. |
| Sport ohne Spielplan | Für Sportarten, die OpenLigaDB nicht führt (Basketball, Handball-Liga): keine Spieltags-Erkennung, wechselnder Hashtag über SKYRELAY_HASHTAG. |
| Anderer Zweck | Für Vereine, Veranstaltungen, Projekte: wie oben, zusätzlich mit neutral formulierten Vorgabetexten statt Fußballsprache. |
In allen Fällen werden Beitragstexte, Profil-Statuszeile und Zeitfenster abgefragt,
auf Wunsch die Bluesky-Anmeldung geprüft und daraus die fertige skyrelay.conf
geschrieben. Einmal gepflegte Kürzel bleiben dabei erhalten — auch nach einem
Ligawechsel, damit Pokalgegner aus anderen Ligen weiterhin korrekt benannt werden. Ein erneuter Aufruf dient zum Ändern: Vorhandene Werte
werden als Vorgabe angeboten, und von der alten Datei wird eine Sicherung angelegt.
Wer lieber von Hand arbeitet, kopiert die kommentierte Vorlage:
cp skyrelay.conf.example skyrelay.confFür den Spieltags-Ticker sind mindestens [bluesky] handle und
[source] channel_invite_link nötig; für einen anderen Verein zusätzlich
[team] (OpenLigaDB-Suchbegriff und Team-Nummer) sowie die Kürzel unter
[team_codes]. Für die Instagram-Spiegelung genügt der Abschnitt [feed]
(Profil, Zweitkonto und optional ein eigenes Bluesky-Konto). Alle Abschnitte sind
in der Vorlage kommentiert, eine Übersicht steht im
CheatSheet.
Die eigene skyrelay.conf steht in .gitignore und gehört nicht ins Repository.
Mehrere Vereine parallel betreibst du über SKYRELAY_CONFIG=/pfad/zur/datei.conf.
Wer von einer älteren Fassung umsteigt, muss nichts neu koppeln: vorhandene
dsc_ticker_*-Dateien werden beim ersten Start automatisch übernommen.
Das App-Passwort gehört niemals in eine Datei, sondern in eine Umgebungsvariable — je eine pro Bot, damit die Zuordnung eindeutig bleibt:
export BLUESKY_TICKER_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"; export BLUESKY_FEED_APP_PASSWORD="yyyy-yyyy-yyyy-yyyy"Nutzen beide Bots dasselbe Konto, steht in beiden Variablen einfach dasselbe Passwort.
~/.bashrc noch ~/.profile. Für den automatischen Betrieb
gehören die Variablen oben in die crontab (crontab -e) — dort ohne
Anführungszeichen, sonst würden sie Teil des Werts:
BLUESKY_TICKER_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx
BLUESKY_FEED_APP_PASSWORD=yyyy-yyyy-yyyy-yyyyEinmalig und interaktiv im Terminal (nicht per cron):
SKYRELAY_FORCE=1 SKYRELAY_DRY_RUN=1 venv/bin/python skyrelay-matchday.pyEs erscheint ein QR-Code, den du im Handy unter WhatsApp → Einstellungen →
Verknüpfte Geräte → Gerät hinzufügen scannst. Praxistipp: Bildschirm hell stellen und
das Terminal stark vergrößern, sonst findet die Kamera zu wenig Kontrast. Alternativ
per Zahlencode koppeln — dazu SKYRELAY_PAIR_PHONE=49xxxxxxxxx ergänzen.
Ein Login über web.whatsapp.com im Browser hilft nicht: Das Programm ist ein
eigenes verknüpftes Gerät mit eigener Sitzung. Diese liegt danach in
*_session.sqlite3 und wird bei allen weiteren Läufen wiederverwendet.
Im Regelbetrieb genügt ein täglicher cron-Eintrag — ob heute überhaupt gespielt wird, entscheidet das Programm selbst:
0 6 * * * BLUESKY_TICKER_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx" /pfad/zu/SkyRelay/venv/bin/python3 /pfad/zu/SkyRelay/skyrelay-matchday.py >/dev/null 2>&1Zwei häufige Stolpersteine: Pfade sind groß-/kleinschreibungsabhängig, und eine
Ausgabeumleitung (>> skyrelay.log) ist nicht nötig — das Programm schreibt sein
Protokoll selbst, sonst steht jede Zeile doppelt darin.
Läuft der Bot gerade?
pgrep -af skyrelay-matchdayAlle weiteren Betriebsarten (Testläufe, Nachholen verpasster Beiträge, Einzeltests) stehen in CHEATSHEET-matchday.md.
Einmalig die Instagram-Sitzung des Zweitkontos anlegen:
venv/bin/instaloader -l dein_zweitkontoDanach regelmäßig per cron aufrufen — das Programm überträgt, was seit dem letzten Lauf neu ist:
*/15 * * * * BLUESKY_FEED_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx" /pfad/zu/SkyRelay/venv/bin/python3 /pfad/zu/SkyRelay/skyrelay-feed.py >/dev/null 2>&1Nutzt du für Instagram ein anderes Bluesky-Konto als für den Ticker, trägst du
es unter [feed] bluesky_handle ein und gibst die App-Passwörter getrennt an
(siehe unten).
| Datei | Inhalt |
|---|---|
README.md |
dieses Dokument |
skyrelay-setup.py |
Einrichtungsassistent (erzeugt und ändert skyrelay.conf) |
skyrelay.conf.example |
kommentierte Vorlage aller Einstellungen |
skyrelay_common.py |
gemeinsame Bausteine beider Programme (Protokoll, Konfiguration, Bilder, Video-Upload) |
CHEATSHEET-matchday.md |
alle Umgebungsvariablen, Betriebsarten, Dateien, Fehlerbehebung |
ISSUE-DRAFT-neonize-newsletter-panic.md |
vorbereiteter Fehlerbericht an das neonize-Projekt |
neonizeist auf0.4.3.post0festgelegt und wurde am 08.08.2026 vollständig geprüft (siehe UPGRADE-TEST.md). Vor einem Wechsel die Sitzungsdatei sichern — das Datenbankschema ändert sich.- Strg+C kann gelegentlich vom Go-Anteil abgefangen werden (ab 0.4.x): Dann
endet das Programm mit
Quit, ohne aufzuräumen, und die Profil-Statuszeile bleibt auf „Bot ist an" stehen. Beobachtet wurde das einmal; im Regelfall läuft das Aufräumen normal. Zurücksetzen notfalls mitSKYRELAY_PROFILE=off. Im cron-Betrieb ohne Bedeutung — dort endet der Ticker regulär zum Tagesende. - Absturz bei gelöschten Kanalbeiträgen: Löscht der Kanal einen Beitrag, bleibt eine leere Nachricht zurück, an der der zugrundeliegende Go-Programmteil abstürzt. Betrifft nur die Nachhol-Betriebsarten (kleineren Wert wählen), nicht den Dauerbetrieb — dieser lauscht auf Ereignisse und ist davon nicht betroffen.
instaloaderist auf 4.15.2 festgelegt. Version 4.15.3 stellte die Profilabfrage auf einen Endpunkt um, den Instagram seit Anfang August 2026 drosselt — schon die erste Anfrage endet mit „429 Too Many Requests" (instaloader#2726, offen). Erst nach dessen Lösung aktualisieren.- Umfragen werden übersprungen: Bluesky kennt dieses Format nicht.
- Videos bis rund 100 MB; größere werden nicht übertragen (Bluesky-Grenze).
- Mehrere Bilder in einem Beitrag: Bluesky nimmt höchstens vier.
- WhatsApp: Der Zugriff erfolgt über einen inoffiziellen Client. Das verstößt gegen die Nutzungsbedingungen und kann zur Sperrung der Nummer führen. Nutze ausschließlich eine Nummer, deren Verlust dich nicht trifft — und niemals deine private. Dasselbe gilt sinngemäß für das Instagram-Zweitkonto.
- Urheberrecht: Du überträgst fremde Inhalte. Kläre für dich, ob du das darfst, und kennzeichne den Bot als inoffiziell (die Beiträge tragen dafür einen Hinweis samt Quellenangabe).
- Keine Verbindung zu Meta, Bluesky oder einem Verein. Marken und Namen werden ausschließlich beschreibend verwendet.
- Ohne Gewähr: Die Schnittstellen sind inoffiziell und können sich jederzeit ändern. Nutzung auf eigenes Risiko.
SkyRelay ist im Wesentlichen Verdrahtung — die eigentliche Arbeit leisten diese Projekte, denen der Dank gebührt:
| Projekt | Wofür | Lizenz |
|---|---|---|
| neonize | Zugriff auf WhatsApp aus Python | Apache-2.0 |
| whatsmeow | die WhatsApp-Umsetzung, auf der neonize aufbaut | MPL-2.0 |
| atproto (Python SDK) | Anbindung an Bluesky | MIT |
| Instaloader | Abruf von Instagram-Beiträgen | MIT |
| Pillow | Bildbearbeitung und Komprimierung | HPND |
| Requests | HTTP-Aufrufe | Apache-2.0 |
| Segno | QR-Code im Terminal für die Kopplung | BSD-3-Clause |
| OpenLigaDB | freie Spielplandaten (Spieltag, Anstoß, Gegner) | Community-Projekt |
Verbesserungen sind willkommen — vor allem Vereinskürzel, die noch fehlen oder unüblich sind. Dafür muss man nicht programmieren können: Ein Issue mit Team-Nummer und Kürzel genügt. Wie das geht, steht in CONTRIBUTING.md.
PolyForm Noncommercial License 1.0.0 — nichtkommerzielle Nutzung ist frei, einschließlich Fanprojekten, gemeinnütziger Organisationen und Bildungseinrichtungen. Der Copyright-Hinweis muss dabei erhalten bleiben.
Kommerzielle Nutzung — etwa durch Vereine als Wirtschaftsunternehmen, Medienhäuser oder werbefinanzierte Angebote — erfordert eine gesonderte Lizenz. Anfragen bitte über ein Issue in diesem Repository.
Hinweis: Das ist eine „source available"-Lizenz, keine Open-Source-Lizenz im Sinne der OSI — die Einschränkung auf nichtkommerzielle Nutzung ist damit unvereinbar.