diff --git a/AGENTS.md b/AGENTS.md index 8248fa8..29f8e92 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## Overview -Indonesian Telegram bot for group profile enforcement (photo + username), captcha verification, and anti-spam protection. Built with python-telegram-bot v20+, SQLModel, Pydantic, and Logfire. +Indonesian Telegram bot for multi-group profile enforcement (photo + username), captcha verification, and anti-spam protection. Built with python-telegram-bot v20+, SQLModel, Pydantic, and Logfire. ## Commands @@ -40,6 +40,7 @@ PythonID/ │ ├── main.py # Entry point + handler registration (priority groups!) │ ├── config.py # Pydantic settings (get_settings() cached) │ ├── constants.py # Indonesian templates + URL whitelists (528 lines) +│ ├── group_config.py # Multi-group config (GroupConfig, GroupRegistry) │ ├── handlers/ # Telegram update handlers │ │ ├── captcha.py # New member verification flow │ │ ├── verify.py # Admin /verify, /unverify commands @@ -57,7 +58,7 @@ PythonID/ │ └── database/ │ ├── models.py # SQLModel schemas (4 tables) │ └── service.py # DatabaseService singleton (645 lines) -├── tests/ # pytest-asyncio (18 files, 100% coverage) +├── tests/ # pytest-asyncio (18 files, 99% coverage) └── data/bot.db # SQLite (auto-created, WAL mode) ``` @@ -71,17 +72,19 @@ PythonID/ | Change config | `config.py` | Pydantic BaseSettings with env vars | | Add URL whitelist | `constants.py` → `WHITELISTED_URL_DOMAINS` | Suffix-based matching | | Add Telegram whitelist | `constants.py` → `WHITELISTED_TELEGRAM_PATHS` | Lowercase, exact path match | +| Multi-group config | `group_config.py` | GroupConfig model, GroupRegistry, groups.json loading | ## Code Map (Key Files) | File | Lines | Role | |------|-------|------| -| `database/service.py` | 645 | **Complexity hotspot** - handles warnings, captcha, probation state | -| `constants.py` | 528 | Templates + massive whitelists (Indonesian tech community) | -| `handlers/captcha.py` | 365 | New member join → restrict → verify → unrestrict lifecycle | -| `handlers/verify.py` | 344 | Admin verification commands + inline button callbacks | -| `handlers/anti_spam.py` | 327 | Probation enforcement with URL whitelisting | -| `main.py` | 293 | Entry point, logging, handler registration, JobQueue setup | +| `group_config.py` | 250 | Multi-group config, registry, JSON loading, .env fallback | +| `database/service.py` | 671 | **Complexity hotspot** - handles warnings, captcha, probation state | +| `constants.py` | 530 | Templates + massive whitelists (Indonesian tech community) | +| `handlers/captcha.py` | 375 | New member join → restrict → verify → unrestrict lifecycle | +| `handlers/verify.py` | 358 | Admin verification commands + inline button callbacks | +| `handlers/anti_spam.py` | 326 | Probation enforcement with URL whitelisting | +| `main.py` | 315 | Entry point, logging, handler registration, JobQueue setup | ## Architecture Patterns @@ -98,6 +101,13 @@ group=1 # message_handler: Runs LAST, profile compliance check - `get_database()` — DatabaseService, lazy init - `BotInfoCache` — Class-level cache for bot username/ID +### Multi-Group Support +- `GroupConfig` — Pydantic model for per-group settings (warning thresholds, captcha, probation) +- `GroupRegistry` — O(1) lookup by group_id, manages all monitored groups +- `groups.json` — Per-group config file; falls back to `.env` for single-group mode +- `get_group_config_for_update()` — Helper to resolve config for incoming Telegram updates +- Exception-isolated loops — Per-group API calls wrapped in try/except to prevent cross-group failures + ### State Machine (Progressive Restriction) ``` 1st violation → Warning with threshold info @@ -179,3 +189,7 @@ if user.id not in admin_ids: - JobQueue auto-restriction job runs every 5 minutes (first run after 5 min delay) - Bot uses `allowed_updates=["message", "callback_query", "chat_member"]` - Captcha uses both `ChatMemberHandler` (for "Hide Join" groups) and `MessageHandler` fallback +- Multi-group: handlers use `get_group_config_for_update()` instead of `settings.group_id` +- Captcha callback data encodes group_id: `captcha_verify_{group_id}_{user_id}` to avoid ambiguity +- Scheduler iterates all groups with per-group exception isolation +- DM handler scans all groups in registry for user membership and unrestriction diff --git a/README.md b/README.md index 0cf674a..8e0ed41 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ A comprehensive Telegram bot for managing group members with profile verificatio ## Features ### Core Monitoring -- Monitors all messages in a configured group +- Monitors all messages in one or more configured groups +- **Multi-group support**: Manage multiple groups from a single bot instance with isolated per-group settings via `groups.json` - Checks if users have a public profile picture - Checks if users have a username set - Sends warnings to a dedicated topic (thread) for non-compliant users @@ -101,6 +102,49 @@ WARNING_TIME_THRESHOLD_MINUTES=180 RULES_LINK=https://t.me/yourgroup/rules ``` +### 6. Multi-Group Configuration (Optional) + +To manage multiple groups from a single bot instance, use a `groups.json` configuration file: + +```bash +cp groups.json.example groups.json +``` + +Add `GROUPS_CONFIG_PATH=groups.json` to your `.env` file, then edit `groups.json`: + +```json +[ + { + "group_id": -1001234567890, + "warning_topic_id": 123, + "restrict_failed_users": false, + "warning_threshold": 3, + "warning_time_threshold_minutes": 180, + "captcha_enabled": false, + "captcha_timeout_seconds": 120, + "new_user_probation_hours": 72, + "new_user_violation_threshold": 3, + "rules_link": "https://t.me/pythonID/290029/321799" + }, + { + "group_id": -1009876543210, + "warning_topic_id": 456, + "restrict_failed_users": true, + "warning_threshold": 5, + "warning_time_threshold_minutes": 60, + "captcha_enabled": true, + "captcha_timeout_seconds": 180, + "new_user_probation_hours": 168, + "new_user_violation_threshold": 2, + "rules_link": "https://t.me/mygroup/rules" + } +] +``` + +When `groups.json` is present, per-group settings override the `.env` defaults. Each group can have its own warning thresholds, captcha settings, probation rules, and rules link. + +**Backward compatibility**: If no `groups.json` is configured (i.e., `GROUPS_CONFIG_PATH` is not set), the bot falls back to single-group mode using `GROUP_ID`, `WARNING_TOPIC_ID`, and other settings from `.env`. + ## Installation ```bash @@ -150,12 +194,12 @@ uv run pytest -v ### Test Coverage The project maintains comprehensive test coverage: -- **Coverage**: 99% (1,216 statements) -- **Tests**: 404 total -- **Pass Rate**: 100% (404/404 passed) +- **Coverage**: 99% (1,396 statements) +- **Tests**: 442 total +- **Pass Rate**: 100% (442/442 passed) - **All modules**: 100% coverage including JobQueue scheduler integration, captcha verification, and anti-spam enforcement - - Services: `bot_info.py` (100%), `scheduler.py` (100%), `user_checker.py` (100%), `telegram_utils.py` (100%), `captcha_recovery.py` (100%) - - Handlers: `anti_spam.py` (100%), `captcha.py` (100%), `check.py` (100%), `dm.py` (100%), `message.py` (100%), `topic_guard.py` (100%), `verify.py` (100%) + - Services: `bot_info.py` (100%), `group_config.py` (97%), `scheduler.py` (100%), `user_checker.py` (100%), `telegram_utils.py` (100%), `captcha_recovery.py` (100%) + - Handlers: `anti_spam.py` (100%), `captcha.py` (100%), `check.py` (98%), `dm.py` (100%), `message.py` (100%), `topic_guard.py` (100%), `verify.py` (100%) - Database: `service.py` (100%), `models.py` (100%) - Config: `config.py` (100%) - Constants: `constants.py` (100%) @@ -192,13 +236,16 @@ PythonID/ │ ├── test_scheduler.py # JobQueue scheduler tests │ ├── test_telegram_utils.py │ ├── test_topic_guard.py +│ ├── test_group_config.py │ ├── test_user_checker.py -│ └── test_verify_handler.py +│ ├── test_verify_handler.py +│ └── test_whitelist.py └── src/ └── bot/ ├── main.py # Entry point with JobQueue integration ├── config.py # Pydantic settings ├── constants.py # Shared constants + ├── group_config.py # Multi-group configuration (GroupConfig, GroupRegistry) ├── handlers/ │ ├── anti_spam.py # Anti-spam handler for probation users │ ├── captcha.py # Captcha verification handler @@ -412,6 +459,7 @@ The bot is organized into clear modules for maintainability: - `service.py`: Database operations with SQLite - `models.py`: Data models using SQLModel (UserWarning, PhotoVerificationWhitelist, PendingCaptchaValidation, NewUserProbation) - **config.py**: Environment configuration using Pydantic +- **group_config.py**: Multi-group configuration management (GroupConfig model, GroupRegistry for O(1) lookup, groups.json loading with .env fallback) - **constants.py**: Centralized message templates and utilities for consistent formatting across handlers ### Group Message Monitoring @@ -484,6 +532,7 @@ When a restricted user DMs the bot (or sends `/start`): | `LOGFIRE_ENABLED` | Enable Logfire logging integration | `true` | | `LOGFIRE_TOKEN` | Logfire API token (optional) | None | | `LOG_LEVEL` | Logging level (DEBUG/INFO/WARNING/ERROR) | `INFO` | +| `GROUPS_CONFIG_PATH` | Path to `groups.json` for multi-group support | None (single-group mode from `.env`) | ### Restriction Modes