如何使用atexit模块修复Discord机器人退出时的RuntimeWarning?
解决Discord机器人退出时发送Embed消息的RuntimeWarning问题
问题说明
我想给Discord机器人做一个退出处理器,让它每次离线时发送一条Embed消息。用atexit模块写了处理函数,但运行时出现以下警告:
/home/container/main.py:186: RuntimeWarning: coroutine 'Messageable.send' was never awaited final.send(embed=embed) RuntimeWarning: Enable tracemalloc to get the object allocation traceback
尝试把处理函数改成async def后又引发了其他问题,求修复方法。
原代码
import atexit # 其他discord.py相关导入... def my_exit_handler(): print("Exiting the program") time = get_current_timestamp() # 这个函数能正常工作 embed = discord.Embed( title=" ", description=f"## 🔁 Bot is Restarting. Time now: <t:{time}> (<t:{time}:R>)", color=0x005300 # 绿色 ) channel_id = 1149359551464882229 final = bot.get_channel(channel_id) final.send(embed=embed) save_thread_data() # 其他业务代码 save_leaderboard_data() # 其他业务代码 # 主程序代码... atexit.register(my_exit_handler) bot.run('my token')
解决方案
核心原因
final.send()是异步协程,必须用await调用,但atexit的回调是同步函数,没法直接用await;直接把处理函数改成async def后注册给atexit,协程也不会被执行,所以会报错。
推荐用以下两种方法解决:
方法1:使用discord.py自带的on_close事件(最推荐)
discord.py提供了on_close异步事件,会在机器人连接关闭时自动触发,完美适配异步操作:
# 先把处理函数改成异步函数 async def my_exit_handler(): print("Exiting the program") time = get_current_timestamp() embed = discord.Embed( title=" ", description=f"## 🔁 Bot is Restarting. Time now: <t:{time}> (<t:{time}:R>)", color=0x005300 # 绿色 ) channel_id = 1149359551464882229 final = bot.get_channel(channel_id) if final: # 先检查频道是否存在,避免找不到频道报错 await final.send(embed=embed) save_thread_data() save_leaderboard_data() # 注册on_close事件 @bot.event async def on_close(): await my_exit_handler() # 主程序代码... # 不需要再用atexit.register了 bot.run('my token')
方法2:如果必须用atexit(兼容其他退出场景)
如果还要处理非Discord触发的退出逻辑,需要手动获取异步事件循环,在同步回调里运行异步处理函数:
import asyncio import atexit # 异步处理函数 async def my_exit_handler(): print("Exiting the program") time = get_current_timestamp() embed = discord.Embed( title=" ", description=f"## 🔁 Bot is Restarting. Time now: <t:{time}> (<t:{time}:R>)", color=0x005300 # 绿色 ) channel_id = 1149359551464882229 final = bot.get_channel(channel_id) if final: await final.send(embed=embed) save_thread_data() save_leaderboard_data() # 同步回调函数,用来调用异步处理函数 def sync_exit_handler(): loop = asyncio.get_event_loop() # 如果事件循环还在运行,用create_task;如果已经关闭,重新创建并运行 if loop.is_running(): loop.create_task(my_exit_handler()) else: loop.run_until_complete(my_exit_handler()) # 注册atexit回调 atexit.register(sync_exit_handler) # 主程序代码... bot.run('my token')
注意:方法2可能存在事件循环已关闭的情况,不如方法1稳定,优先推荐方法1。
内容的提问来源于stack exchange,提问作者Planetech
相关产品推荐
相关产品推荐

