# 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` (built from `webapp-next/`, Next.js static export). 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.
