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

如何并发获取Discord服务器多个频道的历史消息提升查询效率

问题根因
  • 变量作用域问题:Python中字符串、整数属于不可变类型,将msg和num作为参数传入starthistory时,函数内修改的只是局部副本,外部原始变量不会被更新。同时你没有等待asyncio.create_task创建的任务执行完成,主函数直接执行后续逻辑时遍历还未跑完,num自然保持初始值0。
  • 串行执行耗时高:你用for循环逐个查询频道历史,每个查询都要等待上一个完成才能开始,服务器频道数量多的时候耗时会线性增长。
修复方案

核心是用asyncio.gather并发执行所有频道的查询任务,避免共享可变变量,直接收集符合条件的结果即可,同时修复原代码中embed.author读取的逻辑问题(discord.py中embed.author是对象,需要取name属性才能得到字符串):

import asyncio
import textwrap

# 抽离单个频道的最新消息检查逻辑,符合条件返回频道对象,不符合返回None
async def check_channel(self, channel, key_lower):
    try:
        async for message in channel.history(limit=1):
            message_content = message.content.lower()
            if message.embeds:
                embed = message.embeds[0]
                if embed.title:
                    message_content = embed.title.lower()
                elif embed.author and embed.author.name:
                    message_content = embed.author.name.lower()
                elif embed.description:
                    message_content = embed.description.lower()
            if message_content.startswith(key_lower):
                return channel
    except Exception:
        # 自动跳过无读取权限的频道
        return None

@_list.command(name="starts_with",
               aliases=["startswith", "sw", "s"],
               brief="Lists all channels with message starting with <key>.",
               help="Lists all channels with last message starting with the word/phrase <key>.",
               case_insensitive=True)
async def _starts_with(self, ctx, *, key):
    key_lower = key.lower()
    wait = await ctx.send(f"Looking for messages starting with `{key}`...")

    # 为每个文本频道创建检查任务,并发执行
    tasks = [self.check_channel(channel, key_lower) for channel in ctx.guild.text_channels]
    # 等待所有任务完成,收集结果
    results = await asyncio.gather(*tasks)
    # 过滤得到符合条件的频道列表
    matched_channels = [ch for ch in results if ch is not None]

    # 拼装结果消息
    msg = f"Channels with last message starting with `{key}`:\n"
    if not matched_channels:
        msg += "**None**\n"
    else:
        for idx, channel in enumerate(matched_channels, start=1):
            msg += f"**{idx}.** {channel.mention} - **{channel.name}**\n"
    msg += f"\nTotal number of channels = **{len(matched_channels)}**"

    # 分段发送消息
    for para in textwrap.wrap(msg, 2000, expand_tabs=False, replace_whitespace=False, fix_sentence_endings=False, break_long_words=False, drop_whitespace=False, break_on_hyphens=False, max_lines=None):
        await ctx.send(para)
        await asyncio.sleep(0.5)
    await wait.edit(content="✅ Done.")
优化说明
  • 并发执行所有频道的查询请求,耗时从O(n)降低到接近单次查询的耗时,服务器频道越多性能提升越明显
  • 去掉了全局共享的msg、num变量,直接收集结果再拼装,避免了不可变类型参数传递的坑
  • 增加了异常捕获,自动跳过没有读取权限的频道,避免命令直接报错
  • 修复了原代码中直接读取embed.author的逻辑错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 06:06:03