Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add buttons to ruff rule embed #68

Merged
merged 3 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/byte/lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class RuffRule(BaseRuffRule):

class FormattedRuffRule(BaseRuffRule):
rule_link: str
rule_anchor_link: str


__all__ = (
Expand Down Expand Up @@ -224,15 +225,18 @@ 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"),
"summary": rule_data.get("summary", "No summary available"),
"explanation": explanation_formatted,
"fix": rule_data.get("fix", "No fix available"),
"rule_link": rule_link,
"rule_anchor_link": rule_anchor_link,
}


Expand Down
20 changes: 15 additions & 5 deletions src/byte/plugins/astral.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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=minified_embed, view=view)

@app_command(name="format")
async def format_code(self, interaction: Interaction, code_block: str) -> None: # noqa: ARG002
Expand Down
94 changes: 94 additions & 0 deletions src/byte/views/astral.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""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 typing import Self

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.response.edit_message(embed=self.minified_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[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(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 object.
"""
await interaction.response.send_message(embed=self.original_embed, ephemeral=True)
2 changes: 1 addition & 1 deletion src/byte/views/forums.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Discord UI Views."""
"""Discord UI views used in forums."""

from discord import ButtonStyle, Interaction
from discord.ext.commands import Bot
Expand Down
Loading