Feed, ktery sbira dobre zpravy podle hashtagu #dobrázpráva z ATProto

README.md

Dobré zprávy – feed pro Bluesky #

Vlastní feed pro Bluesky, který sbírá všechny posty s hashtagem #dobrázpráva. Vychází z oficiálního feed-generator starter kitu, ale místo firehose odebírá Jetstream, který je mnohem lehčí na CPU i provoz.

Jak to funguje #

  1. src/util/subscription.ts se připojí na Jetstream přes WebSocket a odebírá jen kolekci app.bsky.feed.post. Při výpadku se znovu připojí a naváže od uloženého kurzoru (time_us, ukládá se do tabulky sub_state).
  2. src/subscription.ts každý nový post zkontroluje. Do SQLite uloží jen ty, které mají hashtag dobrázpráva v tag facetu nebo v poli tags. Smazané posty z databáze odstraní.
    • Porovnání ignoruje velikost písmen i diakritiku, takže projde #dobrázpráva, #DobráZpráva i #dobrazprava. Upravit to jde ve funkci normalizeTag.
  3. src/algos/dobra-zprava.ts vrací uložené posty od nejnovějších, se stránkováním přes kurzor (čas události z Jetstreamu + CID).
  4. Server (Express) odpovídá na app.bsky.feed.getFeedSkeleton a app.bsky.feed.describeFeedGenerator. Bluesky z vrácených URI sám poskládá hotové posty.

Požadavky #

  • Node.js 22 (nebo ≥ 18) a Yarn 1
  • Veřejná doména s HTTPS na portu 443, na které feed poběží (identifikuje se přes did:web)
  • Bluesky účet a app password pro publikaci feedu

Konfigurace #

Vytvoř soubor .env (načítá se přes dotenv):

Proměnná Výchozí Popis
FEEDGEN_HOSTNAME example.com Doména, na které feed běží
FEEDGEN_SERVICE_DID did:web:<hostname> DID služby
FEEDGEN_PUBLISHER_DID did:example:alice DID účtu, který feed publikuje
FEEDGEN_PORT 3000 Port serveru
FEEDGEN_LISTENHOST localhost Adresa, na které server naslouchá
FEEDGEN_SQLITE_LOCATION :memory: Cesta k SQLite souboru. Pro ostrý provoz nastav soubor, jinak se po restartu data ztratí
FEEDGEN_SUBSCRIPTION_ENDPOINT wss://jetstream2.us-east.bsky.network/subscribe Jetstream endpoint
FEEDGEN_SUBSCRIPTION_RECONNECT_DELAY 3000 Prodleva před opětovným připojením (ms)

Spuštění #

yarn
yarn start

Feed si pak můžeš lokálně vyzkoušet na: http://localhost:3000/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://<PUBLISHER_DID>/app.bsky.feed.generator/dobra-zprava

Docker viz sekce Nasazení.

Nasazení (Docker) #

Repo obsahuje Dockerfile a compose.yaml. Kontejner naslouchá na 127.0.0.1:3100 hostitele, databáze je ve volume feed-data (/data/feed.db). Před kontejner patří reverzní proxy s HTTPS, např. Caddy:

feed.atproto.cz {
  reverse_proxy http://localhost:3100
}

Na serveru:

git clone <repo> /opt/dobrezpravyfeed && cd /opt/dobrezpravyfeed
cp deploy/env.example .env      # a uprav FEEDGEN_PUBLISHER_DID
docker compose up -d --build

Kontrola: curl https://feed.atproto.cz/.well-known/did.json a docker compose logs -f feed.

Aktualizace: git pull && docker compose up -d --build. Databáze přežije, je ve volume.

Skripty běží uvnitř kontejneru, aby viděly stejnou databázi:

docker compose exec feed yarn moderate block-author did:plc:...

Zálohu databáze uděláš např. přes docker compose exec feed cp /data/feed.db /data/backup.db, případně zálohou volume.

Publikace feedu #

yarn publishFeed

Skript se interaktivně zeptá na handle, app password, název záznamu (použij dobra-zprava, musí odpovídat názvu algoritmu v src/algos), zobrazované jméno, popis a avatar. Opětovným spuštěním aktualizuješ údaje feedu. Zrušit publikaci jde přes yarn unpublishFeed.

Zpětné naplnění #

Server indexuje jen nové posty. Starší posty s hashtagem doplníš skriptem, který je dohledá přes vyhledávání Bluesky (vyžaduje přihlášení, doporučen app password):

docker compose exec feed yarn backfill

Skript prochází výsledky od nejnovějších po nejstarší, použije stejný filtr i blocklist jako živá indexace a lze ho bezpečně spouštět opakovaně. Dostupná historie je dána hloubkou vyhledávacího indexu Bluesky.

Jazyky obsahu #

Feed nefiltruje podle jazyka, ale klienti Bluesky ano. Podle nastavení Content languages (Nastavení → Languages) skrývají ve feedech posty v jazycích, které uživatel nemá zapnuté. Kdo má zapnutou jen angličtinu, neuvidí česky psané posty a feed mu bude připadat neúplný nebo prázdný. Řešení je v klientu přidat Čeština (případně Slovenština) mezi jazyky obsahu. Na straně serveru se to ovlivnit nedá.

Moderace #

Posty a autory lze z feedu vyřadit příkazem (vyžaduje FEEDGEN_SQLITE_LOCATION nastavené na souborovou databázi, stejnou jako používá server):

yarn moderate block-post at://did:plc:.../app.bsky.feed.post/...
yarn moderate block-author did:plc:...
yarn moderate unblock-post <at-uri>
yarn moderate unblock-author <did>
  • block-post smaže post z feedu a zabrání jeho opětovné indexaci.
  • block-author smaže z feedu všechny dosavadní posty autora a ignoruje jeho nové.
  • Odblokování smazané posty nevrací, jen umožní indexaci nových.
  • Změna se projeví hned, server není potřeba restartovat.

Struktura #

src/
  index.ts            načtení konfigurace a start serveru
  server.ts           Express + XRPC server
  subscription.ts     filtr postů s #dobrázpráva
  util/subscription.ts  Jetstream klient (reconnect, kurzor)
  algos/              algoritmy feedu (název souboru = rkey feedu)
  moderation.ts       blokace postů a autorů
  db/                 SQLite schéma a migrace
  methods/            XRPC endpointy
scripts/              publikace / zrušení feedu, moderace, backfill

Licence #

MIT, viz LICENSE. Původní kód je z bluesky-social/feed-generator.