Files
duty-teller/docs/architecture.md
Nikolay Tatarinov 28b769b9d6
All checks were successful
CI / lint-and-test (push) Successful in 24s
refactor: update group duty pin functionality and documentation
- Changed the behavior of the group duty pin feature to send a new message, unpin the old one, and pin the new one instead of editing the existing message. This ensures the pinned message is always fresh.
- Updated the `DUTY_PIN_NOTIFY` configuration description in the documentation to reflect the new message handling approach.
- Revised the architecture documentation to clarify the updated group duty pin process.
- Enhanced tests to verify the new behavior of the group duty pin functionality, ensuring proper message handling and scheduling.
2026-02-24 11:49:07 +03:00

68 lines
3.8 KiB
Markdown

# Architecture
High-level architecture of Duty Teller: components, data flow, and package relationships.
## Components
- **Bot** — [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) v22 (Application API). Handles commands and group messages; runs in polling mode.
- **FastAPI** — HTTP server: REST API (`/api/duties`, `/api/calendar-events`, `/api/calendar/ical/{token}.ics`) and static miniapp at `/app`. Runs in a separate thread alongside the bot.
- **Database** — SQLAlchemy ORM with Alembic migrations. Default backend: SQLite (`data/duty_teller.db`). Stores users, duties (with event types: duty, unavailable, vacation), group duty pins, calendar subscription tokens.
- **Duty-schedule import** — Two-step admin flow: handover time (timezone → UTC), then JSON file. Parser produces per-person date lists; import service deletes existing duties in range and inserts new ones.
- **Group duty pin** — In groups, the bot can pin the current duty message; time/timezone for the pinned text come from `DUTY_DISPLAY_TZ`. Pin state is restored on startup from the database. When the duty changes on schedule, the bot sends a new message, unpins the previous one and pins the new one; if `DUTY_PIN_NOTIFY` is enabled (default), pinning the new message triggers a Telegram notification for members. The first pin (bot added to group or `/pin_duty`) is always silent.
## Data flow
- **Telegram → bot**
User/group messages → handlers → services or DB. Handlers use `duty_teller.services` (e.g. import, group duty pin) and `duty_teller.db` (repository, session). Messages use `duty_teller.i18n` for Russian/English.
- **Miniapp → API**
Browser opens `/app`; frontend calls `GET /api/duties` and `GET /api/calendar-events` with date range. FastAPI dependencies: DB session, Telegram initData validation (`require_miniapp_username`), date validation. Data is read via `duty_teller.db.repository`.
- **Import**
Admin sends JSON file via `/import_duty_schedule`. Handler reads file → `duty_teller.importers.duty_schedule.parse_duty_schedule()``DutyScheduleResult``duty_teller.services.import_service.run_import()` → repository (`get_or_create_user_by_full_name`, `delete_duties_in_range`, `insert_duty`).
- **Personal calendar ICS**
`GET /api/calendar/ical/{token}.ics` uses the secret token only (no Telegram auth); repository resolves user by token and returns duties; `personal_calendar_ics.build_personal_ics()` produces ICS bytes.
## Package layout
```mermaid
flowchart LR
subgraph entry
main[main.py / duty-teller]
end
subgraph duty_teller
run[run.py]
config[config.py]
handlers[handlers]
api[api]
db[db]
services[services]
importers[importers]
i18n[i18n]
utils[utils]
end
main --> run
run --> config
run --> handlers
run --> api
handlers --> services
handlers --> db
handlers --> i18n
api --> db
api --> config
services --> db
services --> importers
importers --> .
```
- **handlers** — Telegram command and message handlers; call `services` and `db`, use `i18n` for user-facing text.
- **api** — FastAPI app, dependencies (auth, DB session, date validation), calendar ICS builders; uses `db.repository` and `config`.
- **db** — Models, session (`session_scope`), repository (CRUD for users, duties, pins, calendar tokens), schemas for API.
- **services** — Business logic (import, group duty pin); receive DB session from caller, use `importers` for parsing.
- **importers** — Duty-schedule JSON parser; no DB access, returns structured result.
- **i18n** — Translations and language detection (ru/en) for bot and API.
- **utils** — Shared helpers (dates, user, handover).
See [Project layout](../README.md#project-layout) in README for file-level details.