A single Cloudflare Worker that runs one factory bot (controlled only by you) plus unlimited child bots, each entirely defined by a JSON action DSL you upload through the factory bot. Ever[...]
This is a from-scratch Workers reimplementation of an earlier python-telegram-bot long-polling project, preserving the same powerful DSL philosophy while re-deriving every mechanism for the serv[...]
- π€ Unlimited Child Bots β One factory, infinite possibilities
- π JSON DSL β Define bot behavior with simple, expressive action declarations
- β‘ Webhook-Driven β No polling loops, instant response times
- π Military-Grade Security β AES-GCM encryption at rest, sandboxed evaluators
- π TON Wallet Integration β Built-in TON Connect v2 and signing helpers
- π§ AI-Powered β Workers AI integration for intelligent bot generation
- π GitHub Actions Gateway β Control workflows from Telegram
- π« Impossible to Pwn β No
shell, noeval, no RCE vectors
./setup.shThe script will automatically:
- β
Check for / install
wranglerand log you in - β
Create the
BOT_KVKV namespace (prod + preview) and patchwrangler.toml - β
Prompt for your Telegram numeric user id and write it into
wrangler.toml - β
Prompt for and set the two Worker secrets (
SECRET_PASSPHRASE,TELEGRAM_WEBHOOK_SECRET) - β Deploy the Worker
- β
Register your factory bot and store it in KV as
bot:factory
After that, open a chat with your factory bot on Telegram and send /start.
npm install
npx wrangler login
npx wrangler kv namespace create BOT_KV
npx wrangler kv namespace create BOT_KV --preview
# paste the two ids into wrangler.toml under [[kv_namespaces]]
# edit wrangler.toml: set FACTORY_OWNER_ID to your numeric Telegram user id
# (get it from @userinfobot on Telegram)
npx wrangler secret put SECRET_PASSPHRASE
npx wrangler secret put TELEGRAM_WEBHOOK_SECRET
# (any long random strings; TELEGRAM_WEBHOOK_SECRET must be 1-256 chars,
# A-Z a-z 0-9 _ - only, per Telegram's setWebhook requirements)
npx wrangler deploy
# Register the factory bot itself (one-time):
curl -s "https://api.telegram.org/bot<FACTORY_TOKEN>/setWebhook" \
-d "url=https://<your-worker>.workers.dev/hook/factory" \
-d "secret_token=<same value you put in TELEGRAM_WEBHOOK_SECRET>"
# Put the factory bot's record into KV:
npx wrangler kv key put --binding=BOT_KV "bot:factory" \
'{"botId":"factory","token":"<FACTORY_TOKEN>","ownerId":"<your-id>","visibility":"private","createdAt":"2026-07-31T00:00:00Z"}'Open your factory bot on Telegram and send /help for the full command list. Typical flow:
/newbot 123456:AA... β validate token, then tap Public/Private
/setconfig mychildbot β attach examples/simple-public-bot.json
Or define it inline:
/json mychildbot {"version":1,"commands":[{"command":"start","actions":[{"type":"send_message","text":"hi {user.first_name}"}]}]}
Pre-built examples in examples/:
| Config | Purpose |
|---|---|
simple-public-bot.json |
Dice, polls, and ask-based name capture |
price-tracker-bot.json |
Real-time data requests and margin calculator |
interface-anything-bot.json |
Generic Telegram API calls for unlimited flexibility |
ton-wallet-bot.json |
TON wallet connections and signing flows |
Store API keys, tokens, and sensitive data per-bot, encrypted at rest with AES-GCM:
/set_secret mychildbot
> (bot asks for the name) API_KEY
> (bot asks for the value) sk-abc123...
β
Saved secret API_KEY for mychildbot. I deleted your message containing the value.
Reference in your config with {secrets.API_KEY} β resolved server-side only, never exposed unless you explicitly include it.
Enable GitHub workflow control directly from Telegram:
/set_secret factory β GITHUB_TOKEN
/set_secret factory β GITHUB_OWNER
/set_secret factory β GITHUB_REPO
Then use /gh_workflows, /gh_runs, /gh_dispatch <workflow> [ref], etc.
For features not yet wrapped as DSL actions, use telegram_api:
{
"type": "telegram_api",
"method": "sendVenue",
"payload": {
"latitude": 40.758,
"longitude": -73.9855,
"title": "Meet here",
"address": "Times Square, New York"
}
}Built-in TON-specific helpers for mainnet and testnet:
ton_connectβ Build TON Connect v2 links, send as inline buttons, requestton_prooffor authenticationton_signβ Build signing URLs for your HTTPS signing page with network, payload type, and state
Both support placeholders like {user.id}, {chat.id}, {vars.some_value} and reject secret placeholders for external transmission.
Telegram ββPOST /hook/factoryβββΆ Worker βββΆ factory.ts (owner-only commands)
Telegram ββPOST /hook/:botIdββββΆ Worker βββΆ dsl/interpreter.ts (runActions)
β
βββββββββββββββββββΌβββββββββββββββββββββββ
βΌ βΌ βΌ
BOT_KV ChatSession DO Workers AI / fetch()
(bot registry, (vars, paused "ask" (compute doesn't
configs, secrets flows, rate limits reach these β
blobs) β per botId:chatId) sandboxed evaluator)
| Component | Purpose |
|---|---|
KV (BOT_KV) |
Bot registry, configs, encrypted secrets, transient factory-flow state |
| ChatSession DO | Per-chat conversational state, paused ask flows, token-bucket rate limiting |
dsl/expression.ts |
Sandboxed compute evaluator (arithmetic + whitelisted functions only) |
dsl/interpreter.ts |
DSL execution engine (runActions) |
β
Factory Bot: Hardcoded owner check (FACTORY_OWNER_ID) gates all access
β
Private Bots: Only the owner can interact; silent 200 OK for probes
β
Public Bots: Configs validated at save time, rejected if unsafe; rate-limited requests
β
No Shell/Python: No shell or python actions exist in the type system β impossible RCE vector
β
Sandboxed Compute: Custom formulas run in a non-Turing-complete whitelist evaluator, never reaching eval or Function
This reimplementation deliberately changes:
-
No
shell/pythonactions β Completely absent from type system, schema, and interpreter. Replaced withcompute/transformusing a whitelisted evaluator. -
Session state in Durable Objects β Not in-process memory. Workers isolates are ephemeral, so
varsand paused flows live externally in a DO (trades memory for network round-trip, gains [...] -
Web Crypto AES-GCM β Same goal as Fernet (symmetric encryption), different primitive (Fernet unavailable in workerd).
-
Native
env.AIbinding β Direct Workers AI integration instead of authenticated HTTP calls. One fewer secret to manage. -
Nested
condition+askresume behavior β Resume continues the inner branch only, not sibling actions after the condition (interpreter simplicity tradeoff). -
Webhooks instead of long-polling β No
getUpdatesloop, no persistent process. Every update routes to your Worker, verified viaX-Telegram-Bot-Api-Secret-Tokenheader.
wrangler.toml Worker config: KV, DO, AI bindings
package.json / tsconfig.json
src/
βββ index.ts Hono app: /hook/factory & /hook/:botId routes
βββ telegram.ts Telegram Bot API client (fetch-based)
βββ env.ts Shared Env (bindings + vars) interface
βββ secrets.ts Web Crypto AES-GCM encrypt/decrypt
βββ session.do.ts ChatSession Durable Object
βββ session-client.ts DO communication wrapper
βββ factory.ts Factory-bot command handlers
βββ github.ts GitHub Actions gateway
βββ ai.ts Workers AI-backed command generator
βββ dsl/
βββ types.ts Action & config type definitions
βββ schema.ts Shape validator & public-bot safety checks
βββ expression.ts Sandboxed compute evaluator
βββ template.ts {user.x}/{vars.x}/{secrets.x} interpolation
βββ interpreter.ts DSL execution engine (runActions)
examples/
βββ simple-public-bot.json
βββ price-tracker-bot.json
βββ interface-anything-bot.json
βββ ton-wallet-bot.json
assets/ Images & logos
setup.sh One-shot wrangler setup + deploy script
- π₯ Large Configs:
getFiledownloads capped at 20MB by Telegram; use/jsonin chunks or host externally - π¦ GitHub Logs:
/gh_logsreturns a signed, time-limited download URL (not proxied through Worker) - π Nested Conditionals:
condition+ nestedaskresume only the inner branch (see differences #5)
- π Read
/helpin your factory bot for detailed command reference - π Explore the
examples/directory for real-world config templates - π Check
wrangler.tomlfor available bindings and environment variables - π¬ Open an issue for feature requests or bug reports
Built with β‘ on Cloudflare Workers
Made by quickerup β’ MIT License
