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, simple, and fully asynchronous library for creating Telegram bots in Python using httpx. It provides a clean API, built-in FSM (Finite State Machine), anti-flood protection, and a variety of handlers for messages, commands, and callbacks.
Current Version: 0.8.1
🚀 Installation
Extergram requires Python 3.8+.
pip install extergram
⚡ 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())
📚 API Reference
Core Concepts
- Bot – the main class that interacts with Telegram API.
- ContextTypes – passed to handlers, provides access to bot, update, and FSM.
- Handlers – define how to process updates (messages, commands, callbacks). Handlers are checked sequentially; the first matching handler is executed.
- FSM – built-in finite state machine for multi-step dialogs.
1. Bot Class
Constructor
Bot(token: str, default_parse_mode: str = None)
token– your bot token from BotFather.default_parse_mode– default parse mode for messages ("HTML"or"MarkdownV2"). If not set, parse mode must be specified explicitly in each call.
Methods
async polling(timeout: int = 30)
Starts long polling. This coroutine runs forever until interrupted.
add_handler(handler: BaseHandler)
Registers a handler. Must be an instance of BaseHandler.
async get_updates(offset: int = None, timeout: int = 30) -> List[dict]
Fetches updates from Telegram. Normally you don't need to call this directly.
Sending Messages
async send_message(
chat_id: int,
text: str,
parse_mode: str = None,
disable_web_page_preview: bool = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup = None
) -> dict
reply_markupcan be aButtonsDesigninstance or a dict.
Editing Messages
chat_id: int,
message_id: int,
text: str,
parse_mode: str = None,
reply_markup = None
) -> dict
Deleting Messages
async delete_message(chat_id: int, message_id: int) -> dict
Answering Callback Queries
async answer_callback_query(
callback_query_id: str,
text: str = None,
show_alert: bool = False,
url: str = None,
cache_time: int = None
) -> dict
Sending Media
All media methods accept a file path (local) or URL. Local files are read into memory and sent as multipart/form-data.
send_photo(chat_id, photo, caption=None, parse_mode=None, reply_markup=None)send_document(chat_id, document, caption=None, parse_mode=None, reply_markup=None)send_video(chat_id, video, caption=None, parse_mode=None, reply_markup=None)send_voice(chat_id, voice, caption=None, parse_mode=None, reply_markup=None)send_video_note(chat_id, video_note, reply_markup=None)send_animation(chat_id, animation, caption=None, parse_mode=None, reply_markup=None)
Editing Reply Markup
async edit_message_reply_markup(chat_id: int, message_id: int, reply_markup=None) -> dict
Setting Bot Commands
async set_my_commands(commands: List[BotCommand]) -> dict
BotCommand is a simple class with command and description attributes.
Administration Methods
ban_chat_member(chat_id, user_id, until_date=None, revoke_messages=None)unban_chat_member(chat_id, user_id, only_if_banned=None)restrict_chat_member(chat_id, user_id, permissions: ChatPermissions, until_date=None)-promote_chat_member(chat_id, user_id, **permissions)approve_chat_join_request(chat_id, user_id)decline_chat_join_request(chat_id, user_id)
ChatPermissions is a class that allows setting specific permissions (see below).
Anti-Flood
The bot automatically manages request delays to avoid hitting Telegram limits. No configuration needed.
2. ContextTypes
Passed to handlers (new style). Provides:
bot– the Bot instance.update– the raw Update object.message– the Message object (if present).callback_query– the CallbackQuery object (if present).effective_user– the user who triggered the update.effective_chat– the chat where the update occurred.state– anFSMContextinstance for the current user/chat.
3. Data Classes
These classes mirror Telegram API objects. They are created automatically from incoming updates.
- User:
id,is_bot,first_name,last_name,username - Chat:
id,type,title,username - Message:
message_id,from_user,chat,date,text,caption - CallbackQuery:
id,from_user,message,data - Update:
update_id,message,edited_message,callback_query - BotCommand: simple container for commands.
- ChatPermissions: used with
restrict_chat_member. All permissions default toNone(meaning "don't change"). UseTrueto allow,Falseto disallow.
Example:
perms = ChatPermissions(can_send_messages=False, can_send_media_messages=False)
4. UI: ButtonsDesign
Build inline keyboards.
from extergram.ui import ButtonsDesign
kb = ButtonsDesign()
kb.add_row(
ButtonsDesign.create_button("Click me", "callback_data"),
ButtonsDesign.create_url_button("GitHub", "https://github.com")
)
# Use in send_message: reply_markup=kb
create_button(text, callback_data)create_url_button(text, url)add_row(*buttons)– adds a row of buttons.to_dict()– returns the keyboard as a dict for the API.
5. Utilities
Markdown Helper
Safely builds MarkdownV2 strings with automatic escaping.
from extergram.utils import Markdown
text = str(
Markdown("Hello ")
.bold("World")
.text(" this is ")
.italic("escaped")
)
await bot.send_message(chat_id, text, parse_mode="MarkdownV2")
Methods:
text(text)– appends plain text (escaped).bold(text)– appends bold text.italic(text)– appends italic text.__str__()– returns the final string.
escape_markdown_v2(text)
Low-level function to escape MarkdownV2 special characters.
6. Documentation Access
from extergram import Docs
print(Docs.get_docs()) # returns a link to GitHub README
Docs.print_docs() # prints the link
7. Handlers (extergram.ext)
All handlers inherit from BaseHandler and must implement check_update. They are registered via bot.add_handler().
- BaseHandler – abstract base class.
- MessageHandler – triggers on any text message.
- CommandHandler(command, callback) – triggers on a command (e.g.,
/start). Accepts a single command or a list. - CallbackQueryHandler – triggers on any callback query.
- StateHandler(state, handler) – wraps another handler and only triggers if the user is in a specific FSM state. Must be used with
MemoryFSMStorage.
Example:
from extergram.ext import StateHandler, MessageHandler
bot.add_handler(StateHandler("waiting_for_name", MessageHandler(process_name)))
8. FSM (Finite State Machine)
Extergram provides a simple in-memory FSM.
Storage
MemoryFSMStorage– default storage used by Bot.
FSMContext
Obtained via context.state. Methods:
async get_state() -> Optional[str]async set_state(state: Optional[str])async get_data() -> dictasync set_data(data: dict)async update_data(**kwargs)async clear()
9. Exceptions
All exceptions inherit from extergram.errors.ExtergramError.
APIError– base for API errors.NetworkError– network-related issues.-BadRequestError(400)UnauthorizedError(401)ForbiddenError(403)NotFoundError(404)ConflictError(409) – another instance is running.EntityTooLargeError(413)InternalServerError(500)BadGatewayError(502)TelegramAdminError– insufficient rights for admin actions.
🧪 Examples
Example 1: Echo Bot with Commands
import asyncio
from extergram import Bot, ContextTypes
from extergram.ext import CommandHandler, MessageHandler
async def start(context: ContextTypes):
await context.bot.send_message(
context.message.chat.id,
"Welcome! Send me any text and I'll echo it."
)
async def echo(context: ContextTypes):
await context.bot.send_message(
context.message.chat.id,
f"Echo: {context.message.text}"
)
async def main():
bot = Bot(token="YOUR_TOKEN")
bot.add_handler(CommandHandler("start", start))
bot.add_handler(MessageHandler(echo))
await bot.polling()
asyncio.run(main())
Example 2: Inline Keyboard with Callback
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("Green", "color_green"),
ButtonsDesign.create_button("Red", "color_red")
)
await context.bot.send_message(
context.message.chat.id,
"Choose a color:",
reply_markup=kb
)
async def button_handler(context: ContextTypes):
query = context.callback_query
color = query.data.split('_')[1]
await context.bot.answer_callback_query(query.id, text=f"You chose {color}")
await context.bot.send_message(
query.message.chat.id,
f"Your favorite color is {color}!"
)
async def main():
bot = Bot(token="YOUR_TOKEN")
bot.add_handler(CommandHandler("start", start))
bot.add_handler(CallbackQueryHandler(button_handler))
await bot.polling()
asyncio.run(main())
Example 3: FSM – Collecting User Info
import asyncio
from extergram import Bot, ContextTypes
from extergram.ext import CommandHandler, MessageHandler, StateHandler
async def start(context: ContextTypes):
await context.state.set_state("waiting_for_name")
await context.bot.send_message(
context.message.chat.id,
"What's your name?"
)
async def process_name(context: ContextTypes):
name = context.message.text
await context.state.update_data(name=name)
await context.state.set_state("waiting_for_age")
await context.bot.send_message(
context.message.chat.id,
f"Nice to meet you, {name}! How old are you?"
)
async def process_age(context: ContextTypes):
try:
age = int(context.message.text)
except ValueError:
await context.bot.send_message(
context.message.chat.id,
"Please enter a number."
)
return
data = await context.state.get_data()
name = data.get("name")
await context.state.clear()
await context.bot.send_message(
context.message.chat.id,
f"Thank you! You are {name}, {age} years old."
)
async def main():
bot = Bot(token="YOUR_TOKEN")
bot.add_handler(CommandHandler("start", start))
bot.add_handler(StateHandler("waiting_for_name", MessageHandler(process_name)))
bot.add_handler(StateHandler("waiting_for_age", MessageHandler(process_age)))
await bot.polling()
asyncio.run(main())
Example 4: Sending Photo and Document
import asyncio
from extergram import Bot, ContextTypes
from extergram.ext import CommandHandler
async def photo(context: ContextTypes):
await context.bot.send_photo(
context.message.chat.id,
photo="https://picsum.photos/400/300",
caption="Random photo"
)
async def doc(context: ContextTypes):
await context.bot.send_document(
context.message.chat.id,
document="/path/to/file.pdf",
caption="My document"
)
async def main(): bot = Bot(token="YOUR_TOKEN")
bot.add_handler(CommandHandler("photo", photo))
bot.add_handler(CommandHandler("doc", doc))
await bot.polling()
asyncio.run(main())
Example 5: Admin Command – Kick User
import asyncio
from extergram import Bot, ContextTypes, errors
from extergram.ext import CommandHandler
async def kick(context: ContextTypes):
if not context.message.reply_to_message:
await context.bot.send_message(
context.message.chat.id,
"Reply to a user's message to kick them."
)
return
user_id = context.message.reply_to_message.from_user.id
try:
await context.bot.ban_chat_member(context.message.chat.id, user_id)
await context.bot.send_message(
context.message.chat.id,
f"User {user_id} has been kicked."
)
except errors.TelegramAdminError as e:
await context.bot.send_message(
context.message.chat.id,
f"Failed: {e}"
)
async def main():
bot = Bot(token="YOUR_TOKEN")
bot.add_handler(CommandHandler("kick", kick))
await bot.polling()
asyncio.run(main())
Example 6: Using Markdown for Formatting
import asyncio
from extergram import Bot, ContextTypes, Markdown
from extergram.ext import CommandHandler
async def start(context: ContextTypes):
text = str(
Markdown("Welcome to ")
.bold("Extergram")
.text("!\n")
.italic("The async bot framework")
)
await context.bot.send_message(
context.message.chat.id,
text,
parse_mode="MarkdownV2"
)
async def main():
bot = Bot(token="YOUR_TOKEN")
bot.add_handler(CommandHandler("start", start))
await bot.polling()
asyncio.run(main())
🛡 Anti-Flood System
Extergram automatically tracks request frequency and introduces dynamic delays when approaching Telegram's rate limits. This helps prevent 429 Too Many Requests errors. No configuration is needed.
📄 License
This project is licensed under the MIT License. See the LICENSE file for details.
🌐 Links
- GitHub: https://github.com/TIBI624/extergram
- Issue Tracker: https://github.com/TIBI624/extergram/issues
- PyPI: https://pypi.org/project/extergram/
- Contribute: We welcome bug reports and pull requests! ❤️
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-0.8.1.tar.gz.
File metadata
- Download URL: extergram-0.8.1.tar.gz
- Upload date:
- Size: 19.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
341fd05adf35b558f414de96db90df044a15e77c50e677602c2620be5a1edd99
|
|
| MD5 |
705904bc2193aac7423c6a6a2bc8c351
|
|
| BLAKE2b-256 |
09638d91cd6dcea544704d6126d2c1ab4f3e5784e7c4848b41b8c3e1de644d17
|
File details
Details for the file extergram-0.8.1-py3-none-any.whl.
File metadata
- Download URL: extergram-0.8.1-py3-none-any.whl
- Upload date:
- Size: 17.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f63ef7d385aa4025064d6f30a7f123cdfbabe73bd1b40198b9a63147c7c1f692
|
|
| MD5 |
1b4bb79e9964a6b444ab0e33a7f992a8
|
|
| BLAKE2b-256 |
5847aabdc2a411382a9eb999bf236ab69e4b88edd9ef33905352677c7baeb25c
|