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
相关产品推荐
相关产品推荐

