diff options
-rw-r--r-- | bot/constants.py | 1 | ||||
-rw-r--r-- | bot/exts/moderation/incidents.py | 73 |
2 files changed, 70 insertions, 4 deletions
diff --git a/bot/constants.py b/bot/constants.py index f99913b17..33c911874 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -481,6 +481,7 @@ class Webhooks(metaclass=YAMLGetter): big_brother: int dev_log: int duck_pond: int + incidents: int incidents_archive: int diff --git a/bot/exts/moderation/incidents.py b/bot/exts/moderation/incidents.py index a3d90e3fe..0d63ef34f 100644 --- a/bot/exts/moderation/incidents.py +++ b/bot/exts/moderation/incidents.py @@ -1,16 +1,18 @@ import asyncio import logging +import re import typing as t from datetime import datetime from enum import Enum import discord +from async_rediscache import RedisCache from discord.ext.commands import Cog from bot.bot import Bot from bot.constants import Channels, Colours, Emojis, Guild, Webhooks from bot.utils import scheduling -from bot.utils.messages import sub_clyde +from bot.utils.messages import format_user, sub_clyde log = logging.getLogger(__name__) @@ -22,6 +24,10 @@ CRAWL_LIMIT = 50 # Seconds for `crawl_task` to sleep after adding reactions to a message CRAWL_SLEEP = 2 +DISCORD_MESSAGE_LINK_RE = re.compile( + r"discord(?:[\.,]|dot)com(?:\/|slash)channels(?:\/|slash)[0-9]{18}(?:\/|slash)[0-9]{18}(?:\/|slash)[0-9]{18}" +) + class Signal(Enum): """ @@ -114,9 +120,9 @@ def is_incident(message: discord.Message) -> bool: """True if `message` qualifies as an incident, False otherwise.""" conditions = ( message.channel.id == Channels.incidents, # Message sent in #incidents - not message.author.bot, # Not by a bot - not message.content.startswith("#"), # Doesn't start with a hash - not message.pinned, # And isn't header + not message.author.bot, # Not by a bot + not message.content.startswith("#"), # Doesn't start with a hash + not message.pinned, # And isn't header ) return all(conditions) @@ -131,6 +137,32 @@ def has_signals(message: discord.Message) -> bool: return ALL_SIGNALS.issubset(own_reactions(message)) +async def make_message_link_embed(incident: discord.Message, message_link: str) -> discord.Embed: + """ + Create an embed representation of discord message link contained in the incident report. + + The Embed would contain the following information --> + Author: @Jason Terror ♦ (736234578745884682) + Channel: Special/#bot-commands (814190307980607493) + Content: This is a very important message! + """ + channel_id = int(message_link.split("/")[3]) + msg_id = int(message_link.split("/")[4]) + + channel = incident.guild.get_channel(channel_id) + message = await channel.fetch_message(msg_id) + + text = message.content + channel = message.channel + description = ( + f"**Author:** {format_user(message.author)}\n" + f"**Channel:** {channel.category}/#{channel.name} (`{channel.id}`)\n" + f"**Content:** {text[:2045] + '...' if len(text) > 2048 else text}\n" + "\n" + ) + return discord.Embed(description=description) + + async def add_signals(incident: discord.Message) -> None: """ Add `Signal` member emoji to `incident` as reactions. @@ -186,6 +218,10 @@ class Incidents(Cog): Please refer to function docstrings for implementation details. """ + # This dictionary maps a incident message to the message link embeds(s) sent by it + # RedisCache[discord.Message.id, List[discord.Message.id]] + message_link_embeds_cache = RedisCache() + def __init__(self, bot: Bot) -> None: """Prepare `event_lock` and schedule `crawl_task` on start-up.""" self.bot = bot @@ -340,6 +376,12 @@ class Incidents(Cog): else: log.trace("Deletion was confirmed") + log.trace("Deleting discord links webhook message.") + webhook_msg_id = await self.message_link_embeds_cache.get(incident.id) + webhook = await self.bot.fetch_webhook(Webhooks.incidents) + await webhook.delete_message(webhook_msg_id) + log.trace("Successfully deleted discord links webhook message.") + async def resolve_message(self, message_id: int) -> t.Optional[discord.Message]: """ Get `discord.Message` for `message_id` from cache, or API. @@ -421,6 +463,29 @@ class Incidents(Cog): async def on_message(self, message: discord.Message) -> None: """Pass `message` to `add_signals` if and only if it satisfies `is_incident`.""" if is_incident(message): + message_links = DISCORD_MESSAGE_LINK_RE.findall(message.content) + if message_links: + + embeds = [] + for message_link in message_links: + embeds.append( + await make_message_link_embed(message, message_link) + ) + + try: + webhook = await self.bot.fetch_webhook(Webhooks.incidents) + webhook_msg = await webhook.send( + embeds=embeds, + username=sub_clyde(message.author.name), + avatar_url=message.author.avatar_url, + wait=True + ) + except Exception: + log.exception(f"Failed to send message link embeds {message.id} to #incidents") + else: + log.trace("Message Link Embeds Sent successfully!") + await self.message_link_embeds_cache.set(message.id, webhook_msg.id) + await add_signals(message) |