Ein Krisen-Feed. In drei Sprachen. In Sekunden.
Kuratierte, KI-übersetzte Echtzeit-News für die iranische Diaspora. RSS-In, Telegram-Out, volles RTL, unter 50 €/Monat.

Warum es das gibt.
Für die iranische Diaspora — und Menschen im Iran selbst — gibt es während Krisenereignissen keine zentrale, mehrsprachige Stelle, die verifizierte Nachrichten in Echtzeit bündelt. Relevante Meldungen verteilen sich über Dutzende persischer Telegram-Kanäle, Twitter-Accounts und westliche Medien mit Verzögerung.
Bestehende Newsportale sind entweder nur auf Farsi (für die zweite Diaspora-Generation nicht lesbar), nur auf Englisch/Deutsch (ohne persischen Kontext) oder reine Aggregatoren ohne Krisen-Klassifikation und Push. AzadiFeed kuratiert RSS-Quellen automatisiert, übersetzt KI-gestützt in EN / DE / FA und pusht Breaking News in Sekunden an Telegram-Abonnenten mit Stadt-Filter.
Der Rahmen, in den es passen musste.
Wie es läuft.
◦ Schematische Darstellung, Zahlen illustrativ.
Vier bewusste Entscheidungen.
Coolify + Docker statt Vercel.
setInterval-Scheduler in einem eigenen Modulvercel.json-Cron-JobsMongoDB als Queue, statt Redis.
pending_feeds → published_feeds) mit aiProcessed: false-Flag als Work-Queue, Distributed-Lock via Mongo-DokumentOpenAI Multi-Model-Routing zur Laufzeit.
Socket.IO statt SSE oder Pusher.
Was nicht offensichtlich war.
RTL mit gemischten LTR-Segmenten
#MahsaAmini) und Zahlen. Naive RTL-Setups brechen das Layout, weil Browser den Bidi-Algorithmus inkonsistent auf gemischte Strings anwenden.Aktueller Stand:
<html dir="rtl"> per Locale, Vazirmatn als Variable-Font mit unicode-range-Fallback auf Inter für lateinische Glyphen — der Browser-Bidi-Algorithmus (UAX #9) übernimmt den Rest. Trägt Mainstream-Cases. Auf der Roadmap: unicode-bidi: plaintext plus explizites Unicode-Wrapping \u202B…\u202C der OpenAI-Outputs — Bidi gehört in die Content-Pipeline, nicht ins Stylesheet.Distributed Lock via MongoDB-Dokument
Lösung im Cron-Lock-Modul: atomares
findOneAndUpdate auf ein locks-Dokument plus ein expireAfterSeconds: 0-TTL-Index auf expiresAt; Lock-Dauer per Job konfigurierbar (300–900 s). Der Prozess, der den Lock verliert, returnt früh. TTL fängt auch Crashes ab — kein hängender Prozess nach OOM-Kill.Content-Enrichment-Fallback-Chain
Dreistufige Chain, verdrahtet im RSS-Sync-Cron: (1) Artikel-URL via Content-Fetcher per JSDOM nach
og:image / twitter:image / link rel=image_src scrapen, (2) Fallback auf die RSS-Image-Felder in Priorität: media:content (höchste Auflösung) > <enclosure> > media:thumbnail > erstes <img> im Content-HTML, (3) liefern beide Stufen nichts, rendert <NewsPlaceholder> eine Gradient-Card mit Kategorie- und Krisen-Typ-passendem Lucide-Icon — niemals ein Broken-Image. Jeder Netzwerk-Fetch ist in einen 10–15 s AbortController-Timeout gewickelt — eine tote Quelle blockiert die Pipeline nicht.Crisis-Relevance als Auto-Publish-Gate
crisisRelevanceScore: 0–100 (persistiert als crisisRelevance). Admin-konfigurierbarer Threshold (minCrisisRelevanceThreshold) entscheidet über autoPost — darunter landet das Item in der Mod-Queue.Löst zwei Probleme auf einmal: (a) Wetter-News werden nicht als „Breaking“ gepusht, (b) der Admin senkt den Threshold bei akuten Lagen temporär, um mehr durchzulassen.
Was läuft.
Was ich gelernt habe.
Budget als Architektur-Constraint.
Die 50-€/Monat-Grenze hat mehr gute Entscheidungen erzwungen, als sie verhindert hat. MongoDB-als-Queue, Multi-Model-Routing, eigener Socket-Server — alles Entscheidungen, die ich ohne den Cap nicht so konsequent durchgezogen hätte. Constraints are features.
Bidi ist keine CSS-Eigenschaft, sondern ein Produkt-Problem.
RTL mit eingestreutem LTR-Content braucht eine Content-Pipeline-Entscheidung, keine Stylesheet-Property. Wer das auf die CSS-Schicht schiebt, scheitert. Beim nächsten Multilingual-Projekt plane ich Bidi-Normalisierung von Anfang an als Translation-Output-Format ein.