فصل ۱۰: کار با دکمه‌های Inline و Callback Query در ربات تلگرام

""

مقدمه فصل

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

در این فصل، ما:

  1. دکمه‌های Inline رو تعریف می‌کنیم.
  2. با مفهوم Callback Query آشنا می‌شویم.
  3. روش ساخت یک منوی ساده با دکمه Inline را یاد می‌گیریم.
  4. مثال پیشرفته منوی چندمرحله‌ای را پیاده‌سازی می‌کنیم.
  5. اشتباهات رایج و نکات حرفه‌ای را بررسی می‌کنیم.

بخش ۱: دکمه‌های Inline چیست؟

دکمه‌های Inline (In-message Buttons) دکمه‌هایی هستند که درون یک پیام تلگرام تعبیه می‌شوند و دقیقا زیر همان پیام نمایش داده می‌شوند.

مثال کاربرد آن‌ها:

  • ارسال لینک‌های وب‌سایت بدون وارد کردن متن در چت
    (مثلاً دکمه “مشاهده محصول” که کاربر را به سایت فروش هدایت می‌کند)
  • منوهای چند مرحله‌ای در ربات بدون شلوغ شدن چت
    (مثلاً منو انتخاب دسته آموزش → انتخاب زبان برنامه‌نویسی → نمایش محتوا)
  • گرفتن نظر یا پاسخ سریع از کاربر
    (مثلاً نظرسنجی با دکمه «بله» و «خیر»)

ویژگی دکمه‌های Inline:

ویژگیتوضیح
ظاهر شکیلدکمه درون پیام است و چت کاربر را شلوغ نمی‌کند.
تعامل مستقیمبا استفاده از Callback Query، کلیک کاربر بلافاصله به ربات اعلام می‌شود.
پشتیبانی از لینکمی‌توان به جای ارسال فرمان، لینک یک سایت را قرار داد.

بخش ۲: Callback Query چیست؟

وقتی کاربر روی یک دکمه Inline کلیک می‌کند، تلگرام به جای اینکه یک پیام جدید به ربات بفرستد، یک شیء (Object) به نام Callback Query ارسال می‌کند.

این شیء شامل اطلاعاتی مثل:

  • callback_query_id → شناسه‌ای برای پاسخ دادن به آن
  • from → اطلاعات کاربری که کلیک کرده
  • data → همان داده‌ای که شما موقع ساخت دکمه در callback_data گذاشته‌اید

به زبان ساده:
Callback Query مثل پیام مخفی‌ای است که تلگرام به ربات شما می‌فرستد و می‌گوید کاربر فلان دکمه را فشار داده است.


بخش ۳: نصب کتابخانه python-telegram-bot

برای کار با تلگرام در پایتون، کتابخانه‌ی معروف و قابل اعتماد python-telegram-bot را استفاده می‌کنیم.

برای نصب:

pip install python-telegram-bot==20.3

نکته: نسخه ۲۰۳ به بعد این کتابخانه، از ساختار Async (غیرهمزمان) استفاده می‌کند، یعنی توابع ربات باید با async تعریف شوند و برای صدا زدن تابع‌ها از await استفاده می‌کنیم.


بخش ۴: ساخت یک ربات با یک دکمه Inline ساده

بیایید یک مثال ساده بزنیم: منویی که از کاربر می‌خواهد زبان خود را انتخاب کند.

from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ApplicationBuilder, CommandHandler, CallbackQueryHandler, ContextTypes

TOKEN = "توکن_ربات_اینجا"

# تابع /start
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    keyboard = [
        [InlineKeyboardButton("🇮🇷 فارسی", callback_data="lang_fa")],
        [InlineKeyboardButton("🇬🇧 English", callback_data="lang_en")],
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    await update.message.reply_text("زبان خود را انتخاب کنید:", reply_markup=reply_markup)

# تابعی که کلیک روی دکمه را مدیریت می‌کند
async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()  # توقف لودینگ تلگرام
    if query.data == "lang_fa":
        await query.edit_message_text(text="زبان فارسی انتخاب شد ✅")
    elif query.data == "lang_en":
        await query.edit_message_text(text="English language selected ✅")

# ساخت اپلیکیشن
app = ApplicationBuilder().token(TOKEN).build()

# اضافه کردن هندلرها
app.add_handler(CommandHandler("start", start))
app.add_handler(CallbackQueryHandler(button_handler))

print("ربات فعال شد...")
app.run_polling()

بخش ۵: توضیح خط‌به‌خط کد بالا

  1. from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update

این خط، کلاس‌های لازم برای ساخت دکمه و کیبورد Inline را ایمپورت می‌کند:

  • InlineKeyboardButton → برای ساخت یک دکمه
  • InlineKeyboardMarkup → برای گروه‌بندی دکمه‌ها در قالب کیبورد
  • Update → شیئی که داده آپدیت را از تلگرام دریافت می‌کند
from telegram.ext import ApplicationBuilder, CommandHandler, CallbackQueryHandler, ContextTypes

اینجا ابزارهای مورد نیاز برای راه‌اندازی ربات را وارد می‌کنیم:

  • ApplicationBuilder → ساخت اسکلت اصلی ربات
  • CommandHandler → مدیریت دستورات مثل /start
  • CallbackQueryHandler → مدیریت کلیک روی دکمه‌ها
  • ContextTypes → مدیریت اطلاعات جانبی در طول اجرا
TOKEN = "توکن_ربات_اینجا"

اینجا باید توکن رباتی که از BotFather گرفته‌اید را وارد کنید.

  1. تابع start:
keyboard = [
    [InlineKeyboardButton("🇮🇷 فارسی", callback_data="lang_fa")],
    [InlineKeyboardButton("🇬🇧 English", callback_data="lang_en")],
]

ما یک لیست دوبعدی ساختیم:

  • هر لیست داخلی یک ردیف کیبورد را تشکیل می‌دهد.
  • هر دکمه با عنوان و callback_data (داده برگشتی) ساخته می‌شود.
reply_markup = InlineKeyboardMarkup(keyboard)

لیست دکمه‌ها را به یک شیء کیبورد تبدیل می‌کند.

  1. await update.message.reply_text("زبان خود را انتخاب کنید:", reply_markup=reply_markup)

پیامی به کاربر ارسال می‌کند و کیبورد را زیر آن پیام نمایش می‌دهد.

  1. تابع button_handler:
query = update.callback_query
await query.answer()
  • update.callback_query اطلاعات کلیک را می‌دهد.
  • query.answer() لودینگ تلگرام را متوقف می‌کند (اجباری است).
if query.data == "lang_fa":
    await query.edit_message_text(text="زبان فارسی انتخاب شد ✅")
  • query.data همان callback_data است که در دکمه تعریف کرده‌ایم.
  • به‌جای ارسال پیام جدید، پیام قبلی را ویرایش می‌کنیم تا چت شلوغ نشود.
  1. ساخت اپلیکیشن و اضافه کردن هندلرها:
app = ApplicationBuilder().token(TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CallbackQueryHandler(button_handler))
app.run_polling()
  • run_polling() یعنی ربات منتظر بماند و هر آپدیت جدید تلگرام را بررسی کند.

بخش ۶: ساخت منوی چندمرحله‌ای با دکمه‌های Inline

فرض کنید می‌خواهیم یک منوی چندمرحله‌ای بسازیم که کاربر اول دسته‌بندی انتخاب کند، بعد زیرمنو را ببیند.

کد:

async def menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
    keyboard = [
        [InlineKeyboardButton("📚 آموزش", callback_data="cat_tutorial")],
        [InlineKeyboardButton("📰 اخبار", callback_data="cat_news")],
    ]
    await update.message.reply_text(
        "یک دسته‌بندی را انتخاب کنید:",
        reply_markup=InlineKeyboardMarkup(keyboard)
    )

async def menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()

    if query.data == "cat_tutorial":
        keyboard = [
            [InlineKeyboardButton("Python", callback_data="tutorial_py")],
            [InlineKeyboardButton("Django", callback_data="tutorial_dj")],
        ]
        await query.edit_message_text(
            "موضوع آموزش:",
            reply_markup=InlineKeyboardMarkup(keyboard)
        )

    elif query.data == "cat_news":
        keyboard = [
            [InlineKeyboardButton("Technology", callback_data="news_tech")],
            [InlineKeyboardButton("Sports", callback_data="news_sport")],
        ]
        await query.edit_message_text(
            "موضوع اخبار:",
            reply_markup=InlineKeyboardMarkup(keyboard)
        )

توضیح کد:

  1. در menu منوی اصلی ساخته می‌شود.
  2. در menu_callback بسته به انتخاب کاربر، منوی جدید ساخته و جایگزین پیام قبلی می‌شود.
  3. با این روش، فقط یک پیام برای کل منو استفاده می‌شود.

بخش ۷: سوالات متداول (FAQ)

سوال: آیا می‌توان روی دکمه Inline لینک گذاشت؟
پاسخ: بله، در سازنده دکمه به جای callback_data از url='https://example.com' استفاده کنید.

سوال: محدودیت طول callback_data چیست؟
حداکثر ۶۴ بایت.

سوال: آیا می‌شود به‌صورت همزمان متن پیام را تغییر نداد ولی عملیات انجام داد؟
بله، کافیست از query.answer(text="پیام کوتاه") استفاده کنید تا نوتیفیکیشن کوتاهی روی صفحه کاربر بیاید.


بخش ۸: اشتباهات رایج

اشتباهتوضیح
ننوشتن query.answer()باعث می‌شود لودینگ تلگرام تمام نشود.
طولانی کردن callback_dataبه خاطر محدودیت ۶۴ بایت ارور خواهد داد.
ارسال پیام جدید به‌جای edit_message_textباعث شلوغ شدن گفت‌وگو می‌شود.

بخش ۹: نکات تکمیلی و حرفه‌ای

  • می‌توانید callback_data را به شکل JSON کوچک شده ارسال کنید و در سمت ربات با json.loads بخوانید.
  • از سیستم State برای مدیریت منوهای پیچیده استفاده کنید.
  • داده‌های انتخاب شده کاربر را در دیتابیس ذخیره کنید تا بعداً بتوانید تجربه شخصی‌سازی‌شده بدهید.
  • برای ظاهر بهتر، از ایموجی در نام دکمه‌ها استفاده کنید.
محمد وب‌سایت

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *