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

Discord机器人如何高效实现多用户并行可终止定时提醒功能

解决方案

原有代码核心问题

  • 用了阻塞的time.sleep(),会直接卡住discord.py的整个异步事件循环,导致所有事件处理都变慢
  • 在on_message回调里直接写while死循环,会让该协程一直占用资源不释放,用户量上来后资源直接耗尽
  • 用列表存储运行中用户的ID,查询、删除的时间复杂度都是O(n),用户多了效率极低

最优实现方案

用asyncio.Task管理每个用户的独立定时任务,是discord.py生态下处理多定时任务的标准方案,支持上千并发完全无压力。完整实现代码如下:

import asyncio
from datetime import datetime, timedelta
import discord

# key为用户ID,value为对应的定时任务Task对象
user_timers = {}
NOTIFY_CHANNEL_ID = 877932875566153789
prefix = "!" # 替换为自己的命令前缀

# 独立的用户定时提醒协程
async def timer_worker(user_id: int, channel):
    try:
        while True:
            # 非阻塞等待3小时,不会卡住事件循环
            await asyncio.sleep(3 * 3600)
            await channel.send(f"<@{user_id}> Your timer is running!")
    except asyncio.CancelledError:
        # 任务被取消时自动清理
        if user_id in user_timers:
            del user_timers[user_id]
        raise

@client.event
async def on_message(message):
    if message.author == client.user:
        return
    command = message.content.split(' ')

    # 启动定时器逻辑
    if command[0] == (f'{prefix}start'):
        try:
            # 原有获取时区、计算本地时间逻辑保留
            timezone = timezoneDatabase[str(message.author.id)][0].split(':')
            hours = int(timezone[0])
            minutes = int(timezone[1])
            utcTime = datetime.utcnow()
            hours_added = timedelta(hours=hours, minutes=minutes)
            calculated_time = (utcTime + hours_added).strftime("%Y-%m-%d %H:%M:%S")
            
            # 校验用户是否已启动定时器
            if data.getStartTime(str(message.author)) != False:
                embed = discord.Embed(
                    title='Timer started', 
                    description=f'<@{message.author.id}> Your timer has already started!', 
                    color=0x8f6c54
                )
                await message.channel.send(embed=embed)
                return
            if data.logStartTime(message.author, calculated_time) != 'ok':
                # 谷歌表写入失败的自定义异常处理可以放在这里
                return
            
            # 创建并存储用户专属定时任务
            channel = client.get_channel(NOTIFY_CHANNEL_ID)
            timer_task = asyncio.create_task(timer_worker(message.author.id, channel))
            user_timers[message.author.id] = timer_task

            embed = discord.Embed(
                title='Timer started', 
                description=f'<@{message.author.id}> Your timer has started counting!', 
                color=0x8f6c54
            )
            await message.channel.send(embed=embed)
        except Exception as e:
            # 按需添加异常处理
            pass
    
    # 停止定时器逻辑
    if command[0] == f'{prefix}stop':
        user_id = message.author.id
        if user_id not in user_timers:
            embed = discord.Embed(
                title='Timer not found', 
                description=f'<@{user_id}> You don\'t have a running timer!', 
                color=0x8f6c54
            )
            await message.channel.send(embed=embed)
            return
        
        # 取消定时任务
        user_timers[user_id].cancel()
        # 更新谷歌表状态
        data.clearStartTime(message.author)
        embed = discord.Embed(
            title='Timer stopped', 
            description=f'<@{user_id}> Your timer has been stopped!', 
            color=0x8f6c54
        )
        await message.channel.send(embed=embed)

补充优化点

  • 机器人重启时可以遍历谷歌表里所有未停止的定时器,根据启动时间计算下一次提醒的剩余时间,重新创建Task加入user_timers,避免重启丢失所有定时任务
  • 如果需要更精准的定时(比如避免asyncio.sleep的误差),可以每次计算下一次提醒的绝对时间,用await asyncio.sleep((next_run_time - datetime.utcnow()).total_seconds())替代固定3小时等待
  • 可以给Task加done回调,处理异常退出的情况,避免用户定时器莫名挂掉没有通知

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 13:54:03