From 193e72ea422a2070924aaf1073f760296e12c225 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 19:56:19 +0000 Subject: [PATCH 1/2] Initial plan From f65bf8f8d534f75baee8dae99e338af10157cd26 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 20:00:10 +0000 Subject: [PATCH 2/2] Add GitHub Copilot instructions and Claude project specifications Co-authored-by: MattiaFailla <11872425+MattiaFailla@users.noreply.github.com> --- .github/copilot-instructions.md | 63 +++++++++++++++ CLAUDE.md | 136 ++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 .github/copilot-instructions.md create mode 100644 CLAUDE.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..237569f9e --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,63 @@ +# Copilot Instructions for python-italy-telegram-bot + +## Project Overview + +**Electus** is the official Telegram bot for Italian Python community groups. +It handles welcome captcha verification, moderation (ban/mute/report), spam detection, and multi-group management. +Built with `python-telegram-bot` (async), PostgreSQL via `psycopg`, and deployed on Fly.io. + +## Tech Stack + +- **Language**: Python 3.14+ +- **Bot Framework**: python-telegram-bot >= 22.0 (async API) +- **Database**: PostgreSQL via psycopg 3 with async connection pooling; in-memory fallback for dev +- **Config**: python-dotenv for environment variables +- **Build**: Hatchling; dependency management with uv +- **Linting/Formatting**: ruff +- **Type Checking**: mypy +- **Testing**: pytest + +## Architecture + +Layered architecture with strict separation of concerns: + +``` +Handlers (telegram.ext) → Services (business logic) → Repository (data access) +``` + +- **Handlers** (`src/python_italy_bot/handlers/`): Receive Telegram updates, delegate to services. Each module exposes a `create_*_handlers()` factory that returns a list of `telegram.ext` handler objects. +- **Services** (`src/python_italy_bot/services/`): Contain business logic (`CaptchaService`, `ModerationService`). Depend on `AsyncRepository`. +- **Repository** (`src/python_italy_bot/db/`): Abstract `AsyncRepository` base class with `InMemoryRepository` and `PostgresRepository` implementations. Factory function `create_repository()` selects based on `DATABASE_URL`. +- **Models** (`src/python_italy_bot/db/models.py`): Domain dataclasses (`Ban`, `Mute`, `Report`). +- **Config** (`src/python_italy_bot/config.py`): `Settings` class loads all env vars. +- **Strings** (`src/python_italy_bot/strings.py`): Centralized bot message templates. + +Dependency injection is done via `context.bot_data` dictionary, populated in `_post_init`. + +## Coding Standards + +- **Type hints**: Use modern Python type syntax everywhere (`int | None`, `list[int]`), no `Optional` or `Union`. +- **Async/await**: All handlers, services, and repository methods are `async def`. +- **Docstrings**: Module-level docstring on every file. One-line docstrings on functions and classes. +- **Imports**: Use relative imports within the package (`from ..services.captcha import CaptchaService`). +- **Handler pattern**: Define private `async def _handle_*` functions; expose a public `create_*_handlers()` factory returning `list`. +- **Error handling**: Wrap Telegram API calls in `try/except`, log warnings, degrade gracefully. +- **Logging**: Use `logging.getLogger(__name__)` per module. +- **String formatting**: Use f-strings or `.format()` with named placeholders from `strings.py`. +- **No global mutable state**: Pass dependencies through services and `bot_data`. + +## Testing Rules + +- Use `pytest` with async support for testing async code. +- Test services and repository implementations independently. +- Use `InMemoryRepository` for unit tests instead of mocking the database. +- Keep tests in the `tests/` directory mirroring the `src/` structure. + +## Common Pitfalls + +- **Permissions**: Always check that the bot has admin permissions before calling `restrict_chat_member` or `ban_chat_member`. +- **Null checks**: `update.effective_chat`, `update.effective_user`, and `update.message` can all be `None`; guard every handler. +- **Global vs per-chat**: Verification and bans operate globally across all tracked chats. Use `register_chat()` to track new chats. +- **Connection pool**: `PostgresRepository` uses `psycopg_pool.AsyncConnectionPool`; always access connections via `async with self._pool.connection()`. +- **Environment variables**: Required vars raise `ValueError` if missing. Optional vars default to `None`. See `.env.example` for the full list. +- **Bot messages are in Italian**: Keep all user-facing strings in `strings.py` in Italian. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..f97dd51ad --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,136 @@ +# CLAUDE.md — Project Specification for python-italy-telegram-bot + + + python-italy-telegram-bot (Electus) + + Official Telegram bot for the Italian Python community groups. + Handles welcome captcha verification, moderation (ban/mute/report), + spam detection, and multi-group management. + + Python 3.14+ + MIT + + + + Python 3.14+ (async/await throughout) + python-telegram-bot >= 22.0 (async API via telegram.ext) + PostgreSQL via psycopg 3 with AsyncConnectionPool; InMemoryRepository fallback + python-dotenv for environment variables + Hatchling build backend; uv for dependency management + Docker multi-stage build; deployed on Fly.io (polling mode) + ruff (linter and formatter) + mypy + pytest + + + + + Layered architecture: Handlers → Services → Repository → Database. + Dependency injection via context.bot_data dictionary populated at startup. + + + + Receive Telegram updates and delegate to services. + Each module exposes a create_*_handlers() factory returning a list of telegram.ext handler objects. + Private async handler functions follow the _handle_* naming convention. + Modules: welcome.py, moderation.py, spam.py, settings.py, id.py, announce.py, ping.py. + + + + Business logic layer. Classes: CaptchaService, ModerationService, SpamDetector. + Depend on AsyncRepository for persistence. Stateless except for repository reference. + + + + Abstract AsyncRepository base class (db/base.py) with two implementations: + - InMemoryRepository (db/in_memory.py) — for development and testing + - PostgresRepository (db/postgres.py) — production with psycopg AsyncConnectionPool + Factory function create_repository() in db/__init__.py selects implementation based on DATABASE_URL. + Domain models (Ban, Mute, Report) are dataclasses in db/models.py. + + + + Settings class loads environment variables. Required vars raise ValueError if missing. + See .env.example for the full list of configuration options. + + + + All user-facing bot messages centralized here, in Italian. + Uses named placeholders for .format() substitution. + + + + Creates ApplicationBuilder, registers handlers via _post_init callback, + initializes repository and services, runs polling loop. + + + + + + Use modern Python type syntax: int | None, list[int], dict[str, Any]. + Do not use Optional or Union from typing. + + + All handlers, service methods, and repository methods must be async def. + + + Every file has a module-level docstring. Functions and classes have one-line docstrings. + + + Use relative imports within the package (from ..services.captcha import CaptchaService). + + + Define private async _handle_* functions. Expose a public create_*_handlers() factory returning list. + + + Wrap Telegram API calls in try/except, log warnings with logger, degrade gracefully. + + + Use logging.getLogger(__name__) per module. + + + All user-facing text lives in strings.py in Italian. Use named placeholders. + + + No global mutable state. Pass dependencies through services and bot_data. + + + + + uv sync --dev + uv run python-italy-bot + uv run ruff check src/ + uv run ruff format src/ + uv run mypy src/ + uv run pytest + + + + pytest with async support + tests/ + + - Test services and repository implementations independently. + - Use InMemoryRepository for unit tests; do not mock the database interface. + - Mirror the src/ directory structure in tests/. + - Keep tests focused and avoid testing Telegram API internals. + + + + + Bot token from @BotFather + PostgreSQL connection string; omit for in-memory + Secret command for captcha verification + Path to rules file + Main group chat ID + Comma-separated local group IDs + External rules page URL + Owner user ID for /announce + + + + Always guard against None for update.effective_chat, update.effective_user, and update.message. + Verification and bans are global across all tracked chats. Use register_chat() for new chats. + PostgresRepository requires async with self._pool.connection() for all DB access. + Bot must have admin permissions to restrict or ban members. + Bot messages are in Italian — keep strings.py consistent. +