کار با دکمه‌های شیشه‌ای (Inline Keyboards) در Telebot: افزایش کارایی ربات

فهرست مطالب

کار با دکمه‌های شیشه‌ای (Inline Keyboards) در Telebot: افزایش کارایی ربات

در دنیای امروز که تعاملات دیجیتال بخش جدایی‌ناپذیری از زندگی روزمره ما شده‌اند، ربات‌های تلگرام به ابزارهایی قدرتمند برای اتوماسیون، اطلاع‌رسانی و ارائه خدمات مختلف تبدیل شده‌اند. با این حال، کارایی یک ربات نه تنها به قابلیت‌های پردازشی آن، بلکه به سهولت و شهودپذیری رابط کاربری آن نیز بستگی دارد. در این میان، دکمه‌های شیشه‌ای (Inline Keyboards) یکی از کاربردی‌ترین و پیشرفته‌ترین قابلیت‌هایی هستند که کتابخانه Telebot (PyTelegramBotAPI) برای توسعه ربات‌های تلگرام در اختیار ما قرار می‌دهد. این دکمه‌ها، که برخلاف کیبوردهای پاسخ‌دهنده (Reply Keyboards) مستقیماً به پیام متصل بوده و فضای چت را اشغال نمی‌کنند، امکانات بی‌نظیری برای ایجاد تجربه‌های کاربری پویا و تعاملی فراهم می‌آورند و نقش کلیدی در افزایش کارایی ربات ایفا می‌کنند.

این مقاله به صورت جامع و تخصصی به بررسی نحوه کار با دکمه‌های شیشه‌ای در Telebot می‌پردازد. از مفاهیم پایه‌ای و پیاده‌سازی اولیه گرفته تا سناریوهای پیچیده، مدیریت داده‌های برگشتی (Callback Data)، ادغام با پایگاه داده و بهترین شیوه‌های طراحی تجربه کاربری، تمام جنبه‌ها را پوشش خواهیم داد. هدف ما این است که شما را با تمام ابزارهای لازم برای ساخت ربات‌های تلگرام قدرتمند و تعاملی مجهز کنیم که نه تنها نیازهای فنی شما را برآورده سازند، بلکه لذت کاربری بی‌نظیری را نیز ارائه دهند.

مقدمه‌ای بر دکمه‌های شیشه‌ای (Inline Keyboards) و جایگاه آن‌ها در Telebot

برای شروع، لازم است درک عمیقی از ماهیت دکمه‌های شیشه‌ای و تفاوت‌های بنیادین آن‌ها با سایر روش‌های تعاملی در تلگرام به دست آوریم. Telebot، که یک کتابخانه پایتونی برای تعامل با Telegram Bot API است، امکان ساخت هر دو نوع کیبورد پاسخ‌دهنده و دکمه‌های شیشه‌ای را فراهم می‌کند. اما کاربرد و فلسفه طراحی هر یک کاملاً متفاوت است.

تفاوت اساسی: Inline Keyboards در مقابل Reply Keyboards

کیبوردهای پاسخ‌دهنده (Reply Keyboards): این کیبوردها در پایین صفحه چت ظاهر می‌شوند، دقیقاً بالای فیلد ورودی متن. آن‌ها برای ارائه گزینه‌های ثابت و عمومی به کاربر مناسب هستند، مانند “شروع مجدد”، “راهنما” یا “لیست محصولات”. ویژگی اصلی آن‌ها این است که با انتخاب یک دکمه، متن آن دکمه به ربات ارسال شده و از دید کاربر، انگار خودش آن متن را تایپ کرده است. این کیبوردها فضای چت را اشغال می‌کنند و می‌توانند توسط کاربر پنهان یا باز شوند.

دکمه‌های شیشه‌ای (Inline Keyboards): این دکمه‌ها مستقیماً به یک پیام خاص در چت پیوست می‌شوند. برخلاف Reply Keyboards، با کلیک بر روی دکمه‌های شیشه‌ای، یک “Callback Query” به ربات ارسال می‌شود که شامل داده‌های تعریف شده توسط توسعه‌دهنده است. این دکمه‌ها فضای چت را اشغال نمی‌کنند و همواره در زیر پیامی که به آن متصل هستند، نمایش داده می‌شوند. این ویژگی آن‌ها را برای سناریوهایی که نیاز به تعامل با محتوای یک پیام خاص داریم (مانند رأی‌گیری، پیمایش در لیست‌ها، تأیید عملیات) ایده‌آل می‌سازد.

چرا Inline Keyboards برای افزایش کارایی ربات ضروری هستند؟

  1. تعامل متنی با پیام: Inline Keyboards به شما امکان می‌دهند تا دکمه‌هایی را دقیقاً در کنار اطلاعات مرتبط قرار دهید. به عنوان مثال، اگر ربات شما یک لیست از محصولات را ارسال می‌کند، می‌توانید دکمه “افزودن به سبد خرید” را دقیقاً زیر هر محصول قرار دهید. این کار تجربه کاربری را به شدت بهبود می‌بخشد و ابهام را از بین می‌برد.
  2. پویایی و تغییر: محتوای دکمه‌های شیشه‌ای می‌تواند پس از ارسال پیام به صورت پویا تغییر کند. به عنوان مثال، پس از انتخاب یک گزینه، می‌توانید همان پیام را با دکمه‌های جدید یا بدون دکمه ویرایش کنید. این قابلیت برای ساخت منوهای تو در تو، سیستم‌های رأی‌گیری زنده، یا فرم‌های چند مرحله‌ای حیاتی است.
  3. صرفه‌جویی در فضای چت: چون دکمه‌های شیشه‌ای به پیام متصل هستند و در فضای ورودی متن نیستند، فضای چت کمتر شلوغ می‌شود و پیام‌ها واضح‌تر دیده می‌شوند.
  4. انعطاف‌پذیری بالا: قابلیت اتصال به URL، ارسال Inline Query جدید، و مهم‌تر از همه، ارسال داده‌های سفارشی (Callback Data) به ربات، انعطاف‌پذیری بی‌نظیری را برای پیاده‌سازی منطق‌های پیچیده فراهم می‌کند.

در واقع، بدون دکمه‌های شیشه‌ای، بسیاری از قابلیت‌های پیشرفته و مدرن ربات‌های تلگرام غیرقابل پیاده‌سازی یا به شدت دشوار خواهند بود. آن‌ها ستون فقرات رابط کاربری برای ربات‌های تعاملی و قدرتمند را تشکیل می‌دهند.

ساختار پایه و پیاده‌سازی دکمه‌های شیشه‌ای در Telebot

برای شروع کار با دکمه‌های شیشه‌ای در Telebot، باید با دو کلاس اصلی آشنا شویم: InlineKeyboardMarkup و InlineKeyboardButton.

InlineKeyboardMarkup: کانتینر دکمه‌ها

این کلاس به عنوان یک کانتینر (Container) برای نگهداری تمام دکمه‌های شیشه‌ای شما عمل می‌کند. شما یک نمونه از این کلاس را ایجاد می‌کنید و سپس دکمه‌های خود را به آن اضافه می‌کنید. این InlineKeyboardMarkup نهایتاً به عنوان پارامتر reply_markup به تابع ارسال پیام (مانند bot.send_message یا bot.edit_message_text) ارسال می‌شود.

InlineKeyboardButton: تعریف یک دکمه

هر دکمه شیشه‌ای توسط یک نمونه از کلاس InlineKeyboardButton تعریف می‌شود. این کلاس پارامترهای مختلفی را می‌پذیرد که رفتار دکمه را تعیین می‌کنند:

  • text: متنی که روی دکمه نمایش داده می‌شود. (الزامی)
  • callback_data: داده‌ای که هنگام کلیک کاربر روی دکمه به ربات ارسال می‌شود. این داده باید یک رشته باشد و حداکثر 64 بایت طول دارد. (معمولاً برای دکمه‌های عملیاتی استفاده می‌شود)
  • url: یک آدرس اینترنتی که با کلیک روی دکمه، کاربر به آن هدایت می‌شود. (برای دکمه‌های لینک)
  • switch_inline_query: متنی که با کلیک روی دکمه، فیلد ورودی متن را به حالت Inline Query تغییر می‌دهد و متن داده شده را در آن وارد می‌کند.
  • switch_inline_query_current_chat: مشابه switch_inline_query، اما Inline Query را فقط در چت فعلی فعال می‌کند.
  • login_url: (مخصوص) برای لینک‌های ورود به سایت با OAuth 2.0.

برای یک دکمه معمولی که یک عملیات را در ربات شما آغاز می‌کند، معمولاً از text و callback_data استفاده می‌کنیم.

مثال پایه: ایجاد و ارسال یک دکمه شیشه‌ای ساده

بیایید یک مثال ساده را پیاده‌سازی کنیم که یک پیام با یک دکمه “سلام” ارسال می‌کند:


import telebot
from telebot import types

# توکن ربات خود را اینجا وارد کنید
TOKEN = "YOUR_BOT_TOKEN"
bot = telebot.TeleBot(TOKEN)

@bot.message_handler(commands=['start'])
def send_welcome(message):
    # ایجاد یک کیبورد Inline
    markup = types.InlineKeyboardMarkup()
    
    # ایجاد یک دکمه Inline
    # text: متنی که روی دکمه نمایش داده می‌شود
    # callback_data: داده‌ای که هنگام کلیک روی دکمه به ربات ارسال می‌شود
    button_hello = types.InlineKeyboardButton("سلام!", callback_data="say_hello")
    
    # اضافه کردن دکمه به کیبورد
    markup.add(button_hello)
    
    # ارسال پیام به همراه کیبورد Inline
    bot.send_message(message.chat.id, "برای سلام کردن دکمه زیر را فشار دهید:", reply_markup=markup)

# ربات را اجرا کنید
bot.infinity_polling()

در این مثال، وقتی کاربر دستور `/start` را ارسال می‌کند، ربات پیامی را به همراه یک دکمه “سلام!” ارسال می‌کند. اما در حال حاضر، کلیک کردن روی این دکمه هیچ اتفاقی را در ربات ما رقم نمی‌زند، زیرا هنوز Handler مربوط به `callback_query` را تعریف نکرده‌ایم. اینجاست که مفهوم callback_data و Handlerهای مربوط به آن وارد عمل می‌شوند.

مدیریت داده‌های برگشتی (Callback Data) و دریافت پاسخ از کاربر

مهمترین ویژگی دکمه‌های شیشه‌ای، توانایی آن‌ها در ارسال داده‌های سفارشی به ربات بدون نیاز به تایپ کردن کاربر است. این داده‌ها توسط پارامتر callback_data در InlineKeyboardButton تعریف می‌شوند و هنگامی که کاربر روی دکمه کلیک می‌کند، در قالب یک CallbackQuery به ربات ارسال می‌گردند.

ساختار CallbackQuery

هنگامی که کاربری روی یک دکمه شیشه‌ای کلیک می‌کند، تلگرام یک به‌روزرسانی (Update) از نوع callback_query به ربات شما ارسال می‌کند. این CallbackQuery شامل اطلاعاتی مانند:

  • id: شناسه منحصر به فرد Callback Query.
  • from_user: اطلاعات کاربری که روی دکمه کلیک کرده است.
  • message: شیء پیامی که دکمه به آن پیوست شده بود. (اگر دکمه به یک پیام از ربات متصل باشد)
  • inline_message_id: شناسه پیام اگر دکمه به یک پیام از Inline Query متصل باشد.
  • data: رشته‌ای که شما به عنوان callback_data برای دکمه تعریف کرده‌اید.

استفاده از @bot.callback_query_handler

برای دریافت و پردازش این CallbackQueryها، Telebot یک دکوراتور مخصوص به نام @bot.callback_query_handler را فراهم می‌کند. این دکوراتور تابعی را به عنوان Handler برای Callback Queryها ثبت می‌کند. شما می‌توانید از فیلترهای مختلفی مانند func برای مطابقت با callback_data خاصی استفاده کنید.

مثال: پردازش callback_data

بیایید مثال قبلی را تکمیل کنیم و Handler برای دکمه “سلام!” اضافه کنیم:


import telebot
from telebot import types

TOKEN = "YOUR_BOT_TOKEN"
bot = telebot.TeleBot(TOKEN)

@bot.message_handler(commands=['start'])
def send_welcome(message):
    markup = types.InlineKeyboardMarkup()
    button_hello = types.InlineKeyboardButton("سلام!", callback_data="say_hello")
    markup.add(button_hello)
    bot.send_message(message.chat.id, "برای سلام کردن دکمه زیر را فشار دهید:", reply_markup=markup)

# Handler برای Callback Query ها
@bot.callback_query_handler(func=lambda call: call.data == "say_hello")
def callback_inline(call):
    try:
        if call.data == "say_hello":
            # پاسخ به Callback Query
            # این کار باعث حذف "ساعت شنی" از روی دکمه می‌شود و می‌تواند یک پیام موقت نمایش دهد.
            bot.answer_callback_query(call.id, "شما دکمه سلام را فشار دادید!")
            
            # ارسال یک پیام جدید
            bot.send_message(call.message.chat.id, f"سلام، {call.from_user.first_name}!")
            
            # یا ویرایش پیام قبلی
            # bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, 
            #                       text="دکمه سلام فشرده شد و پیام ویرایش شد!", reply_markup=None)

    except Exception as e:
        print(repr(e))

bot.infinity_polling()

در این کد، @bot.callback_query_handler(func=lambda call: call.data == "say_hello") تضمین می‌کند که تابع callback_inline تنها زمانی فراخوانی شود که callback_data دکمه فشرده شده، برابر با “say_hello” باشد.

bot.answer_callback_query: بازخورد به کاربر

پس از دریافت و پردازش یک CallbackQuery، بسیار مهم است که با استفاده از bot.answer_callback_query(call.id, ...) به تلگرام پاسخ دهید. اگر این کار را نکنید، کاربر پس از کلیک روی دکمه، یک نماد “ساعت شنی” یا “بارگذاری” روی دکمه مشاهده خواهد کرد که تا ابد باقی می‌ماند و تجربه کاربری بدی ایجاد می‌کند. این تابع پارامترهای مختلفی دارد:

  • callback_query_id: (الزامی) شناسه CallbackQuery که در شیء call.id موجود است.
  • text: یک پیام متنی کوتاه که به صورت یک اعلان (Notification) یا Toast (بالا یا پایین صفحه) به کاربر نمایش داده می‌شود. این پارامتر اختیاری است.
  • show_alert: یک مقدار بولین (True/False) که اگر True باشد، text به صورت یک پنجره پاپ‌آپ (Modal Alert) نمایش داده می‌شود. اگر False باشد (پیش‌فرض)، به صورت اعلان کوچک نمایش داده می‌شود.
  • url: یک URL که می‌توان با پاسخ به Callback Query، کاربر را به آن هدایت کرد.
  • cache_time: مدت زمانی بر حسب ثانیه که پاسخ Callback Query باید در سمت کلاینت کش (Cache) شود.

همیشه باید bot.answer_callback_query را فراخوانی کنید، حتی اگر فقط با call.id باشد و پیام متنی نداشته باشد، تا نشانگر بارگذاری از روی دکمه برداشته شود.

پیاده‌سازی سناریوهای پیشرفته با دکمه‌های شیشه‌ای: از URL تا تغییر حالت

قدرت واقعی دکمه‌های شیشه‌ای در قابلیت‌های پیشرفته آن‌ها نهفته است که امکان ساخت ربات‌های بسیار پیچیده و پویا را فراهم می‌کند. در این بخش، به بررسی این سناریوها می‌پردازیم.

دکمه‌های URL

این نوع دکمه‌ها کاربران را به یک آدرس اینترنتی خاص هدایت می‌کنند. برای مثال، می‌توانید دکمه “بازدید از سایت” یا “لینک محصول” را اضافه کنید.


import telebot
from telebot import types

TOKEN = "YOUR_BOT_TOKEN"
bot = telebot.TeleBot(TOKEN)

@bot.message_handler(commands=['website'])
def send_website_button(message):
    markup = types.InlineKeyboardMarkup()
    # دکمه‌ای که به URL هدایت می‌کند
    button_website = types.InlineKeyboardButton("بازدید از وبسایت", url="https://www.example.com")
    markup.add(button_website)
    bot.send_message(message.chat.id, "برای بازدید از وبسایت ما، روی دکمه زیر کلیک کنید:", reply_markup=markup)

bot.infinity_polling()

دکمه‌های URL نیازی به callback_data یا پردازش توسط ربات ندارند، زیرا تلگرام خود وظیفه باز کردن لینک را بر عهده می‌گیرد.

دکمه‌های Switch Inline Query

این دکمه‌ها به کاربر اجازه می‌دهند تا به حالت Inline Query برود و یک متن از پیش تعریف شده را در فیلد ورودی متن قرار دهد. این قابلیت برای ربات‌هایی که می‌توانند جستجو یا تولید محتوا به صورت Inline انجام دهند، بسیار مفید است.


import telebot
from telebot import types

TOKEN = "YOUR_BOT_TOKEN"
bot = telebot.TeleBot(TOKEN)

@bot.message_handler(commands=['inline_search'])
def send_inline_search_button(message):
    markup = types.InlineKeyboardMarkup()
    # دکمه‌ای برای شروع Inline Query در هر چتی
    button_global_inline = types.InlineKeyboardButton("جستجوی سراسری", switch_inline_query="مثال جستجو")
    
    # دکمه‌ای برای شروع Inline Query فقط در چت فعلی
    button_current_chat_inline = types.InlineKeyboardButton("جستجو در اینجا", switch_inline_query_current_chat="جستجوی داخلی")
    
    markup.add(button_global_inline, button_current_chat_inline)
    bot.send_message(message.chat.id, "می‌خواهید جستجو را امتحان کنید؟", reply_markup=markup)

# Handler برای Inline Query (مورد نیاز برای فعال کردن قابلیت switch_inline_query)
@bot.inline_handler(lambda query: True)
def query_text(inline_query):
    try:
        # نمونه‌ای از پاسخ Inline Query
        r = types.InlineQueryResultArticle('1', 'عنوان نمونه', types.InputTextMessageContent(f"شما جستجو کردید: {inline_query.query}"))
        bot.answer_inline_query(inline_query.id, [r])
    except Exception as e:
        print(repr(e))

bot.infinity_polling()

ساخت دکمه‌های چند ردیفه و چند ستونی

InlineKeyboardMarkup به شما اجازه می‌دهد تا دکمه‌ها را در ردیف‌های مختلف اضافه کنید. متد add() هر تعداد دکمه‌ای که به آن بدهید را در یک ردیف قرار می‌دهد. برای ایجاد ردیف‌های جدید، کافیست دوباره add() را فراخوانی کنید.


import telebot
from telebot import types

TOKEN = "YOUR_BOT_TOKEN"
bot = telebot.TeleBot(TOKEN)

@bot.message_handler(commands=['menu'])
def show_menu(message):
    markup = types.InlineKeyboardMarkup()
    
    # ردیف اول: دو دکمه
    btn1 = types.InlineKeyboardButton("گزینه ۱", callback_data="option_1")
    btn2 = types.InlineKeyboardButton("گزینه ۲", callback_data="option_2")
    markup.add(btn1, btn2)
    
    # ردیف دوم: یک دکمه عریض
    btn3 = types.InlineKeyboardButton("گزینه ۳", callback_data="option_3")
    markup.add(btn3)
    
    # ردیف سوم: سه دکمه
    btn4 = types.InlineKeyboardButton("آ", callback_data="char_a")
    btn5 = types.InlineKeyboardButton("ب", callback_data="char_b")
    btn6 = types.InlineKeyboardButton("پ", callback_data="char_p")
    markup.add(btn4, btn5, btn6)
    
    bot.send_message(message.chat.id, "یک گزینه را انتخاب کنید:", reply_markup=markup)

@bot.callback_query_handler(func=lambda call: call.data.startswith(('option_', 'char_')))
def process_menu_selection(call):
    bot.answer_callback_query(call.id, f"شما {call.data} را انتخاب کردید.")
    bot.edit_message_text(chat_id=call.message.chat.id, 
                          message_id=call.message.message_id, 
                          text=f"شما گزینه '{call.data}' را انتخاب کردید.", 
                          reply_markup=None) # حذف کیبورد پس از انتخاب

bot.infinity_polling()

صفحه‌بندی (Pagination) با دکمه‌های شیشه‌ای

یکی از قدرتمندترین کاربردهای دکمه‌های شیشه‌ای، پیاده‌سازی صفحه‌بندی برای نمایش لیست‌های طولانی از آیتم‌ها است. این کار با ویرایش پیام قبلی و تغییر دکمه‌ها انجام می‌شود.


import telebot
from telebot import types

TOKEN = "YOUR_BOT_TOKEN"
bot = telebot.TeleBot(TOKEN)

# یک لیست فرضی از آیتم‌ها
items = [f"آیتم {i}" for i in range(1, 21)]
items_per_page = 5

def get_page_keyboard(page=0):
    markup = types.InlineKeyboardMarkup()
    start_index = page * items_per_page
    end_index = start_index + items_per_page
    
    current_page_items = items[start_index:end_index]
    
    for item in current_page_items:
        markup.add(types.InlineKeyboardButton(item, callback_data=f"select_{item}"))
    
    nav_buttons = []
    if page > 0:
        nav_buttons.append(types.InlineKeyboardButton("<< قبلی", callback_data=f"page_{page - 1}"))
    
    total_pages = (len(items) + items_per_page - 1) // items_per_page
    if page < total_pages - 1:
        nav_buttons.append(types.InlineKeyboardButton("بعدی >>", callback_data=f"page_{page + 1}"))
        
    if nav_buttons:
        markup.add(*nav_buttons) # اضافه کردن دکمه‌های ناوبری در یک ردیف
        
    return markup, current_page_items

@bot.message_handler(commands=['list'])
def send_list(message):
    markup, current_items = get_page_keyboard(0)
    bot.send_message(message.chat.id, f"لیست آیتم‌ها (صفحه 1):\n{'n'.join(current_items)}", reply_markup=markup)

@bot.callback_query_handler(func=lambda call: call.data.startswith('page_') or call.data.startswith('select_'))
def process_pagination(call):
    if call.data.startswith('page_'):
        page = int(call.data.split('_')[1])
        markup, current_items = get_page_keyboard(page)
        
        # ویرایش پیام قبلی با دکمه‌های جدید و محتوای جدید
        bot.edit_message_text(chat_id=call.message.chat.id,
                              message_id=call.message.message_id,
                              text=f"لیست آیتم‌ها (صفحه {page + 1}):\n{'n'.join(current_items)}",
                              reply_markup=markup)
        bot.answer_callback_query(call.id, f"به صفحه {page + 1} رفتید.")
    elif call.data.startswith('select_'):
        selected_item = call.data.split('_')[1]
        bot.answer_callback_query(call.id, f"شما '{selected_item}' را انتخاب کردید.")
        bot.edit_message_text(chat_id=call.message.chat.id,
                              message_id=call.message.message_id,
                              text=f"شما '{selected_item}' را انتخاب کردید!nپیام اصلی بسته شد.",
                              reply_markup=None) # حذف کیبورد پس از انتخاب
    
bot.infinity_polling()

این مثال نشان می‌دهد که چگونه می‌توان با ترکیب callback_data و bot.edit_message_text یک سیستم صفحه‌بندی پویا ایجاد کرد. توجه داشته باشید که bot.edit_message_text به ما اجازه می‌دهد تا هم متن پیام و هم reply_markup آن را تغییر دهیم، که برای منوهای پویا ضروری است.

طراحی تجربه کاربری (UX) و بهترین شیوه‌ها برای دکمه‌های شیشه‌ای

صرف نظر از قابلیت‌های فنی، طراحی دکمه‌های شیشه‌ای باید با در نظر گرفتن بهترین شیوه‌های UX انجام شود تا تجربه کاربری روان و دلپذیری را ارائه دهد.

۱. خوانایی و اختصار در متن دکمه

متن روی دکمه‌ها باید کوتاه، واضح و گویا باشد. از جملات طولانی خودداری کنید. هر دکمه باید به وضوح عمل خود را نشان دهد (مثلاً “تأیید”، “لغو”، “بعدی”، “انتخاب”).

۲. ساختار منطقی callback_data

callback_data شما باید ساختار یافته و قابل پیش‌بینی باشد تا پردازش آن در Handlerها آسان شود. یک روش رایج استفاده از الگوی {action}_{id/param} است. مثلاً: edit_item_123، delete_user_abc، page_next_5. این کار به شما امکان می‌دهد تا با یک Handler کلی، انواع مختلفی از Callback Query را پردازش کنید و سپس بر اساس action (مثلاً edit یا delete) منطق متفاوتی را اعمال کنید.


# در یک Handler callback_query_handler عمومی
@bot.callback_query_handler(func=lambda call: True)
def process_all_callbacks(call):
    data_parts = call.data.split('_')
    action = data_parts[0]
    
    if action == 'edit':
        item_id = data_parts[1]
        # منطق ویرایش
    elif action == 'delete':
        user_id = data_parts[1]
        # منطق حذف
    elif action == 'page':
        page_number = int(data_parts[1])
        # منطق صفحه‌بندی
    
    bot.answer_callback_query(call.id, f"عملیات {action} انجام شد.")

توجه داشته باشید که callback_data دارای محدودیت 64 بایتی است. برای داده‌های پیچیده‌تر یا طولانی‌تر، باید از روش‌های دیگری مانند ذخیره وضعیت در دیتابیس یا استفاده از شناسه‌های کوتاه استفاده کنید.

۳. ارائه بازخورد به کاربر

همیشه پس از پردازش یک CallbackQuery، با استفاده از bot.answer_callback_query به کاربر بازخورد دهید. این بازخورد می‌تواند یک پیام Toast کوچک یا یک پنجره Alert باشد. این کار به کاربر اطمینان می‌دهد که درخواستش دریافت و پردازش شده است.

۴. حذف یا ویرایش دکمه‌ها پس از استفاده

در بسیاری از موارد، پس از اینکه کاربر روی یک دکمه کلیک کرد، آن دکمه دیگر کاربردی ندارد (مثلاً دکمه “تأیید” یک عملیات). در این حالت، بهترین کار این است که پیام حاوی دکمه‌ها را ویرایش کرده و کیبورد شیشه‌ای را حذف کنید (با reply_markup=None) یا آن را با دکمه‌های جدید جایگزین کنید. این کار از شلوغی رابط کاربری جلوگیری می‌کند و از کلیک‌های تکراری روی دکمه‌های منسوخ شده جلوگیری می‌کند.

۵. مدیریت خطاها و زمان انقضا

ممکن است کاربر پس از مدت زمان طولانی روی دکمه‌ای کلیک کند که دیگر معتبر نیست (مثلاً دکمه مربوط به یک رویداد منقضی شده). ربات شما باید بتواند این حالت‌ها را مدیریت کند و پیام مناسبی به کاربر نمایش دهد (مثلاً “این عمل دیگر معتبر نیست.”). می‌توانید با افزودن یک timestamp به callback_data و بررسی آن در Handler، این موارد را مدیریت کنید. همچنین، استفاده از بلوک try-except برای Catch کردن استثنائات احتمالی در Handlerها ضروری است.

۶. امنیت Callback Data

فرض کنید callback_data قابل دستکاری توسط کاربر است. هرگز اطلاعات حساس یا عملیات‌های حیاتی را تنها بر اساس callback_data بدون تأیید اعتبار از منابع دیگر انجام ندهید. به عنوان مثال، اگر callback_data شامل delete_user_123 است، قبل از حذف کاربر 123، مطمئن شوید که کاربری که روی دکمه کلیک کرده، دارای مجوزهای لازم برای حذف آن کاربر است.

ادغام دکمه‌های شیشه‌ای با منطق پیچیده ربات و پایگاه داده

برای ساخت ربات‌های قدرتمند، لازم است که دکمه‌های شیشه‌ای را با منطق پیچیده‌تر و سیستم‌های ذخیره‌سازی داده مانند پایگاه داده ادغام کنیم.

ذخیره وضعیت در callback_data (با احتیاط)

همانطور که قبلاً اشاره شد، callback_data محدودیت 64 بایتی دارد. این محدودیت مانع از ذخیره حجم زیادی از اطلاعات می‌شود. اما برای حفظ وضعیت‌های کوچک (مثلاً شماره صفحه، شناسه آیتم، یا مرحله فعلی یک فرایند) بسیار مفید است.

مثال: یک ربات نظرسنجی که مراحل مختلف دارد.


import telebot
from telebot import types

TOKEN = "YOUR_BOT_TOKEN"
bot = telebot.TeleBot(TOKEN)

# یک دیکشنری ساده برای ذخیره وضعیت کاربران
user_states = {}

@bot.message_handler(commands=['survey'])
def start_survey(message):
    user_id = message.chat.id
    user_states[user_id] = {'step': 'q1'}
    
    markup = types.InlineKeyboardMarkup()
    markup.add(types.InlineKeyboardButton("بله", callback_data="ans_q1_yes"))
    markup.add(types.InlineKeyboardButton("خیر", callback_data="ans_q1_no"))
    bot.send_message(user_id, "سوال 1: آیا از ربات راضی هستید؟", reply_markup=markup)

@bot.callback_query_handler(func=lambda call: call.data.startswith('ans_'))
def process_survey_answer(call):
    user_id = call.from_user.id
    data_parts = call.data.split('_') # ans_q1_yes
    
    if user_id not in user_states:
        bot.answer_callback_query(call.id, "نظرسنجی منقضی شده است. /survey را دوباره امتحان کنید.")
        bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, 
                              text="نظرسنجی منقضی شده است. لطفا دوباره شروع کنید.", reply_markup=None)
        return
        
    current_step = user_states[user_id]['step']
    question = data_parts[1] # q1
    answer = data_parts[2] # yes / no
    
    if question == 'q1' and current_step == 'q1':
        user_states[user_id]['q1_answer'] = answer
        user_states[user_id]['step'] = 'q2'
        
        markup = types.InlineKeyboardMarkup()
        markup.add(types.InlineKeyboardButton("خیلی خوب", callback_data="ans_q2_good"))
        markup.add(types.InlineKeyboardButton("متوسط", callback_data="ans_q2_neutral"))
        markup.add(types.InlineKeyboardButton("بد", callback_data="ans_q2_bad"))
        
        bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, 
                              text="سوال 2: کیفیت خدمات را چگونه ارزیابی می‌کنید؟", reply_markup=markup)
        bot.answer_callback_query(call.id, "پاسخ شما ثبت شد.")
        
    elif question == 'q2' and current_step == 'q2':
        user_states[user_id]['q2_answer'] = answer
        
        final_message = f"با تشکر از شما {call.from_user.first_name}!\n" \
                        f"پاسخ سوال 1: {user_states[user_id]['q1_answer']}\n" \
                        f"پاسخ سوال 2: {user_states[user_id]['q2_answer']}"
                        
        bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, 
                              text=final_message, reply_markup=None)
        bot.answer_callback_query(call.id, "نظرسنجی به پایان رسید.")
        del user_states[user_id] # حذف وضعیت کاربر پس از اتمام
        
    else:
        bot.answer_callback_query(call.id, "خطا: ترتیب سوالات رعایت نشد یا وضعیت اشتباه است.")

bot.infinity_polling()

در این مثال، user_states یک دیکشنری ساده در حافظه است. برای یک ربات تولیدی، باید از پایگاه داده (مانند PostgreSQL, MongoDB, SQLite) برای ذخیره وضعیت‌ها به صورت پایدار استفاده کنید.

تولید دکمه‌های پویا از پایگاه داده

یکی از رایج‌ترین سناریوها، نمایش لیستی از آیتم‌ها (محصولات، کاربران، مقالات و غیره) است که از یک پایگاه داده واکشی می‌شوند. دکمه‌های شیشه‌ای در اینجا برای انتخاب آیتم‌ها، ویرایش آن‌ها یا انجام عملیات روی آن‌ها کاربرد دارند.

فرض کنید یک جدول products در دیتابیس دارید با ستون‌های id و name.


import telebot
from telebot import types
import sqlite3 # به عنوان مثال، از SQLite استفاده می‌کنیم

TOKEN = "YOUR_BOT_TOKEN"
bot = telebot.TeleBot(TOKEN)

# تابع کمکی برای اتصال به دیتابیس
def get_db_connection():
    conn = sqlite3.connect('my_database.db')
    conn.row_factory = sqlite3.Row # برای دسترسی به ستون‌ها با نام
    return conn

# ایجاد جدول محصولات اگر وجود ندارد
def init_db():
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS products (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            price REAL NOT NULL
        );
    ''')
    # افزودن چند محصول نمونه اگر جدول خالی است
    cursor.execute("INSERT OR IGNORE INTO products (id, name, price) VALUES (1, 'لپ تاپ X', 1200.00)")
    cursor.execute("INSERT OR IGNORE INTO products (id, name, price) VALUES (2, 'ماوس گیمینگ Y', 75.50)")
    cursor.execute("INSERT OR IGNORE INTO products (id, name, price) VALUES (3, 'کیبورد مکانیکی Z', 150.00)")
    conn.commit()
    conn.close()

init_db() # مطمئن شوید دیتابیس و جدول ایجاد شده است

@bot.message_handler(commands=['products'])
def show_products(message):
    conn = get_db_connection()
    products = conn.execute('SELECT id, name FROM products').fetchall()
    conn.close()
    
    if not products:
        bot.send_message(message.chat.id, "هیچ محصولی یافت نشد.")
        return
        
    markup = types.InlineKeyboardMarkup()
    for product in products:
        # ساخت دکمه برای هر محصول با استفاده از شناسه محصول در callback_data
        markup.add(types.InlineKeyboardButton(product['name'], callback_data=f"product_info_{product['id']}"))
    
    bot.send_message(message.chat.id, "لیست محصولات:", reply_markup=markup)

@bot.callback_query_handler(func=lambda call: call.data.startswith('product_info_'))
def process_product_info(call):
    product_id = int(call.data.split('_')[2])
    
    conn = get_db_connection()
    product = conn.execute('SELECT name, price FROM products WHERE id = ?', (product_id,)).fetchone()
    conn.close()
    
    if product:
        info_message = f"نام محصول: {product['name']}\nقیمت: {product['price']} دلار"
        
        # دکمه برای خرید یا افزودن به سبد خرید
        buy_markup = types.InlineKeyboardMarkup()
        buy_markup.add(types.InlineKeyboardButton("خرید این محصول", callback_data=f"buy_product_{product_id}"))
        
        bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
                              text=info_message, reply_markup=buy_markup)
        bot.answer_callback_query(call.id, "جزئیات محصول بارگذاری شد.")
    else:
        bot.answer_callback_query(call.id, "محصول یافت نشد.", show_alert=True)
        
@bot.callback_query_handler(func=lambda call: call.data.startswith('buy_product_'))
def process_buy_product(call):
    product_id = int(call.data.split('_')[2])
    
    conn = get_db_connection()
    product = conn.execute('SELECT name FROM products WHERE id = ?', (product_id,)).fetchone()
    conn.close()
    
    if product:
        bot.answer_callback_query(call.id, f"شما درخواست خرید {product['name']} را دادید!", show_alert=True)
        bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
                              text=f"درخواست خرید برای '{product['name']}' ارسال شد.n(این یک مثال است، منطق خرید واقعی پیاده‌سازی نشده است.)",
                              reply_markup=None)
    else:
        bot.answer_callback_query(call.id, "خطا در خرید محصول.", show_alert=True)

bot.infinity_polling()

این مثال نشان می‌دهد که چگونه دکمه‌های شیشه‌ای می‌توانند به عنوان دروازه‌ای برای تعامل با داده‌های پایگاه داده عمل کنند، به کاربر اجازه دهند آیتم‌ها را مشاهده کند، اطلاعات بیشتری دریافت کند و سپس عملیات‌های مربوط به آن آیتم را انجام دهد.

نکات پیشرفته، بهینه‌سازی و حل مشکلات رایج در کار با Inline Keyboards

در این بخش، به برخی از نکات پیشرفته، روش‌های بهینه‌سازی و حل مشکلات متداولی که ممکن است در توسعه با Inline Keyboards با آن‌ها مواجه شوید، می‌پردازیم.

۱. بهینه‌سازی حجم callback_data

محدودیت 64 بایتی برای callback_data می‌تواند چالش‌برانگیز باشد. برای داده‌های طولانی‌تر، از روش‌های زیر استفاده کنید:

  • شناسه‌های کوتاه: به جای ارسال نام کامل یا متن طولانی، از شناسه‌های عددی یا رشته‌ای کوتاه (UUID) استفاده کنید و اطلاعات کامل را از پایگاه داده یا سیستم ذخیره‌سازی خود بازیابی کنید.
  • فشرده‌سازی: برای داده‌های ساختاریافته کوچک، می‌توانید از فرمت‌های فشرده مانند JSON فشرده شده (مثلاً با Base64) استفاده کنید، اما معمولاً بهتر است از شناسه‌ها استفاده شود.
  • ذخیره موقت وضعیت: اگر نیاز به ذخیره یک وضعیت پیچیده دارید که فقط برای مدت کوتاهی لازم است، می‌توانید آن را در یک حافظه پنهان (Cache) در سمت سرور (مثلاً یک دیکشنری در ربات یا Redis) با یک کلید یکتا ذخیره کرده و فقط آن کلید را در callback_data ارسال کنید.

۲. جلوگیری از “Flood” با Callback Queries

اگر کاربران با سرعت زیادی روی دکمه‌ها کلیک کنند، ممکن است ربات شما با Callback Queryهای زیادی مواجه شود. Telegram Bot API دارای محدودیت نرخ (Rate Limiting) است. اطمینان حاصل کنید که ربات شما بتواند Callback Queryها را به سرعت و کارآمدی پردازش کند. Telebot تا حدودی این مسائل را مدیریت می‌کند، اما منطق ربات شما باید بهینه‌سازی شده باشد.

۳. مدیریت Callback Queryهای همزمان

اگر چندین کاربر همزمان روی دکمه‌ها کلیک کنند، Handler شما باید Thread-Safe باشد و بتواند به درستی چندین درخواست را مدیریت کند. در پایتون، به دلیل GIL (Global Interpreter Lock)، همزمان‌سازی واقعی Threadها محدود است، اما Telebot معمولاً از Threading برای پردازش به‌روزرسانی‌ها استفاده می‌کند. با این حال، مطمئن شوید که دسترسی به منابع مشترک (مانند دیکشنری user_states در مثال ما یا دیتابیس) به درستی همزمان‌سازی شده باشد تا از race condition جلوگیری شود. در مثال‌های فوق، استفاده از SQLite که به صورت ذاتی یک قفل روی فایل دیتابیس ایجاد می‌کند، یا دیکشنری که در یک Thread اصلی مدیریت می‌شود، تا حدی این مشکل را پوشش می‌دهد.

۴. تست و اشکال‌زدایی

تست دقیق دکمه‌های شیشه‌ای و Handlerهای آن‌ها بسیار مهم است. موارد زیر را تست کنید:

  • کلیک روی هر دکمه و اطمینان از عملکرد صحیح آن.
  • بررسی سناریوهای مرزی (مثلاً صفحه‌بندی در صفحه اول یا آخر، لیست‌های خالی).
  • کلیک‌های مکرر و سریع.
  • کلیک روی دکمه‌های منقضی شده یا نامعتبر (در صورتی که منطق مربوطه را پیاده‌سازی کرده‌اید).
  • ارزیابی سرعت پاسخ‌دهی ربات.

۵. استفاده از Middleware (میان‌افزارها) در Telebot

برای مدیریت متمرکز Callback Queryها، می‌توانید از Middlewareهای Telebot استفاده کنید. این میان‌افزارها به شما امکان می‌دهند تا قبل از رسیدن Callback Query به Handler اصلی، یک سری عملیات مشترک (مانند احراز هویت، ثبت لاگ، یا بررسی اعتبار) را روی آن انجام دهید.


# تعریف یک Middleware ساده
@bot.middleware_handler(update_types=['callback_query'])
def callback_middleware(bot_instance, call):
    print(f"Callback Query از کاربر {call.from_user.id} دریافت شد: {call.data}")
    # می‌توانید در اینجا لاگ بگیرید، احراز هویت کنید، یا حتی درخواست را رد کنید.
    # مثال: بررسی اینکه کاربر اجازه استفاده از ربات را دارد یا خیر
    # if not is_user_allowed(call.from_user.id):
    #     bot.answer_callback_query(call.id, "شما اجازه این کار را ندارید.")
    #     raise telebot.StopMyHandlers() # جلوگیری از پردازش توسط Handlerهای دیگر

۶. محدودیت تعداد دکمه‌ها در یک پیام

در حالی که تلگرام تعداد دقیقی از دکمه‌ها در یک کیبورد شیشه‌ای را مشخص نمی‌کند، توصیه می‌شود برای حفظ خوانایی و تجربه کاربری، از تعداد معقولی از دکمه‌ها در هر پیام استفاده کنید. در هر ردیف، سه تا چهار دکمه و مجموعاً حداکثر حدود ۲۰-۳۰ دکمه در یک کیبورد، معمولاً یک حد معقول است. برای لیست‌های طولانی‌تر، از صفحه‌بندی استفاده کنید.

۷. زمانی که نباید از Inline Keyboards استفاده کرد

با وجود تمام مزایا، Inline Keyboards برای همه سناریوها بهترین گزینه نیستند:

  • ورودی‌های متنی پیچیده: اگر نیاز به دریافت متن طولانی، تاریخ، یا اطلاعات ساختاریافته از کاربر دارید، بهتر است از پیام‌های متنی و حالت‌های مختلف (States) ربات استفاده کنید.
  • گزینه‌های ثابت و عمومی: برای گزینه‌های کلی مانند “شروع مجدد”، “راهنما” یا “لغو” که همیشه در دسترس هستند، Reply Keyboards ممکن است گزینه مناسب‌تری باشند زیرا همیشه در پایین صفحه چت ظاهر می‌شوند.

در نهایت، انتخاب بین Inline و Reply Keyboards، یا ترکیب آن‌ها، به ماهیت تعامل و نیازهای خاص ربات شما بستگی دارد. با درک عمیق از هر دو، می‌توانید بهترین تصمیم را برای افزایش کارایی و جذابیت ربات تلگرام خود بگیرید.

نتیجه‌گیری

دکمه‌های شیشه‌ای (Inline Keyboards) در Telebot ابزاری حیاتی برای ایجاد ربات‌های تلگرام پویا، تعاملی و کاربرپسند هستند. با قابلیت‌هایی مانند callback_data، ویرایش پیام، و پشتیبانی از انواع مختلف دکمه‌ها (URL، switch inline query)، آن‌ها به توسعه‌دهندگان این امکان را می‌دهند که تجربه‌های کاربری پیچیده‌ای را با سادگی نسبی پیاده‌سازی کنند.

از پیاده‌سازی منوهای چندمرحله‌ای و سیستم‌های رأی‌گیری گرفته تا صفحه‌بندی لیست‌های طولانی و ادغام با پایگاه‌های داده، Inline Keyboards می‌توانند به طور چشمگیری کارایی و جذابیت ربات شما را افزایش دهند. با رعایت بهترین شیوه‌های طراحی UX، مدیریت صحیح Callback Data، و توجه به نکات بهینه‌سازی و امنیتی، می‌توانید ربات‌هایی بسازید که نه تنها از نظر فنی قدرتمند هستند، بلکه کاربران از تعامل با آن‌ها لذت ببرند.

امیدواریم این راهنمای جامع شما را در مسیر توسعه ربات‌های تلگرام پیشرفته با Telebot یاری رسانده باشد و به شما انگیزه دهد تا مرزهای خلاقیت را در طراحی رابط‌های کاربری ربات‌های خود گسترش دهید. هرچه بیشتر با این ابزارهای قدرتمند کار کنید، پتانسیل‌های جدیدتری را کشف خواهید کرد و ربات‌هایی خواهید ساخت که فراتر از انتظارات عمل می‌کنند.

“تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT”

قیمت اصلی 2.290.000 ریال بود.قیمت فعلی 1.590.000 ریال است.

"تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT"

"با شرکت در این دوره جامع و کاربردی، به راحتی مهارت‌های برنامه‌نویسی پایتون را از سطح مبتدی تا پیشرفته با کمک هوش مصنوعی ChatGPT بیاموزید. این دوره، با بیش از 6 ساعت محتوای آموزشی، شما را قادر می‌سازد تا به سرعت الگوریتم‌های پیچیده را درک کرده و اپلیکیشن‌های هوشمند ایجاد کنید. مناسب برای تمامی سطوح با زیرنویس فارسی حرفه‌ای و امکان دانلود و تماشای آنلاین."

ویژگی‌های کلیدی:

بدون نیاز به تجربه قبلی برنامه‌نویسی

زیرنویس فارسی با ترجمه حرفه‌ای

۳۰ ٪ تخفیف ویژه برای دانشجویان و دانش آموزان