From b503385542b76054005de329329729d880d09692 Mon Sep 17 00:00:00 2001 From: Choco <94597336+ChocoMeow@users.noreply.github.com> Date: Mon, 6 Oct 2025 15:56:30 +0800 Subject: [PATCH] Refactor modal usage and error handling in views Replaced custom Modal class with BaseModal in embed_builder.py for consistency and maintainability. Updated modal instantiation to use custom_id and improved value handling. Removed redundant on_error methods from help.py, pagination.py, and search.py, and delegated error handling to superclass in controller.py for cleaner error management. --- voicelink/views/controller.py | 8 +- voicelink/views/embed_builder.py | 323 ++++++++++++++++--------------- voicelink/views/help.py | 3 - voicelink/views/pagination.py | 3 - voicelink/views/search.py | 3 - 5 files changed, 166 insertions(+), 174 deletions(-) diff --git a/voicelink/views/controller.py b/voicelink/views/controller.py index b6e4b2f..1879dfc 100644 --- a/voicelink/views/controller.py +++ b/voicelink/views/controller.py @@ -484,10 +484,6 @@ class InteractiveController(discord.ui.View): async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item) -> None: if isinstance(error, ButtonOnCooldown): sec = int(error.retry_after) - await interaction.response.send_message(f"You're on cooldown for {sec} second{'' if sec == 1 else 's'}!", ephemeral=True) + return await interaction.response.send_message(f"You're on cooldown for {sec} second{'' if sec == 1 else 's'}!", ephemeral=True) - elif isinstance(error, Exception): - traceback.print_exception(error) - await interaction.response.send_message(error) - - return \ No newline at end of file + super().on_error(interaction, error, item) \ No newline at end of file diff --git a/voicelink/views/embed_builder.py b/voicelink/views/embed_builder.py index 0a4294d..8d092da 100644 --- a/voicelink/views/embed_builder.py +++ b/voicelink/views/embed_builder.py @@ -24,26 +24,12 @@ SOFTWARE. import discord import copy -from typing import List from discord.ext import commands +from .utils import BaseModal from ..mongodb import MongoDBHandler from ..placeholders import PlayerPlaceholder -class Modal(discord.ui.Modal): - def __init__(self, items: List[discord.ui.Item], *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - for item in items: - self.add_item(item) - - self.values: dict = {} - - async def on_submit(self, interaction: discord.Interaction) -> None: - await interaction.response.defer() - for children in self.children: - self.values[children.label.lower()] = children.value - - self.stop() class Dropdown(discord.ui.Select): def __init__(self): @@ -90,42 +76,47 @@ class EmbedBuilderView(discord.ui.View): @discord.ui.button(label="Edit Content", style=discord.ButtonStyle.blurple) async def edit_content(self, interaction: discord.Interaction, button: discord.ui.Button): data = self.data.get(self.embed_type, {}) - items = [ - discord.ui.TextInput( - label="Title", - placeholder="The title of the embed", - style=discord.TextStyle.paragraph, - max_length=1000, - default=data.get("title", {}).get("name"), - required=False - ), - discord.ui.TextInput( - label="Url", - placeholder="The url of the title", - style=discord.TextStyle.short, - max_length=100, - default=data.get("title", {}).get("url"), - required=False - ), - discord.ui.TextInput( - label="Color", - placeholder="The color of the embed", - style=discord.TextStyle.short, - max_length=100, - default=data.get("color"), - required=False - ), - discord.ui.TextInput( - label="Description", - placeholder="The description of the title", - style=discord.TextStyle.paragraph, - max_length=200, - default=data.get("description"), - required=False - ) - ] - - modal = Modal(items, title="Edit Content") + modal = BaseModal( + title="Edit Content", + custom_id="edit_content", + items=[ + discord.ui.TextInput( + label="Title", + placeholder="The title of the embed", + style=discord.TextStyle.paragraph, + custom_id="title", + max_length=1000, + default=data.get("title", {}).get("name"), + required=False + ), + discord.ui.TextInput( + label="Url", + placeholder="The url of the title", + style=discord.TextStyle.short, + custom_id="url", + max_length=100, + default=data.get("title", {}).get("url"), + required=False + ), + discord.ui.TextInput( + label="Color", + placeholder="The color of the embed", + style=discord.TextStyle.short, + custom_id="color", + max_length=100, + default=data.get("color"), + required=False + ), + discord.ui.TextInput( + label="Description", + placeholder="The description of the title", + style=discord.TextStyle.paragraph, + custom_id="description", + max_length=200, + default=data.get("description"), + required=False + ) + ]) await interaction.response.send_modal(modal) await modal.wait() @@ -142,112 +133,120 @@ class EmbedBuilderView(discord.ui.View): except: pass - return await interaction.edit_original_response(embed=self.build_embed()) + return await self.response.edit(embed=self.build_embed()) @discord.ui.button(label="Edit Author",) async def edit_author(self, interaction: discord.Interaction, button: discord.ui.Button): data = self.data.get(self.embed_type, {}) - items = [ - discord.ui.TextInput( - label="Name", - placeholder="The name of the author", - style=discord.TextStyle.paragraph, - max_length=200, - default=data.get("author", {}).get("name"), - required=False - ), - discord.ui.TextInput( - label="Url", - placeholder="The url of the author", - style=discord.TextStyle.short, - max_length=100, - default=data.get("author", {}).get("url"), - required=False - ), - discord.ui.TextInput( - label="Icon Url", - placeholder="The icon url of the author", - style=discord.TextStyle.short, - max_length=100, - default=data.get("author", {}).get("icon_url"), - required=False - ), - ] - - modal = Modal(items, title="Edit Author") + modal = BaseModal( + title="Edit Author", + custom_id="edit_author", + items=[ + discord.ui.TextInput( + label="Name", + placeholder="The name of the author", + style=discord.TextStyle.paragraph, + custom_id="name", + max_length=200, + default=data.get("author", {}).get("name"), + required=False + ), + discord.ui.TextInput( + label="Url", + placeholder="The url of the author", + style=discord.TextStyle.short, + custom_id="url", + max_length=100, + default=data.get("author", {}).get("url"), + required=False + ), + discord.ui.TextInput( + label="Icon Url", + placeholder="The icon url of the author", + style=discord.TextStyle.short, + custom_id="icon_url", + max_length=100, + default=data.get("author", {}).get("icon_url"), + required=False + ), + ]) await interaction.response.send_modal(modal) await modal.wait() v = modal.values - if v['name'] != "": if "author" not in data: data["author"] = {} data["author"]["name"] = v['name'] data["author"]["url"] = v['url'] - data["author"]["icon_url"] = v['icon url'] + data["author"]["icon_url"] = v['icon_url'] else: del data["author"] - return await interaction.edit_original_response(embed=self.build_embed()) + return await self.response.edit(embed=self.build_embed()) @discord.ui.button(label="Edit Image") async def edit_image(self, interaction: discord.Interaction, button: discord.ui.Button): data = self.data.get(self.embed_type, {}) - items = [ - discord.ui.TextInput( - label="Thumbnail", - placeholder="The url of the thumbnail", - style=discord.TextStyle.short, - max_length=200, - default=data.get("thumbnail"), - required=False - ), - discord.ui.TextInput( - label="Image", - placeholder="The url of the image", - style=discord.TextStyle.short, - max_length=100, - default=data.get("image"), - required=False - ) - ] - - modal = Modal(items, title="Edit Image") + modal = BaseModal( + title="Edit Image", + custom_id="edit_image", + items=[ + discord.ui.TextInput( + label="Thumbnail", + placeholder="The url of the thumbnail", + style=discord.TextStyle.short, + custom_id="thumbnail", + max_length=200, + default=data.get("thumbnail"), + required=False + ), + discord.ui.TextInput( + label="Image", + placeholder="The url of the image", + style=discord.TextStyle.short, + custom_id="image", + max_length=100, + default=data.get("image"), + required=False + ) + ]) await interaction.response.send_modal(modal) await modal.wait() v = modal.values - data["thumbnail"] = v['thumbnail'] data["image"] = v['image'] - return await interaction.edit_original_response(embed=self.build_embed()) + return await self.response.edit(embed=self.build_embed()) @discord.ui.button(label="Edit Footer") async def edit_footer(self, interaction: discord.Interaction, button: discord.ui.Button): data = self.data.get(self.embed_type, {}) - items = [ - discord.ui.TextInput( - label="Text", - placeholder="The text of the footer", - style=discord.TextStyle.paragraph, - max_length=200, - default=data.get("footer", {}).get("text"), - required=False - ), - discord.ui.TextInput( - label="Icon Url", - placeholder="The url of the icon", - style=discord.TextStyle.short, - max_length=100, - default=data.get("footer", {}).get("icon_url"), - required=False - ) - ] - - modal = Modal(items, title="Edit Footer") + modal = BaseModal( + title="Edit Footer", + custom_id="edit_footer", + items=[ + discord.ui.TextInput( + label="Text", + placeholder="The text of the footer", + style=discord.TextStyle.paragraph, + custom_id="text", + max_length=200, + default=data.get("footer", {}).get("text"), + required=False + ), + discord.ui.TextInput( + label="Icon Url", + placeholder="The url of the icon", + style=discord.TextStyle.short, + custom_id="icon_url", + max_length=100, + default=data.get("footer", {}).get("icon_url"), + required=False + ) + ]) await interaction.response.send_modal(modal) await modal.wait() @@ -256,40 +255,44 @@ class EmbedBuilderView(discord.ui.View): data["footer"] = {} data["footer"]["text"] = v['text'] - data["footer"]["icon_url"] = v['icon url'] + data["footer"]["icon_url"] = v['icon_url'] - return await interaction.edit_original_response(embed=self.build_embed()) + return await self.response.edit(embed=self.build_embed()) @discord.ui.button(label="Add Field", style=discord.ButtonStyle.green, row=1) async def add_field(self, interaction: discord.Interaction, button: discord.ui.Button): data = self.data.get(self.embed_type) - items = [ - discord.ui.TextInput( - label="Name", - placeholder="The name of the field", - style=discord.TextStyle.paragraph, - max_length=256 - ), - discord.ui.TextInput( - label="Value", - placeholder="The value of the field", - style=discord.TextStyle.long, - max_length=1024 - ), - discord.ui.TextInput( - label="Inline", - placeholder="The inline of the field, e.g. True or False", - style=discord.TextStyle.short - ) - ] - if "fields" not in data: data["fields"] = [] if len(data["fields"]) >= 25: return await interaction.response.send_message("You have already reached the maximum of fields!", ephemeral=True) - modal = Modal(items, title="Add Field") + modal = BaseModal( + title="Add Field", + custom_id="add_field", + items=[ + discord.ui.TextInput( + label="Name", + placeholder="The name of the field", + style=discord.TextStyle.paragraph, + custom_id="name", + max_length=256 + ), + discord.ui.TextInput( + label="Value", + placeholder="The value of the field", + style=discord.TextStyle.long, + custom_id="value", + max_length=1024 + ), + discord.ui.TextInput( + label="Inline", + placeholder="The inline of the field, e.g. True or False", + style=discord.TextStyle.short, + custom_id="inline", + ) + ]) await interaction.response.send_modal(modal) await modal.wait() @@ -300,18 +303,10 @@ class EmbedBuilderView(discord.ui.View): "inline": True if v["inline"].lower() == "true" else False }) - return await interaction.edit_original_response(embed=self.build_embed()) + return await self.response.edit(embed=self.build_embed()) @discord.ui.button(label="Remove Field", style=discord.ButtonStyle.red, row=1) async def remove_field(self, interaction: discord.Interaction, button: discord.ui.Button): - items = [ - discord.ui.TextInput( - label="Index", - placeholder="The number of fields to remove, e.g. 1", - style=discord.TextStyle.short - ) - ] - data = self.data.get(self.embed_type) if "fields" not in data: data["fields"] = [] @@ -319,7 +314,17 @@ class EmbedBuilderView(discord.ui.View): if len(data["fields"]) == 0: return await interaction.response.send_message("There are no fields to remove!", ephemeral=True) - modal = Modal(items, title="Remove Field") + modal = BaseModal( + title="Remove Field", + custom_id="remove_field", + items = [ + discord.ui.TextInput( + label="Index", + placeholder="The number of fields to remove, e.g. 1", + style=discord.TextStyle.short, + custom_id="index", + ) + ]) await interaction.response.send_modal(modal) await modal.wait() @@ -328,7 +333,7 @@ class EmbedBuilderView(discord.ui.View): except: return await interaction.followup.send("Can't found the field", ephemeral=True) - return await interaction.edit_original_response(embed=self.build_embed()) + return await self.response.edit(embed=self.build_embed()) @discord.ui.button(label="Apply", style=discord.ButtonStyle.green, row=1) async def apply(self, interaction: discord.Interaction, button: discord.ui.Button): diff --git a/voicelink/views/help.py b/voicelink/views/help.py index f24dc3a..5857c24 100644 --- a/voicelink/views/help.py +++ b/voicelink/views/help.py @@ -61,9 +61,6 @@ class HelpView(discord.ui.View): self.add_item(discord.ui.Button(label='Github', emoji=':github:1098265017268322406', url='https://github.com/ChocoMeow/Vocard')) self.add_item(discord.ui.Button(label='Donate', emoji=':patreon:913397909024800878', url='https://www.patreon.com/Vocard')) self.add_item(HelpDropdown(self.categories)) - - async def on_error(self, error, item, interaction) -> None: - return async def on_timeout(self) -> None: for child in self.children: diff --git a/voicelink/views/pagination.py b/voicelink/views/pagination.py index 3d131fd..fb5d193 100644 --- a/voicelink/views/pagination.py +++ b/voicelink/views/pagination.py @@ -73,9 +73,6 @@ class PaginationView(discord.ui.View): child.disabled = state.get("disabled", False) child.label = state.get("label") - async def on_error(self, error: Exception, item: discord.ui.Item, interaction: discord.Interaction) -> None: - return - async def update_message(self, interaction: discord.Interaction) -> None: """Update the view and edit the message.""" diff --git a/voicelink/views/search.py b/voicelink/views/search.py index 39e9023..f841943 100644 --- a/voicelink/views/search.py +++ b/voicelink/views/search.py @@ -54,9 +54,6 @@ class SearchView(discord.ui.View): self.values: list[str] = None self.add_item(SearchDropdown(tracks, texts)) - async def on_error(self, error, item, interaction): - return - async def on_timeout(self): for child in self.children: child.disabled = True