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.
+