如何修复Telegram机器人定时消息函数并使用Heroku Scheduler?
修复定时消息函数及Heroku Scheduler使用指南
先拆解你代码里的几个关键问题,一步步解决:
1. 线程创建的核心错误
你创建线程时直接调用了函数(比如bot.polling()),这会导致函数在主线程立即执行,而非放到子线程中运行。正确做法是把函数对象传给target参数,不要加括号:
from threading import Thread # ❌ 错误写法:直接调用函数,会立刻执行而非进入子线程 # t1 = Thread(target=bot.polling()) # t2 = Thread(send_wishes_loop()) # ✅ 正确写法:传递函数对象 t1 = Thread(target=bot.polling) t2 = Thread(target=send_wishes_loop) t1.start() t2.start()
2. SQL注入风险与查询优化
用format拼接SQL语句存在严重的SQL注入风险,且数据类型不匹配时会出错。改用PostgreSQL的参数化查询(用%s作为占位符)更安全:
# ❌ 错误写法 # cursor.execute("""SELECT userid FROM usersdata WHERE morning_time={}""".format(hour)) # ✅ 正确写法:参数化查询 cursor.execute("SELECT userid FROM usersdata WHERE morning_time = %s", (hour,))
3. 定时sleep逻辑的bug
你的sleep计算逻辑不够严谨,比如当当前分钟接近60时,可能会计算出不合理的休眠时间。应该直接计算当前时间到下一个整点的秒数,确保每次循环都精准在整点执行:
import datetime # 替换原来的sleep代码 now = datetime.datetime.now() # 生成下一个整点的时间对象 next_hour = now.replace(minute=0, second=0, microsecond=0) + datetime.timedelta(hours=1) sleep_seconds = (next_hour - now).total_seconds() time.sleep(sleep_seconds)
4. 数据库连接优化
每次循环都新建数据库连接会浪费资源,建议把连接放在循环外复用,同时添加基础异常处理避免连接泄漏:
def send_wishes_loop(): # 连接放在循环外,复用连接 connection = psycopg2.connect(...) connection.autocommit = True # 开启自动提交,避免手动commit遗漏 cursor = connection.cursor() while True: now = datetime.datetime.now() hour = now.hour # 参数化查询获取目标用户 cursor.execute("SELECT userid FROM usersdata WHERE morning_time = %s", (hour,)) user_ids = cursor.fetchall() # 一次性获取所有结果 for userid in user_ids: if userid: # random.choices返回列表,取第一个元素作为祝福语 wish = random.choices(wishes_list.good_morning_wishes)[0] bot.send_message(userid[0], wish) # 计算到下一个整点的休眠时间 next_hour = now.replace(minute=0, second=0, microsecond=0) + datetime.timedelta(hours=1) sleep_seconds = (next_hour - now).total_seconds() time.sleep(sleep_seconds) # 注意:无限循环下这段关闭代码不会执行,建议添加退出信号触发关闭逻辑 cursor.close() connection.close()
关于Heroku Scheduler的使用
如果你的应用部署在Heroku上,线程循环的方式有个致命问题:免费dyno会在30分钟无请求后休眠,导致定时任务中断。Heroku Scheduler是更适配的方案,它可以定时触发独立脚本,步骤如下:
1. 重构代码为独立脚本
把发送消息的逻辑拆成单次执行的独立脚本(比如send_wishes.py),不需要循环:
# send_wishes.py import psycopg2 import datetime import random from your_bot_module import bot, wishes_list def send_wishes_once(): connection = psycopg2.connect(...) cursor = connection.cursor() hour = datetime.datetime.now().hour cursor.execute("SELECT userid FROM usersdata WHERE morning_time = %s", (hour,)) user_ids = cursor.fetchall() for userid in user_ids: if userid: wish = random.choices(wishes_list.good_morning_wishes)[0] bot.send_message(userid[0], wish) cursor.close() connection.commit() connection.close() if __name__ == "__main__": send_wishes_once()
2. 配置Heroku Scheduler
- 登录Heroku控制台,进入你的应用
- 在「Resources」标签页,搜索并添加「Heroku Scheduler」插件(免费版可用)
- 打开Scheduler配置页面,点击「Add job」
- 设置定时规则:比如希望每小时执行一次,就选择「Every hour」,然后在「Run Command」里填写
python send_wishes.py - 保存设置,Heroku会按照你指定的时间自动触发脚本执行
这种方式不需要保持线程持续运行,完美适配Heroku的dyno休眠机制,稳定性更高。
内容的提问来源于stack exchange,提问作者Aleksei Grabor
相关产品推荐
相关产品推荐

