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

discord.py中如何等待需异步执行的Timer协程?

Discord机器人延迟提醒功能报错解决方案

问题背景

开发Discord机器人实现延迟私信提醒功能,代码如下:

@bot.command()
async def reminder(ctx: commands.Context, message: str) -> None:
    print("scheduling reminder")
    event = {
        "message": message,
        "timer": Timer(5.0, send_rem, args=[ctx.author, message])
    }

    user_events[str(ctx.author.id)] = event
    event["timer"].start()
    print("scheduling reminder")

async def send_rem(author, message):
    print("sending reminder")
    DM_CHNL = await author.create_dm() if (author.dm_channel is None) else author.dm_channel
    if DM_CHNL != None:
        await DM_CHNL.send(message)

执行reminder命令后,定时器到期时触发错误:

C:\Users\xxxxx\AppData\Local\Programs\Python\Python313\Lib\threading.py:1342: RuntimeWarning: coroutine 'send_rem' was never awaited
  self.function(*self.args, **self.kwargs)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

核心问题:send_rem是异步函数,但threading.Timer无法直接处理协程,必须通过异步事件桥接或使用适配异步的调度工具。


推荐解决方案:使用discord.py内置事件循环

discord.py基于asyncio开发,直接用事件循环调度异步任务是最适配的方案,无需处理线程冲突:

基础版实现

import asyncio
from discord.ext import commands

bot = commands.Bot(command_prefix="!", intents=...)
user_events = {}  # 用于管理用户的提醒任务

@bot.command()
async def reminder(ctx: commands.Context, message: str) -> None:
    print("scheduling reminder")
    
    async def delayed_reminder():
        await asyncio.sleep(5.0)  # 延迟5秒
        print("sending reminder")
        # 获取或创建私信频道
        dm_channel = ctx.author.dm_channel or await ctx.author.create_dm()
        if dm_channel:
            await dm_channel.send(message)
        # 任务完成后清理记录
        user_id = str(ctx.author.id)
        if user_id in user_events:
            del user_events[user_id]
    
    # 将任务加入机器人事件循环
    task = bot.loop.create_task(delayed_reminder())
    user_events[str(ctx.author.id)] = task
    await ctx.send("提醒已设置")

# 可选:添加取消提醒命令
@bot.command()
async def cancel_reminder(ctx: commands.Context):
    user_id = str(ctx.author.id)
    if user_id in user_events:
        task = user_events[user_id]
        if not task.done():
            task.cancel()
            del user_events[user_id]
            await ctx.send("提醒已取消")
        else:
            await ctx.send("当前无活跃提醒")
    else:
        await ctx.send("当前无活跃提醒")

备选方案:兼容threading.Timer的写法

如果一定要用threading.Timer,需要在同步回调中把异步任务提交到机器人的事件循环:

import asyncio
from threading import Timer
from discord.ext import commands

bot = commands.Bot(command_prefix="!", intents=...)
user_events = {}

async def send_rem(author, message):
    print("sending reminder")
    dm_channel = author.dm_channel or await author.create_dm()
    if dm_channel:
        await dm_channel.send(message)

def sync_trigger(loop, author, msg):
    # 将异步函数提交到事件循环执行
    loop.create_task(send_rem(author, msg))

@bot.command()
async def reminder(ctx: commands.Context, message: str) -> None:
    print("scheduling reminder")
    # 传入机器人的事件循环到同步回调
    timer = Timer(5.0, sync_trigger, args=[bot.loop, ctx.author, message])
    event = {"message": message, "timer": timer}
    user_events[str(ctx.author.id)] = event
    timer.start()
    await ctx.send("提醒已设置")

错误原因解释

threading.Timer的回调在独立线程中同步执行,调用异步函数send_rem会直接返回协程对象,但没有任何机制去await它,因此触发"coroutine was never awaited"警告。必须通过asyncio事件循环来调度异步任务,才能正确执行Discord的异步操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 14:05:54