aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/exts/filtering/_filter_lists/filter_list.py8
-rw-r--r--bot/exts/filtering/_ui/__init__.py0
-rw-r--r--bot/exts/filtering/_ui/filter.py (renamed from bot/exts/filtering/_ui.py)0
-rw-r--r--bot/exts/filtering/_ui/filter_list.py31
-rw-r--r--bot/exts/filtering/filtering.py48
5 files changed, 84 insertions, 3 deletions
diff --git a/bot/exts/filtering/_filter_lists/filter_list.py b/bot/exts/filtering/_filter_lists/filter_list.py
index a62013192..a4f22aed4 100644
--- a/bot/exts/filtering/_filter_lists/filter_list.py
+++ b/bot/exts/filtering/_filter_lists/filter_list.py
@@ -61,6 +61,14 @@ class FilterList(FieldRequiring):
for filter_data in list_data["filters"]:
self.add_filter(filter_data, list_type)
+ def remove_list(self, list_type: ListType) -> None:
+ """Remove the list associated with the given type from the FilterList object."""
+ if list_type not in self.filter_lists:
+ return
+ self.filter_lists.pop(list_type)
+ self.defaults.pop(list_type)
+ self.list_ids.pop(list_type)
+
def add_filter(self, filter_data: dict, list_type: ListType) -> Filter:
"""Add a filter to the list of the specified type."""
try:
diff --git a/bot/exts/filtering/_ui/__init__.py b/bot/exts/filtering/_ui/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/bot/exts/filtering/_ui/__init__.py
diff --git a/bot/exts/filtering/_ui.py b/bot/exts/filtering/_ui/filter.py
index a6bc1addd..a6bc1addd 100644
--- a/bot/exts/filtering/_ui.py
+++ b/bot/exts/filtering/_ui/filter.py
diff --git a/bot/exts/filtering/_ui/filter_list.py b/bot/exts/filtering/_ui/filter_list.py
new file mode 100644
index 000000000..26852f13b
--- /dev/null
+++ b/bot/exts/filtering/_ui/filter_list.py
@@ -0,0 +1,31 @@
+from typing import Callable
+
+import discord
+from discord import Interaction, Member, User
+
+# Amount of seconds to confirm the operation.
+DELETION_TIMEOUT = 60
+
+
+class DeleteConfirmationView(discord.ui.View):
+ """A view to confirm the deletion of a filter list."""
+
+ def __init__(self, author: Member | User, callback: Callable):
+ super().__init__(timeout=DELETION_TIMEOUT)
+ self.author = author
+ self.callback = callback
+
+ async def interaction_check(self, interaction: Interaction) -> bool:
+ """Only allow interactions from the command invoker."""
+ return interaction.user.id == self.author.id
+
+ @discord.ui.button(label="Delete", style=discord.ButtonStyle.red, row=0)
+ async def confirm(self, interaction: Interaction, button: discord.ui.Button) -> None:
+ """Invoke the filter list deletion."""
+ await interaction.response.edit_message(view=None)
+ await self.callback()
+
+ @discord.ui.button(label="Cancel", row=0)
+ 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)
diff --git a/bot/exts/filtering/filtering.py b/bot/exts/filtering/filtering.py
index 427735add..2b37f1ee5 100644
--- a/bot/exts/filtering/filtering.py
+++ b/bot/exts/filtering/filtering.py
@@ -3,25 +3,28 @@ import operator
import re
from collections import defaultdict
from functools import partial, reduce
+from io import BytesIO
from typing import Literal, Optional, get_type_hints
+import discord
from discord import Colour, Embed, HTTPException, Message
from discord.ext import commands
from discord.ext.commands import BadArgument, Cog, Context, has_any_role
from discord.utils import escape_markdown
import bot
-import bot.exts.filtering._ui as filters_ui
+import bot.exts.filtering._ui.filter as filters_ui
from bot.bot import Bot
from bot.constants import Colours, MODERATION_ROLES, Webhooks
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._filters.filter import Filter
from bot.exts.filtering._settings import ActionSettings
-from bot.exts.filtering._ui import (
+from bot.exts.filtering._ui.filter import (
ArgumentCompletionView, build_filter_repr_dict, description_and_settings_converter, filter_overrides,
populate_embed_from_dict
)
+from bot.exts.filtering._ui.filter_list import DeleteConfirmationView
from bot.exts.filtering._utils import past_tense, to_serializable
from bot.log import get_logger
from bot.pagination import LinePaginator
@@ -92,6 +95,15 @@ class Filtering(Cog):
if filter_list not in self._subscriptions[event]:
self._subscriptions[event].append(filter_list)
+ def unsubscribe(self, filter_list: FilterList, *events: Event) -> None:
+ """Unsubscribe a filter list from the given events. If no events given, unsubscribe from every event."""
+ if not events:
+ events = list(self._subscriptions)
+
+ for event in events:
+ if filter_list in self._subscriptions.get(event, []):
+ self._subscriptions[event].remove(filter_list)
+
def collect_loaded_types(self) -> None:
"""
Go over the classes used in initialization and collect them to dictionaries.
@@ -483,7 +495,7 @@ class Filtering(Cog):
@filterlist.command(name="describe", aliases=("explain", "manual", "id"))
async def fl_describe(
- self, ctx: Context, list_type: Optional[list_type_converter] = None, list_name: Optional[str] = None
+ self, ctx: Context, list_type: Optional[list_type_converter] = None, list_name: Optional[str] = None
) -> None:
"""Show a description of the specified filter list, or a list of possible values if no values are provided."""
if not list_type and not list_name:
@@ -512,6 +524,36 @@ class Filtering(Cog):
)
await ctx.send(embed=embed)
+ @filterlist.command(name="delete", aliases=("remove",))
+ async def fl_delete(
+ self, ctx: Context, list_type: Optional[list_type_converter] = None, list_name: Optional[str] = None
+ ) -> None:
+ """Remove the filter list and all of its filters from the database."""
+ async def delete_list() -> None:
+ """The actual removal routine."""
+ list_data = await bot.instance.api_client.get(f"bot/filter/filter_lists/{list_id}")
+ file = discord.File(BytesIO(json.dumps(list_data, indent=4).encode("utf-8")), f"{list_description}.json")
+ message = await ctx.send("⏳ Annihilation in progress, please hold...", file=file)
+ # Unload the filter list.
+ filter_list.remove_list(list_type)
+ if not filter_list.filter_lists: # There's nothing left, remove from the cog.
+ self.filter_lists.pop(filter_list.name)
+ self.unsubscribe(filter_list)
+
+ await bot.instance.api_client.delete(f"bot/filter/filter_lists/{list_id}")
+ await message.edit(content=f"✅ The {list_description} list has been deleted.")
+
+ result = await self._resolve_list_type_and_name(ctx, list_type, list_name)
+ if result is None:
+ return
+ list_type, filter_list = result
+ list_id = filter_list.list_ids[list_type]
+ list_description = f"{past_tense(list_type.name.lower())} {filter_list.name}"
+ await ctx.reply(
+ f"Are you sure you want to delete the {list_description} list?",
+ view=DeleteConfirmationView(ctx.author, delete_list)
+ )
+
# endregion
# region: helper functions