A Telegram bot that runs MultiAgency's live task board:
- Contributors onboard with
/start, pick their roles (video editing, writing, design, amplification, research, community), and get notified the moment a matching task goes live. - Admins post tasks with
/newtask— a guided flow that captures a full spec (deadline, references, specs, assets, definition of done) so no task goes out vague. The bot broadcasts each task only to contributors with the matching role. - Claiming is first-come-first-served up to the slot limit, with live slot counters edited into every broadcast message — no over-claiming. Contributors see the complete standard before they claim.
- Submissions come back through
/submit(link or file) and land with every admin as pending review, with Accept / Request changes buttons. Nothing counts as done until an admin accepts it. - Track record: every contributor builds a history (accepted tasks,
change requests, acceptance rate) visible via
/profile(their own) and/contributors(admin roster). - Tiers (optional): contributors climb 🌱 New → ⭐ Proven → 🏆 Core as work
gets accepted; tasks can be reserved for higher tiers. Toggle with
/tiers on|off. - Amplification: accepted work gets a Route for amplification button — one tap creates a new task for the post-maker role and broadcasts it.
- AI agent (optional): with a NEAR AI Cloud key, an agent watches the coordination group, reasons about what work is worth creating (via Claude), and drafts complete task briefs for an admin to approve, edit, or discard. It proposes; humans decide. See Agentic layer.
Built with python-telegram-bot v21 (async) and SQLite. The core bot needs only a bot token; the AI agent additionally uses NEAR AI Cloud (an OpenAI-compatible endpoint) with Claude as the reasoning model.
| File | What it does |
|---|---|
bot.py |
All handlers and wiring — run this |
db.py |
SQLite schema and queries (atomic claim logic lives here) |
messages.py |
Every bot-facing message, in MultiAgency's voice |
config.py |
Roles list, env loading, validation, agent settings |
agent.py |
The AI agent: NEAR AI Cloud + Claude reasoning and drafting |
1. Create the bot — talk to @BotFather on
Telegram, /newbot, and copy the token.
2. Get admin IDs — each admin messages @userinfobot and notes the numeric ID it replies with.
3. Configure — copy .env.example to .env and fill it in:
BOT_TOKEN=123456:ABC-your-token-here
ADMIN_IDS=123456789,9876543214. Install and run (Python 3.11+):
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux: source .venv/bin/activate
pip install -r requirements.txt
python bot.pyThe bot uses long polling — no webhook, domain, or open port required. The
SQLite database (multiagency.db) is created automatically on first run.
5. Smoke-test it — from your own Telegram account: /start, pick a role,
then (as an admin) /newtask and post a task for that role. You should
receive the offer with a Claim button.
Edit the ROLES dict in config.py. Keys are stored in the database
(keep them short, no :), labels are what contributors see. Adding or
removing roles is safe for existing data.
Contributors: /start join · /roles change roles · /mytasks claimed
tasks · /submit hand in work · /profile track record and tier ·
/cancel back out of any flow · /help
Admins: /newtask post a task · /draft <idea> have the AI agent draft a
task for you · /tasks status board (open / fully claimed / closed / done, with
claim and review counts) · /contributors roster with track records ·
/tiers on|off toggle tier gating · /close <id> stop new claims · /onboard
(in a group the bot is in) post a registration pitch with a button that
deep-links members into the bot's DM. Reviewing, amplification, and approving
agent proposals all happen via buttons on the messages the bot sends you.
All other commands only respond in the bot's DM, so adding the bot to a group won't create noise there.
/newtask walks you through ten steps; the task cannot be broadcast until
the required ones are filled:
| # | Field | Required |
|---|---|---|
| 1 | Title | ✅ |
| 2 | Role | ✅ (buttons) |
| 3 | Reward | ✅ |
| 4 | Claim slots | ✅ |
| 5 | Deadline | ✅ |
| 6 | Description — what's needed | ✅ |
| 7 | Reference/inspiration links | optional (Skip button) |
| 8 | Specs — format, dimensions, tone, brand elements | optional (Skip) |
| 9 | Assets provided — raw materials, logos | optional (Skip) |
| 10 | Definition of done — the acceptance checklist | ✅ |
If tiers are on, one extra step asks who can claim (open to all / ⭐ Proven+ / 🏆 Core only). You then see the task exactly as contributors will and confirm with Post it. Contributors receive the complete spec in the broadcast and again in their claim confirmation, and reviews are held against the definition of done.
Submitting never completes a task. Every submission arrives as pending review; only an admin's Accept marks it complete (and the task flips to done once every slot has accepted work). Request changes sends your feedback to the contributor verbatim and reopens their claim so they can resubmit against the same task.
Each accept/change-request builds the contributor's record — accepted count,
change requests, acceptance rate — computed live from history (nothing to
maintain). Contributors see theirs with /profile; admins see everyone with
/contributors.
Contributors climb tiers as work gets accepted:
| Tier | Default threshold |
|---|---|
| 🌱 New | 0 accepted |
| ⭐ Proven | 3 accepted |
| 🏆 Core | 10 accepted |
- Toggle at runtime:
/tiers on//tiers off(admin, no redeploy needed; stored in the database). When off, every task is open to anyone with the matching role and the tier step is skipped in/newtask. - Thresholds: set env vars
TIER_PROVEN_AT/TIER_CORE_AT, or edit theTIERSlist inconfig.py(names, count, and thresholds are all yours to change). - Tier-restricted tasks are only broadcast to eligible contributors, and the claim button double-checks eligibility — someone below the tier gets a friendly note showing exactly how many accepted tasks away they are.
The agent turns the bot from a manual routing tool into an agentic coordination system: it watches the coordination group, reasons about what work is worth doing, and drafts structured briefs — but never routes anything itself. Every draft is proposed to an admin who approves, edits, or discards it. All reasoning runs through Claude on NEAR AI Cloud.
Add to your .env (the bot runs fine without these — the agent just stays off):
# Your NEAR AI Cloud key — get one at https://cloud.near.ai
NEAR_AI_API_KEY=sk-...
# The coordination group the agent watches (a negative chat id).
COORD_GROUP_ID=-1001234567890Finding the group id: add the bot to your coordination group, send any message there, and the bot logs the chat id — or forward a group message to @userinfobot. Optional overrides:
| Variable | Default | Meaning |
|---|---|---|
NEAR_AI_MODEL |
anthropic/claude-sonnet-4-5 |
The Claude model NEAR AI Cloud exposes (see its /models list) |
NEAR_AI_BASE_URL |
https://cloud-api.near.ai/v1 |
OpenAI-compatible endpoint |
AGENT_OBSERVE_EVERY |
12 |
Group messages to accrue before it considers proposing |
AGENT_MIN_INTERVAL_MIN |
45 |
It never proposes more often than this — keeps it non-spammy |
On startup the log line tells you the agent's state (Agent enabled … /
Agent disabled …). Each admin who wants to use the agent should have started
the bot in DM at least once.
- Observe. The agent reads messages in the coordination group and buffers
them. Once
AGENT_OBSERVE_EVERYfresh messages have landed and the rate-limit window has passed, it asks Claude whether the recent chatter is a genuine signal that work should be commissioned — a builder shipping something, a launch, an explicit "we should make something on this." Banter and vague ideas are deliberately ignored; it errs toward silence. - Draft. When it decides something's worth doing, Claude drafts a complete
brief in the same structured format
/newtaskproduces — title, role, suggested reward, deadline, description, references, specs, and a concrete definition of done. - Propose. The agent posts the draft into the group with ✅ Approve & route, ✏️ Edit, and 🗑 Discard buttons, plus a one-line reason it thinks it's worth doing. Nothing has reached contributors yet.
- Human approves. An admin taps:
- Approve → the draft becomes a real task and broadcasts to matching contributors through the normal flow (role routing, slots, review gate).
- Edit → the admin replies in plain language ("make it 2 slots, raise the reward to 40 USDC, tighten the deadline") and Claude revises the draft in place; approve when it's right.
- Discard → gone, no noise.
Admins can also ask directly, in the group or in DM:
/draft a 60s recap video of the Korea meetup
/draft a launch-day thread for the new vault
Claude returns a full structured brief with the same Approve / Edit / Discard controls — fast task creation even when a human starts it.
When you accept a submission, the agent considers (via Claude) whether the work is worth amplifying. If it is, it drafts an amplification task for the post-making role and proposes it for your approval — the same human-owned gate. This is in addition to the manual Route for amplification button, which still works exactly as before.
- The agent proposes; humans approve. No task reaches contributors without an admin's Approve. Approvals are admin-only and idempotent (a second tap on an already-handled draft is refused).
- Rate-limited. It won't propose more often than
AGENT_MIN_INTERVAL_MIN, and only after enough real activity — it surfaces worthwhile work, not everything. - Fails quietly. If NEAR AI Cloud is unreachable or returns something unusable, the agent simply says nothing; the rest of the bot is unaffected.
- The key never touches the code. It's read from the environment only.
The bot is a single long-running process. Any always-on host works; three good options:
- Push this folder to a GitHub repo (
.gitignorealready excludes.envand the database). - On railway.app: New Project → Deploy from GitHub repo.
- Set start command to
python bot.py, and addBOT_TOKENandADMIN_IDSas environment variables in the service settings. - Add a volume mounted at
/dataand setDB_PATH=/data/multiagency.dbso the database survives redeploys.
Render, Fly.io, and similar platforms work the same way — it's a background worker (not a web service), so pick that service type where offered.
# on the server
git clone <your repo> /opt/multiagency-bot && cd /opt/multiagency-bot
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
cp .env.example .env && nano .env # fill in token + admin IDsCreate /etc/systemd/system/multiagency-bot.service:
[Unit]
Description=MultiAgency Task-Routing Bot
After=network-online.target
[Service]
WorkingDirectory=/opt/multiagency-bot
ExecStart=/opt/multiagency-bot/.venv/bin/python bot.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetsudo systemctl enable --now multiagency-bot
journalctl -u multiagency-bot -f # watch the logsRestart=always brings the bot back automatically if it ever crashes or the
server reboots.
python bot.py in a terminal works, but stops when the machine sleeps.
Fine for testing; use A or B for production.
Everything lives in one SQLite file (multiagency.db): contributor profiles,
tasks (including the spec fields deadline, refs, specs, assets, dod,
min_tier), claims, submissions, runtime settings (the tiers switch and the
agent's rate-limit timestamp), agent proposals (drafts and their approve/
discard status), and broadcast bookkeeping. Track records are derived from
submission history at read time, so there are no counters to corrupt. Databases
created before the Quality & Standards or agentic layers are migrated
automatically on startup. Back it up by copying
the file while the bot is stopped (or use sqlite3 multiagency.db ".backup backup.db" live). If you outgrow SQLite, the query layer is isolated in
db.py, so a Postgres migration touches one file.
- Claiming a full or closed task → polite "just missed it" message; the broadcast's slot counter is corrected.
- Double-claiming → "you already have a slot" reminder.
- Submitting to a task you didn't claim → blocked (only your active claims
are offered in
/submit). - Two admins reviewing the same submission → second one gets "already handled".
- Contributors who blocked the bot are skipped during broadcasts and logged.
- Any unexpected exception is logged and the user gets a friendly retry note.