From 7975a52d06c0b243323c3a53e0d7ea6978dba91b Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Wed, 28 Feb 2024 01:25:54 -0600 Subject: [PATCH 1/3] feat: add buttons to ruff rule embed --- src/byte/lib/utils.py | 8 ++- src/byte/plugins/astral.py | 20 +++++-- src/byte/views/astral.py | 111 +++++++++++++++++++++++++++++++++++++ src/byte/views/forums.py | 2 +- 4 files changed, 133 insertions(+), 8 deletions(-) create mode 100644 src/byte/views/astral.py diff --git a/src/byte/lib/utils.py b/src/byte/lib/utils.py index dbb2440..4711495 100644 --- a/src/byte/lib/utils.py +++ b/src/byte/lib/utils.py @@ -41,6 +41,7 @@ class RuffRule(BaseRuffRule): class FormattedRuffRule(BaseRuffRule): rule_link: str + rule_anchor_link: str __all__ = ( @@ -224,8 +225,10 @@ def format_ruff_rule(rule_data: RuffRule) -> FormattedRuffRule: FormattedRuffRule: The formatted rule data. """ explanation_formatted = re.sub(r"## (.+)", r"**\1**", rule_data["explanation"]) - rule_name = rule_data["code"] - rule_link = f"https://docs.astral.sh/ruff/rules/#{rule_name}" + rule_code = rule_data["code"] + rule_name = rule_data["name"] + rule_link = f"https://docs.astral.sh/ruff/rules/{rule_name}" + rule_anchor_link = f"https://docs.astral.sh/ruff/rules/#{rule_code}" return { "name": rule_data.get("name", "No name available"), @@ -233,6 +236,7 @@ def format_ruff_rule(rule_data: RuffRule) -> FormattedRuffRule: "explanation": explanation_formatted, "fix": rule_data.get("fix", "No fix available"), "rule_link": rule_link, + "rule_anchor_link": rule_anchor_link, } diff --git a/src/byte/plugins/astral.py b/src/byte/plugins/astral.py index f3c91f6..4265d17 100644 --- a/src/byte/plugins/astral.py +++ b/src/byte/plugins/astral.py @@ -10,6 +10,7 @@ from byte.lib.common import ruff_logo from byte.lib.utils import chunk_sequence, format_ruff_rule, query_all_ruff_rules +from byte.views.astral import RuffView if TYPE_CHECKING: from byte.lib.utils import RuffRule @@ -52,21 +53,30 @@ async def ruff_rule(self, interaction: Interaction, rule: str) -> None: return formatted_rule_details = format_ruff_rule(rule_details) + docs_field = ( + f"- [Rule Documentation]({formatted_rule_details['rule_link']})\n" + f"- [Similar Rules]({formatted_rule_details['rule_anchor_link']})" + ) + + # TODO: investigate if we can clean this up + minified_embed = Embed(title=f"Ruff Rule: {formatted_rule_details['name']}", color=0xD7FF64) + minified_embed.add_field(name="Summary", value=formatted_rule_details["summary"], inline=False) + minified_embed.add_field(name="Documentation", value=docs_field, inline=False) + minified_embed.set_thumbnail(url=ruff_logo) embed = Embed(title=f"Ruff Rule: {formatted_rule_details['name']}", color=0xD7FF64) embed.add_field(name="Summary", value=formatted_rule_details["summary"], inline=False) # TODO: Better chunking for idx, chunk in enumerate(chunk_sequence(formatted_rule_details["explanation"], 1000)): - embed.add_field(name="Explanation" if not idx else "", value="".join(chunk), inline=False) + embed.add_field(name="" if idx else "Explanation", value="".join(chunk), inline=False) embed.add_field(name="Fix", value=formatted_rule_details["fix"], inline=False) - embed.add_field( - name="Documentation", value=f"[Rule Documentation]({formatted_rule_details['rule_link']})", inline=False - ) + embed.add_field(name="Documentation", value=docs_field, inline=False) embed.set_thumbnail(url=ruff_logo) - await interaction.followup.send(embed=embed) + view = RuffView(author=interaction.user.id, bot=self.bot, original_embed=embed, minified_embed=minified_embed) + await interaction.followup.send(embed=embed, view=view) @app_command(name="format") async def format_code(self, interaction: Interaction, code_block: str) -> None: # noqa: ARG002 diff --git a/src/byte/views/astral.py b/src/byte/views/astral.py new file mode 100644 index 0000000..37533d1 --- /dev/null +++ b/src/byte/views/astral.py @@ -0,0 +1,111 @@ +"""Discord UI views used in Astral commands.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +from discord import ButtonStyle, Embed, Interaction +from discord.ui import Button, View, button + +from byte.lib.log import get_logger + +if TYPE_CHECKING: + from discord.ext.commands import Bot + +__all__ = ("RuffView",) + +logger = get_logger() + + +class RuffView(View): + """View for the Ruff embed.""" + + def __init__(self, author: int, bot: Bot, original_embed: Embed, minified_embed: Embed, *args, **kwargs) -> None: + """Initialize the view. + + Args: + author: Author ID. + bot: Bot object. + original_embed: The original embed to display. + minified_embed: The minified embed to display. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + """ + super().__init__(*args, **kwargs) + self.author_id = author + self.bot = bot + self.original_embed = original_embed + self.minified_embed = minified_embed + + async def interaction_check(self, interaction: Interaction) -> bool: + """Check if the user is the author or a guild admin. + + Args: + interaction: Interaction object. + + Returns: + True if the user is the author or a guild admin, False otherwise. + """ + if interaction.user.id == self.author_id or interaction.user.guild_permissions.administrator: + return True + await interaction.response.send_message( + "You do not have permission to interact with this message.", ephemeral=True + ) + return False + + @staticmethod + async def delete_button_callback(interaction: Interaction) -> None: + """Delete the message this view is attached to. + + Args: + interaction: Interaction object. + """ + await interaction.message.delete() + + async def collapse_button_callback(self, interaction: Interaction) -> None: + """Minify the message to show less information but not none. + + Args: + interaction: Interaction object. + button: Button object. + """ + await interaction.message.edit(embed=self.minified_embed, view=self) + + async def expand_button_callback(self, interaction: Interaction) -> None: + """Expand the message to show full information. + + Args: + interaction: Interaction object. + button: Button object. + """ + await interaction.message.edit(embed=self.original_embed, view=self) + + # Define buttons using decorators + @button(label="Delete", style=ButtonStyle.red, custom_id="delete_button") + async def delete_button(self, interaction: Interaction, button: Button) -> None: + """Button to delete the message this view is attached to. + + Args: + interaction: Interaction object. + button: Button object. + """ + await self.delete_button_callback(interaction, button) + + @button(label="Collapse", style=ButtonStyle.grey, custom_id="collapse_button") + async def collapse_button(self, interaction: Interaction, button: Button) -> None: + """Button to minify the embed to show less information but not none. + + Args: + interaction: Interaction object. + button: Button object. + """ + await self.collapse_button_callback(interaction, button) + + @button(label="Expand", style=ButtonStyle.green, custom_id="expand_button", disabled=True) + async def expand_button(self, interaction: Interaction, button: Button) -> None: + """Button to expand the embed to show full information. + + Args: + interaction: Interaction object. + button: Button object. + """ + await self.expand_button_callback(interaction, button) diff --git a/src/byte/views/forums.py b/src/byte/views/forums.py index b4dc350..d6fcf6e 100644 --- a/src/byte/views/forums.py +++ b/src/byte/views/forums.py @@ -1,4 +1,4 @@ -"""Discord UI Views.""" +"""Discord UI views used in forums.""" from discord import ButtonStyle, Interaction from discord.ext.commands import Bot From 42c1d1984915ce794c721c6a8243ef8787cdf449 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Wed, 28 Feb 2024 11:52:22 -0600 Subject: [PATCH 2/3] fix(astral-ruff): Fix "This interaction failed" and added type hints (#69) Co-authored-by: Alc-Alc --- src/byte/views/astral.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/byte/views/astral.py b/src/byte/views/astral.py index 37533d1..b1aec79 100644 --- a/src/byte/views/astral.py +++ b/src/byte/views/astral.py @@ -9,6 +9,8 @@ from byte.lib.log import get_logger if TYPE_CHECKING: + from typing import Self + from discord.ext.commands import Bot __all__ = ("RuffView",) @@ -68,7 +70,7 @@ async def collapse_button_callback(self, interaction: Interaction) -> None: interaction: Interaction object. button: Button object. """ - await interaction.message.edit(embed=self.minified_embed, view=self) + await interaction.response.edit_message(embed=self.minified_embed, view=self) async def expand_button_callback(self, interaction: Interaction) -> None: """Expand the message to show full information. @@ -81,27 +83,27 @@ async def expand_button_callback(self, interaction: Interaction) -> None: # Define buttons using decorators @button(label="Delete", style=ButtonStyle.red, custom_id="delete_button") - async def delete_button(self, interaction: Interaction, button: Button) -> None: + async def delete_button(self, interaction: Interaction, button: Button[Self]) -> None: """Button to delete the message this view is attached to. Args: interaction: Interaction object. button: Button object. """ - await self.delete_button_callback(interaction, button) + await self.delete_button_callback(interaction) @button(label="Collapse", style=ButtonStyle.grey, custom_id="collapse_button") - async def collapse_button(self, interaction: Interaction, button: Button) -> None: + async def collapse_button(self, interaction: Interaction, button: Button[Self]) -> None: """Button to minify the embed to show less information but not none. Args: interaction: Interaction object. button: Button object. """ - await self.collapse_button_callback(interaction, button) + await self.collapse_button_callback(interaction) @button(label="Expand", style=ButtonStyle.green, custom_id="expand_button", disabled=True) - async def expand_button(self, interaction: Interaction, button: Button) -> None: + async def expand_button(self, interaction: Interaction, button: Button[Self]) -> None: """Button to expand the embed to show full information. Args: From 56203b107d230642a8c687630dccc2dc30227607 Mon Sep 17 00:00:00 2001 From: Alc-Alc <45509143+Alc-Alc@users.noreply.github.com> Date: Thu, 29 Feb 2024 01:06:39 +0530 Subject: [PATCH 3/3] feat(astral-ruff): Minimal response and ephemeral expand (#70) * feat(astral-ruff): Minimal response and ephemeral expand * Update src/byte/views/astral.py --------- Co-authored-by: Alc-Alc Co-authored-by: Jacob Coffee --- src/byte/plugins/astral.py | 2 +- src/byte/views/astral.py | 31 ++++++------------------------- 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/src/byte/plugins/astral.py b/src/byte/plugins/astral.py index 4265d17..d374a73 100644 --- a/src/byte/plugins/astral.py +++ b/src/byte/plugins/astral.py @@ -76,7 +76,7 @@ async def ruff_rule(self, interaction: Interaction, rule: str) -> None: embed.set_thumbnail(url=ruff_logo) view = RuffView(author=interaction.user.id, bot=self.bot, original_embed=embed, minified_embed=minified_embed) - await interaction.followup.send(embed=embed, view=view) + await interaction.followup.send(embed=minified_embed, view=view) @app_command(name="format") async def format_code(self, interaction: Interaction, code_block: str) -> None: # noqa: ARG002 diff --git a/src/byte/views/astral.py b/src/byte/views/astral.py index b1aec79..f2936ab 100644 --- a/src/byte/views/astral.py +++ b/src/byte/views/astral.py @@ -72,18 +72,9 @@ async def collapse_button_callback(self, interaction: Interaction) -> None: """ await interaction.response.edit_message(embed=self.minified_embed, view=self) - async def expand_button_callback(self, interaction: Interaction) -> None: - """Expand the message to show full information. - - Args: - interaction: Interaction object. - button: Button object. - """ - await interaction.message.edit(embed=self.original_embed, view=self) - # Define buttons using decorators @button(label="Delete", style=ButtonStyle.red, custom_id="delete_button") - async def delete_button(self, interaction: Interaction, button: Button[Self]) -> None: + async def delete_button(self, interaction: Interaction, _: Button[Self]) -> None: """Button to delete the message this view is attached to. Args: @@ -92,22 +83,12 @@ async def delete_button(self, interaction: Interaction, button: Button[Self]) -> """ await self.delete_button_callback(interaction) - @button(label="Collapse", style=ButtonStyle.grey, custom_id="collapse_button") - async def collapse_button(self, interaction: Interaction, button: Button[Self]) -> None: - """Button to minify the embed to show less information but not none. + @button(label="Learn More", style=ButtonStyle.green, custom_id="learn_more_button") + async def learn_more_button(self, interaction: Interaction, _: Button[Self]) -> None: + """Button to privately message the requesting user the full embed. Args: interaction: Interaction object. - button: Button object. - """ - await self.collapse_button_callback(interaction) - - @button(label="Expand", style=ButtonStyle.green, custom_id="expand_button", disabled=True) - async def expand_button(self, interaction: Interaction, button: Button[Self]) -> None: - """Button to expand the embed to show full information. - - Args: - interaction: Interaction object. - button: Button object. + _: Button object. """ - await self.expand_button_callback(interaction, button) + await interaction.response.send_message(embed=self.original_embed, ephemeral=True)