Part 8 - Command Checks#

For this section, we’ll be making a purge command, which will delete messages in bulk.

You don’t want just anyone to be able to use this command, only those who can delete messages themselves, so we’re gonna need to add some command checks to ensure that!

So, create a new file named mod.py in the extensions folder.

In it paste the following:

 1import datetime
 2
 3import hikari
 4import lightbulb
 5
 6mod_plugin = lightbulb.Plugin("Mod")
 7
 8
 9@mod_plugin.command
10@lightbulb.option(
11    "sent_by",
12    "Only purge messages sent by this user.",
13    type=hikari.User,
14    required=False,
15)
16@lightbulb.option(
17    "messages",
18    "The number of messages to purge.",
19    type=int,
20    required=True,
21    min_value=2,
22    max_value=200,
23)
24@lightbulb.command("purge", "Purge messages.", auto_defer=True)
25@lightbulb.implements(lightbulb.SlashCommand)
26async def purge_messages(ctx: lightbulb.SlashContext) -> None:
27    num_msgs = ctx.options.messages
28    sent_by = ctx.options.sent_by
29    channel = ctx.channel_id
30
31    bulk_delete_limit = datetime.datetime.now(
32        datetime.timezone.utc
33    ) - datetime.timedelta(days=14)
34
35    iterator = (
36        ctx.bot.rest.fetch_messages(channel)
37        .take_while(lambda msg: msg.created_at > bulk_delete_limit)
38        .filter(lambda msg: not (msg.flags & hikari.MessageFlag.LOADING))
39    )
40    if sent_by:
41        iterator = iterator.filter(lambda msg: msg.author.id == sent_by.id)
42
43    iterator = iterator.limit(num_msgs)
44
45    count = 0
46
47    async for messages in iterator.chunk(100):
48        count += len(messages)
49        await ctx.bot.rest.delete_messages(channel, messages)
50
51    await ctx.respond(f"{count} messages deleted.", delete_after=5)
52
53
54def load(bot: lightbulb.BotApp) -> None:
55    bot.add_plugin(mod_plugin)
  • Line 10-23 - Set up the command options

    • For the messages option, we’ve set limits.
      When bulk deleting messages, Discord says you must have a minimum value of 2 messages. And because we don’t want our bot to get rate limited while deleting huge amounts of messages, we also set our own maximum value of 200.

  • Line 24 - Pass a new kwarg auto_defer, setting it to True

    • This will respond to the command with a DEFERRED response type, showing the user a "<bot name> is thinking..." message until we respond properly after deleting the messages

  • Line 27-28 - If we don’t use pass_options=True in the command decorator (like with the userinfo command), we can’t pass the options as function parameters, but their values can still be accessed via ctx.options

  • Line 31-33 - Bots can’t bulk delete messages older than 2 weeks, so we set a limit to only fetch messages younger than 2 weeks

  • Line 35-43

    • Line 36 - Create an iterator which fetches the most recent messages in the channel
      Read the docs - fetch_messages
      Read the docs - LazyIterator

    • Line 37 - Take only the messages younger than 2 weeks
      Read the docs - .take_while()

    • Line 38 - Filter the iterator, ignoring messages that have the LOADING message flag. These are the messages that display "<bot name> is thinking..."
      Read the docs - .filter()

    • Line 40-41 - If a sent_by user was provided, then filter the iterator to only fetch messages sent by that user

    • Line 43 - Finally, after all our filters have been applied, limit the number of messages to fetch

  • Line 47-49 - Delete the messages in the iterator, in chunks of 100

    • A maximum of 100 messages can be passed per bulk delete request, so we chunk them into groups of 100 and make multiple requests

This command works fine, but now everyone can delete messages using the bot. We only want people with the Manage Messages permission to do this, so this is where checks come in.

Just below line 9 (@mod_plugin.command), add the following:

@lightbulb.app_command_permissions(hikari.Permissions.MANAGE_MESSAGES, dm_enabled=False)
@lightbulb.add_checks(
    lightbulb.bot_has_guild_permissions(hikari.Permissions.MANAGE_MESSAGES),
)
  • Line 1 - Using Discord’s app command permissions, we set default permissions of MANAGE_MESSAGES for the user, and we disable the command in DMs

  • Line 2-4 - Check the bot also has permission to delete messages in the guild.

If the both the user and bot have permission to run the command, it will work. However if the bot doesn’t have the MANAGE_MESSAGES permission, the command will raise CheckFailure.

But raising an error and the command failing isn’t that useful, we want to tell the user what happened.

So, onto error handling!