From dc919a7f7c83bc754e9de338aa54f1e9cd6bcc2c Mon Sep 17 00:00:00 2001 From: Samyak Mishra Date: Mon, 27 May 2024 21:01:29 +0530 Subject: [PATCH 1/4] Read bot admin IDs set in .env file --- .env.template | 1 + bot/main.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.env.template b/.env.template index 35647c5..f816416 100644 --- a/.env.template +++ b/.env.template @@ -23,6 +23,7 @@ MONGO_URI="mongodb://127.0.0.1:100" # needed by ./bot DISCORD_TOKEN="" +BOT_ADMINS="" # needed by ./portal CAS_LINK="https://cas.my-org.com/login" diff --git a/bot/main.py b/bot/main.py index cefe147..25335c7 100644 --- a/bot/main.py +++ b/bot/main.py @@ -57,6 +57,8 @@ SUBPATH = os.getenv("SUBPATH") BASE_URL = f"{PROTOCOL}://{HOST}{_PORT_AS_SUFFIX}{SUBPATH}" +BOT_ADMINS = {int(id) for id in os.getenv("BOT_ADMINS", "").split(",") if id} + intent = discord.Intents.default() intent.message_content = True bot = commands.Bot(command_prefix=".", intents=intent) From 538409545acf074fb610089993880607a7706b86 Mon Sep 17 00:00:00 2001 From: Ankith Date: Wed, 5 Jun 2024 18:49:11 +0530 Subject: [PATCH 2/4] Implement and use check_bot_admin in backend_info Co-authored-by: Samyak Mishra --- bot/main.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/bot/main.py b/bot/main.py index 25335c7..5d9d002 100644 --- a/bot/main.py +++ b/bot/main.py @@ -6,6 +6,7 @@ - `get_users_from_discordid()`: Find users from DB given user ID - `is_verified()`: If a user is present in DB or not +- `check_bot_admin()`: Check if a user is a bot admin or not - `get_realname_from_discordid()`: Get a user's real name from their Discord ID. - `send_link()`: Send link for reattempting authentication. - `create_roles_if_missing()`: Adds missing roles to a server. @@ -15,6 +16,7 @@ - `post_verification()`: Handle role add/delete and nickname set post-verification of given user. - `verify_user()`: Implements `.verify`. - `backend_info()`: Logs server details for debug purposes +- `backend_info_error()`: If the author of the message is not a bot admin then reply accordingly. - `is_academic()`: Checks if server is for academic use. - `query()`: Returns user details, uses Discord ID to find in DB. - `query_error()`: Replies eror message if server is not academic. @@ -91,6 +93,12 @@ def is_verified(user_id: int): return True if get_users_from_discordid(user_id) else False +@commands.check +def check_bot_admin(ctx: commands.Context): + """Checks if the user with the given discord ID is a bot admin or not.""" + return ctx.author.id in BOT_ADMINS + + def get_realname_from_discordid(user_id: int): """Returns the real name of the first user who matches the given ID.""" users = get_users_from_discordid(user_id) @@ -223,6 +231,7 @@ async def verify_user(ctx: commands.Context): @bot.hybrid_command(name="backend_info") +@check_bot_admin async def backend_info(ctx: commands.Context): """For debugging server info; sends details of the server.""" uname = platform.uname() @@ -237,6 +246,13 @@ async def backend_info(ctx: commands.Context): ) +@backend_info.error +async def backend_info_error(ctx: commands.Context, error: Exception): + """If the author of the message is not a bot admin then reply accordingly.""" + if isinstance(error, commands.CheckFailure): + await ctx.reply(f"{ctx.author.mention} is not a bot admin.", ephemeral=True) + + def is_academic(ctx: commands.Context): """Checks if the server is an academic server.""" if ctx.guild is None: From 0da63bba991e3ff0167ea9dc6221d7152449214c Mon Sep 17 00:00:00 2001 From: Ankith Date: Wed, 5 Jun 2024 18:50:36 +0530 Subject: [PATCH 3/4] Guard query/roll behind admin or academic check Co-authored-by: Samyak Mishra --- bot/main.py | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/bot/main.py b/bot/main.py index 5d9d002..9c15646 100644 --- a/bot/main.py +++ b/bot/main.py @@ -17,11 +17,10 @@ - `verify_user()`: Implements `.verify`. - `backend_info()`: Logs server details for debug purposes - `backend_info_error()`: If the author of the message is not a bot admin then reply accordingly. -- `is_academic()`: Checks if server is for academic use. +- `check_is_academic()`: Checks if server is for academic use. - `query()`: Returns user details, uses Discord ID to find in DB. -- `query_error()`: Replies eror message if server is not academic. - `roll()`: Returns user details, uses roll number to find in DB. -- `roll_error()`: Replies eror message if server is not academic. +- `roll_or_query_error()`: Replies eror message if server is not academic or author is not a bot admin. - `on_ready()`: Logs a message when the bot joins a server. - `main()`: Reads server config, loads DB and starts bot. @@ -253,7 +252,8 @@ async def backend_info_error(ctx: commands.Context, error: Exception): await ctx.reply(f"{ctx.author.mention} is not a bot admin.", ephemeral=True) -def is_academic(ctx: commands.Context): +@commands.check +def check_is_academic(ctx: commands.Context): """Checks if the server is an academic server.""" if ctx.guild is None: return False @@ -265,15 +265,16 @@ def is_academic(ctx: commands.Context): @bot.hybrid_command(name="query") -@commands.check(is_academic) +@commands.check_any(check_is_academic, check_bot_admin) async def query( ctx: commands.Context, identifier: discord.User, ): """ - First checks if the server is an academic one. If so, finds the user who invoked the - command (by Discord ID) in the DB. If present, replies with their name, email and - roll number. Otherwise replies telling the user they are not registed with CAS. + First checks if the server is an academic one or if the author is a bot admin. + If so, finds the user mentioned (by Discord ID) in the command in the DB. + If present, replies with their name, email and roll number. Otherwise + replies telling the author that the mentioned user is not registed with CAS. """ if db is None: await ctx.reply( @@ -295,27 +296,20 @@ async def query( ) -@query.error -async def query_error(ctx: commands.Context, error: Exception): - """ - For the `query` command, if the server is not academic, replies with error message. - """ - if isinstance(error, commands.CheckFailure): - await ctx.reply("This server is not for academic purposes.", ephemeral=True) - - @bot.hybrid_command(name="roll") -@commands.check(is_academic) +@commands.check_any(check_is_academic, check_bot_admin) async def roll( ctx: commands.Context, identifier: int, ): """ - First checks if the server is an academic one. If so, finds the user who invoked the - command in the DB. If present, replies with their name, email and - roll number. Otherwise replies telling the user they are not registed with CAS. + First checks if the server is an academic one or if the author is a bot admin. + If so, finds the user mentioned in the command in the DB. If present, replies + with their name, email and roll number. Otherwise replies telling the author + that the mentioned user is not registed with CAS. - Same as the `query` command, except this searches by roll number instead of Discord ID. + Same as the `query` command, except the user is mentioned by roll number + instead of Discord ID. """ if db is None: await ctx.reply( @@ -337,13 +331,19 @@ async def roll( ) +@query.error @roll.error -async def roll_error(ctx: commands.Context, error: Exception): +async def roll_or_query_error(ctx: commands.Context, error: Exception): """ - For the `roll` command, if the server is not academic, replies with error message. + For the `roll` and `query` commands, if the server is not academic and if + the author is not a bot admin, replies with an error message. """ if isinstance(error, commands.CheckFailure): - await ctx.reply("This server is not for academic purposes.", ephemeral=True) + await ctx.reply( + "This server is not for academic purposes " + f"and {ctx.author.mention} is not a bot admin.", + ephemeral=True, + ) @bot.event From 082ba89c50f6af80185e94afa0b12e3069098637 Mon Sep 17 00:00:00 2001 From: Ankith Date: Wed, 5 Jun 2024 20:14:20 +0530 Subject: [PATCH 4/4] Extend academic check to also check for mod perms --- bot/main.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/bot/main.py b/bot/main.py index 9c15646..3ec4480 100644 --- a/bot/main.py +++ b/bot/main.py @@ -17,7 +17,7 @@ - `verify_user()`: Implements `.verify`. - `backend_info()`: Logs server details for debug purposes - `backend_info_error()`: If the author of the message is not a bot admin then reply accordingly. -- `check_is_academic()`: Checks if server is for academic use. +- `check_is_academic_mod()`: Checks if server is for academic use. - `query()`: Returns user details, uses Discord ID to find in DB. - `roll()`: Returns user details, uses roll number to find in DB. - `roll_or_query_error()`: Replies eror message if server is not academic or author is not a bot admin. @@ -253,19 +253,26 @@ async def backend_info_error(ctx: commands.Context, error: Exception): @commands.check -def check_is_academic(ctx: commands.Context): - """Checks if the server is an academic server.""" +async def check_is_academic_mod(ctx: commands.Context): + """ + Checks if the server is an academic server, and that the invoker has moderation + permissions + """ if ctx.guild is None: return False try: - return server_configs[ctx.guild.id]["is_academic"] + if server_configs[ctx.guild.id]["is_academic"]: + return await commands.has_permissions(moderate_members=True).predicate(ctx) + except KeyError: - return False + pass + + return False @bot.hybrid_command(name="query") -@commands.check_any(check_is_academic, check_bot_admin) +@commands.check_any(check_is_academic_mod, check_bot_admin) async def query( ctx: commands.Context, identifier: discord.User, @@ -297,7 +304,7 @@ async def query( @bot.hybrid_command(name="roll") -@commands.check_any(check_is_academic, check_bot_admin) +@commands.check_any(check_is_academic_mod, check_bot_admin) async def roll( ctx: commands.Context, identifier: int,