Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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)
```

Expand All @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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
63 changes: 56 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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%)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down