From 61f467a323bccbe1228959a9d8be744b8667cbad Mon Sep 17 00:00:00 2001 From: Draper <27962761+Drapersniper@users.noreply.github.com> Date: Fri, 20 Dec 2019 06:55:34 +0000 Subject: [PATCH] Audio converters - Remove all da fetches (#3089) * Removes `MAX_BALANCE` from bank, user `bank.get_max_balance()` now `[p]bankset maxbal` can be used to set the maximum bank balance Signed-off-by: Guy * Update Audio Scope converters to respect changes done in #3075 To be merged after #3075 Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Change logs Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Fix Typo Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Fix an attribute error when the converter returned None Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * :facepalm: Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * :facepalm: 2x Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Address Aika's review Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> --- changelog.d/audio/3089.misc.1.rst | 1 + redbot/cogs/audio/converters.py | 314 ++++++++++++++++-------------- redbot/cogs/audio/errors.py | 4 + 3 files changed, 177 insertions(+), 142 deletions(-) create mode 100644 changelog.d/audio/3089.misc.1.rst diff --git a/changelog.d/audio/3089.misc.1.rst b/changelog.d/audio/3089.misc.1.rst new file mode 100644 index 000000000..7daf5861a --- /dev/null +++ b/changelog.d/audio/3089.misc.1.rst @@ -0,0 +1 @@ +Remove API calls from Audio converters. \ No newline at end of file diff --git a/redbot/cogs/audio/converters.py b/redbot/cogs/audio/converters.py index 05736cf8b..4842b8bf0 100644 --- a/redbot/cogs/audio/converters.py +++ b/redbot/cogs/audio/converters.py @@ -1,9 +1,11 @@ import argparse import functools +import re from typing import Optional, Tuple, Union import discord +from redbot.cogs.audio.errors import TooManyMatches, NoMatchesFound from redbot.core import Config, commands from redbot.core.bot import Red from redbot.core.i18n import Translator @@ -35,7 +37,7 @@ _USER_HELP = """ Author must be a valid version of one of the following: ​ ​ ​ ​ User ID ​ ​ ​ ​ User Mention -​ ​ ​ ​ User Name#123 +​ ​ ​ ​ User Name#123 """ _GUILD_HELP = """ Guild must be a valid version of one of the following: @@ -43,6 +45,8 @@ Guild must be a valid version of one of the following: ​ ​ ​ ​ Exact guild name """ +MENTION_RE = re.compile(r"^?$") + def _pass_config_to_converters(config: Config, bot: Red): global _config, _bot @@ -52,6 +56,86 @@ def _pass_config_to_converters(config: Config, bot: Red): _bot = bot +def _match_id(arg: str) -> Optional[int]: + m = MENTION_RE.match(arg) + if m: + return int(m.group(1)) + + +async def global_unique_guild_finder(ctx: commands.Context, arg: str) -> discord.Guild: + 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 + + maybe_matches = [] + for obj in bot.guilds: + if obj.name == arg or str(obj) == arg: + maybe_matches.append(obj) + + if not maybe_matches: + raise NoMatchesFound( + _( + '"{arg}" was not found. It must be the ID or ' + "complete name of a server which the bot can see." + ).format(arg=arg) + ) + elif len(maybe_matches) == 1: + return maybe_matches[0] + else: + raise TooManyMatches( + _( + '"{arg}" does not refer to a unique server. ' + "Please use the ID for the server you're trying to specify." + ).format(arg=arg) + ) + + +async def global_unique_user_finder( + ctx: commands.Context, arg: str, guild: discord.guild = None +) -> discord.abc.User: + bot: commands.Bot = ctx.bot + guild = guild or ctx.guild + _id = _match_id(arg) + + if _id is not None: + user: discord.User = bot.get_user(_id) + if user is not None: + return user + + objects = bot.users + + maybe_matches = [] + for obj in objects: + if obj.name == arg or str(obj) == arg: + maybe_matches.append(obj) + + if guild is not None: + for member in 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 NoMatchesFound( + _( + '"{arg}" was not found. It must be the ID or name or ' + "mention a user which the bot can see." + ).format(arg=arg) + ) + elif len(maybe_matches) == 1: + return maybe_matches[0] + else: + raise TooManyMatches( + _( + '"{arg}" does not refer to a unique server. ' + "Please use the ID for the server you're trying to specify." + ).format(arg=arg) + ) + + class PlaylistConverter(commands.Converter): async def convert(self, ctx: commands.Context, arg: str) -> dict: global_scope = await _config.custom(PlaylistScope.GLOBAL.value).all() @@ -94,9 +178,10 @@ class ScopeParser(commands.Converter): async def convert( self, ctx: commands.Context, argument: str ) -> Tuple[str, discord.User, Optional[discord.Guild], bool]: - target_scope: str = PlaylistScope.GUILD.value - target_user: Optional[Union[discord.Member, discord.User]] = ctx.author - target_guild: Optional[discord.Guild] = ctx.guild + + target_scope: Optional[str] = None + target_user: Optional[Union[discord.Member, discord.User]] = None + target_guild: Optional[discord.Guild] = None specified_user = False argument = argument.replace("—", "--") @@ -108,7 +193,6 @@ class ScopeParser(commands.Converter): command = None parser = NoExitParser(description="Playlist Scope Parsing.", add_help=False) - parser.add_argument("--scope", nargs="*", dest="scope", default=[]) parser.add_argument("--guild", nargs="*", dest="guild", default=[]) parser.add_argument("--server", nargs="*", dest="guild", default=[]) @@ -145,27 +229,20 @@ class ScopeParser(commands.Converter): is_owner = await ctx.bot.is_owner(ctx.author) guild = vals.get("guild", None) or vals.get("server", None) if is_owner and guild: + server_error = "" target_guild = None guild_raw = " ".join(guild).strip() - if guild_raw.isnumeric(): - guild_raw = int(guild_raw) - try: - target_guild = ctx.bot.get_guild(guild_raw) - except Exception: - target_guild = None - guild_raw = str(guild_raw) + try: + target_guild = await global_unique_guild_finder(ctx, guild_raw) + except TooManyMatches as err: + server_error = f"{err}\n" + except NoMatchesFound as err: + server_error = f"{err}\n" if target_guild is None: - try: - target_guild = await commands.GuildConverter.convert(ctx, guild_raw) - except Exception: - target_guild = None - if target_guild is None: - try: - target_guild = await ctx.bot.fetch_guild(guild_raw) - except Exception: - target_guild = None - if target_guild is None: - raise commands.ArgParserFailure("--guild", guild_raw, custom_help=_GUILD_HELP) + raise commands.ArgParserFailure( + "--guild", guild_raw, custom_help=f"{server_error}{_GUILD_HELP}" + ) + elif not is_owner and (guild or any(x in argument for x in ["--guild", "--server"])): raise commands.BadArgument("You cannot use `--guild`") elif any(x in argument for x in ["--guild", "--server"]): @@ -173,37 +250,28 @@ class ScopeParser(commands.Converter): author = vals.get("author", None) or vals.get("user", None) or vals.get("member", None) if author: + user_error = "" target_user = None user_raw = " ".join(author).strip() - if user_raw.isnumeric(): - user_raw = int(user_raw) - try: - target_user = ctx.bot.get_user(user_raw) - except Exception: - target_user = None - user_raw = str(user_raw) - if target_user is None: - member_converter = commands.MemberConverter() - user_converter = commands.UserConverter() - try: - target_user = await member_converter.convert(ctx, user_raw) - except Exception: - try: - target_user = await user_converter.convert(ctx, user_raw) - except Exception: - target_user = None - if target_user is None: - try: - target_user = await ctx.bot.fetch_user(user_raw) - except Exception: - target_user = None - if target_user is None: - raise commands.ArgParserFailure("--author", user_raw, custom_help=_USER_HELP) - else: + try: + target_user = await global_unique_user_finder(ctx, user_raw, guild=target_guild) specified_user = True + except TooManyMatches as err: + user_error = f"{err}\n" + except NoMatchesFound as err: + user_error = f"{err}\n" + + if target_user is None: + raise commands.ArgParserFailure( + "--author", user_raw, custom_help=f"{user_error}{_USER_HELP}" + ) elif any(x in argument for x in ["--author", "--user", "--member"]): raise commands.ArgParserFailure("--scope", "Nothing", custom_help=_USER_HELP) + target_scope: str = target_scope or PlaylistScope.GUILD.value + target_user: Union[discord.Member, discord.User] = target_user or ctx.author + target_guild: discord.Guild = target_guild or ctx.guild + return target_scope, target_user, target_guild, specified_user @@ -221,14 +289,14 @@ class ComplexScopeParser(commands.Converter): bool, ]: - target_scope: str = PlaylistScope.GUILD.value - target_user: Optional[Union[discord.Member, discord.User]] = ctx.author - target_guild: Optional[discord.Guild] = ctx.guild + target_scope: Optional[str] = None + target_user: Optional[Union[discord.Member, discord.User]] = None + target_guild: Optional[discord.Guild] = None specified_target_user = False - source_scope: str = PlaylistScope.GUILD.value - source_user: Optional[Union[discord.Member, discord.User]] = ctx.author - source_guild: Optional[discord.Guild] = ctx.guild + source_scope: Optional[str] = None + source_user: Optional[Union[discord.Member, discord.User]] = None + source_guild: Optional[discord.Guild] = None specified_source_user = False argument = argument.replace("—", "--") @@ -299,28 +367,18 @@ class ComplexScopeParser(commands.Converter): to_guild = vals.get("to_guild", None) or vals.get("to_server", None) if is_owner and to_guild: + target_server_error = "" target_guild = None to_guild_raw = " ".join(to_guild).strip() - if to_guild_raw.isnumeric(): - to_guild_raw = int(to_guild_raw) - try: - target_guild = ctx.bot.get_guild(to_guild_raw) - except Exception: - target_guild = None - to_guild_raw = str(to_guild_raw) - if target_guild is None: - try: - target_guild = await commands.GuildConverter.convert(ctx, to_guild_raw) - except Exception: - target_guild = None - if target_guild is None: - try: - target_guild = await ctx.bot.fetch_guild(to_guild_raw) - except Exception: - target_guild = None + try: + target_guild = await global_unique_guild_finder(ctx, to_guild_raw) + except TooManyMatches as err: + target_server_error = f"{err}\n" + except NoMatchesFound as err: + target_server_error = f"{err}\n" if target_guild is None: raise commands.ArgParserFailure( - "--to-guild", to_guild_raw, custom_help=_GUILD_HELP + "--to-guild", to_guild_raw, custom_help=f"{target_server_error}{_GUILD_HELP}" ) elif not is_owner and ( to_guild or any(x in argument for x in ["--to-guild", "--to-server"]) @@ -331,28 +389,20 @@ class ComplexScopeParser(commands.Converter): from_guild = vals.get("from_guild", None) or vals.get("from_server", None) if is_owner and from_guild: + source_server_error = "" source_guild = None - from_guild_raw = " ".join(from_guild).strip() - if from_guild_raw.isnumeric(): - from_guild_raw = int(from_guild_raw) - try: - source_guild = ctx.bot.get_guild(from_guild_raw) - except Exception: - source_guild = None - from_guild_raw = str(from_guild_raw) - if source_guild is None: - try: - source_guild = await commands.GuildConverter.convert(ctx, from_guild_raw) - except Exception: - source_guild = None - if source_guild is None: - try: - source_guild = await ctx.bot.fetch_guild(from_guild_raw) - except Exception: - source_guild = None + from_guild_raw = " ".join(to_guild).strip() + try: + source_guild = await global_unique_guild_finder(ctx, from_guild_raw) + except TooManyMatches as err: + source_server_error = f"{err}\n" + except NoMatchesFound as err: + source_server_error = f"{err}\n" if source_guild is None: raise commands.ArgParserFailure( - "--from-guild", from_guild_raw, custom_help=_GUILD_HELP + "--from-guild", + from_guild_raw, + custom_help=f"{source_server_error}{_GUILD_HELP}", ) elif not is_owner and ( from_guild or any(x in argument for x in ["--from-guild", "--from-server"]) @@ -365,34 +415,20 @@ class ComplexScopeParser(commands.Converter): vals.get("to_author", None) or vals.get("to_user", None) or vals.get("to_member", None) ) if to_author: + target_user_error = "" target_user = None to_user_raw = " ".join(to_author).strip() - if to_user_raw.isnumeric(): - to_user_raw = int(to_user_raw) - try: - source_user = ctx.bot.get_user(to_user_raw) - except Exception: - source_user = None - to_user_raw = str(to_user_raw) - if target_user is None: - member_converter = commands.MemberConverter() - user_converter = commands.UserConverter() - try: - target_user = await member_converter.convert(ctx, to_user_raw) - except Exception: - try: - target_user = await user_converter.convert(ctx, to_user_raw) - except Exception: - target_user = None - if target_user is None: - try: - target_user = await ctx.bot.fetch_user(to_user_raw) - except Exception: - target_user = None - if target_user is None: - raise commands.ArgParserFailure("--to-author", to_user_raw, custom_help=_USER_HELP) - else: + try: + target_user = await global_unique_user_finder(ctx, to_user_raw, guild=target_guild) specified_target_user = True + except TooManyMatches as err: + target_user_error = f"{err}\n" + except NoMatchesFound as err: + target_user_error = f"{err}\n" + if target_user is None: + raise commands.ArgParserFailure( + "--to-author", to_user_raw, custom_help=f"{target_user_error}{_USER_HELP}" + ) elif any(x in argument for x in ["--to-author", "--to-user", "--to-member"]): raise commands.ArgParserFailure("--to-user", "Nothing", custom_help=_USER_HELP) @@ -402,39 +438,33 @@ class ComplexScopeParser(commands.Converter): or vals.get("from_member", None) ) if from_author: + source_user_error = "" source_user = None - from_user_raw = " ".join(from_author).strip() - if from_user_raw.isnumeric(): - from_user_raw = int(from_user_raw) - try: - target_user = ctx.bot.get_user(from_user_raw) - except Exception: - source_user = None - from_user_raw = str(from_user_raw) - if source_user is None: - member_converter = commands.MemberConverter() - user_converter = commands.UserConverter() - try: - source_user = await member_converter.convert(ctx, from_user_raw) - except Exception: - try: - source_user = await user_converter.convert(ctx, from_user_raw) - except Exception: - source_user = None - if source_user is None: - try: - source_user = await ctx.bot.fetch_user(from_user_raw) - except Exception: - source_user = None + from_user_raw = " ".join(to_author).strip() + try: + source_user = await global_unique_user_finder( + ctx, from_user_raw, guild=target_guild + ) + specified_target_user = True + except TooManyMatches as err: + source_user_error = f"{err}\n" + except NoMatchesFound as err: + source_user_error = f"{err}\n" if source_user is None: raise commands.ArgParserFailure( - "--from-author", from_user_raw, custom_help=_USER_HELP + "--from-author", from_user_raw, custom_help=f"{source_user_error}{_USER_HELP}" ) - else: - specified_source_user = True elif any(x in argument for x in ["--from-author", "--from-user", "--from-member"]): raise commands.ArgParserFailure("--from-user", "Nothing", custom_help=_USER_HELP) + target_scope: str = target_scope or PlaylistScope.GUILD.value + target_user: Union[discord.Member, discord.User] = target_user or ctx.author + target_guild: discord.Guild = target_guild or ctx.guild + + source_scope: str = source_scope or PlaylistScope.GUILD.value + source_user: Union[discord.Member, discord.User] = source_user or ctx.author + source_guild: discord.Guild = source_guild or ctx.guild + return ( source_scope, source_user, diff --git a/redbot/cogs/audio/errors.py b/redbot/cogs/audio/errors.py index 6fa72bb96..41374a9a8 100644 --- a/redbot/cogs/audio/errors.py +++ b/redbot/cogs/audio/errors.py @@ -53,6 +53,10 @@ class TooManyMatches(PlayListError): """Too many playlist match user input.""" +class NoMatchesFound(PlayListError): + """No entries found for this input.""" + + class NotAllowed(PlayListError): """Too many playlist match user input."""