From 7d483fe6a1185f605ea9196d8d125a1131646d2d Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Thu, 16 May 2024 03:41:19 +0530 Subject: [PATCH 01/10] 2.0 rewrite --- .env.template | 31 --------------------- .gitignore | 1 + bot/config_verification.py | 12 ++++----- bot/main.py | 55 +++++++++++++++++++++++--------------- bot/requirements.txt | 18 +++++++------ 5 files changed, 50 insertions(+), 67 deletions(-) delete mode 100644 .env.template diff --git a/.env.template b/.env.template deleted file mode 100644 index 2450354..0000000 --- a/.env.template +++ /dev/null @@ -1,31 +0,0 @@ -# To configure the project, rename this file to .env and fill in your secrets -# configuration variables needed both ./bot and ./portal - -# this parameter must be either http or https. For local testing this can be -# http, but on production it must be https -PROTOCOL="http" - -# just the domain name part of the URL, like "example.com". If under a subdomain, must -# also be included here. Can also be an IP address like "xxx.xxx.xxx.xxx" -HOST="172.20.0.5" - -# the port must be specified here. Leave the field empty for the system to pick -# the default port (i.e 80) -PORT="" - -# any subpath used must be entered here. If this is configured, must have a leading -# slash and no trailing slash (for example: "/casbot"). Must be left empty if no -# subpath is used. -SUBPATH="" - -MONGO_DATABASE="casbot" -MONGO_URI="mongodb://127.0.0.1:100" - -# needed by ./bot -DISCORD_TOKEN="" - -# needed by ./portal -CAS_LINK="https://cas.my-org.com/login" -DISCORD_CLIENT_ID="" -DISCORD_SECRET="" -SECRET="" diff --git a/.gitignore b/.gitignore index 00639be..ddd9cb6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .venv node_modules __pycache__ +.github/ diff --git a/bot/config_verification.py b/bot/config_verification.py index 4db8b2c..767fbd9 100644 --- a/bot/config_verification.py +++ b/bot/config_verification.py @@ -8,7 +8,6 @@ from configparser import ConfigParser import sys - def read_and_validate_config(server_config: ConfigParser, config_file_path): """ Take in a `ConfigParser` object along with the path to a config file. @@ -20,7 +19,7 @@ def read_and_validate_config(server_config: ConfigParser, config_file_path): - `config_file_path`: a string, the path to the file containing server configurations """ - server_config.read(config_file_path, encoding="utf-8") + server_config.read(config_file_path) for section in server_config.sections(): section_obj = server_config[section] @@ -29,7 +28,7 @@ def read_and_validate_config(server_config: ConfigParser, config_file_path): "deleteroles", "is_academic", "setrealname", - } # Add optional keys here + } for key in section_obj.keys(): if key not in all_keys: @@ -39,12 +38,11 @@ def read_and_validate_config(server_config: ConfigParser, config_file_path): if len(req_keys) != 0: print(f"Missing keys: {' ,'.join(req_keys)} in section {section}") return False - + print(f"{section} config is valid!") - + return True - if __name__ == "__main__": if not read_and_validate_config(ConfigParser(), "server_config.ini"): - sys.exit(1) + sys.exit(1) \ No newline at end of file diff --git a/bot/main.py b/bot/main.py index 027682b..e21ee80 100644 --- a/bot/main.py +++ b/bot/main.py @@ -41,6 +41,7 @@ from config_verification import read_and_validate_config load_dotenv() + TOKEN = os.getenv("DISCORD_TOKEN") MONGO_DATABASE = os.getenv("MONGO_DATABASE") MONGO_URI = os.getenv("MONGO_URI") @@ -55,9 +56,11 @@ BASE_URL = f"{PROTOCOL}://{HOST}{_PORT_AS_SUFFIX}{SUBPATH}" SERVER_CONFIG = ConfigParser() -bot = commands.Bot(command_prefix=".") -db: database.Database = None # assigned in main function +intent = discord.Intents.default() +intent.message_content = True +bot = commands.Bot(command_prefix=".", intents=intent) +db: database.Database = None # assigned in main function def get_users_from_discordid(user_id): """ @@ -82,7 +85,7 @@ def get_realname_from_discordid(user_id): async def send_link(ctx): """Sends the base url for users to reattempt sign-in.""" - await ctx.send(f"<{BASE_URL}>\nSign in through our portal, and try again.") + await ctx.send(f"<{BASE_URL}>\nSign in through our portal, and try again.", ephemeral=True) def get_config(server_id: str): @@ -161,7 +164,8 @@ async def post_verification(ctx, user): if server_config is None: await ctx.send( - "This server is not authorized to work with CAS-bot. Read the instructions to invite the bot in the project README" + "This server is not authorized to work with CAS-bot. Read the instructions to invite the bot in the project README", + ephemeral=True ) await ctx.guild.leave() return @@ -172,12 +176,12 @@ async def post_verification(ctx, user): try: await set_nickname(user, server_config) except discord.DiscordException: - await ctx.send("Bot should have a role higher than you to change your nickname") + await ctx.send("Bot should have a role higher than you to change your nickname", ephemeral=True) - await ctx.send(f"<@{user.id}> has been CAS-verified!") + await ctx.send(f"<@{user.id}> has been CAS-verified!", ephemeral=True) -@bot.command(name="verify") +@bot.hybrid_command(name="verify") async def verify_user(ctx): """ Runs when the user types `.verify` in the server. First tries to find the user in the DB. @@ -200,11 +204,12 @@ async def verify_user(ctx): else: await ctx.send( f"Sorry <@{user_id}>, could not auto-detect your verification. \ - Please run `.verify` again." + Please run `.verify` again.", + ephemeral=True ) -@bot.command(name="backend_info") +@bot.hybrid_command(name="backend_info") async def backend_info(ctx): """For debugging server info; sends details of the server.""" uname = platform.uname() @@ -214,7 +219,8 @@ async def backend_info(ctx): f"node: {uname.node}\n" f"release: {uname.release}\n" f"version: {uname.version}\n" - f"machine: {uname.machine}" + f"machine: {uname.machine}", + ephemeral=True ) @@ -227,7 +233,7 @@ def is_academic(ctx: commands.Context): return server_config.get("is_academic", False) -@bot.command(name="query") +@bot.hybrid_command(name="query") @commands.check(is_academic) async def query( ctx: commands.Context, @@ -240,11 +246,12 @@ async def query( """ user = db.users.find_one({"discordId": str(identifier.id)}) if user: - await ctx.reply( - f"Name: {user['name']}\nEmail: {user['email']}\nRoll Number: {user['rollno']}" + await ctx.send( + f"Name: {user['name']}\nEmail: {user['email']}\nRoll Number: {user['rollno']}", + ephemeral=True ) else: - await ctx.reply(f"{identifier} is not registered with IIIT-CAS.") + await ctx.send(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) @query.error @@ -253,10 +260,10 @@ async def query_error(ctx, error): 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.") + await ctx.send("This server is not for academic purposes.", ephemeral=True) -@bot.command(name="roll") +@bot.hybrid_command(name="roll") @commands.check(is_academic) async def roll( ctx: commands.Context, @@ -271,11 +278,12 @@ async def roll( """ user = db.users.find_one({"rollno": str(identifier)}) if user: - await ctx.reply( - f"Name: {user['name']}\nEmail: {user['email']}\nRoll Number: {user['rollno']}" + await ctx.send( + f"Name: {user['name']}\nEmail: {user['email']}\nRoll Number: {user['rollno']}", + ephemeral=True ) else: - await ctx.reply(f"{identifier} is not registered with IIIT-CAS.") + await ctx.send(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) @roll.error @@ -284,7 +292,7 @@ async def roll_error(ctx, error): For the `roll` 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.") + await ctx.reply("This server is not for academic purposes.", ephemeral=True) @bot.event @@ -310,6 +318,11 @@ async def on_guild_join(guild): async def on_ready(): """This is executed when the bot connects to a server.""" print(f"{bot.user.name} has connected to Discord!") + try: + synced = await bot.tree.sync() + print(f"Synced {len(synced)} commands.") + except Exception as e: + print(e) def main(): @@ -332,4 +345,4 @@ def main(): if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/bot/requirements.txt b/bot/requirements.txt index 8a9447b..3affe6d 100644 --- a/bot/requirements.txt +++ b/bot/requirements.txt @@ -1,12 +1,14 @@ -aiohttp==3.7.4.post0 -async-timeout==3.0.1 +aiohttp==3.9.5 +aiosignal==1.3.1 +async-timeout==4.0.3 attrs==23.2.0 -chardet==4.0.0 -discord.py==1.7.2 -dnspython==1.16.0 -idna==3.6 +chardet==5.2.0 +discord.py==2.3.2 +dnspython==2.6.1 +frozenlist==1.4.1 +idna==3.7 multidict==6.0.5 -pymongo==3.13.0 +pymongo==4.7.2 python-dotenv==1.0.1 -typing_extensions==4.10.0 +typing_extensions==4.11.0 yarl==1.9.4 From e3411f0e94867974e32c2f7635988461f30c2de5 Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Wed, 15 May 2024 22:11:57 +0000 Subject: [PATCH 02/10] Apply Linting & Formatting Fixes --- bot/config_verification.py | 8 +++++--- bot/main.py | 22 ++++++++++++++-------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/bot/config_verification.py b/bot/config_verification.py index 767fbd9..2aa1bcf 100644 --- a/bot/config_verification.py +++ b/bot/config_verification.py @@ -8,6 +8,7 @@ from configparser import ConfigParser import sys + def read_and_validate_config(server_config: ConfigParser, config_file_path): """ Take in a `ConfigParser` object along with the path to a config file. @@ -38,11 +39,12 @@ def read_and_validate_config(server_config: ConfigParser, config_file_path): if len(req_keys) != 0: print(f"Missing keys: {' ,'.join(req_keys)} in section {section}") return False - + print(f"{section} config is valid!") - + return True + if __name__ == "__main__": if not read_and_validate_config(ConfigParser(), "server_config.ini"): - sys.exit(1) \ No newline at end of file + sys.exit(1) diff --git a/bot/main.py b/bot/main.py index e21ee80..adcb73a 100644 --- a/bot/main.py +++ b/bot/main.py @@ -62,6 +62,7 @@ db: database.Database = None # assigned in main function + def get_users_from_discordid(user_id): """ Finds users from the database, given their ID and returns @@ -85,7 +86,9 @@ def get_realname_from_discordid(user_id): async def send_link(ctx): """Sends the base url for users to reattempt sign-in.""" - await ctx.send(f"<{BASE_URL}>\nSign in through our portal, and try again.", ephemeral=True) + await ctx.send( + f"<{BASE_URL}>\nSign in through our portal, and try again.", ephemeral=True + ) def get_config(server_id: str): @@ -165,7 +168,7 @@ async def post_verification(ctx, user): if server_config is None: await ctx.send( "This server is not authorized to work with CAS-bot. Read the instructions to invite the bot in the project README", - ephemeral=True + ephemeral=True, ) await ctx.guild.leave() return @@ -176,7 +179,10 @@ async def post_verification(ctx, user): try: await set_nickname(user, server_config) except discord.DiscordException: - await ctx.send("Bot should have a role higher than you to change your nickname", ephemeral=True) + await ctx.send( + "Bot should have a role higher than you to change your nickname", + ephemeral=True, + ) await ctx.send(f"<@{user.id}> has been CAS-verified!", ephemeral=True) @@ -205,7 +211,7 @@ async def verify_user(ctx): await ctx.send( f"Sorry <@{user_id}>, could not auto-detect your verification. \ Please run `.verify` again.", - ephemeral=True + ephemeral=True, ) @@ -220,7 +226,7 @@ async def backend_info(ctx): f"release: {uname.release}\n" f"version: {uname.version}\n" f"machine: {uname.machine}", - ephemeral=True + ephemeral=True, ) @@ -248,7 +254,7 @@ async def query( if user: await ctx.send( f"Name: {user['name']}\nEmail: {user['email']}\nRoll Number: {user['rollno']}", - ephemeral=True + ephemeral=True, ) else: await ctx.send(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) @@ -280,7 +286,7 @@ async def roll( if user: await ctx.send( f"Name: {user['name']}\nEmail: {user['email']}\nRoll Number: {user['rollno']}", - ephemeral=True + ephemeral=True, ) else: await ctx.send(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) @@ -345,4 +351,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() From 6f7c2f9c5a358c7243589aed7e594b9a6066ce4e Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Thu, 16 May 2024 03:43:50 +0530 Subject: [PATCH 03/10] Added a comment --- bot/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/main.py b/bot/main.py index e21ee80..927a84d 100644 --- a/bot/main.py +++ b/bot/main.py @@ -59,6 +59,7 @@ intent = discord.Intents.default() intent.message_content = True bot = commands.Bot(command_prefix=".", intents=intent) +# to get message privelege db: database.Database = None # assigned in main function From b1c19e9b5b1fdba92a3d411998a976e6a7ac1a0e Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Thu, 16 May 2024 03:47:38 +0530 Subject: [PATCH 04/10] Correcting error in .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index ddd9cb6..00639be 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,3 @@ .venv node_modules __pycache__ -.github/ From 96d0fa94ae21d4f05d71aafc2352ba993d146581 Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Thu, 16 May 2024 03:50:43 +0530 Subject: [PATCH 05/10] Removing errors from config_verification.py --- bot/config_verification.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/config_verification.py b/bot/config_verification.py index 2aa1bcf..777bcbc 100644 --- a/bot/config_verification.py +++ b/bot/config_verification.py @@ -20,7 +20,7 @@ def read_and_validate_config(server_config: ConfigParser, config_file_path): - `config_file_path`: a string, the path to the file containing server configurations """ - server_config.read(config_file_path) + server_config.read(config_file_path, encoding="utf-8") for section in server_config.sections(): section_obj = server_config[section] @@ -29,7 +29,7 @@ def read_and_validate_config(server_config: ConfigParser, config_file_path): "deleteroles", "is_academic", "setrealname", - } + } # Add optional keys here for key in section_obj.keys(): if key not in all_keys: @@ -47,4 +47,4 @@ def read_and_validate_config(server_config: ConfigParser, config_file_path): if __name__ == "__main__": if not read_and_validate_config(ConfigParser(), "server_config.ini"): - sys.exit(1) + sys.exit(1) \ No newline at end of file From cbf2453f98a54087fa2c5324390d93cc18345e42 Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Wed, 15 May 2024 22:21:24 +0000 Subject: [PATCH 06/10] Apply Linting & Formatting Fixes --- bot/config_verification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/config_verification.py b/bot/config_verification.py index 777bcbc..4db8b2c 100644 --- a/bot/config_verification.py +++ b/bot/config_verification.py @@ -47,4 +47,4 @@ def read_and_validate_config(server_config: ConfigParser, config_file_path): if __name__ == "__main__": if not read_and_validate_config(ConfigParser(), "server_config.ini"): - sys.exit(1) \ No newline at end of file + sys.exit(1) From b460fb1886f58b8420c48a39308abe4b2aaef9b0 Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Thu, 16 May 2024 03:53:36 +0530 Subject: [PATCH 07/10] Added env.template back --- .env.template | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .env.template diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..35647c5 --- /dev/null +++ b/.env.template @@ -0,0 +1,31 @@ +# To configure the project, rename this file to .env and fill in your secrets +# configuration variables needed both ./bot and ./portal + +# this parameter must be either http or https. For local testing this can be +# http, but on production it must be https +PROTOCOL="http" + +# just the domain name part of the URL, like "example.com". If under a subdomain, must +# also be included here. Can also be an IP address like "xxx.xxx.xxx.xxx" +HOST="172.20.0.5" + +# the port must be specified here. Leave the field empty for the system to pick +# the default port (i.e 80) +PORT="" + +# any subpath used must be entered here. If this is configured, must have a leading +# slash and no trailing slash (for example: "/casbot"). Must be left empty if no +# subpath is used. +SUBPATH="" + +MONGO_DATABASE="casbot" +MONGO_URI="mongodb://127.0.0.1:100" + +# needed by ./bot +DISCORD_TOKEN="" + +# needed by ./portal +CAS_LINK="https://cas.my-org.com/login" +DISCORD_CLIENT_ID="" +DISCORD_SECRET="" +SECRET="" \ No newline at end of file From f06a5fcb1cef40b12055dc612105250a6de2276d Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Sun, 19 May 2024 01:53:10 +0530 Subject: [PATCH 08/10] Successful verification reply is no longer ephemeral --- bot/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/main.py b/bot/main.py index f372fcb..f78dfba 100644 --- a/bot/main.py +++ b/bot/main.py @@ -185,7 +185,7 @@ async def post_verification(ctx, user): ephemeral=True, ) - await ctx.send(f"<@{user.id}> has been CAS-verified!", ephemeral=True) + await ctx.send(f"<@{user.id}> has been CAS-verified!") @bot.hybrid_command(name="verify") From b9738f271a6dcc4f8808d267bc89aa9bec9fae48 Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Mon, 20 May 2024 02:25:57 +0530 Subject: [PATCH 09/10] Replace all ctx.send() functions with ctx.reply() --- bot/main.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bot/main.py b/bot/main.py index f78dfba..762cba0 100644 --- a/bot/main.py +++ b/bot/main.py @@ -87,7 +87,7 @@ def get_realname_from_discordid(user_id): async def send_link(ctx): """Sends the base url for users to reattempt sign-in.""" - await ctx.send( + await ctx.reply( f"<{BASE_URL}>\nSign in through our portal, and try again.", ephemeral=True ) @@ -167,7 +167,7 @@ async def post_verification(ctx, user): server_config = get_config(server_id) if server_config is None: - await ctx.send( + await ctx.reply( "This server is not authorized to work with CAS-bot. Read the instructions to invite the bot in the project README", ephemeral=True, ) @@ -180,12 +180,12 @@ async def post_verification(ctx, user): try: await set_nickname(user, server_config) except discord.DiscordException: - await ctx.send( + await ctx.reply( "Bot should have a role higher than you to change your nickname", ephemeral=True, ) - await ctx.send(f"<@{user.id}> has been CAS-verified!") + await ctx.reply(f"<@{user.id}> has been CAS-verified!") @bot.hybrid_command(name="verify") @@ -209,7 +209,7 @@ async def verify_user(ctx): await send_link(ctx) await asyncio.sleep(60) else: - await ctx.send( + await ctx.reply( f"Sorry <@{user_id}>, could not auto-detect your verification. \ Please run `.verify` again.", ephemeral=True, @@ -220,7 +220,7 @@ async def verify_user(ctx): async def backend_info(ctx): """For debugging server info; sends details of the server.""" uname = platform.uname() - await ctx.send( + await ctx.reply( f"Here are the server details:\n" f"system: {uname.system}\n" f"node: {uname.node}\n" @@ -253,12 +253,12 @@ async def query( """ user = db.users.find_one({"discordId": str(identifier.id)}) if user: - await ctx.send( + await ctx.reply( f"Name: {user['name']}\nEmail: {user['email']}\nRoll Number: {user['rollno']}", ephemeral=True, ) else: - await ctx.send(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) + await ctx.reply(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) @query.error @@ -267,7 +267,7 @@ async def query_error(ctx, error): For the `query` command, if the server is not academic, replies with error message. """ if isinstance(error, commands.CheckFailure): - await ctx.send("This server is not for academic purposes.", ephemeral=True) + await ctx.reply("This server is not for academic purposes.", ephemeral=True) @bot.hybrid_command(name="roll") @@ -285,12 +285,12 @@ async def roll( """ user = db.users.find_one({"rollno": str(identifier)}) if user: - await ctx.send( + await ctx.reply( f"Name: {user['name']}\nEmail: {user['email']}\nRoll Number: {user['rollno']}", ephemeral=True, ) else: - await ctx.send(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) + await ctx.reply(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) @roll.error From 5d1e3eced28371a5d95fff54ea6ab17b29795837 Mon Sep 17 00:00:00 2001 From: The-Coder-Kishor Date: Sun, 19 May 2024 20:56:30 +0000 Subject: [PATCH 10/10] Apply Linting & Formatting Fixes --- bot/main.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bot/main.py b/bot/main.py index 762cba0..ae09349 100644 --- a/bot/main.py +++ b/bot/main.py @@ -258,7 +258,9 @@ async def query( ephemeral=True, ) else: - await ctx.reply(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) + await ctx.reply( + f"{identifier} is not registered with IIIT-CAS.", ephemeral=True + ) @query.error @@ -290,7 +292,9 @@ async def roll( ephemeral=True, ) else: - await ctx.reply(f"{identifier} is not registered with IIIT-CAS.", ephemeral=True) + await ctx.reply( + f"{identifier} is not registered with IIIT-CAS.", ephemeral=True + ) @roll.error