async def convert(self, ctx: commands.Context, argument: str) -> discord.Role: try: member = await super().convert(ctx, argument) except BadArgument: guild = ctx.guild result = [] for m in process.extract( argument, {m: unidecode(m.display_name) for m in guild.members}, limit=None, score_cutoff=75, ): result.append((m[2], m[1])) if not result: raise BadArgument(f'Member "{argument}" not found.' if self. response else None) sorted_result = sorted(result, key=lambda r: r[1], reverse=True) member = sorted_result[0][0] return member
async def convert(cls, ctx: Context, argument: str): start: datetime command: Optional[str] = None recur: Optional[timedelta] = None # Blame iOS smart punctuation, # and end users who use it for this (minor) perf loss argument = argument.replace("—", "--") command, *arguments = argument.split(" -- ") if arguments: argument = " -- ".join(arguments) else: command = None parser = NoExitParser(description="Scheduler event parsing", add_help=False) parser.add_argument( "-q", "--quiet", action="store_true", dest="quiet", default=False ) parser.add_argument("--every", nargs="*", dest="every", default=[]) if not command: parser.add_argument("command", nargs="*") at_or_in = parser.add_mutually_exclusive_group() at_or_in.add_argument("--start-at", nargs="*", dest="at", default=[]) at_or_in.add_argument("--start-in", nargs="*", dest="in", default=[]) try: vals = vars(parser.parse_args(argument.split(" "))) except Exception as exc: raise BadArgument() from exc if not (vals["at"] or vals["in"]): raise BadArgument("You must provide one of `--start-in` or `--start-at`") if not command and not vals["command"]: raise BadArgument("You have to provide a command to run") command = command or " ".join(vals["command"]) for delta in ("in", "every"): if vals[delta]: parsed = parse_timedelta(" ".join(vals[delta])) if not parsed: raise BadArgument("I couldn't understand that time interval") if delta == "in": start = datetime.now(timezone.utc) + parsed else: recur = parsed if recur.total_seconds() < 60: raise BadArgument( "You can't schedule something to happen that frequently, " "I'll get ratelimited." ) if vals["at"]: try: start = parse_time(" ".join(vals["at"])) except Exception: raise BadArgument("I couldn't understand that starting time.") from None return cls(command=command, start=start, recur=recur, quiet=vals["quiet"])
async def convert(self, ctx, argument): argument = argument.replace("—", "--") parser = NoExitParser(description="Pokecord Search", add_help=False) pokemon = parser.add_mutually_exclusive_group() pokemon.add_argument("--name", "--n", nargs="*", dest="names", default=[]) pokemon.add_argument("--level", "--l", nargs="*", dest="level", type=int, default=0) pokemon.add_argument("--id", "--i", nargs="*", dest="id", type=int, default=0) pokemon.add_argument("--variant", "--v", nargs="*", dest="variant", default=[]) pokemon.add_argument("--gender", "--g", nargs="*", dest="gender", default=[]) pokemon.add_argument("--iv", nargs="*", dest="iv", type=int, default=0) pokemon.add_argument("--type", "--t", nargs="*", dest="type", default=[]) try: vals = vars(parser.parse_args(argument.split(" "))) except Exception as error: raise BadArgument() from error if not any([ vals["names"], vals["level"], vals["id"], vals["variant"], vals["gender"], vals["iv"], vals["type"], ]): raise BadArgument( "You must provide one of `--name`, `--level`, `--id`, `--variant`, `--iv`, `--gender` or `--type``" ) vals["names"] = " ".join(vals["names"]) vals["variant"] = " ".join(vals["variant"]) vals["gender"] = " ".join(vals["gender"]) vals["type"] = " ".join(vals["type"]) return vals
async def convert(self, ctx: commands.Converter, argument: str) -> str: command = ctx.bot.get_command(argument) if command: raise BadArgument(f"`{argument}` is already a registered command.") return "".join(argument.split())
def error(self, message): # Specifically not a parser error, which we have custom handling for. raise BadArgument() from None
def parse_accage(cls, accage: int): if not account_age_checker(accage): raise BadArgument("Days must be less than Discord's creation date")
def parser(self, ctx: Context): if self.account_age: if not isinstance(self.account_age, int): raise BadArgument( "Account age days must be int, not {}".format( type(self.account_age).__name__)) if not account_age_checker(self.account_age): raise BadArgument( "Account age days must be less than Discord's creation date" ) if self.join_age: if not isinstance(self.join_age, int): raise BadArgument("Join age days must be int, not {}".format( type(self.join_age).__name__)) if not join_age_checker(ctx, self.join_age): raise BadArgument( "Join age days must be less than this guild's creation date" ) if self.maximum_entries: if not isinstance(self.maximum_entries, int): raise BadArgument("Maximum entries must be int, not {}".format( type(self.maximum_entries).__name__)) if self.name: if not isinstance(self.name, str): raise BadArgument("Name must be str, not {}".format( type(self.name).__name__)) if len(self.name) > 25: raise BadArgument( "Name must be under 25 characters, your raffle name had {}" .format(len(self.name))) for char in self.name: if char == "_": # We want to allow underscores continue if not char.isalnum(): index = self.name.index(char) marker = ( f"{self.name}\n{' ' * (index)}^\n" f"Characters must be alphanumeric or underscores, not \"{char}\"" ) raise RaffleSyntaxError( f"In \"name\" field, character {index+1}\n\n{marker}") else: raise RequiredKeyError("name") if self.description: if not isinstance(self.description, str): raise BadArgument("Description must be str, not {}".format( type(self.description).__name__)) if self.roles_needed_to_enter: if not isinstance(self.roles_needed_to_enter, list): raise BadArgument( "Roles must be a list of Discord role IDs, not {}".format( type(self.roles_needed_to_enter).__name__)) for r in self.roles_needed_to_enter: if not ctx.guild.get_role(r): raise UnknownEntityError(r, "role") if self.prevented_users: if not isinstance(self.prevented_users, list): raise BadArgument( "Prevented users must be a list of Discord user IDs, not {}" .format(type(self.prevented_users).__name__)) for u in self.prevented_users: if not ctx.bot.get_user(u): raise UnknownEntityError(u, "user") if self.allowed_users: if not isinstance(self.allowed_users, list): raise BadArgument( "Allowed users must be a list of Discord user IDs, not {}". format(type(self.allowed_users).__name__)) for u in self.allowed_users: if not ctx.bot.get_user(u): raise UnknownEntityError(u, "user") if self.end_message: if not isinstance(self.end_message, str): # Will render {} without quotes, best not to include the type.__name__ here raise BadArgument("End message must be str") try: # This will raise BadArgument self.end_message.format(winner=RaffleSafeMember( discord.Member), raffle=r"{raffle}") except KeyError as e: raise BadArgument( f"{e} was an unexpected argument in your end_message block" ) if self.on_end_action: valid_actions = ("end", "remove_winner", "keep_winner") if not isinstance(self.on_end_action, str) or self.on_end_action not in valid_actions: raise BadArgument( "on_end_action must be one of 'end', 'remove_winner', or 'keep_winner'" )
async def convert(cls, ctx: Context, argument: str): if argument.lower() in PLATFORMS: return PLATFORMS[argument.lower()] raise BadArgument("Platform isn't found, please specify either psn, xbox or pc.")
async def convert(cls, ctx: Context, argument: str): if argument.lower() in REGIONS: return REGIONS[argument.lower()] raise BadArgument("Region not found, please specify either na, eu or asia.")
def get_content(self, data: dict, *, content: str = None) -> Optional[str]: content = data.pop("content", content) if content is not None and not self.allow_content: raise BadArgument("The `content` field is not supported for this command.") return content
class SteamUser: """SteamCommunity profile""" def __init__(self, steam: API, player_id: str): self._steam = steam self._user = self._steam["ISteamUser"] self._player = self._steam["IPlayerService"] self._userdata = self._user.GetPlayerSummaries( player_id)["response"]["players"][0] self._bandata = self._user.GetPlayerBans(player_id)["players"][0] self._personastate = self._userdata.get("personastate", 0) visibilites = { 1: _("Private"), 2: _("Friends only"), 3: _("Public"), # Friends of friends 4: _("Users only"), 5: _("Public"), } acctypes = ["I", "U", "M", "G", "A", "P", "C", "g", "T", "", "a"] self.steamid64 = self._userdata.get("steamid") self.createdat = self._userdata.get("timecreated") self.personaname = self._userdata.get("personaname") self.profileurl = self._userdata.get("profileurl") self.avatar32 = self._userdata.get("avatar") self.avatar64 = self._userdata.get("avatarmedium") self.avatar184 = self._userdata.get("avatarfull") self.visibility = visibilites[self._userdata.get( "communityvisibilitystate", 1)] self.hasprofile = bool(self._userdata.get("profilestate")) self.lastlogoff = self._userdata.get("lastlogoff") self.comments = self._userdata.get("commentpermission") self.realname = self._userdata.get("realname") self.clanid = self._userdata.get("primaryclanid") self.gameid = self._userdata.get("gameid") gameserver = self._userdata.get("gameserverip") self.gameserver = gameserver if gameserver != any(["0.0.0.0:0", None ]) else None self.gameextrainfo = self._userdata.get("gameextrainfo") self.country = self._userdata.get("loccountrycode") self.state = self._userdata.get("locstatecode") self.cityid = self._userdata.get("loccityid") self.level = self._player.GetSteamLevel(player_id)["response"].get( "player_level", 0) self.communitybanned = self._bandata.get("CommunityBanned") self.VACbanned = self._bandata.get("VACBanned") self.VACbans = self._bandata.get("NumberOfVACBans") self.sincelastban = self._bandata.get("DaysSinceLastBan") self.gamebans = self._bandata.get("NumberOfGameBans") economyban = self._bandata.get("EconomyBan") self.economyban = economyban if economyban != "none" else None self.iduniverse = int(self.steamid64) >> 56 self.idpart = int(self.steamid64) & 0b1 self.accountnumber = (int(self.steamid64) & 0b11111111111111111111111111111110) >> 1 self.accountid = int( self.steamid64) & 0b11111111111111111111111111111111 self.idinstance = ( int(self.steamid64) & 0b1111111111111111111100000000000000000000000000000000) >> 32 self.idtype = ( int(self.steamid64) & 0b11110000000000000000000000000000000000000000000000000000) >> 52 self.steamid = "STEAM_{}:{}:{}".format(self.iduniverse, self.idpart, self.accountnumber) self.sid3 = "[{}:{}:{}]".format(acctypes[self.idtype], self.iduniverse, self.accountid) @classmethod async def convert(cls, ctx, argument): steam = ctx.cog.steam if "ISteamUser" not in list(steam._interfaces.keys()): raise BadArgument(_("ApiKey not set or incorrect.")) userapi = steam["ISteamUser"] argument = argument.replace("\\", "/") if (url_parsed := urlparse(argument)).scheme in ["http", "https"]: if url_parsed.netloc != "steamcommunity.com": raise BadArgument( _("{} is not a Steam Community domain name.").format( url_parsed.netloc)) argument = PurePosixPath(url_parsed.path).name if argument.isdigit(): id64 = argument else: if argument.startswith("STEAM_"): try: id64 = SteamID.from_text(argument).as_64() except SteamIDError: raise BadArgument(_("Incorrect SteamID32 provided.")) else: try: id64 = (userapi.ResolveVanityURL(argument).get( "response", {}).get("steamid", "")) except JSONDecodeError: raise BadArgument( _("Unable to resolve {} into SteamID. " "Check your input or try again later.").format( argument)) if not id64.isnumeric(): raise BadArgument( _("User with SteamID {} not found.").format(argument)) async with ctx.typing(): try: profile = await ctx.bot.loop.run_in_executor( None, SteamUser, steam, id64) except IndexError: raise BadArgument( _("Unable to get profile for {} ({}). Check your input or try again later." ).format(argument, id64)) return profile
def check_data_type(self, ctx: commands.Context, data, *, data_type=dict): if not isinstance(data, data_type): raise BadArgument( f"This doesn't seem to be properly formatted embed {self.conversion_type.upper()}. " f"Refer to the link on `{ctx.clean_prefix}help {ctx.command.qualified_name}`." )
class StringToEmbed(Converter): def __init__( self, *, conversion_type: str = "json", validate: bool = True, content: bool = False ): self.CONVERSION_TYPES = { "json": self.load_from_json, "yaml": self.load_from_yaml, } self.validate = validate self.conversion_type = conversion_type.lower() self.allow_content = content try: self.converter = self.CONVERSION_TYPES[self.conversion_type] except KeyError as exc: raise ValueError( f"{conversion_type} is not a valid conversion type for Embed conversion." ) from exc async def convert(self, ctx: commands.Context, argument: str) -> discord.Embed: data = argument.strip("`") data = await self.converter(ctx, data) content = self.get_content(data) if data.get("embed"): data = data["embed"] elif data.get("embeds"): data = data.get("embeds")[0] self.check_data_type(ctx, data) fields = await self.create_embed(ctx, data, content=content) content = fields["content"] embed = fields["embed"] if self.validate: await self.validate_embed(ctx, embed, content=content) return embed def check_data_type(self, ctx: commands.Context, data, *, data_type=dict): if not isinstance(data, data_type): raise BadArgument( f"This doesn't seem to be properly formatted embed {self.conversion_type.upper()}. " f"Refer to the link on `{ctx.clean_prefix}help {ctx.command.qualified_name}`." ) async def load_from_json(self, ctx: commands.Context, data: str, **kwargs) -> dict: try: data = json.loads(data) except json.decoder.JSONDecodeError as error: await self.embed_convert_error(ctx, "JSON Parse Error", error) self.check_data_type(ctx, data, **kwargs) return data async def load_from_yaml(self, ctx: commands.Context, data: str, **kwargs) -> dict: try: data = yaml.safe_load(data) except Exception as error: await self.embed_convert_error(ctx, "YAML Parse Error", error) self.check_data_type(ctx, data, **kwargs) return data def get_content(self, data: dict, *, content: str = None) -> Optional[str]: content = data.pop("content", content) if content is not None and not self.allow_content: raise BadArgument("The `content` field is not supported for this command.") return content async def create_embed( self, ctx: commands.Context, data: dict, *, content: str = None ) -> Dict[str, Union[discord.Embed, str]]: content = self.get_content(data, content=content) if timestamp := data.get("timestamp"): data["timestamp"] = timestamp.strip("Z") try: e = discord.Embed.from_dict(data) length = len(e) if length > 6000: raise BadArgument( f"Embed size exceeds Discord limit of 6000 characters ({length})." ) except BadArgument: raise except Exception as error: await self.embed_convert_error(ctx, "Embed Parse Error", error) return {"embed": e, "content": content}
async def convert(self, ctx: Context, arg: str) -> datetime.datetime: parsed = dateparser.parse(arg) if parsed is None: raise BadArgument("Unrecognized date/time.") return parsed
async def convert(self, ctx, argument): argument = argument.replace("—", "--") parser = NoExitParser(description="Giveaway Created", add_help=False) # Required Arguments parser.add_argument("--prize", "--p", dest="prize", nargs="*", default=[]) timer = parser.add_mutually_exclusive_group() timer.add_argument("--duration", "--d", dest="duration", nargs="*", default=[]) timer.add_argument("--end", "--e", dest="end", nargs="*", default=[]) # Optional Arguments parser.add_argument("--channel", dest="channel", default=None, nargs="?") parser.add_argument("--roles", "--r", "--restrict", dest="roles", nargs="*", default=[]) parser.add_argument("--multiplier", "--m", dest="multi", default=None, type=int, nargs="?") parser.add_argument("--multi-roles", "--mr", nargs="*", dest="multi-roles", default=[]) parser.add_argument("--joined", dest="joined", default=None, type=int, nargs="?") parser.add_argument("--created", dest="created", default=None, type=int, nargs="?") parser.add_argument("--blacklist", dest="blacklist", nargs="*", default=[]) parser.add_argument("--winners", dest="winners", default=None, type=int, nargs="?") parser.add_argument("--mentions", dest="mentions", nargs="*", default=[]) parser.add_argument("--description", dest="description", default=[], nargs="*") parser.add_argument("--emoji", dest="emoji", default=None, nargs="*") # Setting arguments parser.add_argument("--multientry", action="store_true") parser.add_argument("--notify", action="store_true") parser.add_argument("--congratulate", action="store_true") parser.add_argument("--announce", action="store_true") parser.add_argument("--ateveryone", action="store_true") parser.add_argument("--athere", action="store_true") parser.add_argument("--show-requirements", action="store_true") # Integrations parser.add_argument("--cost", dest="cost", default=None, type=int, nargs="?") parser.add_argument("--level-req", dest="levelreq", default=None, type=int, nargs="?") parser.add_argument("--rep-req", dest="repreq", default=None, type=int, nargs="?") parser.add_argument("--tatsu-level", default=None, type=int, nargs="?") parser.add_argument("--tatsu-rep", default=None, type=int, nargs="?") parser.add_argument("--mee6-level", default=None, type=int, nargs="?") parser.add_argument("--amari-level", default=None, type=int, nargs="?") parser.add_argument("--amari-weekly-xp", default=None, type=int, nargs="?") try: vals = vars(parser.parse_args(argument.split(" "))) except Exception as error: raise BadArgument( "Could not parse flags correctly, ensure flags are correctly used." ) from error if not vals["prize"]: raise BadArgument("You must specify a prize. Use `--prize` or `-p`") # if not any([vals["duration"], vals["end"]]): raise BadArgument( "You must specify a duration or end date. Use `--duration` or `-d` or `--end` or `-e`" ) nums = [vals["cost"], vals["joined"], vals["created"], vals["winners"]] for val in nums: if val is None: continue if val < 1: raise BadArgument("Number must be greater than 0") valid_multi_roles = [] for role in vals["multi-roles"]: try: role = await RoleConverter().convert(ctx, role) valid_multi_roles.append(role.id) except BadArgument: raise BadArgument(f"The role {role} does not exist within this server.") vals["multi-roles"] = valid_multi_roles valid_exclusive_roles = [] for role in vals["roles"]: try: role = await RoleConverter().convert(ctx, role) valid_exclusive_roles.append(role.id) except BadArgument: raise BadArgument(f"The role {role} does not exist within this server.") vals["roles"] = valid_exclusive_roles valid_blacklist_roles = [] for role in vals["blacklist"]: try: role = await RoleConverter().convert(ctx, role) valid_blacklist_roles.append(role.id) except BadArgument: raise BadArgument(f"The role {role} does not exist within this server.") vals["blacklist"] = valid_blacklist_roles = [] valid_mentions = [] for role in vals["mentions"]: try: role = await RoleConverter().convert(ctx, role) valid_mentions.append(role.id) except BadArgument: raise BadArgument(f"The role {role} does not exist within this server.") vals["mentions"] = valid_mentions if vals["channel"]: try: vals["channel"] = await TextChannelConverter().convert(ctx, vals["channel"]) except BadArgument: raise BadArgument("Invalid channel.") if vals["levelreq"] or vals["repreq"]: cog = ctx.bot.get_cog("Leveler") if not cog: raise BadArgument("Leveler cog not loaded.") if not hasattr(cog, "db"): raise BadArgument( "This may be the wrong leveling cog. Ensure you are using Fixators." ) if vals["tatsu_level"] or vals["tatsu_rep"]: token = await ctx.bot.get_shared_api_tokens("tatsumaki") if not token.get("authorization"): raise BadArgument( f"You do not have a valid Tatsumaki API token. Check `{ctx.clean_prefix}gw integrations` for more info." ) if vals["amari_level"] or vals["amari_weekly_xp"]: token = await ctx.bot.get_shared_api_tokens("amari") if not token.get("authorization"): raise BadArgument( f"You do not have a valid Amari API token. Check `{ctx.clean_prefix}gw integrations` for more info." ) if (vals["multi"] or vals["multi-roles"]) and not (vals["multi"] and vals["multi-roles"]): raise BadArgument( "You must specify a multiplier and roles. Use `--multiplier` or `-m` and `--multi-roles` or `-mr`" ) if ( (vals["ateveryone"] or vals["athere"]) and not ctx.channel.permissions_for(ctx.me).mention_everyone and not ctx.channel.permissions_for(ctx.author).mention_everyone ): raise BadArgument( "You do not have permission to mention everyone. Please ensure the bot and you have `Mention Everyone` permission." ) if vals["description"]: vals["description"] = " ".join(vals["description"]) if len(vals["description"]) > 1000: raise BadArgument("Description must be less than 1000 characters.") if vals["emoji"]: vals["emoji"] = " ".join(vals["emoji"]).rstrip().lstrip() custom = False try: vals["emoji"] = await EmojiConverter().convert(ctx, vals["emoji"]) custom = True except Exception: vals["emoji"] = str(vals["emoji"]).replace("\N{VARIATION SELECTOR-16}", "") try: await ctx.message.add_reaction(vals["emoji"]) await ctx.message.remove_reaction(vals["emoji"], ctx.me) except Exception: raise BadArgument("Invalid emoji.") if custom: vals["emoji"] = vals["emoji"].id vals["prize"] = " ".join(vals["prize"]) if vals["duration"]: tc = TimedeltaConverter() try: vals["duration"] = await tc.convert(ctx, " ".join(vals["duration"])) except BadArgument: raise BadArgument("Invalid duration. Use `--duration` or `-d`") else: try: time = dateparser.parse(" ".join(vals["end"])) if time.tzinfo is None: time = time.replace(tzinfo=timezone.utc) if datetime.now(timezone.utc) > time: raise BadArgument("End date must be in the future.") time = time - datetime.now(timezone.utc) vals["duration"] = time except Exception: raise BadArgument( "Invalid end date. Use `--end` or `-e`. Ensure to pass a timezone, otherwise it defaults to UTC." ) return vals
async def convert(self, ctx: commands.Context, argument: str) -> discord.Member: member = await super().convert(ctx, argument) if member.bot: raise BadArgument("Keep bots out of this. We aren't susceptible to human diseases.") return member
if matches: urls.extend(match.group(1) for match in matches) if emojis: for emoji in emojis: ext = "gif" if emoji.group(2) else "png" url = "https://cdn.discordapp.com/emojis/{id}.{ext}?v=1".format( id=emoji.group(3), ext=ext) urls.append(url) if mentions: for mention in mentions: user = ctx.guild.get_member(int(mention.group(1))) if user is not None: url = IMAGE_LINKS.search( str(user.avatar_url_as(format="png"))) urls.append(url.group(1)) if not urls and ids: for possible_id in ids: if user := ctx.guild.get_member(int(possible_id.group(0))): url = IMAGE_LINKS.search( str(user.avatar_url_as(format="png"))) urls.append(url.group(1)) if attachments: urls.extend(attachment.url for attachment in attachments) if not urls and ctx.guild: if user := ctx.guild.get_member_named(argument): url = user.avatar_url_as(format="png") urls.append(url) if not urls: raise BadArgument("No images found.") return urls[0]
async def convert(self, ctx: Context, argument: str) -> int: if argument.isnumeric() and len(argument) >= 17: return int(argument) raise BadArgument(_("{} doesn't look like a valid message ID.").format(argument))
def parse_joinage(cls, ctx: Context, new_join_age: int): guildage = (now - ctx.guild.created_at).days if not join_age_checker(ctx, new_join_age): raise BadArgument( "Days must be less than this guild's creation date ({} days)". format(guildage))
def parse(self, argument: str): """Parse natural language argument list.""" mat = re.search(PAT_OBS_LINK, argument) if mat and mat["url"]: return argument try: arg_normalized = re.sub(r"((^| )(id|not)) ?by ", r"\2\3-by ", argument, re.I) arg_normalized = re.sub(r"((^| )in ?prj) ", r"\2in-prj ", arg_normalized, re.I) arg_normalized = re.sub(r"((^| )added ?(on|since|until)) ", r"\2added-\3 ", arg_normalized, re.I) tokens = shlex.split(arg_normalized, posix=False) except ValueError as err: raise BadArgument(err.args[0]) from err opts = [] macro_by = "" macro_from = "" macro_of = "" expanded_tokens = [] # - macro expansions are allowed anywhere in the taxon argument, but # otherwise only when not immediately after an option token # - e.g. `--of rg birds` will expand the `rg` macro, but `--from home` # will not expand the `home` macro suppress_macro = False arg_count = 0 expected_args = ARGPARSE_ARGS for _token in tokens: tok = _token.lower() argument_expected = False is_unix_arg = False if tok in expected_args: tok = f"--{tok}" if re.match(r"--", tok): is_unix_arg = True arg_count += 1 # Every option token expects at least one argument. argument_expected = True if tok == "--of": # Ignore "--of" after it is explicitly inserted. expected_args = REMAINING_ARGS suppress_macro = False else: suppress_macro = True # Insert at head of explicit "opt" any collected opt macro # expansions. This allows them to be combined in the same query, # e.g. # `reverse birds opt observed_on=2021-06-13` -> # `--of birds --opt order=asc observed_on=2021-06-13` if tok == "--opt" and opts: expanded_tokens.extend(["--opt", *opts]) opts = [] continue # Discard any prior macro expansions of these; see note below if tok == "--by": macro_by = "" if tok == "--from": macro_from = "" if tok == "--of": macro_of = "" if not suppress_macro: if tok in MACROS: macro = MACROS[tok] if macro: # Collect any expansions and continue: _macro_opt_args = macro.get("opt") if _macro_opt_args: opts.extend(_macro_opt_args) _macro_by = macro.get("by") if _macro_by: macro_by = _macro_by _macro_from = macro.get("from") if _macro_from: macro_from = _macro_from _macro_of = macro.get("of") if _macro_of: macro_of = _macro_of continue # If it's an ordinary word token appearing before all other args, # then it's treated implicitly as first word of the "--of" argument, # which is inserted here. if arg_count == 0: arg_count += 1 expanded_tokens.append("--of") macro_of = "" expected_args = REMAINING_ARGS # Append the unix option token (downcased) or ordinary word expanded_tokens.append(tok if is_unix_arg else _token) # Macros allowed again after first non-ARGS token is consumed: if not argument_expected: suppress_macro = False # Handle collected arguments that were not already # inserted into filtered_args above by appending them: if opts: expanded_tokens.extend(["--opt", *opts]) # Note: There can only be one of macro_by, macro_from, or macro_of until we support # multiple users / places / taxa, so the last user, place, or taxon given wins, # superseding anything given earlier in the query. if macro_by: expanded_tokens.extend(["--by", macro_by]) if macro_from: expanded_tokens.extend(["--from", macro_from]) if macro_of: expanded_tokens.extend(["--of", macro_of]) # Treat any unexpanded args before the first option keyword # argument as implicit "--of" option arguments: if not re.match(r"^--", expanded_tokens[0]): expanded_tokens.insert(0, "--of") argument_normalized = " ".join(expanded_tokens) return super().parse(argument_normalized)
async def convert(self, ctx: commands.Context, argument: str) -> str: if argument.lower() not in ["kick", "ban"]: raise BadArgument( "This is not a valid action. The valid actions are kick and ban. For roles, supply a role." ) return argument.lower()
def __getattr__(self, *_args): raise BadArgument(r"Your `{winner}` received an unexpected attribute")
async def convert(self, ctx, argument): argument = argument.replace("—", "--") parser = NoExitParser(description="Targeter argument parser", add_help=False) # Nicknames / Usernames names = parser.add_argument_group() names.add_argument("--nick", nargs="*", dest="nick", default=[]) names.add_argument("--user", nargs="*", dest="user", default=[]) names.add_argument("--name", nargs="*", dest="name", default=[]) names.add_argument("--not-nick", nargs="*", dest="not-nick", default=[]) names.add_argument("--not-user", nargs="*", dest="not-user", default=[]) names.add_argument("--not-name", nargs="*", dest="not-name", default=[]) names.add_argument("--a-nick", dest="a-nick", action="store_true") names.add_argument("--no-nick", dest="no-nick", action="store_true") discs = parser.add_mutually_exclusive_group() discs.add_argument("--disc", nargs="*", dest="disc", default=[]) discs.add_argument("--not-disc", nargs="*", dest="ndisc", default=[]) # Roles parser.add_argument("--roles", nargs="*", dest="roles", default=[]) parser.add_argument("--any-role", nargs="*", dest="any-role", default=[]) parser.add_argument("--not-roles", nargs="*", dest="not-roles", default=[]) parser.add_argument("--not-any-role", nargs="*", dest="not-any-role", default=[]) single = parser.add_mutually_exclusive_group() single.add_argument("--a-role", dest="a-role", action="store_true") single.add_argument("--no-role", dest="no-role", action="store_true") # Date stuff jd = parser.add_argument_group() jd.add_argument("--joined-on", nargs="*", dest="joined-on", default=[]) jd.add_argument("--joined-before", nargs="*", dest="joined-be", default=[]) jd.add_argument("--joined-after", nargs="*", dest="joined-af", default="") cd = parser.add_argument_group() cd.add_argument("--created-on", nargs="*", dest="created-on", default=[]) cd.add_argument("--created-before", nargs="*", dest="created-be", default=[]) cd.add_argument("--created-after", nargs="*", dest="created-af", default=[]) # Status / Activity / Device / Just Basically Profile Stuff parser.add_argument("--status", nargs="*", dest="status", default=[]) parser.add_argument("--device", nargs="*", dest="device", default=[]) bots = parser.add_mutually_exclusive_group() bots.add_argument("--only-bots", dest="bots", action="store_true") bots.add_argument("--no-bots", dest="nbots", action="store_true") parser.add_argument("--activity-type", nargs="*", dest="at", default=[]) parser.add_argument("--activity", nargs="*", dest="a", default=[]) at = parser.add_mutually_exclusive_group() at.add_argument("--no-activity", dest="na", action="store_true") at.add_argument("--an-activity", dest="aa", action="store_true") # Permissions parser.add_argument("--perms", nargs="*", dest="perms", default=[]) parser.add_argument("--any-perm", nargs="*", dest="any-perm", default=[]) parser.add_argument("--not-perms", nargs="*", dest="not-perms", default=[]) parser.add_argument("--not-any-perm", nargs="*", dest="not-any-perm", default=[]) # Extra parser.add_argument("--format", nargs="*", dest="format", default=["menu"]) try: vals = vars(parser.parse_args(argument.split(" "))) except Exception as exc: raise BadArgument() from exc try: for key, value in vals.items(): if type(value) == list: split_words = value word_list = [] tmp = "" for word in split_words: if not word.startswith('"') and not word.endswith('"') and not tmp: if word.startswith(r"\""): word = word[1:] word_list.append(word) else: echanged = False if word.endswith(r"\""): word = word[:-2] + '"' echanged = True schanged = False if word.startswith(r"\""): word = word[1:] schanged = True if word.startswith('"') and not schanged: if word.startswith('"') and word.endswith('"') and len(word) > 1: word_list.append(word) else: if tmp.endswith(" "): word_list.append(tmp) tmp = "" continue tmp += word[1:] + " " elif word.endswith('"') and not echanged: tmp += word[:-1] word_list.append(tmp) tmp = "" else: if schanged or echanged: word_list.append(word) continue tmp += word + " " if tmp: raise BadArgument("A quote was started but never finished.") vals[key] = word_list except Exception as e: raise BadArgument(str(e)) if any(s for s in vals["status"] if not s.lower() in ["online", "dnd", "idle", "offline"]): raise BadArgument( "Invalid status. Must be either `online`, `dnd`, `idle` or `offline`." ) # Useeeeeeeeeeeeeeeeeeeernames (and Stuff) if vals["disc"]: new = [] for disc in vals["disc"]: if len(disc) != 4: raise BadArgument("Discriminators must have the length of 4") try: new.append(int(disc)) except ValueError: raise BadArgument("Discriminators must be valid integers") vals["disc"] = new if vals["ndisc"]: new = [] for disc in vals["ndisc"]: if len(disc) != 4: raise BadArgument("Discriminators must have the length of 4") try: new.append(int(disc)) except ValueError: raise BadArgument("Discriminators must be valid integers") vals["ndisc"] = new # Rooooooooooooooooles rc = RoleConverter() new = [] for role in vals["roles"]: r = await rc.convert(ctx, role) if not r: raise BadArgument(f"Couldn't find a role matching: {role}") new.append(r) vals["roles"] = new new = [] for role in vals["any-role"]: r = await rc.convert(ctx, role) if not r: raise BadArgument(f"Couldn't find a role matching: {role}") new.append(r) vals["any-role"] = new new = [] for role in vals["not-roles"]: r = await rc.convert(ctx, role) if not r: raise BadArgument(f"Couldn't find a role matching: {role}") new.append(r) vals["not-roles"] = new new = [] for role in vals["not-any-role"]: r = await rc.convert(ctx, role) if not r: raise BadArgument(f"Couldn't find a role matching: {role}") new.append(r) vals["not-any-role"] = new # Daaaaaaaaaaaaaaaaaates if vals["joined-on"]: try: vals["joined-on"] = parse(" ".join(vals["joined-on"])) except: raise BadArgument("Failed to parse --joined-on argument") if vals["joined-be"]: try: vals["joined-be"] = parse(" ".join(vals["joined-be"])) except: raise BadArgument("Failed to parse --joined-be argument") if vals["joined-af"]: try: vals["joined-af"] = parse(" ".join(vals["joined-af"])) except: raise BadArgument("Failed to parse --joined-after argument") if vals["created-on"]: try: vals["created-on"] = parse(" ".join(vals["created-on"])) except: raise BadArgument("Failed to parse --created-on argument") if vals["created-be"]: try: vals["created-be"] = parse(" ".join(vals["created-be"])) except: raise BadArgument("Failed to parse --created-be argument") if vals["created-af"]: try: vals["created-af"] = parse(" ".join(vals["created-af"])) except: raise BadArgument("Failed to parse --created-af argument") # Actiiiiiiiiiiiiiiiiivities if vals["device"]: if not all(d in ["desktop", "mobile", "web"] for d in vals["device"]): raise BadArgument("Bad device. Must be `desktop`, `mobile` or `web`.") if vals["at"]: at = discord.ActivityType switcher = { "unknown": at.unknown, "playing": at.playing, "streaming": at.streaming, "listening": at.listening, "watching": at.watching, "competing": at.competing, } if not all([a.lower() in switcher for a in vals["at"]]): raise BadArgument( "Invalid Activity Type. Must be either `unknown`, `playing`, `streaming`, `listening`, `competing` or `watching`." ) new = [switcher[name.lower()] for name in vals["at"]] vals["at"] = new new = [] for perm in vals["perms"]: perm = perm.replace(" ", "_") if not perm.lower() in PERMS: raise BadArgument( f"Invalid permission. Run `{ctx.prefix}target permissions` to see a list of valid permissions." ) new.append(perm) vals["perms"] = new new = [] for perm in vals["any-perm"]: perm = perm.replace(" ", "_") if not perm.lower() in PERMS: raise BadArgument( f"Invalid permission. Run `{ctx.prefix}target permissions` to see a list of valid permissions." ) new.append(perm) vals["any-perm"] = new new = [] for perm in vals["not-perms"]: perm = perm.replace(" ", "_") if not perm.lower() in PERMS: raise BadArgument( f"Invalid permission. Run `{ctx.prefix}target permissions` to see a list of valid permissions." ) new.append(perm) vals["not-perms"] = new new = [] for perm in vals["not-any-perm"]: perm = perm.replace(" ", "_") if not perm.lower() in PERMS: raise BadArgument( f"Invalid permission. Run `{ctx.prefix}target permissions` to see a list of valid permissions." ) new.append(perm) vals["not-any-perm"] = new if vals["format"]: if not vals["format"][0].lower() in ["menu", "csv"]: raise BadArgument( "Invalid format. Must be `csv` for a CSV file or `menu` for in an embed." ) vals["format"] = vals["format"][0].lower() return vals
async def convert(self, ctx: Context, argument: str): if len(argument) > 256: raise BadArgument("Your title must be 256 characters or fewer.") return argument
async def convert(cls, context: Context, argument: str): if argument.isdigit(): raise BadArgument("Event names must contain at least 1 non-numeric value") return cls(argument)
def error(self, message): raise BadArgument("Query not understood") from None
async def convert(cls, ctx: Context, argument: str): parser = NoExitParser(description="Role management syntax help", add_help=False) parser.add_argument("--has-any", nargs="*", dest="hasany", default=[]) parser.add_argument("--has-all", nargs="*", dest="hasall", default=[]) parser.add_argument("--has-none", nargs="*", dest="none", default=[]) parser.add_argument("--has-no-roles", action="store_true", default=False, dest="noroles") parser.add_argument("--has-perms", nargs="*", dest="hasperm", default=[]) parser.add_argument("--any-perm", nargs="*", dest="anyperm", default=[]) parser.add_argument("--not-perm", nargs="*", dest="notperm", default=[]) parser.add_argument("--csv", action="store_true", default=False) parser.add_argument("--has-exactly-nroles", dest="quantity", type=int, default=None) parser.add_argument("--has-more-than-nroles", dest="gt", type=int, default=None) parser.add_argument("--has-less-than-nroles", dest="lt", type=int, default=None) parser.add_argument("--above", dest="above", type=str, default=None) parser.add_argument("--below", dest="below", type=str, default=None) hum_or_bot = parser.add_mutually_exclusive_group() hum_or_bot.add_argument("--humans", action="store_true", default=False, dest="humans") hum_or_bot.add_argument("--bots", action="store_true", default=False, dest="bots") hum_or_bot.add_argument("--everyone", action="store_true", default=False, dest="everyone") try: vals = vars(parser.parse_args(shlex.split(argument))) except Exception: raise BadArgument() if not any(( vals["humans"], vals["everyone"], vals["bots"], vals["hasany"], vals["hasall"], vals["none"], vals["hasperm"], vals["notperm"], vals["anyperm"], vals["noroles"], bool(vals["quantity"] is not None), bool(vals["gt"] is not None), bool(vals["lt"] is not None), vals["above"], vals["below"], )): raise BadArgument( "You need to provide at least 1 search criterion") for attr in ("hasany", "hasall", "none"): vals[attr] = [ await _role_converter_instance.convert(ctx, r) for r in vals[attr] ] for attr in ("below", "above"): if vals[attr] is None: continue vals[attr] = await _role_converter_instance.convert( ctx, vals[attr]) for attr in ("hasperm", "anyperm", "notperm"): vals[attr] = [ i.replace("_", " ").lower().replace(" ", "_").replace("server", "guild") for i in vals[attr] ] if any(perm not in dir(discord.Permissions) for perm in vals[attr]): raise BadArgument("You gave an invalid permission") return cls(ctx=ctx, **vals)
def non_numeric(arg: str) -> str: if arg.isdigit(): raise BadArgument( "Event names must contain at least 1 non-numeric value") return arg
def error(self, message): raise BadArgument()
class RawUserIds(Converter): async def convert(self, ctx, argument): if match := _id_regex.match(argument) or _mention_regex.match(argument): return int(match.group(1)) raise BadArgument("{} doesn't look like a valid user ID.").format(argument)