From 93ff5b6f4515c724e92867afa3e53f9e98b6ee4e Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 13:03:11 +0530 Subject: [PATCH 01/69] Fixed Bugs --- cogs/ActivityNotices.py | 2 +- cogs/CustomCommands.py | 29 +++++++---- cogs/Punishments.py | 2 +- erm.py | 75 +++++++++++++++-------------- events/on_shift_end.py | 9 ++++ menus.py | 103 +++++++++++++++------------------------- 6 files changed, 106 insertions(+), 114 deletions(-) diff --git a/cogs/ActivityNotices.py b/cogs/ActivityNotices.py index 0ded1480..50a8e409 100644 --- a/cogs/ActivityNotices.py +++ b/cogs/ActivityNotices.py @@ -465,7 +465,7 @@ async def respond(embed: discord.Embed): async def core_command_request(self, ctx: commands.Context, request_type_object: str, duration: str, reason: str, return_bypass = None, override_victim = None): settings = await self.bot.settings.find_by_id(ctx.guild.id) if not settings.get('staff_management') or not settings.get('staff_management', {}).get( - f'{request_type_object.lower()}_role', None) or not settings.get('staff_management', {}).get('channel'): + f'{request_type_object.lower()}_role', None) or not settings.get('staff_management', {}).get('enabled'): await ctx.send( embed=discord.Embed( title="Not Enabled", diff --git a/cogs/CustomCommands.py b/cogs/CustomCommands.py index c68e7429..4269f5cd 100644 --- a/cogs/CustomCommands.py +++ b/cogs/CustomCommands.py @@ -190,12 +190,22 @@ async def custom_manage(self, ctx): ) ) status = False - data = { - "name": name, - "id": existing_command_data["id"], - "message": existing_command_data["message"], - "author": existing_command_data["author"] - } + try: + data = { + "name": name, + "id": existing_command_data["id"], + "message": existing_command_data["message"], + "author": existing_command_data["author"] + } + except KeyError: + await new_msg.edit( + embed=discord.Embed( + title="Command Mismatch", + description="This custom command doesn't exist.", + color=BLANK_COLOR + ) + ) + status = False view = CustomCommandModification(ctx.author.id, data) if status == True: await new_msg.edit(view=view, @@ -312,8 +322,11 @@ async def run(self, ctx, command: str, channel: discord.TextChannel = None): else ctx.channel ) embeds = [] - for embed in selected["message"]["embeds"]: - embeds.append(await interpret_embed(bot, ctx, channel, embed, selected['id'])) + if selected["message"]["embeds"] is not None: + for embed in selected["message"]["embeds"]: + embeds.append(await interpret_embed(bot, ctx, channel, embed, selected['id'])) + elif selected["message"]["embeds"] is None: + embeds.append(invis_embed) view = discord.ui.View() for item in selected.get('buttons', []): diff --git a/cogs/Punishments.py b/cogs/Punishments.py index 7cc20963..14152b5a 100644 --- a/cogs/Punishments.py +++ b/cogs/Punishments.py @@ -279,7 +279,7 @@ async def punishments(self, ctx): @is_staff() async def punishment_manage(self, ctx: commands.Context): embed = discord.Embed( - title="Management Options", + title="Staff Options", description="Using this menu, you can **Manage Punishment Types** as well as **Modify Punishment**.", color=BLANK_COLOR ) diff --git a/erm.py b/erm.py index d7517049..b26344ca 100644 --- a/erm.py +++ b/erm.py @@ -151,7 +151,7 @@ async def setup_hook(self) -> None: self.oauth2_users = OAuth2Users(self.db, "oauth2") self.roblox = roblox.Client() - self.prc_api = PRCApiClient(self, base_url=config('PRC_API_URL'), api_key=config('PRC_API_KEY')) + self.prc_api = PRCApiClient(self, base_url=config('PRC_API_URL', default='https://api.policeroleplay.community/v1'), api_key=config('PRC_API_KEY', default='default_api_key')) self.bloxlink = Bloxlink(self, config('BLOXLINK_API_KEY')) Extensions = [m.name for m in iter_modules(["cogs"], prefix="cogs.")] @@ -710,30 +710,57 @@ async def statistics_check(): end_time = time.time() logging.warning(f"Event statistics_check took {end_time - initial_time} seconds") +async def run_command(guild_id, username, message): + while True: + command = f":pm {username} {message}" + command_response = await bot.prc_api.run_command(guild_id, command) + if command_response[0] == 200: + logging.info(f"Sent PM to {username} in guild {guild_id}") + break + elif command_response[0] == 429: + retry_after = int(command_response[1].get('Retry-After', 5)) + logging.warning(f"Rate limited. Retrying after {retry_after} seconds.") + await asyncio.sleep(retry_after) + else: + logging.error(f"Failed to send PM to {username} in guild {guild_id}") + break + +def is_whitelisted(vehicle_name, whitelisted_vehicle): + vehicle_year_match = re.search(r'\d{4}$', vehicle_name) + whitelisted_year_match = re.search(r'\d{4}$', whitelisted_vehicle) + if vehicle_year_match and whitelisted_year_match: + vehicle_year = vehicle_year_match.group() + whitelisted_year = whitelisted_year_match.group() + if vehicle_year != whitelisted_year: + return False + vehicle_name_base = vehicle_name[:vehicle_year_match.start()].strip() + whitelisted_vehicle_base = whitelisted_vehicle[:whitelisted_year_match.start()].strip() + return fuzz.ratio(vehicle_name_base.lower(), whitelisted_vehicle_base.lower()) > 80 + return False + pm_counter = {} @tasks.loop(minutes=2, reconnect=True) async def check_whitelisted_car(): initial_time = time.time() - async for items in bot.settings.db.find({'ERLC': {'$exists': True}}): + async for items in bot.settings.db.find( + {"ERLC.vehicle_restrictions.enabled": {"$exists": True, "$eq": True}} + ): guild_id = items['_id'] try: guild = await bot.fetch_guild(guild_id) except discord.errors.NotFound: continue try: - enabled = items['ERLC'].get('enable_vehicle_restrictions', False) - whitelisted_vehicle_roles = items['ERLC'].get('whitelisted_vehicles_roles') - alert_channel_id = items['ERLC'].get('whitelisted_vehicle_alert_channel') - whitelisted_vehicles = items['ERLC'].get('whitelisted_vehicles', []) - alert_message = items["ERLC"].get("alert_message", "You do not have the required role to use this vehicle. Switch it or risk being moderated.") + whitelisted_vehicle_roles = items['ERLC'].get('vehicle_restrictions').get('roles') + alert_channel_id = items['ERLC'].get('vehicle_restrictions').get('channel') + whitelisted_vehicles = items['ERLC'].get('vehicle_restrictions').get('cars', []) + alert_message = items["ERLC"].get("vehicle_restrictions").get('message', "You do not have the required role to use this vehicle. Switch it or risk being moderated.") except KeyError: logging.error(f"KeyError for guild {guild_id}") continue - if not enabled: - continue - if not whitelisted_vehicle_roles or not alert_channel_id: + logging.warning(f"Skipping guild {guild_id} due to missing whitelisted vehicle roles or alert channel.") continue if isinstance(whitelisted_vehicle_roles, int): @@ -859,34 +886,6 @@ async def check_whitelisted_car(): end_time = time.time() logging.warning(f"Event check_whitelisted_car took {end_time - initial_time} seconds") - -async def run_command(guild_id, username, message): - while True: - command = f":pm {username} {message}" - command_response = await bot.prc_api.run_command(guild_id, command) - if command_response[0] == 200: - logging.info(f"Sent PM to {username} in guild {guild_id}") - break - elif command_response[0] == 429: - retry_after = int(command_response[1].get('Retry-After', 5)) - logging.warning(f"Rate limited. Retrying after {retry_after} seconds.") - await asyncio.sleep(retry_after) - else: - logging.error(f"Failed to send PM to {username} in guild {guild_id}") - break - -def is_whitelisted(vehicle_name, whitelisted_vehicle): - vehicle_year_match = re.search(r'\d{4}$', vehicle_name) - whitelisted_year_match = re.search(r'\d{4}$', whitelisted_vehicle) - if vehicle_year_match and whitelisted_year_match: - vehicle_year = vehicle_year_match.group() - whitelisted_year = whitelisted_year_match.group() - if vehicle_year != whitelisted_year: - return False - vehicle_name_base = vehicle_name[:vehicle_year_match.start()].strip() - whitelisted_vehicle_base = whitelisted_vehicle[:whitelisted_year_match.start()].strip() - return fuzz.ratio(vehicle_name_base.lower(), whitelisted_vehicle_base.lower()) > 80 - return False async def get_guild(guild_id): guild = bot.get_guild(guild_id) diff --git a/events/on_shift_end.py b/events/on_shift_end.py index 49c10156..01a8ac9f 100644 --- a/events/on_shift_end.py +++ b/events/on_shift_end.py @@ -97,6 +97,15 @@ async def on_shift_end(self, object_id: ObjectId): f"> **Nickname:** `{shift.nickname}`\n" ), inline=False + ).add_field( + name="Moderation Details:", + value=( + "\n".join([ + f"> **{moderation_type.capitalize()}s:** {count}" + for moderation_type, count in moderation_counts.items() + ]) if moderation_counts else "> No Moderations Found" + ), + inline=False ).set_author( name=guild.name, icon_url=guild.icon.url if guild.icon else '' diff --git a/menus.py b/menus.py index 296ef3de..733920c3 100644 --- a/menus.py +++ b/menus.py @@ -6177,7 +6177,7 @@ async def unadmin_staff_member(self, interaction: discord.Interaction, button: d field1 = interaction.message.embeds[0].fields[0] user_id = field1.value.split('**User ID:** ')[1].split('\n') - user_id = ''.join([i if i in '1234567890' else '' for i in user_id]) + user_id = ''.join(filter(str.isdigit, user_id)) await interaction.response.defer(ephemeral=True, thinking=False) command_response = await bot.prc_api.run_command(interaction.guild.id, f":unadmin {user_id}") @@ -6218,7 +6218,7 @@ async def kick_abuser(self, interaction: discord.Interaction, button: discord.ui guild = interaction.guild field1 = interaction.message.embeds[0].fields[0] user_id = field1.value.split('**User ID:** ')[1].split('\n') - user_id = ''.join([i if i in '1234567890' else '' for i in user_id]) + user_id = ''.join(filter(str.isdigit, user_id)) await interaction.response.defer(ephemeral=True, thinking=False) command_response = await bot.prc_api.run_command(interaction.guild.id, f":kick {user_id}") @@ -6253,7 +6253,7 @@ async def ban_abuser(self, interaction: discord.Interaction, button: discord.ui. guild = interaction.guild field1 = interaction.message.embeds[0].fields[0] user_id = field1.value.split('**User ID:** ')[1].split('\n') - user_id = ''.join([i if i in '1234567890' else '' for i in user_id]) + user_id = ''.join(filter(str.isdigit, user_id)) await interaction.response.defer(ephemeral=True, thinking=False) command_response = await bot.prc_api.run_command(interaction.guild.id, f":ban {user_id}") @@ -6868,14 +6868,12 @@ def __init__(self, bot, guild_id, enable_vehicle_restrictions=None, whitelisted_ row=2 ) - # Add components to the view self.add_item(self.whitelisted_vehicles_roles_select) self.add_item(self.whitelisted_vehicle_alert_channel_select) self.add_item(self.add_vehicle_button) self.add_item(self.add_message_button) self.add_item(self.enable_vehicle_restrictions_button) - # Set the callback for the select menus and button self.whitelisted_vehicles_roles_select.callback = self.create_callback(self.whitelisted_vehicles_roles_callback, self.whitelisted_vehicles_roles_select) self.whitelisted_vehicle_alert_channel_select.callback = self.create_callback(self.whitelisted_vehicle_alert_channel_callback, self.whitelisted_vehicle_alert_channel_select) self.add_vehicle_button.callback = self.create_callback(self.add_vehicle_to_role, self.add_vehicle_button) @@ -6884,7 +6882,6 @@ def __init__(self, bot, guild_id, enable_vehicle_restrictions=None, whitelisted_ def create_callback(self, func, component): async def callback(interaction: discord.Interaction): - # Call the original function with the correct arguments if isinstance(component, discord.ui.RoleSelect): return await func(interaction, component) elif isinstance(component, discord.ui.Button): @@ -6905,20 +6902,14 @@ async def toggle_vehicle_restrictions(self, interaction: discord.Interaction, bu bot = self.bot sett = await bot.settings.find_by_id(guild_id) if not sett.get('ERLC'): - sett['ERLC'] = { - 'whitelisted_vehicles_roles': [], - 'whitelisted_vehicle_alert_channel': 0, - 'whitelisted_vehicles': [] - } - try: - sett['ERLC']['enable_vehicle_restrictions'] = not sett['ERLC'].get('enable_vehicle_restrictions', False) - except KeyError: - sett['ERLC'] = { - 'enable_vehicle_restrictions': True, - } + sett['ERLC'] = {"vehicle_restrictions": {}} + + vehicle_restrictions = sett['ERLC'].get("vehicle_restrictions", {}) + vehicle_restrictions["enabled"] = not vehicle_restrictions.get("enabled", False) + sett['ERLC']["vehicle_restrictions"] = vehicle_restrictions await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] - embed.set_field_at(0, name="Vehicle Restrictions", value=f"If enabled, users will be alerted if they use a whitelisted vehicle without the correct roles.\nCurrent Status: {'Enabled' if sett['ERLC'].get('enable_vehicle_restrictions', False) else 'Disabled'}") + embed.set_field_at(0, name="Enable/Disable Vehicle Restrictions", value=f"If enabled, users will be alerted if they use a whitelisted vehicle without the correct roles.\n**Current Status:** {'Enabled' if vehicle_restrictions['enabled'] else 'Disabled'}") await interaction.edit_original_response(embed=embed) async def whitelisted_vehicles_roles_callback(self, interaction: discord.Interaction, select: discord.ui.RoleSelect): @@ -6932,18 +6923,11 @@ async def whitelisted_vehicles_roles_callback(self, interaction: discord.Interac bot = self.bot sett = await bot.settings.find_by_id(guild_id) if not sett.get('ERLC'): - sett['ERLC'] = { - 'whitelisted_vehicles_roles': [], - 'whitelisted_vehicle_alert_channel': 0, - 'whitelisted_vehicles': [] - } - try: - sett['ERLC']['whitelisted_vehicles_roles'] = [i.id for i in select.values] - except KeyError: - sett['ERLC'] = { - 'whitelisted_vehicle_alert_channel': 0, - } - sett['ERLC']['whitelisted_vehicles_roles'] = [i.id for i in select.values] + sett['ERLC'] = {"vehicle_restrictions": {}} + + vehicle_restrictions = sett['ERLC'].get("vehicle_restrictions", {}) + vehicle_restrictions["roles"] = [i.id for i in select.values] + sett['ERLC']["vehicle_restrictions"] = vehicle_restrictions await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(5, name="Current Roles", value=", ".join([f"<@&{i.id}>" for i in select.values]) if select.values else "None") @@ -6961,18 +6945,11 @@ async def whitelisted_vehicle_alert_channel_callback(self, interaction: discord. bot = self.bot sett = await bot.settings.find_by_id(guild_id) if not sett.get('ERLC'): - sett['ERLC'] = { - 'whitelisted_vehicles_roles': [], - 'whitelisted_vehicle_alert_channel': 0, - 'whitelisted_vehicles': [] - } - try: - sett['ERLC']['whitelisted_vehicle_alert_channel'] = select.values[0].id if select.values else 0 - except KeyError: - sett['ERLC'] = { - 'whitelisted_vehicles_roles': [], - } - sett['ERLC']['whitelisted_vehicle_alert_channel'] = select.values[0].id if select.values else 0 + sett['ERLC'] = {"vehicle_restrictions": {}} + + vehicle_restrictions = sett['ERLC'].get("vehicle_restrictions", {}) + vehicle_restrictions["channel"] = select.values[0].id + sett['ERLC']["vehicle_restrictions"] = vehicle_restrictions await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(6, name="Current Channel", value=f"<#{select.values[0].id}>") @@ -6983,11 +6960,9 @@ async def add_vehicle_to_role(self, interaction: discord.Interaction, button: di guild_id = interaction.guild.id bot = self.bot - # Retrieve existing whitelisted vehicles sett = await bot.settings.find_by_id(guild_id) - existing_vehicles = sett.get('ERLC', {}).get('whitelisted_vehicles', []) + existing_vehicles = sett.get('ERLC', {}).get('vehicle_restrictions', {}).get('cars', []) - # Pre-fill the modal text input with existing vehicles, separated by commas existing_vehicles_str = ', '.join(existing_vehicles) modal = CustomModal( @@ -7018,17 +6993,16 @@ async def add_vehicle_to_role(self, interaction: discord.Interaction, button: di if not sett.get('ERLC'): sett['ERLC'] = { - 'whitelisted_vehicles_roles': [], - 'whitelisted_vehicle_alert_channel': 0, - 'whitelisted_vehicles': [] + "vehicle_restrictions" : {} } try: - sett['ERLC']['whitelisted_vehicles'] = vehicles + sett['ERLC']['vehicle_restrictions']['cars'] = vehicles except KeyError: sett['ERLC'] = { - 'whitelisted_vehicles_roles': [], + 'vehicle_restrictions': { + 'cars': vehicles + } } - sett['ERLC']['whitelisted_vehicles'] = vehicles await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(7, name="Current Whitelisted Vehicles", value=", ".join(vehicles) if vehicles else "None") @@ -7039,9 +7013,8 @@ async def add_alert_message(self, interaction: discord.Interaction, button: disc guild_id = interaction.guild.id bot = self.bot - # Retrieve existing message if any sett = await bot.settings.find_by_id(guild_id) - existing_message = sett.get('ERLC', {}).get('alert_message', "") + existing_message = sett.get('ERLC', {}).get('vehicle_restrictions', {}).get('message', "") modal = CustomModal( "Add Alert Message", @@ -7067,18 +7040,16 @@ async def add_alert_message(self, interaction: discord.Interaction, button: disc if not sett.get('ERLC'): sett['ERLC'] = { - 'whitelisted_vehicles_roles': [], - 'whitelisted_vehicle_alert_channel': 0, - 'whitelisted_vehicles': [], - 'alert_message': "" + "vehicle_restrictions" : {} } try: - sett['ERLC']['alert_message'] = modal.message.value + sett['ERLC']['vehicle_restrictions']['message'] = modal.message.value except KeyError: sett['ERLC'] = { - 'alert_message': "" + 'vehicle_restrictions': { + 'message': modal.message.value + } } - sett['ERLC']['alert_message'] = modal.message.value await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(8, name="Alert Message", value=modal.message.value) @@ -7273,11 +7244,11 @@ async def add_vehicle_restriction(self, interaction: discord.Interaction, button return settings = await self.bot.settings.find_by_id(interaction.guild.id) - enable_vehicle_restrictions = settings.get('ERLC', {}).get('enable_vehicle_restrictions', False) - vehicle_restrictions_roles = settings.get('ERLC', {}).get('whitelisted_vehicles_roles', []) - vehicle_restrictions_channel = settings.get('ERLC', {}).get('whitelisted_vehicle_alert_channel', 0) - vehicle_restrictions_cars = settings.get('ERLC', {}).get('whitelisted_vehicles', []) - alert_message = settings.get('ERLC', {}).get('alert_message', "") + enable_vehicle_restrictions = settings.get('ERLC', {}).get('vehicle_restrictions',{}).get('enabled', False) + vehicle_restrictions_roles = settings.get('ERLC', {}).get('vehicle_restrictions', {}).get('roles', []) + vehicle_restrictions_channel = settings.get('ERLC', {}).get('vehicle_restrictions', {}).get('channel', 0) + vehicle_restrictions_cars = settings.get('ERLC', {}).get('vehicle_restrictions', {}).get('cars', []) + alert_message = settings.get('ERLC', {}).get('vehicle_restrictions', {}).get('message', "") view = WhitelistVehiclesManagement( self.bot, @@ -7294,7 +7265,7 @@ async def add_vehicle_restriction(self, interaction: discord.Interaction, button description=" " ).add_field( name="Enable/Disable Vehicle Restrictions", - value=f"If enabled, users will be alerted if they use a whitelisted vehicle without the correct roles.\nCurrent Status: {'Enabled' if enable_vehicle_restrictions else 'Disabled'}", + value=f"If enabled, users will be alerted if they use a whitelisted vehicle without the correct roles.\n**Current Status:** {'Enabled' if enable_vehicle_restrictions else 'Disabled'}", ).add_field( name="Whitelisted Vehicles Roles", value="These roles are given to those who are allowed to drive whitelisted cars in your server. They allow users to drive exotics in-game without any alerts.", From 4e501f05a8bf12d38cd1044e8d5ffa073dc5b325 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 14:07:44 +0530 Subject: [PATCH 02/69] Updated on_message.py --- events/on_message.py | 63 ++++++++++++++++++++++++++++++++++++++++- events/on_punishment.py | 2 +- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/events/on_message.py b/events/on_message.py index c4870052..1bf61596 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -87,6 +87,7 @@ async def on_message(self, message: discord.Message): if dataset.get('ERLC', {}).get('remote_commands'): remote_commands = True remote_command_channel = dataset["ERLC"]["remote_commands"]["webhook_channel"] if dataset["ERLC"]["remote_commands"].get("webhook_channel", None) else None + print(f"Remote commands: {remote_command_channel}") if "game_security" in dataset.keys(): if "enabled" in dataset["game_security"].keys(): @@ -187,7 +188,67 @@ async def on_message(self, message: discord.Message): ), view=view ) - + if remote_commands and remote_command_channel is not None and message.channel.id in [remote_command_channel]: + for embed in message.embeds: + if not embed.description or not embed.title: + continue + + if 'Player Kicked' in embed.title: + action_type = 'Kick' + elif 'Player Banned' in embed.title: + action_type = 'Ban' + else: + continue + + raw_content = embed.description + + if ('kicked' not in raw_content and action_type == 'Kick') or ('banned' not in raw_content and action_type == 'Ban'): + continue + + try: + if action_type == 'Kick': + user_info, command_info = raw_content.split('kicked ', 1) + else: + user_info, command_info = raw_content.split('banned ', 1) + + user_info = user_info.strip() + command_info = command_info.strip() + roblox_user = user_info.split(':')[0].replace('[', '').replace(']', '').strip() + profile_link = user_info.split('(')[1].split(')')[0].strip() + roblox_id_str = profile_link.split('/')[-2] + + if not roblox_id_str.isdigit(): + raise ValueError(f"Extracted Roblox ID is not a number: {roblox_id_str}") + + roblox_id = int(roblox_id_str) + + reason = command_info.split('`')[1].strip() + except (IndexError, ValueError): + continue + + discord_user = 0 + async for document in bot.oauth2_users.db.find({"roblox_id": roblox_id}): + discord_user = document['discord_id'] + + if discord_user == 0: + await message.add_reaction('❌') + return await message.add_reaction('6️⃣') + + user = discord.utils.get(message.guild.members, id=discord_user) + if not user: + try: + user = await message.guild.fetch_member(discord_user) + except Exception as e: + await message.add_reaction('❌') + return await message.add_reaction('7️⃣') + + new_message = copy.copy(message) + new_message.author = user + prefix = (await get_prefix(bot, message))[-1] + violator_user, reason = command_info.split('`')[1].split(' ') + new_message.content = f"{prefix}punish {violator_user} {action_type} {reason}" + await bot.process_commands(new_message) + if remote_commands and remote_command_channel is not None and message.channel.id in [remote_command_channel]: for embed in message.embeds: diff --git a/events/on_punishment.py b/events/on_punishment.py index b4ff4db2..25983350 100644 --- a/events/on_punishment.py +++ b/events/on_punishment.py @@ -129,7 +129,7 @@ async def get_discord_id_by_roblox_id(self, roblox_id): await user_to_dm.send(embed=embed) logging.info(f"Sent DM to user {warned_discord_id} about punishment.") except Exception as e: - logging.error(f"Error sending DM to user: {e}") + pass embed = discord.Embed( title="Punishment Issued", From 3dfc66d2deb3f9b28cdd55a9371fc6a11f489e36 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 14:17:40 +0530 Subject: [PATCH 03/69] Updated on_message.py --- events/on_message.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/events/on_message.py b/events/on_message.py index 1bf61596..17c89bd6 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -246,6 +246,9 @@ async def on_message(self, message: discord.Message): new_message.author = user prefix = (await get_prefix(bot, message))[-1] violator_user, reason = command_info.split('`')[1].split(' ') + #If reason is not provided, then make it as 'No reason provided' + if not reason: + reason = 'No reason provided' new_message.content = f"{prefix}punish {violator_user} {action_type} {reason}" await bot.process_commands(new_message) From fa0ed87fa251338db9bdf9f2e9489930fa467dec Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 14:18:50 +0530 Subject: [PATCH 04/69] Updated on_message.py --- events/on_message.py | 1 - 1 file changed, 1 deletion(-) diff --git a/events/on_message.py b/events/on_message.py index 17c89bd6..1c33616b 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -246,7 +246,6 @@ async def on_message(self, message: discord.Message): new_message.author = user prefix = (await get_prefix(bot, message))[-1] violator_user, reason = command_info.split('`')[1].split(' ') - #If reason is not provided, then make it as 'No reason provided' if not reason: reason = 'No reason provided' new_message.content = f"{prefix}punish {violator_user} {action_type} {reason}" From 2578b931e43bde7f53c88c100daa04b65950865f Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 14:23:22 +0530 Subject: [PATCH 05/69] Updated CustomCommands,py --- cogs/CustomCommands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/CustomCommands.py b/cogs/CustomCommands.py index 4269f5cd..d3c698bd 100644 --- a/cogs/CustomCommands.py +++ b/cogs/CustomCommands.py @@ -326,7 +326,7 @@ async def run(self, ctx, command: str, channel: discord.TextChannel = None): for embed in selected["message"]["embeds"]: embeds.append(await interpret_embed(bot, ctx, channel, embed, selected['id'])) elif selected["message"]["embeds"] is None: - embeds.append(invis_embed) + pass view = discord.ui.View() for item in selected.get('buttons', []): From 736fa657e5c958d4c5d4839e334ceccdac4aca1d Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 19:29:47 +0530 Subject: [PATCH 06/69] Updated erm.py --- erm.py | 329 +++++++++++++++++++++---------------------- events/on_message.py | 11 +- utils/prc_api.py | 4 +- 3 files changed, 175 insertions(+), 169 deletions(-) diff --git a/erm.py b/erm.py index b26344ca..e6ab6139 100644 --- a/erm.py +++ b/erm.py @@ -738,6 +738,13 @@ def is_whitelisted(vehicle_name, whitelisted_vehicle): return fuzz.ratio(vehicle_name_base.lower(), whitelisted_vehicle_base.lower()) > 80 return False +async def get_player_avatar_url(player_id): + url = f"https://thumbnails.roblox.com/v1/users/avatar?userIds={player_id}&size=180x180&format=Png&isCircular=false" + async with aiohttp.ClientSession() as session: + async with session.get(url) as response: + data = await response.json() + return data['data'][0]['imageUrl'] + pm_counter = {} @tasks.loop(minutes=2, reconnect=True) async def check_whitelisted_car(): @@ -887,182 +894,172 @@ async def check_whitelisted_car(): end_time = time.time() logging.warning(f"Event check_whitelisted_car took {end_time - initial_time} seconds") -async def get_guild(guild_id): - guild = bot.get_guild(guild_id) - if not guild: + +@tasks.loop(seconds=120, reconnect=True) +async def iterate_prc_logs(): + # This will aim to constantly update the PRC Logs + # and the relevant storage data. + async for item in bot.settings.db.find({'ERLC': {'$exists': True}}): try: - guild = await bot.fetch_guild(guild_id) + guild = await bot.fetch_guild(item['_id']) except discord.HTTPException: - return None - return guild - -async def get_player_avatar_url(player_id): - url = f"https://thumbnails.roblox.com/v1/users/avatar?userIds={player_id}&size=180x180&format=Png&isCircular=false" - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - data = await response.json() - return data['data'][0]['imageUrl'] - -async def fetch_logs(guild_id): - try: - kill_logs = await bot.prc_api.fetch_kill_logs(guild_id) - player_logs = await bot.prc_api.fetch_player_logs(guild_id) - return kill_logs, player_logs - except Exception as e: - channel = await bot.fetch_channel(1213523576603410452) - await channel.send(content=f"[3] {(str(e) or repr(e))=}") - await asyncio.sleep(1) - return None, None - -async def process_kill_logs(guild, kill_logs_channel, kill_logs, current_timestamp): - players = {} - if kill_logs_channel: - for item in kill_logs: - if (current_timestamp - item.timestamp) > 75: - continue - - if item.killer_username not in players: - players[item.killer_username] = [1, [item]] - else: - players[item.killer_username][0] += 1 - players[item.killer_username][1].append(item) - - embed = discord.Embed( - title="Kill Log", - color=BLANK_COLOR, - description=f"[{item.killer_username}](https://roblox.com/users/{item.killer_user_id}/profile) killed [{item.killed_username}](https://roblox.com/users/{item.killed_user_id}/profile) • " - ) - await kill_logs_channel.send(embed=embed) - - return players - -async def notify_rdm(guild, players): - settings = await bot.settings.find_by_id(guild.id) - channel = await fetch_get_channel(guild, (settings or {}).get('ERLC', {}).get('rdm_channel', 0)) - if channel: - for username, value in players.items(): - count, items = value - if count > 3: - roblox_player = await bot.roblox.get_user_by_username(username) - thumbnails = await bot.roblox.thumbnails.get_user_avatar_thumbnails([roblox_player], size=(420, 420)) - thumbnail = thumbnails[0].image_url - pings = [guild.get_role(role_id).mention for role_id in (settings or {}).get('ERLC', {}).get('rdm_mentionables', []) if guild.get_role(role_id)] - - embed = discord.Embed( - title="<:security:1169804198741823538> RDM Detected", - color=BLANK_COLOR - ).add_field( - name="User Information", - value=( - f"> **Username:** {roblox_player.name}\n" - f"> **User ID:** {roblox_player.id}\n" - f"> **Profile Link:** [Click here](https://roblox.com/users/{roblox_player.id}/profile)\n" - f"> **Account Created:** " - ), - inline=False - ).add_field( - name="Abuse Information", - value=( - f"> **Type:** Mass RDM\n" - f"> **Individuals Affected [{count}]:** {', '.join([f'[{i.killed_username}](https://roblox.com/users/{i.killed_user_id}/profile)' for i in items])}\n" - f"> **At:** " - ), - inline=False - ).set_thumbnail( - url=thumbnail - ) - - await channel.send( - ', '.join(pings) if pings else '', - embed=embed, - allowed_mentions=discord.AllowedMentions( - everyone=True, - users=True, - roles=True, - replied_user=True, - ), - view=RDMActions(bot) - ) - -async def handle_shifts(guild, player_logs, current_timestamp, player_logs_channel): - settings = await bot.settings.find_by_id(guild.id) - automatic_shifts_enabled = ((settings.get('ERLC', {}) or {}).get('automatic_shifts', {}) or {}).get('enabled', False) - automatic_shift_type = ((settings.get('ERLC', {}) or {}).get('automatic_shifts', {}) or {}).get('shift_type', '') - - staff_roles = await get_staff_roles(guild, settings) - perm_staff = [member for member in guild.members if member.guild_permissions.manage_messages or member.guild_permissions.manage_guild or member.guild_permissions.administrator and not member.bot] - - roblox_to_discord = {int((await bot.oauth2_users.db.find_one({"discord_id": member.id}) or {}).get("roblox_id", 0)): member for member in perm_staff} - - if player_logs_channel: - for item in player_logs: - if (current_timestamp - item.timestamp) > 75: - continue - - if item.user_id in roblox_to_discord: - if automatic_shifts_enabled: - consent_item = await bot.consent.find_by_id(roblox_to_discord[item.user_id].id) - if (consent_item or {}).get('auto_shifts', True): - shift = await bot.shift_management.get_current_shift(roblox_to_discord[item.user_id], guild.id) - if item.type == 'join' and not shift: - await bot.shift_management.add_shift_by_user(roblox_to_discord[item.user_id], automatic_shift_type, [], guild.id, timestamp=item.timestamp) - elif item.type == 'leave' and shift: - await bot.shift_management.end_shift(shift['_id'], guild.id, timestamp=item.timestamp) - embed = discord.Embed( - title=f"Player {'Join' if item.type == 'join' else 'Leave'} Log", - description=f"[{item.username}](https://roblox.com/users/{item.user_id}/profile) {'joined the server' if item.type == 'join' else 'left the server'} • ", - color=GREEN_COLOR if item.type == 'join' else RED_COLOR - ) - await player_logs_channel.send(embed=embed) - -async def get_staff_roles(guild, settings): - staff_roles = [] - if settings["staff_management"].get("role"): - if isinstance(settings["staff_management"]["role"], int): - staff_roles.append(settings["staff_management"]["role"]) - elif isinstance(settings["staff_management"]["role"], list): - staff_roles.extend(settings["staff_management"]["role"]) - - if settings["staff_management"].get("management_role"): - if isinstance(settings["staff_management"]["management_role"], int): - staff_roles.append(settings["staff_management"]["management_role"]) - elif isinstance(settings["staff_management"]["management_role"], list): - staff_roles.extend(settings["staff_management"]["management_role"]) - - staff_roles = [guild.get_role(role) for role in staff_roles if guild.get_role(role)] - return staff_roles - -async def handle_guild_logs(item): - guild = await get_guild(item['_id']) - if not guild: - return + continue - kill_logs_channel = await fetch_get_channel(guild, item['ERLC'].get('kill_logs')) - player_logs_channel = await fetch_get_channel(guild, item['ERLC'].get('player_logs')) + settings = await bot.settings.find_by_id(guild.id) + try: + kill_logs_channel = await fetch_get_channel(guild, item['ERLC'].get('kill_logs')) + player_logs_channel = await fetch_get_channel(guild, item['ERLC'].get('player_logs')) + except KeyError: + continue - if not kill_logs_channel and not player_logs_channel: - return + if not kill_logs_channel and not player_logs_channel: + continue + try: + kill_logs: list[prc_api.KillLog] = await bot.prc_api.fetch_kill_logs(guild.id) + player_logs: list[prc_api.JoinLeaveLog] = await bot.prc_api.fetch_player_logs(guild.id) + except prc_api.ResponseFailure: + continue - kill_logs, player_logs = await fetch_logs(guild.id) - if kill_logs is None or player_logs is None: - return + sorted_kill_logs = sorted(kill_logs, key=lambda x: x.timestamp, reverse=False) + sorted_player_logs = sorted(player_logs, key=lambda x: x.timestamp, reverse=False) + players = {} + current_timestamp = int(datetime.datetime.now(tz=pytz.UTC).timestamp()) - sorted_kill_logs = sorted(kill_logs, key=lambda x: x.timestamp) - sorted_player_logs = sorted(player_logs, key=lambda x: x.timestamp) + if kill_logs_channel is not None: + for item in sorted_kill_logs: + if (current_timestamp - item.timestamp) > 120: + continue + if not players.get(item.killer_username): + players[item.killer_username] = [1, [item]] + else: + players[item.killer_username] = [players[item.killer_username][0]+1, players[item.killer_username][1] + [item]] + await kill_logs_channel.send(embed=discord.Embed(title="Kill Log", color=BLANK_COLOR, description=f"[{item.killer_username}](https://roblox.com/users/{item.killer_user_id}/profile) killed [{item.killed_username}](https://roblox.com/users/{item.killed_user_id}/profile) • ")) - current_timestamp = int(datetime.datetime.now().timestamp()) - players = await process_kill_logs(guild, kill_logs_channel, sorted_kill_logs, current_timestamp) - await notify_rdm(guild, players) - await handle_shifts(guild, sorted_player_logs, current_timestamp, player_logs_channel) + channel = ((settings or {}).get('ERLC', {}) or {}).get('rdm_channel', 0) + try: + channel = await (await bot.fetch_guild(guild.id)).fetch_channel(channel) + except discord.HTTPException: + channel = None -@tasks.loop(seconds=75, reconnect=True) -async def iterate_prc_logs(): - # This will aim to constantly update the PRC Logs - # and the relevant storage data. - async for item in bot.settings.db.find({'ERLC': {'$exists': True}}): - await handle_guild_logs(item) + if channel: + for username, value in players.items(): + count = value[0] + items = value[1] + if count > 3: + roblox_player = await bot.roblox.get_user_by_username(username) + thumbnails = await bot.roblox.thumbnails.get_user_avatar_thumbnails([roblox_player], size=(420, 420)) + thumbnail = thumbnails[0].image_url + pings = [] + pings = [((guild.get_role(role_id)).mention) if guild.get_role(role_id) else None for role_id in (settings or {}).get('ERLC', {}).get('rdm_mentionables', [])] + pings = list(filter(lambda x: x is not None, pings)) + + await channel.send( + ', '.join(pings) if pings not in [[], None] else '', + embed=discord.Embed( + title="<:security:1169804198741823538> RDM Detected", + color=BLANK_COLOR + ).add_field( + name="User Information", + value=( + f"> **Username:** {roblox_player.name}\n" + f"> **User ID:** {roblox_player.id}\n" + f"> **Profile Link:** [Click here](https://roblox.com/users/{roblox_player.id}/profile)\n" + f"> **Account Created:** " + ), + inline=False + ).add_field( + name="Abuse Information", + value=( + f"> **Type:** Mass RDM\n" + f"> **Individuals Affected [{count}]:** {', '.join([f'[{i.killed_username}](https://roblox.com/users/{i.killed_user_id}/profile)' for i in items])}\n" + f"> **At:** " + ), + inline=False + ).set_thumbnail( + url=thumbnail + ), + allowed_mentions=discord.AllowedMentions( + everyone=True, + users=True, + roles=True, + replied_user=True, + ), + view=RDMActions(bot) + ) + staff_roles = [] + if settings["staff_management"].get("role"): + if isinstance(settings["staff_management"]["role"], int): + staff_roles.append(settings["staff_management"]["role"]) + elif isinstance(settings["staff_management"]["role"], list): + for role in settings["staff_management"]["role"]: + staff_roles.append(role) + + if settings["staff_management"].get("management_role"): + if isinstance(settings["staff_management"]["management_role"], int): + staff_roles.append(settings["staff_management"]["management_role"]) + elif isinstance(settings["staff_management"]["management_role"], list): + for role in settings["staff_management"]["management_role"]: + staff_roles.append(role) + + await guild.chunk() + staff_roles = [guild.get_role(role) for role in staff_roles] + added_staff = [] + + for role in staff_roles.copy(): + if role is None: + staff_roles.remove(role) + + perm_staff = list( + filter( + lambda m: ( + m.guild_permissions.manage_messages + or m.guild_permissions.manage_guild + or m.guild_permissions.administrator + ) + and not m.bot, + guild.members + ) + ) + for role in staff_roles: + for member in role.members: + if not member.bot and member not in added_staff: + added_staff.append(member) + + for member in perm_staff: + if member not in added_staff: + added_staff.append(member) + + automatic_shifts_enabled = ((settings.get('ERLC', {}) or {}).get('automatic_shifts', {}) or {}).get('enabled', False) + automatic_shift_type = ((settings.get('ERLC', {}) or {}).get('automatic_shifts', {}) or {}).get('shift_type', '') + roblox_to_discord = {} + for item in perm_staff: + roblox_to_discord[int(((await bot.oauth2_users.db.find_one({"discord_id": item.id})) or {}).get("roblox_id", 0))] = item + + if player_logs_channel is not None: + for item in sorted_player_logs: + if (current_timestamp - item.timestamp) > 120: + continue + if item.user_id in roblox_to_discord.keys(): + if automatic_shifts_enabled: + consent_item = await bot.consent.find_by_id(roblox_to_discord[item.user_id].id) + if (consent_item or {}).get('auto_shifts', True) is True: + shift = await bot.shift_management.get_current_shift(roblox_to_discord[item.user_id], guild.id) + if item.type == 'join': + if not shift: + await bot.shift_management.add_shift_by_user(roblox_to_discord[item.user_id], automatic_shift_type, [], guild.id, timestamp=item.timestamp) + else: + if shift: + await bot.shift_management.end_shift(shift['_id'], guild.id, timestamp=item.timestamp) + + await player_logs_channel.send( + embed=discord.Embed( + title=f"Player {'Join' if item.type == 'join' else 'Leave'} Log", + description=f"[{item.username}](https://roblox.com/users/{item.user_id}/profile) {'joined the server' if item.type == 'join' else 'left the server'} • ", + color=GREEN_COLOR if item.type == 'join' else RED_COLOR + ) + ) @iterate_prc_logs.before_loop async def anti_fetch_measure(): diff --git a/events/on_message.py b/events/on_message.py index 1c33616b..8137063e 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -245,7 +245,16 @@ async def on_message(self, message: discord.Message): new_message = copy.copy(message) new_message.author = user prefix = (await get_prefix(bot, message))[-1] - violator_user, reason = command_info.split('`')[1].split(' ') + reason_info = command_info.split('`')[1].strip() + split_index = reason_info.find(' ') + if split_index != -1: + violator_user = reason_info[:split_index].strip() + reason = reason_info[split_index:].strip() + else: + violator_user = reason_info + reason = 'No reason provided' + if reason.endswith('- Player Not In Game'): + reason = reason[:-len('- Player Not In Game')] if not reason: reason = 'No reason provided' new_message.content = f"{prefix}punish {violator_user} {action_type} {reason}" diff --git a/utils/prc_api.py b/utils/prc_api.py index 701ca28d..8ef50df7 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -257,7 +257,7 @@ async def fetch_kill_logs(self, guild_id: int): killed_user_id=log_item['Killed'].split(':')[1] ) for log_item in response_json] elif status_code == 429: - retry_after = int(response_json[1].get('Retry-After', 5)) + retry_after = int(response_json[1].get('retry_after', 5)) await asyncio.sleep(retry_after) else: raise ResponseFailure( @@ -291,7 +291,7 @@ async def fetch_player_logs(self, guild_id: int): type='join' if log_item['Join'] is True else 'leave' ) for log_item in response_json] elif status_code == 429: - retry_after = int(response_json[1].get('Retry-After', 5)) + retry_after = int(response_json[1].get('retry_after', 5)) await asyncio.sleep(retry_after) else: raise ResponseFailure( From a4c321fff15d36fbe34f0501995c9ba64cc5b384 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 20:32:15 +0530 Subject: [PATCH 07/69] Updated on_message.py --- events/on_message.py | 104 +++++++++++++++++++++++++++++++++++++++- events/on_punishment.py | 4 -- 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/events/on_message.py b/events/on_message.py index 8137063e..5c26ac1e 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -3,6 +3,7 @@ import logging import string +import pytz import aiohttp import discord import num2words @@ -10,6 +11,10 @@ from discord.ext import commands from reactionmenu import Page, ViewButton, ViewMenu, ViewSelect +from utils.utils import ( + get_roblox_by_username, +) +from datamodels.Warnings import WarningItem from utils.prc_api import Player from utils.constants import BLANK_COLOR, GREEN_COLOR from utils.utils import generator @@ -257,8 +262,105 @@ async def on_message(self, message: discord.Message): reason = reason[:-len('- Player Not In Game')] if not reason: reason = 'No reason provided' - new_message.content = f"{prefix}punish {violator_user} {action_type} {reason}" + ctx = await bot.get_context(new_message) + roblox_user = await get_roblox_by_username(violator_user, self.bot, ctx) + roblox_client = roblox.client.Client() + if not roblox_user or roblox_user.get('errors') is not None: + return await ctx.send(embed=discord.Embed( + title="Could not find player", + description="I could not find a Roblox player with that username.", + color=BLANK_COLOR + )) + + roblox_player = await roblox_client.get_user(roblox_user['id']) + thumbnails = await roblox_client.thumbnails.get_user_avatar_thumbnails([roblox_player], type=roblox.thumbnails.AvatarThumbnailType.headshot) + thumbnail = thumbnails[0].image_url + + punishment_types = await self.bot.punishment_types.get_punishment_types(message.guild.id) + types = (punishment_types or {}).get('types', []) + preset_types = ["Warning", "Kick", "Ban", "BOLO"] + actual_types = [] + for item in preset_types+types: + if isinstance(item, str): + actual_types.append(item) + else: + actual_types.append(item['name']) + #If action type in lowercase not in actual type lovercase, then return + if action_type.lower() not in [item.lower() for item in actual_types]: + await message.add_reaction('❌') + return await ctx.send(embed=discord.Embed( + title="Invalid Type", + description="The type you provided is not valid.", + color=BLANK_COLOR + )) + for item in actual_types: + safe_item = item.lower() + if action_type.lower() == safe_item: + actual_type = item + break + if action_type.lower() in ["tempban", "temporary ban"]: + return await ctx.send(embed=discord.Embed( + title="Not Supported", + description="Temporary Bans are not supported.", + color=BLANK_COLOR + )) + oid = await self.bot.punishments.insert_warning( + user.id, + user.name, + roblox_player.id, + roblox_player.name, + ctx.guild.id, + reason, + actual_type, + datetime.datetime.now(tz=pytz.UTC).timestamp() + ) + + self.bot.dispatch('punishment', oid) + flags = [] + if "--kick" in reason.lower(): + flags.append('autokick') + reason = reason.replace("--kick", "") + elif "--ban" in reason.lower(): + flags.append('autoban') + reason = reason.replace("--ban", "") + warning: WarningItem = await self.bot.punishments.fetch_warning(oid) + newline = '\n' + await user.send( + embed=discord.Embed( + title="<:success:1163149118366040106> Logged Punishment", + description=( + "I have successfully logged the following punishment!" + ), + color=GREEN_COLOR + ).add_field( + name="Punishment", + value=( + f"> **Player:** {warning.username}\n" + f"> **Type:** {warning.warning_type}\n" + f"> **Moderator:** <@{warning.moderator_id}>\n" + f"> **Reason:** {warning.reason}\n" + f"> **At:** \n" + f'{"> **Until:** {}".format(int(warning.until_epoch), newline) if warning.until_epoch is not None else ""}' + f"> **ID:** `{warning.snowflake}`\n" + f"> **Custom Flags:** {'`N/A`' if len(flags) == 0 else '{}'.format(', '.join(flags))}" + ), + inline=False + ).set_thumbnail( + url=thumbnail + ) + ) + if 'autoban' in flags: + try: + await self.bot.prc_api.run_command(ctx.guild.id, ":ban {}".format(warning.username)) + except: + pass + elif 'autokick' in flags: + try: + await self.bot.prc_api.run_command(ctx.guild.id, ":kick {}".format(warning.username)) + except: + pass await bot.process_commands(new_message) + await message.add_reaction('✅') if remote_commands and remote_command_channel is not None and message.channel.id in [remote_command_channel]: diff --git a/events/on_punishment.py b/events/on_punishment.py index 25983350..bb1a60d8 100644 --- a/events/on_punishment.py +++ b/events/on_punishment.py @@ -60,10 +60,6 @@ async def on_punishment(self, objectid: ObjectId): logging.error(f"Moderator with ID {warning.moderator_id} not found.") return - if not moderator: - logging.error(f"Moderator with ID {warning.moderator_id} not found in guild {guild.id}.") - return - roblox_client: Client = Client() roblox_user = await roblox_client.get_user(warning.user_id) thumbnails = await roblox_client.thumbnails.get_user_avatar_thumbnails( From 36119c5d3f981400c7bce40a8426ac30d8a19de8 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 20:33:34 +0530 Subject: [PATCH 08/69] Updated on_message.py --- events/on_message.py | 1 - 1 file changed, 1 deletion(-) diff --git a/events/on_message.py b/events/on_message.py index 5c26ac1e..e5c3284b 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -285,7 +285,6 @@ async def on_message(self, message: discord.Message): actual_types.append(item) else: actual_types.append(item['name']) - #If action type in lowercase not in actual type lovercase, then return if action_type.lower() not in [item.lower() for item in actual_types]: await message.add_reaction('❌') return await ctx.send(embed=discord.Embed( From efb73b69a00760b57ee0217a3f5f91bc7f0ad0e4 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 20:39:15 +0530 Subject: [PATCH 09/69] Updated erm.py --- erm.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/erm.py b/erm.py index e6ab6139..0b91f8df 100644 --- a/erm.py +++ b/erm.py @@ -567,11 +567,9 @@ async def check_reminders(): replied_user=True, everyone=True, roles=True, users=True )) except Exception as e: - # print(e) - pass + raise e except Exception as e: - # print(e) - pass + raise e @tasks.loop(minutes=1, reconnect=True) async def tempban_checks(): @@ -611,7 +609,7 @@ async def tempban_checks(): try: cached_servers[punishment_item['Guild']] = await bot.prc_api.fetch_bans(punishment_item['Guild']) except: - continue + raise Exception("Failed to fetch bans from PRC API") punishment_item['CheckExecuted'] = True From a0c3ca2558c69a669604472954ea2dbff40cd85f Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 22:48:45 +0530 Subject: [PATCH 10/69] Updated menu.py --- events/on_message.py | 7 ++- menus.py | 100 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/events/on_message.py b/events/on_message.py index e5c3284b..a8526d58 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -88,11 +88,14 @@ async def on_message(self, message: discord.Message): webhook_channel = None remote_commands = False remote_command_channel = None + auto_kick_ban_channel = None if dataset.get('ERLC', {}).get('remote_commands'): remote_commands = True remote_command_channel = dataset["ERLC"]["remote_commands"]["webhook_channel"] if dataset["ERLC"]["remote_commands"].get("webhook_channel", None) else None - print(f"Remote commands: {remote_command_channel}") + + if dataset.get('ERLC',{}).get('auto_kick_ban_log'): + auto_kick_ban_channel = dataset["ERLC"]["auto_kick_ban_log"] if dataset["ERLC"]["auto_kick_ban_log"] else None if "game_security" in dataset.keys(): if "enabled" in dataset["game_security"].keys(): @@ -193,7 +196,7 @@ async def on_message(self, message: discord.Message): ), view=view ) - if remote_commands and remote_command_channel is not None and message.channel.id in [remote_command_channel]: + if auto_kick_ban_channel is not None and message.channel.id == auto_kick_ban_channel: for embed in message.embeds: if not embed.description or not embed.title: continue diff --git a/menus.py b/menus.py index 733920c3..dc997840 100644 --- a/menus.py +++ b/menus.py @@ -7301,6 +7301,33 @@ async def add_vehicle_restriction(self, interaction: discord.Interaction, button ephemeral=True ) + @discord.ui.button( + label="More Features", + row=3 + ) + async def more_features(self, interaction: discord.Interaction, button: discord.ui.Button): + val = await self.interaction_check(interaction) + if val is False: + return + sett = await self.bot.settings.find_by_id(interaction.guild.id) + view = MoreOptions(self.bot, interaction.guild.id) + await interaction.response.send_message( + embed = discord.Embed( + title="More Options", + description="", + color=BLANK_COLOR + ), + view=view, + ephemeral=True + ) + + +class MoreOptions(discord.ui.View): + def __init__(self, bot, guild_id): + super().__init__(timeout=900.0) + self.bot = bot + self.guild_id = guild_id + @discord.ui.button( label="ER:LC Statistics Updates", row=3 @@ -7340,6 +7367,79 @@ async def erlc_statics(self, interaction: discord.Interaction, button: discord.u ephemeral=True ) + @discord.ui.button( + label="Auto Kick/Ban Logging", + row=3 + ) + async def auto_kick_ban(self, interaction: discord.Interaction, button: discord.ui.Button): + val = await self.interaction_check(interaction) + if val is False: + return + sett = await self.bot.settings.find_by_id(interaction.guild.id) + if not sett: + return + if not sett.get('ERLC'): + sett['ERLC'] = { + 'auto_kick_ban_log': 0 + } + view = AutoLogging(self.bot,sett) + embed=discord.Embed( + title="Auto Kick/Ban Logging", + description="This channel is where the bot will read the webhooks from the game server.", + color=BLANK_COLOR + ).set_author( + name=interaction.guild.name, + icon_url=interaction.guild.icon.url if interaction.guild.icon else '' + ) + await interaction.response.send_message( + embed = embed, + view=view, + ephemeral=True + ) + +class AutoLogging(discord.ui.View): + def __init__(self,bot,sett): + super().__init__(timeout=600.0) + self.bot = bot + self.sett = sett + try: + channel = self.bot.get_channel(self.sett.get('ERLC', {}).get('auto_kick_ban_log', 0)) + except AttributeError: + channel = 0 + + + self.select = discord.ui.ChannelSelect( + placeholder="Auto Kick/Ban Log Channel", + max_values=1, + min_values=0, + channel_types=[discord.ChannelType.text], + default_values=[channel] if channel else [] + ) + self.add_item(self.select) + self.select.callback = self.select_callback + + async def select_callback(self, interaction: discord.Interaction): + guild_id = interaction.guild.id + + bot = self.bot + sett = await bot.settings.find_by_id(guild_id) + if not sett.get('ERLC'): + sett['ERLC'] = { + 'auto_kick_ban_log': 0 + } + + selected_channel_id = self.select.values[0].id if self.select.values else 0 + sett['ERLC']['auto_kick_ban_log'] = selected_channel_id + embed = discord.Embed( + title="<:check:1163142000271429662> Channel Saved", + description=f"Auto Kick/Ban Log Channel set to <#{selected_channel_id}>", + color=GREEN_COLOR + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + await bot.settings.update_by_id(sett) + await config_change_log(self.bot, interaction.guild, interaction.user, f"ERLC Kick/Ban Webhook Channel changed to: <#{selected_channel_id}>") + + class ERLCStats(discord.ui.View): def __init__(self, bot, user_id, guild_id): super().__init__(timeout=600.0) From f5726ea6e851642e0aecf59cc4481fe53b0e3c9c Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 1 Aug 2024 22:57:22 +0530 Subject: [PATCH 11/69] Updated erm.py --- erm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erm.py b/erm.py index 0b91f8df..fb17b106 100644 --- a/erm.py +++ b/erm.py @@ -893,7 +893,7 @@ async def check_whitelisted_car(): logging.warning(f"Event check_whitelisted_car took {end_time - initial_time} seconds") -@tasks.loop(seconds=120, reconnect=True) +@tasks.loop(seconds=60, reconnect=True) async def iterate_prc_logs(): # This will aim to constantly update the PRC Logs # and the relevant storage data. @@ -925,7 +925,7 @@ async def iterate_prc_logs(): if kill_logs_channel is not None: for item in sorted_kill_logs: - if (current_timestamp - item.timestamp) > 120: + if (current_timestamp - item.timestamp) > 60: continue if not players.get(item.killer_username): players[item.killer_username] = [1, [item]] From a47afc6a9ed5f73e508ea94d3289fb666736a68e Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sat, 3 Aug 2024 11:10:09 +0530 Subject: [PATCH 12/69] Fixed Duty Admin/Manage Bug --- cogs/ShiftLogging.py | 6 ++++-- events/on_shift_end.py | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cogs/ShiftLogging.py b/cogs/ShiftLogging.py index e54e27af..78de3ae9 100644 --- a/cogs/ShiftLogging.py +++ b/cogs/ShiftLogging.py @@ -239,7 +239,8 @@ async def duty_admin(self, ctx, member: discord.Member, type: str = "Default",fo previous_shifts = [i async for i in self.bot.shift_management.shifts.db.find({ "UserID": member.id, "Guild": ctx.guild.id, - "EndEpoch": {'$ne': 0} + "EndEpoch": {'$ne': 0}, + "Type": type })] embed = discord.Embed( color=BLANK_COLOR @@ -468,7 +469,8 @@ async def duty_manage(self, ctx, *, type: str = "Default"): previous_shifts = [i async for i in self.bot.shift_management.shifts.db.find({ "UserID": ctx.author.id, "Guild": ctx.guild.id, - "EndEpoch": {'$ne': 0} + "EndEpoch": {'$ne': 0}, + "Type": type })] embed = discord.Embed( color=BLANK_COLOR diff --git a/events/on_shift_end.py b/events/on_shift_end.py index 01a8ac9f..610c1e2d 100644 --- a/events/on_shift_end.py +++ b/events/on_shift_end.py @@ -78,6 +78,12 @@ async def on_shift_end(self, object_id: ObjectId): pass if channel is not None: + moderation_counts = {} + for entry in shift.moderations: + if entry['type'] in moderation_counts: + moderation_counts[entry['type']] += entry['count'] + else: + moderation_counts[entry['type']] = entry['count'] await channel.send(embed=discord.Embed( title="Shift Ended", color=BLANK_COLOR @@ -120,12 +126,6 @@ async def on_shift_end(self, object_id: ObjectId): else True ) if shift_reports_enabled: - moderation_counts = {} - for entry in shift.moderations: - if entry['type'] in moderation_counts: - moderation_counts[entry['type']] += entry['count'] - else: - moderation_counts[entry['type']] = entry['count'] embed = discord.Embed( title="Shift Report", From 7164606ad382e916733bc57ace71a3476598fb1e Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sat, 3 Aug 2024 11:39:26 +0530 Subject: [PATCH 13/69] Fixed erlc logs embed bug --- cogs/ERLC.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cogs/ERLC.py b/cogs/ERLC.py index f7c042a7..9da8cfb5 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -458,7 +458,8 @@ async def operate_and_reload_commandlogs(msg, guild_id: str): for log in sorted_logs: if len(embed.description) > 3800: break - embed.description += f"> [{log.username}](https://roblox.com/users/{log.user_id}/profile) ran the command `{log.command}` • \n" + cleaned_command = log.command.replace('`', '') + embed.description += f"> [{log.username}](https://roblox.com/users/{log.user_id}/profile) ran the command `{cleaned_command}` • \n" if embed.description in ['', '\n']: embed.description = "> No player logs found." From 6d72c73565764d5d3e1d9f9bd4aca8f4b6293819 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 5 Aug 2024 08:49:47 +0530 Subject: [PATCH 14/69] Updated Auto Kick/Ban Logging --- events/on_message.py | 8 ++++---- menus.py | 47 +++++++++++++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/events/on_message.py b/events/on_message.py index a8526d58..ebabdafb 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -88,14 +88,14 @@ async def on_message(self, message: discord.Message): webhook_channel = None remote_commands = False remote_command_channel = None - auto_kick_ban_channel = None + kick_ban_webhook_channel = None if dataset.get('ERLC', {}).get('remote_commands'): remote_commands = True remote_command_channel = dataset["ERLC"]["remote_commands"]["webhook_channel"] if dataset["ERLC"]["remote_commands"].get("webhook_channel", None) else None - if dataset.get('ERLC',{}).get('auto_kick_ban_log'): - auto_kick_ban_channel = dataset["ERLC"]["auto_kick_ban_log"] if dataset["ERLC"]["auto_kick_ban_log"] else None + if dataset.get('ERLC',{}).get('auto_kick_ban').get('channel'): + kick_ban_webhook_channel = dataset["ERLC"]["auto_kick_ban"]["channel"] if "game_security" in dataset.keys(): if "enabled" in dataset["game_security"].keys(): @@ -196,7 +196,7 @@ async def on_message(self, message: discord.Message): ), view=view ) - if auto_kick_ban_channel is not None and message.channel.id == auto_kick_ban_channel: + if kick_ban_webhook_channel is not None and message.channel.id == kick_ban_webhook_channel: for embed in message.embeds: if not embed.description or not embed.title: continue diff --git a/menus.py b/menus.py index dc997840..797cce75 100644 --- a/menus.py +++ b/menus.py @@ -7313,7 +7313,7 @@ async def more_features(self, interaction: discord.Interaction, button: discord. view = MoreOptions(self.bot, interaction.guild.id) await interaction.response.send_message( embed = discord.Embed( - title="More Options", + title="More Features", description="", color=BLANK_COLOR ), @@ -7351,16 +7351,16 @@ async def erlc_statics(self, interaction: discord.Interaction, button: discord.u embed=discord.Embed( title="ER:LC Statistics", description="", - color=BLANK_COLOR + color=BLANK_COLOR ).set_author( name=interaction.guild.name, icon_url=interaction.guild.icon.url if interaction.guild.icon else '' ) - if statistics.items not in [None, {}]: + if not statistics.items(): + embed.description += "No Statistics Channels Set" + else: for key, value in statistics.items(): embed.description += f"**Channel:** <#{key}>\n> **Format:** `{value.get('format', 'None')}`\n" - else: - embed.description = "No Statistics Channels Set" await interaction.response.send_message( embed = embed, view=view, @@ -7368,7 +7368,7 @@ async def erlc_statics(self, interaction: discord.Interaction, button: discord.u ) @discord.ui.button( - label="Auto Kick/Ban Logging", + label="Auto Kick/Ban", row=3 ) async def auto_kick_ban(self, interaction: discord.Interaction, button: discord.ui.Button): @@ -7380,13 +7380,18 @@ async def auto_kick_ban(self, interaction: discord.Interaction, button: discord. return if not sett.get('ERLC'): sett['ERLC'] = { - 'auto_kick_ban_log': 0 + 'auto_kick_ban': { + 'channel': 0 + } } view = AutoLogging(self.bot,sett) embed=discord.Embed( - title="Auto Kick/Ban Logging", - description="This channel is where the bot will read the webhooks from the game server.", + title="Auto Kick/Ban", + description=" ", color=BLANK_COLOR + ).add_field( + name="Kick/Ban Webhook Channel", + value="This is the channel where all automatic kick/ban logs are sent to via ERLC." ).set_author( name=interaction.guild.name, icon_url=interaction.guild.icon.url if interaction.guild.icon else '' @@ -7403,9 +7408,9 @@ def __init__(self,bot,sett): self.bot = bot self.sett = sett try: - channel = self.bot.get_channel(self.sett.get('ERLC', {}).get('auto_kick_ban_log', 0)) - except AttributeError: - channel = 0 + channel = self.bot.get_channel(self.sett.get('ERLC', {}).get('auto_kick_ban', {}).get('channel')) + except KeyError: + channel = None self.select = discord.ui.ChannelSelect( @@ -7425,11 +7430,25 @@ async def select_callback(self, interaction: discord.Interaction): sett = await bot.settings.find_by_id(guild_id) if not sett.get('ERLC'): sett['ERLC'] = { - 'auto_kick_ban_log': 0 + 'auto_kick_ban': { + 'channel': 0 + } } selected_channel_id = self.select.values[0].id if self.select.values else 0 - sett['ERLC']['auto_kick_ban_log'] = selected_channel_id + try: + sett['ERLC']['auto_kick_ban']['channel'] = selected_channel_id + except KeyError: + try: + sett['ERLC']['auto_kick_ban'] = { + 'channel': selected_channel_id + } + except KeyError: + sett['ERLC'] = { + 'auto_kick_ban': { + 'channel': selected_channel_id + } + } embed = discord.Embed( title="<:check:1163142000271429662> Channel Saved", description=f"Auto Kick/Ban Log Channel set to <#{selected_channel_id}>", From 090efd17af913a4ef5c2ecf6cbe7c29e60fa8fe0 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 5 Aug 2024 10:03:18 +0530 Subject: [PATCH 15/69] Fixed Data Retention --- cogs/ActivityNotices.py | 2 +- cogs/CustomCommands.py | 7 ++-- cogs/Punishments.py | 2 +- erm.py | 38 +++++--------------- menus.py | 80 ++++++++++++++++++++++++++--------------- utils/utils.py | 32 +++++++++++++++++ 6 files changed, 95 insertions(+), 66 deletions(-) diff --git a/cogs/ActivityNotices.py b/cogs/ActivityNotices.py index 50a8e409..0ded1480 100644 --- a/cogs/ActivityNotices.py +++ b/cogs/ActivityNotices.py @@ -465,7 +465,7 @@ async def respond(embed: discord.Embed): async def core_command_request(self, ctx: commands.Context, request_type_object: str, duration: str, reason: str, return_bypass = None, override_victim = None): settings = await self.bot.settings.find_by_id(ctx.guild.id) if not settings.get('staff_management') or not settings.get('staff_management', {}).get( - f'{request_type_object.lower()}_role', None) or not settings.get('staff_management', {}).get('enabled'): + f'{request_type_object.lower()}_role', None) or not settings.get('staff_management', {}).get('channel'): await ctx.send( embed=discord.Embed( title="Not Enabled", diff --git a/cogs/CustomCommands.py b/cogs/CustomCommands.py index d3c698bd..10c22f30 100644 --- a/cogs/CustomCommands.py +++ b/cogs/CustomCommands.py @@ -322,11 +322,8 @@ async def run(self, ctx, command: str, channel: discord.TextChannel = None): else ctx.channel ) embeds = [] - if selected["message"]["embeds"] is not None: - for embed in selected["message"]["embeds"]: - embeds.append(await interpret_embed(bot, ctx, channel, embed, selected['id'])) - elif selected["message"]["embeds"] is None: - pass + for embed in selected["message"]["embeds"]: + embeds.append(await interpret_embed(bot, ctx, channel, embed, selected['id'])) view = discord.ui.View() for item in selected.get('buttons', []): diff --git a/cogs/Punishments.py b/cogs/Punishments.py index 14152b5a..7cc20963 100644 --- a/cogs/Punishments.py +++ b/cogs/Punishments.py @@ -279,7 +279,7 @@ async def punishments(self, ctx): @is_staff() async def punishment_manage(self, ctx: commands.Context): embed = discord.Embed( - title="Staff Options", + title="Management Options", description="Using this menu, you can **Manage Punishment Types** as well as **Modify Punishment**.", color=BLANK_COLOR ) diff --git a/erm.py b/erm.py index fb17b106..1dfba8c6 100644 --- a/erm.py +++ b/erm.py @@ -708,34 +708,6 @@ async def statistics_check(): end_time = time.time() logging.warning(f"Event statistics_check took {end_time - initial_time} seconds") -async def run_command(guild_id, username, message): - while True: - command = f":pm {username} {message}" - command_response = await bot.prc_api.run_command(guild_id, command) - if command_response[0] == 200: - logging.info(f"Sent PM to {username} in guild {guild_id}") - break - elif command_response[0] == 429: - retry_after = int(command_response[1].get('Retry-After', 5)) - logging.warning(f"Rate limited. Retrying after {retry_after} seconds.") - await asyncio.sleep(retry_after) - else: - logging.error(f"Failed to send PM to {username} in guild {guild_id}") - break - -def is_whitelisted(vehicle_name, whitelisted_vehicle): - vehicle_year_match = re.search(r'\d{4}$', vehicle_name) - whitelisted_year_match = re.search(r'\d{4}$', whitelisted_vehicle) - if vehicle_year_match and whitelisted_year_match: - vehicle_year = vehicle_year_match.group() - whitelisted_year = whitelisted_year_match.group() - if vehicle_year != whitelisted_year: - return False - vehicle_name_base = vehicle_name[:vehicle_year_match.start()].strip() - whitelisted_vehicle_base = whitelisted_vehicle[:whitelisted_year_match.start()].strip() - return fuzz.ratio(vehicle_name_base.lower(), whitelisted_vehicle_base.lower()) > 80 - return False - async def get_player_avatar_url(player_id): url = f"https://thumbnails.roblox.com/v1/users/avatar?userIds={player_id}&size=180x180&format=Png&isCircular=false" async with aiohttp.ClientSession() as session: @@ -757,8 +729,14 @@ async def check_whitelisted_car(): continue try: whitelisted_vehicle_roles = items['ERLC'].get('vehicle_restrictions').get('roles') + if not whitelisted_vehicle_roles: + whitelisted_vehicle_roles = items['ERLC'].get('whitelisted_vehicles_roles', []) alert_channel_id = items['ERLC'].get('vehicle_restrictions').get('channel') + if not alert_channel_id: + alert_channel_id = items['ERLC'].get('whitelisted_vehicle_alert_channel', 0) whitelisted_vehicles = items['ERLC'].get('vehicle_restrictions').get('cars', []) + if not whitelisted_vehicles: + whitelisted_vehicles = items['ERLC'].get('whitelisted_vehicles', []) alert_message = items["ERLC"].get("vehicle_restrictions").get('message', "You do not have the required role to use this vehicle. Switch it or risk being moderated.") except KeyError: logging.error(f"KeyError for guild {guild_id}") @@ -823,7 +801,7 @@ async def check_whitelisted_car(): if not has_exotic_role: logging.debug(f"Player {player.username} does not have the required role for their whitelisted vehicle.") - await run_command(guild_id, player.username, alert_message) + await run_pm_command(guild_id, player.username, alert_message,bot) if player.username not in pm_counter: pm_counter[player.username] = 1 @@ -855,7 +833,7 @@ async def check_whitelisted_car(): break elif member_found == False: logging.debug(f"Member with username {player.username} not found in guild {guild.name}.") - await run_command(guild_id, player.username, alert_message) + await run_pm_command(guild_id, player.username, alert_message,bot) if player.username not in pm_counter: pm_counter[player.username] = 1 diff --git a/menus.py b/menus.py index 797cce75..7afdcce9 100644 --- a/menus.py +++ b/menus.py @@ -6901,12 +6901,14 @@ async def toggle_vehicle_restrictions(self, interaction: discord.Interaction, bu bot = self.bot sett = await bot.settings.find_by_id(guild_id) - if not sett.get('ERLC'): + if 'ERLC' not in sett: sett['ERLC'] = {"vehicle_restrictions": {}} - - vehicle_restrictions = sett['ERLC'].get("vehicle_restrictions", {}) - vehicle_restrictions["enabled"] = not vehicle_restrictions.get("enabled", False) - sett['ERLC']["vehicle_restrictions"] = vehicle_restrictions + try: + sett['ERLC']['enable_vehicle_restrictions'] = not sett['ERLC']['enable_vehicle_restrictions'] + except KeyError: + vehicle_restrictions = sett['ERLC'].get('vehicle_restrictions', {}) + vehicle_restrictions['enabled'] = not vehicle_restrictions.get('enabled', False) + sett['ERLC']['vehicle_restrictions'] = vehicle_restrictions await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(0, name="Enable/Disable Vehicle Restrictions", value=f"If enabled, users will be alerted if they use a whitelisted vehicle without the correct roles.\n**Current Status:** {'Enabled' if vehicle_restrictions['enabled'] else 'Disabled'}") @@ -6922,12 +6924,14 @@ async def whitelisted_vehicles_roles_callback(self, interaction: discord.Interac bot = self.bot sett = await bot.settings.find_by_id(guild_id) - if not sett.get('ERLC'): + if 'ERLC' not in sett: sett['ERLC'] = {"vehicle_restrictions": {}} - - vehicle_restrictions = sett['ERLC'].get("vehicle_restrictions", {}) - vehicle_restrictions["roles"] = [i.id for i in select.values] - sett['ERLC']["vehicle_restrictions"] = vehicle_restrictions + try: + sett['ERLC']['whitelisted_vehicles_roles'] = [i.id for i in select.values] + except KeyError: + vehicle_restrictions = sett['ERLC'].get("vehicle_restrictions", {}) + vehicle_restrictions["roles"] = [i.id for i in select.values] + sett['ERLC']["vehicle_restrictions"] = vehicle_restrictions await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(5, name="Current Roles", value=", ".join([f"<@&{i.id}>" for i in select.values]) if select.values else "None") @@ -6944,12 +6948,14 @@ async def whitelisted_vehicle_alert_channel_callback(self, interaction: discord. bot = self.bot sett = await bot.settings.find_by_id(guild_id) - if not sett.get('ERLC'): + if 'ERLC' not in sett: sett['ERLC'] = {"vehicle_restrictions": {}} - - vehicle_restrictions = sett['ERLC'].get("vehicle_restrictions", {}) - vehicle_restrictions["channel"] = select.values[0].id - sett['ERLC']["vehicle_restrictions"] = vehicle_restrictions + try: + sett['ERLC']['whitelisted_vehicle_alert_channel'] = select.values[0].id + except KeyError: + vehicle_restrictions = sett['ERLC'].get("vehicle_restrictions", {}) + vehicle_restrictions["channel"] = select.values[0].id + sett['ERLC']["vehicle_restrictions"] = vehicle_restrictions await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(6, name="Current Channel", value=f"<#{select.values[0].id}>") @@ -6963,6 +6969,9 @@ async def add_vehicle_to_role(self, interaction: discord.Interaction, button: di sett = await bot.settings.find_by_id(guild_id) existing_vehicles = sett.get('ERLC', {}).get('vehicle_restrictions', {}).get('cars', []) + if not existing_vehicles: + existing_vehicles = sett.get('ERLC', {}).get('whitelisted_vehicles', []) + existing_vehicles_str = ', '.join(existing_vehicles) modal = CustomModal( @@ -6991,18 +7000,18 @@ async def add_vehicle_to_role(self, interaction: discord.Interaction, button: di if not vehicles: return - if not sett.get('ERLC'): + if 'ERLC' not in sett: sett['ERLC'] = { "vehicle_restrictions" : {} } try: - sett['ERLC']['vehicle_restrictions']['cars'] = vehicles + sett['ERLC']['whitelisted_vehicles'] = vehicles except KeyError: - sett['ERLC'] = { - 'vehicle_restrictions': { - 'cars': vehicles - } - } + if 'ERLC' not in sett: + sett['ERLC'] = {} + if 'vehicle_restrictions' not in sett['ERLC']: + sett['ERLC']['vehicle_restrictions'] = {} + sett['ERLC']['vehicle_restrictions']['cars'] = vehicles await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(7, name="Current Whitelisted Vehicles", value=", ".join(vehicles) if vehicles else "None") @@ -7016,6 +7025,9 @@ async def add_alert_message(self, interaction: discord.Interaction, button: disc sett = await bot.settings.find_by_id(guild_id) existing_message = sett.get('ERLC', {}).get('vehicle_restrictions', {}).get('message', "") + if not existing_message: + existing_message = sett.get('ERLC', {}).get('alert_message', "") + modal = CustomModal( "Add Alert Message", [ @@ -7038,18 +7050,18 @@ async def add_alert_message(self, interaction: discord.Interaction, button: disc if not modal.message.value: return - if not sett.get('ERLC'): + if 'ERLC' not in sett: sett['ERLC'] = { "vehicle_restrictions" : {} } try: - sett['ERLC']['vehicle_restrictions']['message'] = modal.message.value + sett["ERLC"]["alert_message"] = modal.message.value except KeyError: - sett['ERLC'] = { - 'vehicle_restrictions': { - 'message': modal.message.value - } - } + if 'ERLC' not in sett: + sett['ERLC'] = {} + if 'vehicle_restrictions' not in sett['ERLC']: + sett['ERLC']['vehicle_restrictions'] = {} + sett['ERLC']['vehicle_restrictions']['message'] = modal.message.value await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(8, name="Alert Message", value=modal.message.value) @@ -7245,10 +7257,20 @@ async def add_vehicle_restriction(self, interaction: discord.Interaction, button settings = await self.bot.settings.find_by_id(interaction.guild.id) enable_vehicle_restrictions = settings.get('ERLC', {}).get('vehicle_restrictions',{}).get('enabled', False) + if not enable_vehicle_restrictions: + enable_vehicle_restrictions = settings.get('ERLC', {}).get('enable_vehicle_restrictions', False) vehicle_restrictions_roles = settings.get('ERLC', {}).get('vehicle_restrictions', {}).get('roles', []) + if not vehicle_restrictions_roles: + vehicle_restrictions_roles = settings.get('ERLC', {}).get('whitelisted_vehicles_roles', []) vehicle_restrictions_channel = settings.get('ERLC', {}).get('vehicle_restrictions', {}).get('channel', 0) + if not vehicle_restrictions_channel: + vehicle_restrictions_channel = settings.get('ERLC', {}).get('whitelisted_vehicle_alert_channel', 0) vehicle_restrictions_cars = settings.get('ERLC', {}).get('vehicle_restrictions', {}).get('cars', []) + if not vehicle_restrictions_cars: + vehicle_restrictions_cars = settings.get('ERLC', {}).get('whitelisted_vehicles', []) alert_message = settings.get('ERLC', {}).get('vehicle_restrictions', {}).get('message', "") + if not alert_message: + alert_message = settings.get('ERLC', {}).get('alert_message', "") view = WhitelistVehiclesManagement( self.bot, diff --git a/utils/utils.py b/utils/utils.py index 9c6f9768..ceb2b3bf 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -15,6 +15,10 @@ import utils.prc_api as prc_api import requests import json +import re +from fuzzywuzzy import fuzz +from discord.ext.commands import Context +import logging class ArgumentMockingInstance: def __init__(self, **kwargs): @@ -589,3 +593,31 @@ async def config_change_log(bot,guild,member,data): embed.set_author(name=member.name, icon_url=member.display_avatar.url) embed.timestamp = datetime.datetime.now(datetime.timezone.utc) await log_channel.send(embed=embed) + +async def run_pm_command(guild_id, username, message, bot): + while True: + command = f":pm {username} {message}" + command_response = await bot.prc_api.run_command(guild_id, command) + if command_response[0] == 200: + logging.info(f"Sent PM to {username} in guild {guild_id}") + break + elif command_response[0] == 429: + retry_after = int(command_response[1].get('Retry-After', 5)) + logging.warning(f"Rate limited. Retrying after {retry_after} seconds.") + await asyncio.sleep(retry_after) + else: + logging.error(f"Failed to send PM to {username} in guild {guild_id}") + break + +def is_whitelisted(vehicle_name, whitelisted_vehicle): + vehicle_year_match = re.search(r'\d{4}$', vehicle_name) + whitelisted_year_match = re.search(r'\d{4}$', whitelisted_vehicle) + if vehicle_year_match and whitelisted_year_match: + vehicle_year = vehicle_year_match.group() + whitelisted_year = whitelisted_year_match.group() + if vehicle_year != whitelisted_year: + return False + vehicle_name_base = vehicle_name[:vehicle_year_match.start()].strip() + whitelisted_vehicle_base = whitelisted_vehicle[:whitelisted_year_match.start()].strip() + return fuzz.ratio(vehicle_name_base.lower(), whitelisted_vehicle_base.lower()) > 80 + return False From f1133ad1e7e1d1be696c202aa0cdcb2ef4113870 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 5 Aug 2024 10:10:01 +0530 Subject: [PATCH 16/69] Fixing Merge --- erm.py | 4 ++-- events/on_message.py | 1 + events/on_shift_end.py | 17 ++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/erm.py b/erm.py index 1dfba8c6..68449ee6 100644 --- a/erm.py +++ b/erm.py @@ -68,7 +68,7 @@ sentry_url = "" bloxlink_api_key = "" -discord.utils.setup_logging(level=logging.DEBUG) +discord.utils.setup_logging(level=logging.INFO) intents = discord.Intents.default() intents.message_content = True @@ -545,7 +545,7 @@ async def check_reminders(): status = await bot.prc_api.get_server_status(channel.guild.id) except prc_api.ResponseFailure as e: do_not_complete = True - print(status) + #print(status) if not do_not_complete: resp = await bot.prc_api.run_command(channel.guild.id, total) diff --git a/events/on_message.py b/events/on_message.py index ebabdafb..be7231b8 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -93,6 +93,7 @@ async def on_message(self, message: discord.Message): if dataset.get('ERLC', {}).get('remote_commands'): remote_commands = True remote_command_channel = dataset["ERLC"]["remote_commands"]["webhook_channel"] if dataset["ERLC"]["remote_commands"].get("webhook_channel", None) else None + # print(f"Remote commands: {remote_command_channel}") if dataset.get('ERLC',{}).get('auto_kick_ban').get('channel'): kick_ban_webhook_channel = dataset["ERLC"]["auto_kick_ban"]["channel"] diff --git a/events/on_shift_end.py b/events/on_shift_end.py index 610c1e2d..fdd9c920 100644 --- a/events/on_shift_end.py +++ b/events/on_shift_end.py @@ -77,13 +77,14 @@ async def on_shift_end(self, object_id: ObjectId): except discord.HTTPException: pass + moderation_counts = {} + for entry in shift.moderations: + if entry['type'] in moderation_counts: + moderation_counts[entry['type']] += entry['count'] + else: + moderation_counts[entry['type']] = entry['count'] + if channel is not None: - moderation_counts = {} - for entry in shift.moderations: - if entry['type'] in moderation_counts: - moderation_counts[entry['type']] += entry['count'] - else: - moderation_counts[entry['type']] = entry['count'] await channel.send(embed=discord.Embed( title="Shift Ended", color=BLANK_COLOR @@ -125,8 +126,7 @@ async def on_shift_end(self, object_id: ObjectId): if document.get("shift_reports") is not None else True ) - if shift_reports_enabled: - + if shift_reports_enabled: embed = discord.Embed( title="Shift Report", color=BLANK_COLOR @@ -160,4 +160,3 @@ async def on_shift_end(self, object_id: ObjectId): async def setup(bot: commands.Bot): await bot.add_cog(OnShiftEnd(bot)) - From 843efcc9b28c52eb92e55191ad4afeb57310d20b Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 5 Aug 2024 10:13:13 +0530 Subject: [PATCH 17/69] Fixed on_message.py --- events/on_message.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/events/on_message.py b/events/on_message.py index 1a533c9e..be7231b8 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -95,6 +95,9 @@ async def on_message(self, message: discord.Message): remote_command_channel = dataset["ERLC"]["remote_commands"]["webhook_channel"] if dataset["ERLC"]["remote_commands"].get("webhook_channel", None) else None # print(f"Remote commands: {remote_command_channel}") + if dataset.get('ERLC',{}).get('auto_kick_ban').get('channel'): + kick_ban_webhook_channel = dataset["ERLC"]["auto_kick_ban"]["channel"] + if "game_security" in dataset.keys(): if "enabled" in dataset["game_security"].keys(): if ( From c2811e848914b88caef6ef1ce6f4668a388d0eeb Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 5 Aug 2024 10:38:53 +0530 Subject: [PATCH 18/69] Moved functions to utils.py --- erm.py | 31 ------------------------------- utils/utils.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/erm.py b/erm.py index 58d01d81..cfcbd1cc 100644 --- a/erm.py +++ b/erm.py @@ -633,30 +633,6 @@ async def tempban_checks(): end_time = time.time() logging.warning('Event tempban_checks took {} seconds'.format(str(end_time - initial_time))) -async def update_channel(guild, channel_id, stat, placeholders): - try: - format_string = stat["format"] - channel_id = int(channel_id) - channel = await fetch_get_channel(guild, channel_id) - if channel: - for key, value in placeholders.items(): - format_string = format_string.replace(f"{{{key}}}", str(value)) - await channel.edit(name=format_string) - logging.info(f"Updated channel {channel_id} in guild {guild.id}") - else: - logging.error(f"Channel {channel_id} not found in guild {guild.id}") - except Exception as e: - logging.error(f"Failed to update channel {channel_id} in guild {guild.id}: {e}", exc_info=True) - -async def fetch_get_channel(target, identifier): - channel = target.get_channel(identifier) - if not channel: - try: - channel = await target.fetch_channel(identifier) - except discord.HTTPException as e: - channel = None - return channel - @tasks.loop(seconds=45, reconnect=True) async def statistics_check(): initial_time = time.time() @@ -708,13 +684,6 @@ async def statistics_check(): end_time = time.time() logging.warning(f"Event statistics_check took {end_time - initial_time} seconds") -async def get_player_avatar_url(player_id): - url = f"https://thumbnails.roblox.com/v1/users/avatar?userIds={player_id}&size=180x180&format=Png&isCircular=false" - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - data = await response.json() - return data['data'][0]['imageUrl'] - pm_counter = {} @tasks.loop(minutes=2, reconnect=True) async def check_whitelisted_car(): diff --git a/utils/utils.py b/utils/utils.py index ceb2b3bf..48e4b54e 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -621,3 +621,34 @@ def is_whitelisted(vehicle_name, whitelisted_vehicle): whitelisted_vehicle_base = whitelisted_vehicle[:whitelisted_year_match.start()].strip() return fuzz.ratio(vehicle_name_base.lower(), whitelisted_vehicle_base.lower()) > 80 return False + + +async def fetch_get_channel(target, identifier): + channel = target.get_channel(identifier) + if not channel: + try: + channel = await target.fetch_channel(identifier) + except discord.HTTPException as e: + channel = None + return channel +async def update_channel(guild, channel_id, stat, placeholders): + try: + format_string = stat["format"] + channel_id = int(channel_id) + channel = await fetch_get_channel(guild, channel_id) + if channel: + for key, value in placeholders.items(): + format_string = format_string.replace(f"{{{key}}}", str(value)) + await channel.edit(name=format_string) + logging.info(f"Updated channel {channel_id} in guild {guild.id}") + else: + logging.error(f"Channel {channel_id} not found in guild {guild.id}") + except Exception as e: + logging.error(f"Failed to update channel {channel_id} in guild {guild.id}: {e}", exc_info=True) + +async def get_player_avatar_url(player_id): + url = f"https://thumbnails.roblox.com/v1/users/avatar?userIds={player_id}&size=180x180&format=Png&isCircular=false" + async with aiohttp.ClientSession() as session: + async with session.get(url) as response: + data = await response.json() + return data['data'][0]['imageUrl'] From e6325b5ef651980ca63355d3c18308ae94a5ffef Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 5 Aug 2024 11:14:07 +0530 Subject: [PATCH 19/69] Updated menu.py --- menus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menus.py b/menus.py index 7afdcce9..06cd8e9c 100644 --- a/menus.py +++ b/menus.py @@ -6384,7 +6384,7 @@ async def antiping_enabled(self, interaction: discord.Interaction, select: disco for i in select.options: i.default = False - @discord.ui.select(cls=discord.ui.RoleSelect, placeholder="Affected Roles", row=1, max_values=5, min_values=0) + @discord.ui.select(cls=discord.ui.RoleSelect, placeholder="Affected Roles", row=1, max_values=25, min_values=0) async def affected_roles( self, interaction: discord.Interaction, select: discord.ui.RoleSelect ): @@ -6406,7 +6406,7 @@ async def affected_roles( await bot.settings.update_by_id(sett) await config_change_log(self.bot, interaction.guild, interaction.user, f"Anti Ping Affected Roles: {', '.join([f'<@&{i.id}>' for i in select.values])}.") - @discord.ui.select(cls=discord.ui.RoleSelect, placeholder="Bypass Roles", row=2, max_values=5, min_values=0) + @discord.ui.select(cls=discord.ui.RoleSelect, placeholder="Bypass Roles", row=2, max_values=25, min_values=0) async def bypass_roles( self, interaction: discord.Interaction, select: discord.ui.RoleSelect ): From 286883600fa4cbacbadaabc444c89d4393b289c8 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 5 Aug 2024 21:18:48 +0530 Subject: [PATCH 20/69] Updated menu.py --- erm.py | 2 +- menus.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erm.py b/erm.py index cfcbd1cc..d8ba4f70 100644 --- a/erm.py +++ b/erm.py @@ -151,7 +151,7 @@ async def setup_hook(self) -> None: self.oauth2_users = OAuth2Users(self.db, "oauth2") self.roblox = roblox.Client() - self.prc_api = PRCApiClient(self, base_url=config('PRC_API_URL', default='https://api.policeroleplay.community/v1'), api_key=config('PRC_API_KEY', default='default_api_key')) + self.prc_api = PRCApiClient(self, base_url=config('PRC_API_URL'), api_key=config('PRC_API_KEY')) self.bloxlink = Bloxlink(self, config('BLOXLINK_API_KEY')) Extensions = [m.name for m in iter_modules(["cogs"], prefix="cogs.")] diff --git a/menus.py b/menus.py index 06cd8e9c..c7d32d89 100644 --- a/menus.py +++ b/menus.py @@ -7379,7 +7379,7 @@ async def erlc_statics(self, interaction: discord.Interaction, button: discord.u icon_url=interaction.guild.icon.url if interaction.guild.icon else '' ) if not statistics.items(): - embed.description += "No Statistics Channels Set" + embed.description += "> No Statistics Channels Set" else: for key, value in statistics.items(): embed.description += f"**Channel:** <#{key}>\n> **Format:** `{value.get('format', 'None')}`\n" From 096b956bbb33f05147a2ab317ebadf2fe673c23a Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 7 Aug 2024 10:33:04 +0530 Subject: [PATCH 21/69] Review Patched --- .env.template | 5 ++- cogs/CustomCommands.py | 7 ++- erm.py | 2 +- events/on_message.py | 97 +----------------------------------------- 4 files changed, 11 insertions(+), 100 deletions(-) diff --git a/.env.template b/.env.template index ac62be1c..dea32f65 100644 --- a/.env.template +++ b/.env.template @@ -36,4 +36,7 @@ AI_API_ENABLED=TRUE # TRUE, or FALSE # Other ERM API Services API_PRIVATE_KEY= API_STATIC_TOKEN= -BASE_API_URL= \ No newline at end of file +BASE_API_URL= +# PRC API Services +PRC_API_URL= +PRC_API_KEY= \ No newline at end of file diff --git a/cogs/CustomCommands.py b/cogs/CustomCommands.py index 10c22f30..d3c698bd 100644 --- a/cogs/CustomCommands.py +++ b/cogs/CustomCommands.py @@ -322,8 +322,11 @@ async def run(self, ctx, command: str, channel: discord.TextChannel = None): else ctx.channel ) embeds = [] - for embed in selected["message"]["embeds"]: - embeds.append(await interpret_embed(bot, ctx, channel, embed, selected['id'])) + if selected["message"]["embeds"] is not None: + for embed in selected["message"]["embeds"]: + embeds.append(await interpret_embed(bot, ctx, channel, embed, selected['id'])) + elif selected["message"]["embeds"] is None: + pass view = discord.ui.View() for item in selected.get('buttons', []): diff --git a/erm.py b/erm.py index d8ba4f70..308e0366 100644 --- a/erm.py +++ b/erm.py @@ -609,7 +609,7 @@ async def tempban_checks(): try: cached_servers[punishment_item['Guild']] = await bot.prc_api.fetch_bans(punishment_item['Guild']) except: - raise Exception("Failed to fetch bans from PRC API") + continue punishment_item['CheckExecuted'] = True diff --git a/events/on_message.py b/events/on_message.py index be7231b8..2af1518d 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -266,102 +266,7 @@ async def on_message(self, message: discord.Message): reason = reason[:-len('- Player Not In Game')] if not reason: reason = 'No reason provided' - ctx = await bot.get_context(new_message) - roblox_user = await get_roblox_by_username(violator_user, self.bot, ctx) - roblox_client = roblox.client.Client() - if not roblox_user or roblox_user.get('errors') is not None: - return await ctx.send(embed=discord.Embed( - title="Could not find player", - description="I could not find a Roblox player with that username.", - color=BLANK_COLOR - )) - - roblox_player = await roblox_client.get_user(roblox_user['id']) - thumbnails = await roblox_client.thumbnails.get_user_avatar_thumbnails([roblox_player], type=roblox.thumbnails.AvatarThumbnailType.headshot) - thumbnail = thumbnails[0].image_url - - punishment_types = await self.bot.punishment_types.get_punishment_types(message.guild.id) - types = (punishment_types or {}).get('types', []) - preset_types = ["Warning", "Kick", "Ban", "BOLO"] - actual_types = [] - for item in preset_types+types: - if isinstance(item, str): - actual_types.append(item) - else: - actual_types.append(item['name']) - if action_type.lower() not in [item.lower() for item in actual_types]: - await message.add_reaction('❌') - return await ctx.send(embed=discord.Embed( - title="Invalid Type", - description="The type you provided is not valid.", - color=BLANK_COLOR - )) - for item in actual_types: - safe_item = item.lower() - if action_type.lower() == safe_item: - actual_type = item - break - if action_type.lower() in ["tempban", "temporary ban"]: - return await ctx.send(embed=discord.Embed( - title="Not Supported", - description="Temporary Bans are not supported.", - color=BLANK_COLOR - )) - oid = await self.bot.punishments.insert_warning( - user.id, - user.name, - roblox_player.id, - roblox_player.name, - ctx.guild.id, - reason, - actual_type, - datetime.datetime.now(tz=pytz.UTC).timestamp() - ) - - self.bot.dispatch('punishment', oid) - flags = [] - if "--kick" in reason.lower(): - flags.append('autokick') - reason = reason.replace("--kick", "") - elif "--ban" in reason.lower(): - flags.append('autoban') - reason = reason.replace("--ban", "") - warning: WarningItem = await self.bot.punishments.fetch_warning(oid) - newline = '\n' - await user.send( - embed=discord.Embed( - title="<:success:1163149118366040106> Logged Punishment", - description=( - "I have successfully logged the following punishment!" - ), - color=GREEN_COLOR - ).add_field( - name="Punishment", - value=( - f"> **Player:** {warning.username}\n" - f"> **Type:** {warning.warning_type}\n" - f"> **Moderator:** <@{warning.moderator_id}>\n" - f"> **Reason:** {warning.reason}\n" - f"> **At:** \n" - f'{"> **Until:** {}".format(int(warning.until_epoch), newline) if warning.until_epoch is not None else ""}' - f"> **ID:** `{warning.snowflake}`\n" - f"> **Custom Flags:** {'`N/A`' if len(flags) == 0 else '{}'.format(', '.join(flags))}" - ), - inline=False - ).set_thumbnail( - url=thumbnail - ) - ) - if 'autoban' in flags: - try: - await self.bot.prc_api.run_command(ctx.guild.id, ":ban {}".format(warning.username)) - except: - pass - elif 'autokick' in flags: - try: - await self.bot.prc_api.run_command(ctx.guild.id, ":kick {}".format(warning.username)) - except: - pass + new_message.content = f"{prefix}punish {violator_user} {action_type} {reason}" await bot.process_commands(new_message) await message.add_reaction('✅') From 27aab69224c6e17337c8bc9565e82d7ad18defc1 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 7 Aug 2024 10:58:05 +0530 Subject: [PATCH 22/69] Updated ShiftLogging.py --- cogs/ShiftLogging.py | 109 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/cogs/ShiftLogging.py b/cogs/ShiftLogging.py index 398e5c8c..4e8e18c8 100644 --- a/cogs/ShiftLogging.py +++ b/cogs/ShiftLogging.py @@ -1258,7 +1258,116 @@ async def response_func( view=view_page ) + @commands.guild_only() + @duty.command( + name="shifts", + description="Show all past shifts of user by shift type", + extras={"category": "Shift Management"}, + ) + @require_settings() + @app_commands.autocomplete( + type=shift_type_autocomplete + ) + @is_management() + async def duty_shifts(self, ctx: commands.Context, user: discord.User,type: str = 'Default'): + if self.bot.shift_management_disabled is True: + return await new_failure_embed( + ctx, + "Maintenance", + "This command is currently disabled as ERM is currently undergoing maintenance updates. This command will be turned off briefly to ensure that no data is lost during the maintenance.", + ) + + bot = self.bot + configItem = await bot.settings.find_by_id(ctx.guild.id) + if not configItem.get('shift_management', {}).get('enabled', False): + return await ctx.send( + embed=discord.Embed( + title="Not Enabled", + description="Shift Logging is not enabled on this server." + ) + ) + + shift_types = configItem.get('shift_types', {}).get('types', []) + msg = None + shift_type_item = None + if shift_types: + if type.lower() not in [t['name'].lower() for t in shift_types]: + msg = await ctx.send( + embed=discord.Embed( + title="Incorrect Shift Type", + description="The shift type provided is not valid.", + color=BLANK_COLOR + ), + view=(view := CustomSelectMenu( + ctx.author.id, + [ + discord.SelectOption( + label=i["name"], + value=i["name"], + description=i["name"], + ) + for i in shift_types + ] + )) + ) + timeout = await view.wait() + if timeout: + return + if view.value: + type = view.value + for item in shift_types: + if item['name'].lower() == type.lower(): + shift_type_item = item + + if not shift_type_item: + return await ctx.send( + embed=discord.Embed( + title="Incorrect Shift Type", + description="The shift type provided is not valid.", + color=BLANK_COLOR + ) + ) + + shift_cursor = self.bot.shift_management.shifts.db.find({"UserID": user.id, "Guild": ctx.guild.id, "Type": shift_type_item["name"]}) + shifts = await shift_cursor.to_list(length=None) + if not shifts: + return await ctx.send( + embed=discord.Embed( + title="No Shifts", + description="No shifts have been found for this user.", + color=BLANK_COLOR + ) + ) + + embeds = [] + for shift in shifts: + embed = discord.Embed( + title=f"{user.name}'s Shifts", + color=BLANK_COLOR + ) + embed.add_field( + name="Shift Information", + value=( + f"> **Started:** \n" + f"> **Ended:** \n" + f"> **Total Time:** {td_format(datetime.timedelta(seconds=get_elapsed_time(shift)))}\n" + f"> **Moderations:** {len(shift.get('Moderations', []))}\n" + f"> **Breaks:** {len(shift.get('Breaks', []))}" + ), + inline=False + ).set_author( + name=f"{ctx.guild.name}", + icon_url=ctx.guild.icon, + ).set_footer( + text=f"Shift Type: {shift_type_item['name']}" + ).set_thumbnail( + url=user.avatar.url if user.avatar else user.default_avatar.url + ) + embeds.append(embed) + + paginator = SelectPagination(ctx.author.id, [CustomPage(embeds=[embed], identifier=str(index+1)) for index, embed in enumerate(embeds)]) + await ctx.reply(embed=embeds[0], view=paginator.get_current_view()) async def setup(bot): await bot.add_cog(ShiftLogging(bot)) From bb5b049417b8d117af6a803d24ec79303a12f1db Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 7 Aug 2024 11:36:16 +0530 Subject: [PATCH 23/69] Updated ShiftLogging.py --- cogs/ShiftLogging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/ShiftLogging.py b/cogs/ShiftLogging.py index 4e8e18c8..88415d1d 100644 --- a/cogs/ShiftLogging.py +++ b/cogs/ShiftLogging.py @@ -1260,7 +1260,7 @@ async def response_func( @commands.guild_only() @duty.command( - name="shifts", + name="history", description="Show all past shifts of user by shift type", extras={"category": "Shift Management"}, ) From 97240f685cc5bd5c87c6d11621ba436d80ee5978 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 7 Aug 2024 11:37:07 +0530 Subject: [PATCH 24/69] Added `h` aliases in duty history. --- cogs/ShiftLogging.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cogs/ShiftLogging.py b/cogs/ShiftLogging.py index 88415d1d..7c2dc2a4 100644 --- a/cogs/ShiftLogging.py +++ b/cogs/ShiftLogging.py @@ -1263,6 +1263,7 @@ async def response_func( name="history", description="Show all past shifts of user by shift type", extras={"category": "Shift Management"}, + aliases=["h"], ) @require_settings() @app_commands.autocomplete( From dcf4b8d819bbd47625391d5898f9504cc48d735e Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 7 Aug 2024 12:39:55 +0530 Subject: [PATCH 25/69] Updated menu.py --- menus.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 8 deletions(-) diff --git a/menus.py b/menus.py index c7d32d89..8bcea2ff 100644 --- a/menus.py +++ b/menus.py @@ -6772,7 +6772,8 @@ def __init__(self, bot, sustained_interaction: Interaction, shift_types: list, a cls=discord.ui.ChannelSelect, placeholder="Webhook Channel", max_values=1, - min_values=0 + min_values=0, + channel_types=[discord.ChannelType.text] ) async def webhook_channel(self, interaction: discord.Interaction, select: discord.ui.Select): if len(select.values) == 0: @@ -6782,7 +6783,7 @@ async def webhook_channel(self, interaction: discord.Interaction, select: discor print(self.auto_data) embed = discord.Embed( title="Remote Commands", - description="", + description="This channel is where the bot will read the command log webhooks from the game server. This is used for remote commands.\n\n", color=BLANK_COLOR ) for key, value in self.auto_data.items(): @@ -7152,7 +7153,7 @@ async def kill_logs_channel( await config_change_log(self.bot, interaction.guild, interaction.user, f"Kill Logs Channel Set: <#{select.values[0].id}>") @discord.ui.button( - label='More Options', + label='RDM Alerts', row=3 ) async def more_options(self, interaction: discord.Interaction, button: discord.Button): @@ -7227,7 +7228,7 @@ async def remote_commands(self, interaction: discord.Interaction, button: discor embed = discord.Embed( title="Remote Commands", - description="", + description="This channel is where the bot will read the command log webhooks from the game server. This is used for remote commands.\n\n", color=BLANK_COLOR ) for key, value in auto_shift_data.items(): @@ -7247,7 +7248,7 @@ async def remote_commands(self, interaction: discord.Interaction, button: discor ) @discord.ui.button( - label="Add Vehicle Restriction", + label="Vehicle Restrictions", row=3 ) async def add_vehicle_restriction(self, interaction: discord.Interaction, button: discord.ui.Button): @@ -7338,6 +7339,13 @@ async def more_features(self, interaction: discord.Interaction, button: discord. title="More Features", description="", color=BLANK_COLOR + ).add_field( + name="ER:LC Statistics Updates", + value="This is where you can configure the statistics updates for ER:LC.", + inline=False + ).add_field( + name="Auto Kick/Ban Logging", + value="This is where you can configure the auto kick/ban feature for ER:LC.", ), view=view, ephemeral=True @@ -7390,7 +7398,7 @@ async def erlc_statics(self, interaction: discord.Interaction, button: discord.u ) @discord.ui.button( - label="Auto Kick/Ban", + label="Auto Kick/Ban Logging", row=3 ) async def auto_kick_ban(self, interaction: discord.Interaction, button: discord.ui.Button): @@ -7413,7 +7421,7 @@ async def auto_kick_ban(self, interaction: discord.Interaction, button: discord. color=BLANK_COLOR ).add_field( name="Kick/Ban Webhook Channel", - value="This is the channel where all automatic kick/ban logs are sent to via ERLC." + value="This is the channel where all automatic kick/ban logs are sent to via ERLC and ERM automatically logs punishment.", ).set_author( name=interaction.guild.name, icon_url=interaction.guild.icon.url if interaction.guild.icon else '' @@ -7573,6 +7581,52 @@ async def view_variables(self, interaction: discord.Interaction, button: discord ) await interaction.response.send_message(embed=embed, ephemeral=True) + @discord.ui.button(label="Help", style=discord.ButtonStyle.secondary, row=2) + async def help(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user.id != self.user_id: + return + embed = discord.Embed( + title="ER:LC Statistics Management Guide", + description=( + "Manage your ER:LC (Emergency Response: Liberty County) statistics with ease. " + "Follow the steps below to create, edit, or delete your statistics. " + "Ensure to use `{}` around variables to incorporate dynamic data in your statistics." + ), + color=BLANK_COLOR + ).add_field( + name="Creating ER:LC Statistics", + value=( + "1. Click on the **Create** button.\n" + "2. A Channel select dropdown will appear asking you to select a voice channel to set as a statistics channel.\n" + "3. Follow the prompts to complete the setup." + ), + inline=False + ).add_field( + name="Editing ER:LC Statistics", + value=( + "1. Click on the **Edit** button.\n" + "2. A Channel select dropdown will appear asking you to select a voice channel to edit statistics.\n" + "3. Follow the prompts to complete the changes." + ), + inline=False + ).add_field( + name="Deleting ER:LC Statistics", + value=( + "1. Click on the **Delete** button.\n" + "2. A Channel select dropdown will appear asking you to select a voice channel to remove from statistics.\n" + "3. Follow the prompts to complete the deletion." + ), + inline=False + ).add_field( + name="Formatting Tips", + value=( + "When adding variables to your statistics format, ensure they are enclosed in `{}`.\n" + "Example: `OnDuty Staff {onduty}`" + ), + inline=False + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + class CreateERLCStats(discord.ui.View): def __init__(self, bot, user_id, guild_id): super().__init__(timeout=600.0) @@ -7621,6 +7675,19 @@ async def done(self, interaction: discord.Interaction, button: discord.ui.Button if not modal.format.value: return channel_id = str(self.value[0].id) + if not channel_id: + return await interaction.edit_original_response( + embed=discord.Embed( + title="<:error:1164666124496019637> Error", + description="No channel selected", + color=RED_COLOR + ).set_author( + name=interaction.guild.name, + icon_url=interaction.guild.icon.url if interaction.guild.icon else '' + ) + , + view=None + ) try: sett = await self.bot.settings.find_by_id(self.guild_id) except KeyError: @@ -7636,7 +7703,7 @@ async def done(self, interaction: discord.Interaction, button: discord.ui.Button embed=discord.Embed( title="<:error:1164666124496019637> Error", description=f"<#{channel_id}> is already set as a statistics channel", - color=discord.Color.red() + color=RED_COLOR ).set_author( name=interaction.guild.name, icon_url=interaction.guild.icon.url if interaction.guild.icon else '' From 14e18bb9bf46ee8f996587ca6edfb67329d413ec Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 7 Aug 2024 21:04:32 +0530 Subject: [PATCH 26/69] Updated erm.py --- erm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erm.py b/erm.py index 225b6718..d8758311 100644 --- a/erm.py +++ b/erm.py @@ -565,9 +565,9 @@ async def check_reminders(): replied_user=True, everyone=True, roles=True, users=True )) except Exception as e: - raise e + pass except Exception as e: - raise e + pass @tasks.loop(minutes=1, reconnect=True) async def tempban_checks(): From 9a71d08e359b01d7b69c660cbe527144ef55759e Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 8 Aug 2024 19:12:10 +0530 Subject: [PATCH 27/69] Updated ERLC.py --- cogs/ERLC.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cogs/ERLC.py b/cogs/ERLC.py index dd5ff7d3..d29d9ce4 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -459,8 +459,7 @@ async def operate_and_reload_commandlogs(msg, guild_id: str): if len(embed.description) > 3800: break - embed.description += f"> [{log.username}](https://roblox.com/users/{log.user_id}/profile) ran the command `{log.command.replace("`", "")}` • \n" - + embed.description += f"> [{log.username}](https://roblox.com/users/{log.user_id}/profile) ran the command `{log.command}` • \n" if embed.description in ['', '\n']: embed.description = "> No player logs found." From 05dc12cfa568c574d31ebad204402496f07a7267 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 12 Aug 2024 17:36:02 +0530 Subject: [PATCH 28/69] Fixing db schema --- events/on_punishment.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/events/on_punishment.py b/events/on_punishment.py index bb1a60d8..31ac7b1d 100644 --- a/events/on_punishment.py +++ b/events/on_punishment.py @@ -77,28 +77,25 @@ async def get_discord_id_by_roblox_id(self, roblox_id): shift = await self.bot.shift_management.get_current_shift(moderator, guild.id) warned_discord_id = await get_discord_id_by_roblox_id(self, warning.user_id) if shift: - moderations_dict = {entry['type']: entry['count'] for entry in shift.get('Moderations', [])} - - # Normalize the warning type to lower case - warning_type_normalized = warning_type.lower() - - # Increment the count for the given warning type - moderations_dict[warning_type_normalized] = moderations_dict.get(warning_type_normalized, 0) + 1 - + # Moderations dictionary with ObjectId as keys to fix Database Schema + moderations_dict = { + ObjectId(entry['type']): entry['count'] for entry in shift.get('Moderations', []) + } + # Convert the warning type to ObjectID + warning_type_id = ObjectId(warning_type) + # Increasing count for warning type with ObjectID + moderations_dict[warning_type_id] = moderations_dict.get(warning_type_id, 0) + 1 # Convert the dictionary back to the required array format - moderations_array = [{'type': key, 'count': value} for key, value in moderations_dict.items()] - - # Create the updated document + moderations_array = [{'type': str(key), 'count': value} for key, value in moderations_dict.items()] doc = { "_id": shift['_id'], "Moderations": moderations_array } - - # Update the shift in the database await self.bot.shift_management.shifts.update_by_id(doc) - logging.info(f"Updated shift {shift['_id']} with {warning_type_normalized}") + logging.info(f"Updated shift {shift['_id']} with {warning_type_id}") + except Exception as e: - logging.error(f"Error updating shift: {e}") + logging.error(f"Failed to update shift: {e}") try: document = await self.bot.consent.db.find_one({"_id": warned_discord_id}) From 58ee5bcb7e52e1f2f543eb307162b4542721964a Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 12 Aug 2024 17:42:08 +0530 Subject: [PATCH 29/69] Updating on_punishment.py --- events/on_punishment.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/events/on_punishment.py b/events/on_punishment.py index 31ac7b1d..130fa297 100644 --- a/events/on_punishment.py +++ b/events/on_punishment.py @@ -77,15 +77,11 @@ async def get_discord_id_by_roblox_id(self, roblox_id): shift = await self.bot.shift_management.get_current_shift(moderator, guild.id) warned_discord_id = await get_discord_id_by_roblox_id(self, warning.user_id) if shift: - # Moderations dictionary with ObjectId as keys to fix Database Schema moderations_dict = { ObjectId(entry['type']): entry['count'] for entry in shift.get('Moderations', []) } - # Convert the warning type to ObjectID warning_type_id = ObjectId(warning_type) - # Increasing count for warning type with ObjectID moderations_dict[warning_type_id] = moderations_dict.get(warning_type_id, 0) + 1 - # Convert the dictionary back to the required array format moderations_array = [{'type': str(key), 'count': value} for key, value in moderations_dict.items()] doc = { "_id": shift['_id'], From 42215fe2a978a46d29c9ebd082c58be0b2b85bf9 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 12 Aug 2024 18:02:20 +0530 Subject: [PATCH 30/69] Merge main branch from "https://github.com/MikeyUsersREC/ERM" --- events/on_punishment.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/events/on_punishment.py b/events/on_punishment.py index 130fa297..3658cd0a 100644 --- a/events/on_punishment.py +++ b/events/on_punishment.py @@ -74,24 +74,9 @@ async def get_discord_id_by_roblox_id(self, roblox_id): if channel is not None: try: - shift = await self.bot.shift_management.get_current_shift(moderator, guild.id) warned_discord_id = await get_discord_id_by_roblox_id(self, warning.user_id) - if shift: - moderations_dict = { - ObjectId(entry['type']): entry['count'] for entry in shift.get('Moderations', []) - } - warning_type_id = ObjectId(warning_type) - moderations_dict[warning_type_id] = moderations_dict.get(warning_type_id, 0) + 1 - moderations_array = [{'type': str(key), 'count': value} for key, value in moderations_dict.items()] - doc = { - "_id": shift['_id'], - "Moderations": moderations_array - } - await self.bot.shift_management.shifts.update_by_id(doc) - logging.info(f"Updated shift {shift['_id']} with {warning_type_id}") - except Exception as e: - logging.error(f"Failed to update shift: {e}") + logging.error(f"Error getting warned discord ID: {e}") try: document = await self.bot.consent.db.find_one({"_id": warned_discord_id}) From 158987a48f778e45fc00fbbd9fa0eb7509992945 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 14 Aug 2024 18:03:40 +0530 Subject: [PATCH 31/69] Updated Shift Reports --- erm.py | 82 ++++++++++++++++++++--------------------- events/on_punishment.py | 10 +++++ events/on_shift_end.py | 55 +++++++++++++++++---------- 3 files changed, 87 insertions(+), 60 deletions(-) diff --git a/erm.py b/erm.py index d8758311..9669d821 100644 --- a/erm.py +++ b/erm.py @@ -755,19 +755,51 @@ async def check_whitelisted_car(): if is_whitelisted(vehicle.vehicle, whitelisted_vehicle): whitelisted = True break + if not whitelisted: + continue pattern = re.compile(re.escape(player.username), re.IGNORECASE) member_found = False for member in guild.members: if pattern.search(member.name) or pattern.search(member.display_name) or (hasattr(member, 'global_name') and member.global_name and pattern.search(member.global_name)): member_found = True - has_exotic_role = False - for role in exotic_roles: - if role in member.roles: + if member_found == True: + has_exotic_role = False + if any([role in member.roles for role in exotic_roles]): has_exotic_role = True - break - - if not has_exotic_role: - logging.debug(f"Player {player.username} does not have the required role for their whitelisted vehicle.") + + if has_exotic_role == False: + logging.debug(f"Player {player.username} does not have the required role for their whitelisted vehicle.") + await run_pm_command(guild_id, player.username, alert_message,bot) + + if player.username not in pm_counter: + pm_counter[player.username] = 1 + logging.debug(f"PM Counter for {player.username}: 1") + else: + pm_counter[player.username] += 1 + logging.debug(f"PM Counter for {player.username}: {pm_counter[player.username]}") + + if pm_counter[player.username] >= 4: + logging.info(f"Sending warning embed for {player.username} in guild {guild.name}") + try: + embed = discord.Embed( + title="Whitelisted Vehicle Warning", + description=f""" + > Player [{player.username}](https://roblox.com/users/{player.id}/profile) has been PMed 3 times to obtain the required role for their whitelisted vehicle. + """, + color=RED_COLOR, + timestamp=datetime.datetime.now(tz=pytz.UTC) + ).set_footer( + text=f"Guild: {guild.name} | Powered by ERM Systems", + ).set_thumbnail( + url=await get_player_avatar_url(player.id) + ) + await alert_channel.send(embed=embed) + except discord.HTTPException as e: + logging.error(f"Failed to send embed for {player.username} in guild {guild.name}: {e}") + logging.info(f"Removing {player.username} from PM counter") + pm_counter.pop(player.username) + elif member_found == False: + logging.debug(f"Member with username {player.username} not found in guild {guild.name}.") await run_pm_command(guild_id, player.username, alert_message,bot) if player.username not in pm_counter: @@ -797,41 +829,9 @@ async def check_whitelisted_car(): logging.error(f"Failed to send embed for {player.username} in guild {guild.name}: {e}") logging.info(f"Removing {player.username} from PM counter") pm_counter.pop(player.username) - break - elif member_found == False: - logging.debug(f"Member with username {player.username} not found in guild {guild.name}.") - await run_pm_command(guild_id, player.username, alert_message,bot) - - if player.username not in pm_counter: - pm_counter[player.username] = 1 - logging.debug(f"PM Counter for {player.username}: 1") + break else: - pm_counter[player.username] += 1 - logging.debug(f"PM Counter for {player.username}: {pm_counter[player.username]}") - - if pm_counter[player.username] >= 4: - logging.info(f"Sending warning embed for {player.username} in guild {guild.name}") - try: - embed = discord.Embed( - title="Whitelisted Vehicle Warning", - description=f""" - > Player [{player.username}](https://roblox.com/users/{player.id}/profile) has been PMed 3 times to obtain the required role for their whitelisted vehicle. - """, - color=RED_COLOR, - timestamp=datetime.datetime.now(tz=pytz.UTC) - ).set_footer( - text=f"Guild: {guild.name} | Powered by ERM Systems", - ).set_thumbnail( - url=await get_player_avatar_url(player.id) - ) - await alert_channel.send(embed=embed) - except discord.HTTPException as e: - logging.error(f"Failed to send embed for {player.username} in guild {guild.name}: {e}") - logging.info(f"Removing {player.username} from PM counter") - pm_counter.pop(player.username) - break - else: - continue + continue del matched end_time = time.time() diff --git a/events/on_punishment.py b/events/on_punishment.py index 3658cd0a..17ceffa4 100644 --- a/events/on_punishment.py +++ b/events/on_punishment.py @@ -74,7 +74,17 @@ async def get_discord_id_by_roblox_id(self, roblox_id): if channel is not None: try: + shift = await self.bot.shift_management.get_current_shift(moderator, guild.id) warned_discord_id = await get_discord_id_by_roblox_id(self, warning.user_id) + + if shift is not None: + moderations = shift.get('Moderations', []) + moderations.append(objectid) + doc = { + "_id": shift['_id'], + "Moderations": moderations + } + await self.bot.shift_management.shifts.update_by_id(doc) except Exception as e: logging.error(f"Error getting warned discord ID: {e}") diff --git a/events/on_shift_end.py b/events/on_shift_end.py index fdd9c920..db27debf 100644 --- a/events/on_shift_end.py +++ b/events/on_shift_end.py @@ -77,13 +77,40 @@ async def on_shift_end(self, object_id: ObjectId): except discord.HTTPException: pass - moderation_counts = {} - for entry in shift.moderations: - if entry['type'] in moderation_counts: - moderation_counts[entry['type']] += entry['count'] - else: - moderation_counts[entry['type']] = entry['count'] - + mods = shift.moderations if shift.moderations else [] + all_moderation_items = [ + await self.bot.punishments.find_warning_by_spec( + guild.id, identifier=moderation + ) + for moderation in mods + ] + moderations = len(all_moderation_items) + warnings = 0 + kicks = 0 + bans = 0 + bolos = 0 + custom = 0 + + for moderation in all_moderation_items: + if moderation is None: + continue + if moderation["Type"] == "Warning": + warnings += 1 + elif moderation["Type"] == "Kick": + kicks += 1 + elif moderation["Type"] == "Ban": + bans += 1 + elif moderation["Type"] == "BOLO" or moderation['Type'] == "Bolo": + bolos += 1 + elif moderation["Type"] not in [ + "Warning", + "Kick", + "Ban", + "BOLO", + "Bolo", + ]: + custom += 1 + if channel is not None: await channel.send(embed=discord.Embed( title="Shift Ended", @@ -106,12 +133,7 @@ async def on_shift_end(self, object_id: ObjectId): inline=False ).add_field( name="Moderation Details:", - value=( - "\n".join([ - f"> **{moderation_type.capitalize()}s:** {count}" - for moderation_type, count in moderation_counts.items() - ]) if moderation_counts else "> No Moderations Found" - ), + value=f"> **Warnings:** {warnings}\n> **Kicks:** {kicks}\n> **Bans:** {bans}\n> **BOLO:** {bolos}\n> **Custom:** {custom}", inline=False ).set_author( name=guild.name, @@ -141,12 +163,7 @@ async def on_shift_end(self, object_id: ObjectId): inline=False ).add_field( name="Total Moderations:", - value=( - "\n".join([ - f"> **{moderation_type.capitalize()}s:** {count}" - for moderation_type, count in moderation_counts.items() - ]) if moderation_counts else "> No Moderations Found" - ), + value=f"> **Warnings:** {warnings}\n> **Kicks:** {kicks}\n> **Bans:** {bans}\n> **BOLO:** {bolos}\n> **Custom:** {custom}", inline=False ).add_field( name="Elapsed Time", From 7c0f67728723bb7c289963c372d3f40e50d2829e Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 14 Aug 2024 18:07:00 +0530 Subject: [PATCH 32/69] Updated on_message.py --- events/on_message.py | 1 - 1 file changed, 1 deletion(-) diff --git a/events/on_message.py b/events/on_message.py index 2af1518d..8d295e12 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -268,7 +268,6 @@ async def on_message(self, message: discord.Message): reason = 'No reason provided' new_message.content = f"{prefix}punish {violator_user} {action_type} {reason}" await bot.process_commands(new_message) - await message.add_reaction('✅') if remote_commands and remote_command_channel is not None and message.channel.id in [remote_command_channel]: From d928b2018819d16e1192f573f70cdea930a8a52e Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 14 Aug 2024 18:57:06 +0530 Subject: [PATCH 33/69] Fixed ERLC Statistics --- erm.py | 19 +++++++++++++++---- menus.py | 22 +++++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/erm.py b/erm.py index 9669d821..07af23a7 100644 --- a/erm.py +++ b/erm.py @@ -634,17 +634,28 @@ async def tempban_checks(): @tasks.loop(seconds=45, reconnect=True) async def statistics_check(): initial_time = time.time() - async for guild_data in bot.settings.db.find({}): - guild_id = guild_data['_id'] + async for items in bot.settings.db.find( + {"ERLC.statistics": {"$exists": True}} + ): + guild_id = items['_id'] try: guild = await bot.fetch_guild(guild_id) except discord.errors.NotFound: + #logging.error(f"Guild {guild_id} not found") continue settings = await bot.settings.find_by_id(guild_id) - if not settings or "ERLC" not in settings or "statistics" not in settings["ERLC"]: + #print("ERLC keys:", settings['ERLC'].keys()) + + if 'ERLC' not in settings: + #logging.warning(f"Skipping guild {guild_id} due to missing ERLC section") continue + if 'statistics' not in settings['ERLC']: + #logging.warning(f"Statistics key missing in guild {guild_id}") + continue + + #print("ERLC structure:", settings['ERLC']) statistics = settings["ERLC"]["statistics"] @@ -678,7 +689,7 @@ async def statistics_check(): tasks = [update_channel(guild, channel_id, stat_value, placeholders) for channel_id, stat_value in statistics.items()] await asyncio.gather(*tasks) - + end_time = time.time() logging.warning(f"Event statistics_check took {end_time - initial_time} seconds") diff --git a/menus.py b/menus.py index 8bcea2ff..effb2d80 100644 --- a/menus.py +++ b/menus.py @@ -7712,9 +7712,25 @@ async def done(self, interaction: discord.Interaction, button: discord.ui.Button view=None ) - sett["ERLC"]["statistics"][channel_id] = { - "format": modal.format.value - } + try: + sett["ERLC"]["statistics"][channel_id] = { + "format": modal.format.value + } + except KeyError: + try: + sett["ERLC"]["statistics"] = { + channel_id: { + "format": modal.format.value + } + } + except KeyError: + sett["ERLC"] = { + "statistics": { + channel_id: { + "format": modal.format.value + } + } + } await self.bot.settings.update_by_id(sett) await config_change_log(self.bot, interaction.guild, interaction.user, f"<#{channel_id}>: {modal.format.value}") From dc5ebe73f23bcbf5d743da7e9a1e41dff08fea0c Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 14 Aug 2024 19:53:13 +0530 Subject: [PATCH 34/69] Updated LOA --- cogs/ActivityNotices.py | 93 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/cogs/ActivityNotices.py b/cogs/ActivityNotices.py index 0ded1480..09d31773 100644 --- a/cogs/ActivityNotices.py +++ b/cogs/ActivityNotices.py @@ -462,7 +462,7 @@ async def respond(embed: discord.Embed): ) - async def core_command_request(self, ctx: commands.Context, request_type_object: str, duration: str, reason: str, return_bypass = None, override_victim = None): + async def core_command_request(self, ctx: commands.Context, request_type_object: str, duration: str, reason: str, return_bypass = None, override_victim = None, starting: str = None): settings = await self.bot.settings.find_by_id(ctx.guild.id) if not settings.get('staff_management') or not settings.get('staff_management', {}).get( f'{request_type_object.lower()}_role', None) or not settings.get('staff_management', {}).get('channel'): @@ -522,6 +522,9 @@ async def core_command_request(self, ctx: commands.Context, request_type_object: ) current_timestamp = int(datetime.datetime.now().timestamp()) + if starting: + start_after_seconds = time_converter(starting) + current_timestamp += start_after_seconds expiry_timestamp = current_timestamp + duration_seconds @@ -640,6 +643,89 @@ def setup_embed() -> discord.Embed: await ctx.send( embed=embeds[0] ) + ''' + async def core_command_view(self, ctx: commands.Context, request_type_object: str): + settings = await self.bot.settings.find_by_id(ctx.guild.id) + if not settings.get('staff_management') or not settings.get('staff_management', {}).get(f'{request_type_object.lower()}_role', None): + await ctx.send( + embed=discord.Embed( + title="Not Enabled", + description=f"{request_type_object.upper()} Requests are not enabled on this server.", + color=BLANK_COLOR + ) + ) + return + + request_upper = request_type_object.upper() + request_lower = request_type_object.lower() + + active_requests = [] + async for item in self.bot.loas.db.find({ + "guild_id": ctx.guild.id, + "user_id": ctx.author.id, + "accepted": True, + "denied": False, + "expired": False, + "type": request_upper + }): + item['started_at'] = int(item['_id'].split('_')[2]) + active_requests.append(item) + + def setup_embed() -> discord.Embed: + embed = discord.Embed( + title="Activity Notices", + color=BLANK_COLOR + ) + embed.set_author( + name=ctx.guild.name, + icon_url=ctx.guild.icon + ) + return embed + + embeds = [] + for item in active_requests: + if len(embeds) == 0: + embeds.append(setup_embed()) + + if len(embeds[-1].fields) > 4: + embeds.append(setup_embed()) + embeds[-1].add_field( + name=f"Active {item['type']}", + value=( + f"> **Staff:** <@{item['user_id']}>\n" + f"> **Reason:** {item['reason']}\n" + f"> **Started At:** \n" + f"> **Ended At:** " + ), + inline=False + ) + pages = [ + CustomPage( + embeds=[embed], + identifier=str(index + 1) + ) for index, embed in enumerate(embeds) + ] + if len(pages) == 0: + return await ctx.send( + embed=discord.Embed( + title="No Activity Notices", + description="There were no active Activity Notices found.", + color=BLANK_COLOR + ) + ) + + if len(pages) != 1: + paginator = SelectPagination(ctx.author.id, pages=pages) + await ctx.send( + embed=embeds[0], + view=paginator + ) + + else: + await ctx.send( + embed=embeds[0] + ) + ''' class StaffManagement(commands.Cog): def __init__(self, bot): @@ -728,8 +814,9 @@ async def loa_active(self, ctx): @is_staff() @app_commands.describe(time="How long are you going to be on LoA for? (s/m/h/d)") @app_commands.describe(reason="What is your reason for going on LoA?") - async def loa_request(self, ctx, time, *, reason): - await self.core_commands.core_command_request(ctx, 'loa', time, reason) + @app_commands.describe(starting="When would you like to start your LOA? (s/m/h/d)") + async def loa_request(self, ctx, time, *, reason, starting: str = None): + await self.core_commands.core_command_request(ctx, 'loa', time, reason, starting=starting) @commands.guild_only() @loa.command( From 897bb62a3c912677277a50c8a7b7fe016a371f2c Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sun, 18 Aug 2024 10:16:25 +0530 Subject: [PATCH 35/69] Updated ERLC.py --- cogs/ERLC.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cogs/ERLC.py b/cogs/ERLC.py index 04bc3e3b..de05d7e8 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -458,7 +458,9 @@ async def operate_and_reload_commandlogs(msg, guild_id: str): for log in sorted_logs: if len(embed.description) > 3800: break - embed.description += f"> [{log.username}](https://roblox.com/users/{log.user_id}/profile) ran the command `{log.command}` • \n" + #Just trying to fix the log.command multi line issue here + command_text = " ".join(log.command.split()) + embed.description += f"> [{log.username}](https://roblox.com/users/{log.user_id}/profile) ran the command `{command_text}` • \n" if embed.description in ['', '\n']: embed.description = "> No player logs found." From f401e94aa997e55c02f77662955f64c33bd32b9d Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sun, 18 Aug 2024 10:42:12 +0530 Subject: [PATCH 36/69] Updated Discord Checks --- cogs/ERLC.py | 19 ++++++++++++++++ menus.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/cogs/ERLC.py b/cogs/ERLC.py index de05d7e8..e4e70eb3 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -673,6 +673,7 @@ async def server_vehicles(self, ctx: commands.Context): ) @is_staff() @is_server_linked() + @commands.cooldown(1, 60, commands.BucketType.guild) async def check(self, ctx: commands.Context): msg = await ctx.send( embed=discord.Embed( @@ -708,6 +709,8 @@ async def check(self, ctx: commands.Context): description="" ) + all_users = [] + for player in players: pattern = re.compile(re.escape(player.username), re.IGNORECASE) member_found = False @@ -729,6 +732,7 @@ async def check(self, ctx: commands.Context): if not member_found: embed.description += f"> [{player.username}](https://roblox.com/users/{player.id}/profile)\n" + all_users.append(player.username) if embed.description == "": embed.description = "> All players are in the Discord server." @@ -737,6 +741,21 @@ async def check(self, ctx: commands.Context): name=ctx.guild.name, icon_url=ctx.guild.icon ) + sett = self.bot.settings.find_by_id(guild_id) + if all_users and len(all_users) > 0: + try: + message = sett['ERLC']['discord_checks']["message"] + command = f":pm {','.join(all_users)} {message}" + await self.bot.prc_api.run_command(guild_id, command) + embed.set_footer( + text="A message has been sent to the players who are not in the Discord server." + ) + except KeyError: + pass + else: + embed.set_footer( + text="No-one needs to be messaged." + ) await msg.edit(embed=embed) async def setup(bot): diff --git a/menus.py b/menus.py index effb2d80..96a05344 100644 --- a/menus.py +++ b/menus.py @@ -7360,7 +7360,7 @@ def __init__(self, bot, guild_id): @discord.ui.button( label="ER:LC Statistics Updates", - row=3 + row=0 ) async def erlc_statics(self, interaction: discord.Interaction, button: discord.ui.Button): val = await self.interaction_check(interaction) @@ -7399,7 +7399,7 @@ async def erlc_statics(self, interaction: discord.Interaction, button: discord.u @discord.ui.button( label="Auto Kick/Ban Logging", - row=3 + row=0 ) async def auto_kick_ban(self, interaction: discord.Interaction, button: discord.ui.Button): val = await self.interaction_check(interaction) @@ -7432,6 +7432,66 @@ async def auto_kick_ban(self, interaction: discord.Interaction, button: discord. ephemeral=True ) + @discord.ui.button( + label="Discord Check Message", + row=0 + ) + async def discord_check(self, interaction: discord.Interaction, button: discord.ui.Button): + val = await self.interaction_check(interaction) + if val is False: + return + + guild_id = interaction.guild.id + bot = self.bot + + sett = await bot.settings.find_by_id(guild_id) + if not sett: + return + try: + default_message = sett['ERLC']['discord_checks']["message"] + except KeyError: + default_message = "You were not found in our discord server. Please contact any mod/admin." + modal = CustomModal( + "Discord Check Message", + [ + ( + "message", + discord.ui.TextInput( + label="Message", + placeholder="e.g. You were not found in our discord server. Please contact any mod/admin.", + default=default_message, + min_length=0, + ) + ) + ], { + "ephemeral": True + } + ) + + await interaction.response.send_modal(modal) + await modal.wait() + + if not modal.message.value: + return + + if not sett.get('ERLC'): + sett['ERLC'] = {} + try: + sett['ERLC']['discord_checks']["message"] = modal.message.value + except KeyError: + try: + sett['ERLC']['discord_checks'] = { + "message": modal.message.value + } + except KeyError: + sett['ERLC'] = { + 'discord_checks': { + 'message': modal.message.value + } + } + await bot.settings.update_by_id(sett) + await config_change_log(self.bot, interaction.guild, interaction.user, f"Discord Check Message Set: {modal.message.value}") + class AutoLogging(discord.ui.View): def __init__(self,bot,sett): super().__init__(timeout=600.0) From 262501d1ec73cc059fdfabfbdcbb858fc1d9e344 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sun, 18 Aug 2024 15:03:18 +0530 Subject: [PATCH 37/69] Updated ERLC.py --- cogs/ERLC.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cogs/ERLC.py b/cogs/ERLC.py index e4e70eb3..3304491b 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -458,8 +458,8 @@ async def operate_and_reload_commandlogs(msg, guild_id: str): for log in sorted_logs: if len(embed.description) > 3800: break - #Just trying to fix the log.command multi line issue here - command_text = " ".join(log.command.split()) + #Replacing ` with " " to prevent markdown issues + command_text = log.command.replace("`", " ") embed.description += f"> [{log.username}](https://roblox.com/users/{log.user_id}/profile) ran the command `{command_text}` • \n" if embed.description in ['', '\n']: From ae75a64013d02437ac0ba493c016f2477de9456e Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sun, 18 Aug 2024 15:07:47 +0530 Subject: [PATCH 38/69] Updated ERLC.py --- cogs/ERLC.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cogs/ERLC.py b/cogs/ERLC.py index 3304491b..293ef3da 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -73,6 +73,10 @@ async def server(self, ctx: commands.Context): async def erlc_message(self, ctx: commands.Context, *, message: str): guild_id = ctx.guild.id + try: + message = message.replace("`", "'") + except Exception: + pass command_response = await self.bot.prc_api.run_command(guild_id, f":m {message}") if command_response[0] == 200: @@ -106,6 +110,11 @@ async def erlc_message(self, ctx: commands.Context, *, message: str): async def erlc_hint(self, ctx: commands.Context, *, hint: str): guild_id = ctx.guild.id + try: + hint = hint.replace("`", "'") + except Exception: + pass + await self.secure_logging(guild_id, ctx.author.id, 'Hint', hint) command_response = await self.bot.prc_api.run_command(guild_id, f":h {hint}") @@ -181,6 +190,11 @@ async def server_link(self, ctx: commands.Context, key: str): async def server_send_command(self, ctx: commands.Context, *, command: str): if command[0] != ':': command = ':' + command + + try: + command = command.replace("`", "'") + except Exception: + pass elevated_privileges = None status: ServerStatus = await self.bot.prc_api.get_server_status(ctx.guild.id) for item in (status.co_owner_ids + [status.owner_id]): From 5ed32162d577e0e1204cb7d36d3f9c13b7514463 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 19 Aug 2024 11:22:58 +0530 Subject: [PATCH 39/69] Fixed Player Join/Leave Log and Kill Logs --- cogs/ERLC.py | 1 - utils/prc_api.py | 12 +++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/cogs/ERLC.py b/cogs/ERLC.py index 293ef3da..ddc74599 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -687,7 +687,6 @@ async def server_vehicles(self, ctx: commands.Context): ) @is_staff() @is_server_linked() - @commands.cooldown(1, 60, commands.BucketType.guild) async def check(self, ctx: commands.Context): msg = await ctx.send( embed=discord.Embed( diff --git a/utils/prc_api.py b/utils/prc_api.py index 8ef50df7..db86cd23 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -127,7 +127,11 @@ async def _send_api_request(self, method: typing.Literal['GET', 'POST'], endpoin # "ProhibitedUntil": 9999999999 # }) # response.status = 423 - + if response.status == 429: + retry_after = int(response.headers.get('retry-after'+1, 5)) + await asyncio.sleep(retry_after) + return await self._send_api_request(method, endpoint, guild_id, data, key) + return response.status, (await response.json() if response.content_type != "text/html" else {}) @@ -256,9 +260,6 @@ async def fetch_kill_logs(self, guild_id: int): killed_username=log_item['Killed'].split(':')[0], killed_user_id=log_item['Killed'].split(':')[1] ) for log_item in response_json] - elif status_code == 429: - retry_after = int(response_json[1].get('retry_after', 5)) - await asyncio.sleep(retry_after) else: raise ResponseFailure( status_code=status_code, @@ -290,9 +291,6 @@ async def fetch_player_logs(self, guild_id: int): timestamp=log_item['Timestamp'], type='join' if log_item['Join'] is True else 'leave' ) for log_item in response_json] - elif status_code == 429: - retry_after = int(response_json[1].get('retry_after', 5)) - await asyncio.sleep(retry_after) else: raise ResponseFailure( status_code=status_code, From 35c58f35197b833a4a758371ba0e7049fa7d2463 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Mon, 19 Aug 2024 12:25:19 +0530 Subject: [PATCH 40/69] Updated ShiftLogging.py --- cogs/ShiftLogging.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cogs/ShiftLogging.py b/cogs/ShiftLogging.py index 7c2dc2a4..9d8bef54 100644 --- a/cogs/ShiftLogging.py +++ b/cogs/ShiftLogging.py @@ -994,9 +994,9 @@ async def shift_leaderboard(self, ctx: commands.Context, *, type: str = None): time_str = td_format(datetime.timedelta(seconds=i["total_seconds"])) if buffer is None: - buffer = f"{member.name} • {time_str}" + buffer = f"{member.name} • {time_str} • {i['moderations']} Moderations" else: - buffer += f"\n{member.name} • {time_str}" + buffer += f"\n{member.name} • {time_str} • {i['moderations']} Moderations" data.append([ index + 1, From 390bf5ece21d074578b4c9ffe80fb621471ad0bf Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 21 Aug 2024 12:45:51 +0530 Subject: [PATCH 41/69] Custom Command Embed Edit --- cogs/CustomCommands.py | 4 +- menus.py | 745 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 747 insertions(+), 2 deletions(-) diff --git a/cogs/CustomCommands.py b/cogs/CustomCommands.py index d3c698bd..d268465c 100644 --- a/cogs/CustomCommands.py +++ b/cogs/CustomCommands.py @@ -12,7 +12,7 @@ EmbedCustomisation, MessageCustomisation, RemoveCustomCommand, - YesNoColourMenu, CustomCommandOptionSelect, CustomCommandModification, + YesNoColourMenu, CustomCommandOptionSelect, CustomCommandModification, CustomCommandModificationEdit, CounterButton, ViewVotersButton ) from utils.autocompletes import command_autocomplete @@ -206,7 +206,7 @@ async def custom_manage(self, ctx): ) ) status = False - view = CustomCommandModification(ctx.author.id, data) + view = CustomCommandModificationEdit(ctx.author.id, data) if status == True: await new_msg.edit(view=view, embed=discord.Embed( diff --git a/menus.py b/menus.py index 96a05344..fd5b5363 100644 --- a/menus.py +++ b/menus.py @@ -1457,6 +1457,264 @@ async def deny(self, interaction: discord.Interaction, button: discord.ui.Button await interaction.response.defer(ephemeral=True, thinking=True) return await generalised_interaction_check_failure(interaction.followup) +class CustomCommandModificationEdit(discord.ui.View): + def __init__(self, user_id: int, command_data: dict): + super().__init__(timeout=600) + self.user_id = user_id + self.value = None + self.command_data = command_data + + async def check_ability(self, message): + if self.command_data.get('message', None) and self.command_data.get('name', None): + for item in self.children: + if isinstance(item, discord.ui.Button): + if item.label == "Finish": + item.disabled = False + + await message.edit(view=self) + else: + for item in self.children: + if isinstance(item, discord.ui.Button): + if item.label == "Finish": + item.disabled = True + await message.edit(view=self) + + async def interaction_check(self, interaction: Interaction, /) -> bool: + if interaction.user.id == self.user_id: + return True + else: + await interaction.response.send_message(embed=discord.Embed( + title="Not Permitted", + description="You are not permitted to interact with these buttons.", + color=BLANK_COLOR + ), ephemeral=True) + return False + + async def refresh_ui(self, message: discord.Message): + embed = discord.Embed( + title="Custom Commands", + description=( + "**Command Information**\n" + f"> **Command ID:** `{self.command_data['id']}`\n" + f"> **Command Name:** {self.command_data['name']}\n" + f"> **Creator:** <@{self.command_data['author']}>\n" + f"\n**Message:**\n" + f"View the message below by clicking 'View Message'." + ), + color=BLANK_COLOR + ) + await message.edit(embed=embed) + + @discord.ui.button( + label="View Variables", + row=0 + ) + async def view_variables(self, interaction: discord.Interaction, button: discord.ui.Button): + return await interaction.response.send_message( + embed=discord.Embed( + description=( + "With **ERM Custom Commands**, you can use custom variables to adapt to the current circumstances when the command is ran.\n" + "`{user}` - Mention of the person using the command.\n" + "`{username}` - Name of the person using the command.\n" + "`{display_name}` - Display name of the person using the command.\n" + "`{time}` - Timestamp format of the time of the command execution.\n" + "`{server}` - Name of the server this is being ran in.\n" + "`{channel}` - Mention of the channel the command is being ran in.\n" + "`{prefix}` - The custom prefix of the bot.\n" + "`{onduty}` - Number of staff which are on duty within your server.\n" + "\n**PRC Specific Variables**\n" + "`{join_code}` - Join Code of the ERLC server\n" + "`{players}` - Current players in the ERLC server\n" + "`{max_players}` - Maximum players of the ERLC server\n" + "`{queue}` - Number of players in the queue\n" + "`{staff}` - Number of staff members in-game\n" + "`{mods}` - Number of mods in-game\n" + "`{admins}` - Number of admins in-game\n" + ), + color=BLANK_COLOR + ), + ephemeral=True + ) + + + @discord.ui.button( + label="Edit Name", + row=0 + ) + async def edit_custom_command_name(self, interaction: discord.Interaction, button: discord.ui.Button): + modal = CustomModal( + "Edit Custom Command Name", + [ + ( + "name", + discord.ui.TextInput( + label="Custom Command Name", + max_length=50 + ) + ) + ] + ) + + await interaction.response.send_modal(modal) + await modal.wait() + try: + chosen_identifier = modal.name.value + except ValueError: + return + + if not chosen_identifier: + return + + self.command_data['name'] = chosen_identifier + await self.check_ability(interaction.message) + await self.refresh_ui(interaction.message) + + @discord.ui.button( + label="View Message", + row=0 + ) + async def view_custom_command_message(self, interaction: discord.Interaction, button: discord.ui.Button): + await interaction.response.defer(ephemeral=True) + async def _return_failure(): + return await interaction.followup.send( + embed=discord.Embed( + title="No Message Found", + description="There is currently no message associated with this Custom Command.\nYou can add one using 'Edit Message'.", + color=BLANK_COLOR + ), + ephemeral=True + ) + + view = discord.ui.View() + for item in self.command_data.get('buttons') or []: + view.add_item( + discord.ui.Button( + label=item['label'], + url=item['url'], + row=item['row'], + style=discord.ButtonStyle.url + ) + ) + + if not self.command_data.get('message', None): + return await _return_failure() + + if not self.command_data.get('message', {}).get('content', None) and not len( + self.command_data.get('message', {}).get('embeds', [])) > 0: + return await _return_failure() + + converted = [] + for item in self.command_data.get('message').get('embeds', []): + converted.append(discord.Embed.from_dict(item)) + + await interaction.followup.send( + embeds=converted, + content=self.command_data['message'].get('content', None), + ephemeral=True, + view=view + ) + + @discord.ui.button( + label="Edit Message", + row=0 + ) + async def edit_message(self, interaction: discord.Interaction, button: discord.ui.Button): + view = MessageCustomisationEdit(interaction.user.id, self.command_data.get('message', None), external=False, + persist=False) + view.sustained_interaction = interaction + + if not self.command_data.get('message', None): + await interaction.response.send_message( + view=view, + ephemeral=True + ) + else: + converted = [] + for item in self.command_data.get('message', {}).get('embeds', []): + converted.append(discord.Embed.from_dict(item)) + + await interaction.response.send_message( + content=self.command_data.get('message', {}).get('content', None), + embeds=converted, + view=view, + ephemeral=True + ) + + await view.wait() + if view.newView: + await view.newView.wait() + chosen_message = view.newView.msg + else: + chosen_message = view.msg + + new_content = chosen_message.content + new_embeds = [] + for item in chosen_message.embeds or []: + new_embeds.append(item.to_dict()) + + self.command_data['message'] = { + "content": new_content, + "embeds": new_embeds + } + await self.check_ability(interaction.message) + await self.refresh_ui(interaction.message) + await (await interaction.original_response()).delete() + + @discord.ui.button( + label="Edit Buttons", + row=0 + ) + async def edit_buttons(self, interaction: discord.Interaction, button: discord.ui.Button): + view = ButtonCustomisation(self.command_data, interaction.user.id) + view.sustained_interaction = interaction + + if not self.command_data.get('message', None): + await interaction.response.send_message( + view=view, + ephemeral=True + ) + else: + converted = [] + for item in self.command_data.get('message', {}).get('embeds', []): + converted.append(discord.Embed.from_dict(item)) + + await interaction.response.send_message( + content=self.command_data.get('message', {}).get('content', None), + embeds=converted, + view=view, + ephemeral=True + ) + + timeout = await view.wait() + if timeout or not view.value: + return + + # # print(t(t(t(t(view.command_data) + self.command_data['buttons'] = view.command_data.get('buttons', []) + await self.check_ability(interaction.message) + await self.refresh_ui(interaction.message) + await (await interaction.original_response()).delete() + + @discord.ui.button( + label="Cancel", + style=discord.ButtonStyle.danger, + row=1 + ) + async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button): + await interaction.response.defer(thinking=False) + self.value = False + pass + + @discord.ui.button( + label="Finish", + style=discord.ButtonStyle.green, + row=1, + disabled=True + ) + async def finish(self, interaction: discord.Interaction, button: discord.ui.Button): + await interaction.response.defer(thinking=False) + self.value = True + self.stop() class CustomCommandModification(discord.ui.View): def __init__(self, user_id: int, command_data: dict): @@ -2072,6 +2330,114 @@ async def finish(self, interaction: discord.Interaction, button: discord.ui.Butt +class MessageCustomisationEdit(discord.ui.View): + def __init__(self, user_id, data=None, persist=False, external=False): + super().__init__(timeout=600.0) + if data is None: + data = {} + self.persist = persist + self.value: typing.Union[str, None] = None + self.modal: typing.Union[discord.ui.Modal, None] = None + self.newView: typing.Union[EmbedCustomisation, None] = None + self.msg = None + self.has_embeds = False + self.sustained_interaction = None + self.external = external + if data != {}: + msg = data.get('message', data) + content = msg["content"] + embeds = msg.get("embeds") + if embeds != []: + self.has_embeds = True + self.user_id = user_id + + async def check_ability(self, message): + if message.content or message.embeds is not None: + for item in self.children: + if isinstance(item, discord.ui.Button): + if item.label == "Finish": + item.disabled = False + + await message.edit(view=self) + else: + for item in self.children: + if isinstance(item, discord.ui.Button): + if item.label == "Finish": + item.disabled = True + await message.edit(view=self) + + @discord.ui.button( + label="Set Message", + style=discord.ButtonStyle.secondary, + ) + async def content( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + modal = SetContent() + await interaction.response.send_modal(modal) + await modal.wait() + self.modal = modal + if self.sustained_interaction: + await self.check_ability(await self.sustained_interaction.original_response()) + return await (await self.sustained_interaction.original_response()).edit( + content=modal.name.value + ) + await interaction.message.edit(content=modal.name.value) + await self.check_ability(interaction.message) + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Edit Embed", + style=discord.ButtonStyle.secondary, + ) + async def addembed( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + if len(interaction.message.embeds) > 0: + + existing_embed = interaction.message.embeds[0] + + newView = EmbedCustomisationEdit(interaction.user.id, self) + newView.sustained_interaction = self.sustained_interaction + self.newView = newView + + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + + await chosen_interaction_message.edit( + view=newView, embed=existing_embed) + + await interaction.response.defer(thinking=False) + # await self.check_ability(chosen_interaction_message) + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button(label="Finish", style=discord.ButtonStyle.success, disabled=True) + async def finish(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user.id == self.user_id: + self.msg = interaction.message + self.newView = self + self.value = "finish" + if not self.external: + await interaction.response.defer(thinking=False) + else: + await int_invis_embed( + interaction, + "your custom message has been saved. You can now continue with your configuration." + ) + if not self.persist and not self.sustained_interaction: + await interaction.message.delete() + self.stop() + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) class MessageCustomisation(discord.ui.View): def __init__(self, user_id, data=None, persist=False, external=False): @@ -2188,6 +2554,385 @@ async def finish(self, interaction: discord.Interaction, button: discord.ui.Butt await interaction.response.defer(ephemeral=True, thinking=True) return await generalised_interaction_check_failure(interaction.followup) +class EmbedCustomisationEdit(discord.ui.View): + def __init__(self, user_id, view=None, external=False): + super().__init__(timeout=600.0) + self.value: typing.Union[str, None] = None + self.modal: typing.Union[discord.ui.Modal, None] = None + self.msg = None + self.user_id = user_id + self.external = external + self.sustained_interaction = None + if view is not None: + self.parent_view = view + else: + self.parent_view = None + + async def check_ability(self, message): + if message.content or message.embeds is not None: + for item in self.children: + if isinstance(item, discord.ui.Button): + if item.label == "Finish": + item.disabled = False + + await message.edit(view=self) + else: + for item in self.children: + if isinstance(item, discord.ui.Button): + if item.label == "Finish": + item.disabled = True + await message.edit(view=self) + + @discord.ui.button( + label="Set Message", + style=discord.ButtonStyle.secondary, + ) + async def content( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + modal = SetContent() + await interaction.response.send_modal(modal) + await modal.wait() + self.modal = modal + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + await chosen_interaction_message.edit(content=modal.name.value) + await self.check_ability(chosen_interaction_message) + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Remove Embed", + style=discord.ButtonStyle.secondary, + ) + async def remove_embed( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + if len(interaction.message.embeds) > 0: + if self.parent_view is not None: + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + await chosen_interaction_message.edit(view=self.parent_view, embed=None) + await int_invis_embed(interaction, "embed removed.", ephemeral=True) + else: + newView = MessageCustomisation(interaction.user.id) + self.parent_view = newView + await interaction.message.edit(view=newView, embed=None) + return await int_invis_embed( + interaction, "embed removed.", ephemeral=True + ) + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button(label="Finish", style=discord.ButtonStyle.success, disabled=True) + async def finish(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user.id == self.user_id: + for item in self.children: + item.disabled = True + self.msg = interaction.message + self.value = "finish" + if not self.external: + await interaction.response.defer(thinking=False) + else: + await int_invis_embed( + interaction, + "your custom message has been created. You can now continue with your configuration." + ) + if not self.sustained_interaction: + await interaction.message.edit(view=None) + if self.parent_view is not None: + self.parent_view.stop() + self.stop() + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Set Title", + row=1, + style=discord.ButtonStyle.secondary, + ) + async def set_title( + self, interaction: discord.Interaction, _: discord.ui.Button + ): + if interaction.user.id == self.user_id: + modal = SetTitle() + await interaction.response.send_modal(modal) + await modal.wait() + self.modal = modal + embed = interaction.message.embeds[0] + embed.title = modal.name.value + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + await chosen_interaction_message.edit(embed=embed) + await self.check_ability(chosen_interaction_message) + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Set Description", + row=1, + style=discord.ButtonStyle.secondary, + ) + async def set_description( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + modal = SetDescription() + await interaction.response.send_modal(modal) + await modal.wait() + self.modal = modal + embed = interaction.message.embeds[0] + embed.description = modal.name.value + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + await chosen_interaction_message.edit(embed=embed) + await self.check_ability(chosen_interaction_message) + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Set Embed Colour", + row=1, + style=discord.ButtonStyle.secondary, + ) + async def set_color( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + modal = SetColour() + await interaction.response.send_modal(modal) + await modal.wait() + self.modal = modal + embed = interaction.message.embeds[0] + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + try: + embed.colour = modal.name.value + except TypeError: + try: + embed.colour = int(modal.name.value.replace("#", ""), 16) + except TypeError: + return await interaction.response.send_message( + embed=discord.Embed( + title="Invalid Colour", + description="This colour is invalid.", + color=BLANK_COLOR + ), + ephemeral=True + ) + await chosen_interaction_message.edit(embed=embed) + await self.check_ability(chosen_interaction_message) + + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Set Thumbnail", + row=2, + style=discord.ButtonStyle.secondary, + ) + async def set_thumbnail( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + modal = SetThumbnail() + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + await interaction.response.send_modal(modal) + await modal.wait() + self.modal = modal + embed = interaction.message.embeds[0] + embed.set_thumbnail(url=modal.thumbnail.value) + + try: + await chosen_interaction_message.edit(embed=embed) + except discord.HTTPException: + return await interaction.response.send_message( + embed=discord.Embed( + title="Unavailable URL", + description="This URL is invalid or unavailable.", + color=BLANK_COLOR + ), + ephemeral=True + ) + await self.check_ability(chosen_interaction_message) + + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Set Image", + row=2, + style=discord.ButtonStyle.secondary, + ) + async def set_image( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + modal = SetImage() + await interaction.response.send_modal(modal) + await modal.wait() + self.modal = modal + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + embed = interaction.message.embeds[0] + embed.set_image(url=modal.image.value) + try: + await chosen_interaction_message.edit(embed=embed) + except discord.HTTPException: + return await interaction.response.send_message( + embed=discord.Embed( + title="Unavailable URL", + description="This URL is invalid or unavailable.", + color=BLANK_COLOR + ), + ephemeral=True + ) + await self.check_ability(chosen_interaction_message) + + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Add Field", + row=3, + style=discord.ButtonStyle.secondary, + ) + async def add_field( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + modal = AddField() + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + + await interaction.response.send_modal(modal) + timeout = await modal.wait() + if timeout: return + self.modal = modal + if len(interaction.message.embeds) == 0: + return + embed = interaction.message.embeds[0] + try: + inline = modal.inline.value + if inline.lower() in ["yes", "y", "true"]: + inline = True + elif inline.lower() in ["no", "n", "false"]: + inline = False + else: + inline = False + embed.add_field( + name=modal.name.value, value=modal.value.value, inline=inline + ) + except AttributeError: + return + await chosen_interaction_message.edit(embed=embed) + await self.check_ability(chosen_interaction_message) + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Set Footer", + row=3, + style=discord.ButtonStyle.secondary, + ) + async def set_footer( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + modal = SetFooter() + await interaction.response.send_modal(modal) + await modal.wait() + self.modal = modal + embed = interaction.message.embeds[0] + embed.set_footer(text=modal.name.value, icon_url=modal.icon.value) + try: + await chosen_interaction_message.edit(embed=embed) + except discord.HTTPException: + return await interaction.response.send_message( + embed=discord.Embed( + title="Unavailable URL", + description="This URL is invalid or unavailable.", + color=BLANK_COLOR + ), + ephemeral=True + ) + await self.check_ability(chosen_interaction_message) + + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) + + @discord.ui.button( + label="Set Author", + row=3, + style=discord.ButtonStyle.secondary, + ) + async def set_author( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id == self.user_id: + modal = SetAuthor() + await interaction.response.send_modal(modal) + await modal.wait() + self.modal = modal + if self.sustained_interaction: + chosen_interaction_message = await self.sustained_interaction.original_response() + else: + chosen_interaction_message = interaction.message + embed = interaction.message.embeds[0] + embed.set_author( + name=modal.name.value, + url=modal.url.value, + icon_url=modal.icon.value, + ) + try: + await chosen_interaction_message.edit(embed=embed) + except discord.HTTPException: + return await interaction.response.send_message( + embed=discord.Embed( + title="Unavailable URL", + description="This URL is invalid or unavailable.", + color=BLANK_COLOR + ), + ephemeral=True + ) + await self.check_ability(chosen_interaction_message) + + else: + await interaction.response.defer(ephemeral=True, thinking=True) + return await generalised_interaction_check_failure(interaction.followup) class EmbedCustomisation(discord.ui.View): def __init__(self, user_id, view=None, external=False): From f255931298670951fb09845a0c4349d718beacf1 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Wed, 28 Aug 2024 09:54:48 +0530 Subject: [PATCH 42/69] Changed Code --- cogs/ActivityNotices.py | 5 +++-- events/on_message.py | 6 +++--- menus.py | 31 +++++++------------------------ utils/prc_api.py | 2 +- 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/cogs/ActivityNotices.py b/cogs/ActivityNotices.py index 09d31773..57d0e89a 100644 --- a/cogs/ActivityNotices.py +++ b/cogs/ActivityNotices.py @@ -762,8 +762,9 @@ async def ra_active(self, ctx): @is_staff() @app_commands.describe(time="How long are you going to be on RA for? (s/m/h/d)") @app_commands.describe(reason="What is your reason for going on RA?") - async def ra_request(self, ctx, time, *, reason): - await self.core_commands.core_command_request(ctx, 'ra', time, reason) + @app_commands.describe(starting="When would you like to start your RA? (s/m/h/d)") + async def ra_request(self, ctx, time, *, reason,starting: str = None): + await self.core_commands.core_command_request(ctx, 'ra', time, reason, starting=starting) @commands.guild_only() @ra.command( diff --git a/events/on_message.py b/events/on_message.py index 8d295e12..511a3e37 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -88,7 +88,7 @@ async def on_message(self, message: discord.Message): webhook_channel = None remote_commands = False remote_command_channel = None - kick_ban_webhook_channel = None + auto_logging_webhook_channel = None if dataset.get('ERLC', {}).get('remote_commands'): remote_commands = True @@ -96,7 +96,7 @@ async def on_message(self, message: discord.Message): # print(f"Remote commands: {remote_command_channel}") if dataset.get('ERLC',{}).get('auto_kick_ban').get('channel'): - kick_ban_webhook_channel = dataset["ERLC"]["auto_kick_ban"]["channel"] + auto_logging_webhook_channel = dataset["ERLC"]["auto_kick_ban"]["channel"] if "game_security" in dataset.keys(): if "enabled" in dataset["game_security"].keys(): @@ -197,7 +197,7 @@ async def on_message(self, message: discord.Message): ), view=view ) - if kick_ban_webhook_channel is not None and message.channel.id == kick_ban_webhook_channel: + if auto_logging_webhook_channel is not None and message.channel.id == auto_logging_webhook_channel: for embed in message.embeds: if not embed.description or not embed.title: continue diff --git a/menus.py b/menus.py index fd5b5363..c8c3f903 100644 --- a/menus.py +++ b/menus.py @@ -7647,8 +7647,7 @@ async def toggle_vehicle_restrictions(self, interaction: discord.Interaction, bu bot = self.bot sett = await bot.settings.find_by_id(guild_id) - if 'ERLC' not in sett: - sett['ERLC'] = {"vehicle_restrictions": {}} + sett.setdefault('ERLC', {"vehicle_restrictions": {}}) try: sett['ERLC']['enable_vehicle_restrictions'] = not sett['ERLC']['enable_vehicle_restrictions'] except KeyError: @@ -7670,8 +7669,7 @@ async def whitelisted_vehicles_roles_callback(self, interaction: discord.Interac bot = self.bot sett = await bot.settings.find_by_id(guild_id) - if 'ERLC' not in sett: - sett['ERLC'] = {"vehicle_restrictions": {}} + sett.setdefault('ERLC', {"vehicle_restrictions": {}}) try: sett['ERLC']['whitelisted_vehicles_roles'] = [i.id for i in select.values] except KeyError: @@ -7694,8 +7692,7 @@ async def whitelisted_vehicle_alert_channel_callback(self, interaction: discord. bot = self.bot sett = await bot.settings.find_by_id(guild_id) - if 'ERLC' not in sett: - sett['ERLC'] = {"vehicle_restrictions": {}} + sett.setdefault('ERLC', {"vehicle_restrictions": {}}) try: sett['ERLC']['whitelisted_vehicle_alert_channel'] = select.values[0].id except KeyError: @@ -7746,18 +7743,11 @@ async def add_vehicle_to_role(self, interaction: discord.Interaction, button: di if not vehicles: return - if 'ERLC' not in sett: - sett['ERLC'] = { - "vehicle_restrictions" : {} - } + sett.setdefault('ERLC', {"vehicle_restrictions": {}}) try: sett['ERLC']['whitelisted_vehicles'] = vehicles except KeyError: - if 'ERLC' not in sett: - sett['ERLC'] = {} - if 'vehicle_restrictions' not in sett['ERLC']: - sett['ERLC']['vehicle_restrictions'] = {} - sett['ERLC']['vehicle_restrictions']['cars'] = vehicles + sett['ERLC'].setdefault('vehicle_restrictions', {})['cars'] = vehicles await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(7, name="Current Whitelisted Vehicles", value=", ".join(vehicles) if vehicles else "None") @@ -7796,18 +7786,11 @@ async def add_alert_message(self, interaction: discord.Interaction, button: disc if not modal.message.value: return - if 'ERLC' not in sett: - sett['ERLC'] = { - "vehicle_restrictions" : {} - } + sett.setdefault('ERLC', {"vehicle_restrictions": {}}) try: sett["ERLC"]["alert_message"] = modal.message.value except KeyError: - if 'ERLC' not in sett: - sett['ERLC'] = {} - if 'vehicle_restrictions' not in sett['ERLC']: - sett['ERLC']['vehicle_restrictions'] = {} - sett['ERLC']['vehicle_restrictions']['message'] = modal.message.value + sett['ERLC'].setdefault('vehicle_restrictions', {})['message'] = modal.message.value await bot.settings.update_by_id(sett) embed = interaction.message.embeds[0] embed.set_field_at(8, name="Alert Message", value=modal.message.value) diff --git a/utils/prc_api.py b/utils/prc_api.py index db86cd23..7599ac9e 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -128,7 +128,7 @@ async def _send_api_request(self, method: typing.Literal['GET', 'POST'], endpoin # }) # response.status = 423 if response.status == 429: - retry_after = int(response.headers.get('retry-after'+1, 5)) + retry_after = int(response.headers.get('X_RateLimit_Reset'+1, 5)) await asyncio.sleep(retry_after) return await self._send_api_request(method, endpoint, guild_id, data, key) From 84e0af9f516f35545aac7ac5fc407ba816fa651a Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 30 Aug 2024 09:59:48 +0530 Subject: [PATCH 43/69] Updated prc_api.py --- utils/prc_api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/prc_api.py b/utils/prc_api.py index 7599ac9e..178cff6a 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -128,7 +128,8 @@ async def _send_api_request(self, method: typing.Literal['GET', 'POST'], endpoin # }) # response.status = 423 if response.status == 429: - retry_after = int(response.headers.get('X_RateLimit_Reset'+1, 5)) + X_RateLimit_Reset = int(response.headers.get('X-RateLimit-Reset', 0)) + retry_after = X_RateLimit_Reset - int(datetime.datetime.now().timestamp()) await asyncio.sleep(retry_after) return await self._send_api_request(method, endpoint, guild_id, data, key) From 1fe9721a4c21bc4eed2400258e5aaae495d8c196 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sun, 1 Sep 2024 10:57:18 +0530 Subject: [PATCH 44/69] Updated on_message.py --- events/on_message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/events/on_message.py b/events/on_message.py index 511a3e37..6440e79a 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -22,7 +22,7 @@ interpret_content, interpret_embed ) -from menus import CustomSelectMenu, GameSecurityActions +from menus import CustomSelectMenu, GameSecurityActions, CustomModal from utils.timestamp import td_format from utils.utils import get_guild_icon, get_prefix, invis_embed @@ -265,7 +265,7 @@ async def on_message(self, message: discord.Message): if reason.endswith('- Player Not In Game'): reason = reason[:-len('- Player Not In Game')] if not reason: - reason = 'No reason provided' + return await message.channel.send(f"{user.mention} Punishment not logged due to missing reason.") new_message.content = f"{prefix}punish {violator_user} {action_type} {reason}" await bot.process_commands(new_message) From 8e075e17151b9df4783e667c111c735008085e45 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 26 Sep 2024 09:35:04 +0530 Subject: [PATCH 45/69] Updated prc_api.py --- utils/prc_api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/utils/prc_api.py b/utils/prc_api.py index 178cff6a..99f55866 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -128,9 +128,7 @@ async def _send_api_request(self, method: typing.Literal['GET', 'POST'], endpoin # }) # response.status = 423 if response.status == 429: - X_RateLimit_Reset = int(response.headers.get('X-RateLimit-Reset', 0)) - retry_after = X_RateLimit_Reset - int(datetime.datetime.now().timestamp()) - await asyncio.sleep(retry_after) + asyncio.sleep(datetime.datetime.now().timestamp() - response.headers.get('X_RateLimit_Reset'+1, datetime.datetime.now().timestamp() + 5)) return await self._send_api_request(method, endpoint, guild_id, data, key) return response.status, (await response.json() if response.content_type != "text/html" else {}) From 6cbc66743b4fdeb87729da90beef427cb3210cc2 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 26 Sep 2024 10:05:27 +0530 Subject: [PATCH 46/69] Added LOA View command --- cogs/ActivityNotices.py | 78 ++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/cogs/ActivityNotices.py b/cogs/ActivityNotices.py index 57d0e89a..ac19234d 100644 --- a/cogs/ActivityNotices.py +++ b/cogs/ActivityNotices.py @@ -643,7 +643,7 @@ def setup_embed() -> discord.Embed: await ctx.send( embed=embeds[0] ) - ''' + async def core_command_view(self, ctx: commands.Context, request_type_object: str): settings = await self.bot.settings.find_by_id(ctx.guild.id) if not settings.get('staff_management') or not settings.get('staff_management', {}).get(f'{request_type_object.lower()}_role', None): @@ -655,65 +655,58 @@ async def core_command_view(self, ctx: commands.Context, request_type_object: st ) ) return - + request_upper = request_type_object.upper() - request_lower = request_type_object.lower() - - active_requests = [] + + all_requests = [] async for item in self.bot.loas.db.find({ "guild_id": ctx.guild.id, "user_id": ctx.author.id, - "accepted": True, - "denied": False, - "expired": False, "type": request_upper }): - item['started_at'] = int(item['_id'].split('_')[2]) - active_requests.append(item) + all_requests.append(item) def setup_embed() -> discord.Embed: embed = discord.Embed( - title="Activity Notices", - color=BLANK_COLOR + title="Activity Notices", + color=BLANK_COLOR ) embed.set_author( - name=ctx.guild.name, - icon_url=ctx.guild.icon + name=ctx.guild.name, + icon_url=ctx.guild.icon ) return embed - + embeds = [] - for item in active_requests: + for item in all_requests: if len(embeds) == 0: embeds.append(setup_embed()) - if len(embeds[-1].fields) > 4: embeds.append(setup_embed()) embeds[-1].add_field( - name=f"Active {item['type']}", - value=( - f"> **Staff:** <@{item['user_id']}>\n" + name=f"{item['type']}", + value=( f"> **Reason:** {item['reason']}\n" f"> **Started At:** \n" f"> **Ended At:** " - ), - inline=False + ), + inline=False ) pages = [ - CustomPage( + CustomPage( embeds=[embed], identifier=str(index + 1) - ) for index, embed in enumerate(embeds) - ] + ) for index, embed in enumerate(embeds) + ] if len(pages) == 0: - return await ctx.send( + return await ctx.send( embed=discord.Embed( - title="No Activity Notices", - description="There were no active Activity Notices found.", - color=BLANK_COLOR + title="No Activity Notices", + description="There were no active Activity Notices found.", + color=BLANK_COLOR ) - ) - + ) + if len(pages) != 1: paginator = SelectPagination(ctx.author.id, pages=pages) await ctx.send( @@ -725,7 +718,7 @@ def setup_embed() -> discord.Embed: await ctx.send( embed=embeds[0] ) - ''' + class StaffManagement(commands.Cog): def __init__(self, bot): @@ -784,6 +777,17 @@ async def ra_admin(self, ctx, member: discord.Member): await log_command_usage(self.bot,ctx.guild, ctx.user, f"RA Admin: {member}") await self.core_commands.core_command_admin(ctx, 'ra', member) + @commands.guild_only() + @ra.command( + name="view", + description="View your active RA", + extras={"category": "Staff Management"}, + with_app_command=True, + ) + @is_staff() + async def ra_view(self, ctx): + await self.core_commands.core_command_view(ctx, 'ra') + @commands.hybrid_group( name="loa", description="File a Leave of Absence request", @@ -795,6 +799,16 @@ async def ra_admin(self, ctx, member: discord.Member): async def loa(self, ctx, time, *, reason): await ctx.invoke(self.bot.get_command("loa request"), time=time, reason=reason) + @commands.guild_only() + @loa.command( + name="view", + description="View your active LOA", + extras={"category": "Staff Management"}, + with_app_command=True, + ) + @is_staff() + async def loa_view(self, ctx): + await self.core_commands.core_command_view(ctx, 'loa') @loa.command( name="active", From 30363621dffdca5f6d889883f2cb6c2c0c290910 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 26 Sep 2024 10:14:12 +0530 Subject: [PATCH 47/69] Updated ActivityNotice.py --- cogs/ActivityNotices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/ActivityNotices.py b/cogs/ActivityNotices.py index ac19234d..8aa914ee 100644 --- a/cogs/ActivityNotices.py +++ b/cogs/ActivityNotices.py @@ -786,7 +786,7 @@ async def ra_admin(self, ctx, member: discord.Member): ) @is_staff() async def ra_view(self, ctx): - await self.core_commands.core_command_view(ctx, 'ra') + await self.core_commands.core_command_view(ctx, 'loa') @commands.hybrid_group( name="loa", From f292a9bbd58943d5ceeaa9137df082d052fc0494 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 11 Oct 2024 14:05:39 +0530 Subject: [PATCH 48/69] Updated Activity Notice --- cogs/ActivityNotices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/ActivityNotices.py b/cogs/ActivityNotices.py index 8aa914ee..ac19234d 100644 --- a/cogs/ActivityNotices.py +++ b/cogs/ActivityNotices.py @@ -786,7 +786,7 @@ async def ra_admin(self, ctx, member: discord.Member): ) @is_staff() async def ra_view(self, ctx): - await self.core_commands.core_command_view(ctx, 'loa') + await self.core_commands.core_command_view(ctx, 'ra') @commands.hybrid_group( name="loa", From 533f29af0b3242914497cead3ba34e22df2b6c64 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 11 Oct 2024 14:35:35 +0530 Subject: [PATCH 49/69] Changed Added --- cogs/ActivityNotices.py | 32 +++++++++++++++++++++++++------- utils/prc_api.py | 2 +- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/cogs/ActivityNotices.py b/cogs/ActivityNotices.py index ac19234d..cb441d36 100644 --- a/cogs/ActivityNotices.py +++ b/cogs/ActivityNotices.py @@ -707,17 +707,35 @@ def setup_embed() -> discord.Embed: ) ) + if ctx.interaction: + interaction = ctx.interaction + else: + interaction = ctx + if len(pages) != 1: paginator = SelectPagination(ctx.author.id, pages=pages) - await ctx.send( - embed=embeds[0], - view=paginator - ) + try: + await interaction.response.send_message( + embed=embeds[0], + view=paginator, + ephemeral=True + ) + except: + await interaction.channel.send( + embed=embeds[0], + view=paginator + ) else: - await ctx.send( - embed=embeds[0] - ) + try: + await interaction.response.send_message( + embed=embeds[0], + ephemeral=True + ) + except: + await interaction.channel.send( + embed=embeds[0], + ) class StaffManagement(commands.Cog): diff --git a/utils/prc_api.py b/utils/prc_api.py index 99f55866..3c0a744f 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -128,7 +128,7 @@ async def _send_api_request(self, method: typing.Literal['GET', 'POST'], endpoin # }) # response.status = 423 if response.status == 429: - asyncio.sleep(datetime.datetime.now().timestamp() - response.headers.get('X_RateLimit_Reset'+1, datetime.datetime.now().timestamp() + 5)) + await asyncio.sleep(datetime.datetime.now().timestamp() - response.headers.get('X_RateLimit_Reset'+1, datetime.datetime.now().timestamp() + 5)) return await self._send_api_request(method, endpoint, guild_id, data, key) return response.status, (await response.json() if response.content_type != "text/html" else {}) From 95f1617bb2d5a73f703b09f192a34f5eca2120f9 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sun, 13 Oct 2024 11:42:42 +0530 Subject: [PATCH 50/69] Updated utils.py --- utils/utils.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/utils/utils.py b/utils/utils.py index 72cc6985..ec6396e3 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -210,9 +210,13 @@ async def interpret_embed(bot, ctx, channel, embed: dict, ics_id: int): ) except AttributeError: pass - for i in embed.fields: - i.name = await sub_vars(bot, ctx, channel, i.name) - i.value = await sub_vars(bot, ctx, channel, i.value) + new_fields = [] + for field in embed.fields: + new_field = discord.EmbedField(name=await sub_vars(bot, ctx, channel, field.name), value=await sub_vars(bot, ctx, channel, field.value), inline=field.inline) + new_fields.append(new_field) + embed.clear_fields() + for field in new_fields: + embed.add_field(field) if await bot.server_keys.db.count_documents({'_id': ctx.guild.id}) == 0: return embed # end here no point From 12018c40fda6e534a5f0cba9662324928b7e303f Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sun, 13 Oct 2024 11:57:42 +0530 Subject: [PATCH 51/69] Updated utils.py --- utils/utils.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/utils/utils.py b/utils/utils.py index ec6396e3..24ca9375 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -212,11 +212,15 @@ async def interpret_embed(bot, ctx, channel, embed: dict, ics_id: int): pass new_fields = [] for field in embed.fields: - new_field = discord.EmbedField(name=await sub_vars(bot, ctx, channel, field.name), value=await sub_vars(bot, ctx, channel, field.value), inline=field.inline) - new_fields.append(new_field) + new_fields.append( + { + "name": await sub_vars(bot, ctx, channel, field.name), + "value": await sub_vars(bot, ctx, channel, field.value), + } + ) embed.clear_fields() for field in new_fields: - embed.add_field(field) + embed.add_field(name=field["name"], value=field["value"]) if await bot.server_keys.db.count_documents({'_id': ctx.guild.id}) == 0: return embed # end here no point From 3421b78f9965afd7cf9156119177a50099154e79 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Tue, 15 Oct 2024 20:33:57 +0530 Subject: [PATCH 52/69] Updated Discord Check --- cogs/ERLC.py | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/cogs/ERLC.py b/cogs/ERLC.py index 97af2c62..751dd359 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -700,32 +700,35 @@ async def check(self, ctx: commands.Context): description="" ) + guild_members_dict = {} + for member in ctx.guild.members: + guild_members_dict[member.name.lower()] = member + guild_members_dict[member.display_name.lower()] = member + if hasattr(member, 'global_name') and member.global_name: + guild_members_dict[member.global_name.lower()] = member + + guild_member_keys = set(guild_members_dict.keys()) all_users = [] for player in players: - pattern = re.compile(re.escape(player.username), re.IGNORECASE) - member_found = False + player_username_lower = player.username.lower() - for member in ctx.guild.members: - if pattern.search(member.name) or pattern.search(member.display_name) or (hasattr(member, 'global_name') and member.global_name and pattern.search(member.global_name)): - member_found = True - break + if player_username_lower in guild_member_keys: + continue + + try: + discord_id = await get_discord_by_roblox(self.bot, player.username) + if discord_id: + member = ctx.guild.get_member(discord_id) + if member: + continue + except discord.HTTPException: + pass + + embed.description += f"> [{player.username}](https://roblox.com/users/{player.id}/profile)\n" + all_users.append(player.username) - if not member_found: - try: - discord_id = await get_discord_by_roblox(self.bot, player.username) - if discord_id: - member = ctx.guild.get_member(discord_id) - if member: - member_found = True - except discord.HTTPException: - pass - - if not member_found: - embed.description += f"> [{player.username}](https://roblox.com/users/{player.id}/profile)\n" - all_users.append(player.username) - - if embed.description == "": + if not all_users: embed.description = "> All players are in the Discord server." embed.set_author( From 61d0ed665281982bdadd942cb111dd2bd197e9ea Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 11:36:03 +0530 Subject: [PATCH 53/69] Allowing Admin Role To Accept LOAs --- cogs/ActivityNotices.py | 2 +- events/on_message.py | 2 +- menus.py | 24 +++++++++++++++--------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cogs/ActivityNotices.py b/cogs/ActivityNotices.py index 66fa9513..b0e4b98f 100644 --- a/cogs/ActivityNotices.py +++ b/cogs/ActivityNotices.py @@ -465,7 +465,7 @@ async def respond(embed: discord.Embed): async def core_command_request(self, ctx: commands.Context, request_type_object: str, duration: str, reason: str, return_bypass = None, override_victim = None, starting: str = None): settings = await self.bot.settings.find_by_id(ctx.guild.id) if not settings.get('staff_management') or not settings.get('staff_management', {}).get( - f'{request_type_object.lower()}_role', None) or not settings.get('staff_management', {}).get('channel'): + f'{request_type_object.lower()}_role', None) or not settings.get('staff_management', {}).get('enabled'): await ctx.send( embed=discord.Embed( title="Not Enabled", diff --git a/events/on_message.py b/events/on_message.py index ca81f3df..88b6b670 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -197,7 +197,7 @@ async def on_message(self, message: discord.Message): ), view=view ) - if auto_logging_webhook_channel is not None and message.channel.id == auto_logging_webhook_channel: + if remote_commands and remote_command_channel is not None and message.channel.id in [remote_command_channel]: for embed in message.embeds: if not embed.description or not embed.title: continue diff --git a/menus.py b/menus.py index 787b70c2..d379c3ed 100644 --- a/menus.py +++ b/menus.py @@ -899,18 +899,24 @@ def __init__(self, bot, roles, loa_roles, loa_object, user_id, code): async def accept(self, interaction: discord.Interaction, button: discord.ui.Button): # await interaction.response.defer() await interaction.response.defer(ephemeral=True, thinking=True) - + sett = await self.bot.settings.find(interaction.guild.id) if not any( - role in [r.id for r in interaction.user.roles] for role in self.roles + role in [r.id for r in interaction.user.roles] for role in sett.get('staff_management', {}).get("admin_role") ): - # await interaction.response.defer(ephemeral=True, thinking=True) - if ( - not interaction.user.guild_permissions.manage_guild - and not interaction.user.guild_permissions.administrator - and not interaction.user == interaction.guild.owner + await generalised_interaction_check_failure(interaction.followup) + return + else: + if not any( + role in [r.id for r in interaction.user.roles] for role in self.roles ): - await generalised_interaction_check_failure(interaction.followup) - return + # await interaction.response.defer(ephemeral=True, thinking=True) + if ( + not interaction.user.guild_permissions.manage_guild + and not interaction.user.guild_permissions.administrator + and not interaction.user == interaction.guild.owner + ): + await generalised_interaction_check_failure(interaction.followup) + return for item in self.children: item.disabled = True await interaction.message.edit(view=self) From 05d2927d35db069404abc8bd83c08f3955eb7f31 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 13:16:35 +0530 Subject: [PATCH 54/69] Updated menu.py --- menus.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/menus.py b/menus.py index d379c3ed..e0753c0c 100644 --- a/menus.py +++ b/menus.py @@ -900,11 +900,11 @@ async def accept(self, interaction: discord.Interaction, button: discord.ui.Butt # await interaction.response.defer() await interaction.response.defer(ephemeral=True, thinking=True) sett = await self.bot.settings.find(interaction.guild.id) - if not any( - role in [r.id for r in interaction.user.roles] for role in sett.get('staff_management', {}).get("admin_role") - ): - await generalised_interaction_check_failure(interaction.followup) - return + has_admin_role = any( + role in [r.id for r in interaction.user.roles] for role in sett.get('staff_management', {}).get("admin_role", []) + ) + if has_admin_role: + pass else: if not any( role in [r.id for r in interaction.user.roles] for role in self.roles From 74c87d04a2f6d9ba22161d2815c9123bb4a42593 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 13:18:46 +0530 Subject: [PATCH 55/69] Updated prc_api.py --- utils/prc_api.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/utils/prc_api.py b/utils/prc_api.py index 3c0a744f..ef54c6c5 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -127,9 +127,6 @@ async def _send_api_request(self, method: typing.Literal['GET', 'POST'], endpoin # "ProhibitedUntil": 9999999999 # }) # response.status = 423 - if response.status == 429: - await asyncio.sleep(datetime.datetime.now().timestamp() - response.headers.get('X_RateLimit_Reset'+1, datetime.datetime.now().timestamp() + 5)) - return await self._send_api_request(method, endpoint, guild_id, data, key) return response.status, (await response.json() if response.content_type != "text/html" else {}) @@ -259,6 +256,9 @@ async def fetch_kill_logs(self, guild_id: int): killed_username=log_item['Killed'].split(':')[0], killed_user_id=log_item['Killed'].split(':')[1] ) for log_item in response_json] + elif status_code == 429: + retry_after = int(response_json[1].get('retry_after', 5)) + await asyncio.sleep(retry_after) else: raise ResponseFailure( status_code=status_code, @@ -290,6 +290,9 @@ async def fetch_player_logs(self, guild_id: int): timestamp=log_item['Timestamp'], type='join' if log_item['Join'] is True else 'leave' ) for log_item in response_json] + elif status_code == 429: + retry_after = int(response_json[1].get('retry_after', 5)) + await asyncio.sleep(retry_after) else: raise ResponseFailure( status_code=status_code, From 94c6a725db610d248216544d8d89e6fb2e4f4490 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 13:21:35 +0530 Subject: [PATCH 56/69] Updaed erm.py --- erm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erm.py b/erm.py index f7c981cd..5b6841fd 100644 --- a/erm.py +++ b/erm.py @@ -148,7 +148,7 @@ async def setup_hook(self) -> None: self.oauth2_users = OAuth2Users(self.db, "oauth2") self.roblox = roblox.Client() - self.prc_api = PRCApiClient(self, base_url=config('PRC_API_URL'), api_key=config('PRC_API_KEY')) + self.prc_api = PRCApiClient(self, base_url=config('PRC_API_URL', default='https://api.policeroleplay.community/v1'), api_key=config('PRC_API_KEY', default='default_api_key')) self.bloxlink = Bloxlink(self, config('BLOXLINK_API_KEY')) Extensions = [m.name for m in iter_modules(["cogs"], prefix="cogs.")] From 668ca1d1ca2a635f21e2e1f45e9f7bb5ed0a97d8 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 13:24:28 +0530 Subject: [PATCH 57/69] Updated erm.py --- erm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erm.py b/erm.py index 5b6841fd..488efb66 100644 --- a/erm.py +++ b/erm.py @@ -676,9 +676,11 @@ async def statistics_check(): tasks = [update_channel(guild, channel_id, stat_value, placeholders) for channel_id, stat_value in statistics.items()] await asyncio.gather(*tasks) - + end_time = time.time() logging.warning(f"Event statistics_check took {end_time - initial_time} seconds") + del tasks + del placeholders pm_counter = {} @tasks.loop(minutes=2, reconnect=True) From e8d8f25858d57f50b7bd9d1de31d59db14b563c0 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 13:28:46 +0530 Subject: [PATCH 58/69] Updated on_message.py --- events/on_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events/on_message.py b/events/on_message.py index 88b6b670..fded345c 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -197,7 +197,7 @@ async def on_message(self, message: discord.Message): ), view=view ) - if remote_commands and remote_command_channel is not None and message.channel.id in [remote_command_channel]: + if remote_commands and auto_logging_webhook_channel is not None and message.channel.id in [auto_logging_webhook_channel]: for embed in message.embeds: if not embed.description or not embed.title: continue From 9faaced210f9ec7004ad9b7c2af754eae75299c4 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 13:42:46 +0530 Subject: [PATCH 59/69] Updated menu.py --- menus.py | 118 +++++++++++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/menus.py b/menus.py index e0753c0c..e60b9443 100644 --- a/menus.py +++ b/menus.py @@ -8162,65 +8162,65 @@ async def auto_kick_ban(self, interaction: discord.Interaction, button: discord. ephemeral=True ) - @discord.ui.button( - label="Discord Check Message", - row=0 - ) - async def discord_check(self, interaction: discord.Interaction, button: discord.ui.Button): - val = await self.interaction_check(interaction) - if val is False: - return - - guild_id = interaction.guild.id - bot = self.bot - - sett = await bot.settings.find_by_id(guild_id) - if not sett: - return - try: - default_message = sett['ERLC']['discord_checks']["message"] - except KeyError: - default_message = "You were not found in our discord server. Please contact any mod/admin." - modal = CustomModal( - "Discord Check Message", - [ - ( - "message", - discord.ui.TextInput( - label="Message", - placeholder="e.g. You were not found in our discord server. Please contact any mod/admin.", - default=default_message, - min_length=0, - ) - ) - ], { - "ephemeral": True - } - ) - - await interaction.response.send_modal(modal) - await modal.wait() - - if not modal.message.value: - return - - if not sett.get('ERLC'): - sett['ERLC'] = {} - try: - sett['ERLC']['discord_checks']["message"] = modal.message.value - except KeyError: - try: - sett['ERLC']['discord_checks'] = { - "message": modal.message.value - } - except KeyError: - sett['ERLC'] = { - 'discord_checks': { - 'message': modal.message.value - } - } - await bot.settings.update_by_id(sett) - await config_change_log(self.bot, interaction.guild, interaction.user, f"Discord Check Message Set: {modal.message.value}") + #@discord.ui.button( + # label="Discord Check Message", + # row=0 + #) + #async def discord_check(self, interaction: discord.Interaction, button: discord.ui.Button): + # val = await self.interaction_check(interaction) + # if val is False: + # return + # + # guild_id = interaction.guild.id + # bot = self.bot + # + # sett = await bot.settings.find_by_id(guild_id) + # if not sett: + # return + # try: + # default_message = sett['ERLC']['discord_checks']["message"] + # except KeyError: + # default_message = "You were not found in our discord server. Please contact any mod/admin." + # modal = CustomModal( + # "Discord Check Message", + # [ + # ( + # "message", + # discord.ui.TextInput( + # label="Message", + # placeholder="e.g. You were not found in our discord server. Please contact any mod/admin.", + # default=default_message, + # min_length=0, + # ) + # ) + # ], { + # "ephemeral": True + # } + # ) + # + # await interaction.response.send_modal(modal) + # await modal.wait() + # + # if not modal.message.value: + # return + # + # if not sett.get('ERLC'): + # sett['ERLC'] = {} + # try: + # sett['ERLC']['discord_checks']["message"] = modal.message.value + # except KeyError: + # try: + # sett['ERLC']['discord_checks'] = { + # "message": modal.message.value + # } + # except KeyError: + # sett['ERLC'] = { + # 'discord_checks': { + # 'message': modal.message.value + # } + # } + # await bot.settings.update_by_id(sett) + # await config_change_log(self.bot, interaction.guild, interaction.user, f"Discord Check Message Set: {modal.message.value}") class AutoLogging(discord.ui.View): def __init__(self,bot,sett): From 4a78b94c5039ce6452e52122eef0b485d48892ff Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 13:44:02 +0530 Subject: [PATCH 60/69] Updated erm.py --- erm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erm.py b/erm.py index 488efb66..dd7dab8d 100644 --- a/erm.py +++ b/erm.py @@ -676,11 +676,11 @@ async def statistics_check(): tasks = [update_channel(guild, channel_id, stat_value, placeholders) for channel_id, stat_value in statistics.items()] await asyncio.gather(*tasks) + del tasks + del placeholders end_time = time.time() logging.warning(f"Event statistics_check took {end_time - initial_time} seconds") - del tasks - del placeholders pm_counter = {} @tasks.loop(minutes=2, reconnect=True) From 3c3e4afb5f849ae2acfa64a8ab8f04e67f081886 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 13:50:26 +0530 Subject: [PATCH 61/69] Updated Discord Checks --- cogs/ERLC.py | 2 +- menus.py | 118 +++++++++++++++++++++++++-------------------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/cogs/ERLC.py b/cogs/ERLC.py index d59b678c..cc01a23e 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -746,7 +746,7 @@ async def check(self, ctx: commands.Context): sett = self.bot.settings.find_by_id(guild_id) if all_users and len(all_users) > 0: try: - message = sett['ERLC']['discord_checks']["message"] + message = sett.get('ERLC', {}).get('discord_checks', {}).get('message', "Please join the Discord server.") command = f":pm {','.join(all_users)} {message}" await self.bot.prc_api.run_command(guild_id, command) embed.set_footer( diff --git a/menus.py b/menus.py index e60b9443..e0753c0c 100644 --- a/menus.py +++ b/menus.py @@ -8162,65 +8162,65 @@ async def auto_kick_ban(self, interaction: discord.Interaction, button: discord. ephemeral=True ) - #@discord.ui.button( - # label="Discord Check Message", - # row=0 - #) - #async def discord_check(self, interaction: discord.Interaction, button: discord.ui.Button): - # val = await self.interaction_check(interaction) - # if val is False: - # return - # - # guild_id = interaction.guild.id - # bot = self.bot - # - # sett = await bot.settings.find_by_id(guild_id) - # if not sett: - # return - # try: - # default_message = sett['ERLC']['discord_checks']["message"] - # except KeyError: - # default_message = "You were not found in our discord server. Please contact any mod/admin." - # modal = CustomModal( - # "Discord Check Message", - # [ - # ( - # "message", - # discord.ui.TextInput( - # label="Message", - # placeholder="e.g. You were not found in our discord server. Please contact any mod/admin.", - # default=default_message, - # min_length=0, - # ) - # ) - # ], { - # "ephemeral": True - # } - # ) - # - # await interaction.response.send_modal(modal) - # await modal.wait() - # - # if not modal.message.value: - # return - # - # if not sett.get('ERLC'): - # sett['ERLC'] = {} - # try: - # sett['ERLC']['discord_checks']["message"] = modal.message.value - # except KeyError: - # try: - # sett['ERLC']['discord_checks'] = { - # "message": modal.message.value - # } - # except KeyError: - # sett['ERLC'] = { - # 'discord_checks': { - # 'message': modal.message.value - # } - # } - # await bot.settings.update_by_id(sett) - # await config_change_log(self.bot, interaction.guild, interaction.user, f"Discord Check Message Set: {modal.message.value}") + @discord.ui.button( + label="Discord Check Message", + row=0 + ) + async def discord_check(self, interaction: discord.Interaction, button: discord.ui.Button): + val = await self.interaction_check(interaction) + if val is False: + return + + guild_id = interaction.guild.id + bot = self.bot + + sett = await bot.settings.find_by_id(guild_id) + if not sett: + return + try: + default_message = sett['ERLC']['discord_checks']["message"] + except KeyError: + default_message = "You were not found in our discord server. Please contact any mod/admin." + modal = CustomModal( + "Discord Check Message", + [ + ( + "message", + discord.ui.TextInput( + label="Message", + placeholder="e.g. You were not found in our discord server. Please contact any mod/admin.", + default=default_message, + min_length=0, + ) + ) + ], { + "ephemeral": True + } + ) + + await interaction.response.send_modal(modal) + await modal.wait() + + if not modal.message.value: + return + + if not sett.get('ERLC'): + sett['ERLC'] = {} + try: + sett['ERLC']['discord_checks']["message"] = modal.message.value + except KeyError: + try: + sett['ERLC']['discord_checks'] = { + "message": modal.message.value + } + except KeyError: + sett['ERLC'] = { + 'discord_checks': { + 'message': modal.message.value + } + } + await bot.settings.update_by_id(sett) + await config_change_log(self.bot, interaction.guild, interaction.user, f"Discord Check Message Set: {modal.message.value}") class AutoLogging(discord.ui.View): def __init__(self,bot,sett): From a21ac8408a35acf4e42252b01699cef7f36a2325 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 13:58:06 +0530 Subject: [PATCH 62/69] Updated menus.py --- menus.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/menus.py b/menus.py index e0753c0c..5e6e7c17 100644 --- a/menus.py +++ b/menus.py @@ -8076,6 +8076,9 @@ async def more_features(self, interaction: discord.Interaction, button: discord. ).add_field( name="Auto Kick/Ban Logging", value="This is where you can configure the auto kick/ban feature for ER:LC.", + ).add_field( + name="Discord Check Message", + value="This message is sent to in-game users when a Discord Check is performed with bot if user is not found in server." ), view=view, ephemeral=True From 3f24b208333c295b7748f00bb1a7a0399b3aa757 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 22 Nov 2024 14:13:39 +0530 Subject: [PATCH 63/69] Updated menu.py --- menus.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/menus.py b/menus.py index 5e6e7c17..39059320 100644 --- a/menus.py +++ b/menus.py @@ -8076,9 +8076,11 @@ async def more_features(self, interaction: discord.Interaction, button: discord. ).add_field( name="Auto Kick/Ban Logging", value="This is where you can configure the auto kick/ban feature for ER:LC.", + inline=False ).add_field( name="Discord Check Message", - value="This message is sent to in-game users when a Discord Check is performed with bot if user is not found in server." + value="This message is sent to in-game users when a Discord Check is performed with bot if user is not found in server.", + inline=False ), view=view, ephemeral=True From 39be997992c6a0850a6c0833b2ba292d53e28db6 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sat, 23 Nov 2024 18:15:55 +0530 Subject: [PATCH 64/69] Updated on_punishment.py --- events/on_punishment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/events/on_punishment.py b/events/on_punishment.py index 17ceffa4..43105301 100644 --- a/events/on_punishment.py +++ b/events/on_punishment.py @@ -110,7 +110,9 @@ async def get_discord_id_by_roblox_id(self, roblox_id): f"> **Reason:** {warning.reason}\n" ) ).set_thumbnail(url=thumbnail) - await user_to_dm.send(embed=embed) + view = discord.ui.View() + view.add_item(discord.ui.Button(label="Appeal Moderation", url=f"https://ermbot.xyz/{guild.id}/{warning.snowflake}/appeal")) + await user_to_dm.send(embed=embed, view=view) logging.info(f"Sent DM to user {warned_discord_id} about punishment.") except Exception as e: pass From e17170be2088fddc899e4b8cdc438cc14d2404e4 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Tue, 3 Dec 2024 12:28:51 +0530 Subject: [PATCH 65/69] Updated PRC_API --- utils/prc_api.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/prc_api.py b/utils/prc_api.py index 198706e3..e36f83f3 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -4,6 +4,7 @@ import discord import roblox +import pytz from discord.ext import commands import aiohttp from decouple import config @@ -263,7 +264,7 @@ async def fetch_kill_logs(self, guild_id: int): killed_user_id=log_item['Killed'].split(':')[1] ) for log_item in response_json] elif status_code == 429: - rate_limit_reset = int(command_response[1].get('RateLimit-Reset', 5)) + rate_limit_reset = int(response_json[1].get('RateLimit-Reset', 5)) reset_time = int(rate_limit_reset) current_time = int(datetime.datetime.now(tz=pytz.UTC).timestamp()) retry_after = reset_time - current_time @@ -300,7 +301,7 @@ async def fetch_player_logs(self, guild_id: int): type='join' if log_item['Join'] is True else 'leave' ) for log_item in response_json] elif status_code == 429: - rate_limit_reset = int(command_response[1].get('RateLimit-Reset', 5)) + rate_limit_reset = int(response_json[1].get('RateLimit-Reset', 5)) reset_time = int(rate_limit_reset) current_time = int(datetime.datetime.now(tz=pytz.UTC).timestamp()) retry_after = reset_time - current_time @@ -326,7 +327,7 @@ async def unban_user(self, guild_id: int, user_id: int): "command": ":unban {}".format(str(user_id)) }) if status_code == 429: - rate_limit_reset = int(command_response[1].get('RateLimit-Reset', 5)) + rate_limit_reset = int(response_json[1].get('RateLimit-Reset', 5)) reset_time = int(rate_limit_reset) current_time = int(datetime.datetime.now(tz=pytz.UTC).timestamp()) retry_after = reset_time - current_time From 43787c32138e236df35dfb9e939e3cff93e727b8 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Tue, 3 Dec 2024 17:48:28 +0530 Subject: [PATCH 66/69] Fixed ERM Link Command --- erm.py | 27 +++++++++++++++++++++++++++ menus.py | 6 ++++-- utils/utils.py | 27 ++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/erm.py b/erm.py index 7d85ae0b..9c34277f 100644 --- a/erm.py +++ b/erm.py @@ -372,6 +372,33 @@ async def management_predicate(ctx): def is_management(): return commands.check(management_predicate) +async def advance_check(bot_obj, guild, member, permission): + if member.guild_permissions.administrator: + return True + guild_settings = await bot_obj.settings.find_by_id(guild.id) + if guild_settings and "advance_perm" in guild_settings: + member_roles = [str(role.id) for role in member.roles] + for role_id in member_roles: + if role_id in guild_settings["advance_perm"]: + if guild_settings["advance_perm"][role_id] == permission: + return True + + if await management_check(bot_obj, guild, member): + return True + + if await admin_check(bot_obj, guild, member): + return True + + return False + +async def advance_predicate(ctx, permission): + if ctx.guild is None: + return True + else: + return await advance_check(ctx.bot, ctx.guild, ctx.author, permission) + +def advance_perm(permission): + return commands.check(lambda ctx: advance_predicate(ctx, permission)) async def check_privacy(bot: Bot, guild: int, setting: str): privacySettings = await bot.privacy.find_by_id(guild) diff --git a/menus.py b/menus.py index a5b44bc7..0b2f0641 100644 --- a/menus.py +++ b/menus.py @@ -11032,7 +11032,9 @@ async def code_verification(self, interaction: discord.Interaction, button: disc if full_string.lower() in new_user.description.lower(): await self.bot.pending_oauth2.db.delete_one({"discord_id": interaction.user.id}) - await self.bot.oauth2_users.db.insert_one({"roblox_id": new_user.id, "discord_id": interaction.user.id}) + if await self.bot.oauth2_users.db.find_one({"discord_id": interaction.user.id}): + await self.bot.oauth2_users.db.delete_one({"discord_id": interaction.user.id}) + await self.bot.oauth2_users.insert_one({"roblox_id": new_user.id, "discord_id": interaction.user.id}) self.mode = "Code" self.username = new_user.name @@ -11051,4 +11053,4 @@ async def code_verification(self, interaction: discord.Interaction, button: disc color=BLANK_COLOR ), view=None - ) + ) \ No newline at end of file diff --git a/utils/utils.py b/utils/utils.py index 69e09b8f..e860a3cf 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -688,4 +688,29 @@ async def secure_logging(bot, guild_id, author_id, interpret_type: typing.Litera description=f"[{(await bot.bloxlink.get_roblox_info(bloxlink_user['robloxID']))['name']}:{bloxlink_user['robloxID']}](https://roblox.com/users/{bloxlink_user['robloxID']}/profile) attempted to use the command: {'`:m {}`'.format(command_string) if interpret_type == 'Message' else ('`:h {}`'.format(command_string) if interpret_type == 'Hint' else '`{}`'.format(command_string))}", color=RED_COLOR ).set_footer(text=f"Private Server: {server_status.join_key}") - ) \ No newline at end of file + ) + +def map_permissions(permission: str): + permissions = { + "Actions Management": 0, + "Actions Staff": 1, + "Activity Management": 2, + "Activity Staff": 3, + "LOA/RA Management": 4, + "LOA/RA Staff": 5, + "Configuration Management": 6, + "Configuration Staff": 7, + "Custom Commands Management": 8, + "Custom Commands Staff": 9, + "ERLC Management": 10, + "ERLC Staff": 11, + "Game Logging": 12, + "Punishment Management": 13, + "Punishment Staff": 14, + "Reminders Management": 15, + "Reminders Staff": 16, + "Roblox Search": 17, + "Shift Management": 18, + "Shift Staff": 19, + } + return permissions.get(permission, "Unknown") \ No newline at end of file From 17ce13e2f7a4b1c3c984ecba3c482259fc3d3317 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Sat, 28 Dec 2024 09:58:35 +0530 Subject: [PATCH 67/69] Updated erm.py --- erm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erm.py b/erm.py index 732ec677..9b76169a 100644 --- a/erm.py +++ b/erm.py @@ -970,7 +970,6 @@ def process_kill_logs(kill_logs, last_timestamp): for log in sorted(kill_logs): if log.timestamp <= last_timestamp: continue - latest_timestamp = max(latest_timestamp, log.timestamp) embed = discord.Embed( title="Kill Log", @@ -989,7 +988,8 @@ def process_player_logs(player_logs, last_timestamp): for log in sorted(player_logs): if log.timestamp <= last_timestamp: continue - + if log.timestamp < (datetime.datetime.now().timestamp() - 600): + continue latest_timestamp = max(latest_timestamp, log.timestamp) embed = discord.Embed( title=f"Player {'Join' if log.type == 'join' else 'Leave'} Log", From 0d6a7c59af1be744a2acd7d89cc4005b42e91751 Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Thu, 2 Jan 2025 14:09:22 +0530 Subject: [PATCH 68/69] Fixed Conflicts --- menus.py | 47 +++++++++++++++++++++++++++++++++------ tasks/iterate_prc_logs.py | 2 ++ utils/prc_api.py | 6 +---- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/menus.py b/menus.py index 20d86432..2310b098 100644 --- a/menus.py +++ b/menus.py @@ -8171,18 +8171,52 @@ async def vehicle_restrictions(self, interaction: discord.Interaction, button: d ephemeral=True ) - - @discord.ui.button( - label="ER:LC Statistics", - row=3, - disabled=False + label="More Features", + row=3 ) - async def erlc_statistics(self, interaction: discord.Interaction, button: discord.ui.Button): + async def more_features(self, interaction: discord.Interaction, button: discord.ui.Button): val = await self.interaction_check(interaction) if val is False: return + sett = await self.bot.settings.find_by_id(interaction.guild.id) + view = MoreOptions(self.bot, interaction.guild.id) + await interaction.response.send_message( + embed = discord.Embed( + title="More Features", + description="", + color=BLANK_COLOR + ).add_field( + name="ER:LC Statistics Updates", + value="This is where you can configure the statistics updates for ER:LC.", + inline=False + ).add_field( + name="Auto Kick/Ban Logging", + value="This is where you can configure the auto kick/ban feature for ER:LC.", + inline=False + ).add_field( + name="Discord Check Message", + value="This message is sent to in-game users when a Discord Check is performed with bot if user is not found in server.", + inline=False + ), + view=view, + ephemeral=True + ) +class MoreOptions(discord.ui.View): + def __init__(self, bot, guild_id): + super().__init__(timeout=900.0) + self.bot = bot + self.guild_id = guild_id + @discord.ui.button( + label="ER:LC Statistics Updates", + row=0 + ) + async def erlc_statics(self, interaction: discord.Interaction, button: discord.ui.Button): + val = await self.interaction_check(interaction) + if val is False: + return + view = ERLCStats(self.bot,interaction.user.id,interaction.guild.id) sett = await self.bot.settings.find_by_id(interaction.guild.id) if not sett: @@ -8307,7 +8341,6 @@ async def discord_check(self, interaction: discord.Interaction, button: discord. } await bot.settings.update_by_id(sett) await config_change_log(self.bot, interaction.guild, interaction.user, f"Discord Check Message Set: {modal.message.value}") - class AutoLogging(discord.ui.View): def __init__(self,bot,sett): super().__init__(timeout=600.0) diff --git a/tasks/iterate_prc_logs.py b/tasks/iterate_prc_logs.py index a135a6a4..57238269 100644 --- a/tasks/iterate_prc_logs.py +++ b/tasks/iterate_prc_logs.py @@ -217,6 +217,8 @@ async def process_player_logs(bot, settings, guild_id, player_logs, last_timesta for log in sorted(player_logs): if log.timestamp <= last_timestamp: continue + if log.timestamp < bot.start_time: + continue if log.type == 'join': new_join_ids.append(log.user_id) diff --git a/utils/prc_api.py b/utils/prc_api.py index e2578f95..9bd809db 100644 --- a/utils/prc_api.py +++ b/utils/prc_api.py @@ -331,11 +331,7 @@ async def unban_user(self, guild_id: int, user_id: int): "command": ":unban {}".format(str(user_id)) }) if status_code == 429: - rate_limit_reset = int(response_json[1].get('RateLimit-Reset', 5)) - reset_time = int(rate_limit_reset) - current_time = int(datetime.datetime.now(tz=pytz.UTC).timestamp()) - retry_after = reset_time - current_time - await asyncio.sleep(retry_after) + await asyncio.sleep(response_json['retry_after']+0.1) else: return status_code From 23ddc791e28f5bf363c7dadbcb174931f90fee8a Mon Sep 17 00:00:00 2001 From: Shashank Pandey Date: Fri, 3 Jan 2025 13:14:55 +0530 Subject: [PATCH 69/69] Automated Discord Checks --- cogs/ERLC.py | 2 +- erm.py | 2 + menus.py | 91 +++++++++++++++++- tasks/check_whitelisted_car.py | 2 +- tasks/discord_checks.py | 166 +++++++++++++++++++++++++++++++++ 5 files changed, 256 insertions(+), 7 deletions(-) create mode 100644 tasks/discord_checks.py diff --git a/cogs/ERLC.py b/cogs/ERLC.py index 2050fde5..ae63790e 100644 --- a/cogs/ERLC.py +++ b/cogs/ERLC.py @@ -817,7 +817,7 @@ async def check(self, ctx: commands.Context): sett = self.bot.settings.find_by_id(guild_id) if all_users and len(all_users) > 0: try: - message = sett.get('ERLC', {}).get('discord_checks', {}).get('message', "Please join the Discord server.") + message = sett.get('ERLC', {}).get('discord_checks', {}).get('message', "You are not in the communication server. Please join the server to avoid being kicked.") command = f":pm {','.join(all_users)} {message}" await self.bot.prc_api.run_command(guild_id, command) embed.set_footer( diff --git a/erm.py b/erm.py index b6674d38..94605582 100644 --- a/erm.py +++ b/erm.py @@ -16,6 +16,7 @@ from tasks.statistics_check import statistics_check from tasks.change_status import change_status from tasks.check_whitelisted_car import check_whitelisted_car +from tasks.discord_checks import discord_checks from utils.log_tracker import LogTracker from utils.mongo import Document @@ -228,6 +229,7 @@ async def setup_hook(self) -> None: check_whitelisted_car.start(bot) change_status.start(bot) process_scheduled_pms.start(bot) + discord_checks.start(bot) logging.info("Setup_hook complete! All tasks are now running!") async for document in self.views.db.find({}): diff --git a/menus.py b/menus.py index 2310b098..6e2aa438 100644 --- a/menus.py +++ b/menus.py @@ -8195,8 +8195,8 @@ async def more_features(self, interaction: discord.Interaction, button: discord. value="This is where you can configure the auto kick/ban feature for ER:LC.", inline=False ).add_field( - name="Discord Check Message", - value="This message is sent to in-game users when a Discord Check is performed with bot if user is not found in server.", + name="Discord Checks", + value="This is where you can configure the Discord Check message that is sent to in-game users when a Discord Check is performed with bot if user is not found in server and automated alert is sent to the channel.", inline=False ), view=view, @@ -8283,10 +8283,63 @@ async def auto_kick_ban(self, interaction: discord.Interaction, button: discord. ) @discord.ui.button( - label="Discord Check Message", + label="Discord Checks", row=0 ) async def discord_check(self, interaction: discord.Interaction, button: discord.ui.Button): + val = await self.interaction_check(interaction) + if val is False: return + + sett = await self.bot.settings.find_by_id(interaction.guild.id) + + view = DiscordCheck(self.bot, interaction.guild.id, sett) + embed = discord.Embed( + title="Discord Check Setup", + description="This is where you can configure the Discord Check message that is sent to in-game users when a Discord Check is performed with bot if user is not found in server.", + color=BLANK_COLOR + ).set_author( + name=interaction.guild.name, + icon_url=interaction.guild.icon.url if interaction.guild.icon else '' + ) + await interaction.response.send_message( + embed=embed, + view=view, + ephemeral=True + ) + + +class DiscordCheck(discord.ui.View): + def __init__(self, bot, guild_id, sett): + super().__init__(timeout=900.0) + self.bot = bot + self.guild_id = guild_id + self.sett = sett + + self.discord_check_message_button = discord.ui.Button( + label="Set Discord Check Message", + style=discord.ButtonStyle.secondary, + row=0 + ) + + self.add_item(self.discord_check_message_button) + self.discord_check_message_button.callback = self.discord_check_message_button + + default_channel = ( + discord.utils.get(self.bot.get_guild(self.guild_id).channels, id=self.sett.get('ERLC', {}).get('discord_checks', {}).get('channel', 0)) + ) + self.discord_check_channel = discord.ui.ChannelSelect( + placeholder="Discord Check Channel", + max_values=1, + min_values=0, + channel_types=[discord.ChannelType.text], + default_values=[default_channel] if default_channel else [] + ) + + self.add_item(self.discord_check_channel) + self.discord_check_channel.callback = self.discord_check_channel_callback + + + async def discord_check_message_button(self, interaction: discord.Interaction, button: discord.ui.Button): val = await self.interaction_check(interaction) if val is False: return @@ -8298,7 +8351,7 @@ async def discord_check(self, interaction: discord.Interaction, button: discord. if not sett: return try: - default_message = sett['ERLC']['discord_checks']["message"] + default_message = sett.get('ERLC', {}).get('discord_checks', {}).get('message', "") except KeyError: default_message = "You were not found in our discord server. Please contact any mod/admin." modal = CustomModal( @@ -8327,7 +8380,7 @@ async def discord_check(self, interaction: discord.Interaction, button: discord. if not sett.get('ERLC'): sett['ERLC'] = {} try: - sett['ERLC']['discord_checks']["message"] = modal.message.value + sett['ERLC']['discord_checks']['message'] = modal.message.value except KeyError: try: sett['ERLC']['discord_checks'] = { @@ -8341,6 +8394,34 @@ async def discord_check(self, interaction: discord.Interaction, button: discord. } await bot.settings.update_by_id(sett) await config_change_log(self.bot, interaction.guild, interaction.user, f"Discord Check Message Set: {modal.message.value}") + + async def discord_check_channel_callback(self, interaction: discord.Interaction, select: discord.ui.ChannelSelect): + val = await self.interaction_check(interaction) + if val is False: return + + guild_id = interaction.guild.id + bot = self.bot + + sett = await bot.settings.find_by_id(guild_id) + if not sett: + return + + try: + sett['ERLC']['discord_checks']['channel'] = select.values[0].id + except KeyError: + try: + sett['ERLC']['discord_checks'] = { + "channel": select.values[0].id + } + except KeyError: + sett['ERLC'] = { + 'discord_checks': { + 'channel': select.values[0].id + } + } + await bot.settings.update_by_id(sett) + await config_change_log(self.bot, interaction.guild, interaction.user, f"Discord Check Channel Set: <#{select.values[0].id}>") + class AutoLogging(discord.ui.View): def __init__(self,bot,sett): super().__init__(timeout=600.0) diff --git a/tasks/check_whitelisted_car.py b/tasks/check_whitelisted_car.py index acf2a539..e85a11e9 100644 --- a/tasks/check_whitelisted_car.py +++ b/tasks/check_whitelisted_car.py @@ -9,7 +9,7 @@ from utils.constants import RED_COLOR from utils.prc_api import Player from utils import prc_api -from utils.utils import is_whitelisted, run_command +from utils.utils import is_whitelisted, run_command, get_player_avatar_url @tasks.loop(minutes=2, reconnect=True) async def check_whitelisted_car(bot): diff --git a/tasks/discord_checks.py b/tasks/discord_checks.py new file mode 100644 index 00000000..d8f1c76f --- /dev/null +++ b/tasks/discord_checks.py @@ -0,0 +1,166 @@ +import datetime +import re +import time +import discord +import pytz +from discord.ext import commands, tasks +import logging +import asyncio + +from utils.constants import BLANK_COLOR +from utils.prc_api import Player +from utils import prc_api +from utils.utils import run_command, get_discord_by_roblox + + +@tasks.loop(minutes=2, reconnect=True) +async def discord_checks(bot): + total_server_count = await bot.settings.db.aggregate([ + { + '$match': { + 'ERLC': {'$exists': True}, + '$or': [ + {'ERLC.discord_checks.channel': {'$type': 'long', '$ne': 0}} + ] + } + }, + { + '$lookup': { + 'from': 'server_keys', + 'localField': '_id', + 'foreignField': '_id', + 'as': 'server_key' + } + }, + { + '$match': { + 'server_key': {'$ne': []} + } + }, + { + '$count': 'total' + } + ]).to_list(1) + total_server_count = total_server_count[0]['total'] if total_server_count else 0 + + logging.warning(f"[ITERATE] Starting Discord Check Iteration for {total_server_count} servers") + proccessed_servers = 0 + start_time = time.time() + + pipeline = [ + { + '$match': { + 'ERLC': {'$exists': True}, + '$or': [ + {'ERLC.discord_checks.channel': {'$type': 'long', '$ne': 0}} + ] + } + }, + { + '$lookup': { + 'from': 'server_keys', + 'localField': '_id', + 'foreignField': '_id', + 'as': 'server_key' + } + }, + { + '$match': { + 'server_key': {'$ne': []} + } + } + ] + + semaphore = asyncio.Semaphore(20) + tasks = [] + + async def check_servers(server): + async with semaphore: + try: + guild = bot.get_guild(server['_id']) or await bot.fetch_guild(server['_id']) + sett = await bot.settings.db.find_by_id(guild.id) + + discord_check_channel = sett.get('ERLC', {}).get('discord_checks', {}).get('channel', 0) + + if discord_check_channel == 0: + return + + try: + players: list[Player] = await bot.prc_api.get_server_players(guild.id) + + if not players: + return + + embed = discord.Embed( + title="Automatic Discord Checks", + color=BLANK_COLOR, + ).set_footer( + text=f"PM has been sent to the users" + ) + + guild_members_dict = {} + for member in guild.members: + if member.bot: + continue + keys = { + member.name.lower(): member, + member.display_name.lower(): member, + } + if hasattr(member, 'global_name') and member.global_name: + keys[member.global_name.lower()] = member + for key in keys: + if key not in guild_members_dict: + guild_members_dict[key] = [] + guild_members_dict[key].append(member) + + all_users = [] + for player in players: + player_username_lower = player.username.lower() + + if player_username_lower in guild_members_dict and guild_members_dict[player_username_lower]: + continue + + try: + discord_id = await get_discord_by_roblox(bot, player.username) + if discord_id: + member = guild.get_member(discord_id) + if member: + continue + except discord.HTTPException: + pass + + embed.description += f"> [{player.username}](https://roblox.com/users/{player.id}/profile)\n" + all_users.append(player.username) + + if all_users: + all_users_str = ", ".join(all_users) + discord_check_message = sett.get('ERLC', {}).get('discord_checks', {}).get('message', "You are not in the communication server. Please join the server to avoid being kicked.") + await run_command(bot, guild.id, all_users_str, discord_check_message) + + if not all_users: + return + + channel = guild.get_channel(discord_check_channel) + if not channel: + logging.error(f"Channel not found for guild {guild.id}") + return + + await channel.send(embed=embed) + + except prc_api.ResponseFailure: + logging.error(f"PRC ResponseFailure for guild {guild.id}") + + except discord.HTTPException: + logging.error(f"Error fetching guild {server['_id']}") + + async for server in bot.settings.db.aggregate(pipeline): + tasks.append(check_servers(server)) + proccessed_servers += 1 + if proccessed_servers % 10 == 0: + logging.warning(f"[ITERATE] Discord Check Iteration proccessed {proccessed_servers} / {total_server_count} servers") + + await asyncio.gather(*tasks) + end_time = time.time() + logging.warning(f"[ITERATE] Discord Check Iteration finished in {end_time - start_time} seconds") + logging.warning(f"[ITERATE] Next Discord Check Iteration in 2 minutes") +