SwiftBot
SwiftBot is a fast, async-first Telegram bot framework built for simplicity and correctness. One decorator registers a handler, a rich Context object does the talking, and the framework quietly handles everything else — HTTP/2 connection pooling, Retry-After compliance, persistent state storage, and a fully typed error hierarchy. Everything is typed, everything is optional, and you never need an external database to get started.
pip install swiftbot
Why SwiftBot
Writing a Telegram bot should not mean wiring polling loops, rate-limit math, and error handling by hand. SwiftBot gives you a clean decorator API on top of a production-grade runtime: HTTP/2 multiplexing over a keep-alive connection pool, a worker pool with backpressure and a dead-letter queue, a trie-based command router, and full Bot API 2026 coverage — including managed bots, guest mode, business messages, and ephemeral messages.
Quick Start
import asyncio
from swiftbot import SwiftBot, Filters as F
from swiftbot.types import Message
bot = SwiftBot(token="YOUR_BOT_TOKEN")
@bot.on(Message(text="hello"))
async def hello(ctx):
await ctx.reply("Hi there!")
@bot.on(Message(filters=F.command("start")))
async def start(ctx):
await ctx.reply("Welcome! Send me hello.")
asyncio.run(bot.run())
That is the whole bot. No router setup, no dispatch table, no middleware boilerplate.
Core Concepts
Handlers and Filters
Handlers are registered with @bot.on(Message(...)) and the first matching handler wins. Filters are composable — combine commands, regex patterns, and predicates:
@bot.on(Message(filters=F.command("ban") & F.chat(-100123456789)))
async def ban(ctx):
...
@bot.on(Message(pattern=r"^/weather (\w+)"))
async def weather(ctx):
city = ctx.match.group(1)
await ctx.reply(f"Weather in {city}: sunny")
Context
Every handler receives a Context with typed access to the update, plus shortcuts that cover most use cases: ctx.reply(), ctx.edit(), ctx.delete(), ctx.answer_callback(), and ctx.forward_to().
Buttons and Callbacks
from swiftbot import InlineKeyboard, Button, CallbackData
# Declare the payload shape once
page = CallbackData("page", int)
# Attach packed payloads to buttons
kb = InlineKeyboard([]).add_row(
Button.inline("1", page.pack(1)),
Button.inline("2", page.pack(2)),
)
await ctx.reply("Pick a page:", reply_markup=kb.to_dict())
# Filter on valid payloads and decode the typed data
@bot.on(CallbackQuery(pattern=rf"^{page.prefix}:[^:]+:[^:]+$"))
async def on_page(ctx):
page_num = page.unpack(ctx.callback_query.data)[0]
await ctx.answer_callback(f"Page {page_num}")
State and Dialogues
State survives restarts with pluggable storage backends — in-memory, JSON file, or Redis — and the dialogue system models multi-step conversations as declared transition graphs with per-state timeouts:
survey = bot.dialogue("survey")
@survey.state("ask_name", next=["ask_age"])
async def ask_name(ctx, prev=None):
await ctx.reply("What's your name?")
return Dialogue.next("ask_age", carry=ctx.text)
@survey.state("ask_age", timeout=120.0)
async def ask_age(ctx, prev=None):
await ctx.reply(f"Hi {prev}, how old are you?")
return Dialogue.end
Middleware, Pipelines and Scopes
Middleware runs around every handler, and the Composer bundles middleware chains with error boundaries. Pipeline adds dependency injection so handlers stay pure and testable, while scope() attaches middleware to only the updates matching a predicate.
from swiftbot.middleware import Logger, RateLimiter
from swiftbot.pipeline import Pipeline
bot.use(Logger())
bot.use(RateLimiter(rate=20, per=60))
pipe = Pipeline().deps(db=my_db)
async def stats(ctx, db):
await ctx.reply(f"{await db.count()} users")
pipe.handle(F.command("stats"), stats)
bot.pipeline(pipe)
bot.scope(F.private).use(RateLimiter(rate=1, per=1.0))
Safety Nets
Fallback handlers catch anything that no other handler matched, and outbound throttling keeps your bot under Telegram's limits automatically:
@bot.fallback
async def catch_all(ctx):
await ctx.reply("Try /help.")
bot.api.config.use(throttle(max_per_second=20.0, per_chat=4.0))
Testing Without Telegram
The built-in harness runs your real handlers with zero network calls — every outgoing API request is captured and every response is scriptable:
from swiftbot.testing import TestClient
async with TestClient(bot) as client:
await client.send_update({
"update_id": 1,
"message": {
"message_id": 1, "date": 1,
"chat": {"id": 1, "type": "private"},
"from": {"id": 2, "is_bot": False, "first_name": "A"},
"text": "hello",
},
})
assert client.outgoing[0]["method"] == "sendMessage"
More
| Topic | What you get |
|---|---|
| Commands | Declarative command specs with auto-generated /help |
| Transformers | Outbound API layer with auto-typing and recording |
| Deep Linking | Typed ?start= payloads for referrals and auth |
| Webhooks | aiohttp server with secret-token verification and metrics |
| Storage | In-memory, JSON-file, and Redis FSM backends |
| Exceptions | Typed ChatNotFound, TooManyRequests, Forbidden, ... |
The complete documentation lives in the docs/ folder.
Contributing
Contributions are welcome — open an issue or submit a pull request.
License
MIT — Copyright (c) 2026 Arjun-M/SwiftBot
Metadata
Release files for swiftbot 1.6.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| swiftbot-1.6.2.tar.gz | 94.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| swiftbot-1.6.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 203.7 kB
Release files / swiftbot-1.6.2.tar.gz
| Download URL | swiftbot-1.6.2.tar.gz |
|---|---|
| Size | 94.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
775b037cac03652b17890eb2a420a41ce131f7e265eb4b9232b0179d53af99ac
|
|
BLAKE2b-256 checksum How to use checksums |
15f26b1d2ef6f30a7fa61cbd6d4e80ac8d0ac795c7dbd3f100072d8e0c30f93e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / swiftbot-1.6.2-py3-none-any.whl
| Download URL | swiftbot-1.6.2-py3-none-any.whl |
|---|---|
| Size | 109.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4281e6a2807418145fa131d37dc0fbe00b1c39f56e10dab01c4eed527e0c986d
|
|
BLAKE2b-256 checksum How to use checksums |
c8f23434c22139d51b841f8f27f86b129a09f709ee95de8b326c1326bdd24553
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|