Telegram bots are a favourite first project for agents, and the same issues come up: a token pasted into code or a test, blocking requests calls inside async handlers, raw user text in HTML replies, no error handler, a bot that answers every message in a group. This loadout uses python-telegram-bot v22's documented patterns: an Application with registered handlers and an error handler, logic in plain functions that test without Telegram, escaped output, admin allow-lists, and run_webhook with a secret token in production.
What's in it
- Token only from TELEGRAM_BOT_TOKEN; separate test bot for development
- No blocking calls in async handlers
- Logic in plain functions with unit tests; no network in tests
- Escaped user text in HTML replies; quiet in group chats
- Webhooks with secret_token in production
You'll need
- Tools:
uv - Environment variables:
TELEGRAM_BOT_TOKEN
Verification
| Check | Result | What we found |
|---|---|---|
| Listinglisting.yaml matches the schema and declares every file in the folder. | Pass | Schema valid; 6 files declared, none missing or extra
|
| Files parseJSON parses; SKILL.md and .mdc front matter is valid YAML with the fields each agent needs; code fences are closed. | Pass | All 6 files parse; front matter valid |
| AdaptersCLAUDE.md imports @AGENTS.md, GEMINI.md imports @./AGENTS.md, and both match the generator. | Pass | CLAUDE.md and GEMINI.md import AGENTS.md and are up to date
|
| Install pathsEvery file lands where each agent documents it, with no collisions, and the laid-out project is re-discovered correctly. | Pass | Correct paths for all 6 agents; layouts re-discovered
|
| MCP configmcp.json is structurally valid, converts to each agent's format, declares no literal secrets, and referenced packages exist on npm/PyPI. | Not applicable | No MCP servers in this loadout |
| Safety scanThe intake scanner finds no credentials, risky shell, hidden Unicode or prompt-injection wording, and the listing's network flag is honest. | Pass | Clean: no credentials, risky shell or hidden text
|
| ReferencesLinks and file paths between the loadout's files resolve, including for each agent's installed layout. | Pass | 1 reference between files resolves for every agent |
| Size limitsFile count and sizes are within the submission limits; SKILL.md bodies stay under 500 lines. | Pass | 6 files, 6.3 KB total; largest AGENTS.md 2.0 KB
|
| LicenceA recognised SPDX licence, with the licence text available. | Pass | MIT, by Agent Loadout; text in the repository LICENSE |
| Starter buildA minimal fixture project with the loadout applied installs and builds or type-checks, and the loadout's own code examples compile. | Pass | The add-bot-command skill's example /split command and tests drop into a python-telegram-bot v22 app: logic tested as a plain function, handler registered without a token or network, ruff clean
|
What this means: a script checked the files themselves: they parse, they install where each agent looks, any MCP config is sound, the safety scan is clean, references between files resolve, and, where a free and repeatable fixture exists, a small starter project builds with the loadout applied. No AI agent was run. Per-agent results ("tested with agent") are separate and stay untested until an agent actually runs.
Install
Choose your agent. Run the commands from your project root, or download a zip already laid out in the right folders.
Claude Code reads CLAUDE.md (imports AGENTS.md with @AGENTS.md) and skills from .claude/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/AGENTS.md curl -fsSL --create-dirs -o CLAUDE.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/CLAUDE.md curl -fsSL --create-dirs -o .claude/skills/add-bot-command/SKILL.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/SKILL.md curl -fsSL --create-dirs -o .claude/skills/add-bot-command/references/split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/split.py curl -fsSL --create-dirs -o .claude/skills/add-bot-command/references/test_split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/test_split.py
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
CLAUDE.md | CLAUDE.md |
skills/add-bot-command/SKILL.md | .claude/skills/add-bot-command/SKILL.md |
skills/add-bot-command/references/split.py | .claude/skills/add-bot-command/references/split.py |
skills/add-bot-command/references/test_split.py | .claude/skills/add-bot-command/references/test_split.py |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Codex CLI reads AGENTS.md and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/SKILL.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/SKILL.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/split.py curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/test_split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/test_split.py
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/add-bot-command/SKILL.md | .agents/skills/add-bot-command/SKILL.md |
skills/add-bot-command/references/split.py | .agents/skills/add-bot-command/references/split.py |
skills/add-bot-command/references/test_split.py | .agents/skills/add-bot-command/references/test_split.py |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Cursor reads AGENTS.md + .cursor/rules/*.mdc and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/SKILL.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/SKILL.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/split.py curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/test_split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/test_split.py
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/add-bot-command/SKILL.md | .agents/skills/add-bot-command/SKILL.md |
skills/add-bot-command/references/split.py | .agents/skills/add-bot-command/references/split.py |
skills/add-bot-command/references/test_split.py | .agents/skills/add-bot-command/references/test_split.py |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Gemini CLI reads GEMINI.md (imports AGENTS.md with @./AGENTS.md) and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/AGENTS.md curl -fsSL --create-dirs -o GEMINI.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/GEMINI.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/SKILL.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/SKILL.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/split.py curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/test_split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/test_split.py
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
GEMINI.md | GEMINI.md |
skills/add-bot-command/SKILL.md | .agents/skills/add-bot-command/SKILL.md |
skills/add-bot-command/references/split.py | .agents/skills/add-bot-command/references/split.py |
skills/add-bot-command/references/test_split.py | .agents/skills/add-bot-command/references/test_split.py |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Antigravity CLI reads AGENTS.md (read natively; GEMINI.md is also read) and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/SKILL.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/SKILL.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/split.py curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/test_split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/test_split.py
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/add-bot-command/SKILL.md | .agents/skills/add-bot-command/SKILL.md |
skills/add-bot-command/references/split.py | .agents/skills/add-bot-command/references/split.py |
skills/add-bot-command/references/test_split.py | .agents/skills/add-bot-command/references/test_split.py |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
GitHub Copilot reads AGENTS.md and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/SKILL.md https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/SKILL.md curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/split.py curl -fsSL --create-dirs -o .agents/skills/add-bot-command/references/test_split.py https://loadout.jnw-it.com/raw/telegram-bot-python/1.0.0/skills/add-bot-command/references/test_split.py
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/add-bot-command/SKILL.md | .agents/skills/add-bot-command/SKILL.md |
skills/add-bot-command/references/split.py | .agents/skills/add-bot-command/references/split.py |
skills/add-bot-command/references/test_split.py | .agents/skills/add-bot-command/references/test_split.py |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Files
Rules AGENTS.md python-telegram-bot rules for secrets, structure and behaviour. 33 lines
# AGENTS.md: Telegram bot (python-telegram-bot)
Telegram bot in Python with python-telegram-bot v22 (asyncio), managed with uv.
## Commands
- Run locally (long polling): `uv run python -m bot`. Tests: `uv run pytest -q`. Lint: `uv run ruff check .`.
- Done means: tests and ruff pass, and you tried the command in a private chat with a test bot.
## Secrets
- The bot token comes from the `TELEGRAM_BOT_TOKEN` environment variable (local: `.env`, gitignored). Never hard-code,
log, print or commit it, and never paste it into an issue or a test.
- If a token may have leaked, tell the human to revoke it with @BotFather straight away.
- Use a separate test bot for development, never the production token.
## Structure
- `bot/__main__.py` builds the `Application` and registers handlers. One module per feature in `bot/handlers/`.
- Business logic in plain functions that take values and return values, so it can be tested without Telegram.
- Handlers are `async def name(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None`. Never block the event loop:
no `time.sleep`, no `requests`; use `httpx.AsyncClient` or run blocking work with `asyncio.to_thread`.
## Behaviour
- Register an error handler with `application.add_error_handler(...)`; log the exception, reply with a short apology.
- Treat every message as untrusted input: validate arguments, cap lengths, never `eval` or build shell commands from it.
- Group chats: respond only to commands or mentions; check `update.effective_chat.type` before doing anything noisy.
- Admin-only commands check the user id against an allow-list from the environment.
- Respect Telegram's rate limits: no loops that send to many chats without delays; use the job queue for broadcasts.
- Formatting: `ParseMode.HTML` with `telegram.helpers.escape` for user text, or plain text. Never put raw user input
into Markdown or HTML.
## Production
- Webhooks in production: `run_webhook(..., secret_token=...)` with a secret from the environment, behind HTTPS.
- Use the `add-bot-command` skill for new commands. Adapter CLAUDE.md Imports AGENTS.md for Claude Code. 10 lines
# CLAUDE.md
@AGENTS.md
## Claude Code notes
- Skills from this loadout live in `.claude/skills/`: `add-bot-command`. Use them when their description matches the task.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.
<!-- From the Agent Loadout "Telegram bot with python-telegram-bot" v1.0.0 — https://loadout.jnw-it.com/loadouts/telegram-bot-python/ --> Adapter GEMINI.md Imports AGENTS.md for Gemini CLI. 10 lines
# GEMINI.md
@./AGENTS.md
## Gemini CLI notes
- Skills from this loadout live in `.agents/skills/`: `add-bot-command`. Use them when their description matches the task.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.
<!-- From the Agent Loadout "Telegram bot with python-telegram-bot" v1.0.0 — https://loadout.jnw-it.com/loadouts/telegram-bot-python/ --> Skill skills/add-bot-command/SKILL.md New command with testable logic and checks. 17 lines
---
name: add-bot-command
description: Add a python-telegram-bot command with logic in a testable plain function, validated arguments, escaped output and a unit test. Use when adding or changing a bot command.
---
# Add a bot command
1. Write the logic as a plain function first (no Telegram types), like `parse_split` in `references/split.py`.
It validates input and raises `ValueError` with a message the user can read.
2. Write the handler: read `context.args`, call the function, reply with the result. Catch `ValueError` and reply with
its message plus usage. Escape any user text with `telegram.helpers.escape` if you reply in HTML.
3. Register it in `bot/__main__.py`: `application.add_handler(CommandHandler("split", split_command))`.
4. Add the command to the BotFather command list text in the README, so the human can update it.
5. Tests like `references/test_split.py`: the plain function (valid, invalid, edge cases) and that the application
registers the handler. No network, no token.
6. Run `uv run pytest -q` and `uv run ruff check .`, then try it with the test bot. Skill file skills/add-bot-command/references/split.py Example /split command: plain function plus handler. 39 lines
"""Example command: /split <amount> <people> splits a bill. Logic is a plain function; the handler is a thin wrapper."""
from decimal import ROUND_HALF_UP, Decimal, InvalidOperation
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
USAGE = "Usage: /split <amount> <people>, for example /split 42.50 3"
def parse_split(args: list[str]) -> str:
if len(args) != 2:
raise ValueError(USAGE)
try:
amount = Decimal(args[0].replace("£", ""))
people = int(args[1])
except (InvalidOperation, ValueError):
raise ValueError(USAGE) from None
if amount <= 0 or amount > Decimal("100000"):
raise ValueError("Amount must be between 0 and 100,000.")
if not 1 <= people <= 100:
raise ValueError("People must be between 1 and 100.")
share = (amount / people).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
return f"£{amount:.2f} split {people} ways is £{share} each."
async def split_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if update.message is None:
return
try:
text = parse_split(context.args or [])
except ValueError as err:
text = str(err)
await update.message.reply_text(text)
def register(application: Application) -> None:
application.add_handler(CommandHandler("split", split_command)) Skill file skills/add-bot-command/references/test_split.py Unit tests and a no-network registration test. 36 lines
import pytest
from telegram.ext import Application, CommandHandler
from bot.handlers.split import USAGE, parse_split, register
def test_even_split():
assert parse_split(["42.50", "3"]) == "£42.50 split 3 ways is £14.17 each."
def test_pound_sign_allowed():
assert parse_split(["£10", "4"]) == "£10.00 split 4 ways is £2.50 each."
@pytest.mark.parametrize("args", [[], ["10"], ["ten", "2"], ["10", "two"], ["10", "2", "3"]])
def test_bad_arguments_show_usage(args):
with pytest.raises(ValueError, match="Usage"):
parse_split(args)
@pytest.mark.parametrize("args, message", [(["0", "2"], "Amount"), (["10", "0"], "People"), (["10", "1000"], "People")])
def test_out_of_range(args, message):
with pytest.raises(ValueError, match=message):
parse_split(args)
def test_usage_mentions_command():
assert "/split" in USAGE
def test_handler_is_registered_without_network():
app = Application.builder().token("123456:TEST-TOKEN-NOT-REAL").build()
register(app)
commands = [c for h in app.handlers[0] if isinstance(h, CommandHandler) for c in h.commands]
assert "split" in commands All files: source zip · Licence MIT · By Agent Loadout · First listed
Credits
Public work that inspired this loadout. The wording here is our own; follow the links for the originals.
Installing and testing
How do I install Telegram bot with python-telegram-bot in Claude Code?
From your project root, put AGENTS.md, CLAUDE.md, skills/add-bot-command/SKILL.md at .claude/skills/add-bot-command/SKILL.md, skills/add-bot-command/references/split.py at .claude/skills/add-bot-command/references/split.py, skills/add-bot-command/references/test_split.py at .claude/skills/add-bot-command/references/test_split.py. The "Claude Code" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/telegram-bot-python/claude-code.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Telegram bot with python-telegram-bot in Codex CLI?
From your project root, put AGENTS.md, skills/add-bot-command/SKILL.md at .agents/skills/add-bot-command/SKILL.md, skills/add-bot-command/references/split.py at .agents/skills/add-bot-command/references/split.py, skills/add-bot-command/references/test_split.py at .agents/skills/add-bot-command/references/test_split.py. The "Codex CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/telegram-bot-python/codex.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Telegram bot with python-telegram-bot in Cursor?
From your project root, put AGENTS.md, skills/add-bot-command/SKILL.md at .agents/skills/add-bot-command/SKILL.md, skills/add-bot-command/references/split.py at .agents/skills/add-bot-command/references/split.py, skills/add-bot-command/references/test_split.py at .agents/skills/add-bot-command/references/test_split.py. The "Cursor" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/telegram-bot-python/cursor.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Telegram bot with python-telegram-bot in Gemini CLI?
From your project root, put AGENTS.md, GEMINI.md, skills/add-bot-command/SKILL.md at .agents/skills/add-bot-command/SKILL.md, skills/add-bot-command/references/split.py at .agents/skills/add-bot-command/references/split.py, skills/add-bot-command/references/test_split.py at .agents/skills/add-bot-command/references/test_split.py. The "Gemini CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/telegram-bot-python/gemini-cli.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Telegram bot with python-telegram-bot in Antigravity CLI?
From your project root, put AGENTS.md, skills/add-bot-command/SKILL.md at .agents/skills/add-bot-command/SKILL.md, skills/add-bot-command/references/split.py at .agents/skills/add-bot-command/references/split.py, skills/add-bot-command/references/test_split.py at .agents/skills/add-bot-command/references/test_split.py. The "Antigravity CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/telegram-bot-python/antigravity-cli.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Telegram bot with python-telegram-bot in GitHub Copilot?
From your project root, put AGENTS.md, skills/add-bot-command/SKILL.md at .agents/skills/add-bot-command/SKILL.md, skills/add-bot-command/references/split.py at .agents/skills/add-bot-command/references/split.py, skills/add-bot-command/references/test_split.py at .agents/skills/add-bot-command/references/test_split.py. The "GitHub Copilot" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/telegram-bot-python/copilot.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
Has Telegram bot with python-telegram-bot been tested?
It passed the Level 1 automated install checks on 2026-10-17: the files parse, install to each agent's documented paths, and pass the safety scan. No AI agent has run it yet, so no agent pass or fail is claimed.
More for Claude Code, Codex, Cursor, Gemini CLI, Antigravity CLI and GitHub Copilot.