非异步函数场景下获取Discord create_task发送消息信息的问题
问题描述
我尝试不使用异步函数发送Discord消息,发送功能正常,但无法获取消息的相关信息(如message ID)。使用message_info.result返回connector: <aiohttp.connector.TCPConnector object at 0x0000018743206550>,调用message_info.result()则抛出asyncio.exceptions.InvalidStateError: Result is not set错误。
原代码:
import thread, discord from discord.ext import tasks # 补充原代码遗漏的导入 channel = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] # 假设为频道ID列表 @tasks.loop(seconds=10) async def notification(): def template(latest_data): embed = discord.Embed(title="Ring Ring!", description=latest_data["des"], colour=discord.Colour.green()) return embed def send(none,divided_channels,page): for channel_id in divided_channels: message_info = client.loop.create_task(channel.send(embed=page)) # 希望从message_info中获取发送的消息ID def newnotification(page): split = 5 looping = 0 while looping < len(channel): divided_channels = channel[looping:(split+looping)] looping += split var_name = f"variable{looping}" globals()[var_name] = threading.Thread(target=send,args=(divided_channels,divided_channels,page)) globals()[var_name].start() global oldest_data, channel latest_data = file("notification.json") # 此处为读取文件的简化写法 if (oldest_data != latest_data): page = template(latest_data) newnotification(page) else: oldnotification() # 原代码未定义该函数,暂忽略 @client.event async def on_ready(): notification.start()
问题根源
Discord.py是基于asyncio的异步框架,不能在普通线程中直接操作asyncio任务:
- 用threading开启的线程和Discord的事件循环上下文不匹配,导致任务状态无法同步,调用
result()时任务还未完成,就会抛出InvalidStateError;直接读取result属性只能拿到内部连接器对象,不是消息结果。 - 原代码中甚至没有正确获取频道对象(直接用
channel.send是错误的,channel是ID列表,不是Discord频道实例)。
解决方案
彻底抛弃线程,改用asyncio的异步并发方式,这是Discord.py官方推荐的处理批量操作的方式,既安全又高效。
修改后的代码:
import discord import json import asyncio from discord.ext import tasks # 初始化Bot客户端,需要配置正确的权限 client = discord.Client(intents=discord.Intents.default()) channel_ids = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] oldest_data = {} # 初始化全局变量,存储上次的通知数据 def template(latest_data): embed = discord.Embed(title="Ring Ring!", description=latest_data["des"], colour=discord.Colour.green()) return embed async def send_messages(divided_channel_ids, page): tasks_list = [] for channel_id in divided_channel_ids: # 先获取频道实例,避免无效调用 channel = client.get_channel(channel_id) if channel: # 将发送任务加入列表,等待批量执行 tasks_list.append(channel.send(embed=page)) # 等待所有发送任务完成,拿到所有消息对象 sent_messages = await asyncio.gather(*tasks_list, return_exceptions=True) # 遍历结果,提取消息ID for msg in sent_messages: if not isinstance(msg, Exception): print(f"已发送消息ID: {msg.id}") # 这里可以把ID存入数据库或做其他处理 async def new_notification(page): split = 5 looping = 0 while looping < len(channel_ids): divided_channels = channel_ids[looping:(split+looping)] looping += split # 异步调用批量发送函数,无需线程 await send_messages(divided_channels, page) @tasks.loop(seconds=10) async def notification(): global oldest_data # 正确读取JSON文件 with open("notification.json", "r", encoding="utf-8") as f: latest_data = json.load(f) if oldest_data != latest_data: page = template(latest_data) await new_notification(page) oldest_data = latest_data # 更新旧数据,避免重复发送 # else: # await old_notification() # 自行实现旧通知逻辑 @client.event async def on_ready(): print(f"已登录为 {client.user}") notification.start() # 运行Bot,替换为你的Bot令牌 client.run("YOUR_BOT_TOKEN")
关键修改说明
- 移除所有threading相关代码,改用
asyncio.gather实现异步并发发送,完全符合Discord.py的异步架构。 - 将
send和newnotification改为异步函数,确保所有Discord API调用都在异步上下文执行。 - 通过
await asyncio.gather直接获取发送后的Message对象,可直接读取msg.id等消息属性。 - 修复原代码的遗漏问题:创建client实例、正确读取文件、判断频道是否存在等。
- 避免使用
globals()动态创建变量,改用清晰的异步任务处理逻辑。
内容的提问来源于stack exchange,提问作者Andi Dewan
相关产品推荐
相关产品推荐

