# _____ _ # / ____| (_) # | (___ ___ __ _ _____ ___ _ __ ___ _ __ ___ ___ _ __ # \___ \ / _ \/ _` / __\ \ /\ / / | '_ ` _ \| '_ ` _ \ / _ \ '__| # ____) | __/ (_| \__ \\ V V /| | | | | | | | | | | | __/ | # |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_| import discord from red_commons.logging import getLogger from redbot.core import commands from redbot.core.bot import Config, Red from redbot.core.utils.chat_formatting import humanize_list class AntiPolls(commands.Cog): """AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages).""" __author__ = ["SeaswimmerTheFsh"] __version__ = "1.0.0" __documentation__ = "https://seacogs.coastalcommits.com/antipolls/" def __init__(self, bot: Red): super().__init__() self.bot = bot self.logger = getLogger("red.SeaCogs.AntiPolls") self.config = Config.get_conf(self, identifier=23517395243, force_registration=True) self.config.register_guild( role_whitelist=[], channel_whitelist=[], manage_messages=True, ) def format_help_for_context(self, ctx: commands.Context) -> str: pre_processed = super().format_help_for_context(ctx) or "" n = "\n" if "\n\n" not in pre_processed else "" text = [ f"{pre_processed}{n}", f"Cog Version: **{self.__version__}**", f"Author: {humanize_list(self.__author__)}", f"Documentation: {self.__documentation__}", ] return "\n".join(text) async def red_delete_data_for_user(self, **kwargs): """Nothing to delete.""" return @commands.Cog.listener('on_message') async def listener(self, message: discord.Message) -> None: if message.guild is None: return self.logger.verbose("Message in direct messages ignored") if message.author.bot: return self.logger.verbose("Message from bot ignored") if await self.bot.cog_disabled_in_guild(self, message.guild): return self.logger.verbose("Cog disabled in guild") guild_config = await self.config.guild(message.guild).all() if guild_config['manage_messages'] is True and message.author.guild_permissions.manage_messages: return self.logger.verbose("Message from user with Manage Messages permission ignored") if message.channel.id in guild_config['channel_whitelist']: return self.logger.verbose(f"Message in whitelisted channel {message.channel.id} ignored") if any(role.id in guild_config['role_whitelist'] for role in message.author.roles): return self.logger.verbose(f"Message from whitelisted role {message.author.roles} ignored") if not message.content and not message.embeds and not message.attachments and not message.stickers: self.logger.trace(f"Message {message.id} is a poll, attempting to delete") try: await message.delete() except discord.HTTPException as e: return self.logger.error(f"Failed to delete message: {e}") return self.logger.trace(f"Deleted poll message {message.id}") self.logger.verbose(f"Message {message.id} is not a poll, ignoring") @commands.group(name="antipolls", aliases=["ap"]) @commands.guild_only() @commands.admin_or_permissions(manage_guild=True) async def antipolls(self, ctx: commands.Context) -> None: """Manage AntiPolls settings.""" @antipolls.group(name="roles") async def antipolls_roles(self, ctx: commands.Context) -> None: """Manage role whitelist.""" @antipolls_roles.command(name="add") async def antipolls_roles_add(self, ctx: commands.Context, *roles: discord.Role) -> None: """Add roles to the whitelist.""" async with self.config.guild(ctx.guild).role_whitelist() as role_whitelist: role_whitelist: list failed: list[discord.Role] = [] for role in roles: if role.id in role_whitelist: failed.append(role) continue role_whitelist.append(role.id) await ctx.tick() if failed: await ctx.send(f"The following roles were already in the whitelist: {humanize_list(role.mention for role in failed)}", delete_after=10, allowed_mentions=discord.AllowedMentions.none) @antipolls_roles.command(name="remove") async def antipolls_roles_remove(self, ctx: commands.Context, *roles: discord.Role) -> None: """Remove roles from the whitelist.""" async with self.config.guild(ctx.guild).role_whitelist() as role_whitelist: role_whitelist: list failed: list[discord.Role] = [] for role in roles: if role.id not in role_whitelist: failed.append(role) continue role_whitelist.remove(role.id) await ctx.tick() if failed: await ctx.send(f"The following roles were not in the whitelist: {humanize_list(role.mention for role in failed)}", delete_after=10, allowed_mentions=discord.AllowedMentions.none) @antipolls_roles.command(name="list") async def antipolls_roles_list(self, ctx: commands.Context) -> None: """List roles in the whitelist.""" role_whitelist = await self.config.guild(ctx.guild).role_whitelist() if not role_whitelist: return await ctx.send("No roles in the whitelist.") roles = [ctx.guild.get_role(role) for role in role_whitelist] await ctx.send(humanize_list(role.mention for role in roles)) @antipolls.group(name="channels") async def antipolls_channels(self, ctx: commands.Context) -> None: """Manage channel whitelist.""" @antipolls_channels.command(name="add") async def antipolls_channels_add(self, ctx: commands.Context, *channels: discord.TextChannel) -> None: """Add channels to the whitelist.""" async with self.config.guild(ctx.guild).channel_whitelist() as channel_whitelist: channel_whitelist: list failed: list[discord.TextChannel] = [] for channel in channels: if channel.id in channel_whitelist: failed.append(channel) continue channel_whitelist.append(channel.id) await ctx.tick() if failed: await ctx.send(f"The following channels were already in the whitelist: {humanize_list(channel.mention for channel in failed)}", delete_after=10, allowed_mentions=discord.AllowedMentions.none) @antipolls_channels.command(name="remove") async def antipolls_channels_remove(self, ctx: commands.Context, *channels: discord.TextChannel) -> None: """Remove channels from the whitelist.""" async with self.config.guild(ctx.guild).channel_whitelist() as channel_whitelist: channel_whitelist: list failed: list[discord.TextChannel] = [] for channel in channels: if channel.id not in channel_whitelist: failed.append(channel) continue channel_whitelist.remove(channel.id) await ctx.tick() if failed: await ctx.send(f"The following channels were not in the whitelist: {humanize_list(channel.mention for channel in failed)}", delete_after=10, allowed_mentions=discord.AllowedMentions.none) @antipolls_channels.command(name="list") async def antipolls_channels_list(self, ctx: commands.Context) -> None: """List channels in the whitelist.""" channel_whitelist = await self.config.guild(ctx.guild).channel_whitelist() if not channel_whitelist: return await ctx.send("No channels in the whitelist.") channels = [ctx.guild.get_channel(channel) for channel in channel_whitelist] await ctx.send(humanize_list(channel.mention for channel in channels)) @antipolls.command(name="managemessages") async def antipolls_managemessages(self, ctx: commands.Context, enabled: bool) -> None: """Toggle Manage Messages permission check.""" await self.config.guild(ctx.guild).manage_messages.set(enabled) await ctx.tick()