diff --git a/CHANGELOG.md b/CHANGELOG.md index 13cf78d41..db4810020 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), * **Permission system**: Role assignment removal method and cache invalidation for role and command permission changes; command permission check function; enhanced permission error messaging for unconfigured commands * **Utility**: Loguru logging for AFK nickname changes and restorations (target user ID, nickname changes, skipped restore events) * **Config dashboard**: Togglesnippetlock command in configurable command list for command permissions +* **Snippets**: `$snippets`/`$ls` command now accepts an optional member argument to list snippets created by a specific user (`$ls @user`), using `FlexibleUserConverter` to support mentions, user IDs, and usernames including users who have left the guild ### Changed diff --git a/src/tux/modules/snippets/__init__.py b/src/tux/modules/snippets/__init__.py index 8d7fa2b7b..29a83d859 100644 --- a/src/tux/modules/snippets/__init__.py +++ b/src/tux/modules/snippets/__init__.py @@ -64,6 +64,7 @@ def _create_snippets_list_embed( snippets: list[Snippet], total_snippets: int, search_query: str | None = None, + member: discord.User | None = None, ) -> discord.Embed: """Create an embed for displaying a paginated list of snippets. @@ -101,7 +102,11 @@ def _create_snippets_list_embed( ) count = len(snippets) total_snippets = total_snippets or 0 - embed_title = f"Snippets ({count}/{total_snippets})" + embed_title = ( + f"Snippets by {member.display_name} ({count}/{total_snippets})" + if member + else f"Snippets ({count}/{total_snippets})" + ) footer_text, footer_icon_url = EmbedCreator.get_footer( bot=ctx.bot, diff --git a/src/tux/modules/snippets/list_snippets.py b/src/tux/modules/snippets/list_snippets.py index e75116cd6..1d04c8fe0 100644 --- a/src/tux/modules/snippets/list_snippets.py +++ b/src/tux/modules/snippets/list_snippets.py @@ -5,16 +5,20 @@ all available code snippets in Discord guilds. """ +import discord from discord.ext import commands from reactionmenu import ViewButton, ViewMenu from sqlalchemy import desc from tux.core.bot import Tux +from tux.core.converters import FlexibleUserConverter from tux.database.models import Snippet from tux.shared.constants import SNIPPET_PAGINATION_LIMIT from . import SnippetsBaseCog +_MEMBER_PARAM = commands.param(default=None, converter=FlexibleUserConverter()) + class ListSnippets(SnippetsBaseCog): """Discord cog for listing snippets.""" @@ -37,33 +41,39 @@ def __init__(self, bot: Tux) -> None: async def list_snippets( self, ctx: commands.Context[Tux], + member: discord.User | None = _MEMBER_PARAM, *, search_query: str | None = None, ) -> None: - """List snippets, optionally filtering by a search query. + """List snippets, optionally filtering by member or search query. Displays snippets in a paginated embed, sorted by usage count (descending). - The search query filters by snippet name or content (case-insensitive). + Optionally filter by a member's snippets or a search query. Parameters ---------- ctx : commands.Context[Tux] The context of the command. + member : discord.User | None, optional + The member whose snippets to list. search_query : str | None, optional The query to filter snippets by name or content. """ assert ctx.guild - # Fetch snippets with database-level ordering and optional search - if search_query: + if member is not None: + filtered_snippets = await self.db.snippet.get_snippets_by_creator( + member.id, + ctx.guild.id, + ) + filtered_snippets.sort(key=lambda s: s.uses, reverse=True) + elif search_query: filtered_snippets = await self.db.snippet.search_snippets( ctx.guild.id, search_query, ) - # Sort search results by usage count (most used first) filtered_snippets.sort(key=lambda s: s.uses, reverse=True) else: - # Fetch all snippets ordered by usage count from database filtered_snippets = await self.db.snippet.get_all_snippets_by_guild_id( ctx.guild.id, order_by=desc(Snippet.__table__.c.uses), # type: ignore[attr-defined] @@ -72,7 +82,9 @@ async def list_snippets( if not filtered_snippets: await self.send_snippet_error( ctx, - description="No snippets found matching your query." + description=f"No snippets found for {member.display_name}." + if member + else "No snippets found matching your query." if search_query else "No snippets found.", ) @@ -81,7 +93,6 @@ async def list_snippets( # Set up pagination menu menu = ViewMenu(ctx, menu_type=ViewMenu.TypeEmbed, show_page_director=False) - # Add pages based on filtered snippets total_snippets = len(filtered_snippets) for i in range(0, total_snippets, SNIPPET_PAGINATION_LIMIT): @@ -92,6 +103,7 @@ async def list_snippets( page_snippets, total_snippets, search_query, + member, ) menu.add_page(embed)