SeaCogs/antipolls/antipolls.py

181 lines
8.6 KiB
Python

# _____ _
# / ____| (_)
# | (___ ___ __ _ _____ ___ _ __ ___ _ __ ___ ___ _ __
# \___ \ / _ \/ _` / __\ \ /\ / / | '_ ` _ \| '_ ` _ \ / _ \ '__|
# ____) | __/ (_| \__ \\ 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 bold, 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__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"]
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs"
__version__ = "1.0.1"
__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,
)
if not self.bot.intents.message_content:
self.logger.error("Message Content intent is not enabled, cog will not load.")
raise RuntimeError("This cog requires the Message Content intent to function. To prevent potentially destructive behavior, the cog will not load without the intent enabled.")
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"{bold('Cog Version:')} [{self.__version__}]({self.__git__})",
f"{bold('Author:')} {humanize_list(self.__author__)}",
f"{bold('Documentation:')} {self.__documentation__}",
]
return "\n".join(text)
async def red_delete_data_for_user(self, **kwargs): # pylint: disable=unused-argument
"""Nothing to delete."""
return
@commands.Cog.listener('on_message')
async def polls_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("Message ignored, cog is disabled in guild %s", message.guild.id)
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("Message in whitelisted channel %s ignored", message.channel.id)
if any(role.id in guild_config['role_whitelist'] for role in message.author.roles):
return self.logger.verbose("Message from whitelisted role %s ignored", message.author.roles)
if not message.content and not message.embeds and not message.attachments and not message.stickers:
self.logger.trace("Message %s is a poll, attempting to delete", message.id)
try:
await message.delete()
except discord.HTTPException as e:
return self.logger.error("Failed to delete message: %s", e)
return self.logger.trace("Deleted poll message %s", message.id)
self.logger.verbose("Message %s is not a poll, ignoring", message.id)
@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)
@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)
@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)
@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)
@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()