Production-oriented MVP for Telegram-based support automation:
ISSUE_NEWREPLACE_DAMAGEDTOPUP
Stack:
- Python 3.12
- FastAPI
- aiogram v3
- async SQLAlchemy 2.x
- PostgreSQL + Alembic
- Docker / docker-compose
Control Bot(group): task management, status actions, PDS copy actions.Intake Bot(DM): one-time token intake forms for guest-related flows.API: health and task retrieval endpoints.Services: state machine, invite token lifecycle, payload generation, audit.
/Users/andrejeliseev/Documents/TelegramBotForSupport/app— app code/Users/andrejeliseev/Documents/TelegramBotForSupport/migrations— Alembic/Users/andrejeliseev/Documents/TelegramBotForSupport/tests— pytest suite
Create .env from .env.example.
Required vars:
DATABASE_URL=postgresql+asyncpg://tp_user:tp_pass@db:5432/tp_botCONTROL_BOT_TOKEN=<telegram token>INTAKE_BOT_TOKEN=<telegram token>CONTROL_GROUP_ID=<telegram group id>OWNER_TELEGRAM_ID=<telegram id of bot owner for auto-admin bootstrap>INTAKE_BOT_USERNAME=<intake bot username without @>INVITE_EXPIRES_HOURS=24MTG_ROTATION_TARGETS=<name|ssh_target|config_path|service_name;...>MTG_ROTATION_FRONT_DOMAIN=google.comMTG_ROTATION_TIMEOUT_SEC=45MTG_ROTATION_SSH_KEY_PATH=/root/.ssh/id_ed25519
Access bootstrap:
- If
OWNER_TELEGRAM_IDis set, this user is auto-created asADMINon first command. - No manual SQL insert is required for owner access.
Control bot access commands:
/whoami— show your telegram_id and current role./grant <telegram_id> <ADMIN|SYSADMIN>— grant or update role (ADMIN only)./revoke <telegram_id>— remove user access (ADMIN only; owner cannot be revoked by command)./help— show all available control bot commands./menu— open persistent button menu in chat (Russian labels)./rotaciya_proxy— rotate MTG secret on configured servers and send new proxy links to requester's DM.
MTG rotation targets format:
MTG_ROTATION_TARGETS=wolf|root@100.120.178.115|/etc/mtg.toml|mtg;tw|root@100.97.163.69|/etc/mtg.toml|mtgMTG_ROTATION_SSH_KEY_PATHshould point to private key file available tocontrol_botprocess.
- Open
@BotFather. - Create 2 bots:
- control bot (for operators group)
- intake bot (for guest private chat)
- Put tokens into
.env. - Add control bot to operator group and grant rights to send messages.
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"Run migrations:
alembic upgrade headRun API:
uvicorn app.main:app --reloadRun Control Bot:
python -m app.bots.control_botRun Intake Bot:
python -m app.bots.intake_botdocker-compose up --buildMigrate DB in container:
docker-compose exec api alembic upgrade headcd /opt/tpbot
docker compose pull
docker compose up -d --build
docker compose exec api alembic upgrade head
docker compose ps
curl -sv http://127.0.0.1:8000/health
curl -sv http://127.0.0.1:8000/tasks/activeExpected:
/health->200with{"status":"ok","db":"up"}/tasks/active->200(empty list[]is valid)
Workflows:
.github/workflows/ci.yml— runspytest -qon push/PR..github/workflows/deploy.yml— auto-deploys after successful CI onmain.
Required GitHub repository secrets:
DEPLOY_HOST— server host (e.g.100.93.18.31)DEPLOY_USER— SSH user (e.g.elka)DEPLOY_SSH_KEY— private SSH key for deploy userDEPLOY_PATH— project path on server (default/opt/tpbot)
Deploy job runs:
git pull --ff-onlydocker compose up -d --build api control_bot intake_botdocker exec tpbot-api-1 alembic upgrade head- health checks for
/healthand/tasks/active
If /health is 503 or /tasks/active is 500:
cd /opt/tpbot
docker logs --tail=300 tpbot-api-1
docker inspect tpbot-api-1 --format '{{range .Config.Env}}{{println .}}{{end}}' | grep -E 'DATABASE_URL|POSTGRES_'
docker inspect tpbot-db-1 --format '{{range .Config.Env}}{{println .}}{{end}}' | grep -E 'POSTGRES_|PGDATA'
docker exec tpbot-api-1 alembic current
docker exec tpbot-api-1 alembic upgrade head
curl -sv http://127.0.0.1:8000/health
curl -sv http://127.0.0.1:8000/tasks/activeStatuses:
CREATEDDATA_COLLECTEDIN_PROGRESSDONE_BY_SYSADMINCONFIRMEDCLOSEDCANCELLED
Allowed transitions enforced in /Users/andrejeliseev/Documents/TelegramBotForSupport/app/services/state_machine.py.
For ISSUE_NEW and REPLACE_DAMAGED:
- unique UUID token
- token has
expires_at - token can be used only once (
used_at) - deep-link:
https://t.me/<intake_bot>?start=<token>
GET /healthGET /tasks/{id}GET /tasks/active
Schema version: pds-assist-v1
Implemented in /Users/andrejeliseev/Documents/TelegramBotForSupport/app/services/pds_payload_service.py:
- deterministic clean JSON
- no null fields
- card numbers preserved as strings
- phone normalization to
+7XXXXXXXXXXwhere possible - operation-specific payload blocks
- optional hardening fields:
ui_hintsoperator_confirm_requiredhelper_target
Run:
pytest -qIncluded:
test_issue_new_creation.pytest_invite_token_usage.pytest_status_transitions.pytest_pds_payload_generation.pytest_task_transition_idempotency.pytest_permissions.pytest_validation_service.py
Real Telegram end-to-end scenario:
tests/e2e/test_telegram_issue_new_flow.py
This test is disabled by default and runs only when:
E2E_ENABLE=1TELEGRAM_API_IDTELEGRAM_API_HASHE2E_TELEGRAM_SESSION(Telethon StringSession)E2E_CONTROL_BOT_USERNAMEE2E_INTAKE_BOT_USERNAME
Run only e2e:
E2E_ENABLE=1 pytest -q -m e2e