diff --git a/src/modules/moderation/cog.py b/src/modules/moderation/cog.py index 905766ec..4ed52d11 100644 --- a/src/modules/moderation/cog.py +++ b/src/modules/moderation/cog.py @@ -140,12 +140,13 @@ class ModerationCog(LionCog): ) ) @appcmds.rename( + adminrole=ModerationSettings.AdminRole._display_name, modrole=ModerationSettings.ModRole._display_name, ticket_log=ModerationSettings.TicketLog._display_name, alert_channel=ModerationSettings.AlertChannel._display_name, ) @appcmds.describe( - modrole=ModerationSettings.ModRole._desc, + adminrole=ModerationSettings.AdminRole._desc, ticket_log=ModerationSettings.TicketLog._desc, alert_channel=ModerationSettings.AlertChannel._desc, ) @@ -154,6 +155,7 @@ class ModerationCog(LionCog): modrole: Optional[discord.Role] = None, ticket_log: Optional[discord.TextChannel] = None, alert_channel: Optional[discord.TextChannel] = None, + adminrole: Optional[discord.Role] = None, ): if not ctx.guild: return @@ -169,6 +171,12 @@ class ModerationCog(LionCog): instance = setting(ctx.guild.id, modrole.id) modified.append(instance) + if adminrole is not None: + setting = self.settings.AdminRole + await setting._check_value(ctx.guild.id, adminrole) + instance = setting(ctx.guild.id, adminrole.id) + modified.append(instance) + if ticket_log is not None: setting = self.settings.TicketLog await setting._check_value(ctx.guild.id, ticket_log) diff --git a/src/modules/moderation/settings.py b/src/modules/moderation/settings.py index a4a8a157..c0dc2efb 100644 --- a/src/modules/moderation/settings.py +++ b/src/modules/moderation/settings.py @@ -148,7 +148,7 @@ class ModerationSettings(SettingGroup): if value: resp = t(_p( 'guildset:mod_role|set_response:set', - "Members with the {role} will be considered moderators." + "Members with {role} will be considered moderators." )).format(role=value.mention) else: resp = t(_p( @@ -167,3 +167,46 @@ class ModerationSettings(SettingGroup): 'guildset:mod_role|formatted:unset', "Not Set." )) + + class AdminRole(ModelData, RoleSetting): + setting_id = "admin_role" + _event = 'guildset_admin_role' + + _display_name = _p('guildset:admin_role', "admin_role") + _desc = _p( + 'guildset:admin_role|desc', + "Server role allowing access to all administrator level functionality in Leo." + ) + _long_desc = _p( + 'guildset:admin_role|long_desc', + "Members with this role are considered to be server administrators, " + "allowing them to use all of my interfaces and commands, " + "except for managing roles that are above them in the role hierachy. " + "This setting allows giving members administrator-level permissions " + "over my systems, without actually giving the members admin server permissions. " + "Note that the role will also need to be given permission to see the commands " + "through the Discord server integrations interface." + ) + _accepts = _p( + 'guildset:admin_role|accepts', + "Admin role name or id." + ) + + _model = CoreData.Guild + _column = CoreData.Guild.admin_role.name + + @property + def update_message(self) -> str: + t = ctx_translator.get().t + value = self.value + if value: + resp = t(_p( + 'guildset:admin_role|set_response:set', + "Members with {role} will now be considered admins, and have access to my full interface." + )).format(role=value.mention) + else: + resp = t(_p( + 'guildset:admin_role|set_response:unset', + "The admin role has been unset. Only members with administrator permissions will be considered admins." + )) + return resp diff --git a/src/modules/moderation/settingui.py b/src/modules/moderation/settingui.py index 13a6e456..c0f90c5e 100644 --- a/src/modules/moderation/settingui.py +++ b/src/modules/moderation/settingui.py @@ -18,9 +18,10 @@ _p = babel._p class ModerationSettingUI(ConfigUI): setting_classes = ( + ModerationSettings.ModRole, + ModerationSettings.AdminRole, ModerationSettings.TicketLog, ModerationSettings.AlertChannel, - ModerationSettings.ModRole, ) def __init__(self, bot: LionBot, guildid: int, channelid, **kwargs): @@ -103,6 +104,31 @@ class ModerationSettingUI(ConfigUI): "Select Moderator Role" )) + # Admin Role Selector + @select( + cls=RoleSelect, + placeholder="ADMINROLE_MENU_PLACEHOLDER", + min_values=0, max_values=1 + ) + async def adminrole_menu(self, selection: discord.Interaction, selected: RoleSelect): + """ + Single role selector for the `admin_role` setting. + """ + await selection.response.defer(thinking=True, ephemeral=True) + + setting = self.get_instance(ModerationSettings.AdminRole) + setting.value = selected.values[0] if selected.values else None + await setting.write() + await selection.delete_original_response() + + async def adminrole_menu_refresh(self): + menu = self.adminrole_menu + t = self.bot.translator.t + menu.placeholder = t(_p( + 'ui:moderation_config|menu:adminrole|placeholder', + "Select Admin Role" + )) + # ----- UI Flow ----- async def make_message(self) -> MessageArgs: t = self.bot.translator.t @@ -133,13 +159,15 @@ class ModerationSettingUI(ConfigUI): self.ticket_log_menu_refresh(), self.alert_channel_menu_refresh(), self.modrole_menu_refresh(), + self.adminrole_menu_refresh(), ) await asyncio.gather(*component_refresh) self.set_layout( + (self.adminrole_menu,), + (self.modrole_menu,), (self.ticket_log_menu,), (self.alert_channel_menu,), - (self.modrole_menu,), (self.edit_button, self.reset_button, self.close_button,) ) diff --git a/src/wards.py b/src/wards.py index 5db46eb9..c552337c 100644 --- a/src/wards.py +++ b/src/wards.py @@ -26,15 +26,31 @@ async def high_management(bot: LionBot, member: discord.Member, guild: discord.G return True if await sys_admin(bot, member.id): return True - return member.guild_permissions.administrator + if member.guild_permissions.administrator: + return True + lguild = await bot.core.lions.fetch_guild(guild.id) + adminrole = lguild.data.admin_role + roleids = [role.id for role in member.roles] + if (adminrole and adminrole in roleids): + return True async def low_management(bot: LionBot, member: discord.Member, guild: discord.Guild): + """ + Low management is currently identified with moderator permissions. + """ if not guild: return True if await high_management(bot, member, guild): return True - return member.guild_permissions.manage_guild + if member.guild_permissions.manage_guild: + return True + + lguild = await bot.core.lions.fetch_guild(guild.id) + modrole = lguild.data.mod_role + roleids = [role.id for role in member.roles] + if (modrole and modrole in roleids): + return True # Interaction Wards, also return True/False @@ -96,7 +112,7 @@ async def high_management_ward(ctx: LionContext) -> bool: raise CheckFailure( ctx.bot.translator.t(_p( 'ward:high_management|failed', - "You must have the `ADMINISTRATOR` permission in this server to do this!" + "You must have the `ADMINISTRATOR` permission or the configured `admin_role` to do this!" )) ) @@ -112,7 +128,7 @@ async def low_management_ward(ctx: LionContext) -> bool: raise CheckFailure( ctx.bot.translator.t(_p( 'ward:low_management|failed', - "You must have the `MANAGE_GUILD` permission in this server to do this!" + "You must have the `MANAGE_GUILD` permission or the configured `mod_role` to do this!" )) )