From 15613e158c8af767dc2b02adc04934fde5c60bea Mon Sep 17 00:00:00 2001 From: JefeThePug <107787719+JefeThePug@users.noreply.github.com> Date: Sun, 23 Aug 2026 20:20:37 +0900 Subject: [PATCH 1/2] Created Modals for Latex command Made Latex editable without spamming the channel using Modals --- src/bot/cogs/features/_latex_utilities.py | 131 ++++++++++++++++++++++ src/bot/cogs/features/latex.py | 129 ++++++++++++--------- src/bot/static/images/invalid.png | Bin 0 -> 13205 bytes 3 files changed, 208 insertions(+), 52 deletions(-) create mode 100644 src/bot/cogs/features/_latex_utilities.py create mode 100644 src/bot/static/images/invalid.png diff --git a/src/bot/cogs/features/_latex_utilities.py b/src/bot/cogs/features/_latex_utilities.py new file mode 100644 index 0000000..b4f3560 --- /dev/null +++ b/src/bot/cogs/features/_latex_utilities.py @@ -0,0 +1,131 @@ +from __future__ import annotations + +import logging +from pathlib import Path +from typing import TYPE_CHECKING + +import discord + +if TYPE_CHECKING: + from .latex import LaTeX + +logger = logging.getLogger(__name__) + + +class LatexEditModal(discord.ui.Modal, title="Edit LaTeX"): + equation = discord.ui.TextInput( + label="LaTeX equation", + style=discord.TextStyle.paragraph, + required=True, + ) + text_color = discord.ui.TextInput(label="Text color", required=False) + bg_color = discord.ui.TextInput(label="Background color", required=False) + dpi = discord.ui.TextInput(label="DPI", required=False) + + def __init__(self, view: "LatexView"): + super().__init__() + + self.view = view + + self.equation.default = view.equation + self.text_color.default = view.text_color + self.bg_color.default = view.bg_color + self.dpi.default = str(view.dpi) + + async def on_submit(self, interaction: discord.Interaction): + self.view.equation = self.equation.value + + if self.text_color.value: + self.view.text_color = self.text_color.value + + if self.bg_color.value: + self.view.bg_color = self.bg_color.value + + if self.dpi.value: + try: + self.view.dpi = int(self.dpi.value) + except ValueError: + logger.exception( + "%s input a non-number value for DIP in the /latex command.", + interaction.user.display_name, + ) + + embed, file = await self.view.render() + + await interaction.response.edit_message( + embed=embed, + attachments=[file] if file else [], + view=self.view, + ) + + +class LatexView(discord.ui.View): + def __init__( + self, + latex: LaTeX, + interaction: discord.Interaction, + equation: str, + bg_color: str, + text_color: str, + dpi: int, + ): + super().__init__(timeout=300) + + self.latex = latex + self.interaction = interaction + self.equation = equation + self.bg_color = bg_color + self.text_color = text_color + self.dpi = dpi + + async def render(self) -> tuple[discord.Embed, discord.File | None]: + embed = await self.latex.render( + self.equation, + self.bg_color, + self.text_color, + self.dpi, + ) + + if embed.image.url and await self.latex.latex_available(embed.image.url): + return embed, None + + invalid = discord.File( + Path.cwd().joinpath("src", "bot", "static", "images", "invalid.png"), + filename="invalid.png", + ) + embed.set_image(url="attachment://invalid.png") + return embed, invalid + + @discord.ui.button(label="Edit", emoji="✏️", style=discord.ButtonStyle.primary) + async def edit( + self, interaction: discord.Interaction, button: discord.ui.Button + ) -> None: + await interaction.response.send_modal(LatexEditModal(self)) + + @discord.ui.button(label="Send", emoji="✅", style=discord.ButtonStyle.success) + async def publish( + self, interaction: discord.Interaction, button: discord.ui.Button + ) -> None: + embed = await self.latex.render( + self.equation, + self.bg_color, + self.text_color, + self.dpi, + ) + + if embed.image.url is None or not await self.latex.latex_available( + embed.image.url + ): + await interaction.response.send_message( + "Failed to display the LaTeX equation.", + ephemeral=True, + ) + return + + await interaction.response.send_message(embed=embed) + + for child in self.children: + if isinstance(child, discord.ui.Button): + child.disabled = True + + await self.interaction.edit_original_response(view=self) diff --git a/src/bot/cogs/features/latex.py b/src/bot/cogs/features/latex.py index f048738..4a8371e 100644 --- a/src/bot/cogs/features/latex.py +++ b/src/bot/cogs/features/latex.py @@ -11,6 +11,7 @@ from discord.ext import commands from ..constants import LATEX_COLORS +from ._latex_utilities import LatexView logger = logging.getLogger(__name__) @@ -47,6 +48,8 @@ async def cog_unload(self) -> None: @app_commands.command(description="Show the available LaTeX colors.") async def latex_colors(self, interaction: discord.Interaction) -> None: + logger.info("%s used the /latex_colors command.", interaction.user.display_name) + file = discord.File( Path.cwd().joinpath("src", "bot", "static", "images", "colors.png"), filename="colors.png", @@ -59,52 +62,45 @@ async def latex_colors(self, interaction: discord.Interaction) -> None: await interaction.response.send_message(file=file, embed=embed, ephemeral=True) async def latex_available(self, image_path: str) -> bool: + """ + Check whether the CodeCogs JSON response reports a valid equation. + """ try: if not self.session: return False - async with self.session.get(image_path) as response: - return response.status == 200 - except aiohttp.ClientError: - logger.exception("Failed to fetch LaTeX image from CodeCogs") + + json_path = image_path.replace( + "png.image?", + "png.json?", + 1, + ) + + async with self.session.get(json_path) as response: + if response.status != 200: + return False + data = await response.json(content_type="text/json") + + return data["latex"]["valid"] + + except (aiohttp.ClientError, KeyError, TypeError, ValueError): + logger.exception("Failed to validate LaTeX equation with CodeCogs") return False def normalize_color(self, value: str, default: str) -> str: normalized = value.strip().replace(" ", "").lower() return LATEX_COLORS.get(normalized, default) - @app_commands.command() - async def latex( - self, - interaction: discord.Interaction, - equation: str, - *, - bg_color: str = "Black", - txt_color: str = "White", - dpi: int = 300, - ) -> None: - """ - Send a mathematical equation using LaTeX. - - Parameters - ---------- - equation : LaTeX equation (do not include `$` delimiters). - bg_color : Background color. See `/latex_colors` for available colors. - txt_color : Text color. See `/latex_colors` for available colors. - dpi : Image resolution in DPI (200–500). - """ - logger.info( - "%s used the %s command.", - interaction.user.display_name, - interaction.command, - ) - - # check for valid user specified color for text and background + async def render( + self, equation: str, bg_color: str, txt_color: str, dpi: int + ) -> discord.Embed: + # Check for valid user specified color for text and background. bg_color = self.normalize_color(bg_color, "Black") txt_color = self.normalize_color(txt_color, "White") + if txt_color == bg_color: txt_color = "White" if bg_color == "Black" else "Black" - # handle necessary character replacements + # Handle necessary character replacements. character_replacements = { "\n": r"\n", "\r": r"\r", @@ -113,17 +109,13 @@ async def latex( "\x0c": r"\f", "\x0b": r"\v", "\x07": r"\a", - "%": r"\%", - "&": r"\&", - "#": r"%23", + "#": "&hash;", " ": "&space;", } + for old, new in character_replacements.items(): + equation = equation.replace(old, new) - for e, c in character_replacements.items(): - equation = equation.replace(e, c) - - # prevent user specified dpi from being too big/small - dpi = max(200, min(dpi, 500)) + dpi = max(200, min(dpi, 800)) image_path = ( rf"https://latex.codecogs.com/png.image?" @@ -134,18 +126,51 @@ async def latex( rf"\colorbox{{{bg_color}}}{{${equation}$}}" ) - if await self.latex_available(image_path): - embed = embed_info( - "LaTeX", - equation.replace("&space;", " "), - image_path, - ) - # Send the embed with the image - await interaction.response.send_message(embed=embed) - else: - await interaction.response.send_message( - f"Failed to fetch the image ({image_path}) from the API", ephemeral=True - ) + return embed_info( + "LaTeX", + equation.replace("&space;", " "), + image_path, + ) + + @app_commands.command() + async def latex( + self, + interaction: discord.Interaction, + equation: str, + *, + bg_color: str = "Black", + txt_color: str = "White", + dpi: int = 300, + ) -> None: + """ + Send a mathematical equation using LaTeX. + + Parameters + ---------- + equation : LaTeX equation (do not include `$` delimiters). + bg_color : Background color. See `/latex_colors` for available colors. + txt_color : Text color. See `/latex_colors` for available colors. + dpi : Image resolution in DPI (200–800). + """ + logger.info("%s used the /latex command.", interaction.user.display_name) + + view = LatexView( + self, + interaction, + equation, + bg_color, + txt_color, + dpi, + ) + + embed, file = await view.render() + + await interaction.response.send_message( + embed=embed, + files=[file] if file else [], + view=view, + ephemeral=True, + ) async def setup(bot: commands.Bot) -> None: diff --git a/src/bot/static/images/invalid.png b/src/bot/static/images/invalid.png new file mode 100644 index 0000000000000000000000000000000000000000..dd70902f2202e36bef67a77c3786b56153fefab9 GIT binary patch literal 13205 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 Ngovzgm7t#g{{#HK0mJ|R literal 0 HcmV?d00001 From 9fcd2ad81c40d44b37c7001989b045c8d292b3b8 Mon Sep 17 00:00:00 2001 From: JefeThePug <107787719+JefeThePug@users.noreply.github.com> Date: Sun, 23 Aug 2026 23:39:24 +0900 Subject: [PATCH 2/2] defer() Added defer() where requested and adjusted the interaction methods accordingly. --- src/bot/cogs/features/_latex_utilities.py | 12 ++++++++---- src/bot/cogs/features/latex.py | 7 ++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/bot/cogs/features/_latex_utilities.py b/src/bot/cogs/features/_latex_utilities.py index b4f3560..c97a6e6 100644 --- a/src/bot/cogs/features/_latex_utilities.py +++ b/src/bot/cogs/features/_latex_utilities.py @@ -50,9 +50,11 @@ async def on_submit(self, interaction: discord.Interaction): interaction.user.display_name, ) + await interaction.response.defer(ephemeral=True) + embed, file = await self.view.render() - await interaction.response.edit_message( + await interaction.edit_original_response( embed=embed, attachments=[file] if file else [], view=self.view, @@ -106,6 +108,8 @@ async def edit( async def publish( self, interaction: discord.Interaction, button: discord.ui.Button ) -> None: + await interaction.response.defer(ephemeral=True) + embed = await self.latex.render( self.equation, self.bg_color, @@ -116,13 +120,13 @@ async def publish( if embed.image.url is None or not await self.latex.latex_available( embed.image.url ): - await interaction.response.send_message( - "Failed to display the LaTeX equation.", + await interaction.followup.send( + content="Failed to display the LaTeX equation.", ephemeral=True, ) return - await interaction.response.send_message(embed=embed) + await interaction.followup.send(embed=embed) for child in self.children: if isinstance(child, discord.ui.Button): diff --git a/src/bot/cogs/features/latex.py b/src/bot/cogs/features/latex.py index 4a8371e..a9ff2b9 100644 --- a/src/bot/cogs/features/latex.py +++ b/src/bot/cogs/features/latex.py @@ -163,13 +163,14 @@ async def latex( dpi, ) + await interaction.response.defer(ephemeral=True) + embed, file = await view.render() - await interaction.response.send_message( + await interaction.edit_original_response( embed=embed, - files=[file] if file else [], + attachments=[file] if file else [], view=view, - ephemeral=True, )