Files
Red-DiscordBot/redbot/cogs/audio/converters.py
Drapersniper 85a1057af6 working
Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com>
2019-12-18 20:09:09 +00:00

485 lines
19 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import argparse
import functools
from typing import Optional, Tuple, Union
import discord
from redbot.core import Config, commands
from redbot.core.bot import Red
from redbot.core.i18n import Translator
from .playlists import PlaylistScope, standardize_scope, get_all_playlist_converter
_ = Translator("Audio", __file__)
__all__ = [
"ComplexScopeParser",
"PlaylistConverter",
"ScopeParser",
"LazyGreedyConverter",
"standardize_scope",
"get_lazy_converter",
"get_playlist_converter",
]
_config: Config = None
_bot: Red = None
_SCOPE_HELP = """
Scope must be a valid version of one of the following:
Global
Guild
User
"""
_USER_HELP = """
Author must be a valid version of one of the following:
User ID
User Mention
User Name#123
"""
_GUILD_HELP = """
Guild must be a valid version of one of the following:
Guild ID
Exact guild name
"""
def _pass_config_to_converters(config: Config, bot: Red):
global _config, _bot
if _config is None:
_config = config
if _bot is None:
_bot = bot
class PlaylistConverter(commands.Converter):
async def convert(self, ctx: commands.Context, arg: str) -> dict:
global_matches = await get_all_playlist_converter(
PlaylistScope.GLOBAL.value, _bot, arg, guild=ctx.guild, author=ctx.author
)
guild_matches = await get_all_playlist_converter(
PlaylistScope.GUILD.value, _bot, arg, guild=ctx.guild, author=ctx.author
)
user_matches = await get_all_playlist_converter(
PlaylistScope.USER.value, _bot, arg, guild=ctx.guild, author=ctx.author
)
if not user_matches and not guild_matches and not global_matches:
raise commands.BadArgument(_("Could not match '{}' to a playlist.").format(arg))
return {
PlaylistScope.GLOBAL.value: global_matches,
PlaylistScope.GUILD.value: guild_matches,
PlaylistScope.USER.value: user_matches,
"arg": arg,
}
class NoExitParser(argparse.ArgumentParser):
def error(self, message):
raise commands.BadArgument()
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
specified_user = False
argument = argument.replace("", "--")
command, *arguments = argument.split(" -- ")
if arguments:
argument = " -- ".join(arguments)
else:
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=[])
parser.add_argument("--author", nargs="*", dest="author", default=[])
parser.add_argument("--user", nargs="*", dest="author", default=[])
parser.add_argument("--member", nargs="*", dest="author", default=[])
if not command:
parser.add_argument("command", nargs="*")
try:
vals = vars(parser.parse_args(argument.split()))
except Exception as exc:
raise commands.BadArgument() from exc
if vals["scope"]:
scope_raw = " ".join(vals["scope"]).strip()
scope = scope_raw.upper().strip()
valid_scopes = PlaylistScope.list() + [
"GLOBAL",
"GUILD",
"AUTHOR",
"USER",
"SERVER",
"MEMBER",
"BOT",
]
if scope not in valid_scopes:
raise commands.ArgParserFailure("--scope", scope_raw, custom_help=_SCOPE_HELP)
target_scope = standardize_scope(scope)
elif "--scope" in argument and not vals["scope"]:
raise commands.ArgParserFailure("--scope", "Nothing", custom_help=_SCOPE_HELP)
is_owner = await ctx.bot.is_owner(ctx.author)
guild = vals.get("guild", None) or vals.get("server", None)
if is_owner and guild:
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)
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)
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"]):
raise commands.ArgParserFailure("--guild", "Nothing", custom_help=_GUILD_HELP)
author = vals.get("author", None) or vals.get("user", None) or vals.get("member", None)
if author:
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:
specified_user = True
elif any(x in argument for x in ["--author", "--user", "--member"]):
raise commands.ArgParserFailure("--scope", "Nothing", custom_help=_USER_HELP)
return target_scope, target_user, target_guild, specified_user
class ComplexScopeParser(commands.Converter):
async def convert(
self, ctx: commands.Context, argument: str
) -> Tuple[
str,
discord.User,
Optional[discord.Guild],
bool,
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
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
specified_source_user = False
argument = argument.replace("", "--")
command, *arguments = argument.split(" -- ")
if arguments:
argument = " -- ".join(arguments)
else:
command = None
parser = NoExitParser(description="Playlist Scope Parsing.", add_help=False)
parser.add_argument("--to-scope", nargs="*", dest="to_scope", default=[])
parser.add_argument("--to-guild", nargs="*", dest="to_guild", default=[])
parser.add_argument("--to-server", nargs="*", dest="to_server", default=[])
parser.add_argument("--to-author", nargs="*", dest="to_author", default=[])
parser.add_argument("--to-user", nargs="*", dest="to_user", default=[])
parser.add_argument("--to-member", nargs="*", dest="to_member", default=[])
parser.add_argument("--from-scope", nargs="*", dest="from_scope", default=[])
parser.add_argument("--from-guild", nargs="*", dest="from_guild", default=[])
parser.add_argument("--from-server", nargs="*", dest="from_server", default=[])
parser.add_argument("--from-author", nargs="*", dest="from_author", default=[])
parser.add_argument("--from-user", nargs="*", dest="from_user", default=[])
parser.add_argument("--from-member", nargs="*", dest="from_member", default=[])
if not command:
parser.add_argument("command", nargs="*")
try:
vals = vars(parser.parse_args(argument.split()))
except Exception as exc:
raise commands.BadArgument() from exc
is_owner = await ctx.bot.is_owner(ctx.author)
valid_scopes = PlaylistScope.list() + [
"GLOBAL",
"GUILD",
"AUTHOR",
"USER",
"SERVER",
"MEMBER",
"BOT",
]
if vals["to_scope"]:
to_scope_raw = " ".join(vals["to_scope"]).strip()
to_scope = to_scope_raw.upper().strip()
if to_scope not in valid_scopes:
raise commands.ArgParserFailure(
"--to-scope", to_scope_raw, custom_help=_SCOPE_HELP
)
target_scope = standardize_scope(to_scope)
elif "--to-scope" in argument and not vals["to_scope"]:
raise commands.ArgParserFailure("--to-scope", "Nothing", custom_help=_SCOPE_HELP)
if vals["from_scope"]:
from_scope_raw = " ".join(vals["from_scope"]).strip()
from_scope = from_scope_raw.upper().strip()
if from_scope not in valid_scopes:
raise commands.ArgParserFailure(
"--from-scope", from_scope_raw, custom_help=_SCOPE_HELP
)
source_scope = standardize_scope(from_scope)
elif "--from-scope" in argument and not vals["to_scope"]:
raise commands.ArgParserFailure("--to-scope", "Nothing", custom_help=_SCOPE_HELP)
to_guild = vals.get("to_guild", None) or vals.get("to_server", None)
if is_owner and to_guild:
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
if target_guild is None:
raise commands.ArgParserFailure(
"--to-guild", to_guild_raw, custom_help=_GUILD_HELP
)
elif not is_owner and (
to_guild or any(x in argument for x in ["--to-guild", "--to-server"])
):
raise commands.BadArgument("You cannot use `--to-server`")
elif any(x in argument for x in ["--to-guild", "--to-server"]):
raise commands.ArgParserFailure("--to-server", "Nothing", custom_help=_GUILD_HELP)
from_guild = vals.get("from_guild", None) or vals.get("from_server", None)
if is_owner and from_guild:
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
if source_guild is None:
raise commands.ArgParserFailure(
"--from-guild", from_guild_raw, custom_help=_GUILD_HELP
)
elif not is_owner and (
from_guild or any(x in argument for x in ["--from-guild", "--from-server"])
):
raise commands.BadArgument("You cannot use `--from-server`")
elif any(x in argument for x in ["--from-guild", "--from-server"]):
raise commands.ArgParserFailure("--from-server", "Nothing", custom_help=_GUILD_HELP)
to_author = (
vals.get("to_author", None) or vals.get("to_user", None) or vals.get("to_member", None)
)
if to_author:
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:
specified_target_user = True
elif any(x in argument for x in ["--to-author", "--to-user", "--to-member"]):
raise commands.ArgParserFailure("--to-user", "Nothing", custom_help=_USER_HELP)
from_author = (
vals.get("from_author", None)
or vals.get("from_user", None)
or vals.get("from_member", None)
)
if from_author:
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
if source_user is None:
raise commands.ArgParserFailure(
"--from-author", from_user_raw, custom_help=_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)
return (
source_scope,
source_user,
source_guild,
specified_source_user,
target_scope,
target_user,
target_guild,
specified_target_user,
)
class LazyGreedyConverter(commands.Converter):
def __init__(self, splitter: str):
self.splitter_Value = splitter
async def convert(self, ctx: commands.Context, argument: str) -> str:
full_message = ctx.message.content.partition(f" {argument} ")
if len(full_message) == 1:
full_message = (
(argument if argument not in full_message else "") + " " + full_message[0]
)
elif len(full_message) > 1:
full_message = (
(argument if argument not in full_message else "") + " " + full_message[-1]
)
greedy_output = (" " + full_message.replace("", "--")).partition(
f" {self.splitter_Value}"
)[0]
return f"{greedy_output}".strip()
def get_lazy_converter(splitter: str) -> type:
"""
Returns a typechecking safe `LazyGreedyConverter` suitable for use with discord.py.
"""
class PartialMeta(type(LazyGreedyConverter)):
__call__ = functools.partialmethod(type(LazyGreedyConverter).__call__, splitter)
class ValidatedConverter(LazyGreedyConverter, metaclass=PartialMeta):
pass
return ValidatedConverter
def get_playlist_converter() -> type:
"""
Returns a typechecking safe `PlaylistConverter` suitable for use with discord.py.
"""
class PartialMeta(type(PlaylistConverter)):
__call__ = functools.partialmethod(type(PlaylistConverter).__call__)
class ValidatedConverter(PlaylistConverter, metaclass=PartialMeta):
pass
return ValidatedConverter