aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar mbaruh <[email protected]>2022-11-26 21:27:17 +0200
committerGravatar mbaruh <[email protected]>2022-11-27 19:22:32 +0200
commit2def5b6b4da89ae1579f2f16df2a70a69953bf42 (patch)
tree1e17d5bfe1be0642776c840bd6cd6067c48f40e8
parentPhishing filter add command (diff)
Add alert view
The view provides info about the offending user. This change required changing the cog to generate a fresh webhook on startup so that it's authenticated to the bot. Only webhooks authenticated to the bot can send message components. In the future, this view might be used for more advanced usages such as having a button to pardon the user.
-rw-r--r--bot/constants.py1
-rw-r--r--bot/exts/filtering/_filter_lists/antispam.py14
-rw-r--r--bot/exts/filtering/_ui/ui.py40
-rw-r--r--bot/exts/filtering/filtering.py36
-rw-r--r--config-default.yml1
5 files changed, 74 insertions, 18 deletions
diff --git a/bot/constants.py b/bot/constants.py
index 8a2571a98..1d6ab8e7e 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -455,7 +455,6 @@ class Webhooks(metaclass=YAMLGetter):
duck_pond: int
incidents: int
incidents_archive: int
- filters: int
class Roles(metaclass=YAMLGetter):
diff --git a/bot/exts/filtering/_filter_lists/antispam.py b/bot/exts/filtering/_filter_lists/antispam.py
index 41eda9878..e549404c4 100644
--- a/bot/exts/filtering/_filter_lists/antispam.py
+++ b/bot/exts/filtering/_filter_lists/antispam.py
@@ -10,17 +10,15 @@ from operator import add, or_
import arrow
from botcore.utils import scheduling
from botcore.utils.logging import get_logger
-from discord import HTTPException, Member
+from discord import Member
-import bot
-from bot.constants import Webhooks
from bot.exts.filtering._filter_context import FilterContext
from bot.exts.filtering._filter_lists.filter_list import ListType, SubscribingAtomicList, UniquesListBase
from bot.exts.filtering._filters.antispam import antispam_filter_types
from bot.exts.filtering._filters.filter import Filter, UniqueFilter
from bot.exts.filtering._settings import ActionSettings
from bot.exts.filtering._settings_types.actions.infraction_and_notification import Infraction
-from bot.exts.filtering._ui.ui import build_mod_alert
+from bot.exts.filtering._ui.ui import AlertView, build_mod_alert
if typing.TYPE_CHECKING:
from bot.exts.filtering.filtering import Filtering
@@ -147,9 +145,9 @@ class DeletionContext:
"""Post the mod alert."""
if not self.contexts or not self.rules:
return
- try:
- webhook = await bot.instance.fetch_webhook(Webhooks.filters)
- except HTTPException:
+
+ webhook = antispam_list.filtering_cog.webhook
+ if not webhook:
return
ctx, *other_contexts = self.contexts
@@ -182,4 +180,4 @@ class DeletionContext:
embed.set_footer(
text="The list of actions taken includes actions from additional contexts after deletion began."
)
- await webhook.send(username="Anti-Spam", content=ctx.alert_content, embeds=[embed])
+ await webhook.send(username="Anti-Spam", content=ctx.alert_content, embeds=[embed], view=AlertView(new_ctx))
diff --git a/bot/exts/filtering/_ui/ui.py b/bot/exts/filtering/_ui/ui.py
index ec549725c..e71bab0d1 100644
--- a/bot/exts/filtering/_ui/ui.py
+++ b/bot/exts/filtering/_ui/ui.py
@@ -20,6 +20,7 @@ import bot
from bot.constants import Colours
from bot.exts.filtering._filter_context import FilterContext
from bot.exts.filtering._filter_lists import FilterList
+from bot.exts.filtering._utils import FakeContext
from bot.utils.messages import format_channel, format_user, upload_log
log = get_logger(__name__)
@@ -40,6 +41,8 @@ MAX_MODAL_TITLE_LENGTH = 45
# Max number of items in a select
MAX_SELECT_ITEMS = 25
MAX_EMBED_DESCRIPTION = 4080
+# Number of seconds before timeout of the alert view
+ALERT_VIEW_TIMEOUT = 3600
SETTINGS_DELIMITER = re.compile(r"\s+(?=\S+=\S+)")
SINGLE_SETTING_PATTERN = re.compile(r"[\w/]+=.+")
@@ -502,3 +505,40 @@ class DeleteConfirmationView(discord.ui.View):
async def cancel(self, interaction: Interaction, button: discord.ui.Button) -> None:
"""Cancel the filter list deletion."""
await interaction.response.edit_message(content="🚫 Operation canceled.", view=None)
+
+
+class AlertView(discord.ui.View):
+ """A view providing info about the offending user."""
+
+ def __init__(self, ctx: FilterContext):
+ super().__init__(timeout=ALERT_VIEW_TIMEOUT)
+ self.ctx = ctx
+
+ @discord.ui.button(label="ID")
+ async def user_id(self, interaction: Interaction, button: discord.ui.Button) -> None:
+ """Reply with the ID of the offending user."""
+ await interaction.response.send_message(self.ctx.author.id, ephemeral=True)
+
+ @discord.ui.button(emoji="👤")
+ async def user_info(self, interaction: Interaction, button: discord.ui.Button) -> None:
+ """Send the info embed of the offending user."""
+ command = bot.instance.get_command("user")
+ if not command:
+ await interaction.response.send_message("The command `user` is not loaded.", ephemeral=True)
+ return
+
+ await interaction.response.defer()
+ fake_ctx = FakeContext(interaction.channel, command, author=interaction.user)
+ await command(fake_ctx, self.ctx.author)
+
+ @discord.ui.button(emoji="âš ")
+ async def user_infractions(self, interaction: Interaction, button: discord.ui.Button) -> None:
+ """Send the infractions embed of the offending user."""
+ command = bot.instance.get_command("infraction search")
+ if not command:
+ await interaction.response.send_message("The command `infraction search` is not loaded.", ephemeral=True)
+ return
+
+ await interaction.response.defer()
+ fake_ctx = FakeContext(interaction.channel, command, author=interaction.user)
+ await command(fake_ctx, self.ctx.author)
diff --git a/bot/exts/filtering/filtering.py b/bot/exts/filtering/filtering.py
index 673b5487c..2e433aff6 100644
--- a/bot/exts/filtering/filtering.py
+++ b/bot/exts/filtering/filtering.py
@@ -23,7 +23,8 @@ import bot
import bot.exts.filtering._ui.filter as filters_ui
from bot import constants
from bot.bot import Bot
-from bot.constants import Channels, MODERATION_ROLES, Roles, Webhooks
+from bot.constants import Channels, Guild, MODERATION_ROLES, Roles
+from bot.exts.backend.branding._repository import HEADERS, PARAMS
from bot.exts.filtering._filter_context import Event, FilterContext
from bot.exts.filtering._filter_lists import FilterList, ListType, filter_list_types, list_type_converter
from bot.exts.filtering._filter_lists.filter_list import AtomicList
@@ -36,7 +37,7 @@ from bot.exts.filtering._ui.filter import (
from bot.exts.filtering._ui.filter_list import FilterListAddView, FilterListEditView, settings_converter
from bot.exts.filtering._ui.search import SearchEditView, search_criteria_converter
from bot.exts.filtering._ui.ui import (
- ArgumentCompletionView, DeleteConfirmationView, build_mod_alert, format_response_error
+ AlertView, ArgumentCompletionView, DeleteConfirmationView, build_mod_alert, format_response_error
)
from bot.exts.filtering._utils import past_tense, repr_equals, starting_value, to_serializable
from bot.exts.moderation.infraction.infractions import COMP_BAN_DURATION, COMP_BAN_REASON
@@ -48,6 +49,7 @@ from bot.utils.message_cache import MessageCache
log = get_logger(__name__)
+WEBHOOK_ICON_URL = r"https://github.com/python-discord/branding/raw/main/icons/filter/filter_pfp.png"
CACHE_SIZE = 100
HOURS_BETWEEN_NICKNAME_ALERTS = 1
OFFENSIVE_MSG_DELETE_TIME = datetime.timedelta(days=7)
@@ -70,7 +72,7 @@ class Filtering(Cog):
self.filter_lists: dict[str, FilterList] = {}
self._subscriptions: defaultdict[Event, list[FilterList]] = defaultdict(list)
self.delete_scheduler = scheduling.Scheduler(self.__class__.__name__)
- self.webhook = None
+ self.webhook: discord.Webhook = None
self.loaded_settings = {}
self.loaded_filters = {}
@@ -93,10 +95,8 @@ class Filtering(Cog):
if not example_list and loaded_list:
example_list = loaded_list
- try:
- self.webhook = await self.bot.fetch_webhook(Webhooks.filters)
- except HTTPException:
- log.error(f"Failed to fetch filters webhook with ID `{Webhooks.filters}`.")
+ # The webhook must be generated by the bot to send messages with components through it.
+ self.webhook = await self._generate_webhook()
self.collect_loaded_types(example_list)
await self.schedule_offending_messages_deletion()
@@ -861,6 +861,24 @@ class Filtering(Cog):
self.filter_lists[list_name] = filter_list_types[list_name](self)
return self.filter_lists[list_name].add_list(list_data)
+ async def _generate_webhook(self) -> discord.Webhook | None:
+ """Generate a webhook with the filtering avatar."""
+ # Download the filtering avatar from the branding repository.
+ webhook_icon = None
+ async with self.bot.http_session.get(WEBHOOK_ICON_URL, params=PARAMS, headers=HEADERS) as response:
+ if response.status == 200:
+ log.debug("Successfully fetched filtering webhook icon, reading payload.")
+ webhook_icon = await response.read()
+ else:
+ log.warning(f"Failed to fetch filtering webhook icon due to status: {response.status}")
+
+ alerts_channel = self.bot.get_guild(Guild.id).get_channel(Channels.mod_alerts)
+ try:
+ return await alerts_channel.create_webhook(name="Filtering System", avatar=webhook_icon)
+ except HTTPException:
+ log.error("Failed to create filters webhook.")
+ return None
+
async def _resolve_action(
self, ctx: FilterContext
) -> tuple[ActionSettings | None, dict[FilterList, list[str]], dict[AtomicList, list[Filter]]]:
@@ -895,7 +913,9 @@ class Filtering(Cog):
name = f"{ctx.event.name.replace('_', ' ').title()} Filter"
embed = await build_mod_alert(ctx, triggered_filters)
# There shouldn't be more than 10, but if there are it's not very useful to send them all.
- await self.webhook.send(username=name, content=ctx.alert_content, embeds=[embed, *ctx.alert_embeds][:10])
+ await self.webhook.send(
+ username=name, content=ctx.alert_content, embeds=[embed, *ctx.alert_embeds][:10], view=AlertView(ctx)
+ )
async def _recently_alerted_name(self, member: discord.Member) -> bool:
"""When it hasn't been `HOURS_BETWEEN_NICKNAME_ALERTS` since last alert, return False, otherwise True."""
diff --git a/config-default.yml b/config-default.yml
index 4407177d9..f0e217d6c 100644
--- a/config-default.yml
+++ b/config-default.yml
@@ -324,7 +324,6 @@ guild:
incidents: 816650601844572212
incidents_archive: 720671599790915702
python_news: &PYNEWS_WEBHOOK 704381182279942324
- filters: 926442964463521843
keys: