Lightweight Telegram Bot API client - send messages, keyboards, webhooks, async, zero deps
Project description
telegram-bot-lite-py
Lightweight Telegram Bot API client — send messages, build keyboards, handle webhooks, all async with zero dependencies.
Pure Python ≥ 3.9. No httpx, no aiohttp, no requests. Just the stdlib.
pip install telegram-bot-lite-py
Features
- Async-first — built on
asyncio+ stdlib SSL - Full Bot API coverage: messages, media, keyboards, commands, callbacks
- Composable update filters (
&,|,~) - Long-polling and webhook modes
- Fluent keyboard builders
- Typed dataclasses for every API object
- Zero third-party dependencies
Quick Start
Echo bot (long polling)
import asyncio
from telegram_bot_lite_py import TelegramBot, Dispatcher
from telegram_bot_lite_py.filters import Filters
bot = TelegramBot("YOUR_BOT_TOKEN")
dp = Dispatcher(bot)
@dp.on_command("start")
async def on_start(bot, update):
await bot.reply(update.message, "Hello! I'm alive 🤖")
@dp.on_message(Filters.text)
async def on_text(bot, update):
await bot.reply(update.message, f"You said: {update.message.text}")
dp.run_polling()
One-shot notification (no dispatcher needed)
import asyncio
from telegram_bot_lite_py import TelegramBot
async def notify():
bot = TelegramBot("YOUR_BOT_TOKEN")
await bot.send_message(CHAT_ID, "<b>Deploy complete!</b>", parse_mode="HTML")
asyncio.run(notify())
Installation
pip install telegram-bot-lite-py
Requires Python 3.9+. No other packages needed.
API Reference
TelegramBot
from telegram_bot_lite_py import TelegramBot
bot = TelegramBot(
token="YOUR_BOT_TOKEN",
timeout=30.0, # per-request timeout in seconds
parse_mode="HTML", # default parse mode: "HTML", "Markdown", "MarkdownV2", or None
)
Sending messages
| Method | Description |
|---|---|
await bot.send_message(chat_id, text, *, parse_mode, reply_markup, ...) |
Send a text message |
await bot.reply(message, text, *, parse_mode, reply_markup) |
Reply to a specific message |
await bot.notify(chat_id, text) |
Shorthand alias for send_message |
await bot.send_photo(chat_id, photo, *, caption, ...) |
Send photo by file_id, URL, or bytes |
await bot.send_document(chat_id, document, *, caption, ...) |
Send document |
await bot.send_audio(chat_id, audio, *, caption, performer, title, ...) |
Send audio |
await bot.send_video(chat_id, video, *, caption, duration, ...) |
Send video |
await bot.send_location(chat_id, lat, lon, ...) |
Send a map pin |
await bot.send_contact(chat_id, phone, first_name, ...) |
Send a contact |
await bot.send_chat_action(chat_id, action) |
Show "typing…" or other indicators |
await bot.forward_message(chat_id, from_chat_id, message_id) |
Forward a message |
await bot.copy_message(chat_id, from_chat_id, message_id) |
Copy (without forward tag) |
Editing & deleting
| Method | Description |
|---|---|
await bot.edit_message_text(text, *, chat_id, message_id, ...) |
Edit message text |
await bot.edit_message_reply_markup(*, chat_id, message_id, reply_markup) |
Update keyboard only |
await bot.delete_message(chat_id, message_id) |
Delete a message |
Callback queries
| Method | Description |
|---|---|
await bot.answer_callback_query(callback_query_id, *, text, show_alert, url) |
Answer an inline button press |
Chat management
| Method | Description |
|---|---|
await bot.get_chat(chat_id) |
Fetch Chat info |
await bot.leave_chat(chat_id) |
Leave a group/channel |
await bot.ban_chat_member(chat_id, user_id, ...) |
Ban a user |
await bot.unban_chat_member(chat_id, user_id) |
Unban a user |
await bot.get_chat_member_count(chat_id) |
Member count |
Bot setup
| Method | Description |
|---|---|
await bot.get_me() |
Fetch the bot's User object |
await bot.set_my_commands(commands) |
Set the command list shown in clients |
await bot.get_my_commands() |
Get current commands |
await bot.delete_my_commands() |
Remove all commands |
Webhook
| Method | Description |
|---|---|
await bot.set_webhook(url, *, secret_token, max_connections, ...) |
Register a webhook |
await bot.delete_webhook(*, drop_pending_updates) |
Remove the webhook |
await bot.get_webhook_info() |
Fetch current webhook status |
Files
| Method | Description |
|---|---|
await bot.get_file(file_id) |
Fetch File metadata |
bot.get_file_url(file_path) |
Build the download URL for a File |
Dispatcher
from telegram_bot_lite_py import Dispatcher
dp = Dispatcher(bot, workers=4)
Decorators
@dp.on_command("start", "help")
async def cmd_handler(bot, update): ...
@dp.on_message(Filters.text & ~Filters.command("start"))
async def text_handler(bot, update): ...
@dp.on_callback("confirm", "cancel")
async def callback_handler(bot, update): ...
@dp.on_callback(prefix="page:")
async def paginate(bot, update): ...
@dp.on_error()
async def error_handler(bot, update, exc): ...
Low-level registration
from telegram_bot_lite_py.filters import Filters
dp.add_handler(my_fn, Filters.photo, priority=10)
Running
# Blocking (runs asyncio event loop internally)
dp.run_polling(timeout=30, drop_pending=True)
# Inside an existing event loop
await dp.start_polling(timeout=30)
Filters
from telegram_bot_lite_py.filters import Filters
Filters.message # any message update
Filters.edited_message # edited message
Filters.callback_query # callback query
Filters.channel_post # channel post
Filters.text # message has text
Filters.photo # message has photo
Filters.document # message has document
Filters.audio
Filters.video
Filters.location
Filters.contact
Filters.sticker
Filters.private # private chat
Filters.group # group or supergroup
Filters.channel # channel
Filters.command("start", "help")
Filters.regex(r"order #\d+")
Filters.text_equals("yes", "no")
Filters.text_contains("hello")
Filters.user(123456, 789012) # whitelist by user_id
Filters.chat(-100123456789) # whitelist by chat_id
Filters.callback_data("ok", "cancel")
Filters.callback_prefix("page:")
Filters.custom_filter(lambda u: u.effective_user.is_premium)
Combine with &, |, ~:
private_text = Filters.text & Filters.private
not_bot = ~Filters.user(bot_id)
media = Filters.photo | Filters.video | Filters.document
Keyboard builders
Inline keyboards
from telegram_bot_lite_py.keyboards import InlineKeyboard, inline_keyboard
# Fluent builder
kb = (
InlineKeyboard()
.button("Yes", callback_data="yes")
.button("No", callback_data="no")
.row()
.button("Visit", url="https://sarmkadan.com")
.build()
)
# Compact factory
kb = inline_keyboard(
[("Option A", "a"), ("Option B", "b")],
[("Cancel", "cancel")],
)
await bot.send_message(chat_id, "Choose:", reply_markup=kb)
Reply keyboards
from telegram_bot_lite_py.keyboards import ReplyKeyboard, reply_keyboard
# Fluent builder
kb = (
ReplyKeyboard(resize=True, one_time=True)
.button("Share Location", request_location=True)
.row()
.button("Share Contact", request_contact=True)
.build()
)
# Compact factory
kb = reply_keyboard(["Yes", "No"], ["Maybe", "Cancel"], resize=True)
await bot.send_message(chat_id, "Choose:", reply_markup=kb)
Remove / force reply
from telegram_bot_lite_py.keyboards import remove_keyboard, force_reply
await bot.send_message(chat_id, "Keyboard removed", reply_markup=remove_keyboard())
await bot.send_message(chat_id, "Enter your name:", reply_markup=force_reply(placeholder="Your name"))
Webhook server
import ssl
from telegram_bot_lite_py import TelegramBot, Dispatcher, WebhookServer
bot = TelegramBot("YOUR_BOT_TOKEN")
dp = Dispatcher(bot)
@dp.on_command("start")
async def start(bot, update):
await bot.reply(update.message, "Hello from webhook!")
# Optional TLS termination
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("cert.pem", "key.pem")
server = WebhookServer(
dp,
host="0.0.0.0",
port=8443,
path="/webhook",
secret_token="my-secret",
ssl_context=ctx,
)
# Register webhook then start
import asyncio
async def main():
await bot.set_webhook(
"https://your-domain.com/webhook",
secret_token="my-secret",
max_connections=40,
)
await server.start()
# keep running
await asyncio.Event().wait()
asyncio.run(main())
Types
All Telegram API objects are plain Python dataclasses.
from telegram_bot_lite_py import Update, Message, User, Chat, CallbackQuery
update: Update
update.update_id
update.message # Message | None
update.edited_message # Message | None
update.callback_query # CallbackQuery | None
update.effective_message # first non-None of message / edited_message / channel_post
update.effective_chat # Chat | None
update.effective_user # User | None
message: Message
message.message_id
message.chat # Chat
message.from_user # User | None
message.text # str | None
message.content_type # "text" | "photo" | "document" | ...
message.get_command() # "/start" | None
message.get_args() # "arg1 arg2" – everything after the command
user: User
user.id
user.full_name # "First Last"
user.mention # "@username" or full name
chat: Chat
chat.id
chat.type # "private" | "group" | "supergroup" | "channel"
chat.is_private # bool
chat.is_group # bool
chat.display_name # title or first_name
Error handling
from telegram_bot_lite_py.exceptions import (
TelegramAPIError,
RetryAfterError,
ForbiddenError,
NetworkError,
)
@dp.on_error()
async def handle_error(bot, update, exc):
if isinstance(exc, RetryAfterError):
print(f"Flood limit. Retry after {exc.retry_after}s")
elif isinstance(exc, ForbiddenError):
print("Bot was blocked by user")
elif isinstance(exc, NetworkError):
print(f"Network problem: {exc}")
else:
print(f"Unhandled error: {exc}")
Examples
Notification bot
import asyncio
from telegram_bot_lite_py import TelegramBot
async def alert(chat_id: int, message: str):
bot = TelegramBot("YOUR_TOKEN")
await bot.send_message(chat_id, f"<b>Alert</b>\n{message}", parse_mode="HTML")
asyncio.run(alert(-100123456789, "Server CPU above 90%"))
Paginated inline buttons
@dp.on_command("list")
async def cmd_list(bot, update):
kb = inline_keyboard(
[("Item 1", "item:1"), ("Item 2", "item:2")],
[("< Prev", "page:0"), ("Next >", "page:2")],
)
await bot.reply(update.message, "Choose an item:", reply_markup=kb)
@dp.on_callback(prefix="item:")
async def on_item(bot, update):
item_id = update.callback_query.data.split(":")[1]
await bot.answer_callback_query(update.callback_query.id, text=f"Selected item {item_id}")
@dp.on_callback(prefix="page:")
async def on_page(bot, update):
page = int(update.callback_query.data.split(":")[1])
await bot.answer_callback_query(update.callback_query.id, text=f"Page {page}")
Admin-only commands
ADMIN_IDS = [123456789]
@dp.on_command("ban", priority=100)
async def cmd_ban(bot, update):
if update.effective_user.id not in ADMIN_IDS:
await bot.reply(update.message, "Access denied.")
return
# ... ban logic
License
MIT — see LICENSE.
© 2024 Vladyslav Zaiets — sarmkadan.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file telegram_bot_lite_py-1.0.0.tar.gz.
File metadata
- Download URL: telegram_bot_lite_py-1.0.0.tar.gz
- Upload date:
- Size: 21.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b400fb1c6691aa237beca3e9bfa7367779786f59f76949e3375c0f0acfaaa2f
|
|
| MD5 |
a525eef11b0d494cb2c7055d5d3c45ed
|
|
| BLAKE2b-256 |
2b7523478ed4b371a0ad7d6e16c0ebb60d421c4e9db8d08366eb3d73ab29c24e
|
File details
Details for the file telegram_bot_lite_py-1.0.0-py3-none-any.whl.
File metadata
- Download URL: telegram_bot_lite_py-1.0.0-py3-none-any.whl
- Upload date:
- Size: 26.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a21fb98e03c329b79bc7e9f1ea4d917939b0a003540213331e6352e89531c28d
|
|
| MD5 |
de4fab18d823e2e9b0769973e3fbae23
|
|
| BLAKE2b-256 |
82a09587feec6002becec1ce54b068be82697f0426bcb47fb151b7caeee1fd56
|