Skip to main content

Architecture Overview

System Diagram​

Single-Process Architecture​

The bot runs as a single process orchestrated by main.py. The async main() function starts components in a specific order:

  1. Database initialization
  2. Survey engine loading
  3. Telegram bot (polling mode)
  4. APScheduler for timed delivery
  5. FastAPI HTTP server

Shutdown reverses this order to ensure clean teardown.

Key Components​

Survey Engine (src/survey_engine.py)​

Loads YAML survey definitions from src/surveys/, resolves composite surveys (which reference other surveys via includes), and supports per-section randomization of question order.

Database (src/database.py)​

Uses aiosqlite with WAL mode for concurrent read access. Key tables:

  • participants -- enrolled study participants
  • surveys -- survey metadata
  • schedules -- cron-based delivery schedules
  • survey_sessions -- active/completed/expired session tracking
  • responses -- individual question responses
  • cognitive_results -- results from cognitive assessment tasks

Scheduler (src/scheduler.py)​

APScheduler handles periodic survey delivery using cron-based schedules. Surveys are pushed to participants at configured times.

API (src/api.py)​

FastAPI REST API with all endpoints under /api/v1/. Authentication uses Authorization: Bearer {API_KEY} header (except /health and /cognitive/complete which uses one-time tokens).

Telegram Bot (src/bot.py)​

Built on python-telegram-bot with handler groups for priority-based message routing:

  • Group 0: ConversationHandler (signup flow), commands, callback query handlers
  • Group 1: Survey message handler (separate group to avoid interfering with ConversationHandler)
  • Group 2: Fallback handler

WebSocket API (src/api_webapp.py)​

WebSocket transport for the PWA frontend. Handles participant registration, WebSocket connections, and survey session management using the same survey logic as the Telegram handler.

Survey Delivery Model​

Surveys are pushed to participants by the scheduler or API -- they are not requested by users. Session state is tracked in the survey_sessions table using a state machine:

active → completed
active → expired

Dependency Injection​

Multiple modules use a set_globals() pattern for dependency injection. These globals must be set before the module is used, which is why main.py enforces a strict startup order.

Callback Data Encoding​

Telegram callback data uses the format s{8-char-session-id}_{payload} (e.g., s1a2b3c4d_3). The 8-character prefix is a truncated uuid4().hex[:8]. This keeps callback data within Telegram's 64-byte limit.