首页 > 解决方案 > discord.py 锁定所有频道的命令需要很长时间

问题描述

我的不和谐机器人上有一个锁定频道的命令。锁定单个频道完美无缺,但是当我尝试锁定服务器中的所有频道时,根据有多少频道,需要很长时间才能完成。我不确定是否有办法加快这个过程,但这是我的代码:

  async def send_m(self, ctx, check: bool):
      for c in ctx.guild.text_channels:
          if c.overwrites_for(ctx.guild.default_role).send_messages != check:
             await c.set_permissions(ctx.guild.default_role, send_messages = check)
            
  #lock
  @commands.check_any(commands.is_owner(), commands.has_permissions(manage_channels = True), commands.guild_only())
  @_channel.command(name="lock",
                   brief="Locks channel(s).",
                   help="Lock current/all channel(s)"
                   )
  async def _lock(self, ctx, channel=None):
      overwrite = ctx.channel.overwrites_for(ctx.guild.default_role)
      try:
          if channel == None and overwrite.send_messages != False:
              await ctx.channel.set_permissions(ctx.guild.default_role, send_messages = False)
              await ctx.send("Locked.")
          
          if channel == None and overwrite.send_messages == False:
              await ctx.send("This channel is already locked.")
      
          if channel == "all":
              await ctx.send(f"This will **lock** *all* channels. Type `{confirm}` to confirm.")
              def check(m):
                  return m.channel.id == ctx.channel.id and m.author.id == ctx.author.id
              try:
                  msg = await self.bot.wait_for("message", timeout=30, check=check)
              except asyncio.TimeoutError:
                  return await ctx.send("Time's up. Aborted.")
              if msg.content.lower() != confirm:
                  return await ctx.send("Aborted.")

              await ctx.send("Locking all channels...")
              await self.send_m(ctx=ctx, check=False)
              await ctx.send("Locked all channels ✅.")
      except Exception as e:
          raise e

如您所见,我尝试将其设为异步函数以查看是否有帮助,但没有。难道我做错了什么?任何帮助,将不胜感激!

标签: pythonpython-3.xdiscord.pypython-asyncio

解决方案


You've written an asynchronous function, but it isn't really running asynchronously.

async def send_m(self, ctx, check: bool):
    for c in ctx.guild.text_channels:
        if c.overwrites_for(ctx.guild.default_role).send_messages != check:
            await c.set_permissions(ctx.guild.default_role, send_messages = check)

await is a blocking operation. When you run send_m, it iterates over the channels and calls c.set_permissions, waiting for each call to complete before moving on to the next.

Instead, you should schedule all of the c.set_permissions calls with the event loop before awaiting anything. One common way to do this is to create a task for each and then gather them.

tasks = []
for c in ctx.guild.text_channels:
    if c.overwrites_for(ctx.guild.default_role).send_messages != check:
        tasks.append(asyncio.create_task(c.set_permissions(ctx.guild.default_role, send_messages=check)))
await asyncio.gather(*tasks)

This will get you the result you want. However, there's no direct benefit to using create_task here since your for loop is synchronous and none of the tasks would be able to start anyway. You can do the same as above but remove the create_task call.

tasks = []
for c in ctx.guild.text_channels:
    if c.overwrites_for(ctx.guild.default_role).send_messages != check:
        tasks.append(c.set_permissions(ctx.guild.default_role, send_messages=check))
await asyncio.gather(*tasks)

推荐阅读