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

如何为Telepot开发的Telegram医疗调研Bot实现定时消息推送?

修复Telepot + APScheduler重复提醒问题

看起来你踩了一个很典型的调度器使用坑——每次用户发消息就新建调度器,导致多任务重复触发。我帮你梳理下问题根源,然后直接给修复后的代码:

问题核心

你的on_chat_message方法会在**每一条用户消息(包括调研过程中的回复)**触发时执行,而你在这个方法里每次都创建了新的BackgroundScheduler并添加重复任务。这就意味着用户每回复一次调研问题,就多一个调度器在后台跑,时间一长自然会收到N条重复提醒。

修复方案

  • 把调度器初始化移到ChatHandler的构造函数:每个用户会话只创建一次调度器,避免重复实例化。
  • 使用唯一任务ID + 检查任务是否存在:防止重复添加同一个任务。
  • 可选:改用Cron表达式实现每日定时:如果需要固定时间(比如每天早8点),比interval更精准。
  • 会话超时关闭调度器:避免无用的调度器占用资源。

修复后的完整代码

import time
import telepot
import json
from telepot.loop import MessageLoop
from telepot.namedtuple import ReplyKeyboardMarkup
from telepot.delegate import pave_event_space, per_chat_id, create_open
from apscheduler.schedulers.background import BackgroundScheduler

# Load token
TOKEN = 'klaghklsfhglka'

class main(telepot.helper.ChatHandler):
    def __init__(self, *args, **kwargs):
        super(main, self).__init__(*args, **kwargs)
        self.indicator = '0'
        self.data = {}
        # 每个用户会话仅初始化一次调度器
        self.schedule = BackgroundScheduler()
        # 设置任务唯一ID,避免重复添加
        self.reminder_job_id = f"reminder_{self.chat_id}"
        # 初始化时添加一次定时任务(这里用interval,也可以换成cron)
        self._setup_reminder()

    def _setup_reminder(self):
        """内部方法:设置提醒任务,仅执行一次"""
        def repeat_message():
            self.sender.sendMessage('Type /survey to start today\'s survey.')
        
        # 检查任务是否已存在,不存在才添加
        if not self.schedule.get_job(self.reminder_job_id):
            # 这里用interval每24小时发送,如果你要固定时间,换成cron:比如每天8点
            # self.schedule.add_job(repeat_message, 'cron', hour=8, id=self.reminder_job_id)
            self.schedule.add_job(repeat_message, 'interval', hours=24, id=self.reminder_job_id)
            self.schedule.start()

    def on_chat_message(self, msg):
        content_type, chat_type, chat_id = telepot.glance(msg)
        print(content_type, chat_type, chat_id)
        print(msg['text'])

        # Survey function
        def survey():
            if self.indicator == 'choose_symptom':
                mark_up = ReplyKeyboardMarkup(
                    keyboard=[['Pain'], ['Trouble with Breathing'], ['Tiredness'], ['Appetite'], 
                              ['Feeling sick'], ['Vomitting'], ['Bowel upset'], ['Hair loss']], 
                    one_time_keyboard=True)
                self.sender.sendMessage('Was there any discomfort today?', reply_markup=mark_up)
                self.indicator = 'rate_symptom'
            elif self.indicator == 'rate_symptom':
                self.data['symptoms'] = msg['text']
                mark_up = ReplyKeyboardMarkup(
                    keyboard=[['Not at all'],['A little'],['Somewhat'],['Very much']], 
                    one_time_keyboard=True)
                self.sender.sendMessage('Please rate the feeling.', reply_markup=mark_up)
                self.indicator = 'set_data'
            elif self.indicator == 'set_data':
                self.data['symptoms_score'] = msg['text']
                self.data['date'] = time.ctime(msg['date'])
                with open('data.json', 'a') as handle:
                    json.dump(self.data, handle)
                    handle.write("\n")
                self.sender.sendMessage('Thank you for feedback.')
                # 调研完成后可以选择暂停提醒,或者保持每日发送
                # self.schedule.pause_job(self.reminder_job_id)
                return

        # Detect start messages
        if msg['text'] == '/start':
            self.sender.sendMessage('Hello.', parse_mode='Markdown')
        elif msg['text'] == '/survey':
            self.indicator = 'choose_symptom'
            print("Survey started...")
            survey()

    def on_close(self, exception):
        """会话关闭时清理调度器"""
        if self.schedule.running:
            self.schedule.shutdown()
        super().on_close(exception)

bot = telepot.DelegatorBot(TOKEN, [
    pave_event_space()(
        per_chat_id(), 
        create_open, 
        main, 
        timeout=30
    ),
])
MessageLoop(bot).run_as_thread()
print('Listening...')
while 1:
    time.sleep(10)

关键修改点说明

  • 调度器移到__init__:每个用户会话只有一个调度器,不会重复创建。
  • 唯一任务ID:用chat_id拼接任务ID,确保每个用户的任务唯一,避免冲突。
  • 任务存在性检查:添加任务前先判断是否已存在,防止重复添加。
  • 使用self.sender代替全局bot:Telepot的ChatHandler内置了sender属性,比直接用全局bot更安全规范。
  • 会话关闭清理:on_close方法里关闭调度器,避免内存泄漏。
  • 可选Cron定时:注释里提供了固定时间发送的写法,比如每天早8点,比interval更适合每日固定时间的需求。

这样修改后,每个用户只会收到每日一次的提醒,不会再出现重复消息的问题啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:34:08