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"^?$") 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) )