From c72793846a9ec91e3f6395d59137788340afae4b Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Wed, 24 Apr 2024 22:55:12 +0530 Subject: [PATCH 1/8] Add framework module to manage the warnings db --- framework/isobot/db/warnings.py | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 framework/isobot/db/warnings.py diff --git a/framework/isobot/db/warnings.py b/framework/isobot/db/warnings.py new file mode 100644 index 0000000..7e3ce29 --- /dev/null +++ b/framework/isobot/db/warnings.py @@ -0,0 +1,56 @@ +"""The framework module library used for managing isobot's warnings database.""" + +# Imports +import json +from framework.isobot.colors import Colors as colors + +# Functions +class Warnings: + """Used to initialize the warnings database.""" + def __init__(self): + print(f"[framework/db/Warnings] {colors.green}Warnings db library initialized.{colors.end}") + + def load(self) -> dict: + """Fetches and returns the latest data from the warnings database.""" + with open("database/warnings.json", 'r', encoding="utf8") as f: db = json.load(f) + return db + + def save(self, data: dict) -> int: + """Dumps all cached data to your local machine.""" + with open("database/warnings.json", 'w+', encoding="utf8") as f: json.dump(data, f) + return 0 + + def generate(self, guild_id: int, user_id: int) -> int: + """Generates a new database key for the user in the specified guild.\n\nReturns `0` if generation is successful.""" + data = self.load() + if str(guild_id) not in data: + data[str(guild_id)] = {} + if str(user_id) not in data[str(guild_id)]: + data[str(guild_id)][str(user_id)] = [] + self.save(data) + return 0 + + def add_warning(self, guild_id: int, user_id: int, moderator_id: int, warning_ts: int, reason: str) -> dict: + """Adds a new warning entry to the user of the specified guild, in the database.\n\nReturns the warning data as a `dict`.""" + data = self.load() + # TODO: Add warning ids later. For now, just keep warnings in a list variable. + warning_data = { + "moderator_id": moderator_id, + "warning_ts": warning_ts, + "reason": reason + } + data[str(guild_id)][str(user_id)].append(warning_data) + self.save(data) + return warning_data + + def clear_all_warnings(self, guild_id: int, user_id: int) -> int: + """Clears all the warnings for the user in the specified guild.""" + data = self.load() + data[str(guild_id)][str(user_id)] = [] + self.save(data) + return 0 + + def fetch_all_warnings(self, guild_id: int, user_id: int) -> list: + """Returns a `list` of all the warnings of the user in a specific guild.""" + data = self.load() + return data[str(guild_id)][str(user_id)] From d8df1a030505d2bf33ed4d48bec9916734d5b353 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Wed, 24 Apr 2024 22:56:29 +0530 Subject: [PATCH 2/8] Add `warnings.json` database auto-generation --- main.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index b18be0b..d0bfd73 100644 --- a/main.py +++ b/main.py @@ -40,7 +40,19 @@ def initial_setup(): # Generating database files try: - databases = ["automod", "currency", "isocard", "items", "levels", "presence", "user_data", "weather", "isobank/accounts", "isobank/auth"] + databases = [ + "automod", + "currency", + "isocard", + "items", + "levels", + "warnings", + "presence", + "user_data", + "weather", + "isobank/accounts", + "isobank/auth" + ] for f in databases: if not os.path.isfile(f"database/{f}.json"): logger.warn(f"[main/Setup] '{f}.json' was not found in database directory. Creating new database...", module="main/Setup", nolog=True) From d4c3a4e5c8fe0f2c904d4f6cae92658aae3a707e Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Wed, 24 Apr 2024 22:57:45 +0530 Subject: [PATCH 3/8] Automatically generate warnings db data keys for guild and user ids --- main.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index d0bfd73..7783c12 100644 --- a/main.py +++ b/main.py @@ -13,7 +13,7 @@ from random import randint from framework.isobot import currency, colors, settings, commands as _commands from framework.isobot.shop import ShopData -from framework.isobot.db import levelling, items, userdata, automod, weather, presence as _presence +from framework.isobot.db import levelling, items, userdata, automod, weather, warnings, presence as _presence from discord import ApplicationContext, option from discord.ext import commands from cogs.isocoin import create_isocoin_key @@ -103,6 +103,7 @@ def initial_setup(): settings = settings.Configurator() levelling = levelling.Levelling() items = items.Items() +warningsdb = warnings.Warnings() userdata = userdata.UserData() automod = automod.Automod() _presence = _presence.Presence() @@ -185,7 +186,9 @@ async def on_message(ctx): settings.generate(ctx.author.id) items.generate(ctx.author.id) levelling.generate(ctx.author.id) - try: automod.generate(ctx.guild.id) + try: + automod.generate(ctx.guild.id) + warningsdb.generate(ctx.guild.id, ctx.author.id) except AttributeError: pass try: From d517951bf16ed397c82c98010bb17712349a5be0 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Wed, 24 Apr 2024 22:59:01 +0530 Subject: [PATCH 4/8] Add `/warn` to let server moderators warn server members --- cogs/moderation.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/cogs/moderation.py b/cogs/moderation.py index 8000cdb..7730260 100644 --- a/cogs/moderation.py +++ b/cogs/moderation.py @@ -1,8 +1,13 @@ # Imports import discord +import time +from framework.isobot.db.warnings import Warnings from discord import option, ApplicationContext from discord.ext import commands +# Variables +warningsdb = Warnings() + # Commands class Moderation(commands.Cog): def __init__(self, bot): @@ -38,5 +43,32 @@ async def ban(self, ctx: ApplicationContext, user, reason=None): await ctx.respond(embed=discord.Embed(title=f'{user} has been banned.', description=f'Reason: {str(reason)}')) except Exception: await ctx.respond(embed=discord.Embed(title='Well, something happened...', description='Either I don\'t have permission to do this, or my role isn\'t high enough.', color=discord.Colour.red())) + @commands.slash_command( + name="warn", + description="Warns the specified user, with a specific reason." + ) + @option(name="user", description="Who do you want to warn?", type=discord.Member) + @option(name="reason", description="The reason why you are warning the user", type=str) + async def warn(self, ctx: ApplicationContext, user: discord.Member, reason: str): + """Warns the specified user, with a specific reason.""" + if not ctx.author.guild_permissions.manage_messages: + return await ctx.respond( + """You can't use this command! You need the `Manage Messages` permission to run this. + If you think this is a mistake, contact your server administrator.""", + ephemeral=True + ) + warningsdb.add_warning(ctx.guild.id, user.id, ctx.author.id, round(time.time()), reason) + warnembed = discord.Embed( + title=f":warning: You have been warned in **{ctx.guild.name}**", + description=f"Reason: {reason}", + color=discord.Color.red() + ) + await user.send(embed=warnembed) + localembed = discord.Embed( + description=f":white_check_mark: **{user.display_name}** has been successfully warned.", + color=discord.Color.green() + ) + await ctx.respond(embed=localembed) + # Initialization def setup(bot): bot.add_cog(Moderation(bot)) From c218a4dafc1b0550148569bdeb43241f550f430a Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Wed, 24 Apr 2024 22:59:36 +0530 Subject: [PATCH 5/8] Add `/clear_all_warnings` to allow moderators to remove all warnings from a user --- cogs/moderation.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cogs/moderation.py b/cogs/moderation.py index 7730260..b432c89 100644 --- a/cogs/moderation.py +++ b/cogs/moderation.py @@ -70,5 +70,24 @@ async def warn(self, ctx: ApplicationContext, user: discord.Member, reason: str) ) await ctx.respond(embed=localembed) + @commands.slash_command( + name="clear_all_warnings", + description="Clears all the warnings from the specified user." + ) + @option(name="user", description="The user whose warnings you want to clear.", type=discord.Member) + async def clear_all_warning(self, ctx: ApplicationContext, user: discord.Member): + """Clears all the warnings from the specified user.""" + if not ctx.author.guild_permissions.manage_messages: + return await ctx.respond( + """You can't use this command! You need the `Manage Messages` permission to run this. + If you think this is a mistake, contact your server administrator.""", + ephemeral=True + ) + warningsdb.clear_all_warnings(ctx.guild.id, user.id) + localembed = discord.Embed( + description=f":white_check_mark: Successfully cleared all server warnings for **{user.display_name}**.", + color=discord.Color.green() + ) + await ctx.respond(embed=localembed) # Initialization def setup(bot): bot.add_cog(Moderation(bot)) From a12ba2cc4ee981236f1f9f90e709326bd1e66ba1 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Wed, 24 Apr 2024 23:00:32 +0530 Subject: [PATCH 6/8] Add `/show_warnings` to let server mods and members view their warnings --- cogs/moderation.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cogs/moderation.py b/cogs/moderation.py index b432c89..4892427 100644 --- a/cogs/moderation.py +++ b/cogs/moderation.py @@ -89,5 +89,30 @@ async def clear_all_warning(self, ctx: ApplicationContext, user: discord.Member) color=discord.Color.green() ) await ctx.respond(embed=localembed) + + @commands.slash_command( + name="show_warnings", + description="See all the server warnings for a specific user." + ) + @option(name="user", description="The user whose warnings you want to view.", type=discord.Member, default=None) + async def show_warnings(self, ctx: ApplicationContext, user: discord.Member = None): + """See all the server warnings for a specific user.""" + await ctx.defer() + if user == None: user = ctx.author + all_user_warnings = warningsdb.fetch_all_warnings(ctx.guild.id, user.id) + parsed_output = str() + count = int() + for warning in all_user_warnings: + count += 1 + mod_ctx = await ctx.bot.fetch_user(warning["moderator_id"]) + parsed_output += f"{count}. **Warned by {mod_ctx.display_name}**\n> Reason: {warning['reason']}\n> Time Since Warn: \n" + if parsed_output == "": + parsed_output = "*Nothing to see here...*" + localembed = discord.Embed( + title=f"All warnings for **{user.display_name}** in **{ctx.guild.name}**", + description=parsed_output, + color=discord.Color.random() + ) + await ctx.respond(embed=localembed) # Initialization def setup(bot): bot.add_cog(Moderation(bot)) From b4116da4a6a2019b436f7d0bb9ee43f29d66a122 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Wed, 24 Apr 2024 23:00:50 +0530 Subject: [PATCH 7/8] Add user app commands to allow easier command access --- cogs/moderation.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cogs/moderation.py b/cogs/moderation.py index 4892427..533f96f 100644 --- a/cogs/moderation.py +++ b/cogs/moderation.py @@ -114,5 +114,19 @@ async def show_warnings(self, ctx: ApplicationContext, user: discord.Member = No color=discord.Color.random() ) await ctx.respond(embed=localembed) + + # User App Commands + @commands.user_command( + name="Clear All Warnings" + ) + async def _clear_all_warnings(self, ctx: ApplicationContext, user: discord.Member): + await self.clear_all_warning(ctx, user) + + @commands.user_command( + name="Show Warnings" + ) + async def _show_warnings(self, ctx: ApplicationContext, user: discord.Member): + await self.show_warnings(ctx, user) + # Initialization def setup(bot): bot.add_cog(Moderation(bot)) From c7024afb2bbca5c22eb492be8056ab83542e71e3 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Wed, 24 Apr 2024 23:13:03 +0530 Subject: [PATCH 8/8] Remove an unused comment --- framework/isobot/db/warnings.py | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/isobot/db/warnings.py b/framework/isobot/db/warnings.py index 7e3ce29..5c19bbd 100644 --- a/framework/isobot/db/warnings.py +++ b/framework/isobot/db/warnings.py @@ -33,7 +33,6 @@ def generate(self, guild_id: int, user_id: int) -> int: def add_warning(self, guild_id: int, user_id: int, moderator_id: int, warning_ts: int, reason: str) -> dict: """Adds a new warning entry to the user of the specified guild, in the database.\n\nReturns the warning data as a `dict`.""" data = self.load() - # TODO: Add warning ids later. For now, just keep warnings in a list variable. warning_data = { "moderator_id": moderator_id, "warning_ts": warning_ts,