Skip to content

Latest commit

 

History

History
204 lines (132 loc) · 11.1 KB

File metadata and controls

204 lines (132 loc) · 11.1 KB

AGENTS.md

What this is

Telegram bot (aiogram 3.13.1) that hosts users' PHP Telegram bots on a pool of Cloudflare Workers. Users upload a PHP file + bot token; the bot uploads the file to a Worker, registers a Telegram webhook via Cloudflare KV routing, and the Worker runs the PHP on incoming updates. Points economy: 1 point = 48 h of hosting, earned via referrals.

Run / verify

pip install -r requirements.txt
cp .env.example .env   # fill in BOT_TOKEN, WORKER_URL, INTERNAL_SECRET
python main.py

No test suite, no linter, no formatter, no CI. Sanity-check Python syntax after edits:

python -m compileall core features main.py config.py

Docker / Railway (railway.toml, Dockerfile) just runs python main.py. The Dockerfile is python:3.11-slim with pip install --no-cache-dir.

Environment variables (config.py)

Required to start: BOT_TOKEN (program exits if missing). The rest are read at import time.

  • BOT_TOKEN — Telegram bot token
  • WORKER_URL, INTERNAL_SECRET — default Worker registered on first boot if DATABASE_URL not set to postgres
  • DATABASE_URL — if starts with postgresql, uses asyncpg; otherwise SQLite at data/hosting.db (created on demand, dir auto-created)
  • ADMIN_IDS — comma-separated Telegram user IDs. If empty, is_admin() returns True for everyone (see features/admin/handlers.py:33)
  • CF_ACCOUNT_ID, CF_KV_ID, CF_API_TOKEN, CF_WEBHOOK_BASE — Cloudflare KV routing for webhooks. If any missing, register_routing logs a warning and no-ops.

Architecture

main.py                  entry: init_db → register default worker → start polling + 3 background loops
core/bot.py              builds Bot/Dispatcher, wires routers + middlewares (order matters)
core/db.py               DB-agnostic layer; branches on global `db_type` ("sqlite"|"postgres")
core/middleware.py       AuthMiddleware (subscription gate) + LoggingMiddleware
core/worker.py            httpx client to Workers; load balancer + health-check loop
core/scheduler.py        expiry + DB-backup background loops
core/keyboards.py        all InlineKeyboardMarkup builders (single source of truth)
core/referrals.py        user creation + referral processing
core/validators.py        PHP file + bot token validation
features/<name>/         one folder per feature: handlers.py + messages.py + __init__.py (exports `router`)
data/                    SQLite DB + uploaded PHP files (`data/bots/<user_id>_bot.php`)

Router registration order (core/bot.py)

  1. start, 2. invite, 3. forced_channels, 4. deploy, 5. manage, 6. admin

Order matters because handlers in earlier routers can swallow updates. Add new feature routers deliberately and check F.data.startswith(...) conflicts.

Middleware order (core/bot.py)

LoggingMiddleware registered before AuthMiddleware on both dp.message and dp.callback_query. AuthMiddleware injects data["user_id"] (so handlers can take user_id: int as a parameter) and short-circuits banned users / non-subscribed users. Admins bypass the subscription gate.

Database

DB-agnostic pattern: every function in core/db.py branches on the module-global db_type. When adding a query, write both dialects (asyncpg uses $1, $2; aiosqlite uses ?). Booleans: SQLite stores INTEGER 0/1, Postgres stores BOOLEAN — code uses bool(user.get("is_banned")) to normalize. Booleans across rows are returned via _row_to_dict (SQLite) / dict(row) (postgres).

pool is a module-global: an asyncpg.Pool for postgres, a single aiosqlite.Connection for sqlite. Note aiosqlite.Connection is not a pool but a connection — it's the global handle for the SQLite path.

Schema is created in core/db.py:init() for both backends. Schema changes: edit both branches; for existing SQLite DBs, the _ensure_column helper adds columns idempotently.

Tables: workers, bots, users, forced_channels.

Callback data conventions

Pattern: feature:action[:param] (string-based, no aiogram CallbackData factories):

  • main:deploy, main:bots, main:invite, main:help
  • deploy:new, deploy:cancel
  • manage:list, manage:view:<bot_id>, manage:back, manage:start:<id>, manage:stop:<id>, manage:restart:<id>, manage:delete:<id>, manage:cancel:<id>, manage:confirm:stop:<id>, manage:confirm:delete:<id>
  • channels:list, channels:view:<id>, channels:add, channels:del:<id>, channels:confirm_del:<id>, channels:check_sub

deploy:cancel is reused across deploy and admin add-worker flows — both have handlers, each checks its own FSM state before clearing.

All keyboards are in core/keyboards.py. The main menu (main_keyboard()) is an InlineKeyboardMarkup — no reply keyboards. Do not introduce ReplyKeyboardMarkup; that pattern was deliberately removed.

Bots / Workers flow

Deploy (features/deploy/handlers.py):

  1. FSM: waiting_file → upload .php to data/bots/<user_id>_bot.phpwaiting_token
  2. Pick worker via select_best_worker() (lowest bots_count among status='active')
  3. POST file to {worker}/deploy with X-Internal-Secret header
  4. Register Cloudflare KV routing user_id → worker_url
  5. setWebhook to {CF_WEBHOOK_BASE}/webhook/{user_id} with the webhook_secret returned by the worker
  6. Insert into bots, increment worker bots_count, consume_point
  7. On error, notify all admins with the error detail

Worker contract: POST /deploy (multipart: file + user_id + bot_token), POST /stop (JSON: user_id), GET /status/{user_id}, GET /health. Responses are JSON; success means {"status": "ok", ...} with optional webhook_secret.

Restart / start use data/bots/<user_id>_bot.php as the file path — restarting a user requires the file to still be on disk. Deleting the bot row does not delete the file.

Points / expiry

  • consume_point is atomic via WHERE points >= 1; returns bool.
  • bots.expires_at is set to now + 48 hours on deploy/start/restart.
  • core/scheduler.py:expiry_check_loop runs every 600 s, marks expired bots stopped, notifies the user.

Background loops (main.py)

  • health_check_loop(60) — pings every worker; flips status between active/dead.
  • expiry_check_loop(bot, 600) — see above.
  • backup_loop(bot, ADMIN_IDS, 86400) — sends data/hosting.db to all admins once a day. SQLite-only; no-op on postgres (file won't exist).

User-facing language

All bot messages are in Arabic (HTML parse mode). Inline Arabic comments in source. Keep messages Arabic when editing features/*/messages.py.

Agent Workflow

This project is developed through an agent-orchestration workflow. The primary agent acts as an intelligent coordinator rather than directly implementing changes.

Core rule

Do not perform implementation work, modify source code, or make changes to the project unless the user explicitly instructs you to do so.

The primary agent should act as an orchestrator:

  • Understand the user's request and determine what kind of agent work is needed.
  • Delegate implementation and exploration work to the appropriate specialized agent.
  • Provide agents with enough context and precise instructions to produce reliable results.
  • Do not make assumptions about implementation details when delegation is more appropriate.
  • The agent may read relevant parts of the codebase when necessary to improve its understanding or to provide better instructions to another agent. Reading code for context is allowed; making changes without explicit authorization is not.

Available agents

The workflow will normally use the following agents:

@General

Use @General when the user asks for an agent to perform an actual implementation task.

The coordinator should provide @General with a precise, comprehensive, and self-contained task description. The description should include:

  • The exact objective and expected behavior.
  • Relevant context about the existing architecture.
  • Important constraints and project conventions.
  • Files or areas that are likely relevant.
  • Any required edge cases.
  • What should and should not be changed.
  • How the work should be verified.

Do not give vague instructions such as "implement this feature." Give the agent enough information to reason about the task and produce a complete implementation.

@Explore

Use @Explore when the user asks for investigation, codebase exploration, research, or planning before implementation.

The expected workflow is:

  1. Launch @Explore with a precise description of what needs to be investigated.
  2. Have it inspect the relevant parts of the codebase.
  3. Have it produce a Markdown document containing the proposed implementation plan.
  4. Review/use that plan as the basis for the implementation phase.
  5. When the user requests implementation, launch @General with the plan and all necessary context to build it.

Do not implement the planned changes yourself merely because @Explore has produced a plan. Wait for explicit user instruction to proceed with implementation.

Required analyzer verification

Any agent that modifies or implements code must run the project's analyzer after completing its work.

The exact command is:

cd ~/Workspaces/bot-creator && pnpm analyze ~/Workspaces/php-hosting

This analyzer is an important part of the project's verification workflow. It performs an in-depth analysis of the codebase and reports discovered problems back to the agent.

The implementation agent must:

  1. Complete the requested implementation.
  2. Run the analyzer using the exact command above.
  3. Carefully inspect the analyzer output.
  4. Fix any issues reported by the analyzer that are caused by the agent's changes.
  5. Run the analyzer again when necessary to confirm the fixes.
  6. Only consider the implementation complete after the analyzer reports that the relevant issues have been resolved.

The analyzer is mandatory and must not be skipped merely because the changes appear simple or because the agent believes the code is already correct.

Commit policy

An agent may create a Git commit only after completing the required verification workflow, including running the project analyzer and addressing the relevant issues it reports.

Do not commit unverified implementation work.

Delegation quality

The quality of the result depends heavily on the quality of the instructions given to delegated agents. Whenever launching @General or @Explore, provide a detailed prompt rather than a short command.

For implementation tasks, the coordinator should make the delegated task as self-contained as possible so the implementation agent can work independently without repeatedly asking for clarification about information that could have been provided in the initial prompt.

Known gaps / gotchas

  • features/invite/handlers.py uses F.data and CallbackQuery but doesn't import them — adds will fail at module load if those decorators are touched. Fix the imports if you edit that file.
  • MemoryStorage for FSM (in-process). Multi-instance deploys will break user state.
  • data/ is gitignored but data/bots/ accumulates uploaded PHP files — no cleanup on bot deletion.
  • No tests, no CI, no README. Do not invent them unless asked.