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)