import aiohttp import discord from redbot.core import Config # # Misc. functions # def construct_embed(interaction: discord.Interaction, fields: list[discord.Embed.fields], color: str): embed = discord.Embed(title="Issue Request", color=color) for item in fields: title = item.label value = item.value if value is not None: if len(value) > 1024: words = value.split() split_value = [] current_part = "" for word in words: if len(current_part) + len(word) + 1 <= 1024: current_part += word + " " else: split_value.append(current_part.strip()) current_part = word + " " if current_part: split_value.append(current_part.strip()) for i, part in enumerate(split_value): embed.add_field( name=title if i == 0 else "\u200b", value=part, inline=False ) else: embed.add_field(name=title, value=value, inline=False) if interaction.user.discriminator == "0": username = interaction.user.name else: username = f"{interaction.user.name}#{interaction.user.discriminator}" embed.set_footer( text=f"Submitted by {username} ({interaction.user.id})", icon_url=interaction.user.display_avatar.url, ) return embed # # Modals for the core '/issues' command. # class BotBugModal(discord.ui.Modal, title="Creating issue..."): def __init__(self, color, cog_instance, original_interaction): super().__init__() self.color = color self.cog_instance = cog_instance self.original_interaction = original_interaction bug_description = discord.ui.TextInput( label="Describe the bug", placeholder="A clear and concise description of what the bug is.", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) reproduction_steps = discord.ui.TextInput( label="To Reproduce", placeholder="What caused the bug?", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) expected_behavior = discord.ui.TextInput( label="Expected Behavior", placeholder="A clear and concise description of what you expected to happen.", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) additional_context = discord.ui.TextInput( label="Additional Context", placeholder="Add any other context about the problem here.", style=discord.TextStyle.paragraph, required=False, max_length=2048 ) async def on_submit(self, interaction: discord.Interaction): fields = [self.bug_description, self.reproduction_steps, self.expected_behavior, self.additional_context] embed = construct_embed(interaction, fields, self.color) embed.set_author(name="Bot Bug Report") await self.cog_instance.submit_issue_request(interaction=interaction, original_interaction=self.original_interaction, embed=embed) class CogBugModal(discord.ui.Modal, title="Creating issue..."): def __init__(self, color, cog_instance, original_interaction): super().__init__() self.color = color self.cog_instance = cog_instance self.original_interaction = original_interaction cog_name = discord.ui.TextInput( label="What cog is causing this error?", placeholder="If unsure, put \"GalaxyCogs\".", style=discord.TextStyle.short, required=True, max_length=50 ) bug_description = discord.ui.TextInput( label="Describe the bug", placeholder="A clear and concise description of what the bug is.", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) reproduction_steps = discord.ui.TextInput( label="To Reproduce", placeholder="What caused the bug?", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) expected_behavior = discord.ui.TextInput( label="Expected Behavior", placeholder="A clear and concise description of what you expected to happen.", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) additional_context = discord.ui.TextInput( label="Additional Context", placeholder="Add any other context about the problem here.", style=discord.TextStyle.paragraph, required=False, max_length=2048 ) async def on_submit(self, interaction: discord.Interaction): fields = [self.cog_name, self.bug_description, self.reproduction_steps, self.expected_behavior, self.additional_context] embed = construct_embed(interaction, fields, self.color) embed.set_author(name="Cog Bug Report") await self.cog_instance.submit_issue_request(interaction=interaction, original_interaction=self.original_interaction, embed=embed) class BotSuggestionModal(discord.ui.Modal, title="Creating issue..."): def __init__(self, color, cog_instance, original_interaction): super().__init__() self.color = color self.cog_instance = cog_instance self.original_interaction = original_interaction suggestion_description = discord.ui.TextInput( label="Describe your suggestion.", placeholder="A clear and concise description of what your suggestion is.", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) alternatives = discord.ui.TextInput( label="Describe alternatives you've considered.", placeholder="A clear and concise description of any alternative solutions or features you've cnosidered.", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) additional_context = discord.ui.TextInput( label="Additional Context", placeholder="Add any other context about your suggestion here.", style=discord.TextStyle.paragraph, required=False, max_length=2048 ) async def on_submit(self, interaction: discord.Interaction): fields = [self.suggestion_description, self.alternatives, self.additional_context] embed = construct_embed(interaction, fields, self.color) embed.set_author(name="Cog Suggestion") await self.cog_instance.submit_issue_request(interaction=interaction, original_interaction=self.original_interaction, embed=embed) class CogSuggestionModal(discord.ui.Modal, title="Creating issue..."): def __init__(self, color, cog_instance, original_interaction): super().__init__() self.color = color self.cog_instance = cog_instance self.original_interaction = original_interaction cog_name = discord.ui.TextInput( label="What cog is your suggestion for?", placeholder="If unsure, put \"GalaxyCogs\".", style=discord.TextStyle.short, required=True, max_length=50 ) suggestion_description = discord.ui.TextInput( label="Describe your suggestion.", placeholder="A clear and concise description of what your suggestion is.", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) alternatives = discord.ui.TextInput( label="Describe alternatives you've considered.", placeholder="A clear and concise description of any alternative solutions or features you've cnosidered.", style=discord.TextStyle.paragraph, required=True, max_length=2048 ) additional_context = discord.ui.TextInput( label="Additional Context", placeholder="Add any other context about your suggestion here.", style=discord.TextStyle.paragraph, required=False, max_length=2048 ) async def on_submit(self, interaction: discord.Interaction): fields = [self.cog_name, self.suggestion_description, self.alternatives, self.additional_context] embed = construct_embed(interaction, fields, self.color) embed.set_author(name="Cog Suggestion") await self.cog_instance.submit_issue_request(interaction=interaction, original_interaction=self.original_interaction, embed=embed) # # Response modal # class IssueResponseModal(discord.ui.Modal, title="Sending response message..."): def __init__(self, channel, message_id, user, approved): super().__init__() self.channel = channel self.message_id = message_id self.user = user self.approved = approved self.config = Config.get_conf(None, cog_name="Issues", identifier=4285273314713) issue_title_input = discord.ui.TextInput( label="Title", placeholder="Only use this if you're accepting the issue request.", style=discord.TextStyle.short, required=False, max_length=200, ) response = discord.ui.TextInput( label="Response", placeholder="", style=discord.TextStyle.paragraph, required=False, max_length=1024, ) async def on_submit(self, interaction: discord.Interaction): message: discord.Message = await self.channel.fetch_message(self.message_id) embed = message.embeds[0] field_values = [] field_names = [] if self.approved: embed.color = 1226519 embed.title = "Issue Request Approved" status = "accepted" else: embed.color = 15671552 embed.title = "Issue Request Denied" status = "denied" await interaction.response.send_message(content=f"Issue request {status}.") if self.response.value is not None: embed.add_field( name=f"Response from {interaction.user.name}", value=self.response.value, inline=False, ) if self.approved: for field in embed.fields: field_names.append(f"**{field.name}**") field_values.append(field.value) if self.issue_title_input.value != "": _issue_title = self.issue_title_input.value else: _issue_title = "Automatically generated issue" if embed.author.name == "Bot Bug Report": issue_title = f"[BOT BUG] {_issue_title}" desired_labels = ["bot", "bug"] elif embed.author.name == "Bot Suggestion": issue_title = f"[BOT SUGGESTION] {_issue_title}" desired_labels = ["bot", "enhancement"] elif embed.author.name == "Cog Bug Report": issue_title = f"[{field_values[0]}] {_issue_title}" desired_labels = ["cog", "bug"] elif embed.author.name == "Cog Suggestion": issue_title = f"[{field_values[0]}] {_issue_title}" desired_labels = ["cog", "enhancement"] headers = { "Authorization": f"Bearer {await self.config.gitea_token()}", "Accept": "application/json", } url = f"{await self.config.gitea_root_url()}/api/v1/repos/{await self.config.gitea_repository_owner()}/{await self.config.gitea_repository()}/issues" issue_body = "\n".join( [f"{name}\n{value}" for name, value in zip(field_names, field_values)] ) async def fetch_labels(): async with aiohttp.ClientSession(headers=headers) as session: async with session.post(url=f"{await self.config.gitea_root_url()}/api/v1/repos/{await self.config.gitea_repository_owner()}/{await self.config.gitea_repository()}/labels") as response: label_list = [] for label in response.json(): if label["name"] in desired_labels: label_list.append(label["id"]) if label_list is None: print("Error! Labels are not properly configured on the target repository.") return await label_list issue_labels = await fetch_labels() if issue_labels is None: issue_data = {"title": issue_title, "body": issue_body} else: issue_data = {"title": issue_title, "body": issue_body, "labels": issue_labels} async def create_issue(): async with aiohttp.ClientSession(headers=headers) as session: async with session.post(url, json=issue_data) as response: return await response.json(), response.status response_json, status_code = await create_issue() if status_code == 201: await interaction.response.send_message( content=f"Issue request {status}.\n[Issue successfully created.]({response_json.get('html_url')})", ephemeral=True, ) embed.url = response_json.get("html_url") await message.edit(embed=embed, view=None) await self.user.send(embed=embed) # # Configuration modal # class IssuesConfigurationModal(discord.ui.Modal, title="Modifying configuration..."): def __init__(self, config, ctx): super().__init__() self.config = config self.ctx = ctx gitea_root_url = discord.ui.TextInput( label="Gitea Root URL", placeholder="https://try.gitea.io", style=discord.TextStyle.short, required=False, max_length=200 ) gitea_repository_owner = discord.ui.TextInput( label="Gitea Repository Owner", placeholder="foo", style=discord.TextStyle.short, required=False, max_length=200 ) gitea_repository = discord.ui.TextInput( label="Gitea Repository Name", placeholder="bar", style=discord.TextStyle.short, required=False, max_length=200 ) gitea_token = discord.ui.TextInput( label="Gitea User Access Token", placeholder="Generate one from your user settings page.", style=discord.TextStyle.short, required=False, max_length=200 ) async def on_submit(self, interaction: discord.Interaction): if self.gitea_token.value != "": await self.config.gitea_token.set(self.gitea_token.value) if self.gitea_root_url.value != "": await self.config.gitea_root_url.set(self.gitea_root_url.value) if self.gitea_repository_owner.value != "": await self.config.gitea_repository_owner.set(self.gitea_repository_owner.value) if self.gitea_repository.value != "": await self.config.gitea_repository.set(self.gitea_repository.value) await interaction.response.send_message(content="Configuration changed!")