English | Π ΡΡΡΠΊΠΈΠΉ
A Telegram bot that tracks quote days and only publishes a quote of the day when the conversation was actually worth quoting.
- π Quote of the Day Flow β scores messages between two daily cutoff points
- π€ AI Scoring β evaluates messages for humor, wit, depth, and memorability via OpenRouter API
- π§΅ Optional Quote Context β adds up to 5 consecutive or reply-linked messages only when needed
- π΄ Boring-Day Detection β if the day feels flat, the bot says so instead of forcing a weak quote
- β€οΈ Reaction Context β sends emoji reactions to AI as context for each message
- π§Ύ AI Audit Log β writes the exact OpenRouter request body and raw response to
logs/ai_audit.jsonlfor 7 days - π Localized Interface β supports Russian, Ukrainian, English, and German UI text
- π§ One-Time Language Detection β if a group has no saved language, the next daily AI run chooses the best interface language and stores it
- π Text Context β stores message length signals for transparent details
- π§ Single
/startControl Panel β stats, group language, and close cleanup live behind inline buttons - π Auto-Pin β pins the winning quote in the chat
- π Statistics β chat stats, personal stats, top authors, and rating breakdown
- β° Scheduler β configurable daily time for quote selection
- π³ Docker Support β easy deployment with Docker Compose
- Add the bot to your Telegram group and grant admin rights
- Members chat as usual β the bot silently collects messages and reactions
- The bot collects messages for the day from the previous cutoff to the next one
- At the scheduled time (default
21:00), the bot evaluates the closed day - If there are fewer than
10messages, the day is skipped silently - If there are
10+messages, AI both scores messages and decides whether the whole day is quote-worthy - The best message is selected by the AI score; reactions and length are only context:
| Component | Weight | Description |
|---|---|---|
| AI Score | 100% | LLM-based evaluation with reaction context |
| Reactions | Context | Emoji reactions sent to AI when present |
| Length | Context | Stored for transparent quote details |
- If the quote needs setup, AI may attach a validated consecutive/reply-linked context block of up to
5messages - If the group has no saved interface language yet, the same AI run selects one of
ru,uk,en, ordeand the bot stores it for future UI messages - If the day is boring, the bot posts a boring-day notice with a
Detailslink instead of a weak quote
| Command | Description |
|---|---|
/start |
Opens the context-aware control panel |
- Python 3.10+
- PostgreSQL 16+ (in production Quoto runs on a shared
coredatabase β see the Database section below) - Docker (optional)
# 1. Clone the repository
git clone https://github.com/FreshLabDev/quoto.git
cd quoto
# 2. Create virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
. venv\Scripts\activate # Windows
# 3. Install dependencies
pip install -r requirements.txt
# 4. Apply database migrations
alembic upgrade headCreate a .env file in the root directory (see .env.example):
BOT_TOKEN=your_telegram_bot_token
BOT_USERNAME=your_bot_username
# Production points DB_URL at the shared core-postgres as the quoto_core role
# (postgresql+asyncpg://quoto_core:***@core-postgres:5432/core). For local dev
# docker-compose builds it from the POSTGRES_* vars β see Database below.
DB_URL=postgresql+asyncpg://user:password@localhost:5432/dbname
OPENROUTER_API_KEY=sk-or-v1-your-key-here
OPENROUTER_EVAL_MODEL=poolside/laguna-s-2.1:free
# Used only if the primary eval model errors out after retries.
OPENROUTER_EVAL_FALLBACK_MODEL=poolside/laguna-s-2.1
OPENROUTER_EVAL_REASONING_EFFORT=medium
OPENROUTER_EVAL_MAX_TOKENS=32000
OPENROUTER_MEDIA_MODEL=nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free
OPENROUTER_MEDIA_FALLBACK_MODEL=google/gemini-2.5-flash-lite
OPENROUTER_MEDIA_REASONING_EFFORT=medium
OPENROUTER_HTTP_REFERER=https://t.me/quototbot
OPENROUTER_APP_TITLE=Quoto
MEDIA_ANALYSIS_ENABLED=True
MEDIA_PHASH_DISTANCE=5
MEDIA_CACHE_PROMPT_VERSION=v2
MEDIA_IMAGE_MAX_SIDE=1280
MEDIA_IMAGE_QUALITY=82
MEDIA_VIDEO_MAX_SECONDS=3600
MEDIA_VIDEO_LOW_RES_MAX_SECONDS=10800
MEDIA_VIDEO_MAX_HEIGHT=720
MEDIA_VIDEO_CRF=30
MEDIA_VIDEO_FPS=12
MEDIA_AUDIO_BITRATE=64k
MEDIA_AUDIO_SAMPLE_RATE=24000
MEDIA_COMMAND_TIMEOUT_SECONDS=300
MEDIA_PENDING_RETRY_INTERVAL_SECONDS=300
MEDIA_PENDING_RETRY_BATCH_SIZE=10
MEDIA_RETRY_MAX_ATTEMPTS=5
MEDIA_RETRY_BASE_DELAY_SECONDS=300
MEDIA_RETRY_MAX_DELAY_SECONDS=3600
MEDIA_PROVIDER_COOLDOWN_SECONDS=900
MEDIA_PENDING_ITEM_TIMEOUT_SECONDS=600
DEVELOPER_IDS=[1234567890]
QUOTE_HOUR=21
QUOTE_MINUTE=0
TIMEZONE=Europe/Kyiv
MIN_MESSAGES_FOR_AUTO_REVIEW=10
WEIGHT_REACTIONS=0.0
WEIGHT_AI=1.0
WEIGHT_LENGTH=0.0Use /start in a private chat or group. In groups, regular users see stats buttons, while admins also get group language controls.
python main.pyYou can easily run the bot using Docker Compose:
docker-compose up -d --buildQuoto stores its data in PostgreSQL and shares one database with the other
FreshLabDev bots (vido, branchy, searcher):
- In production it connects to the shared
coredatabase as the least-privilege rolequoto_core. Quoto's own tables live in thequotoschema; shared identity, chats and language live in thecoreschema (core.person,core.chat, and thecore.set_language/core.effective_languagefunctions). Quoto's tables referencecore.personandcore.chatby the Telegram natural keys (user id, chat id) β there is no separate per-bot users/groups identity table. - For local development
docker-compose upstarts a bundled Postgres seeded with a copy of thecoreschema (deploy/core-init.sql) so the foreign keys intocore.person/core.chatresolve; the app'sDB_URLis built from thePOSTGRES_*variables in.env.
Apply migrations with alembic upgrade head (the Alembic version table lives in
the quoto schema).
| Layer | Technology |
|---|---|
| Framework | Aiogram 3 |
| Database | Shared PostgreSQL core (schema quoto) + SQLAlchemy (Async) |
| Validation | Pydantic |
| AI | OpenRouter API (any LLM) |
| Scheduler | APScheduler |
| HTTP Client | HTTPX |
quoto/
βββ app/
β βββ ai.py # OpenRouter AI integration & message evaluation
β βββ config.py # Settings, logging, environment variables
β βββ core.py # Core business logic (people, groups, messages)
β βββ core_client.py # Helpers over the shared core.* functions (touch, language)
β βββ db.py # Database session & initialization
β βββ handlers.py # Telegram bot handlers & commands
β βββ menu.py # /start control panel rendering
β βββ models.py # SQLAlchemy models (GroupSettings, Message, Quote, β¦) β FK into core.person/core.chat
β βββ scheduler.py # APScheduler jobs & quote of the day pipeline
β βββ scoring.py # Scoring engine & best quote selection
β βββ utils.py # Utility functions
βββ docker-compose.yml
βββ Dockerfile
βββ main.py # Entry point
βββ requirements.in
βββ requirements.txt
βββ .env.example
Contributions are welcome! Feel free to:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
GNUv3 License β see LICENSE file for details
