Python Telegram Bot任务队列在Heroku运行异常问题咨询
问题原因和修复方案
问题1:执行/start立刻发消息、后续定时任务不触发的原因
你在调用run_daily时,第一个参数直接写了purchase_forex(update, context)这种带括号的写法,相当于当场执行了这个函数,所以立刻发送了消息,而函数没有返回值的情况下,传给run_daily的是None,自然不会触发后续的定时执行。正确的用法是直接传函数名作为引用,参数通过context传递即可。
问题2:服务重启自动执行定时任务的实现
不需要依赖用户发送/start命令触发定时任务注册,直接在main函数中完成Updater初始化后,直接调用job_queue的定时注册逻辑即可。
修改后完整代码
from telegram.ext import Updater, CommandHandler, CallbackContext from telegram import ParseMode import os import datetime import pytz import logging from dotenv import load_dotenv # 如果你不需要额外的Flask路由,可以删除下面这行无用导入 # from flask import Flask load_dotenv() TOKEN = os.environ.get('API_KEY') CHAT_ID = os.environ.get('CHAT_ID') PORT = int(os.environ.get('PORT', '8443')) # 启用日志 logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) logger = logging.getLogger(__name__) # 定时任务回调函数仅需接收context参数 def purchase_forex(context: CallbackContext): print('running forex') context.bot.send_message(chat_id=CHAT_ID, text="Daily Forex", parse_mode=ParseMode.HTML) def purchase_sports(context: CallbackContext): print('running sports') context.bot.send_message(chat_id=CHAT_ID, text="Daily Text", parse_mode=ParseMode.HTML) def purchase_trading(context: CallbackContext): print('running trading') context.bot.send_message(chat_id=CHAT_ID, text="Daily Trading Text", parse_mode=ParseMode.HTML) def daily_job(update, context: CallbackContext): """ 手动触发的start命令逻辑,保留供手动调试用 """ sport = datetime.time(15, 12, 10, 000000, tzinfo=pytz.timezone('America/Chicago')) trading = datetime.time(15, 13, 10, 000000, tzinfo=pytz.timezone('America/Chicago')) forex = datetime.time(15, 14, 10, 000000, tzinfo=pytz.timezone('America/Chicago')) print("预定发送体育类消息时间", sport) print("预定发送交易类消息时间", trading) print("预定发送外汇类消息时间", forex) context.bot.send_message(chat_id=update.effective_chat.id, text='已激活每日通知!') # 手动触发可重新注册任务,如需去重可自行增加重复校验逻辑 context.job_queue.run_daily(purchase_forex, forex, days=tuple(range(7))) context.job_queue.run_daily(purchase_sports, sport, days=tuple(range(7))) context.job_queue.run_daily(purchase_trading, trading, days=tuple(range(7))) def error(update, context: CallbackContext): """记录更新导致的错误""" logger.warning('更新 "%s" 触发错误 "%s"', update, context.error) def register_daily_jobs(job_queue): """ 统一注册定时任务,服务启动自动调用 """ sport = datetime.time(15, 12, 10, 000000, tzinfo=pytz.timezone('America/Chicago')) trading = datetime.time(15, 13, 10, 000000, tzinfo=pytz.timezone('America/Chicago')) forex = datetime.time(15, 14, 10, 000000, tzinfo=pytz.timezone('America/Chicago')) print("预定发送体育类消息时间", sport) print("预定发送交易类消息时间", trading) print("预定发送外汇类消息时间", forex) # 直接传函数引用,不要加括号执行 job_queue.run_daily(purchase_forex, forex, days=tuple(range(7))) job_queue.run_daily(purchase_sports, sport, days=tuple(range(7))) job_queue.run_daily(purchase_trading, trading, days=tuple(range(7))) # 启动成功自动发通知,方便确认服务状态 job_queue.run_once(lambda ctx: ctx.bot.send_message(chat_id=CHAT_ID, text='服务重启成功,定时通知已激活'), when=0) def main(): u = Updater(TOKEN, use_context=True) dp = u.dispatcher dp.add_handler(CommandHandler('start', daily_job, pass_job_queue=True)) dp.add_error_handler(error) # 服务启动直接注册定时任务,无需手动触发 register_daily_jobs(u.job_queue) # 启动Webhook,注意替换<appname>为你的Heroku应用名 u.start_webhook(listen="0.0.0.0", port=PORT, url_path=TOKEN, webhook_url="https://<appname>.herokuapp.com/" + TOKEN) u.idle() if __name__ == '__main__': main()
内容的提问来源于stack exchange,提问作者Ayomide Omolewa
相关产品推荐
相关产品推荐

