CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-python-telegram-bot

A pure Python, asynchronous interface for the Telegram Bot API with comprehensive wrapper and high-level framework for building sophisticated Telegram bots

Pending
Overview
Eval results
Files

keyboards.mddocs/

Keyboards and Markup

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.

Capabilities

Reply Keyboards

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 | None

Usage 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()
)

Inline Keyboards

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 | None

Usage 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 Reply

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 | None

Usage 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...")
)

Specialized Button Types

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: str

Usage 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
    )
)

Dynamic Keyboard Building

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)

Handling Keyboard Responses

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))

Common Patterns

Pagination Keyboards

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)

Confirmation Dialogs

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)

Settings Menus

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

docs

advanced-features.md

application-framework.md

bot-api.md

files.md

filters.md

handlers.md

index.md

keyboards.md

telegram-types.md

tile.json