A Persian-language news aggregator that costs nothing to run.
No server. No database. No hosting bill — just a static site rebuilt by a cron job.
Overview · Features · How it works · Stack · Quickstart · Deployment
دایره به معنای "دایره" یا "چرخه" است — تجمیع، پالایش و خلاصهسازی هوشمند اخبار فارسی، بدون هیچ زیرساخت سرور.
Dayereh pulls news from Telegram channels, RSS feeds, and financial market sources, cleans and structures it, and renders it into a single fast, RTL-native static site. A GitHub Actions cron job does the rebuilding — the entire output is static HTML/XML/JSON, committed straight back to the repo. No backend, no database, no cost.
|
📡 Multi-source aggregation Telegram public channels · RSS outlets · TGJU · Live Rates 🧠 AI daily bulletin Gemini-generated geopolitical/economic summary with a persistent rolling memory — each bulletin builds on the last instead of starting cold 🛡️ Resilient scraping Exponential backoff, pooled connections sized for worst-case concurrency, Google News proxy fallback for bot-protected domains |
🎞️ Full media pipeline Thread-safe download cache, gallery/album support, automatic image & video compression, video thumbnail extraction 🎨 Modern static frontend dark theme · fully RTL ⚡ Zero-cost deploy Static assets only — served straight from GitHub, rebuilt on a cron schedule |
sources.json
│
▼
Fetcher ──► Parsers ──► FeedItem ──► Builders ──► index.html
(requests/ (Telegram, (dataclass) (XML / JSON /
Selenium) RSS, TGJU, HTML)
LiveRates)
│
▼
TextCompressor ──► Gemini ──► news-summary.json
│
▼
memory.md (rolling context)
| Step | What happens |
|---|---|
| 1. Fetch | Sources pulled in parallel via ThreadPoolExecutor |
| 2. Parse | Per-source parser normalizes content into FeedItems |
| 3. Media | Thread-safe cache downloads + compresses images/video via ffmpeg/Pillow |
| 4. Build | Per-source XML (MRSS) and a combined JSON price feed |
| 5. Summarize | Feed text is TF-IDF compressed, then sent to Gemini with a fixed analyst persona + rolling memory |
| 6. Render | Static index.html with related-article clustering and read-state tracking |
| 7. Deploy | GitHub Actions commits output back to main on a cron schedule |
.
├── main.py # pipeline entrypoint
├── config/
│ └── sources.json # telegram / rss / tgju / live-rates definitions
├── core/
│ ├── parsers/
│ │ ├── telegram.py # public channel scraper (posts, albums, polls, video)
│ │ ├── rss.py # RSS/Atom item parser
│ │ ├── website.py # generic CSS-selector scraper
│ │ ├── tgju.py # currency/gold/coin market data
│ │ ├── live_rates.py # FX/crypto rate feed
│ │ └── tgstat.py # TGStat channel stats
│ ├── net/
│ │ ├── fetcher.py # requests + Selenium fetch layer
│ │ └── media_downloader.py # retrying media downloader
│ ├── converters/
│ │ ├── media_type.py
│ │ ├── media_compressor.py # ffmpeg/Pillow compression
│ │ └── markdown_to_html.py
│ ├── builders/
│ │ ├── xml_builder.py # per-source MRSS output
│ │ ├── json_builder.py # price feed JSON
│ │ └── html_builder.py # static site renderer
│ ├── ai/
│ │ ├── gemini.py # Gemini API client
│ │ ├── text_compressor.py # TF-IDF summarization pre-pass
│ │ ├── soul.md # analyst persona / system prompt
│ │ └── memory.md # rolling summarization memory
│ ├── models/
│ │ └── feed_item.py
│ └── utils/
│ ├── string_utils.py
│ ├── time_utils.py # Tehran (UTC+3:30) time normalization
│ ├── path_utils.py
│ └── id_generator.py
└── feeds/ # generated output (XML, JSON, static site)
└── view/
├── index.html
└── media/
Sources live in config/sources.json:
{
"telegram": [
{ "app_name": "tgm-example", "title": "کانال نمونه", "url": "https://t.me/s/example" }
],
"rss": [
{ "app_name": "rss-example", "title": "سایت نمونه", "url": "https://example.com/rss" }
],
"tgju": [
{ "slang": "price_dollar_rl", "title": "دلار آمریکا" }
],
"live-rates": [
{ "currency": "GOLD", "title": "طلای جهانی" }
]
}The Gemini key is passed as a CLI argument, never stored in config.
git clone https://github.com/ch3fferson/dayereh.git
cd dayereh
pip install -r requirements.txt
python main.py # build without AI summary
python main.py $GEMINI_API_KEY # build with AI summaryOutput lands in feeds/ and feeds/view/index.html. Skipping the Gemini key reuses the most recent cached bulletin.
Deployment is entirely handled by a scheduled GitHub Actions workflow:
- Cron trigger fixed to Tehran time (UTC+3:30, no DST):
30 */2 * * *(UTC) - Runs on the standard 2-vCPU / 7GB Actions runner
- Commits regenerated
feeds/output back tomain
MIT — see LICENSE.
تقدیم به همه جاویدنامان ایران