181 lines
8.6 KiB
Python
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()
|