first commit
This commit is contained in:
165
CLAUDE.md
Normal file
165
CLAUDE.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Random Coffee Bot is a Telegram bot that organizes random coffee meetings between participants. It automatically creates pairs, sends polls to collect participation, and notifies users about their matches. The bot runs on a scheduled basis using APScheduler.
|
||||
|
||||
**Multi-Group Support**: The bot supports multiple Telegram groups simultaneously, with isolated participant pools and pair tracking per group.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Docker Development (Primary Method)
|
||||
|
||||
```bash
|
||||
# Build the Docker image
|
||||
docker-compose build
|
||||
|
||||
# Start the bot in detached mode
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs randomcoffee_bot
|
||||
docker-compose logs -f randomcoffee_bot # Follow logs
|
||||
|
||||
# Stop the bot
|
||||
docker-compose down
|
||||
|
||||
# Rebuild and restart
|
||||
docker-compose up --build
|
||||
|
||||
# Clean up all resources
|
||||
docker-compose down --rmi all --volumes --remove-orphans
|
||||
```
|
||||
|
||||
### Direct Python Execution
|
||||
|
||||
The bot uses `uv` for dependency management (see `uv.lock`). To run directly:
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
uv sync
|
||||
|
||||
# Run the bot
|
||||
python -m src.main
|
||||
```
|
||||
|
||||
### Testing Bot Commands
|
||||
|
||||
The bot responds to these Telegram commands:
|
||||
- `/create_pairs` - Manually trigger pair creation
|
||||
- `/send_quiz` - Manually send participation quiz
|
||||
|
||||
## Architecture
|
||||
|
||||
### Layered Architecture
|
||||
|
||||
The project follows a clean architecture pattern with clear separation of concerns:
|
||||
|
||||
```
|
||||
src/
|
||||
├── adapter/ # External interfaces (Telegram, Database)
|
||||
├── controller/ # Request handlers and scheduling
|
||||
├── usecase/ # Business logic
|
||||
├── model/ # Domain models (SQLAlchemy)
|
||||
└── config.py # Configuration using Pydantic Settings
|
||||
```
|
||||
|
||||
### Adapter Layer
|
||||
- **telegram/** - Telegram Bot API integration using aiogram 3.x
|
||||
- `connection.py`: Bot instance
|
||||
- `routes.py`: Message/poll sending utilities
|
||||
- **database/** - SQLAlchemy async database operations
|
||||
- `connection.py`: Database helper initialization
|
||||
- `participant.py`: Participant CRUD operations (filtered by group_id)
|
||||
- `pair.py`: Pair CRUD and matching logic (filtered by group_id)
|
||||
- `poll_mapping.py`: Maps poll IDs to group IDs for poll answer handling
|
||||
|
||||
### Controller Layer
|
||||
- **telegram_callback.py**: Aiogram router with command handlers and poll answer handlers
|
||||
- Extracts group_id from message.chat.id for commands
|
||||
- Uses poll_mapping to find group_id for poll answers
|
||||
- **scheduler.py**: APScheduler configuration
|
||||
- Sends quiz to all groups every Friday at 17:00 Moscow time
|
||||
- Creates pairs for all groups every Sunday at 19:00 Moscow time
|
||||
|
||||
### Use Case Layer
|
||||
- **send_quiz.py**: Sends participation poll to specific group and stores poll_id -> group_id mapping
|
||||
- **create_pairs.py**: Generates unique pairs from available participants for specific group
|
||||
- **handle_quiz_answer.py**: Processes poll responses for specific group
|
||||
|
||||
### Model Layer
|
||||
- Based on SQLAlchemy 2.0 with async support
|
||||
- **base.py**: Declarative base with auto table naming
|
||||
- **participant.py**: Participant model with group_id
|
||||
- **pair.py**: Pair model with group_id and week tracking, unique constraint on (group_id, week_start, user1_id, user2_id)
|
||||
- **poll_mapping.py**: Poll ID to group ID mapping for tracking poll sources
|
||||
|
||||
### Shared Utilities
|
||||
Located in `shared/` directory:
|
||||
- **db_helper.py**: Reusable `DatabaseHelper` class for SQLAlchemy async session management
|
||||
- **logger.py**: Logging configuration with admin notifications
|
||||
|
||||
## Configuration
|
||||
|
||||
Configuration uses Pydantic Settings with environment variables:
|
||||
|
||||
```python
|
||||
# Required environment variables (see .env.example)
|
||||
bot__token=<telegram_bot_token>
|
||||
bot__group_chat_ids='[group_id1, group_id2, ...]' # JSON list of group IDs (negative numbers)
|
||||
bot__admin_chat_id=<admin_user_id>
|
||||
db__url=<database_url> # PostgreSQL or SQLite URL
|
||||
```
|
||||
|
||||
**Multi-Group Configuration**: The bot now supports multiple groups via `bot__group_chat_ids` as a JSON list. Each group operates independently with its own participant pool and pair history.
|
||||
|
||||
## Database
|
||||
|
||||
- Supports both SQLite (for local dev) and PostgreSQL
|
||||
- Uses SQLAlchemy 2.0 async API
|
||||
- Database file `random_coffee.db` is mounted as volume in Docker
|
||||
- Tables auto-created on startup via `database.create_tables()`
|
||||
- Session management through `database.session_getter()` context manager
|
||||
|
||||
## Scheduling
|
||||
|
||||
APScheduler (AsyncIOScheduler) runs timezone-aware jobs:
|
||||
- Timezone: Europe/Moscow
|
||||
- Jobs defined in `src/controller/scheduler.py`
|
||||
- All jobs removed and re-added on startup to avoid duplicates
|
||||
|
||||
## Key Workflows
|
||||
|
||||
### Poll → Pair Creation Flow (Per Group)
|
||||
1. Friday 17:00: Quiz sent to all configured groups asking about participation
|
||||
2. Poll ID → group ID mapping stored in poll_mapping table
|
||||
3. Users respond to poll (anonymous=False to track users)
|
||||
4. Responses handled by `handle_quiz_answer` use case → poll_id looked up to find group_id → participant stored with group_id
|
||||
5. Sunday 19:00: `create_pairs` use case runs for each group independently
|
||||
6. Available participants matched using logic in `adapter/database/pair.py` (filtered by group_id)
|
||||
7. Pairs posted to respective group with usernames
|
||||
8. Participants table cleared for that group for next week
|
||||
|
||||
### Pair Matching Logic
|
||||
Located in `src/adapter/database/pair.py`:
|
||||
- Ensures unique pairings (checks historical pairs for that group_id)
|
||||
- Filters participants by group_id
|
||||
- Uses SQL queries with aliases to find valid combinations
|
||||
- Randomizes pair selection
|
||||
|
||||
## Important Notes
|
||||
|
||||
- All Telegram operations use aiogram 3.x async API
|
||||
- Logging configured to file `shared/logs/bot.log` with admin notifications on errors
|
||||
- Bot uses `skip_updates=True` to ignore messages received while offline
|
||||
- Router registered in main.py, actual handlers in `controller/telegram_callback.py`
|
||||
- The main entry point expects `router` to be imported from `src.controller.tg_handlers`, but the actual file is `telegram_callback.py` (verify import path)
|
||||
|
||||
### Multi-Group Implementation
|
||||
- All database queries filter by `group_id` to isolate data per group
|
||||
- Poll answers don't contain chat_id, so poll_id → group_id mapping is stored when quiz is sent
|
||||
- Scheduler iterates over all configured groups for scheduled tasks
|
||||
- Commands (like `/create_pairs`, `/send_quiz`) extract group_id from message.chat.id
|
||||
- Each group maintains independent participant pools and pair history
|
||||
Reference in New Issue
Block a user