← всі звіти · 03-architecture.md

sparc_phase: A project: pediatric-news created: 2026-05-11 author: claude-vps status: draft spec_link: ./01-specification.md pseudocode_link: ./02-pseudocode.md

Architecture — Pediatric News (Telegram + Threads + Infographic)

Стек

Компонент Технологія Чому
Runtime Python 3.11 Уже на VPS, поточний bot.py — Python
RSS-fetch feedparser Stateless, працює на 5 feed'ах
HTTP requests Поточний код, без async-міграції на старті
AI-prompt gemini CLI (subprocess) Google AI Ultra ключ, no extra deps
Image render Pillow (PIL) + DejaVuSans Детермінований, без AI-typo ризиків
Telegram API requests POST sendPhoto / sendMessage Поточна реалізація
Threads API requests POST Graph API v1.0 Сертифікований endpoint Meta
State seen_ids.json (atomic write) + JSONL log Спрощено, без БД
Scheduling cron 0 5,11,17 * * * Існує, не міняємо
Secrets /srv/passepartout/ Стандарт правило #4
Logs /var/log/pediatric-news.log + .jsonl journalctl + structured metrics

Файлова структура

/srv/projects/pediatric-news/
├── bot.py                       # main entry, cron target (рефактор поточного)
├── pipeline/
│   ├── __init__.py
│   ├── rss.py                   # fetch_rss(hours)
│   ├── gemini_select.py         # select_and_format → SelectedPost
│   ├── infographic.py           # build_spec + render_png (PIL)
│   ├── threads_publisher.py     # build_thread + publish_chain
│   ├── telegram_publisher.py    # sendPhoto/sendMessage + fallback
│   └── state.py                 # load_seen / save_seen / log_metric
├── prompts/
│   ├── telegram_post.md         # поточний промпт (з bot.py)
│   ├── infographic_spec.md      # з infographic-builder skill
│   └── threads_thread.md        # з threads-news skill
├── tests/
│   ├── test_rss.py
│   ├── test_infographic.py
│   └── test_threads_publisher.py
├── seen_ids.json                # state, поточний
└── AGENTS.md                    # оновити з Node.js → Python factsheet

Компоненти і відповідальності

Ключові інтерфейси

# pipeline/rss.py
def fetch_rss(max_age_hours: int = 90) -> list[Article]: ...

# pipeline/gemini_select.py
def select_and_format(articles: list[Article]) -> SelectedPost | None: ...

# pipeline/infographic.py
def build_spec(post: SelectedPost) -> InfographicSpec: ...
def render_png(spec: InfographicSpec, out_path: Path) -> Path: ...

# pipeline/threads_publisher.py
def build_thread(post: SelectedPost) -> list[ThreadPost]: ...   # 3 elements
def publish_thread(thread: list[ThreadPost]) -> int:            # returns depth published

# pipeline/telegram_publisher.py
def publish(html: str, png: Path | None) -> bool: ...

Потоки даних (Data flow)

[cron tick]
     │
     ▼
[bot.main()] ──── load_seen() ──── seen_ids.json
     │
     ▼
[fetch_rss] ──── 5 RSS feeds ────► list[Article]
     │
     ▼
[gemini_select] ──── Gemini API ──► SelectedPost (HTML + machine fields)
     │
     ▼ FAN-OUT
     ├─► [build_spec] → [render_png] → /tmp/png ──┐
     │                                            ▼
     ├─► [telegram_publish (HTML + PNG)] ──► Telegram channel
     │
     └─► [build_thread] → [publish_thread] ──► Threads (root + 2 reply)
                                  │
                                  ▼
                          [save_seen] + [log_metric → JSONL]

Безпека

Деплой і інфраструктура

Спостережуваність

Ризики

Відкриті питання

  1. Зберігати PNG локально (/srv/projects/pediatric-news/img/YYYY-MM-DD/) чи /tmp + cleanup?
  2. Окрема Caddy-endpoint для health-check (/health через невеликий FastAPI sidecar) чи cron-only?
  3. Чи мігрувати на systemd-timer замість cron — кращий journalctl?

Sign-off

🤖 PM Changelog