You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 11:45:03