如何同时获取多个频道历史记录?

Posted

技术标签:

【中文标题】如何同时获取多个频道历史记录?【英文标题】:How to fetch multiple channel histories concurrently? 【发布时间】:2021-12-14 13:02:46 【问题描述】:

所以我正在尝试为我的 Discord 机器人创建一个命令,它将检查服务器中的每个频道并检查每个频道中的最后一条消息,然后发送以 key 变量开头的所有频道。

  async def starthistory(self, ctx, key, msg, num):
      for channel in ctx.guild.text_channels:
          async for message in channel.history(limit=1):
              message_content = message.content.lower()
              if len(message.embeds) > 0:
                  if len(message.embeds[0].title) > 0:
                      message_content = message.embeds[0].title.lower()
                  elif len(message.embeds[0].author) > 0:
                      message_content = message.embeds[0].author.lower()
                  elif len(message.embeds[0].description) > 0:
                      message_content = message.embeds[0].description.lower()
                  
              if message_content.startswith(key.lower()):
                  num += 1
                  msg += f"\n**num.** channel.mention - **channel.name**"
                  
  #startswith
  @_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):
      
      msg = f"Channels with last message starting with `key`:"
      num = 0
      wait = await ctx.send(f"Looking for messages starting with `key`...")

      asyncio.create_task(self.starthistory(ctx=ctx, key=key, msg=msg, num=num))
                
      if num == 0:
          msg += "\n**None**"
      msg += f"\n\nTotal number of channels = **num**"
      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.")

我希望它同时查看每个频道的历史记录,这样就不会花很长时间。目前,我的代码不会更改已定义的变量:num 始终为 0,msg 始终为 None

如何同时查看每个频道的历史记录,而不是一次查看一个?

【问题讨论】:

您介意解释一下“这显然行不通”是什么意思吗?你收到并出错了吗?输出不是您预期的吗? 啊,好吧,我设置了 2 个变量,然后创建了一个任务来运行 starthistory 函数,但它不会改变已经定义的变量,所以 'num' 总是 0 而 'msg' 总是“无” 我只是真的需要一种方法来做我想做的事情,但它不必以我尝试做的同样的方式去做。我只需要一种方法来同时查看每个频道的历史记录,而不是一次一个 啊,谢谢您的澄清,您正在尝试使用 create_task 来实现并发,但这对您不起作用。假设您的代码在没有您的并发尝试的情况下工作,我会为此写一个答案。 【参考方案1】:

asyncio.create_task(coro) 创建一个异步任务并在后台运行它。要让for 循环异步运行,同时处理所有文本通道,您应该改用asyncio.gather(coros)

这是工作代码(我将您的代码缩减为仅相关部分):

@staticmethod
async def check_history(msgs, channel, key, semaphore):
    async with semaphore:
        async for message in channel.history(limit=1):
            message_content = message.content.lower()
            # trimmed some code...
            if message_content.startswith(key.lower()):
                num = len(msgs)
                msgs += [f"**num.** channel.mention - **channel.name**"]


@_list.command()
async def _starts_with(self, ctx, *, key):
    msgs = [f"Channels with last message starting with `key`:"]
    tasks = []
    semaphore = asyncio.Semaphore(10)

    for channel in ctx.guild.text_channels:
        tasks += [self.check_history(msgs, channel, key, semaphore)]

    await asyncio.gather(*tasks)

    if len(msgs) == 1:
        msgs += ["**None**"]
    msgs += [f"\nTotal number of channels = **len(msgs)**"]
    msg = "\n".join(msgs)
    print(msg)

注意要点/为什么会这样:

我使用asyncio.gather() 等待所有check_history 协程。 我使用了asyncio.Semaphore(10),它将最大并发限制为10。当您同时发送太多请求时,Discord API 不喜欢它。如果频道过多,您可能会被暂时屏蔽。 通常,不建议将不可变对象(str 和 int)传递给外部函数并尝试更改其值。对于您的用例,我相信最好的选择是使用 msg 列表,然后使用 str.join() 稍后。这也摆脱了num

【讨论】:

以上是关于如何同时获取多个频道历史记录?的主要内容,如果未能解决你的问题,请参考以下文章

如何没有历史记录?

linux 如何调出上网历史记录(用IP分类,因为有多个ip)用程序实现

如何在 django admin 中获取对象的历史记录?

页面链接跳转历史URL不记录的兼容处理

如何通过API从Hipchat获取房间的所有消息历史记录?

如何使用 SVNKit 获取日志/历史记录给出特定文件列表?