async def convert(self, ctx: commands.Context, argument: str) -> discord.Role: try: basic_role = await super().convert(ctx, argument) except commands.BadArgument: pass else: return basic_role guild = ctx.guild result = [(r[2], r[1]) for r in process.extract( argument, {r: unidecode(r.name) for r in guild.roles}, limit=None, score_cutoff=75, )] if not result: raise commands.BadArgument( f'Role "{argument}" not found.' if self.response else None) sorted_result = sorted(result, key=lambda r: r[1], reverse=True) return sorted_result[0][0]
async def search(cls, ctx, argument, globally, _): is_owner = await ctx.bot.is_owner(ctx.author) config = ctx.cog.config guilds = ctx.bot.guilds.copy() if is_owner or globally else [ctx.guild] result = set() for guild in guilds: if not await cls.guild_filter(ctx, is_owner, guild): continue for channel in guild.text_channels.copy(): if cls.channel_filter(ctx, is_owner, channel, argument): if not await config.channel(channel).blacklisted(): result.add(channel) for member in guild.members.copy(): if member not in result and cls.user_filter( ctx, is_owner, member, argument): if not await config.user(member).blacklisted(): result.add(member) if not result: raise commands.BadArgument( _("Destination {!r} not found. Either I don't have access or it has been blacklisted." ).format(argument)) return list(result)
async def convert(cls, ctx: commands.Context, arg: str): """Find best match for member from recent messages.""" if not ctx.guild: return # Handle special 'me' user: if arg.lower() == "me": return cls(ctx.author) # Prefer exact match: try: match = await commands.MemberConverter().convert(ctx, arg) return cls(match) except commands.BadArgument: match = None pat = re.escape(arg) # Try partial match on name or nick from recent messages for this guild. cached_members = { str(msg.author.name): msg.author for msg in ctx.bot.cached_messages if not msg.author.bot and ctx.guild == msg.guild and ctx.guild.get_member(msg.author.id) } matches = [ cached_members[name] for name in cached_members if re.match(pat, name, re.I) or ( cached_members[name].nick and re.match(pat, cached_members[name].nick, re.I)) ] # First match is considered the best match (i.e. more recently active) match = ctx.guild.get_member(matches[0].id) if matches else None if match: return cls(match) # Otherwise no partial match from context, & no exact match raise commands.BadArgument( "No recently active member found. Try exact username or nickname.")
async def blocklist_channel( self, ctx: commands.Context, *, channel: discord.TextChannel = None ): """ Blocklists the current channel or the specified channel. Can also blocklist DM channels. """ if channel and not ctx.guild: raise commands.BadArgument(_("You cannot blocklist a channel in DMs.")) if not ctx.guild: channel = ctx.author group = self.config.user(channel) else: channel = channel or ctx.channel group = self.config.channel(channel) blacklisted = not await group.blacklisted() await group.blacklisted.set(blacklisted) await ctx.send( _("Channel is {} blocklisted.").format("now" if blacklisted else "no longer") ) if blacklisted: await self.close_rifts(ctx.author, channel)
async def convert(self, ctx: commands.Context, argument: str): guild_raw = argument target_guild = None 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.BadArgument(f"Invalid Guild: {argument}") return target_guild
def is_all(argument): if argument.lower() == "all": return True raise commands.BadArgument()
async def convert(cls, _ctx: commands.Context, arg: str) -> DomainsMode: try: return DomainsMode(int(arg)) except ValueError: raise commands.BadArgument( f"{inline(arg)} is not a valid domains list mode.")
async def convert(self, ctx: GuildContext, argument: str) -> str: if (match := URL_RE.search(argument)) is not None: raise commands.BadArgument( f"It looks like you're trying to add a full URL (<{argument}>)" f" rather than just the domain name ({match.group(2)}).\n" "Please try again with just the domain name.")
def nonnegative_int(argument): i = int(argument) if i < 0: raise commands.BadArgument("Argument must not be negative.") return i
async def convert(self, ctx: commands.Context, argument: str) -> str: try: await ctx.cog.validate_tagscript(ctx, argument) except SlashTagException as e: raise commands.BadArgument(str(e)) return argument
async def convert( self, ctx: commands.Context, argument: str ) -> Tuple[Optional[str], discord.User, Optional[discord.Guild], bool]: 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("—", "--") command, *arguments = argument.split(" -- ") if arguments: argument = " -- ".join(arguments) else: command = "" 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: server_error = "" target_guild = None guild_raw = " ".join(guild).strip() 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: 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"]): 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: user_error = "" target_user = None user_raw = " ".join(author).strip() 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: Optional[str] = target_scope or None 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
async def convert(cls, ctx: commands.Context, arg: str) -> discord.Role: role: discord.Role = await _role_converter.convert(ctx, arg) if role.is_default(): raise commands.BadArgument( "You can't set this for the everyone role") return role
def check_data_type(self, ctx: commands.Context, data, *, data_type=dict): if not isinstance(data, data_type): raise commands.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 CogOrCOmmand(NamedTuple): stype: str obj: str @classmethod async def convert(cls, ctx: commands.Context, arg: str): # mypy doesn't do type narrowing for the walrus yet if com := ctx.bot.get_command(arg): assert com, "mypy" # nosec return cls("command", com.qualified_name) if cog := ctx.bot.get_cog(arg): assert cog, "mypy" # nosec return cls("cog", cog.__class__.__name__) raise commands.BadArgument( 'Cog or Command "{arg}" not found.'.format(arg=arg)) class TrinaryBool(NamedTuple): state: Union[bool, None] @classmethod async def convert(cls, ctx: commands.Context, arg: str): try: ret = {"allow": True, "deny": False, "clear": None}[arg.lower()] except KeyError: raise commands.BadArgument( "Was expecting one of `allow`, `deny`, or `clear`, got {arg}". format(arg=arg)) else: return cls(ret)
async def convert(cls, ctx: commands.Context, arg: str): ret = ctx.bot.get_command(arg) if ret: return cls(ret) raise commands.BadArgument( 'Command "{arg}" not found.'.format(arg=arg))
async def user(self, ctx: commands.Context, user: str, number: positive_int, delete_pinned: bool = False): """Delete the last X messages from a specified user. Examples: - `[p]cleanup user @Twentysix 2` - `[p]cleanup user Red 6` **Arguments:** - `<user>` The user whose messages are to be cleaned up. - `<number>` The max number of messages to cleanup. Must be a positive integer. - `<delete_pinned>` Whether to delete pinned messages or not. Defaults to False """ channel = ctx.channel member = None try: member = await commands.MemberConverter().convert(ctx, user) except commands.BadArgument: try: _id = int(user) except ValueError: raise commands.BadArgument() else: _id = member.id author = ctx.author if number > 100: cont = await self.check_100_plus(ctx, number) if not cont: return def check(m): if m.author.id == _id: return True else: return False to_delete = await self.get_messages_for_deletion( channel=channel, number=number, check=check, before=ctx.message, delete_pinned=delete_pinned, ) to_delete.append(ctx.message) reason = ("{}({}) deleted {} messages " " made by {}({}) in channel {}." "".format( author.name, author.id, humanize_number(len(to_delete), override_locale="en_US"), member or "???", _id, channel.name, )) log.info(reason) await mass_purge(to_delete, channel)
async def convert(self, ctx, argument): if argument.title() not in all_timezones: raise commands.BadArgument( "Couldn't find that timezone. Look for it in " "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") return argument
async def convert(self, ctx: commands.Context, argument: str) -> str: if len(argument) > MAX_PREFIX_LENGTH and not await ctx.bot.is_owner( ctx.author): raise commands.BadArgument( f"Prefixes cannot be above {MAX_PREFIX_LENGTH} in length.") return argument
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 commands.BadArgument( "The `content` field is not supported for this command.") return content
async def convert(cls, ctx, argument: str) -> "Options": argument = argument.upper().rstrip("S") try: return cls[argument] except KeyError as ke: raise commands.BadArgument() from ke
class StringToEmbed(commands.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 commands.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 commands.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) except Exception as error: await self.embed_convert_error(ctx, "Embed Parse Error", error) # Embed.__len__ may error which is why it is included in the try/except if length > 6000: raise commands.BadArgument( f"Embed size exceeds Discord limit of 6000 characters ({length})." ) return {"embed": e, "content": content}
async def convert(self, ctx, value): if value.lower() == "all": return "all" raise commands.BadArgument('Input was not "all".')
async def convert(self, ctx: commands.Context, argument: str) -> SlashTag: tag = self.get_tag(ctx, argument) if not tag: raise commands.BadArgument( f'Slash tag "{escape_mentions(argument)}" not found.') return tag
async def convert(cls, ctx, arg): if arg[:3].lower() == "moo": return cls() raise commands.BadArgument()
def error(self, message): raise commands.BadArgument()
def __getattr__(self, arg): raise commands.BadArgument(arg)
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: Optional[str] = None target_user: Optional[Union[discord.Member, discord.User]] = None target_guild: Optional[discord.Guild] = None specified_target_user = False 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("—", "--") command, *arguments = argument.split(" -- ") if arguments: argument = " -- ".join(arguments) else: command = "" 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_server_error = "" target_guild = None to_guild_raw = " ".join(to_guild).strip() 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=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"])): 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_server_error = "" source_guild = None from_guild_raw = " ".join(from_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=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"])): 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_error = "" target_user = None to_user_raw = " ".join(to_author).strip() 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)) from_author = (vals.get("from_author", None) or vals.get("from_user", None) or vals.get("from_member", None)) if from_author: source_user_error = "" source_user = None from_user_raw = " ".join(from_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=f"{source_user_error}{_(_USER_HELP)}", ) 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 = target_scope or PlaylistScope.GUILD.value target_user = target_user or ctx.author target_guild = target_guild or ctx.guild source_scope = source_scope or PlaylistScope.GUILD.value source_user = source_user or ctx.author source_guild = source_guild or ctx.guild return ( source_scope, source_user, source_guild, specified_source_user, target_scope, target_user, target_guild, specified_target_user, )
obj: Union[commands.Command, commands.Cog] # noinspection PyArgumentList @classmethod async def convert(cls, ctx: commands.Context, arg: str) -> "CogOrCommand": ret = None if cog := ctx.bot.get_cog(arg): ret = cls(type="COG", name=cog.qualified_name, obj=cog) elif cmd := ctx.bot.get_command(arg): ret = cls(type="COMMAND", name=cmd.qualified_name, obj=cmd) if ret: if isinstance(ret.obj, commands.commands._RuleDropper): raise commands.BadArgument( "You cannot apply permission rules to this cog or command." ) return ret 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(
async def rift_search(self, ctx: commands.Context, *, scope: str = "channel"): """ Provides info about rifts opened in the specified scope. """ author = Limited(message=ctx.message) if ctx.guild else ctx.author try: scoped = { "user": author, "member": author, "author": author, "channel": ctx.channel if ctx.guild else ctx.author, "guild": ctx.guild, "server": ctx.guild, "global": None, }[scope.casefold()] except KeyError: raise commands.BadArgument( _("Invalid scope. Scope must be author, channel, guild, server, or global.") ) if not scoped and not await ctx.bot.is_owner(ctx.author): raise commands.CheckFailure() if scoped == ctx.guild and not await mod.is_admin_or_superior(ctx.bot, ctx.author): raise commands.CheckFailure() def check(vector): if not scoped: return True if scoped in vector: return True if scoped in map(lambda c: getattr(c, "guild", None), vector): return True return False unique_rifts: Set[Vector[Messageable]] = set() for source, destination in self.rifts.vectors(): if check((source, destination)) and (destination, source) not in unique_rifts: unique_rifts.add((source, destination)) total_rifts = len(unique_rifts) if not total_rifts: return await ctx.send(_("No rifts are connected to this scope.")) pages: List[discord.Embed] = [] for i, (source, destination) in enumerate(unique_rifts, 1): if source in self.rifts.get(destination, ()): delim = "⟷" else: delim = "⟶" embed = discord.Embed( title=f"{source} {delim} {destination}", color=await ctx.embed_color() ) if topic := getattr(destination, "topic", None): embed.description = topic try: members = destination.users except AttributeError: members = destination.members # TODO: format and sort members member_str = humanize_list(list(map(str, members))) short_member_str = next(pagify(member_str, delims=[","])) if len(member_str) != len(short_member_str): short_member_str += " …" embed.add_field(name=f"Connected from {destination}", value=member_str) embed.set_footer(text=f"Rift {i} of {total_rifts}") pages.append(embed)
async def user(self, ctx: commands.Context, user: str, number: int, delete_pinned: bool = False): """Deletes last X messages from specified user. Examples: cleanup user @\u200bTwentysix 2 cleanup user Red 6""" channel = ctx.channel if not channel.permissions_for(ctx.guild.me).manage_messages: await ctx.send("I need the Manage Messages permission to do this.") return member = None try: member = await commands.converter.MemberConverter().convert( ctx, user) except commands.BadArgument: try: _id = int(user) except ValueError: raise commands.BadArgument() else: _id = member.id author = ctx.author is_bot = self.bot.user.bot if number > 100: cont = await self.check_100_plus(ctx, number) if not cont: return def check(m): if m.author.id == _id: return True elif m == ctx.message: return True else: return False to_delete = await self.get_messages_for_deletion( ctx, channel, number, check=check, limit=1000, before=ctx.message, delete_pinned=delete_pinned, ) reason = ("{}({}) deleted {} messages " " made by {}({}) in channel {}." "".format(author.name, author.id, len(to_delete), member or "???", _id, channel.name)) log.info(reason) if is_bot: # For whatever reason the purge endpoint requires manage_messages await mass_purge(to_delete, channel) else: await slow_deletion(to_delete)