A pure Python, asynchronous interface for the Telegram Bot API with comprehensive wrapper and high-level framework for building sophisticated Telegram bots
—
Comprehensive keyboard system for creating interactive interfaces including reply keyboards, inline keyboards, and specialized button types for web apps, contact/location requests, and callback data.
Custom keyboards that replace the standard keyboard in the user's Telegram client.
class ReplyKeyboardMarkup:
def __init__(
self,
keyboard: list[list[KeyboardButton]],
is_persistent: bool = None,
resize_keyboard: bool = None,
one_time_keyboard: bool = None,
input_field_placeholder: str = None,
selective: bool = None
): ...
keyboard: list[list[KeyboardButton]]
is_persistent: bool | None
resize_keyboard: bool | None
one_time_keyboard: bool | None
input_field_placeholder: str | None
selective: bool | None
@classmethod
def from_button(cls, button: KeyboardButton, **kwargs) -> 'ReplyKeyboardMarkup': ...
@classmethod
def from_row(cls, *args: KeyboardButton, **kwargs) -> 'ReplyKeyboardMarkup': ...
@classmethod
def from_column(cls, *args: KeyboardButton, **kwargs) -> 'ReplyKeyboardMarkup': ...
class KeyboardButton:
def __init__(
self,
text: str,
request_users: KeyboardButtonRequestUsers = None,
request_chat: KeyboardButtonRequestChat = None,
request_contact: bool = None,
request_location: bool = None,
request_poll: KeyboardButtonPollType = None,
web_app: WebAppInfo = None
): ...
text: str
request_users: KeyboardButtonRequestUsers | None
request_chat: KeyboardButtonRequestChat | None
request_contact: bool | None
request_location: bool | None
request_poll: KeyboardButtonPollType | None
web_app: WebAppInfo | None
class ReplyKeyboardRemove:
def __init__(self, selective: bool = None): ...
remove_keyboard: bool = True
selective: bool | NoneUsage examples:
from telegram import ReplyKeyboardMarkup, KeyboardButton
# Simple text buttons
keyboard = [
["Option 1", "Option 2"],
["Option 3", "Option 4"]
]
reply_markup = ReplyKeyboardMarkup(keyboard, resize_keyboard=True)
# Using KeyboardButton objects
buttons = [
[KeyboardButton("Share Contact", request_contact=True)],
[KeyboardButton("Share Location", request_location=True)],
[KeyboardButton("Web App", web_app=WebAppInfo(url="https://example.com"))]
]
reply_markup = ReplyKeyboardMarkup(buttons, one_time_keyboard=True)
# Send message with keyboard
await bot.send_message(
chat_id=chat_id,
text="Choose an option:",
reply_markup=reply_markup
)
# Remove keyboard
from telegram import ReplyKeyboardRemove
await bot.send_message(
chat_id=chat_id,
text="Keyboard removed",
reply_markup=ReplyKeyboardRemove()
)Keyboards with buttons that appear directly below messages, supporting callbacks, URLs, and other actions.
class InlineKeyboardMarkup:
def __init__(self, inline_keyboard: list[list[InlineKeyboardButton]]): ...
inline_keyboard: list[list[InlineKeyboardButton]]
@classmethod
def from_button(cls, button: InlineKeyboardButton) -> 'InlineKeyboardMarkup': ...
@classmethod
def from_row(cls, *args: InlineKeyboardButton) -> 'InlineKeyboardMarkup': ...
@classmethod
def from_column(cls, *args: InlineKeyboardButton) -> 'InlineKeyboardMarkup': ...
class InlineKeyboardButton:
def __init__(
self,
text: str,
url: str = None,
callback_data: str = None,
web_app: WebAppInfo = None,
login_url: LoginUrl = None,
switch_inline_query: str = None,
switch_inline_query_current_chat: str = None,
switch_inline_query_chosen_chat: SwitchInlineQueryChosenChat = None,
copy_text: CopyTextButton = None,
callback_game: CallbackGame = None,
pay: bool = None
): ...
text: str
url: str | None
callback_data: str | None
web_app: WebAppInfo | None
login_url: LoginUrl | None
switch_inline_query: str | None
switch_inline_query_current_chat: str | None
switch_inline_query_chosen_chat: SwitchInlineQueryChosenChat | None
copy_text: CopyTextButton | None
callback_game: CallbackGame | None
pay: bool | NoneUsage examples:
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
# Callback data buttons
keyboard = [
[
InlineKeyboardButton("Option A", callback_data="opt_a"),
InlineKeyboardButton("Option B", callback_data="opt_b")
],
[InlineKeyboardButton("Help", callback_data="help")]
]
reply_markup = InlineKeyboardMarkup(keyboard)
# URL buttons
keyboard = [
[InlineKeyboardButton("Visit Website", url="https://example.com")],
[InlineKeyboardButton("Telegram Channel", url="https://t.me/channel")]
]
reply_markup = InlineKeyboardMarkup(keyboard)
# Mixed button types
keyboard = [
[InlineKeyboardButton("Callback", callback_data="cb_data")],
[InlineKeyboardButton("URL", url="https://example.com")],
[InlineKeyboardButton("Switch Inline", switch_inline_query="query")],
[InlineKeyboardButton("Web App", web_app=WebAppInfo(url="https://webapp.com"))]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await bot.send_message(
chat_id=chat_id,
text="Choose an action:",
reply_markup=reply_markup
)Force users to reply to a specific message.
class ForceReply:
def __init__(
self,
input_field_placeholder: str = None,
selective: bool = None
): ...
force_reply: bool = True
input_field_placeholder: str | None
selective: bool | NoneUsage example:
from telegram import ForceReply
await bot.send_message(
chat_id=chat_id,
text="Please tell me your name:",
reply_markup=ForceReply(input_field_placeholder="Enter your name...")
)Advanced button configurations for specific use cases.
class KeyboardButtonRequestUsers:
def __init__(
self,
request_id: int,
user_is_bot: bool = None,
user_is_premium: bool = None,
max_quantity: int = None,
request_name: bool = None,
request_username: bool = None,
request_photo: bool = None
): ...
request_id: int
user_is_bot: bool | None
user_is_premium: bool | None
max_quantity: int | None
request_name: bool | None
request_username: bool | None
request_photo: bool | None
class KeyboardButtonRequestChat:
def __init__(
self,
request_id: int,
chat_is_channel: bool,
chat_is_forum: bool = None,
chat_has_username: bool = None,
chat_is_created: bool = None,
user_administrator_rights: ChatAdministratorRights = None,
bot_administrator_rights: ChatAdministratorRights = None,
bot_is_member: bool = None,
request_title: bool = None,
request_username: bool = None,
request_photo: bool = None
): ...
request_id: int
chat_is_channel: bool
chat_is_forum: bool | None
chat_has_username: bool | None
chat_is_created: bool | None
user_administrator_rights: ChatAdministratorRights | None
bot_administrator_rights: ChatAdministratorRights | None
bot_is_member: bool | None
request_title: bool | None
request_username: bool | None
request_photo: bool | None
class KeyboardButtonPollType:
def __init__(self, type: str = None): ...
type: str | None
class WebAppInfo:
def __init__(self, url: str): ...
url: str
class LoginUrl:
def __init__(
self,
url: str,
forward_text: str = None,
bot_username: str = None,
request_write_access: bool = None
): ...
url: str
forward_text: str | None
bot_username: str | None
request_write_access: bool | None
class SwitchInlineQueryChosenChat:
def __init__(
self,
query: str = None,
allow_user_chats: bool = None,
allow_bot_chats: bool = None,
allow_group_chats: bool = None,
allow_channel_chats: bool = None
): ...
query: str | None
allow_user_chats: bool | None
allow_bot_chats: bool | None
allow_group_chats: bool | None
allow_channel_chats: bool | None
class CopyTextButton:
def __init__(self, text: str): ...
text: strUsage examples:
# Request user selection
user_request = KeyboardButtonRequestUsers(
request_id=1,
user_is_bot=False,
max_quantity=5,
request_name=True,
request_username=True
)
button = KeyboardButton("Select Users", request_users=user_request)
# Request chat selection
chat_request = KeyboardButtonRequestChat(
request_id=2,
chat_is_channel=False,
chat_has_username=True,
request_title=True
)
button = KeyboardButton("Select Chat", request_chat=chat_request)
# Poll type button
poll_button = KeyboardButton(
"Create Poll",
request_poll=KeyboardButtonPollType(type="quiz")
)
# Web app button
webapp_button = InlineKeyboardButton(
"Open Web App",
web_app=WebAppInfo(url="https://your-webapp.com")
)
# Login URL button
login_button = InlineKeyboardButton(
"Login",
login_url=LoginUrl(
url="https://your-site.com/auth",
request_write_access=True
)
)
# Switch inline query button
switch_button = InlineKeyboardButton(
"Share",
switch_inline_query_chosen_chat=SwitchInlineQueryChosenChat(
query="shared content",
allow_user_chats=True,
allow_group_chats=True
)
)Utility methods for building keyboards programmatically.
# Build keyboard from lists
def build_menu(buttons, n_cols, header_buttons=None, footer_buttons=None):
"""Build a menu keyboard with specified column layout."""
menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)]
if header_buttons:
menu.insert(0, header_buttons)
if footer_buttons:
menu.append(footer_buttons)
return menu
# Example usage
buttons = [
InlineKeyboardButton(f"Option {i}", callback_data=f"opt_{i}")
for i in range(1, 10)
]
keyboard = build_menu(
buttons,
n_cols=3,
header_buttons=[InlineKeyboardButton("Header", callback_data="header")],
footer_buttons=[InlineKeyboardButton("Cancel", callback_data="cancel")]
)
reply_markup = InlineKeyboardMarkup(keyboard)Processing user interactions with keyboards.
from telegram.ext import CallbackQueryHandler, MessageHandler, filters
# Handle callback query responses
async def button_handler(update, context):
query = update.callback_query
await query.answer() # Always acknowledge callback queries
data = query.data
if data == "opt_a":
await query.edit_message_text("You chose Option A")
elif data == "opt_b":
await query.edit_message_text("You chose Option B")
app.add_handler(CallbackQueryHandler(button_handler))
# Handle shared users/chats
async def handle_users_shared(update, context):
users_shared = update.message.users_shared
request_id = users_shared.request_id
users = users_shared.users
user_names = [f"{user.first_name}" for user in users if user.first_name]
await update.message.reply_text(f"You shared users: {', '.join(user_names)}")
async def handle_chat_shared(update, context):
chat_shared = update.message.chat_shared
request_id = chat_shared.request_id
chat_id = chat_shared.chat_id
await update.message.reply_text(f"You shared chat ID: {chat_id}")
# Filter for shared content
app.add_handler(MessageHandler(filters.StatusUpdate.USERS_SHARED, handle_users_shared))
app.add_handler(MessageHandler(filters.StatusUpdate.CHAT_SHARED, handle_chat_shared))def create_pagination_keyboard(items, page, per_page=5):
"""Create paginated inline keyboard."""
start = page * per_page
end = start + per_page
current_items = items[start:end]
keyboard = []
# Item buttons
for item in current_items:
keyboard.append([InlineKeyboardButton(item['name'], callback_data=f"item_{item['id']}")])
# Navigation buttons
nav_buttons = []
if page > 0:
nav_buttons.append(InlineKeyboardButton("◀️ Previous", callback_data=f"page_{page-1}"))
if end < len(items):
nav_buttons.append(InlineKeyboardButton("Next ▶️", callback_data=f"page_{page+1}"))
if nav_buttons:
keyboard.append(nav_buttons)
return InlineKeyboardMarkup(keyboard)def create_confirmation_keyboard(action_data):
"""Create Yes/No confirmation keyboard."""
keyboard = [
[
InlineKeyboardButton("✅ Yes", callback_data=f"confirm_{action_data}"),
InlineKeyboardButton("❌ No", callback_data="cancel")
]
]
return InlineKeyboardMarkup(keyboard)def create_settings_keyboard(user_settings):
"""Create settings toggle keyboard."""
keyboard = []
for setting, value in user_settings.items():
status = "✅" if value else "❌"
keyboard.append([
InlineKeyboardButton(
f"{status} {setting.title()}",
callback_data=f"toggle_{setting}"
)
])
keyboard.append([InlineKeyboardButton("💾 Save", callback_data="save_settings")])
return InlineKeyboardMarkup(keyboard)Install with Tessl CLI
npx tessl i tessl/pypi-python-telegram-bot