Home > Software engineering >  discord.py Command for locking all channels takes a long time
discord.py Command for locking all channels takes a long time

Time:10-17

I have a command on my discord bot that locks channels. Locking a single channel works flawlessly, but when i try to lock all channels in a server, it takes quite a while to do so depending on how many channels there are. Im not really sure if there is a way to speed up the process, but here's my code:

  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

As you can see, i tried making it a async function to see if that would help, but it didn't. Am i doing something wrong? Any help would be appreciated!

CodePudding user response:

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)
  • Related