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

如何修复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

  1. 登录Heroku控制台,进入你的应用
  2. 在「Resources」标签页,搜索并添加「Heroku Scheduler」插件(免费版可用)
  3. 打开Scheduler配置页面,点击「Add job」
  4. 设置定时规则:比如希望每小时执行一次,就选择「Every hour」,然后在「Run Command」里填写python send_wishes.py
  5. 保存设置,Heroku会按照你指定的时间自动触发脚本执行

这种方式不需要保持线程持续运行,完美适配Heroku的dyno休眠机制,稳定性更高。

内容的提问来源于stack exchange,提问作者Aleksei Grabor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:16:04