فصل ۱۳: ارسال پیام زمانبندیشده با Job Queue

۱. مقدمه
فرض کنید میخواهید رباتتان به صورت خودکار:
- هر روز صبح پیام هواشناسی بفرستد.
- هر ساعت نرخ ارز را ارسال کند.
- یا فقط در یک تاریخ و ساعت خاص به کاربر یادآوری بدهد.
برای این کار نیاز به Job Queue داریم.
📌 تعریف:
Job Queue در کتابخانه python-telegram-bot یک سیستم داخلی برای زمانبندی وظایف (Task Scheduling) است.
یعنی شما به آن میگویید “این تابع را در فلان زمان یا هر چند ثانیه یک بار اجرا کن” و خودش مدیریت میکند.
۲. چرا از Job Queue استفاده کنیم؟
| روش | مزایا | معایب |
|---|---|---|
| Job Queue (داخلی) | ساده، بدون نیاز به نصب Cron، همه چیز داخل کد | وابسته به اجرای ربات؛ اگر خاموش شود، کارها متوقف میشوند |
| Cronjob (خارجی) | اجرا حتی اگر ربات خاموش باشد | نیاز به تنظیم در سرور و مدیریت جداگانه |
| Thread/while(True) | کنترل کامل در کد | خطر مصرف زیاد CPU یا RAM، مدیریت سختتر |
📌 نتیجه:
برای اکثر رباتهای تلگرامی، Job Queue کافی و ایدهآل است مگر این که بخواهید مستقل از اجرای ربات کار کند.
۳. روشهای زمانبندی در Job Queue
Job Queue چند روش اصلی دارد:
| متد | توضیح | استفاده |
|---|---|---|
run_once(func, when) | اجرای یک بار در زمان مشخص | یادآوری یک قرار مهم |
run_repeating(func, interval, first=None) | اجرای تکراری با فاصله زمانی ثابت | ارسال نرخ ارز هر ۱۰ دقیقه |
run_daily(func, time, days=(0,1,...)) | اجرای هر روز در ساعت مشخص | گزارش هواشناسی هر صبح |
۴. اولین مثال – ارسال پیام یک بار بعد از مدت مشخص
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
TOKEN = "توکن_ربات"
async def send_once(context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_message(context.job.chat_id, "⏰ این پیام بعد از ۱۰ ثانیه ارسال شد!")
async def start_timer(update, context):
context.job_queue.run_once(send_once, when=10, chat_id=update.message.chat_id)
await update.message.reply_text("✅ تایمر ۱۰ ثانیهای فعال شد.")
app = ApplicationBuilder().token(TOKEN).build()
app.add_handler(CommandHandler("starttimer", start_timer))
app.run_polling()
📌 کاربرد:
برای مثال، وقتی کاربر درخواست “یادآوری ۱ ساعته” دارد.
۵. مثال دوم – اجرای تکراری هر چند دقیقه
async def send_repeated(context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_message(context.job.chat_id, "🔄 این پیام هر ۵ دقیقه ارسال میشود.")
async def repeat_timer(update, context):
context.job_queue.run_repeating(
send_repeated,
interval=300, # هر ۵ دقیقه
first=0, # بلافاصله بعد از دستور
chat_id=update.message.chat_id
)
await update.message.reply_text("✅ تایمر ۵ دقیقهای فعال شد.")
app.add_handler(CommandHandler("repeat", repeat_timer))
📌 کاربرد:
برای اعلانهای دورهای مثل قیمت ارز یا هشدار وضعیت.
۶. مثال سوم – اجرای روزانه سر ساعت
import datetime
async def send_daily_weather(context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_message(context.job.chat_id, "☀ هواشناسی امروز: آفتابی!")
async def daily_weather(update, context):
context.job_queue.run_daily(
send_daily_weather,
time=datetime.time(hour=8, minute=0),
days=(0,1,2,3,4,5,6), # همه روزها
chat_id=update.message.chat_id
)
await update.message.reply_text("📅 ارسال روزانه هواشناسی فعال شد!")
app.add_handler(CommandHandler("daily", daily_weather))
📌 کاربرد:
ارسال گزارش روزانه، پیام انگیزشی یا اخبار.
۷. ثبت کاربران برای ارسال پیام گروهی زمانبندیشده
ذخیره User ID در دیتابیس یا فایل:
users = set()
async def register(update, context):
users.add(update.message.chat_id)
await update.message.reply_text("✅ شما در لیست ارسال پیام قرار گرفتید.")
async def send_to_all(context):
for uid in users:
await context.bot.send_message(uid, "📢 پیام روزانه!")
async def set_broadcast(update, context):
context.job_queue.run_daily(
send_to_all,
time=datetime.time(hour=9, minute=0)
)
await update.message.reply_text("📢 ارسال پیام به همه کاربران هر روز ساعت ۹ فعال شد.")
app.add_handler(CommandHandler("register", register))
app.add_handler(CommandHandler("broadcast", set_broadcast))
۸. نکات مهم عملی
- ذخیره کاربران را حتماً در دیتابیس بگذارید، چون با ریستارت ربات لیست پاک میشود.
- اگر از APIها استفاده میکنید، به محدودیت آنها (Rate Limit) دقت کنید.
- پیامهای تکراری زیاد ممکن است باعث مزاحمت کاربر و بلاک شدن ربات شود.
۹. سوالات متداول
س: اگر کاربر بخواهد تایمر خود را لغو کند چطور؟
ج: باید Job را هنگام ایجاد ذخیره کنید و با job.schedule_removal() آن را حذف کنید.
س: اگر اینترنت یا ربات قطع شود چه میشود؟
ج: Job Queue متوقف میشود. بهتر است ربات را روی یک سرور یا سرویس پایدار (مثل VPS یا Railway) اجرا کنید.
س: آیا میتوان زمان را بر اساس ساعت محلی کاربر تنظیم کرد؟
ج: بله، ولی باید ساعت محلی کاربر را بگیرید و تایم UTC را محاسبه کنید.
۱۰. اشتباهات رایج
| اشتباه | توضیح |
|---|---|
| اجرای Job بدون پاک کردن قبلی | باعث چندبار ارسال پیام یکسان میشود |
| تعیین interval کوتاه | فشار به سرور و API زیاد میشود |
| ذخیره لیست کاربران در حافظه | با ریستارت پاک میشود |
۱۱. نکات حرفهای
- ارسال دادههای هوش مصنوعی به صورت روزانه (ترکیب فصل ۱۲ و ۱۳)
- استفاده از Inline Button برای فعال/غیرفعال کردن Job
- ثبت زمان Job در دیتابیس برای مدیریت بهتر
- استفاده ترکیبی از Job Queue و Thread برای پردازشهای طولانی