Confused on this Discord.py Rewrite + Reaction Light Code - Need Explanation

Viewed 219

So while we're all I'm surely aware of the entire copy and paste someone elses code, and magically it works, it looses some context and understanding when figuring out how this code actually works and functions.

I'm working with the Discord.py Rewrite, and a chunk of code called Reaction Light in order to create a bot that allows for self-assigning roles in my Discord Server. The bot is 100% functional and works as intended. Now, I have changed some things around from their code, so my methods are in different locations and are called from different areas.

Here's where I'm confused:

I have a method called isadmin() this is called whenever there needs to be a check completed to figure out if the user issuing the command is an admin. Admin roles are defined in the .env file which I'm retrieving using the dotenv module. Pretty straight forward stuff. (I'll rewrite that portion of it later and hopefully put all the admin role id's into a file and grab them from there.) However I'm confused on what exactly the third argument of this method does. msg=False

Whenever I wrote my cogs for this I'm calling this method as such without passing that 'msg' argument to it as illustrated below:

admin.py

# admin.py
# Simple ping pong command
@commands.command()
    async def ping(self, ctx):
        if helpers.isadmin(ctx):
            print("Running Command from admin.py")
            await ctx.send('Pong!')

Now in my on_message() listener method, it is passing the msg argument, then executing some code unrelated to the ping command but related to the functionality of the self-assigning role portion of the bot.

message.py

# message.py
@commands.Cog.listener()
    async def on_message(self, message):
        if helpers.isadmin(message, msg=True):
            # Execute some code here for the self-assigning roles

The way this workflow works is this as far as I know, and I'll be using the ping command as an example which is called by the r.ping command.

  1. on_message() listens for all messages being sent in the server
  2. Ping Command Issued In Channel By User
  3. on_message() hears the message & checks if the user is an admin, but also passes the msg argument
  4. The ping command is called from admin.py and then checks (again?) if the user is an admin, and if he/she is, follows through with the command.

So, I'm trying to figure out when or when not to use this msg argument, and if it's already checking to see if the user is an admin in the listener do I have to check again in the actual command itself? Isn't that a bit redundant?

Here's the isadmin() method in the helpers.py file

# helpers.py
def isadmin(self, ctx, msg=False):
        # Checks if command author has one of .env admin role IDs
        try:
            check = (
                [role.id for role in ctx.author.roles]
                if msg
                else [role.id for role in ctx.message.author.roles]
            )
            if self.admin_a in check or self.admin_b in check or self.admin_c in check:
                return True
            return False
        except AttributeError:
            # Error raised from 'fake' users, such as webhooks
            return False
1 Answers

To be entirely honest I'm not sure why that's there. If you have access to ctx.author you have access to ctx.message as ctx.author is simply the author of that message, seems redundent. However I strongly suggest you use checks for this. For example I have:

def is_owner():
    def predicate(ctx):
        return ctx.author.id in ctx.bot.config()["owners"]
    return commands.check(predicate)

and I use this as a decorator

# utils/checks/checks.py
from utils.checks import checks

@checks.is_owner()
@commands.group(hidden=True, case_insensitive=True, description="Load a module")
async def load(self, ctx):
    if not ctx.invoked_subcommand:
        return await ctx.send_help(ctx.command)

For example you could have

def is_admin():
    def predicate(ctx):
        role_id = 123123123123123 # replace this with os.getenv("wherever your admin role is")
        return role_id in [x.id for x in ctx.author.roles]
    return commands.check(predicate)

It's just much cleaner and much easier to use/understand in my opinion.

Related