mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
This is a safety measure to prevent accidentally passing a model which has the same name as another model, potentially modifying rules for the unwanted one.
179 lines
5.7 KiB
Python
179 lines
5.7 KiB
Python
import itertools
|
|
import re
|
|
from typing import NamedTuple, Union, Optional
|
|
|
|
import discord
|
|
|
|
from redbot.core import commands
|
|
from redbot.core.i18n import Translator
|
|
|
|
_ = Translator("PermissionsConverters", __file__)
|
|
|
|
MENTION_RE = re.compile(r"^<?(?:(?:@[!&]?)?|#)(\d{15,21})>?$")
|
|
|
|
|
|
def _match_id(arg: str) -> Optional[int]:
|
|
m = MENTION_RE.match(arg)
|
|
if m:
|
|
return int(m.group(1))
|
|
|
|
|
|
class GlobalUniqueObjectFinder(commands.Converter):
|
|
async def convert(
|
|
self, ctx: commands.Context, arg: str
|
|
) -> Union[discord.Guild, discord.abc.GuildChannel, discord.abc.User, discord.Role]:
|
|
bot: commands.Bot = ctx.bot
|
|
_id = _match_id(arg)
|
|
|
|
if _id is not None:
|
|
guild: discord.Guild = bot.get_guild(_id)
|
|
if guild is not None:
|
|
return guild
|
|
channel: discord.abc.GuildChannel = bot.get_channel(_id)
|
|
if channel is not None:
|
|
return channel
|
|
|
|
user: discord.User = bot.get_user(_id)
|
|
if user is not None:
|
|
return user
|
|
|
|
for guild in bot.guilds:
|
|
role: discord.Role = guild.get_role(_id)
|
|
if role is not None:
|
|
return role
|
|
|
|
objects = itertools.chain(
|
|
bot.get_all_channels(),
|
|
bot.users,
|
|
bot.guilds,
|
|
*(filter(lambda r: not r.is_default(), guild.roles) for guild in bot.guilds),
|
|
)
|
|
|
|
maybe_matches = []
|
|
for obj in objects:
|
|
if obj.name == arg or str(obj) == arg:
|
|
maybe_matches.append(obj)
|
|
|
|
if ctx.guild is not None:
|
|
for member in ctx.guild.members:
|
|
if member.nick == arg and not any(obj.id == member.id for obj in maybe_matches):
|
|
maybe_matches.append(member)
|
|
|
|
if not maybe_matches:
|
|
raise commands.BadArgument(
|
|
_(
|
|
'"{arg}" was not found. It must be the ID, mention, or name of a server, '
|
|
"channel, user or role which the bot can see."
|
|
).format(arg=arg)
|
|
)
|
|
elif len(maybe_matches) == 1:
|
|
return maybe_matches[0]
|
|
else:
|
|
raise commands.BadArgument(
|
|
_(
|
|
'"{arg}" does not refer to a unique server, channel, user or role. Please use '
|
|
"the ID for whatever/whoever you're trying to specify, or mention it/them."
|
|
).format(arg=arg)
|
|
)
|
|
|
|
|
|
class GuildUniqueObjectFinder(commands.Converter):
|
|
async def convert(
|
|
self, ctx: commands.Context, arg: str
|
|
) -> Union[discord.abc.GuildChannel, discord.Member, discord.Role]:
|
|
guild: discord.Guild = ctx.guild
|
|
_id = _match_id(arg)
|
|
|
|
if _id is not None:
|
|
channel: discord.abc.GuildChannel = guild.get_channel(_id)
|
|
if channel is not None:
|
|
return channel
|
|
|
|
member: discord.Member = guild.get_member(_id)
|
|
if member is not None:
|
|
return member
|
|
|
|
role: discord.Role = guild.get_role(_id)
|
|
if role is not None and not role.is_default():
|
|
return role
|
|
|
|
objects = itertools.chain(
|
|
guild.channels, guild.members, filter(lambda r: not r.is_default(), guild.roles)
|
|
)
|
|
|
|
maybe_matches = []
|
|
for obj in objects:
|
|
if obj.name == arg or str(obj) == arg:
|
|
maybe_matches.append(obj)
|
|
try:
|
|
if obj.nick == arg:
|
|
maybe_matches.append(obj)
|
|
except AttributeError:
|
|
pass
|
|
|
|
if not maybe_matches:
|
|
raise commands.BadArgument(
|
|
_(
|
|
'"{arg}" was not found. It must be the ID, mention, or name of a channel, '
|
|
"user or role in this server."
|
|
).format(arg=arg)
|
|
)
|
|
elif len(maybe_matches) == 1:
|
|
return maybe_matches[0]
|
|
else:
|
|
raise commands.BadArgument(
|
|
_(
|
|
'"{arg}" does not refer to a unique channel, user or role. Please use the ID '
|
|
"for whatever/whoever you're trying to specify, or mention it/them."
|
|
).format(arg=arg)
|
|
)
|
|
|
|
|
|
class CogOrCommand(NamedTuple):
|
|
type: str
|
|
name: str
|
|
obj: Union[commands.Command, commands.Cog]
|
|
|
|
# noinspection PyArgumentList
|
|
@classmethod
|
|
async def convert(cls, ctx: commands.Context, arg: str) -> "CogOrCommand":
|
|
cog = ctx.bot.get_cog(arg)
|
|
if cog:
|
|
return cls(type="COG", name=cog.__class__.__name__, obj=cog)
|
|
cmd = ctx.bot.get_command(arg)
|
|
if cmd:
|
|
return cls(type="COMMAND", name=cmd.qualified_name, obj=cmd)
|
|
|
|
raise commands.BadArgument(
|
|
_(
|
|
'Cog or command "{name}" not found. Please note that this is case sensitive.'
|
|
).format(name=arg)
|
|
)
|
|
|
|
|
|
def RuleType(arg: str) -> bool:
|
|
if arg.lower() in ("allow", "whitelist", "allowed"):
|
|
return True
|
|
if arg.lower() in ("deny", "blacklist", "denied"):
|
|
return False
|
|
|
|
raise commands.BadArgument(
|
|
_('"{arg}" is not a valid rule. Valid rules are "allow" or "deny"').format(arg=arg)
|
|
)
|
|
|
|
|
|
def ClearableRuleType(arg: str) -> Optional[bool]:
|
|
if arg.lower() in ("allow", "whitelist", "allowed"):
|
|
return True
|
|
if arg.lower() in ("deny", "blacklist", "denied"):
|
|
return False
|
|
if arg.lower() in ("clear", "reset"):
|
|
return None
|
|
|
|
raise commands.BadArgument(
|
|
_(
|
|
'"{arg}" is not a valid rule. Valid rules are "allow" or "deny", or "clear" to '
|
|
"remove the rule"
|
|
).format(arg=arg)
|
|
)
|