Skip to content

Commit f65bf8f

Browse files
CopilotMattiaFailla
andcommitted
Add GitHub Copilot instructions and Claude project specifications
Co-authored-by: MattiaFailla <11872425+MattiaFailla@users.noreply.github.com>
1 parent 193e72e commit f65bf8f

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

.github/copilot-instructions.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Copilot Instructions for python-italy-telegram-bot
2+
3+
## Project Overview
4+
5+
**Electus** is the official Telegram bot for Italian Python community groups.
6+
It handles welcome captcha verification, moderation (ban/mute/report), spam detection, and multi-group management.
7+
Built with `python-telegram-bot` (async), PostgreSQL via `psycopg`, and deployed on Fly.io.
8+
9+
## Tech Stack
10+
11+
- **Language**: Python 3.14+
12+
- **Bot Framework**: python-telegram-bot >= 22.0 (async API)
13+
- **Database**: PostgreSQL via psycopg 3 with async connection pooling; in-memory fallback for dev
14+
- **Config**: python-dotenv for environment variables
15+
- **Build**: Hatchling; dependency management with uv
16+
- **Linting/Formatting**: ruff
17+
- **Type Checking**: mypy
18+
- **Testing**: pytest
19+
20+
## Architecture
21+
22+
Layered architecture with strict separation of concerns:
23+
24+
```
25+
Handlers (telegram.ext) → Services (business logic) → Repository (data access)
26+
```
27+
28+
- **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.
29+
- **Services** (`src/python_italy_bot/services/`): Contain business logic (`CaptchaService`, `ModerationService`). Depend on `AsyncRepository`.
30+
- **Repository** (`src/python_italy_bot/db/`): Abstract `AsyncRepository` base class with `InMemoryRepository` and `PostgresRepository` implementations. Factory function `create_repository()` selects based on `DATABASE_URL`.
31+
- **Models** (`src/python_italy_bot/db/models.py`): Domain dataclasses (`Ban`, `Mute`, `Report`).
32+
- **Config** (`src/python_italy_bot/config.py`): `Settings` class loads all env vars.
33+
- **Strings** (`src/python_italy_bot/strings.py`): Centralized bot message templates.
34+
35+
Dependency injection is done via `context.bot_data` dictionary, populated in `_post_init`.
36+
37+
## Coding Standards
38+
39+
- **Type hints**: Use modern Python type syntax everywhere (`int | None`, `list[int]`), no `Optional` or `Union`.
40+
- **Async/await**: All handlers, services, and repository methods are `async def`.
41+
- **Docstrings**: Module-level docstring on every file. One-line docstrings on functions and classes.
42+
- **Imports**: Use relative imports within the package (`from ..services.captcha import CaptchaService`).
43+
- **Handler pattern**: Define private `async def _handle_*` functions; expose a public `create_*_handlers()` factory returning `list`.
44+
- **Error handling**: Wrap Telegram API calls in `try/except`, log warnings, degrade gracefully.
45+
- **Logging**: Use `logging.getLogger(__name__)` per module.
46+
- **String formatting**: Use f-strings or `.format()` with named placeholders from `strings.py`.
47+
- **No global mutable state**: Pass dependencies through services and `bot_data`.
48+
49+
## Testing Rules
50+
51+
- Use `pytest` with async support for testing async code.
52+
- Test services and repository implementations independently.
53+
- Use `InMemoryRepository` for unit tests instead of mocking the database.
54+
- Keep tests in the `tests/` directory mirroring the `src/` structure.
55+
56+
## Common Pitfalls
57+
58+
- **Permissions**: Always check that the bot has admin permissions before calling `restrict_chat_member` or `ban_chat_member`.
59+
- **Null checks**: `update.effective_chat`, `update.effective_user`, and `update.message` can all be `None`; guard every handler.
60+
- **Global vs per-chat**: Verification and bans operate globally across all tracked chats. Use `register_chat()` to track new chats.
61+
- **Connection pool**: `PostgresRepository` uses `psycopg_pool.AsyncConnectionPool`; always access connections via `async with self._pool.connection()`.
62+
- **Environment variables**: Required vars raise `ValueError` if missing. Optional vars default to `None`. See `.env.example` for the full list.
63+
- **Bot messages are in Italian**: Keep all user-facing strings in `strings.py` in Italian.

CLAUDE.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# CLAUDE.md — Project Specification for python-italy-telegram-bot
2+
3+
<project>
4+
<name>python-italy-telegram-bot (Electus)</name>
5+
<description>
6+
Official Telegram bot for the Italian Python community groups.
7+
Handles welcome captcha verification, moderation (ban/mute/report),
8+
spam detection, and multi-group management.
9+
</description>
10+
<language>Python 3.14+</language>
11+
<license>MIT</license>
12+
</project>
13+
14+
<tech_stack>
15+
<runtime>Python 3.14+ (async/await throughout)</runtime>
16+
<framework>python-telegram-bot >= 22.0 (async API via telegram.ext)</framework>
17+
<database>PostgreSQL via psycopg 3 with AsyncConnectionPool; InMemoryRepository fallback</database>
18+
<config>python-dotenv for environment variables</config>
19+
<build>Hatchling build backend; uv for dependency management</build>
20+
<deployment>Docker multi-stage build; deployed on Fly.io (polling mode)</deployment>
21+
<linting>ruff (linter and formatter)</linting>
22+
<type_checking>mypy</type_checking>
23+
<testing>pytest</testing>
24+
</tech_stack>
25+
26+
<architecture>
27+
<overview>
28+
Layered architecture: Handlers → Services → Repository → Database.
29+
Dependency injection via context.bot_data dictionary populated at startup.
30+
</overview>
31+
32+
<layer name="handlers" path="src/python_italy_bot/handlers/">
33+
Receive Telegram updates and delegate to services.
34+
Each module exposes a create_*_handlers() factory returning a list of telegram.ext handler objects.
35+
Private async handler functions follow the _handle_* naming convention.
36+
Modules: welcome.py, moderation.py, spam.py, settings.py, id.py, announce.py, ping.py.
37+
</layer>
38+
39+
<layer name="services" path="src/python_italy_bot/services/">
40+
Business logic layer. Classes: CaptchaService, ModerationService, SpamDetector.
41+
Depend on AsyncRepository for persistence. Stateless except for repository reference.
42+
</layer>
43+
44+
<layer name="repository" path="src/python_italy_bot/db/">
45+
Abstract AsyncRepository base class (db/base.py) with two implementations:
46+
- InMemoryRepository (db/in_memory.py) — for development and testing
47+
- PostgresRepository (db/postgres.py) — production with psycopg AsyncConnectionPool
48+
Factory function create_repository() in db/__init__.py selects implementation based on DATABASE_URL.
49+
Domain models (Ban, Mute, Report) are dataclasses in db/models.py.
50+
</layer>
51+
52+
<layer name="config" path="src/python_italy_bot/config.py">
53+
Settings class loads environment variables. Required vars raise ValueError if missing.
54+
See .env.example for the full list of configuration options.
55+
</layer>
56+
57+
<layer name="strings" path="src/python_italy_bot/strings.py">
58+
All user-facing bot messages centralized here, in Italian.
59+
Uses named placeholders for .format() substitution.
60+
</layer>
61+
62+
<layer name="entry_point" path="src/python_italy_bot/main.py">
63+
Creates ApplicationBuilder, registers handlers via _post_init callback,
64+
initializes repository and services, runs polling loop.
65+
</layer>
66+
</architecture>
67+
68+
<coding_conventions>
69+
<rule name="type_hints">
70+
Use modern Python type syntax: int | None, list[int], dict[str, Any].
71+
Do not use Optional or Union from typing.
72+
</rule>
73+
<rule name="async">
74+
All handlers, service methods, and repository methods must be async def.
75+
</rule>
76+
<rule name="docstrings">
77+
Every file has a module-level docstring. Functions and classes have one-line docstrings.
78+
</rule>
79+
<rule name="imports">
80+
Use relative imports within the package (from ..services.captcha import CaptchaService).
81+
</rule>
82+
<rule name="handler_pattern">
83+
Define private async _handle_* functions. Expose a public create_*_handlers() factory returning list.
84+
</rule>
85+
<rule name="error_handling">
86+
Wrap Telegram API calls in try/except, log warnings with logger, degrade gracefully.
87+
</rule>
88+
<rule name="logging">
89+
Use logging.getLogger(__name__) per module.
90+
</rule>
91+
<rule name="strings">
92+
All user-facing text lives in strings.py in Italian. Use named placeholders.
93+
</rule>
94+
<rule name="no_global_state">
95+
No global mutable state. Pass dependencies through services and bot_data.
96+
</rule>
97+
</coding_conventions>
98+
99+
<commands>
100+
<command name="install">uv sync --dev</command>
101+
<command name="run">uv run python-italy-bot</command>
102+
<command name="lint">uv run ruff check src/</command>
103+
<command name="format">uv run ruff format src/</command>
104+
<command name="typecheck">uv run mypy src/</command>
105+
<command name="test">uv run pytest</command>
106+
</commands>
107+
108+
<testing>
109+
<framework>pytest with async support</framework>
110+
<directory>tests/</directory>
111+
<guidelines>
112+
- Test services and repository implementations independently.
113+
- Use InMemoryRepository for unit tests; do not mock the database interface.
114+
- Mirror the src/ directory structure in tests/.
115+
- Keep tests focused and avoid testing Telegram API internals.
116+
</guidelines>
117+
</testing>
118+
119+
<environment>
120+
<variable name="TELEGRAM_BOT_TOKEN" required="true">Bot token from @BotFather</variable>
121+
<variable name="DATABASE_URL" required="false">PostgreSQL connection string; omit for in-memory</variable>
122+
<variable name="CAPTCHA_SECRET_COMMAND" required="false" default="python-italy">Secret command for captcha verification</variable>
123+
<variable name="CAPTCHA_FILE_PATH" required="false" default="assets/regolamento.md">Path to rules file</variable>
124+
<variable name="MAIN_GROUP_ID" required="false">Main group chat ID</variable>
125+
<variable name="LOCAL_GROUP_IDS" required="false">Comma-separated local group IDs</variable>
126+
<variable name="RULES_URL" required="false">External rules page URL</variable>
127+
<variable name="BOT_OWNER_ID" required="false">Owner user ID for /announce</variable>
128+
</environment>
129+
130+
<pitfalls>
131+
<pitfall>Always guard against None for update.effective_chat, update.effective_user, and update.message.</pitfall>
132+
<pitfall>Verification and bans are global across all tracked chats. Use register_chat() for new chats.</pitfall>
133+
<pitfall>PostgresRepository requires async with self._pool.connection() for all DB access.</pitfall>
134+
<pitfall>Bot must have admin permissions to restrict or ban members.</pitfall>
135+
<pitfall>Bot messages are in Italian — keep strings.py consistent.</pitfall>
136+
</pitfalls>

0 commit comments

Comments
 (0)