All checks were successful
CI / lint-and-test (push) Successful in 24s
- 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.
68 lines
3.8 KiB
Markdown
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.
|