Перейти до вмісту

Abitly Telegram bot

ПолеЗначення
ПродуктAbitly.org
ТипBot
Статус🟢 prod (перевірено: ECS-сервіс abitly-prod-tg-bot 1/1 RUNNING, таск HEALTHY)
Власник@Vladbandurin

Telegram-бот Abitly.org (@abitlybot) для абітурієнтів: моніторинг вступу — пропозиції (офери), бали й рейтинг, шанси на вступ, дні відкритих дверей. Щодня о 07:00 (Київ) надсилає нагадування про дні відкритих дверей (за 1 і 3 дні); адмінам — розсилки.

Репозиторій та рантайм

Section titled “Репозиторій та рантайм”
Репоabitly-org/abitly-tg-bot-v3 (main)
Стекaiogram 3 · SQLAlchemy 2.0 (async) · asyncpg · Redis · APScheduler · aiohttp · pydantic-settings · aiolimiter (Python 3.12)
ХостингAWS ECS Fargate (eu-central-1), кластер abitly-prod-backend, сервіс abitly-prod-tg-bot (1 таск, 256 CPU / 512 MB)
Контейнериbot (образ ECR abitly/tg-bot) + redis:7-alpine sidecar (FSM + link-токени на localhost:6379)
ДеплойGitHub Actions (OIDC, без статичних AWS-ключів) → ECR abitly/tg-bot → ECS, push у main (.github/workflows/deploy-bot.yml)
РежимLong polling, строго один інстанс (ADR 0001)
HTTP healthcheckGET /healthcheck на :3000 (aiohttp; ECS health-check + readiness)
CronAPScheduler 0 7 * * * (Europe/Kyiv) → нагадування про дні відкритих дверей
Bot usernameprod — @abitlybot · dev/staging — @abtltestbot (сервіс abitly-dev-tg-bot, dev-БД)
СекретиSSM Parameter Store: /abitly-prod/tg-bot/BOT_TOKEN, …/DB_PASSWORD (SecureString)

User-facing (меню setMyCommands, UI українською):

КомандаЩо робить
/startголовне меню + привітання; deep-links: link_<token> (прив’язка акаунту), chances_<spec>_<score> (шанси на вступ), open_day_university_<id> (дні відкритих дверей ЗВО)
/myprofileпрофіль і бали
/myoffersвідстежувані пропозиції (ліміт моніторингу — 5)
/statisticsстатистика по оферу (позиції: середня / мін / макс)
/myopendaysмої дні відкритих дверей (пагінація)
/opendayfiltersфільтри сповіщень (за регіоном)
/notificationsналаштування підписок
/helpу меню — «Довідка», але хендлера немає → no-op (відоме обмеження, див. нижче)
(текст)вставлення URL офера (abitly.org/uk/offers/result/<id> або vstup.edbo.gov.ua/offer/<id>) → бали + рейтинг (catch-all, реєструється останнім)

Admin (за ADMIN_IDS):

КомандаЩо робить
/broadcastрозсилка всім користувачам
/notifyOpenDaysUpdateфан-аут оновлення дня відкритих дверей (матч за регіональним фільтром — ADR 0008)

Масова відправка — MessageSender: обмежена конкурентність (деф. 5), глобальний rate-cap (деф. 30/с, aiolimiter), retry на 429/TelegramRetryAfter, відкидає заблокованих користувачів (TelegramForbiddenError) без зупинки черги (ADR 0005).

UML (use-case, state, class) + sequence-діаграми всіх user-сценаріїв бота (привітання, deep-links, моніторинг, дні відкритих дверей, сповіщення, прив’язка акаунту, адмін-розсилки) → Telegram-бот: user-сценарії. Базовий крос-сервісний потік — data-flows.

  • Залежить від: Abitly API (abitly-api-v2 — власник схеми) · Postgres схема abitly (read-mostly, через SQLAlchemy 2.0 reflection, без міграцій — ADR 0006; camelCase-колонки збережено, бо їх генерує TypeORM бекенду) · sidecar-Redis (FSM + link-токени) · Telegram API
  • Власні дані: схема telegram на тому ж кластері (telegram.update_log / telegram.sent_log — логування вхідних/вихідних, ADR 0009/0010)
  • Domain entities (з коду): BaseUser, TelegramUser, Offer, OpenDay, Speciality + reference-таблиці та monitoring-зв’язки
  • Від нього залежать:

Значення — у secret-сховищі, ніколи в git. SecureString-секрети — у SSM /abitly-{prod,dev}/tg-bot/; решта — plain env у ECS task-definition. Повний індекс назв — environments.

КатегоріяЗмінні (* — секрет у SSM)
TelegramBOT_TOKEN*, ADMIN_IDS, ACCOUNT_LINK_URL, SEND_MAX_CONCURRENCY, SEND_RATE_PER_SECOND, DEFAULT_TELEGRAM_MESSAGE_MAX_RETRY, DEFAULT_TELEGRAM_MESSAGE_RETRY_DELAY_MS
PostgresDB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD*, DB_NAME, DB_SCHEMA (abitly), DB_SSL (true), DB_CA_FILE (RDS CA bundle)
RedisREDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_DB
ЛогуванняLOG_LEVEL, LOG_SCHEMA (telegram), LOG_MESSAGES_ENABLED (true)
RuntimePORT (3000), TZ (Europe/Kyiv)
Terminal window
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"
cp .env.example .env # заповнити BOT_TOKEN
python -m abitly_bot # polling + healthcheck на $PORT
ruff check . && mypy src && pytest # offline quality-gate
  • Deploy: push у main (paths src/**, Dockerfile, pyproject.toml, uv.lock) → GitHub Actions збирає образ, пушить у ECR abitly/tg-bot:<sha> + :latest, рендерить нову task-definition і деплоїть у ECS з wait-for-service-stability. Concurrency cancel-in-progress: false — ніколи два деплої одночасно (один інстанс). Деталі → deploy-pipeline.
  • Rollback: ECS → попередня ревізія task-definition (або redeploy попереднього :<sha>-образу з ECR). Доступ — aws CLI (профіль abitly) / ECS Exec (enable_execute_command=true). Загальний процес → deploy-rollback.
  • CloudWatch Logs /abitly-prod/tg-bot (30 дн; stream-prefix bot / redis); dev — /abitly-dev/tg-bot (14 дн).
  • Стан сервісу: aws ecs describe-services --cluster abitly-prod-backend --services abitly-prod-tg-bot --profile abitly (див. MCP-реєстр → AWS).
  • Доступ у контейнер: ECS Exec.
  • Самологування в БД: telegram.update_log / telegram.sent_log.
  • 409 Conflict / TelegramConflictError → два getUpdates на один токен (напр. локальний запуск + prod, або два сервіси з тим самим токеном). Тримати рівно один інстанс (ADR 0001). → service-down
  • Бот не відповідає → ECS service/task health + CloudWatch. → service-down
  • Помилки БД / TLS → перевірити DB_CA_FILE (RDS CA) і доступ security-group до RDS :5432. → db-issues
  • Schema drift → reflection-тест падає, якщо схему abitly змінив бекенд; синхронізувати моделі (ADR 0006).

Власна документація бота

Section titled “Власна документація бота”

Канонічні дизайн-доки й ADR живуть у самому репо: Astro + Starlight docs-site (docs-site/, деплой на Cloudflare Pages, проєкт abitly-bot-docs) — overview, architecture, data-model, runtime-flows, operations, 10 ADR. Цей хаб дає крос-сервісний контекст; за деталями внутрішнього дизайну — у docs-site бота.

  • Прив’язка акаунту: узгодити Redis (бекенд мінтить abitly:link:* у спільний Redis ↔ бот має sidecar-Redis) і підтвердити mint-ендпоінт у abitly-api-v2.
  • /help — no-op (команда в меню без хендлера).
  • Prod Terraform (infra/terraform/) без backend.tf → схоже на local state (dev — на S3 remote). Імпортувати в remote state.
  • abitly-dev-tg-bot (dev-сервіс) керується out-of-band, не цим Terraform → terraform import.