From 8cf9b2bf7690d88a75897ccbb7b560b932adf2fc Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 16:41:08 -0400 Subject: [PATCH 01/60] Minor Error Handling fixes os.environ['TOKEN'] raised a KeyError instead of falling through to the else branch setup_logger called with bool(os.getenv("STREAM_LOGS")) -- bool("false") is True since any non-empty string is truthy int(os.getenv("BOT_LOG_LEVEL")) will crash if env var is missing If BOT_LOG_LEVEL is unset, os.getenv() raises TypeError --- src/bot/main.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bot/main.py b/src/bot/main.py index ff146ba..78a4ea1 100644 --- a/src/bot/main.py +++ b/src/bot/main.py @@ -10,7 +10,10 @@ discord.VoiceClient.warn_nacl = False logger = logging.getLogger(__name__) -setup_logger(level=int(os.getenv("BOT_LOG_LEVEL")), stream_logs=bool(os.getenv("STREAM_LOGS"))) +setup_logger( + level=int(os.getenv("BOT_LOG_LEVEL", "20")), + stream_logs=os.getenv("STREAM_LOGS", "true").lower() == "true" +) intents = discord.Intents.all() bot = commands.Bot(command_prefix=os.getenv("PREFIX"), intents=intents) @@ -87,9 +90,9 @@ def boink() -> None: logger.debug('Loading Token from arg.') bot.run(token) - elif os.environ['TOKEN'] is not None: # if not in args, check the env vars + elif token := os.environ.get('TOKEN'): # if not in args, check the env vars logger.debug('Loading Token from environment variable.') - bot.run(os.environ['TOKEN']) + bot.run(token) else: logger.critical('You must include a bot token...') From c7c0d0e5d921ff47c5419a438f1e3ce46c5f0229 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 17:03:30 -0400 Subject: [PATCH 02/60] Fix response formatting and update setting retrieval --- src/api/routes/settings.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/api/routes/settings.py b/src/api/routes/settings.py index db8c2c8..824d021 100644 --- a/src/api/routes/settings.py +++ b/src/api/routes/settings.py @@ -11,21 +11,20 @@ @settings.route('/settings', methods=['GET']) @settings.route('/settings/', methods=['GET']) -def get_setting(setting_id): +def get_setting(setting_id=None): """ Retrieve settings from the database. :param setting_id: Optional integer ID of a specific setting :return: JSON response with setting """ - if setting_id == 0: - # Retrieve all settings + if setting_id is None or setting_id == 0: # Retrieve all settings result = eos.db.get_settings() else: # Retrieve a single setting result = eos.db.get_setting(setting_id) - return jsonify(result, 200) + return jsonify(result),200 @settings.route('/settings/', methods=['PUT']) @@ -36,9 +35,9 @@ def update_setting(setting_id): if request.method == 'PUT': data = request.json result = eos.db.update_setting(int(setting_id), data['value']) - return jsonify(result, 200) + return jsonify(result) 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @settings.route('/settings', methods=['POST']) def add_setting(): @@ -50,7 +49,7 @@ def add_setting(): result = eos.db.add_setting(data['name'], data['value']) return jsonify(result, 201) - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @settings.route('/settings/', methods=['DELETE']) def delete_setting(setting_id): @@ -61,4 +60,4 @@ def delete_setting(setting_id): result = eos.db.delete_setting(setting_id) return jsonify(result, 200) - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 From 697b87ce4aa7ea4bede80d1a0dcb2c860390532c Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 17:04:07 -0400 Subject: [PATCH 03/60] Fix response formatting in healthchecks.py --- src/api/routes/healthchecks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/routes/healthchecks.py b/src/api/routes/healthchecks.py index 34448d8..9d6d12d 100644 --- a/src/api/routes/healthchecks.py +++ b/src/api/routes/healthchecks.py @@ -19,7 +19,7 @@ def api_health_check(): if request.method == 'GET': return jsonify({'status': 'ok'}, 200) - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @health_checks.route('/hc_db', methods=['GET']) @@ -33,6 +33,6 @@ def database_health_check(): return jsonify(hc, 200) except TypeError as ded: - return jsonify({"status": "unhealthy", "error": "DB unreachable"}, 404) + return jsonify({"status": "unhealthy", "error": "DB unreachable"}), 404 - return jsonify({'message': 'improper request method'}, 404) + return jsonify({'message': 'improper request method'}), 404 From 606dbeeffd70b94c8a1085c5d370b2ff2ae8ce02 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 17:05:24 -0400 Subject: [PATCH 04/60] Fix jsonify return statements in logging.py --- src/api/routes/logging.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api/routes/logging.py b/src/api/routes/logging.py index 6d26100..8eeed05 100644 --- a/src/api/routes/logging.py +++ b/src/api/routes/logging.py @@ -25,7 +25,7 @@ def get_log_setting(log_id=None): # Retrieve a single setting result = eos.db.get_log_setting(log_id) - return jsonify(result, 200) + return jsonify(result), 200 # @settings.route('/log_settings', methods=['GET']) # def get_log_settings(): @@ -46,9 +46,9 @@ def update_log_setting(log_id): if request.method == 'PUT': data = request.json result = eos.db.update_logging(int(log_id), data['value']) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @logs.route('/logging', methods=['POST']) def add_log_setting(): @@ -60,7 +60,7 @@ def add_log_setting(): result = eos.db.add_log_setting(data['name'], data['value']) return jsonify(result, 201) - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @logs.route('/logging/', methods=['DELETE']) def delete_log_setting(log_id): @@ -69,6 +69,6 @@ def delete_log_setting(log_id): """ if request.method == 'DELETE': result = eos.db.delete_log_setting(log_id) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 From d0e66b4aeccf1f1fcad17123d402597149273264 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 17:06:58 -0400 Subject: [PATCH 05/60] Fix jsonify calls in roles.py --- src/api/routes/roles.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api/routes/roles.py b/src/api/routes/roles.py index 08b6752..ac37470 100644 --- a/src/api/routes/roles.py +++ b/src/api/routes/roles.py @@ -25,7 +25,7 @@ def get_role(role_id=None): # Retrieve a single role result = eos.db.get_role(role_id) - return jsonify(result, 200) + return jsonify(result), 200 @role.route('/role/', methods=['PUT']) def update_role(role_id): @@ -35,9 +35,9 @@ def update_role(role_id): if request.method == 'PUT': data = request.json result = eos.db.update_role(int(role_id), data['value']) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @role.route('/role', methods=['POST']) def add_role(): @@ -47,9 +47,9 @@ def add_role(): if request.method == 'POST': data = request.json result = eos.db.add_role(data['name'], data['value']) - return jsonify(result, 201) + return jsonify(result), 201 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @role.route('/role/', methods=['DELETE']) def delete_role(role_id): @@ -58,6 +58,6 @@ def delete_role(role_id): """ if request.method == 'DELETE': result = eos.db.delete_role(role_id) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 From c0e8bd0fbcdb3255a07de8e7c7854e918cf28e91 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 17:18:57 -0400 Subject: [PATCH 06/60] Refactor points update message and error handling --- src/bot/cogs/admin/points.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/bot/cogs/admin/points.py b/src/bot/cogs/admin/points.py index 99e312e..b96c051 100644 --- a/src/bot/cogs/admin/points.py +++ b/src/bot/cogs/admin/points.py @@ -101,9 +101,11 @@ async def update_points(self, ctx: commands.Context, user: discord.Member, amoun if update_points['status'] == 'ok': await ctx.reply( embed=embed_info( - "" - , f"{amount.lstrip('-+')} points {'removed from' if amount.startswith('-') else 'added to'} {user.display_name}" - , discord.Color.green() if not amount.startswith('-') else discord.Color.red() + "", + f"{abs(amount)} points " + f"{'removed from' if amount < 0 else 'added to'} " + f"{user.display_name}", + discord.Color.red() if amount < 0 else discord.Color.green() ) ) else: @@ -181,12 +183,10 @@ async def on_command_error(self, ctx: commands.Context, error): Error handling for the >update_points command. """ if isinstance(error, commands.MissingRequiredArgument): - await ctx.reply( - embed=embed_info( - "Error!", "You must provide a required argument." - , discord.Color.dark_gray() - ) - ) + await ctx.reply(embed=embed_info("Error!", "You must provide a required argument.", discord.Color.dark_gray())) + elif isinstance(error, commands.MissingPermissions): + logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") + await ctx.send('For one reason, or another, YOU cannot use this command.') @sync_users.error async def sync_users_command_error(self, ctx, error): @@ -194,11 +194,11 @@ async def sync_users_command_error(self, ctx, error): logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") await ctx.send('For one reason, or another, YOU cannot use this command.') - @update_points.error - async def update_points_command_error(self, ctx, error): - if isinstance(error, commands.CheckFailure): - logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") - await ctx.send('For one reason, or another, YOU cannot use this command.') + #@update_points.error + #async def update_points_command_error(self, ctx, error): + # if isinstance(error, commands.CheckFailure): + # logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") + # await ctx.send('For one reason, or another, YOU cannot use this command.') async def setup(bot: commands.Bot) -> None: From 6926579e444961f9653ddd71cc8cff3de6bff59a Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 20:27:50 -0400 Subject: [PATCH 07/60] Fix return statements in settings routes --- src/api/routes/settings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/routes/settings.py b/src/api/routes/settings.py index 824d021..e188017 100644 --- a/src/api/routes/settings.py +++ b/src/api/routes/settings.py @@ -24,7 +24,7 @@ def get_setting(setting_id=None): # Retrieve a single setting result = eos.db.get_setting(setting_id) - return jsonify(result),200 + return jsonify(result), 200 @settings.route('/settings/', methods=['PUT']) @@ -35,7 +35,7 @@ def update_setting(setting_id): if request.method == 'PUT': data = request.json result = eos.db.update_setting(int(setting_id), data['value']) - return jsonify(result) 200 + return jsonify(result), 200 return jsonify({'message': 'improper request method'}), 405 @@ -47,7 +47,7 @@ def add_setting(): if request.method == 'POST': data = request.json result = eos.db.add_setting(data['name'], data['value']) - return jsonify(result, 201) + return jsonify(result), 201 return jsonify({'message': 'improper request method'}), 405 @@ -58,6 +58,6 @@ def delete_setting(setting_id): """ if request.method == 'DELETE': result = eos.db.delete_setting(setting_id) - return jsonify(result, 200) + return jsonify(result), 200 return jsonify({'message': 'improper request method'}), 405 From 1161320f961e3b6e3b0880c39e5d8c0ce1c5be24 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 20:29:31 -0400 Subject: [PATCH 08/60] Fix formatting in settings.py From 22e28a38901ce8a44dc1ad50a192402b39f9d8b4 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 22:29:31 -0400 Subject: [PATCH 09/60] Please Dont Break. Using VS to commit changes. --- src/docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/docker-compose.yml b/src/docker-compose.yml index 74e2e26..90fca48 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -32,6 +32,8 @@ services: image: eos-api:latest networks: - eos + ports: + - "5000:5000" depends_on: migration: condition: service_completed_successfully @@ -48,6 +50,7 @@ services: options: max-size: "10m" max-file: "3" + postgres: container_name: db From 99fccab1900757bbc35682a44d8a916834f8f4f2 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 22:31:02 -0400 Subject: [PATCH 10/60] oops i broke the api routes for settings, but i fixed it and added a few more things to the .gitignore --- .gitignore | 13 ++++-- src/api/routes/settings.py | 8 ++-- src/bot/cogs/admin/points.py | 4 +- src/bot/cogs/admin/settings.py | 72 ++++++++++++++++++---------------- 4 files changed, 53 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index 6233bb3..b115186 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,11 @@ .env /src/db/postgres-data/ +/src/*docker +src/docker-compose.yml + +scratchpad/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -131,10 +136,7 @@ venv/ ENV/ env.bak/ venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject +.vscode/ # Rope project settings .ropeproject @@ -157,3 +159,6 @@ dmypy.json cython_debug/ /.obsidian/ +src/docker-compose.yml +src/docker-compose.yml +src/docker-compose.yml diff --git a/src/api/routes/settings.py b/src/api/routes/settings.py index 824d021..7e68d16 100644 --- a/src/api/routes/settings.py +++ b/src/api/routes/settings.py @@ -18,7 +18,7 @@ def get_setting(setting_id=None): :param setting_id: Optional integer ID of a specific setting :return: JSON response with setting """ - if setting_id is None or setting_id == 0: # Retrieve all settings + if setting_id: # Retrieve all settings result = eos.db.get_settings() else: # Retrieve a single setting @@ -35,7 +35,7 @@ def update_setting(setting_id): if request.method == 'PUT': data = request.json result = eos.db.update_setting(int(setting_id), data['value']) - return jsonify(result) 200 + return jsonify(result), 200 return jsonify({'message': 'improper request method'}), 405 @@ -47,7 +47,7 @@ def add_setting(): if request.method == 'POST': data = request.json result = eos.db.add_setting(data['name'], data['value']) - return jsonify(result, 201) + return jsonify(result), 201 return jsonify({'message': 'improper request method'}), 405 @@ -58,6 +58,6 @@ def delete_setting(setting_id): """ if request.method == 'DELETE': result = eos.db.delete_setting(setting_id) - return jsonify(result, 200) + return jsonify(result), 200 return jsonify({'message': 'improper request method'}), 405 diff --git a/src/bot/cogs/admin/points.py b/src/bot/cogs/admin/points.py index b96c051..feadeb1 100644 --- a/src/bot/cogs/admin/points.py +++ b/src/bot/cogs/admin/points.py @@ -48,8 +48,8 @@ async def sync_users(self, ctx: commands.Context) -> None: Users that exist in the DB will not be synced. """ added = 0 - for user in ctx.guild.members: - self.bot.api.add_user_to_points(user.id) + for user in ctx.guild.members: # type: ignore + self.bot.api.add_user_to_points(user.id) # type: ignore added += 1 await ctx.reply( embed=embed_info( diff --git a/src/bot/cogs/admin/settings.py b/src/bot/cogs/admin/settings.py index b0c5a16..7a572e8 100644 --- a/src/bot/cogs/admin/settings.py +++ b/src/bot/cogs/admin/settings.py @@ -28,42 +28,46 @@ def __init__(self, bot: commands.Bot) -> None: @is_admin() @commands.hybrid_command() async def settings(self, ctx: commands.Context): - """ - List all available settings. - """ - - server_settings = self.bot.api.get_all_settings() - log_settings = self.bot.api.get_all_log_settings() - - if server_settings[0]["status"] != "ok": - await ctx.send(f"Failed to retrieve settings: {server_settings['message']}") - return - if log_settings[0]["status"] != "ok": - await ctx.send(f"Failed to retrieve settings: {log_settings['message']}") - return - - embed = discord.Embed(title="-- Settings --", - description="Here, you can see the current settings for the server.", - colour=0x000000, - timestamp=datetime.datetime.now()) - - for setting in server_settings[0]["setting"]: - value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' - embed.add_field(name="" - , value=f"**{setting[1]}**:{value}" - , inline=False) - - for setting in log_settings[0]["logging"]: - value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' - embed.add_field(name=f"" - , value=f"**{setting[1]}**:{value}" - , inline=False) - - - embed.set_footer(text=ctx.guild.name, + """ + List all available settings. + """ + + server_settings = self.bot.api.get_all_settings() + log_settings = self.bot.api.get_all_log_settings() + try: + if server_settings[0]["status"] != "ok": + await ctx.send(f"Failed to retrieve settings: {server_settings['message']}") + return + if log_settings[0]["status"] != "ok": + await ctx.send(f"Failed to retrieve settings: {log_settings['message']}") + return + except Exception as e: + logger.error(f"Error retrieving settings: {e}") + await ctx.send("An error occurred while retrieving settings.") + return + + embed = discord.Embed(title="-- Settings --", + description="Here, you can see the current settings for the server.", + colour=0x000000, + timestamp=datetime.datetime.now()) + + for setting in server_settings[0]["setting"]: + value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' + embed.add_field(name="" + , value=f"**{setting[1]}**:{value}" + , inline=False) + + for setting in log_settings[0]["logging"]: + value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' + embed.add_field(name=f"" + , value=f"**{setting[1]}**:{value}" + , inline=False) + + + embed.set_footer(text=ctx.guild.name, icon_url=ctx.guild.icon) - await ctx.send(embed=embed) + await ctx.send(embed=embed) @is_master_guild() @is_admin() From c544e2e184d5c8c22b92caaa5d872669af956961 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 23:00:25 -0400 Subject: [PATCH 11/60] little debug to help find settings error --- src/bot/cogs/admin/settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bot/cogs/admin/settings.py b/src/bot/cogs/admin/settings.py index 7a572e8..f08f64e 100644 --- a/src/bot/cogs/admin/settings.py +++ b/src/bot/cogs/admin/settings.py @@ -35,6 +35,9 @@ async def settings(self, ctx: commands.Context): server_settings = self.bot.api.get_all_settings() log_settings = self.bot.api.get_all_log_settings() try: + logger.debug(f"Server settings response: {server_settings}") + logger.debug(f"Log settings response: {log_settings}") + # {"status": "error", "message": str(err)} or return {"status": "ok", "setting": result} if server_settings[0]["status"] != "ok": await ctx.send(f"Failed to retrieve settings: {server_settings['message']}") return From 307c56463fe2afae6e8c0381a433131ea685f5a8 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 23:10:04 -0400 Subject: [PATCH 12/60] More Debug Testing... --- src/api/core/db_helper.py | 10 +++++++--- src/bot/cogs/admin/settings.py | 3 --- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index d1a47d1..1959cec 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -101,9 +101,11 @@ def get_setting(self, setting_id): try: self.cursor.execute("SELECT * FROM serversettings where id = %s", (setting_id,)) result = self.cursor.fetchone() - return {"status": "ok", "setting": result} + response = {"status": "ok", "setting": result} + logger.debug(f"DB Response:\n{response}") + return response except OperationalError as err: - logger.error(f"Error fetching logging: {err}") + logger.error(f"Error fetching setting: {err}") return {"status": "error", "message": str(err)} def get_settings(self): @@ -111,7 +113,9 @@ def get_settings(self): try: self.cursor.execute("SELECT * FROM serversettings") # case sensitive result = self.cursor.fetchall() - return {"status": "ok", "setting": result} + response = {"status": "ok", "setting": result} + logger.debug(f"DB Response:\n{response}") + return response except OperationalError as err: logger.error(f"Error fetching setting: {err}") return {"status": "error", "message": str(err)} diff --git a/src/bot/cogs/admin/settings.py b/src/bot/cogs/admin/settings.py index f08f64e..7a572e8 100644 --- a/src/bot/cogs/admin/settings.py +++ b/src/bot/cogs/admin/settings.py @@ -35,9 +35,6 @@ async def settings(self, ctx: commands.Context): server_settings = self.bot.api.get_all_settings() log_settings = self.bot.api.get_all_log_settings() try: - logger.debug(f"Server settings response: {server_settings}") - logger.debug(f"Log settings response: {log_settings}") - # {"status": "error", "message": str(err)} or return {"status": "ok", "setting": result} if server_settings[0]["status"] != "ok": await ctx.send(f"Failed to retrieve settings: {server_settings['message']}") return From 29c5a4a04f5c108ff037a3a91ab297c66ea4b552 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 23:19:32 -0400 Subject: [PATCH 13/60] Last of the usless prints --- src/api/core/db_helper.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index 1959cec..2753d11 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -51,7 +51,9 @@ def get_log_settings(self): try: self.cursor.execute("SELECT * FROM logging") # case sensitive result = self.cursor.fetchall() - return {"status": "ok", "logging": result} + response = {"status": "ok", "logging": result} + logger.debug(f"DB Response:\n{response}") + return response except OperationalError as err: logger.error(f"Error fetching logging: {err}") return {"status": "error", "message": str(err)} @@ -61,7 +63,9 @@ def get_logging(self): try: self.cursor.execute("SELECT * FROM logging") result = self.cursor.fetchall() - return {"status": "ok", "logging": result} + response = {"status": "ok", "logging": result} + logger.debug(f"DB Response:\n{response}") + return response except OperationalError as err: logger.error(f"Error fetching logging: {err}") return {"status": "error", "message": str(err)} @@ -96,6 +100,16 @@ def delete_log_setting(self, log_id): ################## ## Settings ## ################## + + # {'status': 'ok', + # 'setting': [ + # (3, 'Staff Channel', '1506111576946511923'), + # (1, 'Verification Channel', '1506114864828121158'), + # (4, 'Bot Spam Channel', '1506114864828121158'), + # (2, 'Quarantine Channel', '1506114864828121158') + # ] + # } + def get_setting(self, setting_id): logger.debug("API attempting to contact DB for get_setting...") try: From 0b8483f26f15deffa4968261fa4fa85ef1382b51 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 23:42:47 -0400 Subject: [PATCH 14/60] Extension 'cogs.logging.logging_message_delete' raised an error: KeyError: 0 --- src/api/core/db_helper.py | 18 +++++++++++------- src/api/routes/settings.py | 4 ++-- src/bot/cogs/logging/logging_message_delete.py | 2 ++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index 2753d11..c7e0f03 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -46,6 +46,15 @@ def get_log_setting(self, setting_id): logger.error(f"Error fetching logging: {err}") return {"status": "error", "message": str(err)} + # { + # 'status': 'ok', + # 'setting': [ + # (3, 'Staff Channel', '1506111576946511923'), + # (1, 'Verification Channel', '1506114864828121158'), + # (4, 'Bot Spam Channel', '1506114864828121158'), + # (2, 'Quarantine Channel', '1506114864828121158') + # ] + # } def get_log_settings(self): logger.debug("API attempting to contact DB for get_log_settings...") try: @@ -115,11 +124,8 @@ def get_setting(self, setting_id): try: self.cursor.execute("SELECT * FROM serversettings where id = %s", (setting_id,)) result = self.cursor.fetchone() - response = {"status": "ok", "setting": result} - logger.debug(f"DB Response:\n{response}") - return response + return {"status": "ok", "setting": result} except OperationalError as err: - logger.error(f"Error fetching setting: {err}") return {"status": "error", "message": str(err)} def get_settings(self): @@ -127,9 +133,7 @@ def get_settings(self): try: self.cursor.execute("SELECT * FROM serversettings") # case sensitive result = self.cursor.fetchall() - response = {"status": "ok", "setting": result} - logger.debug(f"DB Response:\n{response}") - return response + return {"status": "ok", "setting": result} except OperationalError as err: logger.error(f"Error fetching setting: {err}") return {"status": "error", "message": str(err)} diff --git a/src/api/routes/settings.py b/src/api/routes/settings.py index 7e68d16..e188017 100644 --- a/src/api/routes/settings.py +++ b/src/api/routes/settings.py @@ -18,13 +18,13 @@ def get_setting(setting_id=None): :param setting_id: Optional integer ID of a specific setting :return: JSON response with setting """ - if setting_id: # Retrieve all settings + if setting_id is None or setting_id == 0: # Retrieve all settings result = eos.db.get_settings() else: # Retrieve a single setting result = eos.db.get_setting(setting_id) - return jsonify(result),200 + return jsonify(result), 200 @settings.route('/settings/', methods=['PUT']) diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index cd6520e..09844f2 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -50,7 +50,9 @@ class LoggingMessageDelete(commands.Cog): def __init__(self, bot): self.bot = bot self.staff_channel = self.bot.api.get_one_setting('3')[0]['setting'][2] # Staff Channel ID + logger.debug(f"LoggingMessageDelete cog initialized. Staff channel ID: {self.staff_channel}") self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log + logger.debug(f"LoggingMessageDelete cog initialized. Chat log settings: {self.chat_log}") @commands.Cog.listener() async def on_message_delete(self, message): From e797c214cc11f1ad9794aa8014962a12aa507fc6 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 23:45:45 -0400 Subject: [PATCH 15/60] test fix --- src/bot/cogs/logging/logging_message_delete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index 09844f2..e7ea56b 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -49,7 +49,7 @@ class LoggingMessageDelete(commands.Cog): def __init__(self, bot): self.bot = bot - self.staff_channel = self.bot.api.get_one_setting('3')[0]['setting'][2] # Staff Channel ID + self.staff_channel = self.bot.api.get_one_setting('3')['setting'][2] # Staff Channel ID logger.debug(f"LoggingMessageDelete cog initialized. Staff channel ID: {self.staff_channel}") self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log logger.debug(f"LoggingMessageDelete cog initialized. Chat log settings: {self.chat_log}") From 0118ba2556c3e365847e3619a203bf1f52f84531 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 23:50:33 -0400 Subject: [PATCH 16/60] debgging KeyError --- src/bot/cogs/logging/logging_message_delete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index e7ea56b..2dd2276 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -49,7 +49,7 @@ class LoggingMessageDelete(commands.Cog): def __init__(self, bot): self.bot = bot - self.staff_channel = self.bot.api.get_one_setting('3')['setting'][2] # Staff Channel ID + self.staff_channel = self.bot.api.get_one_setting('3')#['setting'][2] # Staff Channel ID logger.debug(f"LoggingMessageDelete cog initialized. Staff channel ID: {self.staff_channel}") self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log logger.debug(f"LoggingMessageDelete cog initialized. Chat log settings: {self.chat_log}") From 21826c135e2073be994f5512c33e0047be5862b9 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 00:01:54 -0400 Subject: [PATCH 17/60] Removed [0] --- .../cogs/logging/logging_message_delete.py | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index 2dd2276..8d28dce 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -49,10 +49,22 @@ class LoggingMessageDelete(commands.Cog): def __init__(self, bot): self.bot = bot - self.staff_channel = self.bot.api.get_one_setting('3')#['setting'][2] # Staff Channel ID - logger.debug(f"LoggingMessageDelete cog initialized. Staff channel ID: {self.staff_channel}") + # 'setting': [3, 'Staff Channel', '1506111576946511923'], 'status': 'ok'} + setting = self.bot.api.get_one_setting('3') + if setting['status'] != 'ok': + logger.error(f"API error. API response not ok. -> {setting}") + return + else: + self.staff_channel = setting['setting'][2] + logger.debug(f"LoggingMessageDelete cog initialized. Staff channel ID: {self.staff_channel}") + + #{'logging': [3, 'Chat Log', '1394473942537994363'], 'status': 'ok'} self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log - logger.debug(f"LoggingMessageDelete cog initialized. Chat log settings: {self.chat_log}") + if self.chat_log['status'] != 'ok': + logger.error(f"API error. API response not ok. -> {self.chat_log}") + return + else: + logger.debug(f"LoggingMessageDelete cog initialized. Chat log settings: {self.chat_log}") @commands.Cog.listener() async def on_message_delete(self, message): @@ -69,11 +81,11 @@ async def on_message_delete(self, message): return audit_log = [entry async for entry in message.guild.audit_logs(limit=1)][0] - if self.chat_log[0]["status"] == "ok": - if self.chat_log[0]["logging"][2] == "0": + if self.chat_log["status"] == "ok": + if self.chat_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.chat_log}") return - logs_channel = await self.bot.fetch_channel(self.chat_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.chat_log["logging"][2]) if str(audit_log.action) == 'AuditLogAction.message_delete': # Then a moderator deleted a message. From 767a2dabb888a1d65b73cb8a939f280c11fef8a8 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 00:08:25 -0400 Subject: [PATCH 18/60] logging redo removing [0] index error from db data return --- src/bot/cogs/logging/logging_avatars.py | 6 +++--- src/bot/cogs/logging/logging_member_ban.py | 10 +++++----- src/bot/cogs/logging/logging_member_kick.py | 8 ++++---- src/bot/cogs/logging/logging_member_leaves.py | 8 ++++---- src/bot/cogs/logging/logging_message_delete.py | 4 ++-- src/bot/cogs/logging/logging_message_edit.py | 8 ++++---- src/bot/cogs/logging/logging_name_changes.py | 6 +++--- src/bot/cogs/logging/logging_roles.py | 6 +++--- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/bot/cogs/logging/logging_avatars.py b/src/bot/cogs/logging/logging_avatars.py index d1d2f26..b7bf1e8 100644 --- a/src/bot/cogs/logging/logging_avatars.py +++ b/src/bot/cogs/logging/logging_avatars.py @@ -49,11 +49,11 @@ async def on_user_update(self, before, after): # return if before.avatar != after.avatar: - if self.user_log[0]["status"] == "ok": - if self.user_log[0]["logging"][2] == "0": + if self.user_log["status"] == "ok": + if self.user_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.user_log}") return - logs_channel = await self.bot.fetch_channel(self.user_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.user_log["logging"][2]) embed = embed_avatar(before, after) diff --git a/src/bot/cogs/logging/logging_member_ban.py b/src/bot/cogs/logging/logging_member_ban.py index 89a366c..9df1494 100644 --- a/src/bot/cogs/logging/logging_member_ban.py +++ b/src/bot/cogs/logging/logging_member_ban.py @@ -31,7 +31,7 @@ class LoggingBans(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_role = self.bot.api.get_one_role('6')[0]['roles'][2] # Verification role ID + self.verification_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log @commands.Cog.listener() @@ -50,11 +50,11 @@ async def on_member_remove(self, member): audit_log = [entry async for entry in member.guild.audit_logs(limit=1)][0] - if self.mod_log[0]["status"] == "ok": - if self.mod_log[0]["logging"][2] == "0": - logger.debug(f"log was triggered, but logging is disabled. API: {self.join_log}") + if self.mod_log["status"] == "ok": + if self.mod_log["logging"][2] == "0": + logger.debug(f"log was triggered, but logging is disabled. API: {self.mod_log}") return - logs_channel = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.mod_log["logging"][2]) if str(audit_log.action) == "AuditLogAction.ban": if audit_log.target == member: diff --git a/src/bot/cogs/logging/logging_member_kick.py b/src/bot/cogs/logging/logging_member_kick.py index 05309ef..d604499 100644 --- a/src/bot/cogs/logging/logging_member_kick.py +++ b/src/bot/cogs/logging/logging_member_kick.py @@ -33,7 +33,7 @@ class LoggingKicks(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_role = self.bot.api.get_one_role('6')[0]['roles'][2] # Verification role ID + self.verification_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log @commands.Cog.listener() @@ -52,11 +52,11 @@ async def on_member_remove(self, member): audit_log = [entry async for entry in member.guild.audit_logs(limit=1)][0] - if self.mod_log[0]["status"] == "ok": - if self.mod_log[0]["logging"][2] == "0": + if self.mod_log["status"] == "ok": + if self.mod_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.mod_log}") return - logs_channel = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.mod_log["logging"][2]) if str(audit_log.action) == "AuditLogAction.kick": if audit_log.target == member: diff --git a/src/bot/cogs/logging/logging_member_leaves.py b/src/bot/cogs/logging/logging_member_leaves.py index 1fa68e9..329c382 100644 --- a/src/bot/cogs/logging/logging_member_leaves.py +++ b/src/bot/cogs/logging/logging_member_leaves.py @@ -30,7 +30,7 @@ class LoggingLeaves(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_role = self.bot.api.get_one_role('6')[0]['roles'][2] # Verification role ID + self.verification_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID self.join_log = self.bot.api.get_one_log_setting("2") # Join_log @commands.Cog.listener() @@ -47,11 +47,11 @@ async def on_member_remove(self, member): if self.verification_role in [role.id for role in member.roles]: return - if self.join_log[0]["status"] == "ok": - if self.join_log[0]["logging"][2] == "0": + if self.join_log["status"] == "ok": + if self.join_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.join_log}") return - logs_channel = await self.bot.fetch_channel(self.join_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.join_log["logging"][2]) audit_log = [entry async for entry in member.guild.audit_logs(limit=1)][0] diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index 8d28dce..6481fb9 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -57,14 +57,14 @@ def __init__(self, bot): else: self.staff_channel = setting['setting'][2] logger.debug(f"LoggingMessageDelete cog initialized. Staff channel ID: {self.staff_channel}") - + #{'logging': [3, 'Chat Log', '1394473942537994363'], 'status': 'ok'} self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log if self.chat_log['status'] != 'ok': logger.error(f"API error. API response not ok. -> {self.chat_log}") return else: - logger.debug(f"LoggingMessageDelete cog initialized. Chat log settings: {self.chat_log}") + logger.debug(f"LoggingMessageDelete cog initialized. Chat log setting: {self.chat_log}") @commands.Cog.listener() async def on_message_delete(self, message): diff --git a/src/bot/cogs/logging/logging_message_edit.py b/src/bot/cogs/logging/logging_message_edit.py index 5b7cdbe..7748729 100644 --- a/src/bot/cogs/logging/logging_message_edit.py +++ b/src/bot/cogs/logging/logging_message_edit.py @@ -49,7 +49,7 @@ class LoggingMessageEdit(commands.Cog): def __init__(self, bot): self.bot = bot - self.staff_channel = self.bot.api.get_one_setting('3')[0]['setting'][2] # Staff Channel ID + self.staff_channel = self.bot.api.get_one_setting('3')['setting'][2] # Staff Channel ID self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log @commands.Cog.listener() @@ -71,12 +71,12 @@ async def on_message_edit(self, message_before, message_after): return elif message_before.content != message_after.content: - if self.chat_log[0]["status"] == "ok": - if self.chat_log[0]["logging"][2] == "0": + if self.chat_log["status"] == "ok": + if self.chat_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.chat_log}") return - logs_channel = await self.bot.fetch_channel(self.chat_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.chat_log["logging"][2]) # This guy here makes sure we use the displayed name inside the guild. if message_after.author.nick is None: diff --git a/src/bot/cogs/logging/logging_name_changes.py b/src/bot/cogs/logging/logging_name_changes.py index 76efc6b..8b609aa 100644 --- a/src/bot/cogs/logging/logging_name_changes.py +++ b/src/bot/cogs/logging/logging_name_changes.py @@ -54,11 +54,11 @@ async def on_member_update(self, before, after): username_after = after.nick if before.nick != after.nick and before.nick is not None: - if self.user_log[0]["status"] == "ok": - if self.user_log[0]["logging"][2] == "0": + if self.user_log["status"] == "ok": + if self.user_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.user_log}") return - logs_channel = await self.bot.fetch_channel(self.user_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.user_log["logging"][2]) embed = embed_name_change(username_before, username_after) diff --git a/src/bot/cogs/logging/logging_roles.py b/src/bot/cogs/logging/logging_roles.py index 52c01e9..455522b 100644 --- a/src/bot/cogs/logging/logging_roles.py +++ b/src/bot/cogs/logging/logging_roles.py @@ -63,11 +63,11 @@ async def on_member_update(self, before, after): responsible_member = audit_log.user changed_roles = [] - if self.mod_log[0]["status"] == "ok": - if self.mod_log[0]["logging"][2] == "0": + if self.mod_log["status"] == "ok": + if self.mod_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.mod_log}") return - logs_channel = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.mod_log["logging"][2]) if len(before.roles) > len(after.roles): for role in before.roles: From 4ad085edbc3130802497d79b5a21cecda6c75f35 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 00:15:00 -0400 Subject: [PATCH 19/60] Error Handeling Potentialy --- src/bot/cogs/logging/logging_member_ban.py | 8 +++++++- src/bot/cogs/logging/logging_member_kick.py | 9 ++++++++- src/bot/cogs/logging/logging_member_leaves.py | 9 ++++++++- src/bot/cogs/logging/logging_message_edit.py | 8 +++++++- src/bot/cogs/logging/logging_roles.py | 2 +- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/bot/cogs/logging/logging_member_ban.py b/src/bot/cogs/logging/logging_member_ban.py index 9df1494..f84e2f6 100644 --- a/src/bot/cogs/logging/logging_member_ban.py +++ b/src/bot/cogs/logging/logging_member_ban.py @@ -31,7 +31,13 @@ class LoggingBans(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID + setting = self.bot.api.get_one_role('6') + if setting["status"] == "ok": + self.verification_role = setting["roles"][2] # Verification role ID + else: + self.verification_role = 0 + logger.error(f"API error. API response not ok. -> {setting}") + self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log @commands.Cog.listener() diff --git a/src/bot/cogs/logging/logging_member_kick.py b/src/bot/cogs/logging/logging_member_kick.py index d604499..7ac567e 100644 --- a/src/bot/cogs/logging/logging_member_kick.py +++ b/src/bot/cogs/logging/logging_member_kick.py @@ -33,7 +33,14 @@ class LoggingKicks(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID + setting = self.bot.api.get_one_role('6') + if setting["status"] == "ok": + self.verification_role = setting["roles"][2] # Verification role ID + else: + self.verification_role = 0 + logger.error(f"API error. API response not ok. -> {setting}") + + self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log @commands.Cog.listener() diff --git a/src/bot/cogs/logging/logging_member_leaves.py b/src/bot/cogs/logging/logging_member_leaves.py index 329c382..7612b99 100644 --- a/src/bot/cogs/logging/logging_member_leaves.py +++ b/src/bot/cogs/logging/logging_member_leaves.py @@ -30,7 +30,14 @@ class LoggingLeaves(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID + setting = self.bot.api.get_one_role('6') + if setting["status"] == "ok": + self.verification_role = setting["roles"][2] # Verification role ID + else: + self.verification_role = 0 + logger.error(f"API error. API response not ok. -> {setting}") + return + self.join_log = self.bot.api.get_one_log_setting("2") # Join_log @commands.Cog.listener() diff --git a/src/bot/cogs/logging/logging_message_edit.py b/src/bot/cogs/logging/logging_message_edit.py index 7748729..ae26da0 100644 --- a/src/bot/cogs/logging/logging_message_edit.py +++ b/src/bot/cogs/logging/logging_message_edit.py @@ -49,7 +49,13 @@ class LoggingMessageEdit(commands.Cog): def __init__(self, bot): self.bot = bot - self.staff_channel = self.bot.api.get_one_setting('3')['setting'][2] # Staff Channel ID + setting = self.bot.api.get_one_setting('3') + if setting["status"] == "ok": + self.staff_channel = setting["setting"][2] # Staff Channel ID + else: + self.staff_channel = 0 + logger.error(f"API error. API response not ok. -> {setting}") + self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log @commands.Cog.listener() diff --git a/src/bot/cogs/logging/logging_roles.py b/src/bot/cogs/logging/logging_roles.py index 455522b..65cfec6 100644 --- a/src/bot/cogs/logging/logging_roles.py +++ b/src/bot/cogs/logging/logging_roles.py @@ -44,7 +44,7 @@ class LoggingRoles(commands.Cog): def __init__(self, bot): self.bot = bot self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log - + @commands.Cog.listener() async def on_member_update(self, before, after): """ From 37bb9ecd698bb23fba89fad6dd2ce7f0c54a9974 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 00:26:36 -0400 Subject: [PATCH 20/60] swapped check order> --- src/bot/cogs/logging/logging_member_kick.py | 1 + src/bot/cogs/logging/logging_message_edit.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/bot/cogs/logging/logging_member_kick.py b/src/bot/cogs/logging/logging_member_kick.py index 7ac567e..c7ee113 100644 --- a/src/bot/cogs/logging/logging_member_kick.py +++ b/src/bot/cogs/logging/logging_member_kick.py @@ -35,6 +35,7 @@ def __init__(self, bot): self.bot = bot setting = self.bot.api.get_one_role('6') if setting["status"] == "ok": + logger.debug(f"API call successful. Retrieved verification role: {setting}") self.verification_role = setting["roles"][2] # Verification role ID else: self.verification_role = 0 diff --git a/src/bot/cogs/logging/logging_message_edit.py b/src/bot/cogs/logging/logging_message_edit.py index ae26da0..302bf93 100644 --- a/src/bot/cogs/logging/logging_message_edit.py +++ b/src/bot/cogs/logging/logging_message_edit.py @@ -50,13 +50,19 @@ class LoggingMessageEdit(commands.Cog): def __init__(self, bot): self.bot = bot setting = self.bot.api.get_one_setting('3') - if setting["status"] == "ok": - self.staff_channel = setting["setting"][2] # Staff Channel ID - else: - self.staff_channel = 0 + if setting['status'] != 'ok': logger.error(f"API error. API response not ok. -> {setting}") + return + else: + self.staff_channel = setting['setting'][2] + logger.debug(f"LoggingMessageDelete cog initialized. Staff channel ID: {self.staff_channel}") self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log + if self.chat_log['status'] != 'ok': + logger.error(f"API error. API response not ok. -> {self.chat_log}") + return + else: + logger.debug(f"LoggingMessageDelete cog initialized. Chat log setting: {self.chat_log}") @commands.Cog.listener() async def on_message_edit(self, message_before, message_after): From fd5449303cf101f0d5babf599de7ca60aecc1a57 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 00:34:37 -0400 Subject: [PATCH 21/60] Debugging and cleanup of logging --- src/bot/cogs/logging/logging_avatars.py | 2 +- src/bot/cogs/logging/logging_member_ban.py | 3 ++- src/bot/cogs/logging/logging_member_kick.py | 4 ++-- src/bot/cogs/logging/logging_member_leaves.py | 2 ++ src/bot/cogs/logging/logging_message_delete.py | 7 ++----- src/bot/cogs/logging/logging_message_edit.py | 4 +--- src/bot/cogs/logging/logging_name_changes.py | 3 ++- src/bot/cogs/logging/logging_roles.py | 1 + 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/bot/cogs/logging/logging_avatars.py b/src/bot/cogs/logging/logging_avatars.py index b7bf1e8..e41cbcf 100644 --- a/src/bot/cogs/logging/logging_avatars.py +++ b/src/bot/cogs/logging/logging_avatars.py @@ -36,7 +36,7 @@ class LoggingAvatars(commands.Cog): def __init__(self, bot): self.bot = bot self.user_log = self.bot.api.get_one_log_setting("4") # User_log - + logger.info("LoggingAvatars cog initialized") @commands.Cog.listener() async def on_user_update(self, before, after): """ diff --git a/src/bot/cogs/logging/logging_member_ban.py b/src/bot/cogs/logging/logging_member_ban.py index f84e2f6..342e95f 100644 --- a/src/bot/cogs/logging/logging_member_ban.py +++ b/src/bot/cogs/logging/logging_member_ban.py @@ -39,7 +39,8 @@ def __init__(self, bot): logger.error(f"API error. API response not ok. -> {setting}") self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log - + logger.info("LoggingBans cog initialized") + @commands.Cog.listener() async def on_member_remove(self, member): """ diff --git a/src/bot/cogs/logging/logging_member_kick.py b/src/bot/cogs/logging/logging_member_kick.py index c7ee113..af2fddc 100644 --- a/src/bot/cogs/logging/logging_member_kick.py +++ b/src/bot/cogs/logging/logging_member_kick.py @@ -35,7 +35,6 @@ def __init__(self, bot): self.bot = bot setting = self.bot.api.get_one_role('6') if setting["status"] == "ok": - logger.debug(f"API call successful. Retrieved verification role: {setting}") self.verification_role = setting["roles"][2] # Verification role ID else: self.verification_role = 0 @@ -43,7 +42,8 @@ def __init__(self, bot): self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log - + logger.info("LoggingKicks cog initialized") + @commands.Cog.listener() async def on_member_remove(self, member): """ diff --git a/src/bot/cogs/logging/logging_member_leaves.py b/src/bot/cogs/logging/logging_member_leaves.py index 7612b99..c961dee 100644 --- a/src/bot/cogs/logging/logging_member_leaves.py +++ b/src/bot/cogs/logging/logging_member_leaves.py @@ -39,6 +39,8 @@ def __init__(self, bot): return self.join_log = self.bot.api.get_one_log_setting("2") # Join_log + + logger.info("LoggingLeaves cog initialized") @commands.Cog.listener() async def on_member_remove(self, member): diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index 6481fb9..c29f89a 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -49,22 +49,19 @@ class LoggingMessageDelete(commands.Cog): def __init__(self, bot): self.bot = bot - # 'setting': [3, 'Staff Channel', '1506111576946511923'], 'status': 'ok'} setting = self.bot.api.get_one_setting('3') + if setting['status'] != 'ok': logger.error(f"API error. API response not ok. -> {setting}") return else: self.staff_channel = setting['setting'][2] - logger.debug(f"LoggingMessageDelete cog initialized. Staff channel ID: {self.staff_channel}") - #{'logging': [3, 'Chat Log', '1394473942537994363'], 'status': 'ok'} self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log if self.chat_log['status'] != 'ok': logger.error(f"API error. API response not ok. -> {self.chat_log}") return - else: - logger.debug(f"LoggingMessageDelete cog initialized. Chat log setting: {self.chat_log}") + logger.info("LoggingMessageDelete cog loaded successfully.") @commands.Cog.listener() async def on_message_delete(self, message): diff --git a/src/bot/cogs/logging/logging_message_edit.py b/src/bot/cogs/logging/logging_message_edit.py index 302bf93..df0f57c 100644 --- a/src/bot/cogs/logging/logging_message_edit.py +++ b/src/bot/cogs/logging/logging_message_edit.py @@ -55,14 +55,12 @@ def __init__(self, bot): return else: self.staff_channel = setting['setting'][2] - logger.debug(f"LoggingMessageDelete cog initialized. Staff channel ID: {self.staff_channel}") self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log if self.chat_log['status'] != 'ok': logger.error(f"API error. API response not ok. -> {self.chat_log}") return - else: - logger.debug(f"LoggingMessageDelete cog initialized. Chat log setting: {self.chat_log}") + logger.info("LoggingMessageEdit cog initialized") @commands.Cog.listener() async def on_message_edit(self, message_before, message_after): diff --git a/src/bot/cogs/logging/logging_name_changes.py b/src/bot/cogs/logging/logging_name_changes.py index 8b609aa..a5c2e5f 100644 --- a/src/bot/cogs/logging/logging_name_changes.py +++ b/src/bot/cogs/logging/logging_name_changes.py @@ -31,7 +31,8 @@ class LoggingNameChanges(commands.Cog): def __init__(self, bot): self.bot = bot self.user_log = self.bot.api.get_one_log_setting("4") # User_log - + logger.info("LoggingNameChanges cog initialized") + @commands.Cog.listener() async def on_member_update(self, before, after): """ diff --git a/src/bot/cogs/logging/logging_roles.py b/src/bot/cogs/logging/logging_roles.py index 65cfec6..55f9d06 100644 --- a/src/bot/cogs/logging/logging_roles.py +++ b/src/bot/cogs/logging/logging_roles.py @@ -44,6 +44,7 @@ class LoggingRoles(commands.Cog): def __init__(self, bot): self.bot = bot self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log + logger.info("LoggingRoles cog initialized") @commands.Cog.listener() async def on_member_update(self, before, after): From 6698bd39a2d929e389b9495954861c7d0d62548f Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 00:43:48 -0400 Subject: [PATCH 22/60] last removals of [0] --- src/bot/cogs/moderation/admin_purge.py | 8 ++++---- src/bot/cogs/moderation/admin_quarantine.py | 8 ++++---- src/bot/cogs/verification/verification_dropdown.py | 14 +++++++------- src/bot/cogs/verification/verification_on_join.py | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/bot/cogs/moderation/admin_purge.py b/src/bot/cogs/moderation/admin_purge.py index 2599d83..a7840f2 100644 --- a/src/bot/cogs/moderation/admin_purge.py +++ b/src/bot/cogs/moderation/admin_purge.py @@ -28,13 +28,13 @@ def embed_info(message): def api_request_is_ok(request): - if request[0]["status"] == "ok": + if request["status"] == "ok": return True return False def logging_is_activated(request): - if request[0]["logging"][2] == "0": + if request["logging"][2] == "0": return False return True @@ -64,12 +64,12 @@ async def purge_messages(self, interaction: discord.Interaction, amount: int): if api_request_is_ok(self.log_channel_req): logger.info(f"{interaction.user.name} is purging {amount} messages from " - f"the {self.log_channel_req[0]['logging'][1]}") + f"the {self.log_channel_req['logging'][1]}") await interaction.response.defer() await interaction.channel.purge(limit=amount + 1) if logging_is_activated(self.log_channel_req): - logging_channel = await self.bot.fetch_channel(self.log_channel_req[0]["logging"][2]) + logging_channel = await self.bot.fetch_channel(self.log_channel_req["logging"][2]) await logging_channel.send(f"{amount} messages purged" f" from {interaction.channel.mention}" diff --git a/src/bot/cogs/moderation/admin_quarantine.py b/src/bot/cogs/moderation/admin_quarantine.py index 7efabe8..818a36d 100644 --- a/src/bot/cogs/moderation/admin_quarantine.py +++ b/src/bot/cogs/moderation/admin_quarantine.py @@ -67,8 +67,8 @@ class AdminQuarantine(commands.Cog): def __init__(self, bot): self.bot = bot - self.naughty_role = self.bot.api.get_one_role('7')[0]['roles'][2] # quarantine role ID - self.verified_role = self.bot.api.get_one_role('6')[0]['roles'][2] # Verification role ID + self.naughty_role = self.bot.api.get_one_role('7')['roles'][2] # quarantine role ID + self.verified_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log @app_commands.command() @@ -94,7 +94,7 @@ async def quarantine(self, interaction: discord.Interaction, target: discord.Mem if not target.guild_permissions.administrator: message_counter = 0 - mod_log = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + mod_log = await self.bot.fetch_channel(self.mod_log["logging"][2]) verified_role = get(interaction.guild.roles, id=int(self.verified_role)) naughty_role = get(interaction.guild.roles, id=int(self.naughty_role)) @@ -144,7 +144,7 @@ async def release(self, interaction: discord.Interaction, target: discord.Member await interaction.response.defer() logger.info(f"{interaction.user.name} used the release command on {target.name}") if not target.bot: - mod_log = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + mod_log = await self.bot.fetch_channel(self.mod_log["logging"][2]) verified_role = get(interaction.guild.roles, id=int(self.verified_role)) naughty_role = get(interaction.guild.roles, id=int(self.naughty_role)) diff --git a/src/bot/cogs/verification/verification_dropdown.py b/src/bot/cogs/verification/verification_dropdown.py index 16eaa56..010dc15 100644 --- a/src/bot/cogs/verification/verification_dropdown.py +++ b/src/bot/cogs/verification/verification_dropdown.py @@ -31,10 +31,10 @@ def embed_verified_success(name, amount): class VerificationSelector(discord.ui.Select): def __init__(self, bot): self.bot = bot - self.verified_role = self.bot.api.get_one_role('6')[0]['roles'][2] - self.join_log = self.bot.api.get_one_log_setting('2')[0]['logging'][2] - self.verification_log = self.bot.api.get_one_log_setting('1')[0]['logging'][2] - self.verification_channel = self.bot.api.get_one_setting('1')[0]['setting'][2] + self.verified_role = self.bot.api.get_one_role('6')['roles'][2] + self.join_log = self.bot.api.get_one_log_setting('2')['logging'][2] + self.verification_log = self.bot.api.get_one_log_setting('1')['logging'][2] + self.verification_channel = self.bot.api.get_one_setting('1')['setting'][2] self.robot = [discord.SelectOption( label="I'm a robot." @@ -97,9 +97,9 @@ class Verification(commands.Cog): def __init__(self, bot): self.bot = bot # Passed in from main.py - self.join_log = self.bot.api.get_one_log_setting('4')[0]['logging'][2] - self.verification_channel = self.bot.api.get_one_setting('1')[0]['setting'][2] - self.verified_role = self.bot.api.get_one_role('6')[0]['roles'][2] + self.join_log = self.bot.api.get_one_log_setting('4')['logging'][2] + self.verification_channel = self.bot.api.get_one_setting('1')['setting'][2] + self.verified_role = self.bot.api.get_one_role('6')['roles'][2] @commands.command() async def verify(self, ctx): diff --git a/src/bot/cogs/verification/verification_on_join.py b/src/bot/cogs/verification/verification_on_join.py index 2cc9591..a211eb0 100644 --- a/src/bot/cogs/verification/verification_on_join.py +++ b/src/bot/cogs/verification/verification_on_join.py @@ -19,11 +19,11 @@ class LoggingVerification(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_channel = self.bot.api.get_one_setting('1')[0]['setting'][2] - self.verification_log = self.bot.api.get_one_log_setting('1')[0]['logging'][2] - self.join_log = self.bot.api.get_one_log_setting('2')[0]['logging'][2] - self.verified_role = self.bot.api.get_one_role('6')[0]['roles'][2] - self.naughty_role = self.bot.api.get_one_role('7')[0]['roles'][2] + self.verification_channel = self.bot.api.get_one_setting('1')['setting'][2] + self.verification_log = self.bot.api.get_one_log_setting('1')['logging'][2] + self.join_log = self.bot.api.get_one_log_setting('2')['logging'][2] + self.verified_role = self.bot.api.get_one_role('6')['roles'][2] + self.naughty_role = self.bot.api.get_one_role('7')['roles'][2] async def log_unverified_join(self, member, logging_channel): await logging_channel.send(f"<@{member.id}> joined, but has not verified.") From a59447fb9bbad0e85fbef8733b6b772db706c319 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 00:50:10 -0400 Subject: [PATCH 23/60] Fix Setttings from [0] plague --- src/bot/cogs/admin/settings.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bot/cogs/admin/settings.py b/src/bot/cogs/admin/settings.py index 7a572e8..8e2e633 100644 --- a/src/bot/cogs/admin/settings.py +++ b/src/bot/cogs/admin/settings.py @@ -35,10 +35,10 @@ async def settings(self, ctx: commands.Context): server_settings = self.bot.api.get_all_settings() log_settings = self.bot.api.get_all_log_settings() try: - if server_settings[0]["status"] != "ok": + if server_settings["status"] != "ok": await ctx.send(f"Failed to retrieve settings: {server_settings['message']}") return - if log_settings[0]["status"] != "ok": + if log_settings["status"] != "ok": await ctx.send(f"Failed to retrieve settings: {log_settings['message']}") return except Exception as e: @@ -51,13 +51,13 @@ async def settings(self, ctx: commands.Context): colour=0x000000, timestamp=datetime.datetime.now()) - for setting in server_settings[0]["setting"]: + for setting in server_settings["setting"]: value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' embed.add_field(name="" , value=f"**{setting[1]}**:{value}" , inline=False) - for setting in log_settings[0]["logging"]: + for setting in log_settings["logging"]: value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' embed.add_field(name=f"" , value=f"**{setting[1]}**:{value}" @@ -82,9 +82,9 @@ async def update_settings(self, ctx: commands.Context): server_settings = self.bot.api.get_all_settings() # Pull the names out of the returned JSON - logging_types = [item for item in channel_settings[0]['logging']] - role_types = [role for role in role_settings[0]['roles']] - setting_types = [setting for setting in server_settings[0]['setting']] + logging_types = [item for item in channel_settings['logging']] + role_types = [role for role in role_settings['roles']] + setting_types = [setting for setting in server_settings['setting']] # Get the names of the available channels and roles to map channels = [channel for channel in ctx.guild.text_channels if "log" in channel.name] From 9b1e36a5dac1961508dc66e3bb070cfd53ae0526 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 01:19:55 -0400 Subject: [PATCH 24/60] Adjustments to the Fixes --- src/api/core/db_helper.py | 3 ++- src/bot/cogs/logging/logging_avatars.py | 5 +++++ src/bot/cogs/logging/logging_member_ban.py | 4 ++-- src/bot/cogs/logging/logging_member_kick.py | 5 +++-- src/bot/cogs/logging/logging_member_leaves.py | 6 +++--- src/bot/cogs/logging/logging_message_delete.py | 7 ++----- src/bot/cogs/logging/logging_message_edit.py | 6 ++---- src/bot/cogs/logging/logging_name_changes.py | 2 ++ src/bot/cogs/logging/logging_roles.py | 2 ++ src/bot/cogs/moderation/admin_quarantine.py | 5 ++++- 10 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index c7e0f03..1d1b10a 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -126,6 +126,7 @@ def get_setting(self, setting_id): result = self.cursor.fetchone() return {"status": "ok", "setting": result} except OperationalError as err: + logger.error(f"Error fetching setting: {err}") return {"status": "error", "message": str(err)} def get_settings(self): @@ -135,7 +136,7 @@ def get_settings(self): result = self.cursor.fetchall() return {"status": "ok", "setting": result} except OperationalError as err: - logger.error(f"Error fetching setting: {err}") + logger.error(f"Error fetching settings: {err}") return {"status": "error", "message": str(err)} diff --git a/src/bot/cogs/logging/logging_avatars.py b/src/bot/cogs/logging/logging_avatars.py index e41cbcf..450910f 100644 --- a/src/bot/cogs/logging/logging_avatars.py +++ b/src/bot/cogs/logging/logging_avatars.py @@ -36,7 +36,12 @@ class LoggingAvatars(commands.Cog): def __init__(self, bot): self.bot = bot self.user_log = self.bot.api.get_one_log_setting("4") # User_log + if self.user_log['status'] != 'ok': + raise RuntimeError("Failed to fetch user log settings from API.") + logger.info("LoggingAvatars cog initialized") + + @commands.Cog.listener() async def on_user_update(self, before, after): """ diff --git a/src/bot/cogs/logging/logging_member_ban.py b/src/bot/cogs/logging/logging_member_ban.py index 342e95f..6bfebe5 100644 --- a/src/bot/cogs/logging/logging_member_ban.py +++ b/src/bot/cogs/logging/logging_member_ban.py @@ -33,10 +33,10 @@ def __init__(self, bot): self.bot = bot setting = self.bot.api.get_one_role('6') if setting["status"] == "ok": - self.verification_role = setting["roles"][2] # Verification role ID + self.verification_role = int(setting["roles"][2]) # Verification role ID else: self.verification_role = 0 - logger.error(f"API error. API response not ok. -> {setting}") + raise RuntimeError("Failed to fetch verification role from API.") self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log logger.info("LoggingBans cog initialized") diff --git a/src/bot/cogs/logging/logging_member_kick.py b/src/bot/cogs/logging/logging_member_kick.py index af2fddc..2a95f4d 100644 --- a/src/bot/cogs/logging/logging_member_kick.py +++ b/src/bot/cogs/logging/logging_member_kick.py @@ -35,10 +35,11 @@ def __init__(self, bot): self.bot = bot setting = self.bot.api.get_one_role('6') if setting["status"] == "ok": - self.verification_role = setting["roles"][2] # Verification role ID + self.verification_role = int(setting["roles"][2]) # Verification role ID else: self.verification_role = 0 - logger.error(f"API error. API response not ok. -> {setting}") + raise RuntimeError("Failed to fetch verification role from API.") + self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log diff --git a/src/bot/cogs/logging/logging_member_leaves.py b/src/bot/cogs/logging/logging_member_leaves.py index c961dee..2a35e85 100644 --- a/src/bot/cogs/logging/logging_member_leaves.py +++ b/src/bot/cogs/logging/logging_member_leaves.py @@ -32,11 +32,11 @@ def __init__(self, bot): self.bot = bot setting = self.bot.api.get_one_role('6') if setting["status"] == "ok": - self.verification_role = setting["roles"][2] # Verification role ID + self.verification_role = int(setting["roles"][2]) # Verification role ID else: self.verification_role = 0 - logger.error(f"API error. API response not ok. -> {setting}") - return + # exits the init of the cog and also removes it from the bot so there are no conflicts with failure + raise RuntimeError("Failed to fetch verification role from API.") self.join_log = self.bot.api.get_one_log_setting("2") # Join_log diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index c29f89a..4e35390 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -52,16 +52,13 @@ def __init__(self, bot): setting = self.bot.api.get_one_setting('3') if setting['status'] != 'ok': - logger.error(f"API error. API response not ok. -> {setting}") - return + raise RuntimeError("Failed to fetch staff channel setting from API.") else: self.staff_channel = setting['setting'][2] self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log if self.chat_log['status'] != 'ok': - logger.error(f"API error. API response not ok. -> {self.chat_log}") - return - logger.info("LoggingMessageDelete cog loaded successfully.") + raise RuntimeError("Failed to fetch chat log settings from API.") @commands.Cog.listener() async def on_message_delete(self, message): diff --git a/src/bot/cogs/logging/logging_message_edit.py b/src/bot/cogs/logging/logging_message_edit.py index df0f57c..6af1642 100644 --- a/src/bot/cogs/logging/logging_message_edit.py +++ b/src/bot/cogs/logging/logging_message_edit.py @@ -51,15 +51,13 @@ def __init__(self, bot): self.bot = bot setting = self.bot.api.get_one_setting('3') if setting['status'] != 'ok': - logger.error(f"API error. API response not ok. -> {setting}") - return + raise RuntimeError("Failed to fetch staff channel setting from API.") else: self.staff_channel = setting['setting'][2] self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log if self.chat_log['status'] != 'ok': - logger.error(f"API error. API response not ok. -> {self.chat_log}") - return + raise RuntimeError("Failed to fetch chat log settings from API.") logger.info("LoggingMessageEdit cog initialized") @commands.Cog.listener() diff --git a/src/bot/cogs/logging/logging_name_changes.py b/src/bot/cogs/logging/logging_name_changes.py index a5c2e5f..d5c0617 100644 --- a/src/bot/cogs/logging/logging_name_changes.py +++ b/src/bot/cogs/logging/logging_name_changes.py @@ -31,6 +31,8 @@ class LoggingNameChanges(commands.Cog): def __init__(self, bot): self.bot = bot self.user_log = self.bot.api.get_one_log_setting("4") # User_log + if self.user_log['status'] != 'ok': + raise RuntimeError("Failed to fetch user log settings from API.") logger.info("LoggingNameChanges cog initialized") @commands.Cog.listener() diff --git a/src/bot/cogs/logging/logging_roles.py b/src/bot/cogs/logging/logging_roles.py index 55f9d06..36f6980 100644 --- a/src/bot/cogs/logging/logging_roles.py +++ b/src/bot/cogs/logging/logging_roles.py @@ -44,6 +44,8 @@ class LoggingRoles(commands.Cog): def __init__(self, bot): self.bot = bot self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log + if self.mod_log['status'] != 'ok': + raise RuntimeError("Failed to fetch mod log settings from API.") logger.info("LoggingRoles cog initialized") @commands.Cog.listener() diff --git a/src/bot/cogs/moderation/admin_quarantine.py b/src/bot/cogs/moderation/admin_quarantine.py index 818a36d..1df121e 100644 --- a/src/bot/cogs/moderation/admin_quarantine.py +++ b/src/bot/cogs/moderation/admin_quarantine.py @@ -70,7 +70,10 @@ def __init__(self, bot): self.naughty_role = self.bot.api.get_one_role('7')['roles'][2] # quarantine role ID self.verified_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log - + if self.mod_log['status'] != 'ok' or self.naughty_role['status'] != 'ok' or self.verified_role['status'] != 'ok': + raise RuntimeError("Failed to fetch mod log settings from API.") + + @app_commands.command() @is_moderator() @is_master_guild() From 510cf9f9662e9e381d7cfd3509779dc0d24149b2 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Mon, 18 May 2026 17:03:30 -0400 Subject: [PATCH 25/60] Fix response formatting and update setting retrieval Fix Setttings from [0] as well as some debugging --- .gitignore | 13 +++- src/api/core/db_helper.py | 31 +++++++- src/api/routes/healthchecks.py | 6 +- src/api/routes/logging.py | 12 +-- src/api/routes/roles.py | 14 ++-- src/api/routes/settings.py | 19 +++-- src/bot/cogs/admin/points.py | 32 ++++---- src/bot/cogs/admin/settings.py | 78 ++++++++++--------- src/bot/cogs/logging/logging_avatars.py | 11 ++- src/bot/cogs/logging/logging_member_ban.py | 19 +++-- src/bot/cogs/logging/logging_member_kick.py | 19 +++-- src/bot/cogs/logging/logging_member_leaves.py | 17 +++- .../cogs/logging/logging_message_delete.py | 16 +++- src/bot/cogs/logging/logging_message_edit.py | 16 +++- src/bot/cogs/logging/logging_name_changes.py | 11 ++- src/bot/cogs/logging/logging_roles.py | 11 ++- src/bot/cogs/moderation/admin_purge.py | 8 +- src/bot/cogs/moderation/admin_quarantine.py | 13 ++-- .../verification/verification_dropdown.py | 14 ++-- .../cogs/verification/verification_on_join.py | 10 +-- src/docker-compose.yml | 3 + 21 files changed, 231 insertions(+), 142 deletions(-) diff --git a/.gitignore b/.gitignore index 6233bb3..b115186 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,11 @@ .env /src/db/postgres-data/ +/src/*docker +src/docker-compose.yml + +scratchpad/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -131,10 +136,7 @@ venv/ ENV/ env.bak/ venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject +.vscode/ # Rope project settings .ropeproject @@ -157,3 +159,6 @@ dmypy.json cython_debug/ /.obsidian/ +src/docker-compose.yml +src/docker-compose.yml +src/docker-compose.yml diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index d1a47d1..1d1b10a 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -46,12 +46,23 @@ def get_log_setting(self, setting_id): logger.error(f"Error fetching logging: {err}") return {"status": "error", "message": str(err)} + # { + # 'status': 'ok', + # 'setting': [ + # (3, 'Staff Channel', '1506111576946511923'), + # (1, 'Verification Channel', '1506114864828121158'), + # (4, 'Bot Spam Channel', '1506114864828121158'), + # (2, 'Quarantine Channel', '1506114864828121158') + # ] + # } def get_log_settings(self): logger.debug("API attempting to contact DB for get_log_settings...") try: self.cursor.execute("SELECT * FROM logging") # case sensitive result = self.cursor.fetchall() - return {"status": "ok", "logging": result} + response = {"status": "ok", "logging": result} + logger.debug(f"DB Response:\n{response}") + return response except OperationalError as err: logger.error(f"Error fetching logging: {err}") return {"status": "error", "message": str(err)} @@ -61,7 +72,9 @@ def get_logging(self): try: self.cursor.execute("SELECT * FROM logging") result = self.cursor.fetchall() - return {"status": "ok", "logging": result} + response = {"status": "ok", "logging": result} + logger.debug(f"DB Response:\n{response}") + return response except OperationalError as err: logger.error(f"Error fetching logging: {err}") return {"status": "error", "message": str(err)} @@ -96,6 +109,16 @@ def delete_log_setting(self, log_id): ################## ## Settings ## ################## + + # {'status': 'ok', + # 'setting': [ + # (3, 'Staff Channel', '1506111576946511923'), + # (1, 'Verification Channel', '1506114864828121158'), + # (4, 'Bot Spam Channel', '1506114864828121158'), + # (2, 'Quarantine Channel', '1506114864828121158') + # ] + # } + def get_setting(self, setting_id): logger.debug("API attempting to contact DB for get_setting...") try: @@ -103,7 +126,7 @@ def get_setting(self, setting_id): result = self.cursor.fetchone() return {"status": "ok", "setting": result} except OperationalError as err: - logger.error(f"Error fetching logging: {err}") + logger.error(f"Error fetching setting: {err}") return {"status": "error", "message": str(err)} def get_settings(self): @@ -113,7 +136,7 @@ def get_settings(self): result = self.cursor.fetchall() return {"status": "ok", "setting": result} except OperationalError as err: - logger.error(f"Error fetching setting: {err}") + logger.error(f"Error fetching settings: {err}") return {"status": "error", "message": str(err)} diff --git a/src/api/routes/healthchecks.py b/src/api/routes/healthchecks.py index 34448d8..9d6d12d 100644 --- a/src/api/routes/healthchecks.py +++ b/src/api/routes/healthchecks.py @@ -19,7 +19,7 @@ def api_health_check(): if request.method == 'GET': return jsonify({'status': 'ok'}, 200) - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @health_checks.route('/hc_db', methods=['GET']) @@ -33,6 +33,6 @@ def database_health_check(): return jsonify(hc, 200) except TypeError as ded: - return jsonify({"status": "unhealthy", "error": "DB unreachable"}, 404) + return jsonify({"status": "unhealthy", "error": "DB unreachable"}), 404 - return jsonify({'message': 'improper request method'}, 404) + return jsonify({'message': 'improper request method'}), 404 diff --git a/src/api/routes/logging.py b/src/api/routes/logging.py index 6d26100..8eeed05 100644 --- a/src/api/routes/logging.py +++ b/src/api/routes/logging.py @@ -25,7 +25,7 @@ def get_log_setting(log_id=None): # Retrieve a single setting result = eos.db.get_log_setting(log_id) - return jsonify(result, 200) + return jsonify(result), 200 # @settings.route('/log_settings', methods=['GET']) # def get_log_settings(): @@ -46,9 +46,9 @@ def update_log_setting(log_id): if request.method == 'PUT': data = request.json result = eos.db.update_logging(int(log_id), data['value']) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @logs.route('/logging', methods=['POST']) def add_log_setting(): @@ -60,7 +60,7 @@ def add_log_setting(): result = eos.db.add_log_setting(data['name'], data['value']) return jsonify(result, 201) - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @logs.route('/logging/', methods=['DELETE']) def delete_log_setting(log_id): @@ -69,6 +69,6 @@ def delete_log_setting(log_id): """ if request.method == 'DELETE': result = eos.db.delete_log_setting(log_id) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 diff --git a/src/api/routes/roles.py b/src/api/routes/roles.py index 08b6752..ac37470 100644 --- a/src/api/routes/roles.py +++ b/src/api/routes/roles.py @@ -25,7 +25,7 @@ def get_role(role_id=None): # Retrieve a single role result = eos.db.get_role(role_id) - return jsonify(result, 200) + return jsonify(result), 200 @role.route('/role/', methods=['PUT']) def update_role(role_id): @@ -35,9 +35,9 @@ def update_role(role_id): if request.method == 'PUT': data = request.json result = eos.db.update_role(int(role_id), data['value']) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @role.route('/role', methods=['POST']) def add_role(): @@ -47,9 +47,9 @@ def add_role(): if request.method == 'POST': data = request.json result = eos.db.add_role(data['name'], data['value']) - return jsonify(result, 201) + return jsonify(result), 201 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @role.route('/role/', methods=['DELETE']) def delete_role(role_id): @@ -58,6 +58,6 @@ def delete_role(role_id): """ if request.method == 'DELETE': result = eos.db.delete_role(role_id) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 diff --git a/src/api/routes/settings.py b/src/api/routes/settings.py index db8c2c8..e188017 100644 --- a/src/api/routes/settings.py +++ b/src/api/routes/settings.py @@ -11,21 +11,20 @@ @settings.route('/settings', methods=['GET']) @settings.route('/settings/', methods=['GET']) -def get_setting(setting_id): +def get_setting(setting_id=None): """ Retrieve settings from the database. :param setting_id: Optional integer ID of a specific setting :return: JSON response with setting """ - if setting_id == 0: - # Retrieve all settings + if setting_id is None or setting_id == 0: # Retrieve all settings result = eos.db.get_settings() else: # Retrieve a single setting result = eos.db.get_setting(setting_id) - return jsonify(result, 200) + return jsonify(result), 200 @settings.route('/settings/', methods=['PUT']) @@ -36,9 +35,9 @@ def update_setting(setting_id): if request.method == 'PUT': data = request.json result = eos.db.update_setting(int(setting_id), data['value']) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @settings.route('/settings', methods=['POST']) def add_setting(): @@ -48,9 +47,9 @@ def add_setting(): if request.method == 'POST': data = request.json result = eos.db.add_setting(data['name'], data['value']) - return jsonify(result, 201) + return jsonify(result), 201 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 @settings.route('/settings/', methods=['DELETE']) def delete_setting(setting_id): @@ -59,6 +58,6 @@ def delete_setting(setting_id): """ if request.method == 'DELETE': result = eos.db.delete_setting(setting_id) - return jsonify(result, 200) + return jsonify(result), 200 - return jsonify({'message': 'improper request method'}, 405) + return jsonify({'message': 'improper request method'}), 405 diff --git a/src/bot/cogs/admin/points.py b/src/bot/cogs/admin/points.py index 99e312e..feadeb1 100644 --- a/src/bot/cogs/admin/points.py +++ b/src/bot/cogs/admin/points.py @@ -48,8 +48,8 @@ async def sync_users(self, ctx: commands.Context) -> None: Users that exist in the DB will not be synced. """ added = 0 - for user in ctx.guild.members: - self.bot.api.add_user_to_points(user.id) + for user in ctx.guild.members: # type: ignore + self.bot.api.add_user_to_points(user.id) # type: ignore added += 1 await ctx.reply( embed=embed_info( @@ -101,9 +101,11 @@ async def update_points(self, ctx: commands.Context, user: discord.Member, amoun if update_points['status'] == 'ok': await ctx.reply( embed=embed_info( - "" - , f"{amount.lstrip('-+')} points {'removed from' if amount.startswith('-') else 'added to'} {user.display_name}" - , discord.Color.green() if not amount.startswith('-') else discord.Color.red() + "", + f"{abs(amount)} points " + f"{'removed from' if amount < 0 else 'added to'} " + f"{user.display_name}", + discord.Color.red() if amount < 0 else discord.Color.green() ) ) else: @@ -181,12 +183,10 @@ async def on_command_error(self, ctx: commands.Context, error): Error handling for the >update_points command. """ if isinstance(error, commands.MissingRequiredArgument): - await ctx.reply( - embed=embed_info( - "Error!", "You must provide a required argument." - , discord.Color.dark_gray() - ) - ) + await ctx.reply(embed=embed_info("Error!", "You must provide a required argument.", discord.Color.dark_gray())) + elif isinstance(error, commands.MissingPermissions): + logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") + await ctx.send('For one reason, or another, YOU cannot use this command.') @sync_users.error async def sync_users_command_error(self, ctx, error): @@ -194,11 +194,11 @@ async def sync_users_command_error(self, ctx, error): logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") await ctx.send('For one reason, or another, YOU cannot use this command.') - @update_points.error - async def update_points_command_error(self, ctx, error): - if isinstance(error, commands.CheckFailure): - logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") - await ctx.send('For one reason, or another, YOU cannot use this command.') + #@update_points.error + #async def update_points_command_error(self, ctx, error): + # if isinstance(error, commands.CheckFailure): + # logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") + # await ctx.send('For one reason, or another, YOU cannot use this command.') async def setup(bot: commands.Bot) -> None: diff --git a/src/bot/cogs/admin/settings.py b/src/bot/cogs/admin/settings.py index b0c5a16..8e2e633 100644 --- a/src/bot/cogs/admin/settings.py +++ b/src/bot/cogs/admin/settings.py @@ -28,42 +28,46 @@ def __init__(self, bot: commands.Bot) -> None: @is_admin() @commands.hybrid_command() async def settings(self, ctx: commands.Context): - """ - List all available settings. - """ - - server_settings = self.bot.api.get_all_settings() - log_settings = self.bot.api.get_all_log_settings() - - if server_settings[0]["status"] != "ok": - await ctx.send(f"Failed to retrieve settings: {server_settings['message']}") - return - if log_settings[0]["status"] != "ok": - await ctx.send(f"Failed to retrieve settings: {log_settings['message']}") - return - - embed = discord.Embed(title="-- Settings --", - description="Here, you can see the current settings for the server.", - colour=0x000000, - timestamp=datetime.datetime.now()) - - for setting in server_settings[0]["setting"]: - value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' - embed.add_field(name="" - , value=f"**{setting[1]}**:{value}" - , inline=False) - - for setting in log_settings[0]["logging"]: - value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' - embed.add_field(name=f"" - , value=f"**{setting[1]}**:{value}" - , inline=False) - - - embed.set_footer(text=ctx.guild.name, + """ + List all available settings. + """ + + server_settings = self.bot.api.get_all_settings() + log_settings = self.bot.api.get_all_log_settings() + try: + if server_settings["status"] != "ok": + await ctx.send(f"Failed to retrieve settings: {server_settings['message']}") + return + if log_settings["status"] != "ok": + await ctx.send(f"Failed to retrieve settings: {log_settings['message']}") + return + except Exception as e: + logger.error(f"Error retrieving settings: {e}") + await ctx.send("An error occurred while retrieving settings.") + return + + embed = discord.Embed(title="-- Settings --", + description="Here, you can see the current settings for the server.", + colour=0x000000, + timestamp=datetime.datetime.now()) + + for setting in server_settings["setting"]: + value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' + embed.add_field(name="" + , value=f"**{setting[1]}**:{value}" + , inline=False) + + for setting in log_settings["logging"]: + value = f"<#{setting[2]}>" if setting[2] != '0' else 'Off' + embed.add_field(name=f"" + , value=f"**{setting[1]}**:{value}" + , inline=False) + + + embed.set_footer(text=ctx.guild.name, icon_url=ctx.guild.icon) - await ctx.send(embed=embed) + await ctx.send(embed=embed) @is_master_guild() @is_admin() @@ -78,9 +82,9 @@ async def update_settings(self, ctx: commands.Context): server_settings = self.bot.api.get_all_settings() # Pull the names out of the returned JSON - logging_types = [item for item in channel_settings[0]['logging']] - role_types = [role for role in role_settings[0]['roles']] - setting_types = [setting for setting in server_settings[0]['setting']] + logging_types = [item for item in channel_settings['logging']] + role_types = [role for role in role_settings['roles']] + setting_types = [setting for setting in server_settings['setting']] # Get the names of the available channels and roles to map channels = [channel for channel in ctx.guild.text_channels if "log" in channel.name] diff --git a/src/bot/cogs/logging/logging_avatars.py b/src/bot/cogs/logging/logging_avatars.py index d1d2f26..450910f 100644 --- a/src/bot/cogs/logging/logging_avatars.py +++ b/src/bot/cogs/logging/logging_avatars.py @@ -36,7 +36,12 @@ class LoggingAvatars(commands.Cog): def __init__(self, bot): self.bot = bot self.user_log = self.bot.api.get_one_log_setting("4") # User_log + if self.user_log['status'] != 'ok': + raise RuntimeError("Failed to fetch user log settings from API.") + logger.info("LoggingAvatars cog initialized") + + @commands.Cog.listener() async def on_user_update(self, before, after): """ @@ -49,11 +54,11 @@ async def on_user_update(self, before, after): # return if before.avatar != after.avatar: - if self.user_log[0]["status"] == "ok": - if self.user_log[0]["logging"][2] == "0": + if self.user_log["status"] == "ok": + if self.user_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.user_log}") return - logs_channel = await self.bot.fetch_channel(self.user_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.user_log["logging"][2]) embed = embed_avatar(before, after) diff --git a/src/bot/cogs/logging/logging_member_ban.py b/src/bot/cogs/logging/logging_member_ban.py index 89a366c..6bfebe5 100644 --- a/src/bot/cogs/logging/logging_member_ban.py +++ b/src/bot/cogs/logging/logging_member_ban.py @@ -31,9 +31,16 @@ class LoggingBans(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_role = self.bot.api.get_one_role('6')[0]['roles'][2] # Verification role ID + setting = self.bot.api.get_one_role('6') + if setting["status"] == "ok": + self.verification_role = int(setting["roles"][2]) # Verification role ID + else: + self.verification_role = 0 + raise RuntimeError("Failed to fetch verification role from API.") + self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log - + logger.info("LoggingBans cog initialized") + @commands.Cog.listener() async def on_member_remove(self, member): """ @@ -50,11 +57,11 @@ async def on_member_remove(self, member): audit_log = [entry async for entry in member.guild.audit_logs(limit=1)][0] - if self.mod_log[0]["status"] == "ok": - if self.mod_log[0]["logging"][2] == "0": - logger.debug(f"log was triggered, but logging is disabled. API: {self.join_log}") + if self.mod_log["status"] == "ok": + if self.mod_log["logging"][2] == "0": + logger.debug(f"log was triggered, but logging is disabled. API: {self.mod_log}") return - logs_channel = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.mod_log["logging"][2]) if str(audit_log.action) == "AuditLogAction.ban": if audit_log.target == member: diff --git a/src/bot/cogs/logging/logging_member_kick.py b/src/bot/cogs/logging/logging_member_kick.py index 05309ef..2a95f4d 100644 --- a/src/bot/cogs/logging/logging_member_kick.py +++ b/src/bot/cogs/logging/logging_member_kick.py @@ -33,9 +33,18 @@ class LoggingKicks(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_role = self.bot.api.get_one_role('6')[0]['roles'][2] # Verification role ID - self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log + setting = self.bot.api.get_one_role('6') + if setting["status"] == "ok": + self.verification_role = int(setting["roles"][2]) # Verification role ID + else: + self.verification_role = 0 + raise RuntimeError("Failed to fetch verification role from API.") + + + self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log + logger.info("LoggingKicks cog initialized") + @commands.Cog.listener() async def on_member_remove(self, member): """ @@ -52,11 +61,11 @@ async def on_member_remove(self, member): audit_log = [entry async for entry in member.guild.audit_logs(limit=1)][0] - if self.mod_log[0]["status"] == "ok": - if self.mod_log[0]["logging"][2] == "0": + if self.mod_log["status"] == "ok": + if self.mod_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.mod_log}") return - logs_channel = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.mod_log["logging"][2]) if str(audit_log.action) == "AuditLogAction.kick": if audit_log.target == member: diff --git a/src/bot/cogs/logging/logging_member_leaves.py b/src/bot/cogs/logging/logging_member_leaves.py index 1fa68e9..2a35e85 100644 --- a/src/bot/cogs/logging/logging_member_leaves.py +++ b/src/bot/cogs/logging/logging_member_leaves.py @@ -30,8 +30,17 @@ class LoggingLeaves(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_role = self.bot.api.get_one_role('6')[0]['roles'][2] # Verification role ID + setting = self.bot.api.get_one_role('6') + if setting["status"] == "ok": + self.verification_role = int(setting["roles"][2]) # Verification role ID + else: + self.verification_role = 0 + # exits the init of the cog and also removes it from the bot so there are no conflicts with failure + raise RuntimeError("Failed to fetch verification role from API.") + self.join_log = self.bot.api.get_one_log_setting("2") # Join_log + + logger.info("LoggingLeaves cog initialized") @commands.Cog.listener() async def on_member_remove(self, member): @@ -47,11 +56,11 @@ async def on_member_remove(self, member): if self.verification_role in [role.id for role in member.roles]: return - if self.join_log[0]["status"] == "ok": - if self.join_log[0]["logging"][2] == "0": + if self.join_log["status"] == "ok": + if self.join_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.join_log}") return - logs_channel = await self.bot.fetch_channel(self.join_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.join_log["logging"][2]) audit_log = [entry async for entry in member.guild.audit_logs(limit=1)][0] diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index cd6520e..4e35390 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -49,8 +49,16 @@ class LoggingMessageDelete(commands.Cog): def __init__(self, bot): self.bot = bot - self.staff_channel = self.bot.api.get_one_setting('3')[0]['setting'][2] # Staff Channel ID + setting = self.bot.api.get_one_setting('3') + + if setting['status'] != 'ok': + raise RuntimeError("Failed to fetch staff channel setting from API.") + else: + self.staff_channel = setting['setting'][2] + self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log + if self.chat_log['status'] != 'ok': + raise RuntimeError("Failed to fetch chat log settings from API.") @commands.Cog.listener() async def on_message_delete(self, message): @@ -67,11 +75,11 @@ async def on_message_delete(self, message): return audit_log = [entry async for entry in message.guild.audit_logs(limit=1)][0] - if self.chat_log[0]["status"] == "ok": - if self.chat_log[0]["logging"][2] == "0": + if self.chat_log["status"] == "ok": + if self.chat_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.chat_log}") return - logs_channel = await self.bot.fetch_channel(self.chat_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.chat_log["logging"][2]) if str(audit_log.action) == 'AuditLogAction.message_delete': # Then a moderator deleted a message. diff --git a/src/bot/cogs/logging/logging_message_edit.py b/src/bot/cogs/logging/logging_message_edit.py index 5b7cdbe..6af1642 100644 --- a/src/bot/cogs/logging/logging_message_edit.py +++ b/src/bot/cogs/logging/logging_message_edit.py @@ -49,8 +49,16 @@ class LoggingMessageEdit(commands.Cog): def __init__(self, bot): self.bot = bot - self.staff_channel = self.bot.api.get_one_setting('3')[0]['setting'][2] # Staff Channel ID + setting = self.bot.api.get_one_setting('3') + if setting['status'] != 'ok': + raise RuntimeError("Failed to fetch staff channel setting from API.") + else: + self.staff_channel = setting['setting'][2] + self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log + if self.chat_log['status'] != 'ok': + raise RuntimeError("Failed to fetch chat log settings from API.") + logger.info("LoggingMessageEdit cog initialized") @commands.Cog.listener() async def on_message_edit(self, message_before, message_after): @@ -71,12 +79,12 @@ async def on_message_edit(self, message_before, message_after): return elif message_before.content != message_after.content: - if self.chat_log[0]["status"] == "ok": - if self.chat_log[0]["logging"][2] == "0": + if self.chat_log["status"] == "ok": + if self.chat_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.chat_log}") return - logs_channel = await self.bot.fetch_channel(self.chat_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.chat_log["logging"][2]) # This guy here makes sure we use the displayed name inside the guild. if message_after.author.nick is None: diff --git a/src/bot/cogs/logging/logging_name_changes.py b/src/bot/cogs/logging/logging_name_changes.py index 76efc6b..d5c0617 100644 --- a/src/bot/cogs/logging/logging_name_changes.py +++ b/src/bot/cogs/logging/logging_name_changes.py @@ -31,7 +31,10 @@ class LoggingNameChanges(commands.Cog): def __init__(self, bot): self.bot = bot self.user_log = self.bot.api.get_one_log_setting("4") # User_log - + if self.user_log['status'] != 'ok': + raise RuntimeError("Failed to fetch user log settings from API.") + logger.info("LoggingNameChanges cog initialized") + @commands.Cog.listener() async def on_member_update(self, before, after): """ @@ -54,11 +57,11 @@ async def on_member_update(self, before, after): username_after = after.nick if before.nick != after.nick and before.nick is not None: - if self.user_log[0]["status"] == "ok": - if self.user_log[0]["logging"][2] == "0": + if self.user_log["status"] == "ok": + if self.user_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.user_log}") return - logs_channel = await self.bot.fetch_channel(self.user_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.user_log["logging"][2]) embed = embed_name_change(username_before, username_after) diff --git a/src/bot/cogs/logging/logging_roles.py b/src/bot/cogs/logging/logging_roles.py index 52c01e9..36f6980 100644 --- a/src/bot/cogs/logging/logging_roles.py +++ b/src/bot/cogs/logging/logging_roles.py @@ -44,7 +44,10 @@ class LoggingRoles(commands.Cog): def __init__(self, bot): self.bot = bot self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log - + if self.mod_log['status'] != 'ok': + raise RuntimeError("Failed to fetch mod log settings from API.") + logger.info("LoggingRoles cog initialized") + @commands.Cog.listener() async def on_member_update(self, before, after): """ @@ -63,11 +66,11 @@ async def on_member_update(self, before, after): responsible_member = audit_log.user changed_roles = [] - if self.mod_log[0]["status"] == "ok": - if self.mod_log[0]["logging"][2] == "0": + if self.mod_log["status"] == "ok": + if self.mod_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.mod_log}") return - logs_channel = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + logs_channel = await self.bot.fetch_channel(self.mod_log["logging"][2]) if len(before.roles) > len(after.roles): for role in before.roles: diff --git a/src/bot/cogs/moderation/admin_purge.py b/src/bot/cogs/moderation/admin_purge.py index 2599d83..a7840f2 100644 --- a/src/bot/cogs/moderation/admin_purge.py +++ b/src/bot/cogs/moderation/admin_purge.py @@ -28,13 +28,13 @@ def embed_info(message): def api_request_is_ok(request): - if request[0]["status"] == "ok": + if request["status"] == "ok": return True return False def logging_is_activated(request): - if request[0]["logging"][2] == "0": + if request["logging"][2] == "0": return False return True @@ -64,12 +64,12 @@ async def purge_messages(self, interaction: discord.Interaction, amount: int): if api_request_is_ok(self.log_channel_req): logger.info(f"{interaction.user.name} is purging {amount} messages from " - f"the {self.log_channel_req[0]['logging'][1]}") + f"the {self.log_channel_req['logging'][1]}") await interaction.response.defer() await interaction.channel.purge(limit=amount + 1) if logging_is_activated(self.log_channel_req): - logging_channel = await self.bot.fetch_channel(self.log_channel_req[0]["logging"][2]) + logging_channel = await self.bot.fetch_channel(self.log_channel_req["logging"][2]) await logging_channel.send(f"{amount} messages purged" f" from {interaction.channel.mention}" diff --git a/src/bot/cogs/moderation/admin_quarantine.py b/src/bot/cogs/moderation/admin_quarantine.py index 7efabe8..1df121e 100644 --- a/src/bot/cogs/moderation/admin_quarantine.py +++ b/src/bot/cogs/moderation/admin_quarantine.py @@ -67,10 +67,13 @@ class AdminQuarantine(commands.Cog): def __init__(self, bot): self.bot = bot - self.naughty_role = self.bot.api.get_one_role('7')[0]['roles'][2] # quarantine role ID - self.verified_role = self.bot.api.get_one_role('6')[0]['roles'][2] # Verification role ID + self.naughty_role = self.bot.api.get_one_role('7')['roles'][2] # quarantine role ID + self.verified_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log - + if self.mod_log['status'] != 'ok' or self.naughty_role['status'] != 'ok' or self.verified_role['status'] != 'ok': + raise RuntimeError("Failed to fetch mod log settings from API.") + + @app_commands.command() @is_moderator() @is_master_guild() @@ -94,7 +97,7 @@ async def quarantine(self, interaction: discord.Interaction, target: discord.Mem if not target.guild_permissions.administrator: message_counter = 0 - mod_log = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + mod_log = await self.bot.fetch_channel(self.mod_log["logging"][2]) verified_role = get(interaction.guild.roles, id=int(self.verified_role)) naughty_role = get(interaction.guild.roles, id=int(self.naughty_role)) @@ -144,7 +147,7 @@ async def release(self, interaction: discord.Interaction, target: discord.Member await interaction.response.defer() logger.info(f"{interaction.user.name} used the release command on {target.name}") if not target.bot: - mod_log = await self.bot.fetch_channel(self.mod_log[0]["logging"][2]) + mod_log = await self.bot.fetch_channel(self.mod_log["logging"][2]) verified_role = get(interaction.guild.roles, id=int(self.verified_role)) naughty_role = get(interaction.guild.roles, id=int(self.naughty_role)) diff --git a/src/bot/cogs/verification/verification_dropdown.py b/src/bot/cogs/verification/verification_dropdown.py index 16eaa56..010dc15 100644 --- a/src/bot/cogs/verification/verification_dropdown.py +++ b/src/bot/cogs/verification/verification_dropdown.py @@ -31,10 +31,10 @@ def embed_verified_success(name, amount): class VerificationSelector(discord.ui.Select): def __init__(self, bot): self.bot = bot - self.verified_role = self.bot.api.get_one_role('6')[0]['roles'][2] - self.join_log = self.bot.api.get_one_log_setting('2')[0]['logging'][2] - self.verification_log = self.bot.api.get_one_log_setting('1')[0]['logging'][2] - self.verification_channel = self.bot.api.get_one_setting('1')[0]['setting'][2] + self.verified_role = self.bot.api.get_one_role('6')['roles'][2] + self.join_log = self.bot.api.get_one_log_setting('2')['logging'][2] + self.verification_log = self.bot.api.get_one_log_setting('1')['logging'][2] + self.verification_channel = self.bot.api.get_one_setting('1')['setting'][2] self.robot = [discord.SelectOption( label="I'm a robot." @@ -97,9 +97,9 @@ class Verification(commands.Cog): def __init__(self, bot): self.bot = bot # Passed in from main.py - self.join_log = self.bot.api.get_one_log_setting('4')[0]['logging'][2] - self.verification_channel = self.bot.api.get_one_setting('1')[0]['setting'][2] - self.verified_role = self.bot.api.get_one_role('6')[0]['roles'][2] + self.join_log = self.bot.api.get_one_log_setting('4')['logging'][2] + self.verification_channel = self.bot.api.get_one_setting('1')['setting'][2] + self.verified_role = self.bot.api.get_one_role('6')['roles'][2] @commands.command() async def verify(self, ctx): diff --git a/src/bot/cogs/verification/verification_on_join.py b/src/bot/cogs/verification/verification_on_join.py index 2cc9591..a211eb0 100644 --- a/src/bot/cogs/verification/verification_on_join.py +++ b/src/bot/cogs/verification/verification_on_join.py @@ -19,11 +19,11 @@ class LoggingVerification(commands.Cog): def __init__(self, bot): self.bot = bot - self.verification_channel = self.bot.api.get_one_setting('1')[0]['setting'][2] - self.verification_log = self.bot.api.get_one_log_setting('1')[0]['logging'][2] - self.join_log = self.bot.api.get_one_log_setting('2')[0]['logging'][2] - self.verified_role = self.bot.api.get_one_role('6')[0]['roles'][2] - self.naughty_role = self.bot.api.get_one_role('7')[0]['roles'][2] + self.verification_channel = self.bot.api.get_one_setting('1')['setting'][2] + self.verification_log = self.bot.api.get_one_log_setting('1')['logging'][2] + self.join_log = self.bot.api.get_one_log_setting('2')['logging'][2] + self.verified_role = self.bot.api.get_one_role('6')['roles'][2] + self.naughty_role = self.bot.api.get_one_role('7')['roles'][2] async def log_unverified_join(self, member, logging_channel): await logging_channel.send(f"<@{member.id}> joined, but has not verified.") diff --git a/src/docker-compose.yml b/src/docker-compose.yml index 74e2e26..90fca48 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -32,6 +32,8 @@ services: image: eos-api:latest networks: - eos + ports: + - "5000:5000" depends_on: migration: condition: service_completed_successfully @@ -48,6 +50,7 @@ services: options: max-size: "10m" max-file: "3" + postgres: container_name: db From c393bdcd9e70f97efb86e4debd1ba62bf3cce988 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 12:06:20 -0400 Subject: [PATCH 26/60] Testing Image logging --- .../cogs/logging/logging_message_delete.py | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index 4e35390..0d32e96 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -1,6 +1,8 @@ """ Logging for message deletes """ +from importlib.resources import files +from io import BytesIO import os import logging import datetime @@ -59,13 +61,30 @@ def __init__(self, bot): self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log if self.chat_log['status'] != 'ok': raise RuntimeError("Failed to fetch chat log settings from API.") + + async def build_image_embed(self, attachment: discord.Attachment) -> tuple[discord.Embed, discord.File]: + """ + Build an embed for an image attachment. + """ + embed = discord.Embed( + title="Deleted Image Attachment", + description=f"Filename: {attachment.filename}", + color=discord.Color.red(), + timestamp=datetime.datetime.now(datetime.timezone.utc) + ) + + data = await attachment.read() + file = discord.File(BytesIO(data), filename=attachment.filename) + embed.set_image(url=f"attachment://{attachment.filename}") + return embed, file @commands.Cog.listener() - async def on_message_delete(self, message): + async def on_message_delete(self, message) -> None: """ If a mod deletes, take the audit log event. If a user deletes, handle it normally. """ - if message.author.guild.id != int(os.getenv("MASTER_GUILD")) or \ + + if message.author.guild.id != int(os.getenv("MASTER_GUILD", 0)) or \ message.author.guild.id is None: logger.warning(">> on_message_delete fired, but not in master guild. Ignoring event.") return @@ -73,23 +92,37 @@ async def on_message_delete(self, message): if message.channel.id == self.staff_channel: logger.debug("Message delete in staff channel was ignored.") return - + audit_log = [entry async for entry in message.guild.audit_logs(limit=1)][0] + if self.chat_log["status"] == "ok": if self.chat_log["logging"][2] == "0": logger.debug(f"log was triggered, but logging is disabled. API: {self.chat_log}") return logs_channel = await self.bot.fetch_channel(self.chat_log["logging"][2]) - + + + file_embeds = [] + if len(message.attachments) > 0: + for attachment in message.attachments: + if attachment.content_type and attachment.content_type.startswith('image/'): + logger.debug(f"Image attachment detected in deleted message, {attachment.filename}:{attachment.url}") + file_embeds.append(await self.build_image_embed(attachment)) + if str(audit_log.action) == 'AuditLogAction.message_delete': # Then a moderator deleted a message. embed = embed_message_delete(audit_log.target, message, audit_log.user) await logs_channel.send(embed=embed) - + if file_embeds: + for embed, file in file_embeds: + await logs_channel.send(embed=embed, file=file) else: # Otherwise, the author deleted it. username = message.author await logs_channel.send(embed=embed_message_delete(username, message)) + if file_embeds: + for embed, file in file_embeds: + await logs_channel.send(embed=embed, file=file) else: logger.critical(f"API error. API response not ok. -> {self.chat_log}") From 3204e70c0e794c9fad4ec63fceb9514a652f3f3d Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 12:15:53 -0400 Subject: [PATCH 27/60] Added Attachment text to logging so it dosent appear blank. --- src/bot/cogs/logging/logging_message_delete.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index 0d32e96..30b4405 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -22,6 +22,7 @@ def embed_message_delete(some_member, some_message, some_moderator=None): description=f'{some_moderator.mention if some_moderator is not None else some_member.mention} deleted a message' f'\nIn {some_message.channel}\nMessage ' f'author: {some_member.mention}' + f'{f"\n Attachments: {len(some_message.attachments)}" if some_message.attachments else ""}' , color=discord.Color.red() , timestamp=datetime.datetime.now(datetime.timezone.utc) ) From 4d7061777876f83e391bcdf373f330f6bf51bb61 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 12:21:41 -0400 Subject: [PATCH 28/60] Reworked Attachments and removed emtpy message logging. --- .../cogs/logging/logging_message_delete.py | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index 30b4405..b2b64f6 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -20,9 +20,8 @@ def embed_message_delete(some_member, some_message, some_moderator=None): title=f'<:red_circle:1043616578744357085> Deleted Message' , description=f'{some_moderator.mention if some_moderator is not None else some_member.mention} deleted a message' - f'\nIn {some_message.channel}\nMessage ' - f'author: {some_member.mention}' - f'{f"\n Attachments: {len(some_message.attachments)}" if some_message.attachments else ""}' + f'\nIn {some_message.channel}\n' + f'Message author: {some_member.mention}' , color=discord.Color.red() , timestamp=datetime.datetime.now(datetime.timezone.utc) ) @@ -35,11 +34,21 @@ def embed_message_delete(some_member, some_message, some_moderator=None): the_message = some_message.content[0:1020] + '...' else: the_message = some_message.content - embed.add_field( - name='Message: ' - , value=the_message - , inline=True - ) + if len(the_message) == 0: + the_message = "*No text content*" + else: + embed.add_field( + name='Message: ' + , value=the_message + , inline=True + ) + + if some_message.attachments: + embed.add_field( + name='Attachments: ' + , value='\n'.join([attachment.url for attachment in some_message.attachments]) + , inline=False + ) return embed From c8cb4e8c4f4a7306101bbf315ee1f3fad5ffd259 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Tue, 19 May 2026 13:22:21 -0400 Subject: [PATCH 29/60] Changed How logging sends images. --- .../cogs/logging/logging_message_delete.py | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index b2b64f6..37181fd 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -72,21 +72,13 @@ def __init__(self, bot): if self.chat_log['status'] != 'ok': raise RuntimeError("Failed to fetch chat log settings from API.") - async def build_image_embed(self, attachment: discord.Attachment) -> tuple[discord.Embed, discord.File]: + async def build_image_embed(self, attachment: discord.Attachment) -> discord.File: """ - Build an embed for an image attachment. + Return File for message to be included with the logging embed. """ - embed = discord.Embed( - title="Deleted Image Attachment", - description=f"Filename: {attachment.filename}", - color=discord.Color.red(), - timestamp=datetime.datetime.now(datetime.timezone.utc) - ) - data = await attachment.read() file = discord.File(BytesIO(data), filename=attachment.filename) - embed.set_image(url=f"attachment://{attachment.filename}") - return embed, file + return file @commands.Cog.listener() async def on_message_delete(self, message) -> None: @@ -122,17 +114,13 @@ async def on_message_delete(self, message) -> None: if str(audit_log.action) == 'AuditLogAction.message_delete': # Then a moderator deleted a message. embed = embed_message_delete(audit_log.target, message, audit_log.user) - await logs_channel.send(embed=embed) - if file_embeds: - for embed, file in file_embeds: - await logs_channel.send(embed=embed, file=file) + await logs_channel.send(embed=embed,files=file_embeds) + else: # Otherwise, the author deleted it. username = message.author - await logs_channel.send(embed=embed_message_delete(username, message)) - if file_embeds: - for embed, file in file_embeds: - await logs_channel.send(embed=embed, file=file) + await logs_channel.send(embed=embed_message_delete(username, message),files=file_embeds) + else: logger.critical(f"API error. API response not ok. -> {self.chat_log}") From 2a8a6ed850834532d4c1861e7a37d963ae9dcf90 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 05:40:17 -0400 Subject: [PATCH 30/60] removed accidental changes. --- src/docker-compose.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/docker-compose.yml b/src/docker-compose.yml index 90fca48..74e2e26 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -32,8 +32,6 @@ services: image: eos-api:latest networks: - eos - ports: - - "5000:5000" depends_on: migration: condition: service_completed_successfully @@ -50,7 +48,6 @@ services: options: max-size: "10m" max-file: "3" - postgres: container_name: db From 7ff38a80d2f32834bb549973d0156e6432854290 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 05:41:08 -0400 Subject: [PATCH 31/60] Removed accidental changes to .gitigore --- .gitignore | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index b115186..6233bb3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,6 @@ .env /src/db/postgres-data/ -/src/*docker -src/docker-compose.yml - -scratchpad/ - # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -136,7 +131,10 @@ venv/ ENV/ env.bak/ venv.bak/ -.vscode/ + +# Spyder project settings +.spyderproject +.spyproject # Rope project settings .ropeproject @@ -159,6 +157,3 @@ dmypy.json cython_debug/ /.obsidian/ -src/docker-compose.yml -src/docker-compose.yml -src/docker-compose.yml From e487f2b7274673927f278f1123f260ea1cb0bc68 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 08:13:03 -0400 Subject: [PATCH 32/60] TicketTest --- src/api/core/db_helper.py | 91 +++++++--- src/bot/cogs/features/ticket.py | 164 +++++++++++++----- .../cogs/verification/verification_on_join.py | 2 +- src/db/init.sql | 15 +- 4 files changed, 202 insertions(+), 70 deletions(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index 1d1b10a..93917f7 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -46,15 +46,6 @@ def get_log_setting(self, setting_id): logger.error(f"Error fetching logging: {err}") return {"status": "error", "message": str(err)} - # { - # 'status': 'ok', - # 'setting': [ - # (3, 'Staff Channel', '1506111576946511923'), - # (1, 'Verification Channel', '1506114864828121158'), - # (4, 'Bot Spam Channel', '1506114864828121158'), - # (2, 'Quarantine Channel', '1506114864828121158') - # ] - # } def get_log_settings(self): logger.debug("API attempting to contact DB for get_log_settings...") try: @@ -109,16 +100,7 @@ def delete_log_setting(self, log_id): ################## ## Settings ## ################## - - # {'status': 'ok', - # 'setting': [ - # (3, 'Staff Channel', '1506111576946511923'), - # (1, 'Verification Channel', '1506114864828121158'), - # (4, 'Bot Spam Channel', '1506114864828121158'), - # (2, 'Quarantine Channel', '1506114864828121158') - # ] - # } - + def get_setting(self, setting_id): logger.debug("API attempting to contact DB for get_setting...") try: @@ -171,6 +153,7 @@ def delete_setting(self, log_id): ################## ## roles ## ################## + def get_role(self, role_id): logger.debug("API attempting to contact DB for get_role...") try: @@ -278,4 +261,72 @@ def get_top_10(self): except OperationalError as err: logger.error(f"Error deleting user: {err}") self.conn.rollback() - return {"status": "error", "message": str(err)} \ No newline at end of file + return {"status": "error", "message": str(err)} + + + ################## + ## Ticket ## + ################## + + def get_ticket(self, ticket_id: int): + """Fetches a specific ticket from the database by its ID.""" + + logger.debug("API attempting to contact DB for get_ticket...") + try: + self.cursor.execute("SELECT * FROM tickets where id = %s", (ticket_id,)) + result = self.cursor.fetchone() + return {"status": "ok", "ticket": result} + except OperationalError as err: + logger.error(f"Error fetching ticket: {err}") + return {"status": "error", "message": str(err)} + + def get_tickets(self): + """Fetches all tickets from the database.""" + + logger.debug("API attempting to contact DB for get_tickets...") + try: + self.cursor.execute("SELECT * FROM tickets") + result = self.cursor.fetchall() + return {"status": "ok", "tickets": result} + except OperationalError as err: + logger.error(f"Error fetching tickets: {err}") + return {"status": "error", "message": str(err)} + + def add_ticket(self, ticket_id: int, creator_id: int, channel_id: int, status: str = 'open'): + """ + Add a new ticket to the database. + Args: + - ticket_id: The unique ID of the ticket (e.g., thread ID). + - creator_id: The Discord ID of the user who created the ticket. + - channel_id: The Discord ID of the channel associated with the ticket. + - status: The current status of the ticket (e.g., 'open', 'closed') + + """ + + logger.debug(f"API attempting to contact DB for add_ticket with ticket_id:{ticket_id} - creator_id:{creator_id} - channel_id:{channel_id} - status:{status}") + try: + self.cursor.execute("INSERT INTO tickets (id, creator_id, channel_id, status) VALUES (%s, %s, %s, %s)", (ticket_id, creator_id, channel_id, status)) + return {"status": "ok", "message": "New ticket added successfully"} + except OperationalError as err: + logger.error(f"Error adding new ticket: {err}") + return {"status": "error", "message": str(err)} + + def update_ticket_status(self, ticket_id: int, status: str): + logger.debug(f"API attempting to contact DB for update_ticket_status with ticket_id:{ticket_id} - status:{status}") + try: + self.cursor.execute("UPDATE tickets SET status = %s WHERE id = %s", (status, ticket_id)) + return {"status": "ok", "message": "Ticket status updated successfully"} + except OperationalError as err: + logger.error(f"Error updating ticket status: {err}") + return {"status": "error", "message": str(err)} + + def delete_ticket(self, ticket_id: int): + logger.debug(f"API attempting to contact DB for delete_ticket with ticket_id:{ticket_id}") + try: + self.cursor.execute("DELETE FROM tickets WHERE id = %s", (ticket_id,)) + return {"status": "ok", "message": f"Ticket with ID {ticket_id} deleted successfully"} + except OperationalError as err: + logger.error(f"Error deleting ticket: {err}") + return {"status": "error", "message": str(err)} + + \ No newline at end of file diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index b9296b8..33248b2 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -9,71 +9,141 @@ logger = logging.getLogger(__name__) - -class AddTicketButton(commands.Cog): +class TicketReasonModal(discord.ui.Modal, title="Create a Ticket"): """ - This is the slash command that sends our UI element. + This modal appears when the user selects a ticket type. + It allows them to provide a reason or description for the ticket. """ + description = discord.ui.TextInput( + label="Please describe the issue or reason for the ticket. ", + style=discord.TextStyle.paragraph, + required=True, max_length=1000 + ) - def __init__(self, bot): + def __init__(self, bot, ticket_type: str): + super().__init__() + self.ticket_type = ticket_type self.bot = bot - @app_commands.command(description="Make a ticket and contact the Staff.") - async def ticket(self, interaction: discord.Interaction): - """ - A simple command with a view. - """ + async def on_submit(self, interaction: discord.Interaction): + guild = interaction.guild + channel = self.bot.api.get_one_setting('5') - logger.info("%s used the %s command.", interaction.user.name, interaction.command.name) - await interaction.response.defer() + if channel is None or channel['setting'][2] == "0": + logger.warning("Ticket Channel not set in db. Cannot create ticket.") + await interaction.response.send_message("Sorry, the ticket system is not set up yet. Please contact the staff directly.", ephemeral=True) + return + else: + channel = guild.get_channel(int(channel['setting'][2])) # type: ignore + - await interaction.followup.send( - "Do you need help, or do you have a question for the Staff?", - view=MakeATicket(self.bot), - ephemeral=True, - ) + thread_name = f"{self.ticket_type}-{interaction.user.name}".lower().replace(" ", "-") + + + thread = await channel.create_thread( # type: ignore + name=thread_name, + type=discord.ChannelType.private_thread, + invitable=False + ) + await thread.add_user(interaction.user) # type: ignore + + await thread.send( + f'<@&{self.staff_role}> -- {interaction.user.mention} has created a ticket\n' + f'Type: {self.ticket_type}\n' + f'{self.description.value}' + f'\n\n please provide any additional information here and our staff will assist you as soon as possible.' + ) + + self.bot.db.create_ticket(interaction.user.id, thread.id, self.ticket_type, self.description.value) + + await interaction.response.send_message(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) + -class MakeATicket(discord.ui.View): +class TicketDropdown(discord.ui.Select): """ - A UI component that sends a button, which does other things. + This is the dropdown that appears when the user clicks + the button to create a ticket. It allows them to select the + type of ticket they want to create. """ - - def __init__(self, bot, *, timeout=None): - super().__init__(timeout=timeout) + def __init__(self, bot): self.bot = bot + + options = [ + discord.SelectOption( + label="Moderation", + description="Report users or moderation issues" + ), + discord.SelectOption( + label="Support", + description="General support ticket" + ), + discord.SelectOption( + label="Proposition", + description="Suggest an idea or partnership" + ), + discord.SelectOption( + label="Request", + description="Request something from staff" + ) + ] + + super().__init__( + placeholder="Please select a ticket type", + min_values=1, + max_values=1, + options=options) + + async def callback(self, interaction: discord.Interaction): + selected = self.values[0] + await interaction.response.send_modal(TicketReasonModal(self.bot, selected)) - @discord.ui.button(label="Open a support Ticket", style=discord.ButtonStyle.primary) - async def button_callback(self, interaction, button): - """ - The callback on the button, or... what happens on click. - """ - await interaction.response.defer() - button.label = "Ticket Created!" - button.disabled = True - await interaction.edit_original_response(view=self) + - support = interaction.channel #TODO: guild specific settings for a support channel - staff = interaction.guild.get_role(self.bot.api.get_one_role("3")[0]["roles"][2]) # Staff - ticket = await support.create_thread( - name=f"[Ticket] - {interaction.user}", - message=None, - auto_archive_duration=4320, - type=discord.ChannelType.private_thread, - reason=None, - ) +class TicketView(discord.ui.View): + def __init__(self, bot): + super().__init__(timeout=None) + self.bot = bot - for person in interaction.guild.members: - if staff in person.roles: - await ticket.add_user(person) + - await ticket.add_user(interaction.user) - await interaction.delete_original_response() - await ticket.send(f"**{interaction.user.mention}, we have received your ticket.**") - await ticket.send("To better help you, please describe your issue.") +class AddTicketModal(commands.Cog): + """ + This is the slash command that sends our UI element. + """ + + def __init__(self, bot): + self.bot = bot + + ticket_channel = self.bot.api.get_one_setting('5') + + staff_role = self.bot.api.get_one_role('3') + + self.staff_role = staff_role if staff_role['status'] == 'ok' else None + + if ticket_channel is None or ticket_channel['setting'][2] == "0": + logger.warning("Ticket Channel not set in db. Ticket commands will not work until this is set.") + raise RuntimeError("Ticket Channel not set in db. Ticket commands will not work until this is set.") + else: + self.ticket_channel = ticket_channel['setting'][2] + + @app_commands.command(description="Make a ticket and contact the Staff.") + async def ticket(self, interaction: discord.Interaction): + + """ + A simple command with a view. + """ + logger.info("%s used the %s command.", interaction.user.name, interaction.command.name) # type: ignore + await interaction.response.defer() + + await interaction.followup.send( + "Do you need help, or do you have a question for the Staff?", + view=TicketView(self.bot), + ephemeral=True, + ) async def setup(bot: commands.Bot) -> None: """boink""" - await bot.add_cog(AddTicketButton(bot)) + await bot.add_cog(AddTicketModal(bot)) diff --git a/src/bot/cogs/verification/verification_on_join.py b/src/bot/cogs/verification/verification_on_join.py index a211eb0..233e4a2 100644 --- a/src/bot/cogs/verification/verification_on_join.py +++ b/src/bot/cogs/verification/verification_on_join.py @@ -60,7 +60,7 @@ async def on_member_join(self, member: discord.Member): guild = member.guild guild_id = member.guild.id - if guild_id != int(os.getenv("MASTER_GUILD")): + if guild_id != os.getenv("MASTER_GUILD"): logger.warning("on_member_join fired, but not in master guild. Ignoring event.") return diff --git a/src/db/init.sql b/src/db/init.sql index fb0fea7..15b096d 100644 --- a/src/db/init.sql +++ b/src/db/init.sql @@ -17,7 +17,8 @@ VALUES ('Verification Channel', '0'), ('Quarantine Channel', '0'), ('Staff Channel', '0'), - ('Bot Spam Channel', '0'); + ('Bot Spam Channel', '0'), + ('Ticket Channel', '0'); -- Create logging table @@ -62,4 +63,14 @@ CREATE TABLE IF NOT EXISTS users ( points int NOT NULL ); ALTER TABLE users - ADD CONSTRAINT unique_discord_id UNIQUE (discord_id); \ No newline at end of file + ADD CONSTRAINT unique_discord_id UNIQUE (discord_id); + +-- Create open tickets table +CREATE TABLE IF NOT EXISTS tickets ( + id SERIAL PRIMARY KEY, + thread_id BIGINT NOT NULL UNIQUE, + channel_id BIGINT NOT NULL, + creator_id BIGINT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + status VARCHAR(50) DEFAULT 'open' +); \ No newline at end of file From 768a663043392cdc2d9b1f52fd04acf64f86f1b4 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 08:33:18 -0400 Subject: [PATCH 33/60] modal wasnt sending properly. --- src/bot/cogs/features/ticket.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index 33248b2..f4fb3b9 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -135,10 +135,9 @@ async def ticket(self, interaction: discord.Interaction): A simple command with a view. """ logger.info("%s used the %s command.", interaction.user.name, interaction.command.name) # type: ignore - await interaction.response.defer() - await interaction.followup.send( - "Do you need help, or do you have a question for the Staff?", + await interaction.response.send_message( + "creating ticket...", view=TicketView(self.bot), ephemeral=True, ) From 5caca12c464ac96eef8c01fe9bbaabb23578820a Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 08:38:33 -0400 Subject: [PATCH 34/60] Forgot to actualy open the modal this time --- src/bot/cogs/features/ticket.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index f4fb3b9..f8c64b0 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -105,6 +105,7 @@ class TicketView(discord.ui.View): def __init__(self, bot): super().__init__(timeout=None) self.bot = bot + self.add_item(TicketDropdown(bot)) From 9be5363f1fc3141d80958391541e60ce394175ea Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 08:53:33 -0400 Subject: [PATCH 35/60] ReWorked Selector --- src/bot/cogs/features/ticket.py | 85 ++++++++++++++------------------- 1 file changed, 35 insertions(+), 50 deletions(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index f8c64b0..c28a00b 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -9,20 +9,46 @@ logger = logging.getLogger(__name__) -class TicketReasonModal(discord.ui.Modal, title="Create a Ticket"): +class TicketReasonModal(discord.ui.Modal, title="Create Ticket"): """ This modal appears when the user selects a ticket type. It allows them to provide a reason or description for the ticket. """ + + options = [ + discord.SelectOption( + label="Moderation", + description="Report users or moderation issues" + ), + discord.SelectOption( + label="Support", + description="General support ticket" + ), + discord.SelectOption( + label="Proposition", + description="Suggest an idea or partnership" + ), + discord.SelectOption( + label="Request", + description="Request something from staff" + ) + ] + selected_option = discord.ui.Select( + custom_id="ticket_type", + options=options, + placeholder="Select the type of ticket you want to create", + ) + description = discord.ui.TextInput( label="Please describe the issue or reason for the ticket. ", style=discord.TextStyle.paragraph, required=True, max_length=1000 ) - def __init__(self, bot, ticket_type: str): + + + def __init__(self, bot): super().__init__() - self.ticket_type = ticket_type self.bot = bot async def on_submit(self, interaction: discord.Interaction): @@ -36,76 +62,35 @@ async def on_submit(self, interaction: discord.Interaction): else: channel = guild.get_channel(int(channel['setting'][2])) # type: ignore - + option = self.selected_option.values[0] if self.selected_option.values else "None provided" - thread_name = f"{self.ticket_type}-{interaction.user.name}".lower().replace(" ", "-") + thread_name = f"{option}-{interaction.user.name}".lower().replace(" ", "-") thread = await channel.create_thread( # type: ignore name=thread_name, type=discord.ChannelType.private_thread, - invitable=False + invitable=False ) await thread.add_user(interaction.user) # type: ignore await thread.send( f'<@&{self.staff_role}> -- {interaction.user.mention} has created a ticket\n' - f'Type: {self.ticket_type}\n' + f'Type: {option}\n' f'{self.description.value}' f'\n\n please provide any additional information here and our staff will assist you as soon as possible.' ) - self.bot.db.create_ticket(interaction.user.id, thread.id, self.ticket_type, self.description.value) + self.bot.db.create_ticket(interaction.user.id, thread.id, option, self.description.value) await interaction.response.send_message(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) -class TicketDropdown(discord.ui.Select): - """ - This is the dropdown that appears when the user clicks - the button to create a ticket. It allows them to select the - type of ticket they want to create. - """ - def __init__(self, bot): - self.bot = bot - - options = [ - discord.SelectOption( - label="Moderation", - description="Report users or moderation issues" - ), - discord.SelectOption( - label="Support", - description="General support ticket" - ), - discord.SelectOption( - label="Proposition", - description="Suggest an idea or partnership" - ), - discord.SelectOption( - label="Request", - description="Request something from staff" - ) - ] - - super().__init__( - placeholder="Please select a ticket type", - min_values=1, - max_values=1, - options=options) - - async def callback(self, interaction: discord.Interaction): - selected = self.values[0] - await interaction.response.send_modal(TicketReasonModal(self.bot, selected)) - - - - class TicketView(discord.ui.View): def __init__(self, bot): super().__init__(timeout=None) self.bot = bot - self.add_item(TicketDropdown(bot)) + self.add_item(TicketReasonModal(bot)) From e88bb164106736bb304c04a286a8215cde4aa749 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 09:06:21 -0400 Subject: [PATCH 36/60] send_message -> send_modal --- .gitignore | 4 ++++ src/bot/cogs/features/ticket.py | 14 ++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 6233bb3..ae99898 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ /.idea/ .env /src/db/postgres-data/ +.gitignore +.vscode/ # Byte-compiled / optimized / DLL files __pycache__/ @@ -157,3 +159,5 @@ dmypy.json cython_debug/ /.obsidian/ +src/bot/cogs/moderation/admin_detect_spam_message.py +.gitignore diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index c28a00b..a64b1da 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -14,6 +14,8 @@ class TicketReasonModal(discord.ui.Modal, title="Create Ticket"): This modal appears when the user selects a ticket type. It allows them to provide a reason or description for the ticket. """ + + options = [ discord.SelectOption( @@ -40,8 +42,8 @@ class TicketReasonModal(discord.ui.Modal, title="Create Ticket"): ) description = discord.ui.TextInput( - label="Please describe the issue or reason for the ticket. ", - style=discord.TextStyle.paragraph, + label="Please describe The Issue...", + style=discord.TextStyle.short, required=True, max_length=1000 ) @@ -83,7 +85,7 @@ async def on_submit(self, interaction: discord.Interaction): self.bot.db.create_ticket(interaction.user.id, thread.id, option, self.description.value) - await interaction.response.send_message(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) + await interaction.followup.send(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) class TicketView(discord.ui.View): @@ -122,11 +124,7 @@ async def ticket(self, interaction: discord.Interaction): """ logger.info("%s used the %s command.", interaction.user.name, interaction.command.name) # type: ignore - await interaction.response.send_message( - "creating ticket...", - view=TicketView(self.bot), - ephemeral=True, - ) + await interaction.response.send_modal(TicketReasonModal(self.bot)) async def setup(bot: commands.Bot) -> None: From b5cfabf8e0f72f932fcb1b38e04caeacb0436ca7 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 09:17:55 -0400 Subject: [PATCH 37/60] oops lol --- src/bot/cogs/features/ticket.py | 62 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index a64b1da..1282d4e 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -14,44 +14,20 @@ class TicketReasonModal(discord.ui.Modal, title="Create Ticket"): This modal appears when the user selects a ticket type. It allows them to provide a reason or description for the ticket. """ - - - - options = [ - discord.SelectOption( - label="Moderation", - description="Report users or moderation issues" - ), - discord.SelectOption( - label="Support", - description="General support ticket" - ), - discord.SelectOption( - label="Proposition", - description="Suggest an idea or partnership" - ), - discord.SelectOption( - label="Request", - description="Request something from staff" - ) - ] - selected_option = discord.ui.Select( - custom_id="ticket_type", - options=options, - placeholder="Select the type of ticket you want to create", - ) description = discord.ui.TextInput( label="Please describe The Issue...", style=discord.TextStyle.short, - required=True, max_length=1000 + required=True, + max_length=1000 ) - def __init__(self, bot): + def __init__(self, bot, selected_option: str): super().__init__() self.bot = bot + self.selected_option = selected_option async def on_submit(self, interaction: discord.Interaction): guild = interaction.guild @@ -64,7 +40,7 @@ async def on_submit(self, interaction: discord.Interaction): else: channel = guild.get_channel(int(channel['setting'][2])) # type: ignore - option = self.selected_option.values[0] if self.selected_option.values else "None provided" + option = self.selected_option if self.selected_option else "None Selected" thread_name = f"{option}-{interaction.user.name}".lower().replace(" ", "-") @@ -87,12 +63,33 @@ async def on_submit(self, interaction: discord.Interaction): await interaction.followup.send(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) +class TicketDropdown(discord.ui.Select): + def __init__(self, bot): + self.bot = bot + + options = [ + discord.SelectOption(label="Moderation"), + discord.SelectOption(label="Support"), + discord.SelectOption(label="Proposition"), + discord.SelectOption(label="Request"), + ] + + super().__init__( + placeholder="Select ticket type", + options=options + ) + + async def callback(self, interaction: discord.Interaction): + await interaction.response.send_modal( + TicketReasonModal(self.bot, self.values[0]) + ) + class TicketView(discord.ui.View): def __init__(self, bot): - super().__init__(timeout=None) + super().__init__(timeout=100) # View will timeout after 1.5 minutes self.bot = bot - self.add_item(TicketReasonModal(bot)) + self.add_item(TicketDropdown(bot)) @@ -124,8 +121,9 @@ async def ticket(self, interaction: discord.Interaction): """ logger.info("%s used the %s command.", interaction.user.name, interaction.command.name) # type: ignore - await interaction.response.send_modal(TicketReasonModal(self.bot)) + await interaction.response.send_message("creating ticket...", view=TicketView(self.bot), ephemeral=True) + async def setup(bot: commands.Bot) -> None: """boink""" From fddeaac26492a8f516e52a3c09ddbe9cbae02fe0 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 09:23:19 -0400 Subject: [PATCH 38/60] staff_role broke it --- src/bot/cogs/features/ticket.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index 1282d4e..da98419 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -32,6 +32,8 @@ def __init__(self, bot, selected_option: str): async def on_submit(self, interaction: discord.Interaction): guild = interaction.guild channel = self.bot.api.get_one_setting('5') + staff_role = self.bot.api.get_one_role('3') + staff_role = staff_role if staff_role['status'] == 'ok' else None if channel is None or channel['setting'][2] == "0": logger.warning("Ticket Channel not set in db. Cannot create ticket.") @@ -53,7 +55,7 @@ async def on_submit(self, interaction: discord.Interaction): await thread.add_user(interaction.user) # type: ignore await thread.send( - f'<@&{self.staff_role}> -- {interaction.user.mention} has created a ticket\n' + f'<@&{staff_role}> -- {interaction.user.mention} has created a ticket\n' f'Type: {option}\n' f'{self.description.value}' f'\n\n please provide any additional information here and our staff will assist you as soon as possible.' @@ -103,9 +105,6 @@ def __init__(self, bot): ticket_channel = self.bot.api.get_one_setting('5') - staff_role = self.bot.api.get_one_role('3') - - self.staff_role = staff_role if staff_role['status'] == 'ok' else None if ticket_channel is None or ticket_channel['setting'][2] == "0": logger.warning("Ticket Channel not set in db. Ticket commands will not work until this is set.") From b317413a8852b5ae817d6af40ef5492a4469eb58 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 11:29:32 -0400 Subject: [PATCH 39/60] fix db push for ticket. --- src/bot/cogs/features/ticket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index da98419..d0c5545 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -61,7 +61,7 @@ async def on_submit(self, interaction: discord.Interaction): f'\n\n please provide any additional information here and our staff will assist you as soon as possible.' ) - self.bot.db.create_ticket(interaction.user.id, thread.id, option, self.description.value) + self.bot.api.create_ticket(interaction.user.id, thread.id, option, self.description.value) await interaction.followup.send(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) From e0d5c95271d1374e6756d8e610bbc2b151aee7b9 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 11:31:35 -0400 Subject: [PATCH 40/60] add_ticket not create --- src/bot/cogs/features/ticket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index d0c5545..f62fe0b 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -61,7 +61,7 @@ async def on_submit(self, interaction: discord.Interaction): f'\n\n please provide any additional information here and our staff will assist you as soon as possible.' ) - self.bot.api.create_ticket(interaction.user.id, thread.id, option, self.description.value) + self.bot.api.add_ticket(interaction.user.id, thread.id, option, self.description.value) await interaction.followup.send(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) From ba092b68e2b87d2fe22a63b750361a58790f2c44 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 11:34:20 -0400 Subject: [PATCH 41/60] message fix --- src/bot/cogs/features/ticket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index f62fe0b..0714580 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -33,7 +33,7 @@ async def on_submit(self, interaction: discord.Interaction): guild = interaction.guild channel = self.bot.api.get_one_setting('5') staff_role = self.bot.api.get_one_role('3') - staff_role = staff_role if staff_role['status'] == 'ok' else None + staff_role = staff_role['roles'][2] if staff_role['status'] == 'ok' else None if channel is None or channel['setting'][2] == "0": logger.warning("Ticket Channel not set in db. Cannot create ticket.") From d1d08a5651358459510880f84a2e170e57d5df91 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 11:50:34 -0400 Subject: [PATCH 42/60] inital updates to db calls. --- src/api/api.py | 2 + src/api/routes/roles.py | 1 + src/api/routes/tickets.py | 76 +++++++++++++++++++++++++++++++++ src/bot/cogs/features/ticket.py | 14 +++--- src/bot/core/api_helper.py | 39 +++++++++++++++++ 5 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 src/api/routes/tickets.py diff --git a/src/api/api.py b/src/api/api.py index b7590bc..4ee2ce1 100644 --- a/src/api/api.py +++ b/src/api/api.py @@ -10,6 +10,7 @@ from routes.settings import settings from routes.points import points from routes.roles import role +from routes.tickets import tickets from core.db_helper import DB @@ -28,6 +29,7 @@ app.register_blueprint(settings) app.register_blueprint(points) app.register_blueprint(role) +app.register_blueprint(tickets) # Error handlers @app.errorhandler(Exception) diff --git a/src/api/routes/roles.py b/src/api/routes/roles.py index ac37470..7934556 100644 --- a/src/api/routes/roles.py +++ b/src/api/routes/roles.py @@ -61,3 +61,4 @@ def delete_role(role_id): return jsonify(result), 200 return jsonify({'message': 'improper request method'}), 405 + diff --git a/src/api/routes/tickets.py b/src/api/routes/tickets.py new file mode 100644 index 0000000..90a2f6e --- /dev/null +++ b/src/api/routes/tickets.py @@ -0,0 +1,76 @@ +from flask import Blueprint, jsonify, request +from flask import current_app as eos +import logging + +logger = logging.getLogger(__name__) + +tickets = Blueprint('tickets', __name__) + +@tickets.route('/tickets', methods=['GET']) +@tickets.route('/tickets/', methods=['GET']) +def get_ticket(ticket_id=None): + """ + Retrieve tickets from the database. + + :param ticket_id: Optional integer ID of a specific ticket + :return: JSON response with ticket(s) + """ + try: + if ticket_id is None: + # Retrieve all tickets + result = eos.db.get_tickets() + else: + # Retrieve a single ticket + result = eos.db.get_ticket(ticket_id) + except Exception as e: + logger.error(f"Error retrieving ticket(s) from database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while retrieving tickets.'}), 500 + + return jsonify(result), 200 + +@tickets.route('/tickets/', methods=['PUT']) +def update_ticket(ticket_id): + """ + Update an existing ticket in the database. + """ + if request.method == 'PUT': + data = request.json + try: + result = eos.db.update_ticket_status(int(ticket_id), data['status']) + return jsonify(result), 200 + except Exception as e: + logger.error(f"Error updating ticket in database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while updating the ticket.'}), 500 + + return jsonify({'message': 'improper request method'}), 405 + +@tickets.route('/tickets', methods=['POST']) +def add_ticket(): + """ + Add a new ticket to the database. + """ + if request.method == 'POST': + data = request.json + try: + result = eos.db.add_ticket(data) + return jsonify(result), 201 + except Exception as e: + logger.error(f"Error adding ticket to database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while adding the ticket.'}), 500 + + return jsonify({'message': 'improper request method'}), 405 + +@tickets.route('/tickets/', methods=['DELETE']) +def delete_ticket(ticket_id): + """ + Delete a specific ticket from the database. + """ + if request.method == 'DELETE': + try: + result = eos.db.delete_ticket(ticket_id) + return jsonify(result), 200 + except Exception as e: + logger.error(f"Error deleting ticket from database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while deleting the ticket.'}), 500 + + return jsonify({'message': 'improper request method'}), 405 diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index 0714580..0cdf924 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -55,16 +55,20 @@ async def on_submit(self, interaction: discord.Interaction): await thread.add_user(interaction.user) # type: ignore await thread.send( - f'<@&{staff_role}> -- {interaction.user.mention} has created a ticket\n' - f'Type: {option}\n' + f'<@&{staff_role}>\n ## {interaction.user.mention} has created a ticket\n' + f'** Type: ** {option}\n' f'{self.description.value}' - f'\n\n please provide any additional information here and our staff will assist you as soon as possible.' + f'\n\n _ please provide any additional information here and our staff will assist you as soon as possible. _' ) - - self.bot.api.add_ticket(interaction.user.id, thread.id, option, self.description.value) + try: + self.bot.api.add_ticket(interaction.user.id, thread.id, option, self.description.value) + except Exception as e: + logger.error(f"Error adding ticket to database: {e}") + await thread.send("There was an error saving the ticket to the database") await interaction.followup.send(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) + class TicketDropdown(discord.ui.Select): def __init__(self, bot): self.bot = bot diff --git a/src/bot/core/api_helper.py b/src/bot/core/api_helper.py index 7b84620..715aa1c 100644 --- a/src/bot/core/api_helper.py +++ b/src/bot/core/api_helper.py @@ -162,3 +162,42 @@ def update_points(self, user_id, amount): def top_10(self): logger.debug("Bot called the top_10 endpoint.") return requests.get(f"{self.api}/points/top10").json() + + + ################## + ## Ticket ## + ################## + + def get_ticket(self, ticket_id=None): + """Retrieves ticket(s) from the database""" + logger.debug(f"Bot called the get_ticket endpoint. Ticket ID: {ticket_id if ticket_id else 'All tickets'}") + if ticket_id is None: + return requests.get(f"{self.api}/tickets").json() + else: + return requests.get(f"{self.api}/tickets/{ticket_id}").json() + + def add_ticket(self, user_id, channel_id, ticket_type, description): + """Adds a new ticket to the database""" + logger.debug(f"Bot called the add_ticket endpoint. User ID: {user_id} - Channel ID: {channel_id} - Ticket Type: {ticket_type}") + data = { + 'user_id': user_id, + 'channel_id': channel_id, + 'ticket_type': ticket_type, + 'description': description + } + return requests.post(f"{self.api}/tickets", json=data).json() + + def update_ticket_status(self, ticket_id, new_status): + """Updates the status of an existing ticket in the database""" + logger.debug(f"Bot called the update_ticket_status endpoint. Ticket ID: {ticket_id} - New Status: {new_status}") + data = { + 'status': new_status + } + return requests.put(f"{self.api}/tickets/{ticket_id}", json=data).json() + + def delete_ticket(self, ticket_id): + """Deletes a ticket from the database""" + logger.debug(f"Bot called the delete_ticket endpoint. Ticket ID: {ticket_id}") + return requests.delete(f"{self.api}/tickets/{ticket_id}").json() + + \ No newline at end of file From 96cc42caaea005ad388cd758010d8b3055f164c9 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 12:00:37 -0400 Subject: [PATCH 43/60] API fixes? --- src/api/core/db_helper.py | 24 ++++++++++++------------ src/bot/cogs/features/ticket.py | 2 +- src/bot/core/api_helper.py | 9 ++++----- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index 93917f7..c957f4e 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -39,7 +39,7 @@ def database_health_check(self): def get_log_setting(self, setting_id): logger.debug("API attempting to contact DB for get_log_setting...") try: - self.cursor.execute("SELECT * FROM logging where id = %s", (setting_id,)) + self.cursor.execute("SELECT * FROM logging WHERE ticket_id = %s", (setting_id,)) result = self.cursor.fetchone() return {"status": "ok", "logging": result} except OperationalError as err: @@ -73,7 +73,7 @@ def get_logging(self): def update_logging(self, log_id, value): logger.debug(f"API attempting to contact DB for update_logging with log ID:{log_id} - Value:{value}") try: - self.cursor.execute("UPDATE logging SET value = %s WHERE id = %s", (value, log_id)) + self.cursor.execute("UPDATE logging SET value = %s WHERE ticket_id = %s", (value, log_id)) return {"status": "ok", "message": "Log setting updated successfully"} except OperationalError as err: logger.error(f"Error updating log setting: {err}") @@ -91,7 +91,7 @@ def add_log_setting(self, name, value): def delete_log_setting(self, log_id): logger.debug(f"API attempting to contact DB for delete_log with log_ID:{log_id}") try: - self.cursor.execute("DELETE FROM logging WHERE id = %s", (log_id,)) + self.cursor.execute("DELETE FROM logging WHERE ticket_id = %s", (log_id,)) return {"status": "ok", "message": f"Log with ID {log_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting log setting: {err}") @@ -104,7 +104,7 @@ def delete_log_setting(self, log_id): def get_setting(self, setting_id): logger.debug("API attempting to contact DB for get_setting...") try: - self.cursor.execute("SELECT * FROM serversettings where id = %s", (setting_id,)) + self.cursor.execute("SELECT * FROM serversettings WHERE ticket_id = %s", (setting_id,)) result = self.cursor.fetchone() return {"status": "ok", "setting": result} except OperationalError as err: @@ -125,7 +125,7 @@ def get_settings(self): def update_setting(self, setting_id, value): logger.debug(f"API attempting to contact DB for update_setting with setting ID:{setting_id} - Value:{value}") try: - self.cursor.execute("UPDATE serversettings SET value = %s WHERE id = %s", (value, setting_id)) + self.cursor.execute("UPDATE serversettings SET value = %s WHERE ticket_id = %s", (value, setting_id)) return {"status": "ok", "message": "Setting updated successfully"} except OperationalError as err: logger.error(f"Error updating setting: {err}") @@ -143,7 +143,7 @@ def add_setting(self, name, value): def delete_setting(self, log_id): logger.debug(f"API attempting to contact DB for delete_setting with setting_ID:{log_id}") try: - self.cursor.execute("DELETE FROM serversettings WHERE id = %s", (log_id,)) + self.cursor.execute("DELETE FROM serversettings WHERE ticket_id = %s", (log_id,)) return {"status": "ok", "message": f"Setting with ID {log_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting setting: {err}") @@ -157,7 +157,7 @@ def delete_setting(self, log_id): def get_role(self, role_id): logger.debug("API attempting to contact DB for get_role...") try: - self.cursor.execute("SELECT * FROM roles where id = %s", (role_id,)) + self.cursor.execute("SELECT * FROM roles WHERE ticket_id = %s", (role_id,)) result = self.cursor.fetchone() return {"status": "ok", "roles": result} except OperationalError as err: @@ -177,7 +177,7 @@ def get_roles(self): def update_role(self, role_id, value): logger.debug(f"API attempting to contact DB for update_role with role ID:{role_id} - Value:{value}") try: - self.cursor.execute("UPDATE roles SET value = %s WHERE id = %s", (value, role_id)) + self.cursor.execute("UPDATE roles SET value = %s WHERE ticket_id = %s", (value, role_id)) return {"status": "ok", "message": "role updated successfully"} except OperationalError as err: logger.error(f"Error updating role: {err}") @@ -195,7 +195,7 @@ def add_role(self, name, value): def delete_role(self, role_id): logger.debug(f"API attempting to contact DB for delete_role with role_ID:{role_id}") try: - self.cursor.execute("DELETE FROM roles WHERE id = %s", (role_id,)) + self.cursor.execute("DELETE FROM roles WHERE ticket_id = %s", (role_id,)) return {"status": "ok", "message": f"role with ID {role_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting role: {err}") @@ -273,7 +273,7 @@ def get_ticket(self, ticket_id: int): logger.debug("API attempting to contact DB for get_ticket...") try: - self.cursor.execute("SELECT * FROM tickets where id = %s", (ticket_id,)) + self.cursor.execute("SELECT * FROM tickets WHERE ticket_id = %s", (ticket_id,)) result = self.cursor.fetchone() return {"status": "ok", "ticket": result} except OperationalError as err: @@ -314,7 +314,7 @@ def add_ticket(self, ticket_id: int, creator_id: int, channel_id: int, status: s def update_ticket_status(self, ticket_id: int, status: str): logger.debug(f"API attempting to contact DB for update_ticket_status with ticket_id:{ticket_id} - status:{status}") try: - self.cursor.execute("UPDATE tickets SET status = %s WHERE id = %s", (status, ticket_id)) + self.cursor.execute("UPDATE tickets SET status = %s WHERE ticket_id = %s", (status, ticket_id)) return {"status": "ok", "message": "Ticket status updated successfully"} except OperationalError as err: logger.error(f"Error updating ticket status: {err}") @@ -323,7 +323,7 @@ def update_ticket_status(self, ticket_id: int, status: str): def delete_ticket(self, ticket_id: int): logger.debug(f"API attempting to contact DB for delete_ticket with ticket_id:{ticket_id}") try: - self.cursor.execute("DELETE FROM tickets WHERE id = %s", (ticket_id,)) + self.cursor.execute("DELETE FROM tickets WHERE ticket_id = %s", (ticket_id,)) return {"status": "ok", "message": f"Ticket with ID {ticket_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting ticket: {err}") diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index 0cdf924..b01eacf 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -61,7 +61,7 @@ async def on_submit(self, interaction: discord.Interaction): f'\n\n _ please provide any additional information here and our staff will assist you as soon as possible. _' ) try: - self.bot.api.add_ticket(interaction.user.id, thread.id, option, self.description.value) + self.bot.api.add_ticket(thread.id, interaction.user.id, channel.id) except Exception as e: logger.error(f"Error adding ticket to database: {e}") await thread.send("There was an error saving the ticket to the database") diff --git a/src/bot/core/api_helper.py b/src/bot/core/api_helper.py index 715aa1c..3d73c12 100644 --- a/src/bot/core/api_helper.py +++ b/src/bot/core/api_helper.py @@ -176,14 +176,13 @@ def get_ticket(self, ticket_id=None): else: return requests.get(f"{self.api}/tickets/{ticket_id}").json() - def add_ticket(self, user_id, channel_id, ticket_type, description): + def add_ticket(self,ticket_id, user_id, channel_id): """Adds a new ticket to the database""" - logger.debug(f"Bot called the add_ticket endpoint. User ID: {user_id} - Channel ID: {channel_id} - Ticket Type: {ticket_type}") + logger.debug(f"Bot called the add_ticket endpoint. User ID: {user_id} - Channel ID: {channel_id}") data = { - 'user_id': user_id, + 'ticket_id': ticket_id, + 'creator_id': user_id, 'channel_id': channel_id, - 'ticket_type': ticket_type, - 'description': description } return requests.post(f"{self.api}/tickets", json=data).json() From 78ffbf7a0441d9746ff74c95aa6ed47f83930c3e Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 12:05:55 -0400 Subject: [PATCH 44/60] idk --- src/api/core/db_helper.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index c957f4e..29c9910 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -39,7 +39,7 @@ def database_health_check(self): def get_log_setting(self, setting_id): logger.debug("API attempting to contact DB for get_log_setting...") try: - self.cursor.execute("SELECT * FROM logging WHERE ticket_id = %s", (setting_id,)) + self.cursor.execute("SELECT * FROM logging where id = %s", (setting_id,)) result = self.cursor.fetchone() return {"status": "ok", "logging": result} except OperationalError as err: @@ -73,7 +73,7 @@ def get_logging(self): def update_logging(self, log_id, value): logger.debug(f"API attempting to contact DB for update_logging with log ID:{log_id} - Value:{value}") try: - self.cursor.execute("UPDATE logging SET value = %s WHERE ticket_id = %s", (value, log_id)) + self.cursor.execute("UPDATE logging SET value = %s WHERE id = %s", (value, log_id)) return {"status": "ok", "message": "Log setting updated successfully"} except OperationalError as err: logger.error(f"Error updating log setting: {err}") @@ -91,7 +91,7 @@ def add_log_setting(self, name, value): def delete_log_setting(self, log_id): logger.debug(f"API attempting to contact DB for delete_log with log_ID:{log_id}") try: - self.cursor.execute("DELETE FROM logging WHERE ticket_id = %s", (log_id,)) + self.cursor.execute("DELETE FROM logging WHERE id = %s", (log_id,)) return {"status": "ok", "message": f"Log with ID {log_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting log setting: {err}") @@ -104,7 +104,7 @@ def delete_log_setting(self, log_id): def get_setting(self, setting_id): logger.debug("API attempting to contact DB for get_setting...") try: - self.cursor.execute("SELECT * FROM serversettings WHERE ticket_id = %s", (setting_id,)) + self.cursor.execute("SELECT * FROM serversettings where id = %s", (setting_id,)) result = self.cursor.fetchone() return {"status": "ok", "setting": result} except OperationalError as err: @@ -125,7 +125,7 @@ def get_settings(self): def update_setting(self, setting_id, value): logger.debug(f"API attempting to contact DB for update_setting with setting ID:{setting_id} - Value:{value}") try: - self.cursor.execute("UPDATE serversettings SET value = %s WHERE ticket_id = %s", (value, setting_id)) + self.cursor.execute("UPDATE serversettings SET value = %s WHERE id = %s", (value, setting_id)) return {"status": "ok", "message": "Setting updated successfully"} except OperationalError as err: logger.error(f"Error updating setting: {err}") @@ -143,7 +143,7 @@ def add_setting(self, name, value): def delete_setting(self, log_id): logger.debug(f"API attempting to contact DB for delete_setting with setting_ID:{log_id}") try: - self.cursor.execute("DELETE FROM serversettings WHERE ticket_id = %s", (log_id,)) + self.cursor.execute("DELETE FROM serversettings WHERE id = %s", (log_id,)) return {"status": "ok", "message": f"Setting with ID {log_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting setting: {err}") @@ -157,7 +157,7 @@ def delete_setting(self, log_id): def get_role(self, role_id): logger.debug("API attempting to contact DB for get_role...") try: - self.cursor.execute("SELECT * FROM roles WHERE ticket_id = %s", (role_id,)) + self.cursor.execute("SELECT * FROM roles where id = %s", (role_id,)) result = self.cursor.fetchone() return {"status": "ok", "roles": result} except OperationalError as err: @@ -177,7 +177,7 @@ def get_roles(self): def update_role(self, role_id, value): logger.debug(f"API attempting to contact DB for update_role with role ID:{role_id} - Value:{value}") try: - self.cursor.execute("UPDATE roles SET value = %s WHERE ticket_id = %s", (value, role_id)) + self.cursor.execute("UPDATE roles SET value = %s WHERE id = %s", (value, role_id)) return {"status": "ok", "message": "role updated successfully"} except OperationalError as err: logger.error(f"Error updating role: {err}") @@ -195,10 +195,12 @@ def add_role(self, name, value): def delete_role(self, role_id): logger.debug(f"API attempting to contact DB for delete_role with role_ID:{role_id}") try: - self.cursor.execute("DELETE FROM roles WHERE ticket_id = %s", (role_id,)) + self.cursor.execute("DELETE FROM roles WHERE id = %s", (role_id,)) + self.conn.commit() return {"status": "ok", "message": f"role with ID {role_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting role: {err}") + self.conn.rollback() return {"status": "error", "message": str(err)} ################## @@ -273,7 +275,7 @@ def get_ticket(self, ticket_id: int): logger.debug("API attempting to contact DB for get_ticket...") try: - self.cursor.execute("SELECT * FROM tickets WHERE ticket_id = %s", (ticket_id,)) + self.cursor.execute("SELECT * FROM tickets where ticket_id = %s", (ticket_id,)) result = self.cursor.fetchone() return {"status": "ok", "ticket": result} except OperationalError as err: @@ -305,28 +307,34 @@ def add_ticket(self, ticket_id: int, creator_id: int, channel_id: int, status: s logger.debug(f"API attempting to contact DB for add_ticket with ticket_id:{ticket_id} - creator_id:{creator_id} - channel_id:{channel_id} - status:{status}") try: - self.cursor.execute("INSERT INTO tickets (id, creator_id, channel_id, status) VALUES (%s, %s, %s, %s)", (ticket_id, creator_id, channel_id, status)) + self.cursor.execute("INSERT INTO tickets (id, ticket_id, creator_id, channel_id, status) VALUES (%s, %s, %s, %s, %s)", (None, ticket_id, creator_id, channel_id, status)) + self.conn.commit() return {"status": "ok", "message": "New ticket added successfully"} except OperationalError as err: logger.error(f"Error adding new ticket: {err}") + self.conn.rollback() return {"status": "error", "message": str(err)} def update_ticket_status(self, ticket_id: int, status: str): logger.debug(f"API attempting to contact DB for update_ticket_status with ticket_id:{ticket_id} - status:{status}") try: self.cursor.execute("UPDATE tickets SET status = %s WHERE ticket_id = %s", (status, ticket_id)) + self.conn.commit() return {"status": "ok", "message": "Ticket status updated successfully"} except OperationalError as err: logger.error(f"Error updating ticket status: {err}") + self.conn.rollback() return {"status": "error", "message": str(err)} def delete_ticket(self, ticket_id: int): logger.debug(f"API attempting to contact DB for delete_ticket with ticket_id:{ticket_id}") try: self.cursor.execute("DELETE FROM tickets WHERE ticket_id = %s", (ticket_id,)) + self.conn.commit() return {"status": "ok", "message": f"Ticket with ID {ticket_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting ticket: {err}") + self.conn.rollback() return {"status": "error", "message": str(err)} \ No newline at end of file From 418cc11bf1f9d578265aff96d902dce429f95367 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 12:12:12 -0400 Subject: [PATCH 45/60] ssss --- src/api/core/db_helper.py | 9 +++++++++ src/bot/cogs/features/ticket.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index 29c9910..e540c4d 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -337,4 +337,13 @@ def delete_ticket(self, ticket_id: int): self.conn.rollback() return {"status": "error", "message": str(err)} + def get_open_tickets(self): + logger.debug("API attempting to contact DB for get_open_tickets...") + try: + self.cursor.execute("SELECT * FROM tickets WHERE status = 'open'") + result = self.cursor.fetchall() + return {"status": "ok", "tickets": result} + except OperationalError as err: + logger.error(f"Error fetching open tickets: {err}") + return {"status": "error", "message": str(err)} \ No newline at end of file diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index b01eacf..2db488d 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -55,7 +55,7 @@ async def on_submit(self, interaction: discord.Interaction): await thread.add_user(interaction.user) # type: ignore await thread.send( - f'<@&{staff_role}>\n ## {interaction.user.mention} has created a ticket\n' + f'<@&{staff_role}>\n## {interaction.user.mention} has created a ticket\n' f'** Type: ** {option}\n' f'{self.description.value}' f'\n\n _ please provide any additional information here and our staff will assist you as soon as possible. _' From 36f61993fbbcd606b9458106b3ca518a22aee06b Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 12:25:27 -0400 Subject: [PATCH 46/60] forgot to post method --- src/api/routes/tickets.py | 3 ++- src/bot/core/api_helper.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api/routes/tickets.py b/src/api/routes/tickets.py index 90a2f6e..3cab1fd 100644 --- a/src/api/routes/tickets.py +++ b/src/api/routes/tickets.py @@ -51,8 +51,9 @@ def add_ticket(): """ if request.method == 'POST': data = request.json + try: - result = eos.db.add_ticket(data) + result = eos.db.add_ticket(data["ticket_id"], data["creator_id"], data["channel_id"], data.get("status", "open")) return jsonify(result), 201 except Exception as e: logger.error(f"Error adding ticket to database: {e}") diff --git a/src/bot/core/api_helper.py b/src/bot/core/api_helper.py index 3d73c12..2839ae7 100644 --- a/src/bot/core/api_helper.py +++ b/src/bot/core/api_helper.py @@ -178,12 +178,13 @@ def get_ticket(self, ticket_id=None): def add_ticket(self,ticket_id, user_id, channel_id): """Adds a new ticket to the database""" - logger.debug(f"Bot called the add_ticket endpoint. User ID: {user_id} - Channel ID: {channel_id}") data = { 'ticket_id': ticket_id, 'creator_id': user_id, 'channel_id': channel_id, } + logger.debug(f"Bot called the add_ticket endpoint. sending data: {data}") + return requests.post(f"{self.api}/tickets", json=data).json() def update_ticket_status(self, ticket_id, new_status): From e4dbfda53d149b2686bd813203fbe0a1954a38ec Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 12:35:22 -0400 Subject: [PATCH 47/60] renamed and fixed errors. UN --- src/api/core/db_helper.py | 46 +++++++++++++++++++++++++------------- src/api/routes/tickets.py | 24 ++++++++++---------- src/bot/core/api_helper.py | 24 ++++++++++---------- 3 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index e540c4d..a728636 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -270,12 +270,12 @@ def get_top_10(self): ## Ticket ## ################## - def get_ticket(self, ticket_id: int): - """Fetches a specific ticket from the database by its ID.""" + def get_ticket(self, thread_id: int): + """Fetches a specific ticket from the database by its thread ID.""" logger.debug("API attempting to contact DB for get_ticket...") try: - self.cursor.execute("SELECT * FROM tickets where ticket_id = %s", (ticket_id,)) + self.cursor.execute("SELECT * FROM tickets where thread_id = %s", (thread_id,)) result = self.cursor.fetchone() return {"status": "ok", "ticket": result} except OperationalError as err: @@ -294,31 +294,47 @@ def get_tickets(self): logger.error(f"Error fetching tickets: {err}") return {"status": "error", "message": str(err)} - def add_ticket(self, ticket_id: int, creator_id: int, channel_id: int, status: str = 'open'): + def add_ticket(self, thread_id: int, creator_id: int, channel_id: int, status: str = 'open'): """ Add a new ticket to the database. Args: - - ticket_id: The unique ID of the ticket (e.g., thread ID). + - thread_id: The unique ID of the ticket (e.g., thread ID). - creator_id: The Discord ID of the user who created the ticket. - channel_id: The Discord ID of the channel associated with the ticket. - status: The current status of the ticket (e.g., 'open', 'closed') """ - logger.debug(f"API attempting to contact DB for add_ticket with ticket_id:{ticket_id} - creator_id:{creator_id} - channel_id:{channel_id} - status:{status}") + logger.debug(f"API attempting to contact DB for add_ticket with thread_id:{thread_id} - creator_id:{creator_id} - channel_id:{channel_id} - status:{status}") try: - self.cursor.execute("INSERT INTO tickets (id, ticket_id, creator_id, channel_id, status) VALUES (%s, %s, %s, %s, %s)", (None, ticket_id, creator_id, channel_id, status)) + self.cursor.execute( + """ + INSERT INTO tickets ( + thread_id, + creator_id, + channel_id, + status + ) + VALUES (%s, %s, %s, %s)""", + ( + thread_id, + creator_id, + channel_id, + status + ) + ) + self.conn.commit() return {"status": "ok", "message": "New ticket added successfully"} except OperationalError as err: logger.error(f"Error adding new ticket: {err}") self.conn.rollback() return {"status": "error", "message": str(err)} - - def update_ticket_status(self, ticket_id: int, status: str): - logger.debug(f"API attempting to contact DB for update_ticket_status with ticket_id:{ticket_id} - status:{status}") + + def update_ticket_status(self, thread_id: int, status: str): + logger.debug(f"API attempting to contact DB for update_ticket_status with thread_id:{thread_id} - status:{status}") try: - self.cursor.execute("UPDATE tickets SET status = %s WHERE ticket_id = %s", (status, ticket_id)) + self.cursor.execute("UPDATE tickets SET status = %s WHERE thread_id = %s", (status, thread_id)) self.conn.commit() return {"status": "ok", "message": "Ticket status updated successfully"} except OperationalError as err: @@ -326,12 +342,12 @@ def update_ticket_status(self, ticket_id: int, status: str): self.conn.rollback() return {"status": "error", "message": str(err)} - def delete_ticket(self, ticket_id: int): - logger.debug(f"API attempting to contact DB for delete_ticket with ticket_id:{ticket_id}") + def delete_ticket(self, thread_id: int): + logger.debug(f"API attempting to contact DB for delete_ticket with thread_id:{thread_id}") try: - self.cursor.execute("DELETE FROM tickets WHERE ticket_id = %s", (ticket_id,)) + self.cursor.execute("DELETE FROM tickets WHERE thread_id = %s", (thread_id,)) self.conn.commit() - return {"status": "ok", "message": f"Ticket with ID {ticket_id} deleted successfully"} + return {"status": "ok", "message": f"Ticket with thread ID {thread_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting ticket: {err}") self.conn.rollback() diff --git a/src/api/routes/tickets.py b/src/api/routes/tickets.py index 3cab1fd..46a0d68 100644 --- a/src/api/routes/tickets.py +++ b/src/api/routes/tickets.py @@ -7,36 +7,36 @@ tickets = Blueprint('tickets', __name__) @tickets.route('/tickets', methods=['GET']) -@tickets.route('/tickets/', methods=['GET']) -def get_ticket(ticket_id=None): +@tickets.route('/tickets/', methods=['GET']) +def get_ticket(thread_id=None): """ Retrieve tickets from the database. - :param ticket_id: Optional integer ID of a specific ticket + :param thread_id: Optional integer ID of a specific ticket :return: JSON response with ticket(s) """ try: - if ticket_id is None: + if thread_id is None: # Retrieve all tickets result = eos.db.get_tickets() else: # Retrieve a single ticket - result = eos.db.get_ticket(ticket_id) + result = eos.db.get_ticket(thread_id) except Exception as e: logger.error(f"Error retrieving ticket(s) from database: {e}") return jsonify({'status': 'error', 'message': 'An error occurred while retrieving tickets.'}), 500 return jsonify(result), 200 -@tickets.route('/tickets/', methods=['PUT']) -def update_ticket(ticket_id): +@tickets.route('/tickets/', methods=['PUT']) +def update_ticket(thread_id): """ Update an existing ticket in the database. """ if request.method == 'PUT': data = request.json try: - result = eos.db.update_ticket_status(int(ticket_id), data['status']) + result = eos.db.update_ticket_status(int(thread_id), data['status']) return jsonify(result), 200 except Exception as e: logger.error(f"Error updating ticket in database: {e}") @@ -53,7 +53,7 @@ def add_ticket(): data = request.json try: - result = eos.db.add_ticket(data["ticket_id"], data["creator_id"], data["channel_id"], data.get("status", "open")) + result = eos.db.add_ticket(data["thread_id"], data["creator_id"], data["channel_id"], data.get("status", "open")) return jsonify(result), 201 except Exception as e: logger.error(f"Error adding ticket to database: {e}") @@ -61,14 +61,14 @@ def add_ticket(): return jsonify({'message': 'improper request method'}), 405 -@tickets.route('/tickets/', methods=['DELETE']) -def delete_ticket(ticket_id): +@tickets.route('/tickets/', methods=['DELETE']) +def delete_ticket(thread_id): """ Delete a specific ticket from the database. """ if request.method == 'DELETE': try: - result = eos.db.delete_ticket(ticket_id) + result = eos.db.delete_ticket(thread_id) return jsonify(result), 200 except Exception as e: logger.error(f"Error deleting ticket from database: {e}") diff --git a/src/bot/core/api_helper.py b/src/bot/core/api_helper.py index 2839ae7..904b7e1 100644 --- a/src/bot/core/api_helper.py +++ b/src/bot/core/api_helper.py @@ -168,18 +168,18 @@ def top_10(self): ## Ticket ## ################## - def get_ticket(self, ticket_id=None): + def get_ticket(self, thread_id=None): """Retrieves ticket(s) from the database""" - logger.debug(f"Bot called the get_ticket endpoint. Ticket ID: {ticket_id if ticket_id else 'All tickets'}") - if ticket_id is None: + logger.debug(f"Bot called the get_ticket endpoint. Thread ID: {thread_id if thread_id else 'All tickets'}") + if thread_id is None: return requests.get(f"{self.api}/tickets").json() else: - return requests.get(f"{self.api}/tickets/{ticket_id}").json() + return requests.get(f"{self.api}/tickets/{thread_id}").json() - def add_ticket(self,ticket_id, user_id, channel_id): + def add_ticket(self,thread_id, user_id, channel_id): """Adds a new ticket to the database""" data = { - 'ticket_id': ticket_id, + 'thread_id': thread_id, 'creator_id': user_id, 'channel_id': channel_id, } @@ -187,17 +187,17 @@ def add_ticket(self,ticket_id, user_id, channel_id): return requests.post(f"{self.api}/tickets", json=data).json() - def update_ticket_status(self, ticket_id, new_status): + def update_ticket_status(self, thread_id, new_status): """Updates the status of an existing ticket in the database""" - logger.debug(f"Bot called the update_ticket_status endpoint. Ticket ID: {ticket_id} - New Status: {new_status}") + logger.debug(f"Bot called the update_ticket_status endpoint. Thread ID: {thread_id} - New Status: {new_status}") data = { 'status': new_status } - return requests.put(f"{self.api}/tickets/{ticket_id}", json=data).json() + return requests.put(f"{self.api}/tickets/{thread_id}", json=data).json() - def delete_ticket(self, ticket_id): + def delete_ticket(self, thread_id): """Deletes a ticket from the database""" - logger.debug(f"Bot called the delete_ticket endpoint. Ticket ID: {ticket_id}") - return requests.delete(f"{self.api}/tickets/{ticket_id}").json() + logger.debug(f"Bot called the delete_ticket endpoint. Thread ID: {thread_id}") + return requests.delete(f"{self.api}/tickets/{thread_id}").json() \ No newline at end of file From d141e33be32a5d2d15d2a871a95e9d224102fe84 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 12:49:57 -0400 Subject: [PATCH 48/60] defer --- src/bot/cogs/features/ticket.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index 2db488d..9324c14 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -65,9 +65,16 @@ async def on_submit(self, interaction: discord.Interaction): except Exception as e: logger.error(f"Error adding ticket to database: {e}") await thread.send("There was an error saving the ticket to the database") + try: + await interaction.response.send_message(content=f"Your ticket has been created! {thread.jump_url}", ephemeral=True) + except Exception as e: + await interaction.channel.send(content=f"Your ticket has been created! {thread.jump_url}") + logger.error(f"Error sending followup message: {e}") + + async def on_error(self, interaction: discord.Interaction, error: Exception): + await interaction.followup.send("An error occurred while creating your ticket. Please try again later.", ephemeral=True) - await interaction.followup.send(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) - + logger.error(f"Error in TicketReasonModal: {error} \nTraceback: {error.__traceback__}", exc_info=True) class TicketDropdown(discord.ui.Select): def __init__(self, bot): From 2c0ecec9c26a05f8ea49e035b9574e13739aae56 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 13:08:33 -0400 Subject: [PATCH 49/60] added close_thread --- src/bot/cogs/features/ticket.py | 39 ++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index 9324c14..a58d461 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -106,17 +106,14 @@ def __init__(self, bot): -class AddTicketModal(commands.Cog): +class TicketManager(commands.Cog): """ This is the slash command that sends our UI element. """ def __init__(self, bot): self.bot = bot - ticket_channel = self.bot.api.get_one_setting('5') - - if ticket_channel is None or ticket_channel['setting'][2] == "0": logger.warning("Ticket Channel not set in db. Ticket commands will not work until this is set.") raise RuntimeError("Ticket Channel not set in db. Ticket commands will not work until this is set.") @@ -133,8 +130,40 @@ async def ticket(self, interaction: discord.Interaction): await interaction.response.send_message("creating ticket...", view=TicketView(self.bot), ephemeral=True) + @app_commands.command(description="Close a ticket in the current channel.") + async def close_ticket(self, interaction: discord.Interaction): + """ + Closes the ticket in the current channel. + """ + logger.info("%s used the %s command.", interaction.user.name, interaction.command.name) # type: ignore + thread = interaction.channel + + + if str(thread.parent_id) != self.ticket_channel: #type: ignore + await interaction.response.send_message("This command can only be used in a ticket thread.", ephemeral=True) + return + + + try: + self.bot.api.close_ticket(interaction.channel_id) + data = self.bot.api.get_ticket(thread.id) # type: ignore + if data is None or data['status'] != 'ok': + logger.warning(f"Ticket data not found for channel {thread.id}. Cannot close ticket.") + + try: + close = self.bot.api.update_ticket_status(thread.id, "closed") # type: ignore + if close is None or close['status'] != 'ok': + logger.warning(f"Failed to update ticket status to closed for channel {thread.id}.") + except Exception as e: + logger.error(f"Error updating ticket status to closed for channel {thread.id}: {e}") + + await interaction.response.send_message("This thread is unknown to the DB", ephemeral=True) + + except Exception as e: + logger.error(f"Error closing ticket: {e}") + await interaction.response.send_message("There was an error closing the ticket. Please try again later.", ephemeral=True) async def setup(bot: commands.Bot) -> None: """boink""" - await bot.add_cog(AddTicketModal(bot)) + await bot.add_cog(TicketManager(bot)) From da93abcd8c084209b417187b838ab0ccee788c50 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 13:13:18 -0400 Subject: [PATCH 50/60] name_close --- src/bot/cogs/features/ticket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index a58d461..fd6debb 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -130,7 +130,7 @@ async def ticket(self, interaction: discord.Interaction): await interaction.response.send_message("creating ticket...", view=TicketView(self.bot), ephemeral=True) - @app_commands.command(description="Close a ticket in the current channel.") + @app_commands.command(name="close_ticket", description="Close a ticket in the current channel.") async def close_ticket(self, interaction: discord.Interaction): """ Closes the ticket in the current channel. From c243bdacf5725bb9919cbeab5c6f95e5bf5a14ea Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 15:31:01 -0400 Subject: [PATCH 51/60] added close_ticket api --- src/api/routes/tickets.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/api/routes/tickets.py b/src/api/routes/tickets.py index 46a0d68..a4820d5 100644 --- a/src/api/routes/tickets.py +++ b/src/api/routes/tickets.py @@ -44,6 +44,21 @@ def update_ticket(thread_id): return jsonify({'message': 'improper request method'}), 405 +@tickets.route('/tickets/', methods=['PATCH']) +def close_ticket(thread_id): + """ + Close a specific ticket in the database. + """ + if request.method == 'PATCH': + try: + result = eos.db.update_ticket_status(thread_id, "closed") + return jsonify(result), 200 + except Exception as e: + logger.error(f"Error closing ticket in database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while closing the ticket.'}), 500 + + return jsonify({'message': 'improper request method'}), 405 + @tickets.route('/tickets', methods=['POST']) def add_ticket(): """ From 62ea69e1a1e347fe09a05c4a3396debe29e5c9e7 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 15:35:09 -0400 Subject: [PATCH 52/60] added db.close_ticke --- src/api/core/db_helper.py | 17 +++++++++++++++++ src/bot/cogs/features/ticket.py | 1 - 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index a728636..de4fa15 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -331,6 +331,23 @@ def add_ticket(self, thread_id: int, creator_id: int, channel_id: int, status: s self.conn.rollback() return {"status": "error", "message": str(err)} + def close_ticket(self, thread_id: int): + """ + Close a specific ticket in the database by updating its status to 'closed'. + Args: + - thread_id: The unique ID of the ticket (e.g., thread ID) to be closed. + """ + + logger.debug(f"API attempting to contact DB for close_ticket with thread_id:{thread_id}") + try: + self.cursor.execute("UPDATE tickets SET status = 'closed' WHERE thread_id = %s", (thread_id,)) + self.conn.commit() + return {"status": "ok", "message": f"Ticket with thread ID {thread_id} closed successfully"} + except OperationalError as err: + logger.error(f"Error closing ticket: {err}") + self.conn.rollback() + return {"status": "error", "message": str(err)} + def update_ticket_status(self, thread_id: int, status: str): logger.debug(f"API attempting to contact DB for update_ticket_status with thread_id:{thread_id} - status:{status}") try: diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index fd6debb..d19592c 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -145,7 +145,6 @@ async def close_ticket(self, interaction: discord.Interaction): try: - self.bot.api.close_ticket(interaction.channel_id) data = self.bot.api.get_ticket(thread.id) # type: ignore if data is None or data['status'] != 'ok': logger.warning(f"Ticket data not found for channel {thread.id}. Cannot close ticket.") From 4fcfce6714fba3cf9f332dd247b454d84adda37a Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 15:45:34 -0400 Subject: [PATCH 53/60] proper closing --- src/bot/cogs/features/ticket.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index d19592c..f83e9e7 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -153,10 +153,19 @@ async def close_ticket(self, interaction: discord.Interaction): close = self.bot.api.update_ticket_status(thread.id, "closed") # type: ignore if close is None or close['status'] != 'ok': logger.warning(f"Failed to update ticket status to closed for channel {thread.id}.") + else: + await interaction.response.send_message("This thread is unknown to the DB", ephemeral=True) + try: + await thread.send(f"This ticket has been closed by {interaction.user.mention}.") + await thread.edit(archived=True, locked=True) # type: ignore + except Exception as e: + logger.error(f"Error archiving and locking thread {thread.id}: {e}") + await interaction.response.send_message("Ticket status updated to closed, but there was an error archiving the thread.", ephemeral=True) + + except Exception as e: logger.error(f"Error updating ticket status to closed for channel {thread.id}: {e}") - - await interaction.response.send_message("This thread is unknown to the DB", ephemeral=True) + await interaction.response.send_message("This thread is unknown to the DB", ephemeral=True) except Exception as e: logger.error(f"Error closing ticket: {e}") From 17b5265e575f5279e4356b89c7199ce7071b4f73 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 15:52:26 -0400 Subject: [PATCH 54/60] UI improvment --- src/bot/cogs/features/ticket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index f83e9e7..65573d6 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -57,7 +57,7 @@ async def on_submit(self, interaction: discord.Interaction): await thread.send( f'<@&{staff_role}>\n## {interaction.user.mention} has created a ticket\n' f'** Type: ** {option}\n' - f'{self.description.value}' + f'"{self.description.value}" - {interaction.user.nick}\n\n' f'\n\n _ please provide any additional information here and our staff will assist you as soon as possible. _' ) try: From f0d3634d2eaff59172497cef05d4b6ab49b1d501 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 15:58:15 -0400 Subject: [PATCH 55/60] gc --- src/bot/cogs/features/ticket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index 65573d6..6f299e9 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -153,8 +153,8 @@ async def close_ticket(self, interaction: discord.Interaction): close = self.bot.api.update_ticket_status(thread.id, "closed") # type: ignore if close is None or close['status'] != 'ok': logger.warning(f"Failed to update ticket status to closed for channel {thread.id}.") - else: await interaction.response.send_message("This thread is unknown to the DB", ephemeral=True) + else: try: await thread.send(f"This ticket has been closed by {interaction.user.mention}.") await thread.edit(archived=True, locked=True) # type: ignore From 7348d73091bea6a74ba86272c2857d2645e67ccd Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Thu, 21 May 2026 08:33:18 -0400 Subject: [PATCH 56/60] Ticket Rework for users Reworked the ticket system to allow for database to track open threads. Planned Features: Auto Delete Show Open Tickets There is a few bugs with it currently. Im tracking most of them --- .gitignore | 4 + src/api/api.py | 2 + src/api/core/db_helper.py | 80 +++++++++++++--- src/api/routes/roles.py | 1 + src/api/routes/tickets.py | 92 +++++++++++++++++++ src/bot/cogs/features/ticket.py | 158 +++++++++++++++++++------------- src/bot/core/api_helper.py | 39 ++++++++ 7 files changed, 296 insertions(+), 80 deletions(-) create mode 100644 src/api/routes/tickets.py diff --git a/.gitignore b/.gitignore index 6233bb3..ae99898 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ /.idea/ .env /src/db/postgres-data/ +.gitignore +.vscode/ # Byte-compiled / optimized / DLL files __pycache__/ @@ -157,3 +159,5 @@ dmypy.json cython_debug/ /.obsidian/ +src/bot/cogs/moderation/admin_detect_spam_message.py +.gitignore diff --git a/src/api/api.py b/src/api/api.py index b7590bc..4ee2ce1 100644 --- a/src/api/api.py +++ b/src/api/api.py @@ -10,6 +10,7 @@ from routes.settings import settings from routes.points import points from routes.roles import role +from routes.tickets import tickets from core.db_helper import DB @@ -28,6 +29,7 @@ app.register_blueprint(settings) app.register_blueprint(points) app.register_blueprint(role) +app.register_blueprint(tickets) # Error handlers @app.errorhandler(Exception) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index 93917f7..de4fa15 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -196,9 +196,11 @@ def delete_role(self, role_id): logger.debug(f"API attempting to contact DB for delete_role with role_ID:{role_id}") try: self.cursor.execute("DELETE FROM roles WHERE id = %s", (role_id,)) + self.conn.commit() return {"status": "ok", "message": f"role with ID {role_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting role: {err}") + self.conn.rollback() return {"status": "error", "message": str(err)} ################## @@ -268,12 +270,12 @@ def get_top_10(self): ## Ticket ## ################## - def get_ticket(self, ticket_id: int): - """Fetches a specific ticket from the database by its ID.""" + def get_ticket(self, thread_id: int): + """Fetches a specific ticket from the database by its thread ID.""" logger.debug("API attempting to contact DB for get_ticket...") try: - self.cursor.execute("SELECT * FROM tickets where id = %s", (ticket_id,)) + self.cursor.execute("SELECT * FROM tickets where thread_id = %s", (thread_id,)) result = self.cursor.fetchone() return {"status": "ok", "ticket": result} except OperationalError as err: @@ -292,41 +294,89 @@ def get_tickets(self): logger.error(f"Error fetching tickets: {err}") return {"status": "error", "message": str(err)} - def add_ticket(self, ticket_id: int, creator_id: int, channel_id: int, status: str = 'open'): + def add_ticket(self, thread_id: int, creator_id: int, channel_id: int, status: str = 'open'): """ Add a new ticket to the database. Args: - - ticket_id: The unique ID of the ticket (e.g., thread ID). + - thread_id: The unique ID of the ticket (e.g., thread ID). - creator_id: The Discord ID of the user who created the ticket. - channel_id: The Discord ID of the channel associated with the ticket. - status: The current status of the ticket (e.g., 'open', 'closed') """ - logger.debug(f"API attempting to contact DB for add_ticket with ticket_id:{ticket_id} - creator_id:{creator_id} - channel_id:{channel_id} - status:{status}") + logger.debug(f"API attempting to contact DB for add_ticket with thread_id:{thread_id} - creator_id:{creator_id} - channel_id:{channel_id} - status:{status}") try: - self.cursor.execute("INSERT INTO tickets (id, creator_id, channel_id, status) VALUES (%s, %s, %s, %s)", (ticket_id, creator_id, channel_id, status)) + self.cursor.execute( + """ + INSERT INTO tickets ( + thread_id, + creator_id, + channel_id, + status + ) + VALUES (%s, %s, %s, %s)""", + ( + thread_id, + creator_id, + channel_id, + status + ) + ) + + self.conn.commit() return {"status": "ok", "message": "New ticket added successfully"} except OperationalError as err: logger.error(f"Error adding new ticket: {err}") + self.conn.rollback() return {"status": "error", "message": str(err)} - - def update_ticket_status(self, ticket_id: int, status: str): - logger.debug(f"API attempting to contact DB for update_ticket_status with ticket_id:{ticket_id} - status:{status}") + + def close_ticket(self, thread_id: int): + """ + Close a specific ticket in the database by updating its status to 'closed'. + Args: + - thread_id: The unique ID of the ticket (e.g., thread ID) to be closed. + """ + + logger.debug(f"API attempting to contact DB for close_ticket with thread_id:{thread_id}") try: - self.cursor.execute("UPDATE tickets SET status = %s WHERE id = %s", (status, ticket_id)) + self.cursor.execute("UPDATE tickets SET status = 'closed' WHERE thread_id = %s", (thread_id,)) + self.conn.commit() + return {"status": "ok", "message": f"Ticket with thread ID {thread_id} closed successfully"} + except OperationalError as err: + logger.error(f"Error closing ticket: {err}") + self.conn.rollback() + return {"status": "error", "message": str(err)} + + def update_ticket_status(self, thread_id: int, status: str): + logger.debug(f"API attempting to contact DB for update_ticket_status with thread_id:{thread_id} - status:{status}") + try: + self.cursor.execute("UPDATE tickets SET status = %s WHERE thread_id = %s", (status, thread_id)) + self.conn.commit() return {"status": "ok", "message": "Ticket status updated successfully"} except OperationalError as err: logger.error(f"Error updating ticket status: {err}") + self.conn.rollback() return {"status": "error", "message": str(err)} - def delete_ticket(self, ticket_id: int): - logger.debug(f"API attempting to contact DB for delete_ticket with ticket_id:{ticket_id}") + def delete_ticket(self, thread_id: int): + logger.debug(f"API attempting to contact DB for delete_ticket with thread_id:{thread_id}") try: - self.cursor.execute("DELETE FROM tickets WHERE id = %s", (ticket_id,)) - return {"status": "ok", "message": f"Ticket with ID {ticket_id} deleted successfully"} + self.cursor.execute("DELETE FROM tickets WHERE thread_id = %s", (thread_id,)) + self.conn.commit() + return {"status": "ok", "message": f"Ticket with thread ID {thread_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting ticket: {err}") + self.conn.rollback() return {"status": "error", "message": str(err)} + def get_open_tickets(self): + logger.debug("API attempting to contact DB for get_open_tickets...") + try: + self.cursor.execute("SELECT * FROM tickets WHERE status = 'open'") + result = self.cursor.fetchall() + return {"status": "ok", "tickets": result} + except OperationalError as err: + logger.error(f"Error fetching open tickets: {err}") + return {"status": "error", "message": str(err)} \ No newline at end of file diff --git a/src/api/routes/roles.py b/src/api/routes/roles.py index ac37470..7934556 100644 --- a/src/api/routes/roles.py +++ b/src/api/routes/roles.py @@ -61,3 +61,4 @@ def delete_role(role_id): return jsonify(result), 200 return jsonify({'message': 'improper request method'}), 405 + diff --git a/src/api/routes/tickets.py b/src/api/routes/tickets.py new file mode 100644 index 0000000..a4820d5 --- /dev/null +++ b/src/api/routes/tickets.py @@ -0,0 +1,92 @@ +from flask import Blueprint, jsonify, request +from flask import current_app as eos +import logging + +logger = logging.getLogger(__name__) + +tickets = Blueprint('tickets', __name__) + +@tickets.route('/tickets', methods=['GET']) +@tickets.route('/tickets/', methods=['GET']) +def get_ticket(thread_id=None): + """ + Retrieve tickets from the database. + + :param thread_id: Optional integer ID of a specific ticket + :return: JSON response with ticket(s) + """ + try: + if thread_id is None: + # Retrieve all tickets + result = eos.db.get_tickets() + else: + # Retrieve a single ticket + result = eos.db.get_ticket(thread_id) + except Exception as e: + logger.error(f"Error retrieving ticket(s) from database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while retrieving tickets.'}), 500 + + return jsonify(result), 200 + +@tickets.route('/tickets/', methods=['PUT']) +def update_ticket(thread_id): + """ + Update an existing ticket in the database. + """ + if request.method == 'PUT': + data = request.json + try: + result = eos.db.update_ticket_status(int(thread_id), data['status']) + return jsonify(result), 200 + except Exception as e: + logger.error(f"Error updating ticket in database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while updating the ticket.'}), 500 + + return jsonify({'message': 'improper request method'}), 405 + +@tickets.route('/tickets/', methods=['PATCH']) +def close_ticket(thread_id): + """ + Close a specific ticket in the database. + """ + if request.method == 'PATCH': + try: + result = eos.db.update_ticket_status(thread_id, "closed") + return jsonify(result), 200 + except Exception as e: + logger.error(f"Error closing ticket in database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while closing the ticket.'}), 500 + + return jsonify({'message': 'improper request method'}), 405 + +@tickets.route('/tickets', methods=['POST']) +def add_ticket(): + """ + Add a new ticket to the database. + """ + if request.method == 'POST': + data = request.json + + try: + result = eos.db.add_ticket(data["thread_id"], data["creator_id"], data["channel_id"], data.get("status", "open")) + return jsonify(result), 201 + except Exception as e: + logger.error(f"Error adding ticket to database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while adding the ticket.'}), 500 + + return jsonify({'message': 'improper request method'}), 405 + +@tickets.route('/tickets/', methods=['DELETE']) +def delete_ticket(thread_id): + """ + Delete a specific ticket from the database. + """ + if request.method == 'DELETE': + try: + result = eos.db.delete_ticket(thread_id) + return jsonify(result), 200 + except Exception as e: + logger.error(f"Error deleting ticket from database: {e}") + return jsonify({'status': 'error', 'message': 'An error occurred while deleting the ticket.'}), 500 + + return jsonify({'message': 'improper request method'}), 405 diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index 33248b2..6f299e9 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -9,25 +9,31 @@ logger = logging.getLogger(__name__) -class TicketReasonModal(discord.ui.Modal, title="Create a Ticket"): +class TicketReasonModal(discord.ui.Modal, title="Create Ticket"): """ This modal appears when the user selects a ticket type. It allows them to provide a reason or description for the ticket. """ + description = discord.ui.TextInput( - label="Please describe the issue or reason for the ticket. ", - style=discord.TextStyle.paragraph, - required=True, max_length=1000 + label="Please describe The Issue...", + style=discord.TextStyle.short, + required=True, + max_length=1000 ) - def __init__(self, bot, ticket_type: str): + + + def __init__(self, bot, selected_option: str): super().__init__() - self.ticket_type = ticket_type self.bot = bot + self.selected_option = selected_option async def on_submit(self, interaction: discord.Interaction): guild = interaction.guild channel = self.bot.api.get_one_setting('5') + staff_role = self.bot.api.get_one_role('3') + staff_role = staff_role['roles'][2] if staff_role['status'] == 'ok' else None if channel is None or channel['setting'][2] == "0": logger.warning("Ticket Channel not set in db. Cannot create ticket.") @@ -36,92 +42,78 @@ async def on_submit(self, interaction: discord.Interaction): else: channel = guild.get_channel(int(channel['setting'][2])) # type: ignore - + option = self.selected_option if self.selected_option else "None Selected" - thread_name = f"{self.ticket_type}-{interaction.user.name}".lower().replace(" ", "-") + thread_name = f"{option}-{interaction.user.name}".lower().replace(" ", "-") thread = await channel.create_thread( # type: ignore name=thread_name, type=discord.ChannelType.private_thread, - invitable=False + invitable=False ) await thread.add_user(interaction.user) # type: ignore await thread.send( - f'<@&{self.staff_role}> -- {interaction.user.mention} has created a ticket\n' - f'Type: {self.ticket_type}\n' - f'{self.description.value}' - f'\n\n please provide any additional information here and our staff will assist you as soon as possible.' + f'<@&{staff_role}>\n## {interaction.user.mention} has created a ticket\n' + f'** Type: ** {option}\n' + f'"{self.description.value}" - {interaction.user.nick}\n\n' + f'\n\n _ please provide any additional information here and our staff will assist you as soon as possible. _' ) + try: + self.bot.api.add_ticket(thread.id, interaction.user.id, channel.id) + except Exception as e: + logger.error(f"Error adding ticket to database: {e}") + await thread.send("There was an error saving the ticket to the database") + try: + await interaction.response.send_message(content=f"Your ticket has been created! {thread.jump_url}", ephemeral=True) + except Exception as e: + await interaction.channel.send(content=f"Your ticket has been created! {thread.jump_url}") + logger.error(f"Error sending followup message: {e}") + + async def on_error(self, interaction: discord.Interaction, error: Exception): + await interaction.followup.send("An error occurred while creating your ticket. Please try again later.", ephemeral=True) + + logger.error(f"Error in TicketReasonModal: {error} \nTraceback: {error.__traceback__}", exc_info=True) - self.bot.db.create_ticket(interaction.user.id, thread.id, self.ticket_type, self.description.value) - - await interaction.response.send_message(f"Your ticket has been created! {thread.jump_url}", ephemeral=True) - - class TicketDropdown(discord.ui.Select): - """ - This is the dropdown that appears when the user clicks - the button to create a ticket. It allows them to select the - type of ticket they want to create. - """ def __init__(self, bot): self.bot = bot - + options = [ - discord.SelectOption( - label="Moderation", - description="Report users or moderation issues" - ), - discord.SelectOption( - label="Support", - description="General support ticket" - ), - discord.SelectOption( - label="Proposition", - description="Suggest an idea or partnership" - ), - discord.SelectOption( - label="Request", - description="Request something from staff" - ) + discord.SelectOption(label="Moderation"), + discord.SelectOption(label="Support"), + discord.SelectOption(label="Proposition"), + discord.SelectOption(label="Request"), ] - + super().__init__( - placeholder="Please select a ticket type", - min_values=1, - max_values=1, - options=options) - - async def callback(self, interaction: discord.Interaction): - selected = self.values[0] - await interaction.response.send_modal(TicketReasonModal(self.bot, selected)) + placeholder="Select ticket type", + options=options + ) + async def callback(self, interaction: discord.Interaction): + await interaction.response.send_modal( + TicketReasonModal(self.bot, self.values[0]) + ) - class TicketView(discord.ui.View): def __init__(self, bot): - super().__init__(timeout=None) + super().__init__(timeout=100) # View will timeout after 1.5 minutes self.bot = bot + self.add_item(TicketDropdown(bot)) -class AddTicketModal(commands.Cog): +class TicketManager(commands.Cog): """ This is the slash command that sends our UI element. """ def __init__(self, bot): self.bot = bot - ticket_channel = self.bot.api.get_one_setting('5') - - staff_role = self.bot.api.get_one_role('3') - - self.staff_role = staff_role if staff_role['status'] == 'ok' else None - if ticket_channel is None or ticket_channel['setting'][2] == "0": logger.warning("Ticket Channel not set in db. Ticket commands will not work until this is set.") raise RuntimeError("Ticket Channel not set in db. Ticket commands will not work until this is set.") @@ -135,15 +127,51 @@ async def ticket(self, interaction: discord.Interaction): A simple command with a view. """ logger.info("%s used the %s command.", interaction.user.name, interaction.command.name) # type: ignore - await interaction.response.defer() - await interaction.followup.send( - "Do you need help, or do you have a question for the Staff?", - view=TicketView(self.bot), - ephemeral=True, - ) + await interaction.response.send_message("creating ticket...", view=TicketView(self.bot), ephemeral=True) + @app_commands.command(name="close_ticket", description="Close a ticket in the current channel.") + async def close_ticket(self, interaction: discord.Interaction): + """ + Closes the ticket in the current channel. + """ + logger.info("%s used the %s command.", interaction.user.name, interaction.command.name) # type: ignore + thread = interaction.channel + + + if str(thread.parent_id) != self.ticket_channel: #type: ignore + await interaction.response.send_message("This command can only be used in a ticket thread.", ephemeral=True) + return + + + try: + data = self.bot.api.get_ticket(thread.id) # type: ignore + if data is None or data['status'] != 'ok': + logger.warning(f"Ticket data not found for channel {thread.id}. Cannot close ticket.") + + try: + close = self.bot.api.update_ticket_status(thread.id, "closed") # type: ignore + if close is None or close['status'] != 'ok': + logger.warning(f"Failed to update ticket status to closed for channel {thread.id}.") + await interaction.response.send_message("This thread is unknown to the DB", ephemeral=True) + else: + try: + await thread.send(f"This ticket has been closed by {interaction.user.mention}.") + await thread.edit(archived=True, locked=True) # type: ignore + except Exception as e: + logger.error(f"Error archiving and locking thread {thread.id}: {e}") + await interaction.response.send_message("Ticket status updated to closed, but there was an error archiving the thread.", ephemeral=True) + + + except Exception as e: + logger.error(f"Error updating ticket status to closed for channel {thread.id}: {e}") + await interaction.response.send_message("This thread is unknown to the DB", ephemeral=True) + + except Exception as e: + logger.error(f"Error closing ticket: {e}") + await interaction.response.send_message("There was an error closing the ticket. Please try again later.", ephemeral=True) + async def setup(bot: commands.Bot) -> None: """boink""" - await bot.add_cog(AddTicketModal(bot)) + await bot.add_cog(TicketManager(bot)) diff --git a/src/bot/core/api_helper.py b/src/bot/core/api_helper.py index 7b84620..904b7e1 100644 --- a/src/bot/core/api_helper.py +++ b/src/bot/core/api_helper.py @@ -162,3 +162,42 @@ def update_points(self, user_id, amount): def top_10(self): logger.debug("Bot called the top_10 endpoint.") return requests.get(f"{self.api}/points/top10").json() + + + ################## + ## Ticket ## + ################## + + def get_ticket(self, thread_id=None): + """Retrieves ticket(s) from the database""" + logger.debug(f"Bot called the get_ticket endpoint. Thread ID: {thread_id if thread_id else 'All tickets'}") + if thread_id is None: + return requests.get(f"{self.api}/tickets").json() + else: + return requests.get(f"{self.api}/tickets/{thread_id}").json() + + def add_ticket(self,thread_id, user_id, channel_id): + """Adds a new ticket to the database""" + data = { + 'thread_id': thread_id, + 'creator_id': user_id, + 'channel_id': channel_id, + } + logger.debug(f"Bot called the add_ticket endpoint. sending data: {data}") + + return requests.post(f"{self.api}/tickets", json=data).json() + + def update_ticket_status(self, thread_id, new_status): + """Updates the status of an existing ticket in the database""" + logger.debug(f"Bot called the update_ticket_status endpoint. Thread ID: {thread_id} - New Status: {new_status}") + data = { + 'status': new_status + } + return requests.put(f"{self.api}/tickets/{thread_id}", json=data).json() + + def delete_ticket(self, thread_id): + """Deletes a ticket from the database""" + logger.debug(f"Bot called the delete_ticket endpoint. Thread ID: {thread_id}") + return requests.delete(f"{self.api}/tickets/{thread_id}").json() + + \ No newline at end of file From 49181f473b6f49a1344542ecd3395de186516b42 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Fri, 22 May 2026 11:07:31 -0400 Subject: [PATCH 57/60] I keep changing the .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index ae99898..cc3879d 100644 --- a/.gitignore +++ b/.gitignore @@ -159,5 +159,3 @@ dmypy.json cython_debug/ /.obsidian/ -src/bot/cogs/moderation/admin_detect_spam_message.py -.gitignore From 4e378f3a937d7a2d8d12a58f2d08606f06ebc3e8 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Fri, 22 May 2026 12:51:57 -0400 Subject: [PATCH 58/60] Fixed requested review --- src/api/core/db_helper.py | 36 +++++++++---------- src/api/routes/healthchecks.py | 2 +- src/api/routes/logging.py | 4 +-- src/bot/cogs/admin/points.py | 4 +-- src/bot/cogs/features/ticket.py | 7 ++-- src/bot/cogs/logging/logging_member_ban.py | 3 +- src/bot/cogs/logging/logging_member_kick.py | 2 +- src/bot/cogs/logging/logging_member_leaves.py | 2 +- .../cogs/logging/logging_message_delete.py | 2 +- src/bot/cogs/logging/logging_message_edit.py | 2 +- src/bot/cogs/logging/logging_name_changes.py | 2 +- .../cogs/verification/verification_on_join.py | 3 +- 12 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/api/core/db_helper.py b/src/api/core/db_helper.py index de4fa15..cf04ba0 100644 --- a/src/api/core/db_helper.py +++ b/src/api/core/db_helper.py @@ -195,12 +195,10 @@ def add_role(self, name, value): def delete_role(self, role_id): logger.debug(f"API attempting to contact DB for delete_role with role_ID:{role_id}") try: - self.cursor.execute("DELETE FROM roles WHERE id = %s", (role_id,)) - self.conn.commit() + self.cursor.execute("DELETE FROM roles WHERE id = %s", (role_id,)) return {"status": "ok", "message": f"role with ID {role_id} deleted successfully"} except OperationalError as err: - logger.error(f"Error deleting role: {err}") - self.conn.rollback() + logger.error(f"Error deleting role: {err}") return {"status": "error", "message": str(err)} ################## @@ -220,12 +218,10 @@ def get_points_for_user(self, user_id): def update_points(self, user_id, value): try: - self.cursor.execute("UPDATE users SET points = points + %s WHERE discord_id = %s", (value, user_id)) - self.conn.commit() + self.cursor.execute("UPDATE users SET points = points + %s WHERE discord_id = %s", (value, user_id)) return {"status": "ok", "message": "points updated successfully"} except OperationalError as err: logger.error(f"Error updating points: {err}") - self.conn.rollback() return {"status": "error", "message": str(err)} def add_user_to_points(self, user_id): @@ -234,11 +230,11 @@ def add_user_to_points(self, user_id): "INSERT INTO users (discord_id, points) VALUES (%s, 0) ON CONFLICT (discord_id) DO NOTHING;" , (user_id,) ) - # self.conn.commit() + # return {"status": "ok", "message": "New user added to 'points' successfully"} except OperationalError as err: logger.error(f"Error adding new user: {err}") - # self.conn.rollback() + # return {"status": "error", "message": str(err)} def remove_user_from_points(self, user_id): @@ -246,13 +242,13 @@ def remove_user_from_points(self, user_id): self.cursor.execute("DELETE FROM users WHERE discord_id = %s", (user_id,)) affected_rows = self.cursor.rowcount if affected_rows > 0: - self.conn.commit() + return {"status": "ok", "message": f"User with ID: {user_id} deleted successfully"} else: return {"status": "not_found", "message": f"No user found with ID: {user_id}"} except OperationalError as err: logger.error(f"Error deleting user: {err}") - self.conn.rollback() + return {"status": "error", "message": str(err)} def get_top_10(self): @@ -262,7 +258,7 @@ def get_top_10(self): return {"status": "ok", "message": result} except OperationalError as err: logger.error(f"Error deleting user: {err}") - self.conn.rollback() + return {"status": "error", "message": str(err)} @@ -324,11 +320,11 @@ def add_ticket(self, thread_id: int, creator_id: int, channel_id: int, status: s ) ) - self.conn.commit() + return {"status": "ok", "message": "New ticket added successfully"} except OperationalError as err: logger.error(f"Error adding new ticket: {err}") - self.conn.rollback() + return {"status": "error", "message": str(err)} def close_ticket(self, thread_id: int): @@ -341,33 +337,33 @@ def close_ticket(self, thread_id: int): logger.debug(f"API attempting to contact DB for close_ticket with thread_id:{thread_id}") try: self.cursor.execute("UPDATE tickets SET status = 'closed' WHERE thread_id = %s", (thread_id,)) - self.conn.commit() + return {"status": "ok", "message": f"Ticket with thread ID {thread_id} closed successfully"} except OperationalError as err: logger.error(f"Error closing ticket: {err}") - self.conn.rollback() + return {"status": "error", "message": str(err)} def update_ticket_status(self, thread_id: int, status: str): logger.debug(f"API attempting to contact DB for update_ticket_status with thread_id:{thread_id} - status:{status}") try: self.cursor.execute("UPDATE tickets SET status = %s WHERE thread_id = %s", (status, thread_id)) - self.conn.commit() + return {"status": "ok", "message": "Ticket status updated successfully"} except OperationalError as err: logger.error(f"Error updating ticket status: {err}") - self.conn.rollback() + return {"status": "error", "message": str(err)} def delete_ticket(self, thread_id: int): logger.debug(f"API attempting to contact DB for delete_ticket with thread_id:{thread_id}") try: self.cursor.execute("DELETE FROM tickets WHERE thread_id = %s", (thread_id,)) - self.conn.commit() + return {"status": "ok", "message": f"Ticket with thread ID {thread_id} deleted successfully"} except OperationalError as err: logger.error(f"Error deleting ticket: {err}") - self.conn.rollback() + return {"status": "error", "message": str(err)} def get_open_tickets(self): diff --git a/src/api/routes/healthchecks.py b/src/api/routes/healthchecks.py index 9d6d12d..be59676 100644 --- a/src/api/routes/healthchecks.py +++ b/src/api/routes/healthchecks.py @@ -30,7 +30,7 @@ def database_health_check(): if request.method == 'GET': try: hc = eos.db.database_health_check() - return jsonify(hc, 200) + return jsonify(hc), 200 except TypeError as ded: return jsonify({"status": "unhealthy", "error": "DB unreachable"}), 404 diff --git a/src/api/routes/logging.py b/src/api/routes/logging.py index 8eeed05..4d26915 100644 --- a/src/api/routes/logging.py +++ b/src/api/routes/logging.py @@ -36,7 +36,7 @@ def get_log_setting(log_id=None): # """ # result = eos.db.get_log_settings() # -# return jsonify(result, 200) +# return jsonify(result), 200 @logs.route('/logging/', methods=['PUT']) def update_log_setting(log_id): @@ -58,7 +58,7 @@ def add_log_setting(): if request.method == 'POST': data = request.json result = eos.db.add_log_setting(data['name'], data['value']) - return jsonify(result, 201) + return jsonify(result), 201 return jsonify({'message': 'improper request method'}), 405 diff --git a/src/bot/cogs/admin/points.py b/src/bot/cogs/admin/points.py index feadeb1..5913897 100644 --- a/src/bot/cogs/admin/points.py +++ b/src/bot/cogs/admin/points.py @@ -186,13 +186,13 @@ async def on_command_error(self, ctx: commands.Context, error): await ctx.reply(embed=embed_info("Error!", "You must provide a required argument.", discord.Color.dark_gray())) elif isinstance(error, commands.MissingPermissions): logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") - await ctx.send('For one reason, or another, YOU cannot use this command.') + await ctx.reply('For one reason, or another, YOU cannot use this command.') @sync_users.error async def sync_users_command_error(self, ctx, error): if isinstance(error, commands.CheckFailure): logger.warning(f"{ctx.author.name} has attempted to use the {ctx.invoked_with} command, and was not allowed to do so.") - await ctx.send('For one reason, or another, YOU cannot use this command.') + await ctx.reply('For one reason, or another, YOU cannot use this command.') #@update_points.error #async def update_points_command_error(self, ctx, error): diff --git a/src/bot/cogs/features/ticket.py b/src/bot/cogs/features/ticket.py index 6f299e9..ba86660 100644 --- a/src/bot/cogs/features/ticket.py +++ b/src/bot/cogs/features/ticket.py @@ -57,7 +57,7 @@ async def on_submit(self, interaction: discord.Interaction): await thread.send( f'<@&{staff_role}>\n## {interaction.user.mention} has created a ticket\n' f'** Type: ** {option}\n' - f'"{self.description.value}" - {interaction.user.nick}\n\n' + f'"{self.description.value}" - {interaction.user.name if interaction.user.nick is None else interaction.user.nick}\n\n' f'\n\n _ please provide any additional information here and our staff will assist you as soon as possible. _' ) try: @@ -71,10 +71,7 @@ async def on_submit(self, interaction: discord.Interaction): await interaction.channel.send(content=f"Your ticket has been created! {thread.jump_url}") logger.error(f"Error sending followup message: {e}") - async def on_error(self, interaction: discord.Interaction, error: Exception): - await interaction.followup.send("An error occurred while creating your ticket. Please try again later.", ephemeral=True) - - logger.error(f"Error in TicketReasonModal: {error} \nTraceback: {error.__traceback__}", exc_info=True) + class TicketDropdown(discord.ui.Select): def __init__(self, bot): diff --git a/src/bot/cogs/logging/logging_member_ban.py b/src/bot/cogs/logging/logging_member_ban.py index 6bfebe5..3174232 100644 --- a/src/bot/cogs/logging/logging_member_ban.py +++ b/src/bot/cogs/logging/logging_member_ban.py @@ -39,7 +39,6 @@ def __init__(self, bot): raise RuntimeError("Failed to fetch verification role from API.") self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log - logger.info("LoggingBans cog initialized") @commands.Cog.listener() async def on_member_remove(self, member): @@ -59,7 +58,7 @@ async def on_member_remove(self, member): if self.mod_log["status"] == "ok": if self.mod_log["logging"][2] == "0": - logger.debug(f"log was triggered, but logging is disabled. API: {self.mod_log}") + logger.warning(f"log was triggered, but logging is disabled. API: {self.mod_log}") return logs_channel = await self.bot.fetch_channel(self.mod_log["logging"][2]) diff --git a/src/bot/cogs/logging/logging_member_kick.py b/src/bot/cogs/logging/logging_member_kick.py index 2a95f4d..8270501 100644 --- a/src/bot/cogs/logging/logging_member_kick.py +++ b/src/bot/cogs/logging/logging_member_kick.py @@ -63,7 +63,7 @@ async def on_member_remove(self, member): if self.mod_log["status"] == "ok": if self.mod_log["logging"][2] == "0": - logger.debug(f"log was triggered, but logging is disabled. API: {self.mod_log}") + logger.warning(f"log was triggered, but logging is disabled. API: {self.mod_log}") return logs_channel = await self.bot.fetch_channel(self.mod_log["logging"][2]) diff --git a/src/bot/cogs/logging/logging_member_leaves.py b/src/bot/cogs/logging/logging_member_leaves.py index 2a35e85..1661a0e 100644 --- a/src/bot/cogs/logging/logging_member_leaves.py +++ b/src/bot/cogs/logging/logging_member_leaves.py @@ -40,7 +40,7 @@ def __init__(self, bot): self.join_log = self.bot.api.get_one_log_setting("2") # Join_log - logger.info("LoggingLeaves cog initialized") + @commands.Cog.listener() async def on_member_remove(self, member): diff --git a/src/bot/cogs/logging/logging_message_delete.py b/src/bot/cogs/logging/logging_message_delete.py index 37181fd..c473eee 100644 --- a/src/bot/cogs/logging/logging_message_delete.py +++ b/src/bot/cogs/logging/logging_message_delete.py @@ -86,7 +86,7 @@ async def on_message_delete(self, message) -> None: If a mod deletes, take the audit log event. If a user deletes, handle it normally. """ - if message.author.guild.id != int(os.getenv("MASTER_GUILD", 0)) or \ + if message.author.guild.id != int(os.getenv("MASTER_GUILD")) or \ message.author.guild.id is None: logger.warning(">> on_message_delete fired, but not in master guild. Ignoring event.") return diff --git a/src/bot/cogs/logging/logging_message_edit.py b/src/bot/cogs/logging/logging_message_edit.py index 6af1642..9c773d1 100644 --- a/src/bot/cogs/logging/logging_message_edit.py +++ b/src/bot/cogs/logging/logging_message_edit.py @@ -58,7 +58,7 @@ def __init__(self, bot): self.chat_log = self.bot.api.get_one_log_setting("3") # chat_log if self.chat_log['status'] != 'ok': raise RuntimeError("Failed to fetch chat log settings from API.") - logger.info("LoggingMessageEdit cog initialized") + @commands.Cog.listener() async def on_message_edit(self, message_before, message_after): diff --git a/src/bot/cogs/logging/logging_name_changes.py b/src/bot/cogs/logging/logging_name_changes.py index d5c0617..b8f56e6 100644 --- a/src/bot/cogs/logging/logging_name_changes.py +++ b/src/bot/cogs/logging/logging_name_changes.py @@ -33,7 +33,7 @@ def __init__(self, bot): self.user_log = self.bot.api.get_one_log_setting("4") # User_log if self.user_log['status'] != 'ok': raise RuntimeError("Failed to fetch user log settings from API.") - logger.info("LoggingNameChanges cog initialized") + @commands.Cog.listener() async def on_member_update(self, before, after): diff --git a/src/bot/cogs/verification/verification_on_join.py b/src/bot/cogs/verification/verification_on_join.py index 233e4a2..0c3fc3b 100644 --- a/src/bot/cogs/verification/verification_on_join.py +++ b/src/bot/cogs/verification/verification_on_join.py @@ -59,8 +59,7 @@ async def kick_if_not_verified(self, member, time_to_kick, logging_channel): async def on_member_join(self, member: discord.Member): guild = member.guild guild_id = member.guild.id - - if guild_id != os.getenv("MASTER_GUILD"): + if guild_id != int(os.getenv("MASTER_GUILD")): logger.warning("on_member_join fired, but not in master guild. Ignoring event.") return From 805001fb872164b4af8c1fc4cf8cc261ec591079 Mon Sep 17 00:00:00 2001 From: Minus <78946158+Minus-hates-school@users.noreply.github.com> Date: Fri, 22 May 2026 13:29:51 -0400 Subject: [PATCH 59/60] fixed broken error checker --- src/bot/cogs/moderation/admin_quarantine.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bot/cogs/moderation/admin_quarantine.py b/src/bot/cogs/moderation/admin_quarantine.py index 1df121e..e10cc9c 100644 --- a/src/bot/cogs/moderation/admin_quarantine.py +++ b/src/bot/cogs/moderation/admin_quarantine.py @@ -67,12 +67,15 @@ class AdminQuarantine(commands.Cog): def __init__(self, bot): self.bot = bot - self.naughty_role = self.bot.api.get_one_role('7')['roles'][2] # quarantine role ID - self.verified_role = self.bot.api.get_one_role('6')['roles'][2] # Verification role ID - self.mod_log = self.bot.api.get_one_log_setting("5") # mod_log + self.naughty_role = self.bot.api.get_one_role('7') + self.verified_role = self.bot.api.get_one_role('6') + self.mod_log = self.bot.api.get_one_log_setting('5') # mod_log if self.mod_log['status'] != 'ok' or self.naughty_role['status'] != 'ok' or self.verified_role['status'] != 'ok': raise RuntimeError("Failed to fetch mod log settings from API.") - + else: + self.naughty_role = self.naughty_role['roles'][2] + self.verified_role = self.verified_role['roles'][2] + self.mod_log = self.mod_log['logging'][2] @app_commands.command() @is_moderator() From 78e53e104e5423374eb0bb768d875cc27120da75 Mon Sep 17 00:00:00 2001 From: Kritarth Date: Fri, 29 May 2026 13:55:27 +0530 Subject: [PATCH 60/60] Added the migration commands. --- src/db/migrations.sql | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/db/migrations.sql b/src/db/migrations.sql index 640af50..daaa685 100644 --- a/src/db/migrations.sql +++ b/src/db/migrations.sql @@ -1,5 +1,23 @@ BEGIN; - -- Put all your migrations commands here + -- Put all your migrations commands here. + -- It is HIGHLY recommended to use EXISTS guards to not + -- accidentally apply migrations more than once. + + INSERT INTO serversettings (name, value) + SELECT 'Ticket Channel', '0' + WHERE NOT EXISTS ( + SELECT * FROM serversettings + WHERE name='Ticket Channel' + ); + + CREATE TABLE IF NOT EXISTS tickets ( + id SERIAL PRIMARY KEY, + thread_id BIGINT NOT NULL UNIQUE, + channel_id BIGINT NOT NULL, + creator_id BIGINT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + status VARCHAR(50) DEFAULT 'open' + ); COMMIT;