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

非异步函数场景下获取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")

关键修改说明
  1. 移除所有threading相关代码,改用asyncio.gather实现异步并发发送,完全符合Discord.py的异步架构。
  2. 将send和newnotification改为异步函数,确保所有Discord API调用都在异步上下文执行。
  3. 通过await asyncio.gather直接获取发送后的Message对象,可直接读取msg.id等消息属性。
  4. 修复原代码的遗漏问题:创建client实例、正确读取文件、判断频道是否存在等。
  5. 避免使用globals()动态创建变量,改用清晰的异步任务处理逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 14:15:34