A complete reference for developing interactive features for Telegram Bots
Features:
Use Cases: Confirmation/cancellation, menu navigation, pagination control, setting options
Features:
Use Cases: Quick commands, common actions, form input, main menu
Features:
Use Cases: Function index, new user guidance, quick command access
| Feature | Inline | Reply | Command Menu |
|---|---|---|---|
| Position | Below message | Above input field | "/" menu |
| Trigger | Callback query | Text message | Command |
| Persistence | With message | Configurable | Always present |
| Scenario | Temporary interaction | Resident function | Command index |
Advantages:
Installation:
pip install python-telegram-bot==20.7
Advantages:
Installation:
pip install telethon cryptg
python-telegram-bot:
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Display an inline keyboard"""
keyboard = [
[
InlineKeyboardButton("📊 View Data", callback_data="view_data"),
InlineKeyboardButton("⚙️ Settings", callback_data="settings"),
],
[
InlineKeyboardButton("🔗 Visit Website", url="https://example.com"),
],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text("Please choose:", reply_markup=reply_markup)
async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle button clicks"""
query = update.callback_query
await query.answer() # Must be called
if query.data == "view_data":
await query.edit_message_text("Displaying data...")
elif query.data == "settings":
await query.edit_message_text("Settings options...")
# Register handlers
app = Application.builder().token("TOKEN").build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CallbackQueryHandler(button_callback))
app.run_polling()
Telethon:
from telethon import TelegramClient, events, Button
client = TelegramClient('bot', api_id, api_hash).start(bot_token=BOT_TOKEN)
@client.on(events.NewMessage(pattern='/start'))
async def start(event):
buttons = [
[Button.inline("📊 View Data", b"view_data"), Button.inline("⚙️ Settings", b"settings")],
[Button.url("🔗 Visit Website", "https://example.com")]
]
await event.respond("Please choose:", buttons=buttons)
@client.on(events.CallbackQuery)
async def callback(event):
if event.data == b"view_data":
await event.edit("Displaying data...")
elif event.data == b"settings":
await event.edit("Settings options...")
client.run_until_disconnected()
python-telegram-bot:
from telegram import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
async def menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Display a reply keyboard"""
keyboard = [
[KeyboardButton("📊 View Data"), KeyboardButton("⚙️ Settings")],
[KeyboardButton("📚 Help"), KeyboardButton("❌ Hide Keyboard")],
]
reply_markup = ReplyKeyboardMarkup(
keyboard,
resize_keyboard=True,
one_time_keyboard=False
)
await update.message.reply_text("Menu activated", reply_markup=reply_markup)
async def handle_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle text messages"""
text = update.message.text
if text == "📊 View Data":
await update.message.reply_text("Displaying data...")
elif text == "❌ Hide Keyboard":
await update.message.reply_text("Keyboard hidden", reply_markup=ReplyKeyboardRemove())
Telethon:
@client.on(events.NewMessage(pattern='/menu'))
async def menu(event):
buttons = [
[Button.text("📊 View Data"), Button.text("⚙️ Settings")],
[Button.text("📚 Help"), Button.text("❌ Hide Keyboard")]
]
await event.respond("Menu activated", buttons=buttons)
@client.on(events.NewMessage)
async def handle_text(event):
if event.text == "📊 View Data":
await event.respond("Displaying data...")
Via BotFather:
1. Send /setcommands to @BotFather
2. Choose your Bot
3. Enter the list of commands (format per line: command - description)
start - Start the bot
help - Get help
menu - Display the main menu
settings - Configure settings
Via API (python-telegram-bot):
from telegram import BotCommand
async def set_commands(app: Application):
"""Set the command menu"""
commands = [
BotCommand("start", "Start the bot"),
BotCommand("help", "Get help"),
BotCommand("menu", "Display the main menu"),
BotCommand("settings", "Configure settings"),
]
await app.bot.set_my_commands(commands)
# Call on startup
app.post_init = set_commands
telegram_bot/
├── bot.py # Main program
├── config.py # Configuration management
├── requirements.txt
├── .env
├── handlers/
│ ├── command_handlers.py # Command handlers
│ ├── callback_handlers.py # Callback handlers
│ └── message_handlers.py # Message handlers
├── keyboards/
│ ├── inline_keyboards.py # Inline keyboard layouts
│ └── reply_keyboards.py # Reply keyboard layouts
└── utils/
├── logger.py # Logger
└── database.py # Database
Modular Example (keyboards/inline_keyboards.py):
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
def get_main_menu():
"""Main menu keyboard"""
return InlineKeyboardMarkup([
[
InlineKeyboardButton("📊 Data", callback_data="data"),
InlineKeyboardButton("⚙️ Settings", callback_data="settings"),
],
[InlineKeyboardButton("📚 Help", callback_data="help")],
])
def get_data_menu():
"""Data menu keyboard"""
return InlineKeyboardMarkup([
[
InlineKeyboardButton("📈 Real-time", callback_data="data_realtime"),
InlineKeyboardButton("📊 History", callback_data="data_history"),
],
[InlineKeyboardButton("⬅️ Back", callback_data="back")],
])
# Match in order of registration, from most specific to most general
app.add_handler(CommandHandler("start", start)) # 1. Specific command
app.add_handler(CallbackQueryHandler(callback)) # 2. Callback query
app.add_handler(ConversationHandler(...)) # 3. Conversation flow
app.add_handler(MessageHandler(filters.TEXT, text_msg)) # 4. General message (last)
async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Global error handler"""
logger.error(f"Update {update} caused error", exc_info=context.error)
# Notify the user
if update and update.effective_message:
await update.effective_message.reply_text("Operation failed, please try again")
app.add_error_handler(error_handler)
# Use structured callback_data
callback_data = "action:page:item" # e.g., "view:1:product_123"
# Parse callback data
async def callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
parts = query.data.split(":")
action, page, item = parts
if action == "view":
await show_item(query, page, item)
# Verify user permissions
ADMIN_IDS = [123456789]
async def admin_only(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
if user_id not in ADMIN_IDS:
await update.message.reply_text("Permission denied")
return
# Execute admin operations
Webhook (Recommended for production):
from flask import Flask, request
app_flask = Flask(__name__)
@app_flask.route('/webhook', methods=['POST'])
def webhook():
update = Update.de_json(request.get_json(), bot)
application.update_queue.put(update)
return "OK"
# Set webhook
bot.set_webhook(f"https://yourdomain.com/webhook")
Systemd Service (Linux):
[Unit]
Description=Telegram Bot
After=network.target
[Service]
Type=simple
User=your_user
WorkingDirectory=/path/to/bot
ExecStart=/path/to/venv/bin/python bot.py
Restart=always
[Install]
WantedBy=multi-user.target
# requirements.txt
python-telegram-bot==20.7
python-dotenv==1.0.0
aiosqlite==0.19.0
httpx==0.25.2
InlineKeyboardButton("Text", callback_data="data") # Callback button
InlineKeyboardButton("Link", url="https://...") # URL button
InlineKeyboardButton("Switch", switch_inline_query="") # Inline query
InlineKeyboardButton("Login", login_url=...) # Login button
InlineKeyboardButton("Pay", pay=True) # Payment button
InlineKeyboardButton("App", web_app=WebAppInfo(...)) # Mini App
events.NewMessage - New messageevents.CallbackQuery - Callback queryevents.InlineQuery - Inline queryevents.ChatAction - Group actionThis guide covers all the core implementations of Telegram Bot buttons and keyboards!