A simple and convenient library for creating Telegram bots.
Project description
Extergram — Asynchronous Telegram Bot Framework
Disclaimer: This project is an independent open-source library and is not affiliated with, associated with, authorized by, endorsed by, or in any way officially connected with Telegram FZ-LLC or any of its subsidiaries or its affiliates.
Extergram is a modern, fully asynchronous Python framework for building Telegram bots. It provides a clean API, built‑in FSM (Finite State Machine) with multiple storage backends, middleware support, webhooks, anti‑flood protection, and a wide range of Telegram Bot API methods (version 7.2+).
Current Version: 1.0.0
Installation
Requires Python 3.8+.
pip install extergram
Optional dependencies for persistent FSM storage:
- Redis:
pip install redis - JSON and SQLite storages work out of the box.
Quick Start
A minimal echo bot:
import asyncio
from extergram import Bot, ContextTypes
from extergram.ext import CommandHandler, MessageHandler
async def start(context: ContextTypes):
await context.bot.send_message(
chat_id=context.message.chat.id,
text="Hello! I am an Extergram bot."
)
async def echo(context: ContextTypes):
await context.bot.send_message(
chat_id=context.message.chat.id,
text=f"You said: {context.message.text}"
)
async def main():
bot = Bot(token="YOUR_BOT_TOKEN")
bot.add_handler(CommandHandler("start", start))
bot.add_handler(MessageHandler(echo))
await bot.polling()
if __name__ == "__main__":
asyncio.run(main())
Key Features
- 100% Asynchronous – all methods are async/await; no blocking.
- Middleware – global pre‑/post‑processing of updates.
- Webhooks – can be used instead of polling.
- FSM (Finite State Machine) – built‑in with memory, JSON, SQLite, and Redis backends.
- Anti‑flood – automatic retries with dynamic delays.
- Rich Keyboard Support – inline and reply keyboards.
- Full API Coverage – messages, media, polls, admin actions, chat management, and more.
API Reference
Bot Class
Constructor
Bot(token: str, default_parse_mode: str = None, fsm_storage: FSMStorage = None)
token– bot token from BotFather. If empty,EmptyTokenErroris raised.default_parse_mode– global parse mode ("HTML","MarkdownV2", or"Markdown"). IfNone, formatting is disabled by default (noparse_modefield sent).fsm_storage– optional storage for FSM (default:MemoryFSMStorage).
Polling
await bot.polling(timeout=30)
await bot.run_polling(timeout=30) # alias
Webhooks
await bot.set_webhook(url, certificate=None, max_connections=None, allowed_updates=None, drop_pending_updates=None, secret_token=None)
await bot.delete_webhook(drop_pending_updates=None)
info = await bot.get_webhook_info()
await bot.process_webhook(update_dict) # call from your web framework
Middleware
from extergram.ext import BaseMiddleware
class MyMiddleware(BaseMiddleware):
async def process(self, context, call_next):
print("Before handler")
await call_next()
print("After handler")
bot.add_middleware(MyMiddleware())
Handlers
add_handler(handler: BaseHandler)– registers a handler.- Handlers are checked in order; the first matching one executes.
Available handlers:
MessageHandler(callback, filters=None)– triggers on text messages. Filters can be a string (exact match), a compiled regex, or a list of them.CommandHandler(command, callback)– triggers on/command. Accepts a string or list of strings.CallbackQueryHandler(callback)– triggers on inline button presses.StateHandler(state, handler)– wraps any handler and only triggers if the user is in the given FSM state.
Sending Messages
await bot.send_message(chat_id, text, parse_mode=None, disable_web_page_preview=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, message_thread_id=None, business_connection_id=None)
parse_mode: ifNone, the bot'sdefault_parse_modeis used. Pass""to send plain text.reply_markup: can be aButtonsDesign,ReplyKeyboard, or a raw dict.
Media
All media methods accept a local file path or an HTTP/HTTPS URL. Local files are uploaded automatically.
await bot.send_photo(chat_id, photo, caption=None, parse_mode=None, reply_markup=None)
await bot.send_document(chat_id, document, caption=None, parse_mode=None, reply_markup=None)
await bot.send_video(chat_id, video, caption=None, parse_mode=None, reply_markup=None)
await bot.send_animation(chat_id, animation, caption=None, parse_mode=None, reply_markup=None)
await bot.send_voice(chat_id, voice, caption=None, parse_mode=None, reply_markup=None)
await bot.send_video_note(chat_id, video_note, reply_markup=None)
await bot.send_sticker(chat_id, sticker, reply_markup=None, disable_notification=None, message_thread_id=None)
await bot.send_media_group(chat_id, media, disable_notification=None, message_thread_id=None)
Other Send Methods
await bot.send_location(chat_id, latitude, longitude, live_period=None, disable_notification=None, message_thread_id=None, reply_markup=None)
await bot.send_venue(chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None, message_thread_id=None, reply_markup=None)
await bot.send_contact(chat_id, phone_number, first_name, last_name=None, disable_notification=None, message_thread_id=None, reply_markup=None)
await bot.send_poll(chat_id, question, options, is_anonymous=True, type='regular', allows_multiple_answers=False, correct_option_id=None, explanation=None, open_period=None, close_date=None, is_closed=False, disable_notification=None, message_thread_id=None, reply_markup=None)
await bot.send_dice(chat_id, emoji='🎲', disable_notification=None, message_thread_id=None, reply_markup=None)
Message Editing & Deletion
await bot.edit_message_text(chat_id, message_id, text, parse_mode=None, reply_markup=None)
await bot.edit_message_caption(chat_id, message_id, caption, parse_mode=None, reply_markup=None, show_caption_above_media=None)
await bot.edit_message_media(chat_id, message_id, media, reply_markup=None, business_connection_id=None)
await bot.edit_message_reply_markup(chat_id, message_id, reply_markup=None)
await bot.delete_message(chat_id, message_id)
await bot.delete_messages(chat_id, message_ids)
Forwarding & Copying
await bot.forward_message(chat_id, from_chat_id, message_id, disable_notification=None, message_thread_id=None)
await bot.copy_message(chat_id, from_chat_id, message_id, caption=None, parse_mode=None, disable_notification=None, message_thread_id=None, reply_markup=None)
Callback Queries
await bot.answer_callback_query(callback_query_id, text=None, show_alert=False, url=None, cache_time=None)
Polls
await bot.stop_poll(chat_id, message_id, reply_markup=None)
Bot Commands
from extergram import BotCommand
await bot.set_my_commands([BotCommand("start", "Start the bot"), BotCommand("help", "Show help")])
await bot.get_my_commands(scope=None, language_code=None)
await bot.delete_my_commands(scope=None, language_code=None)
Chat & Administration
await bot.get_chat(chat_id)
await bot.get_chat_member(chat_id, user_id)
await bot.get_chat_administrators(chat_id)
await bot.leave_chat(chat_id)
await bot.set_chat_title(chat_id, title)
await bot.set_chat_description(chat_id, description)
await bot.set_chat_photo(chat_id, photo_path)
await bot.delete_chat_photo(chat_id)
await bot.pin_chat_message(chat_id, message_id, disable_notification=None)
await bot.unpin_chat_message(chat_id, message_id)
await bot.unpin_all_chat_messages(chat_id)
await bot.send_chat_action(chat_id, action, message_thread_id=None)
await bot.get_file(file_id)
await bot.ban_chat_member(chat_id, user_id, until_date=None, revoke_messages=None)
await bot.unban_chat_member(chat_id, user_id, only_if_banned=None)
await bot.restrict_chat_member(chat_id, user_id, permissions: ChatPermissions, until_date=None)
await bot.promote_chat_member(chat_id, user_id, **permissions) # e.g. can_pin_messages=True
await bot.ban_chat_sender_chat(chat_id, sender_chat_id)
await bot.unban_chat_sender_chat(chat_id, sender_chat_id)
await bot.set_chat_permissions(chat_id, permissions: ChatPermissions)
await bot.approve_chat_join_request(chat_id, user_id)
await bot.decline_chat_join_request(chat_id, user_id)
await bot.export_chat_invite_link(chat_id)
await bot.create_chat_invite_link(chat_id, name=None, expire_date=None, member_limit=None, creates_join_request=None)
await bot.edit_chat_invite_link(chat_id, invite_link, name=None, expire_date=None, member_limit=None, creates_join_request=None)
await bot.revoke_chat_invite_link(chat_id, invite_link)
Bot Info & Lifecycle
await bot.set_my_description(description, language_code=None)
await bot.set_my_short_description(short_description, language_code=None)
await bot.log_out()
await bot.close()
Inline Queries (new in 1.0.0)
await bot.answer_inline_query(inline_query_id, results: list, cache_time=None, is_personal=None, next_offset=None, switch_pm_text=None, switch_pm_parameter=None)
ContextTypes
Passed to handlers that accept a single argument (context). Provides:
context.bot– the Bot instance.context.update– the raw Update object.context.message– the Message (if any).context.callback_query– the CallbackQuery (if any).context.effective_user– the user who triggered the update.context.effective_chat– the chat where the update occurred.context.state– anFSMContextinstance (requires chat & user ID).
Data Classes
The library provides type‑safe wrappers for Telegram objects:
User,Chat,Message,CallbackQuery,Update,BotCommand,ChatPermissions,InlineKeyboardMarkup,PhotoSize,Document,Animation,Audio,Voice,Video,VideoNote,Sticker,Contact,Dice,Location,Venue,Poll,PollOption,MessageEntity
They are automatically created from API responses.
Keyboards
Inline Keyboard (ButtonsDesign)
from extergram import ButtonsDesign
kb = ButtonsDesign()
kb.add_row(
ButtonsDesign.create_button("Button 1", "callback_data_1"),
ButtonsDesign.create_url_button("GitHub", "https://github.com")
)
await bot.send_message(chat_id, "Choose:", reply_markup=kb)
Reply Keyboard (ReplyKeyboard + KeyboardButton)
from extergram import ReplyKeyboard, KeyboardButton
kb = ReplyKeyboard(resize_keyboard=True, input_field_placeholder="Send contact")
kb.add_row(
KeyboardButton("Send phone", request_contact=True),
KeyboardButton("Send location", request_location=True)
)
await bot.send_message(chat_id, "Press a button:", reply_markup=kb)
FSM (Finite State Machine)
Storages:
MemoryFSMStorage()– in‑memory (default).JSONFSMStorage(file_path="fsm_data.json")SQLiteFSMStorage(db_path="fsm_storage.db")RedisFSMStorage(redis_url="redis://localhost:6379/0")
Usage inside a handler (context.state):
# set state
await context.state.set_state("waiting_for_name")
# store data
await context.state.update_data(name="John")
# retrieve
data = await context.state.get_data()
# clear
await context.state.clear()
Wrap a handler with StateHandler:
from extergram.ext import StateHandler, MessageHandler
bot.add_handler(StateHandler("waiting_for_name", MessageHandler(process_name)))
Utilities
Markdown Helper (safe escaping)
from extergram import Markdown
text = str(Markdown("Hello ").bold("World").text("!"))
# result: Hello *World*!
await bot.send_message(chat_id, text, parse_mode="MarkdownV2")
Low‑level escaping
from extergram import escape_markdown_v2
safe = escape_markdown_v2("Special chars: _ * [ ] ( )")
Exceptions
All exceptions are in extergram.errors.
EmptyTokenError– token is empty or only whitespace.UnauthorizedError– invalid token.FloodControlError– rate limit hit; containsretry_afterattribute.TelegramAdminError– bot lacks admin rights.NetworkError,BadRequestError,ForbiddenError,NotFoundError,ConflictError,EntityTooLargeError,InternalServerError,BadGatewayError,APIError(base).
Examples
Example 1: Command with Inline Keyboard
import asyncio
from extergram import Bot, ContextTypes, ButtonsDesign
from extergram.ext import CommandHandler, CallbackQueryHandler
async def start(context: ContextTypes):
kb = ButtonsDesign().add_row(
ButtonsDesign.create_button("Click me", "btn_click")
)
await context.bot.send_message(context.message.chat.id, "Press the button:", reply_markup=kb)
async def on_click(context: ContextTypes):
await context.bot.answer_callback_query(context.callback_query.id, "Button clicked!")
await context.bot.edit_message_text(
context.callback_query.message.chat.id,
context.callback_query.message.message_id,
"You clicked!"
)
async def main():
bot = Bot("YOUR_TOKEN")
bot.add_handler(CommandHandler("start", start))
bot.add_handler(CallbackQueryHandler(on_click))
await bot.polling()
asyncio.run(main())
Example 2: FSM with SQLite storage
import asyncio
from extergram import Bot, ContextTypes, SQLiteFSMStorage
from extergram.ext import CommandHandler, MessageHandler, StateHandler
async def start(context: ContextTypes):
await context.state.set_state("ask_age")
await context.bot.send_message(context.message.chat.id, "How old are you?")
async def age_received(context: ContextTypes):
age = context.message.text
await context.state.update_data(age=age)
await context.state.set_state(None)
await context.bot.send_message(context.message.chat.id, f"Thanks! Your age is {age}.")
async def main():
storage = SQLiteFSMStorage("my_bot.db")
bot = Bot("YOUR_TOKEN", fsm_storage=storage)
bot.add_handler(CommandHandler("start", start))
bot.add_handler(StateHandler("ask_age", MessageHandler(age_received)))
await bot.polling()
asyncio.run(main())
Example 3: Webhook with aiohttp
from aiohttp import web
import asyncio
from extergram import Bot
bot = Bot("YOUR_TOKEN")
async def webhook(request):
update = await request.json()
await bot.process_webhook(update)
return web.Response(status=200)
async def set_webhook():
await bot.set_webhook("https://your-domain.com/webhook")
async def main():
await set_webhook()
app = web.Application()
app.router.add_post("/webhook", webhook)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, "0.0.0.0", 8080)
await site.start()
await asyncio.Event().wait()
asyncio.run(main())
Example 4: Middleware for logging
import asyncio
from extergram import Bot, ContextTypes
from extergram.ext import BaseMiddleware, CommandHandler
class LogMiddleware(BaseMiddleware):
async def process(self, context, call_next):
print(f"Update from user {context.effective_user.id}")
await call_next()
print("Handler finished")
async def start(context: ContextTypes):
await context.bot.send_message(context.message.chat.id, "Hello!")
async def main():
bot = Bot("YOUR_TOKEN")
bot.add_middleware(LogMiddleware())
bot.add_handler(CommandHandler("start", start))
await bot.polling()
asyncio.run(main())
License
MIT License. See LICENSE file.
Links
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 extergram-1.0.0.tar.gz.
File metadata
- Download URL: extergram-1.0.0.tar.gz
- Upload date:
- Size: 27.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4cd90982639fd8f410503ba267ca8cea0a4dc4f2740dd96c1a9ffe9465d2e9d9
|
|
| MD5 |
be25c53deed542e6467ce62a8c470b20
|
|
| BLAKE2b-256 |
c1f1485b7eb1983da36920c9cade008daec13b990aaec02a41f94716a104e3b7
|
File details
Details for the file extergram-1.0.0-py3-none-any.whl.
File metadata
- Download URL: extergram-1.0.0-py3-none-any.whl
- Upload date:
- Size: 25.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b898a51e3c1225dd1e9599c0f285ba71ce2902864315e0d0dcf8f245149ff0b9
|
|
| MD5 |
010e40e8742dd04d39d0ff50ee4681d6
|
|
| BLAKE2b-256 |
40c7efec1fa54d7ef084b08397b2ce59e4a2f570ce1cb327db091a9c7b43affb
|