python-telegram-bot is a popular and robust Python library for building Telegram bots using the Telegram Bot API. It simplifies many aspects of communicating with the API, handling updates, parsing messages, and implementing bot logic. With python-telegram-bot, developers can focus on their bot's functionality rather than dealing with low-level HTTP requests and JSON parsing. The library is open-source and widely used, with a large community and comprehensive documentation.
Key Features
- Full Wrapper Around Telegram Bot API:
python-telegram-bot covers nearly all features of the Telegram Bot API, enabling you to send and receive messages, media, manage groups and channels, create inline keyboards, and more. - Extensive Documentation & Support:
The library is well-documented, with a detailed wiki, numerous examples, and active community support via GitHub issues and a Telegram support group. - Async and Sync Support:
With the release of v13 and beyond, python-telegram-bot supports both traditional synchronous operations and asyncio-based asynchronous code (introduced in v20), allowing for scalable, high-performance bots. - Update Handling with Different Models:
- Polling: Convenient for development and smaller bots. The bot sends getUpdates requests to Telegram and processes incoming updates.
- Webhooks: For production or performance-sensitive setups, you can set up a webhook so Telegram pushes updates to your server in real-time. The library can run its own webserver or integrate with frameworks like Flask or Django.
- Command and Message Handlers:
python-telegram-bot provides a Dispatcher and a rich set of handlers and filters (e.g., CommandHandler, MessageHandler, CallbackQueryHandler) that map specific message patterns, commands, or callback data to your handling functions. - Inline Queries and Keyboards:
Inline queries and inline keyboards are well-supported. The library provides classes and methods to create InlineKeyboardButtons, InlineKeyboardMarkup, and handle callbacks easily. - ConversationHandler:
A powerful feature to manage multi-step conversations. You can define states and transitions, making it straightforward to build guided user flows, forms, or interactive dialogs. - Persistent Storage:
Supports storing bot data, chat data, and user data across sessions using built-in persistence classes for different backends (like PicklePersistence) or custom persistence methods.
Installation
You can install python-telegram-bot using pip:
| pip install python-telegram-bot |
For async version (from v20 onwards), no special installation is needed since async support is included by default.
Basic Usage Example (Synchronous)
Here's a simple bot that responds to the /start command with a greeting:
| import logging from telegram import Update from telegram.ext import Updater, CommandHandler, CallbackContext # Enable logging logging.basicConfig(format='%(asctime)s – %(name)s – %(levelname)s – %(message)s', level=logging.INFO) def start_command(update: Update, context: CallbackContext): update.message.reply_text("Hello! I am your bot. How can I help you today?") def main(): # Replace 'YOUR_API_TOKEN' with your bot's token from BotFather updater = Updater("YOUR_API_TOKEN", use_context=True) dispatcher = updater.dispatcher # Add a CommandHandler for /start dispatcher.add_handler(CommandHandler("start", start_command)) # Start polling for updates updater.start_polling() # Run until you press Ctrl-C updater.idle() if __name__ == '__main__': main() |
How it works:
- Updater: Manages the connection to Telegram via long polling or webhooks, and channels updates to the Dispatcher.
- Dispatcher: Distributes incoming updates to handlers based on filters or commands.
- CommandHandler("start", start_command): Calls start_command whenever a user sends /start.
Using the Async Version (from v20 onwards)
Starting with v20, python-telegram-bot leverages asyncio. A similar bot using async calls might look like this:
| import asyncio import logging from telegram import Update from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes logging.basicConfig(level=logging.INFO) async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text("Hello! I am your async bot.") async def main(): app = ApplicationBuilder().token("YOUR_API_TOKEN").build() app.add_handler(CommandHandler("start", start_command)) await app.run_polling() if __name__ == '__main__': asyncio.run(main()) |
Here, ApplicationBuilder creates the bot application, and run_polling() is an async method that continuously fetches updates. Handlers and callbacks are async, and you use await when sending messages or performing other I/O tasks.
Handlers and Filters
A key strength of python-telegram-bot is the variety of handlers and filters:
- CommandHandler: Triggers on /command messages.
- MessageHandler: Matches text messages, media, or other content using filters.
- CallbackQueryHandler: Handles button presses on inline keyboards.
- InlineQueryHandler: Handles inline queries when a user types @YourBot in any chat.
Filters can limit which messages a handler should process. For example, Filters.text & ~Filters.command matches any text message that's not a command:
| from telegram.ext import MessageHandler, filters dispatcher.add_handler(MessageHandler(filters.Text() & ~filters.COMMAND, text_handler)) |
Inline Keyboards
To send a message with an inline keyboard:
| from telegram import InlineKeyboardButton, InlineKeyboardMarkup async def ask_question(update: Update, context: ContextTypes.DEFAULT_TYPE): keyboard = [ [InlineKeyboardButton("Option 1", callback_data='1'), InlineKeyboardButton("Option 2", callback_data='2')] ] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text("Choose an option:", reply_markup=reply_markup) async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await query.answer() # Acknowledge the callback choice = query.data await query.edit_message_text(text=f"You chose option {choice}") |
You would add these handlers with:
| app.add_handler(CommandHandler("ask", ask_question)) app.add_handler(CallbackQueryHandler(button_callback)) |
ConversationHandler
For multi-step interactions, define states and transitions:
| from telegram.ext import ConversationHandler, MessageHandler, CommandHandler, filters ASKING_NAME, ASKING_AGE = range(2) async def start_conversation(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text("What is your name?") return ASKING_NAME async def name_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): context.user_data['name'] = update.message.text await update.message.reply_text("What is your age?") return ASKING_AGE async def age_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): age = update.message.text name = context.user_data['name'] await update.message.reply_text(f"Nice to meet you {name}, age {age}.") return ConversationHandler.END async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text("Conversation cancelled.") return ConversationHandler.END conv_handler = ConversationHandler( entry_points=[CommandHandler('start', start_conversation)], states={ ASKING_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, name_handler)], ASKING_AGE: [MessageHandler(filters.TEXT & ~filters.COMMAND, age_handler)], }, fallbacks=[CommandHandler('cancel', cancel)] ) app.add_handler(conv_handler) |
This sets up a conversation flow where the user's responses guide them through different states until it ends.
Persistence
To keep data across restarts:
| from telegram.ext import PicklePersistence persistence = PicklePersistence(filepath='bot_data.pkl') app = ApplicationBuilder().token("TOKEN").persistence(persistence).build() |
Now, context.bot_data, context.chat_data, and context.user_data will be saved and restored automatically.
Webhook Setup
Instead of polling, you can set a webhook:
| app = ApplicationBuilder().token("YOUR_API_TOKEN").build() await app.bot.set_webhook("https://yourdomain.com/webhook") app.run_webhook( listen="0.0.0.0", port=8443, url_path="/webhook", webhook_url="https://yourdomain.com/webhook", # ssl context if needed ) |
Your server will receive updates instantly. Ensure the endpoint is HTTPS and accessible by Telegram.
Error Handling and Logging
Integrate logging and error handlers:
| async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE): # Log the error logging.error(msg="Exception while handling an update:", exc_info=context.error) app.add_error_handler(error_handler) |
This ensures you catch and log unexpected exceptions gracefully.
Common Patterns and Tips
- Environment Variables: Store your bot token in an environment variable and load it at runtime for security.
- Modular Code: Break your bot logic into separate modules or classes for better maintainability.
- Testing Locally: Start with polling in a local environment. For production, move to webhooks.
- Version Compatibility: Check the documentation for version compatibility, especially when migrating from synchronous to async versions (e.g., from v13 to v20).