نسخه 20 پایتون-تلگرام-ربات تغییرات ساختاری عمده ای را به همراه داشت. با توجه به مستندات،

تمام شبکه‌ها و منطق مربوط به I/O اکنون از طریق توابع کوروتین (به عنوان مثال async def ... کارکرد). به طور خاص، تمام روش های telegram.Bot کلاسی که به Bot API درخواست می دهد اکنون توابع اصلی هستند و باید باشند await-ed.” – لاگ تغییرات پایتون-تلگرام-ربات v20

فرآیند انتقال از پایتون-تلگرام-ربات نسخه 13 به نسخه 20 پیچیده تر از آن چیزی بود که من در ابتدا پیش بینی می کردم. تبدیل همزمان def توابع به async def و اضافه کردن await ورود به برنامه های جدید نسبتاً آسان بود. اما مشکل اصلی یافتن اسناد دقیق در مورد چگونگی انجام این کار بود ساخت و استقرار python-telegram-bot v20 webhooks در یک محیط تولید.

این مقاله توضیح می دهد که چرا و چگونه از این کشور مهاجرت کردم:

  1. python-telegram-bot v13 → v20
  2. فلاسک → FastAPI
  3. Gunicorn → Gunicorn + Uvicorn

چرا از نسخه 13 به نسخه 20 ارتقا دهید؟

نسخه 13.x و نسخه‌های قدیمی‌تر دیگر توسط تیم توسعه‌دهنده python-telegram-bot پشتیبانی نمی‌شود. اگر API تلگرام هر ویژگی جدیدی را معرفی کند، فقط در نسخه 20 و بالاتر در دسترس خواهد بود.

چرا از Webhook استفاده کنیم و از نظرسنجی استفاده نکنیم؟

بیشتر نمونه های ارائه شده توسط تیم توسعه دهنده پایتون-تلگرام-ربات استفاده می شود Application.run_polling. اما وب‌هوک‌ها معمولاً برای اکثر موارد استفاده از ربات‌های تلگرام در نظرسنجی توصیه می‌شوند، زیرا نظرسنجی مستلزم آن است که ربات شما دائماً از سرورهای تلگرام درخواست کند که می‌تواند منابع قابل توجهی را مصرف کند. از طرف دیگر، وب هوک ها عملکرد گسترده ای را ارائه می دهند، سریعتر به روز می شوند و مقیاس بهتری دارند.

پیشنهاد می‌کنیم بخوانید:  یک شی را به یک آرایه در جاوا اسکریپت فشار دهید

چالش استفاده از Flask با python-telegram-bot v20

استفاده از WGSI مانند Flask با python-telegram-bot v20 دشوار است.

Flask، یک WSGI (رابط دروازه وب سرور)، همزمان است و می تواند تنها یک درخواست را در یک زمان انجام دهد. اما همچنان می‌توانید توابع غیر همگام را با استفاده از Flask اجرا کنید asyncio.run()مانند نمونه ربات webhook سفارشی ارائه شده توسط تیم توسعه دهنده python-telegram-bot.

asyncio.run() یک حلقه رویداد را شروع می کند و کوروتین داده شده را تا زمانی که کامل شود اجرا می کند. اگر کارهای ناهمزمانی قبل یا بعد از رسیدگی به درخواست وجود داشته باشد، آن وظایف در یک حلقه رویداد جداگانه اجرا می شوند.

# Code snippet from https://docs.python-telegram-bot.org/en/v20.6/examples.customwebhookbot.html

webserver = uvicorn.Server(
    config=uvicorn.Config(
        app=WsgiToAsgi(flask_app),
        port=PORT,
        use_colors=False,
        host="127.0.0.1",
    )
)

async with application:
  await application.start()
  await webserver.serve() # start bot's webserver
  await application.stop()

با این حال، این پیاده سازی کمی ناخوشایند است زیرا Flask ذاتاً با جهانی های async ناسازگار است.

نمونه های موجود در مستندات برای محیط های تولید مناسب نیستند.

استفاده کردن asyncio.run() به عنوان نقطه ورود در تولید معمولا توصیه نمی شود. این asyncio.run() تابع برای اهداف توسعه و آزمایش طراحی شده است و ممکن است همان سطح از استحکام و قابلیت اطمینان سرورهای تولیدی مانند Gunicorn یا UWSGI را ارائه ندهد.

این سرورهای تولیدی بسیاری از ویژگی‌های اضافی مانند ثبت گزارش، نظارت و بررسی سلامت را ارائه می‌دهند که برای اطمینان از ثبات و امنیت یک برنامه تولید ضروری هستند.

اگر می خواهید ربات خود را در تولید مستقر کنید، استفاده از ASGI (رابط دروازه سرور ناهمگام) با اجرای وب سرور ASGI بسیار تمیزتر است.

پیشنهاد می‌کنیم بخوانید:  مرتب سازی شمارش در پایتون

چگونه این کار را انجام دهیم – مهاجرت و استقرار

از فلاسک (WSGI) تا FastAPI (AGSI)

انتقال یک برنامه Flask به یک ASGI ساده است. من FastAPI را انتخاب کردم زیرا یک آموزش جامع مهاجرت را اینجا پیدا کردم. سینتکس هر دو فریمورک کاملاً مشابه است، به این معنی که شما مجبور نخواهید بود تغییرات زیادی در کد ایجاد کنید.

# From python-telegram-bot v20
application = (
    Application.builder()
    .updater(None)
    .token(<your-bot-token>) # replace <your-bot-token>
    .read_timeout(7)
    .get_updates_read_timeout(42)
    .build()
)

# From FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
    async with application:
        await application.start()
        yield
        await application.stop()

به نظر می رسد Quart یک جایگزین عملی باشد، اما برای استقرار با استفاده از Uvicorn، که اجرای وب سروری است که من از اسکریپت ارائه شده توسط تیم پایتون-تلگرام-ربات اقتباس می کردم، پشتیبانی نمی کند.

یک نمونه کار

کد زیر یک نمونه حداقلی را نشان می دهد که از FastAPI برای ساخت یک وب هوک پایتون-تلگرام-ربات v20 استفاده می کند. این ربات با دریافت “شروع…” پاسخ خواهد داد /start فرمان

# main.py

from contextlib import asynccontextmanager
from http import HTTPStatus
from telegram import Update
from telegram.ext import Application, CommandHandler
from telegram.ext._contexttypes import ContextTypes
from fastapi import FastAPI, Request, Response

# Initialize python telegram bot
ptb = (
    Application.builder()
    .updater(None)
    .token(<your-bot-token>) # replace <your-bot-token>
    .read_timeout(7)
    .get_updates_read_timeout(42)
    .build()
)

@asynccontextmanager
async def lifespan(_: FastAPI):
    await ptb.bot.setWebhook(<your-webhook-url>) # replace <your-webhook-url>
    async with ptb:
        await ptb.start()
        yield
        await ptb.stop()

# Initialize FastAPI app (similar to Flask)
app = FastAPI(lifespan=lifespan)

@app.post("/")
async def process_update(request: Request):
    req = await request.json()
    update = Update.de_json(req, ptb.bot)
    await ptb.process_update(update)
    return Response(status_code=HTTPStatus.OK)

# Example handler
async def start(update, _: ContextTypes.DEFAULT_TYPE):
    """Send a message when the command /start is issued."""
    await update.message.reply_text("starting...")

ptb.add_handler(CommandHandler("start", start))

برای راه اندازی ربات، pip تمام وابستگی های مورد نیاز را نصب کرده و دستور start را اجرا کنید: gunicorn main:app -k uvicorn.workers.UvicornWorker.

این قطعه کد از یک ربات واقعی تلگرام در حال تولید اقتباس شده است. کد منبع @cron_telebot را در اینجا بررسی کنید تا نحوه پیاده سازی آن را ببینید. با خیال راحت اسکریپت را متناسب با مورد استفاده خود تطبیق دهید.

نتیجه

در این مقاله نحوه ساخت و استقرار یک وب هوک پایتون-تلگرام-ربات v20 را یاد گرفتیم.

امیدوارم این آموزش به شما کمک کرده باشد. اگر از این مقاله لذت بردید، لطفاً من را در Medium دنبال کنید تا حمایت خود را نشان دهم.

با تشکر از شما برای خواندن!