diff options
| author | 2022-10-23 21:26:38 +0300 | |
|---|---|---|
| committer | 2022-10-23 21:26:38 +0300 | |
| commit | 1d628b533c68f87ede4129516bc7e239459e85f0 (patch) | |
| tree | e2b4540c88d0ab9b326689f8e880b3c6b4a460be | |
| parent | Merge branch 'main' into new-filters (diff) | |
Add rich embed filter
| -rw-r--r-- | bot/exts/filtering/_filter_context.py | 5 | ||||
| -rw-r--r-- | bot/exts/filtering/_filters/unique/rich_embed.py | 43 | ||||
| -rw-r--r-- | bot/exts/filtering/_filters/unique/webhook.py | 23 | ||||
| -rw-r--r-- | bot/exts/filtering/_settings.py | 10 |
4 files changed, 62 insertions, 19 deletions
diff --git a/bot/exts/filtering/_filter_context.py b/bot/exts/filtering/_filter_context.py index da7ba0c77..bcbafe393 100644 --- a/bot/exts/filtering/_filter_context.py +++ b/bot/exts/filtering/_filter_context.py @@ -1,6 +1,6 @@ from __future__ import annotations -from collections.abc import Coroutine +from collections.abc import Callable, Coroutine from dataclasses import dataclass, field, replace from enum import Enum, auto from typing import Optional, Union @@ -35,7 +35,8 @@ class FilterContext: action_descriptions: list = field(default_factory=list) # What actions were taken matches: list = field(default_factory=list) # What exactly was found notification_domain: str = field(default_factory=str) # A domain to send the user for context - additional_actions: list[Coroutine] = field(default_factory=list) # Additional actions to perform + # Additional actions to perform + additional_actions: list[Callable[[FilterContext], Coroutine]] = field(default_factory=list) def replace(self, **changes) -> FilterContext: """Return a new context object assigning new values to the specified fields.""" diff --git a/bot/exts/filtering/_filters/unique/rich_embed.py b/bot/exts/filtering/_filters/unique/rich_embed.py new file mode 100644 index 000000000..75f578d3e --- /dev/null +++ b/bot/exts/filtering/_filters/unique/rich_embed.py @@ -0,0 +1,43 @@ +import re + +from botcore.utils.logging import get_logger + +from bot.exts.filtering._filter_context import Event, FilterContext +from bot.exts.filtering._filters.filter import UniqueFilter +from bot.utils.helpers import remove_subdomain_from_url + +log = get_logger(__name__) + +URL_RE = re.compile(r"(https?://\S+)", flags=re.IGNORECASE) + + +class RichEmbedFilter(UniqueFilter): + """Filter messages which contain rich embeds not auto-generated from a URL.""" + + name = "rich_embed" + events = (Event.MESSAGE, Event.MESSAGE_EDIT) + + def triggered_on(self, ctx: FilterContext) -> bool: + """Determine if `msg` contains any rich embeds not auto-generated from a URL.""" + if ctx.message.embeds: + for embed in ctx.message.embeds: + if embed.type == "rich": + urls = URL_RE.findall(ctx.content) + final_urls = set(urls) + # This is due to the way discord renders relative urls in Embeds + # if the following url is sent: https://mobile.twitter.com/something + # Discord renders it as https://twitter.com/something + for url in urls: + final_urls.add(remove_subdomain_from_url(url)) + if not embed.url or embed.url not in final_urls: + # If `embed.url` does not exist or if `embed.url` is not part of the content + # of the message, it's unlikely to be an auto-generated embed by Discord. + ctx.alert_embeds.extend(ctx.message.embeds) + return True + else: + log.trace( + "Found a rich embed sent by a regular user account, " + "but it was likely just an automatic URL embed." + ) + + return False diff --git a/bot/exts/filtering/_filters/unique/webhook.py b/bot/exts/filtering/_filters/unique/webhook.py index 10f27a922..ee6b7135e 100644 --- a/bot/exts/filtering/_filters/unique/webhook.py +++ b/bot/exts/filtering/_filters/unique/webhook.py @@ -1,4 +1,5 @@ import re +from collections.abc import Callable, Coroutine from botcore.utils.logging import get_logger @@ -41,18 +42,22 @@ class WebhookFilter(UniqueFilter): for i, match in enumerate(matches, start=1): extra = "" if len(matches) == 1 else f" ({i})" # Queue the webhook for deletion. - ctx.additional_actions.append(self._delete_webhook(ctx, match[0], extra)) + ctx.additional_actions.append(self._delete_webhook_wrapper(match[0], extra)) # Don't show the full webhook in places such as the mod alert. ctx.content = ctx.content.replace(match[0], match[1] + "xxx") return True @staticmethod - async def _delete_webhook(ctx: FilterContext, webhook_url: str, extra_message: str) -> None: - """Delete the given webhook and update the filter context.""" - async with bot.instance.http_session.delete(webhook_url) as resp: - # The Discord API Returns a 204 NO CONTENT response on success. - if resp.status == 204: - ctx.action_descriptions.append("webhook deleted" + extra_message) - else: - ctx.action_descriptions.append("failed to delete webhook" + extra_message) + def _delete_webhook_wrapper(webhook_url: str, extra_message: str) -> Callable[[FilterContext], Coroutine]: + """Create the action to perform when a webhook should be deleted.""" + async def _delete_webhook(ctx: FilterContext) -> None: + """Delete the given webhook and update the filter context.""" + async with bot.instance.http_session.delete(webhook_url) as resp: + # The Discord API Returns a 204 NO CONTENT response on success. + if resp.status == 204: + ctx.action_descriptions.append("webhook deleted" + extra_message) + else: + ctx.action_descriptions.append("failed to delete webhook" + extra_message) + + return _delete_webhook diff --git a/bot/exts/filtering/_settings.py b/bot/exts/filtering/_settings.py index d53334c1c..85a6f3d2b 100644 --- a/bot/exts/filtering/_settings.py +++ b/bot/exts/filtering/_settings.py @@ -197,14 +197,8 @@ class ActionSettings(Settings[ActionEntry]): for entry in self.values(): await entry.action(ctx) - _i = len(ctx.additional_actions) - try: - for _i, action in enumerate(ctx.additional_actions): - await action - except Exception: - for action in ctx.additional_actions[_i+1:]: - action.close() - raise + for action in ctx.additional_actions: + await action(ctx) def fallback_to(self, fallback: ActionSettings) -> ActionSettings: """Fill in missing entries from `fallback`.""" |