mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-21 10:17:59 -05:00
[Core] Multiple mod admin roles (#2783)
* Adds Schema versioning - Adds Migration tool - Adds tool to migrate to allow multiple admin and mod roles - Supports Multiple mod and admin roles * Ensures migration is run prior to cog load and connection to discord * Updates to not rely on singular mod/admin role id * Update requires logic for multiple mod/admin roles * Add new commands for managing mod/admin roles * Feedback Update strings Update docstrings Add aliases * Use snowflakelist * paginate * Change variable name * Fix mistake * handle settings view fix * Fix name error * I'm bad at Ux * style fix
This commit is contained in:
@@ -67,14 +67,15 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): # pylint: d
|
||||
api_tokens={},
|
||||
extra_owner_destinations=[],
|
||||
owner_opt_out_list=[],
|
||||
schema_version=0,
|
||||
)
|
||||
|
||||
self.db.register_guild(
|
||||
prefix=[],
|
||||
whitelist=[],
|
||||
blacklist=[],
|
||||
admin_role=None,
|
||||
mod_role=None,
|
||||
admin_role=[],
|
||||
mod_role=[],
|
||||
embeds=None,
|
||||
use_bot_color=False,
|
||||
fuzzy=False,
|
||||
@@ -134,6 +135,38 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): # pylint: d
|
||||
|
||||
self._permissions_hooks: List[commands.CheckPredicate] = []
|
||||
|
||||
async def maybe_update_config(self):
|
||||
"""
|
||||
This should be run prior to loading cogs or connecting to discord.
|
||||
"""
|
||||
schema_version = await self.db.schema_version()
|
||||
|
||||
if schema_version == 0:
|
||||
await self._schema_0_to_1()
|
||||
schema_version += 1
|
||||
await self.db.schema_version.set(schema_version)
|
||||
|
||||
async def _schema_0_to_1(self):
|
||||
"""
|
||||
This contains the migration to allow multiple mod and multiple admin roles.
|
||||
"""
|
||||
|
||||
log.info("Begin updating guild configs to support multiple mod/admin roles")
|
||||
all_guild_data = await self.db.all_guilds()
|
||||
for guild_id, guild_data in all_guild_data.items():
|
||||
guild_obj = discord.Object(id=guild_id)
|
||||
mod_roles, admin_roles = [], []
|
||||
maybe_mod_role_id = guild_data["mod_role"]
|
||||
maybe_admin_role_id = guild_data["admin_role"]
|
||||
|
||||
if maybe_mod_role_id:
|
||||
mod_roles.append(maybe_mod_role_id)
|
||||
await self.db.guild(guild_obj).mod_role.set(mod_roles)
|
||||
if maybe_admin_role_id:
|
||||
admin_roles.append(maybe_admin_role_id)
|
||||
await self.db.guild(guild_obj).admin_role.set(admin_roles)
|
||||
log.info("Done updating guild configs to support multiple mod/admin roles")
|
||||
|
||||
async def send_help_for(
|
||||
self, ctx: commands.Context, help_for: Union[commands.Command, commands.GroupMixin, str]
|
||||
):
|
||||
@@ -191,21 +224,25 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): # pylint: d
|
||||
|
||||
async def is_admin(self, member: discord.Member):
|
||||
"""Checks if a member is an admin of their guild."""
|
||||
admin_role = await self.db.guild(member.guild).admin_role()
|
||||
try:
|
||||
if any(role.id == admin_role for role in member.roles):
|
||||
return True
|
||||
member_snowflakes = member._roles # DEP-WARN
|
||||
for snowflake in await self.db.guild(member.guild).admin_role():
|
||||
if member_snowflakes.has(snowflake): # Dep-WARN
|
||||
return True
|
||||
except AttributeError: # someone passed a webhook to this
|
||||
pass
|
||||
return False
|
||||
|
||||
async def is_mod(self, member: discord.Member):
|
||||
"""Checks if a member is a mod or admin of their guild."""
|
||||
mod_role = await self.db.guild(member.guild).mod_role()
|
||||
admin_role = await self.db.guild(member.guild).admin_role()
|
||||
try:
|
||||
if any(role.id in (mod_role, admin_role) for role in member.roles):
|
||||
return True
|
||||
member_snowflakes = member._roles # DEP-WARN
|
||||
for snowflake in await self.db.guild(member.guild).admin_role():
|
||||
if member_snowflakes.has(snowflake): # DEP-WARN
|
||||
return True
|
||||
for snowflake in await self.db.guild(member.guild).mod_role():
|
||||
if member_snowflakes.has(snowflake): # DEP-WARN
|
||||
return True
|
||||
except AttributeError: # someone passed a webhook to this
|
||||
pass
|
||||
return False
|
||||
|
||||
@@ -126,16 +126,14 @@ class PrivilegeLevel(enum.IntEnum):
|
||||
# The following is simply an optimised way to check if the user has the
|
||||
# admin or mod role.
|
||||
guild_settings = ctx.bot.db.guild(ctx.guild)
|
||||
admin_role_id = await guild_settings.admin_role()
|
||||
mod_role_id = await guild_settings.mod_role()
|
||||
is_mod = False
|
||||
for role in ctx.author.roles:
|
||||
if role.id == admin_role_id:
|
||||
|
||||
member_snowflakes = ctx.author._roles # DEP-WARN
|
||||
for snowflake in await guild_settings.admin_role():
|
||||
if member_snowflakes.has(snowflake): # DEP-WARN
|
||||
return cls.ADMIN
|
||||
elif role.id == mod_role_id:
|
||||
is_mod = True
|
||||
if is_mod:
|
||||
return cls.MOD
|
||||
for snowflake in await guild_settings.mod_role():
|
||||
if member_snowflakes.has(snowflake): # DEP-WARN
|
||||
return cls.MOD
|
||||
|
||||
return cls.NONE
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ from redbot.core import (
|
||||
i18n,
|
||||
)
|
||||
from .utils.predicates import MessagePredicate
|
||||
from .utils.chat_formatting import humanize_timedelta, pagify, box, inline
|
||||
from .utils.chat_formatting import humanize_timedelta, pagify, box, inline, humanize_list
|
||||
|
||||
from .commands.requires import PrivilegeLevel
|
||||
|
||||
@@ -705,15 +705,17 @@ class Core(commands.Cog, CoreLogic):
|
||||
if ctx.invoked_subcommand is None:
|
||||
if ctx.guild:
|
||||
guild = ctx.guild
|
||||
admin_role = (
|
||||
guild.get_role(await ctx.bot.db.guild(ctx.guild).admin_role()) or "Not set"
|
||||
)
|
||||
mod_role = (
|
||||
guild.get_role(await ctx.bot.db.guild(ctx.guild).mod_role()) or "Not set"
|
||||
admin_role_ids = await ctx.bot.db.guild(ctx.guild).admin_role()
|
||||
admin_role_names = [r.name for r in guild.roles if r.id in admin_role_ids]
|
||||
admin_roles_str = (
|
||||
humanize_list(admin_role_names) if admin_role_names else "Not Set."
|
||||
)
|
||||
mod_role_ids = await ctx.bot.db.guild(ctx.guild).mod_role()
|
||||
mod_role_names = [r.name for r in guild.roles if r.id in mod_role_ids]
|
||||
mod_roles_str = humanize_list(mod_role_names) if mod_role_names else "Not Set."
|
||||
prefixes = await ctx.bot.db.guild(ctx.guild).prefix()
|
||||
guild_settings = _("Admin role: {admin}\nMod role: {mod}\n").format(
|
||||
admin=admin_role, mod=mod_role
|
||||
guild_settings = _("Admin roles: {admin}\nMod roles: {mod}\n").format(
|
||||
admin=admin_roles_str, mod=mod_roles_str
|
||||
)
|
||||
else:
|
||||
guild_settings = ""
|
||||
@@ -734,23 +736,60 @@ class Core(commands.Cog, CoreLogic):
|
||||
guild_settings=guild_settings,
|
||||
locale=locale,
|
||||
)
|
||||
await ctx.send(box(settings))
|
||||
for page in pagify(settings):
|
||||
await ctx.send(box(page))
|
||||
|
||||
@_set.command()
|
||||
@checks.guildowner()
|
||||
@commands.guild_only()
|
||||
async def adminrole(self, ctx: commands.Context, *, role: discord.Role):
|
||||
"""Sets the admin role for this server"""
|
||||
await ctx.bot.db.guild(ctx.guild).admin_role.set(role.id)
|
||||
await ctx.send(_("The admin role for this guild has been set."))
|
||||
async def addadminrole(self, ctx: commands.Context, *, role: discord.Role):
|
||||
"""
|
||||
Adds an admin role for this guild.
|
||||
"""
|
||||
async with ctx.bot.db.guild(ctx.guild).admin_role() as roles:
|
||||
if role.id in roles:
|
||||
return await ctx.send(_("This role is already an admin role."))
|
||||
roles.append(role.id)
|
||||
await ctx.send(_("That role is now considered an admin role."))
|
||||
|
||||
@_set.command()
|
||||
@checks.guildowner()
|
||||
@commands.guild_only()
|
||||
async def modrole(self, ctx: commands.Context, *, role: discord.Role):
|
||||
"""Sets the mod role for this server"""
|
||||
await ctx.bot.db.guild(ctx.guild).mod_role.set(role.id)
|
||||
await ctx.send(_("The mod role for this guild has been set."))
|
||||
async def addmodrole(self, ctx: commands.Context, *, role: discord.Role):
|
||||
"""
|
||||
Adds a mod role for this guild.
|
||||
"""
|
||||
async with ctx.bot.db.guild(ctx.guild).mod_role() as roles:
|
||||
if role.id in roles:
|
||||
return await ctx.send(_("This role is already a mod role."))
|
||||
roles.append(role.id)
|
||||
await ctx.send(_("That role is now considered a mod role."))
|
||||
|
||||
@_set.command(aliases=["remadmindrole", "deladminrole", "deleteadminrole"])
|
||||
@checks.guildowner()
|
||||
@commands.guild_only()
|
||||
async def removeadminrole(self, ctx: commands.Context, *, role: discord.Role):
|
||||
"""
|
||||
Removes an admin role for this guild.
|
||||
"""
|
||||
async with ctx.bot.db.guild(ctx.guild).admin_role() as roles:
|
||||
if role.id not in roles:
|
||||
return await ctx.send(_("That role was not an admin role to begin with."))
|
||||
roles.remove(role.id)
|
||||
await ctx.send(_("That role is no longer considered an admin role."))
|
||||
|
||||
@_set.command(aliases=["remmodrole", "delmodrole", "deletemodrole"])
|
||||
@checks.guildowner()
|
||||
@commands.guild_only()
|
||||
async def removemodrole(self, ctx: commands.Context, *, role: discord.Role):
|
||||
"""
|
||||
Removes a mod role for this guild.
|
||||
"""
|
||||
async with ctx.bot.db.guild(ctx.guild).mod_role() as roles:
|
||||
if role.id not in roles:
|
||||
return await ctx.send(_("That role was not a mod role to begin with."))
|
||||
roles.remove(role.id)
|
||||
await ctx.send(_("That role is no longer considered a mod role."))
|
||||
|
||||
@_set.command(aliases=["usebotcolor"])
|
||||
@checks.guildowner()
|
||||
|
||||
@@ -123,29 +123,25 @@ async def is_mod_or_superior(
|
||||
If the wrong type of ``obj`` was passed.
|
||||
|
||||
"""
|
||||
user = None
|
||||
if isinstance(obj, discord.Message):
|
||||
user = obj.author
|
||||
elif isinstance(obj, discord.Member):
|
||||
user = obj
|
||||
elif isinstance(obj, discord.Role):
|
||||
pass
|
||||
if obj.id in await bot.db.guild(obj.guild).mod_role():
|
||||
return True
|
||||
if obj.id in await bot.db.guild(obj.guild).admin_role():
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
raise TypeError("Only messages, members or roles may be passed")
|
||||
|
||||
server = obj.guild
|
||||
admin_role_id = await bot.db.guild(server).admin_role()
|
||||
mod_role_id = await bot.db.guild(server).mod_role()
|
||||
|
||||
if isinstance(obj, discord.Role):
|
||||
return obj.id in [admin_role_id, mod_role_id]
|
||||
|
||||
if await bot.is_owner(user):
|
||||
return True
|
||||
elif discord.utils.find(lambda r: r.id in (admin_role_id, mod_role_id), user.roles):
|
||||
if await bot.is_mod(user):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def strfdelta(delta: timedelta):
|
||||
@@ -208,27 +204,21 @@ async def is_admin_or_superior(
|
||||
If the wrong type of ``obj`` was passed.
|
||||
|
||||
"""
|
||||
user = None
|
||||
if isinstance(obj, discord.Message):
|
||||
user = obj.author
|
||||
elif isinstance(obj, discord.Member):
|
||||
user = obj
|
||||
elif isinstance(obj, discord.Role):
|
||||
pass
|
||||
return obj.id in await bot.db.guild(obj.guild).admin_role()
|
||||
else:
|
||||
raise TypeError("Only messages, members or roles may be passed")
|
||||
|
||||
admin_role_id = await bot.db.guild(obj.guild).admin_role()
|
||||
|
||||
if isinstance(obj, discord.Role):
|
||||
return obj.id == admin_role_id
|
||||
|
||||
if user and await bot.is_owner(user):
|
||||
if await bot.is_owner(user):
|
||||
return True
|
||||
elif discord.utils.get(user.roles, id=admin_role_id):
|
||||
if await bot.is_admin(user):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
|
||||
async def check_permissions(ctx: "Context", perms: Dict[str, bool]) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user