diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a31efc..e2492ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ +**unreleased v0.2.1** + +- Enhancement: view filters command +- Enhancement: more control over view commands +- Enhancement: show active state of viewed subscriptions +- + **v0.2.0** - Fix: Fetch channels if not found in bot cache (error fix) diff --git a/src/extensions/cmds.py b/src/extensions/cmds.py index c2a61c9..51f1971 100644 --- a/src/extensions/cmds.py +++ b/src/extensions/cmds.py @@ -12,11 +12,11 @@ import validators from feedparser import FeedParserDict, parse from discord.ext import commands from discord import Interaction, TextChannel, Embed, Colour -from discord.app_commands import Choice, Group, autocomplete, rename, command +from discord.app_commands import Choice, choices, Group, autocomplete, rename, command from discord.errors import Forbidden from api import API -from feed import Subscription, TrackedContent +from feed import Subscription, TrackedContent, ContentFilter from utils import ( Followup, PaginationView, @@ -78,6 +78,12 @@ async def validate_rss_source(nickname: str, url: str) -> Tuple[str | None, Feed return None, feed +tri_choices = [ + Choice(name="Yes", value=2), + Choice(name="No (default)", value=1), + Choice(name="All", value=0), +] + class CommandsCog(commands.Cog): """ @@ -109,14 +115,13 @@ class CommandsCog(commands.Cog): def formatdata(index, item): item = Subscription.from_dict(item) - - channels = f"{item.channels_count}{' channels' if item.channels_count != 1 else ' channel'}" - filters = f"{len(item.filters)}{' filters' if len(item.filters) != 1 else ' filter'}" - notes = item.extra_notes[:25] + "..." if len(item.extra_notes) > 28 else item.extra_notes - links = f"[RSS Link]({item.url}) ยท [API Link]({API.API_EXTERNAL_ENDPOINT}subscription/{item.id}/)" - description = f"{channels}, {filters}\n" - description += f"{notes}\n" if notes else "" + notes = item.extra_notes[:25] + "..." if len(item.extra_notes) > 28 else item.extra_notes + links = f"[RSS Link]({item.url}) โ€ข [API Link]({API.API_EXTERNAL_ENDPOINT}subscription/{item.id}/)" + activeness = "โœ… `enabled`" if item.active else "๐Ÿšซ `disabled`" + + description = f"๐Ÿ†” `{item.id}`\n{activeness}\n#๏ธโƒฃ `{item.channels_count}` ๐Ÿ”ฝ `{len(item.filters)}`\n" + description = f"{notes}\n" + description if notes else description description += links key = f"{index}. {item.name}" @@ -145,29 +150,48 @@ class CommandsCog(commands.Cog): await pagination.send() @view_group.command(name="tracked-content") - async def cmd_list_tracked(self, inter: Interaction, search: str = ""): - """List Tracked Content from this server, or a given sub""" + @choices(blocked=tri_choices) + async def cmd_list_tracked(self, inter: Interaction, search: str = "", blocked: Choice[int] = 1): + """List Tracked Content from this server""" # TODO: , or a given sub await inter.response.defer() + # If the user picks an option it's an instance of `Choice` otherwise `str` + # Can't figure a way to select a default choices, so blame discordpy for this mess. + if isinstance(blocked, Choice): + blocked = blocked.value + def formatdata(index, item): item = TrackedContent.from_dict(item) sub = Subscription.from_dict(item.subscription) - links = f"[Content Link]({item.url}) ยท [Message Link](https://discord.com/channels/{sub.guild_id}/{item.channel_id}/{item.message_id}/)" - description = f"Subscription: {sub.name}\n{links}" + links = f"[Content Link]({item.url}) ยท [Message Link](https://discord.com/channels/{sub.guild_id}/{item.channel_id}/{item.message_id}/) ยท [API Link]({API.API_EXTERNAL_ENDPOINT}tracked-content/{item.id}/)" + delivery_state = "โœ… Delivered" if not item.blocked else "๐Ÿšซ Blocked" - key = f"{item.id}. {item.title}" + description = f"๐Ÿ†” `{item.id}`\n" + description += f"{delivery_state}\n" if blocked == 0 else "" + description += f"โžก๏ธ *{sub.name}*\n{links}" + + key = f"{index}. {item.title}" return key, description + def determine_blocked(): + match blocked: + case 0: return "" + case 1: return "false" + case 2: return "true" + case _: return "" + async def getdata(page: int, pagesize: int): async with aiohttp.ClientSession() as session: api = API(self.bot.api_token, session) + is_blocked = determine_blocked() return await api.get_tracked_content( subscription__guild_id=inter.guild_id, + blocked=is_blocked, page=page, page_size=pagesize, - search=search + search=search, ) embed = Followup(f"Tracked Content in {inter.guild.name}").info()._embed @@ -182,6 +206,60 @@ class CommandsCog(commands.Cog): ) await pagination.send() + @view_group.command(name="filters") + async def cmd_list_filters(self, inter: Interaction, search: str = ""): + """List Filters from this server.""" + + await inter.response.defer() + + def formatdata(index, item): + item = ContentFilter.from_dict(item) + + matching_algorithm = get_algorithm_name(item.matching_algorithm) + whitelist = "Whitelist" if item.is_whitelist else "Blacklist" + sensitivity = "Case insensitive" if item.is_insensitive else "Case sensitive" + + description = f"๐Ÿ†” `{item.id}`\n" + description += f"๐Ÿ”„ `{matching_algorithm}`\n๐ŸŸฐ `{item.match}`\n" + description += f"โœ… `{whitelist}` ๐Ÿ”  `{sensitivity}`\n" + description += f"[API Link]({API.API_EXTERNAL_ENDPOINT}filter/{item.id}/)" + + key = f"{index}. {item.name}" + return key, description + + def get_algorithm_name(matching_algorithm: int): + match matching_algorithm: + case 0: return "None" + case 1: return "Any word" + case 2: return "All words" + case 3: return "Exact match" + case 4: return "Regex match" + case 5: return "Fuzzy match" + case _: return "unknown" + + + async def getdata(page, pagesize): + async with aiohttp.ClientSession() as session: + api = API(self.bot.api_token, session) + return await api.get_filters( + guild_id=inter.guild_id, + page=page, + page_size=pagesize, + search=search + ) + + embed = Followup(f"Filters in {inter.guild.name}").info()._embed + pagination = PaginationView( + self.bot, + inter=inter, + embed=embed, + getdata=getdata, + formatdata=formatdata, + pagesize=10, + initpage=1 + ) + await pagination.send() + # Group for test related commands test_group = Group( name="test",