Skip to content

# Architecture

High-level architecture of Duty Teller: components, data flow, and package relationships.

## Components

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

## 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

    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 in README for file-level details.