From 58b0c2d2213b63821ef0c44fe154874498368370 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Mon, 20 Oct 2025 22:00:19 -0500 Subject: [PATCH] CLAUDE: Add multi-image support for charts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Image Version: 2.0.3 Added ability to add charts with multiple images and manage them incrementally. Service Layer Changes (chart_service.py): - Added add_image_to_chart() method to append URLs to existing charts - Added remove_image_from_chart() method to remove specific URLs - Validation to prevent duplicate URLs in charts - Protection against removing the last image from a chart Command Layer Changes (charts.py): - Modified /chart-manage add to accept comma-separated URLs - Parse and strip URLs from comma-delimited string - Shows image count in success message - Displays first image in response embed - Added /chart-manage add-image command for incremental additions - Added /chart-manage remove-image command to remove specific URLs - All commands use chart autocomplete for easy selection - Admin/Help Editor permission checks on all management commands Usage Examples: # Add chart with multiple images in one command /chart-manage add defense "Defense Chart" gameplay "https://example.com/def1.png, https://example.com/def2.png" # Add additional images later /chart-manage add-image defense https://example.com/def3.png # Remove specific image /chart-manage remove-image defense https://example.com/def2.png 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- commands/utilities/charts.py | 114 ++++++++++++++++++++++++++++++++++- services/chart_service.py | 52 ++++++++++++++++ 2 files changed, 163 insertions(+), 3 deletions(-) diff --git a/commands/utilities/charts.py b/commands/utilities/charts.py index 2ff8bf5..9e144f9 100644 --- a/commands/utilities/charts.py +++ b/commands/utilities/charts.py @@ -236,7 +236,7 @@ class ChartManageGroup(app_commands.Group): key="Unique identifier for the chart (e.g., 'rest', 'sac-bunt')", name="Display name for the chart", category="Category key (use autocomplete)", - url="Image URL for the chart", + url="Image URL(s) - comma-separated for multiple images", description="Optional description of the chart" ) @app_commands.autocomplete(category=category_autocomplete) @@ -274,12 +274,18 @@ class ChartManageGroup(app_commands.Group): f"Invalid category. Must be one of: {', '.join(valid_categories)}" ) + # Parse comma-separated URLs + urls = [u.strip() for u in url.split(',') if u.strip()] + + if not urls: + raise BotException("At least one valid URL is required") + # Add chart (service will handle duplicate key check) self.chart_service.add_chart( key=key, name=name, category=category, - urls=[url], + urls=urls, description=description or "" ) @@ -290,7 +296,8 @@ class ChartManageGroup(app_commands.Group): ) embed.add_field(name="Key", value=key, inline=True) embed.add_field(name="Category", value=category, inline=True) - embed.set_image(url=url) + embed.add_field(name="Images", value=str(len(urls)), inline=True) + embed.set_image(url=urls[0]) await interaction.response.send_message(embed=embed, ephemeral=True) @@ -461,6 +468,107 @@ class ChartManageGroup(app_commands.Group): await interaction.response.send_message(embed=embed, ephemeral=True) + @app_commands.command( + name="add-image", + description="Add an additional image to an existing chart" + ) + @app_commands.describe( + key="Chart key to add image to", + url="Image URL to add" + ) + @app_commands.autocomplete(key=chart_autocomplete) + @logged_command("/chart-manage add-image") + async def add_image( + self, + interaction: discord.Interaction, + key: str, + url: str + ): + """Add an additional image URL to an existing chart.""" + # Check permissions + if not has_manage_permission(interaction): + embed = EmbedTemplate.error( + title="Permission Denied", + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can manage charts." + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + return + + set_discord_context( + interaction=interaction, + command="/chart-manage add-image", + chart_key=key + ) + + # Add image to chart + self.chart_service.add_image_to_chart(key, url) + + # Get updated chart + chart = self.chart_service.get_chart(key) + if chart is None: + raise BotException(f"Chart '{key}' not found after adding image") + + # Success response + embed = EmbedTemplate.success( + title="Image Added to Chart", + description=f"Successfully added image to chart '{chart.name}'" + ) + embed.add_field(name="Chart Key", value=key, inline=True) + embed.add_field(name="Total Images", value=str(len(chart.urls)), inline=True) + embed.set_image(url=url) + + await interaction.response.send_message(embed=embed, ephemeral=True) + + @app_commands.command( + name="remove-image", + description="Remove an image from a multi-image chart" + ) + @app_commands.describe( + key="Chart key to remove image from", + url="Image URL to remove" + ) + @app_commands.autocomplete(key=chart_autocomplete) + @logged_command("/chart-manage remove-image") + async def remove_image( + self, + interaction: discord.Interaction, + key: str, + url: str + ): + """Remove an image URL from an existing chart.""" + # Check permissions + if not has_manage_permission(interaction): + embed = EmbedTemplate.error( + title="Permission Denied", + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can manage charts." + ) + await interaction.response.send_message(embed=embed, ephemeral=True) + return + + set_discord_context( + interaction=interaction, + command="/chart-manage remove-image", + chart_key=key + ) + + # Remove image from chart + self.chart_service.remove_image_from_chart(key, url) + + # Get updated chart + chart = self.chart_service.get_chart(key) + if chart is None: + raise BotException(f"Chart '{key}' not found after removing image") + + # Success response + embed = EmbedTemplate.success( + title="Image Removed from Chart", + description=f"Successfully removed image from chart '{chart.name}'" + ) + embed.add_field(name="Chart Key", value=key, inline=True) + embed.add_field(name="Remaining Images", value=str(len(chart.urls)), inline=True) + + await interaction.response.send_message(embed=embed, ephemeral=True) + class ChartCategoryGroup(app_commands.Group): """Chart category management commands for administrators and help editors.""" diff --git a/services/chart_service.py b/services/chart_service.py index e3f4855..19f30a9 100644 --- a/services/chart_service.py +++ b/services/chart_service.py @@ -235,6 +235,58 @@ class ChartService: self._save_charts() logger.info(f"Removed chart: {key}") + def add_image_to_chart(self, key: str, url: str) -> None: + """ + Add an additional image URL to an existing chart. + + Args: + key: Chart key to add image to + url: Image URL to append + + Raises: + BotException: If chart doesn't exist or URL already exists + """ + if key not in self._charts: + raise BotException(f"Chart '{key}' not found") + + chart = self._charts[key] + + # Check if URL already exists + if url in chart.urls: + raise BotException(f"Image URL already exists in chart '{key}'") + + chart.urls.append(url) + self._save_charts() + logger.info(f"Added image to chart: {key} (now {len(chart.urls)} images)") + + def remove_image_from_chart(self, key: str, url: str) -> None: + """ + Remove an image URL from an existing chart. + + Args: + key: Chart key to remove image from + url: Image URL to remove + + Raises: + BotException: If chart doesn't exist, URL not found, or would leave chart with no images + """ + if key not in self._charts: + raise BotException(f"Chart '{key}' not found") + + chart = self._charts[key] + + # Check if URL exists + if url not in chart.urls: + raise BotException(f"Image URL not found in chart '{key}'") + + # Don't allow removing the last image + if len(chart.urls) <= 1: + raise BotException(f"Cannot remove the last image from chart '{key}'") + + chart.urls.remove(url) + self._save_charts() + logger.info(f"Removed image from chart: {key} (now {len(chart.urls)} images)") + def add_category(self, key: str, display_name: str) -> None: """ Add a new category.