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

3.8 KiB

Architecture

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

Components

  • Botpython-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()DutyScheduleResultduty_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.