async def unban(self, ctx, user: UserConv, *, reason: str): """ Unbans the id from the guild with a reason. If guild has moderation logging enabled, it is logged """ try: embed = discord.Embed(description="Done! User Unbanned") embed.add_field(name="Reason", value=reason) mod = user_discrim(ctx.author) unbanned = user_discrim(user) clean_reason = escape_backticks(reason) content = f"{mod} unbanned {user.mention} ({unbanned}) with reason: `{clean_reason}`" await ctx.guild.unban(user, reason=f"{reason} - {mod}") await ctx.send(embed=embed) self.journal.send("member/unban", ctx.guild, content, icon="unban", user=user) except discord.errors.Forbidden: raise ManualCheckFailure( content="I don't have permission to unban this user")
async def cleanup_text(self, ctx, text: str, count: int, channel: discord.TextChannel = None): """ Deletes the last <count> messages with the given text. """ await self.check_count(ctx, count) if channel is None: channel = ctx.channel # Deletes the messages with the text text = normalize_caseless(text) deleted = _Counter() def check(message): if deleted < count: if text in normalize_caseless(message.content): deleted.incr() return True return False messages = await channel.purge(limit=count * 2, check=check, before=ctx.message, bulk=True) # Send journal events text = escape_backticks(text) causer = user_discrim(ctx.author) content = f"{causer} deleted {len(messages)} messages in {channel.mention} matching `{text}`" self.journal.send( "text", ctx.guild, content, icon="delete", count=count, channel=channel, messags=messages, text=text, cause=ctx.author, ) obj, file = self.dump_messages(messages) content = f"Cleanup by {causer} in {channel.mention} of `{text}` deleted these messages:" self.dump.send("text", ctx.guild, content, icon="delete", messages=obj, file=file)
async def get_user(self, ctx, name): if name is None: return ctx.author else: conv = UserConv() try: return await conv.convert(ctx, name) except commands.errors.BadArgument: name = escape_backticks(name) prefix = self.bot.prefix(ctx.guild) embed = discord.Embed(colour=discord.Colour.red()) embed.description = f"No user found for `{name}`. Try `{prefix}ufind`." raise CommandFailed(embed=embed)
async def remind_list(self, ctx): """ Lists all reminders for the current user. """ reminders = self.bot.sql.navi.get_reminders(ctx.author) if reminders: descr = StringBuilder() for reminder in reminders: until = fancy_timedelta(reminder.timestamp) message = escape_backticks(reminder.message) descr.writeln( f"ID: #`{reminder.id:05}`: in `{until}` with message: `{message}`" ) embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name=f"Reminders for {ctx.author.display_name}") embed.description = str(descr) else: embed = discord.Embed(colour=discord.Colour.dark_purple()) embed.description = f"No reminders for {ctx.author.mention}" await ctx.send(embed=embed)
async def softban(self, ctx, user: UserConv, *, reason: str): """ Soft-bans the user from the guild with a reason. If guild has moderation logging enabled, it is logged Soft-ban is a kick that cleans up the chat """ try: embed = discord.Embed(description="Done! User Soft-banned") embed.add_field(name="Reason", value=reason) mod = user_discrim(ctx.author) banned = user_discrim(user) clean_reason = escape_backticks(reason) content = f"{mod} soft-banned {user.mention} ({banned}) with reason: `{clean_reason}`" await ctx.guild.ban(user, reason=f"{reason} - {mod}", delete_message_days=1) await asyncio.sleep(0.1) await ctx.guild.unban(user, reason=f"{reason} - {mod}") await ctx.send(embed=embed) self.journal.send( "member/softban", ctx.guild, content, icon="soft", user=user, reason=reason, cause=ctx.author, ) except discord.errors.Forbidden: raise ManualCheckFailure( content="I don't have permission to soft-ban this user")
async def check_name_filter(cog, name, name_type, member, only_filter=None): """ Checks the given name against all filters, and enforces with a dunce. """ logger.debug("Checking name: %r", name) if only_filter is None: # Check all the filters triggered = None for filter_text, (filter, filter_type) in cog.filters[member.guild].items(): if filter.matches(name): if triggered is None or filter_type.value > triggered.filter_type.value: triggered = FoundNameViolation( filter_type=filter_type, filter_text=filter_text ) else: # Only check this filter filter_type = cog.filters[member.guild][only_filter.text][1] if only_filter.matches(name): triggered = FoundNameViolation( filter_type=filter_type, filter_text=only_filter.text ) else: triggered = None if triggered is None: logger.debug("No name violations found!") return filter_type = triggered.filter_type filter_text = triggered.filter_text escaped_name = escape_backticks(name) escaped_filter_text = escape_backticks(filter_text) logger.info( "Punishing name filter violation (%r, level %s) by '%s' (%d)", filter_text, filter_type.value, member.name, member.id, ) roles = cog.bot.sql.settings.get_special_roles(member.guild) async def message_violator(jailed): response = StringBuilder( f"The {name_type.value} you just set violates a {filter_type.value} text filter " f"disallowing `{escaped_filter_text}`.\n" ) if jailed: if roles.jail is not None: response.writeln( f"In the mean time, you have been assigned the `{roles.jail.name}` role, " "revoking your posting privileges until a moderator clears you." ) else: response.writeln( "Your name has been manually cleared. Please do not set your name to " "anything offensive in the future." ) await member.send(content=str(response)) severity = filter_type.level jail_anyways = False if severity >= FilterType.FLAG.level: logger.info("Notifying staff of filter violation") journal_name_violation( cog.journal, member, name_type, filter_type, escaped_filter_text, escaped_name, ) if severity >= FilterType.BLOCK.level: logger.info("Removing bad %s from member", name_type.value) if name_type == NameType.USER: jail_anyways = True await member.edit( nick=MASK_NICK, reason="Hid username for violating {filter_type.value} level name filter", ) elif name_type == NameType.NICK: await member.edit( nick=None, reason=f"Removed nickname for violating {filter_type.value} level name filter", ) else: raise ValueError(f"Unknown value for NameType: {name_type!r}") if severity >= FilterType.JAIL.level or jail_anyways: if roles.jail is None: logger.info( "Jailing user for inappropriate name, except there is no jail role configured!" ) content = f"Cannot jail {member.mention} for name violation because no jail role is set!" cog.journal.send("name/jail", member.guild, content, icon="warning") else: logger.info("Jailing user for inappropriate name") await asyncio.gather( message_violator(jailed=True), cog.bot.punish.jail( member.guild, member, "Jailed for violating name filter" ), )
async def found_text_violation(triggered, roles): """ Processes a violation of the text filter. This coroutine is responsible for actual enforcement, based on the filter_type. """ bot = triggered.bot journal = triggered.journal message = triggered.message content = triggered.content location_type = triggered.location_type filter_type = triggered.filter_type filter_text = triggered.filter_text logger.info( "Punishing %s filter violation (%r, level %s) by '%s' (%d)", location_type.value, filter_text, filter_type.value, message.author.name, message.author.id, ) severity = filter_type.level # Escape content for display escaped_filter_text = escape_backticks(filter_text) escaped_content = escape_backticks(content) if len(escaped_content) > 1800: escaped_content = escaped_content[:1800] + " ... (message too long)" async def message_violator(): logger.debug("Sending message to user who violated the filter") response = StringBuilder( f"The message you posted in {message.channel.mention} violates a {location_type.value} " f"{filter_type.value} filter disallowing `{escaped_filter_text}`.") if severity >= FilterType.JAIL.level: if roles.jail is not None: response.writeln( "This offense is serious enough to warrant immediate revocation of posting privileges.\n" f"As such, you have been assigned the `{roles.jail.name}` role, until a moderator clears you." ) await message.author.send(content=str(response)) response.clear() if message.content != content: embed_caveat = "(including text from all embeds attached to your message)" else: embed_caveat = "" embed = discord.Embed(description=content) embed.timestamp = discord.utils.snowflake_time(message.id) embed.set_author(name=message.author.display_name, icon_url=message.author.avatar_url) to_send = f"The content of the deleted message {embed_caveat} is:" await message.author.send(content=to_send, embed=embed) response.writeln("or, quoted:") response.writeln("```") response.writeln(escaped_content) response.writeln("```") response.writeln("Contact a moderator if you have questions.") await message.author.send(content=str(response)) if severity >= FilterType.FLAG.level: logger.info("Notifying staff of filter violation") journal_violation(journal, "text", message, filter_type, escaped_filter_text, escaped_content) if severity >= FilterType.BLOCK.level: logger.info("Deleting inappropriate message id %d and notifying user", message.id) await asyncio.gather(message.delete(), message_violator()) if severity >= FilterType.JAIL.level: if roles.jail is None: logger.info( "Jailing user for inappropriate message, except there is no jail role configured!" ) content = f"Cannot jail {message.author.mention} for filter violation because no jail role is set!" journal.send("text/jail", message.guild, content, icon="warning") else: logger.info("Jailing user for inappropriate message") await bot.punish.jail(message.guild, message.author, "Jailed for violating file filter")