Telegram meme recommendation bot (@ffmemesbot). Infinite personalized meme feed. User presses /start -> receives meme with Like/Dislike buttons -> reaction triggers next meme.
North star metric: session length (memes per session). NOT like rate.
Goal: Viral growth through better memes -> better crossposting -> more users -> better signal -> better memes.
For current numbers, query the production database (see CLAUDE.md health check query).
| Metric | Value |
|---|---|
| Total users | 22,421 |
| MAU | 876 |
| WAU | 530 |
| Total reactions | 22M |
| Total memes | 535K (205K with status='ok') |
| Meme sources | 750 |
| Like rate | 43.4% |
| Median session | 19 memes |
| D1 retention | 37.7% |
| D30 retention | 48.5% |
User taps Like/Dislike
-> handle_reaction() saves reaction
-> next_message() pops meme from Redis queue
-> if queue low (<= 2): generate_recommendations(limit=5)
-> 9 SQL engines blended by user maturity stage
-> meme sent to user
Sources (TG/VK/IG) -> Parsers (hourly) -> meme_raw_* tables
-> ETL (filter, type detect) -> meme (status=created)
-> Download + Watermark + Upload to TG -> telegram_file_id
-> Ad filter + Dedup -> status='ok'
-> Describe Memes (async, every 30min) -> ocr_result JSONB (description, text, language)
-> Recommendation engines -> Blender -> Redis queue -> User
See specs/ for subsystem documentation:
| File | Scope |
|---|---|
| specs/recommendations.md | Engines, blender, queue, maturity stages |
| specs/reaction-flow.md | Hot path: reaction -> next meme |
| specs/parsing-etl.md | Source parsing, ETL, status pipeline |
| specs/dedup.md | Dedup mechanisms + improvement plan |
| specs/testing.md | Test strategy and coverage gaps |
| specs/describe-memes.md | Vision OCR: OpenRouter free tier, model chain, constraints |
| specs/issues.md | Prioritized issue backlog |
| specs/error-profile.md | Production error analysis |
| specs/data-hypotheses.md | Data analysis findings (H1-H7) |
| specs/experiment-2026-03-14.md | Experiment: queue refill threshold + removed fast_dopamine |
| specs/experiment-2026-03-16-es-ranked.md | Experiment: engagement_score ranked engine |
| specs/experiment-2026-03-20-adaptive-cold-start.md | Experiment: adaptive cold start (3-phase) |
| specs/cohort-analysis-2026-03-29.md | Cohort analysis: super users vs churned |
| specs/channel-growth-optimization.md | Channel growth: Telethon stats, scoring experiments, analysis |
- Only
status='ok'memes are served to users - Every reaction is persisted even if next_message() fails
- Double-tap doesn't deliver duplicate memes (reaction_is_new check)
- Cold start (<30 memes) uses different engine mix than mature users
- Moderators see low_sent_pool (75%) to review new content
- All memes must match user's language_code
- Already-seen memes excluded via LEFT JOIN user_meme_reaction ... IS NULL