-
-
Notifications
You must be signed in to change notification settings - Fork 35
Suggestion Command Draft PR #869
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
base: main
Are you sure you want to change the base?
Changes from all commits
9b9e38c
7ab2de5
3007f7a
b35c714
e62938e
3d2c45e
c6167a9
6d0cd26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import discord | ||
from discord import app_commands | ||
from discord.ext import commands | ||
|
||
from tux.ui.modals.suggestion import SuggestionModal | ||
|
||
|
||
class Suggestion(commands.Cog): | ||
def __init__(self, bot: commands.Bot) -> None: | ||
self.bot = bot | ||
|
||
@app_commands.command(name="suggest") | ||
@app_commands.guild_only() | ||
async def suggestion(self, interaction: discord.Interaction) -> None: | ||
""" | ||
Submit a suggestion for a server | ||
|
||
Parameters | ||
---------- | ||
interaction : discord.Interaction | ||
The interaction that triggered the command. | ||
""" | ||
|
||
modal = SuggestionModal(bot=self.bot) | ||
|
||
await interaction.response.send_modal(modal) | ||
|
||
|
||
async def setup(bot: commands.Bot) -> None: | ||
await bot.add_cog(Suggestion(bot)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ async def get_log_channel(self, guild_id: int, log_type: str) -> int | None: | |
"join": "join_log_id", | ||
"private": "private_log_id", | ||
"report": "report_log_id", | ||
"suggestion": "suggestion_log_id", | ||
"dev": "dev_log_id", | ||
} | ||
return await self.get_guild_config_field_value(guild_id, log_channel_ids[log_type]) | ||
|
@@ -116,7 +117,10 @@ async def get_private_log_id(self, guild_id: int) -> int | None: | |
async def get_report_log_id(self, guild_id: int) -> int | None: | ||
return await self.get_guild_config_field_value(guild_id, "report_log_id") | ||
|
||
async def get_dev_log_id(self, guild_id: int) -> int | None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this rename necessary? |
||
async def get_suggestion_log_channel(self, guild_id: int) -> int | None: | ||
return await self.get_guild_config_field_value(guild_id, "suggestion_log_id") | ||
|
||
async def get_dev_log_channel(self, guild_id: int) -> int | None: | ||
return await self.get_guild_config_field_value(guild_id, "dev_log_id") | ||
|
||
async def get_jail_channel_id(self, guild_id: int) -> int | None: | ||
|
@@ -256,6 +260,23 @@ async def update_report_log_id( | |
}, | ||
) | ||
|
||
async def update_suggestion_log_id( | ||
self, | ||
guild_id: int, | ||
suggestion_log_id: int, | ||
) -> GuildConfig | None: | ||
await self.ensure_guild_exists(guild_id) | ||
return await self.table.upsert( | ||
where={"guild_id": guild_id}, | ||
data={ | ||
"create": { | ||
"guild_id": guild_id, | ||
"suggestion_log_id": suggestion_log_id, | ||
}, | ||
"update": {"suggestion_log_id": suggestion_log_id}, | ||
}, | ||
) | ||
|
||
async def update_dev_log_id( | ||
self, | ||
guild_id: int, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from prisma.enums import SuggestionStatus | ||
from prisma.models import Guild, Suggestion | ||
from tux.database.client import db | ||
|
||
|
||
class SuggestionController: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This controller should probably have a method for retrieving suggestions and maybe even searching them. |
||
def __init__(self) -> None: | ||
self.table = db.suggestion | ||
self.guild_table = db.guild | ||
|
||
async def ensure_guild_exists(self, guild_id: int) -> Guild: | ||
""" | ||
Ensure a guild exists in the database and return the found or created object. | ||
|
||
Parameters | ||
---------- | ||
guild_id : int | ||
The ID of the guild to ensure exists. | ||
|
||
Returns | ||
------- | ||
Guild | ||
The guild database object. | ||
""" | ||
guild = await self.guild_table.find_first(where={"guild_id": guild_id}) | ||
if guild is None: | ||
return await self.guild_table.create(data={"guild_id": guild_id}) | ||
return guild | ||
|
||
async def create_suggestion( | ||
self, | ||
suggestion_title: str, | ||
suggestion_description: str, | ||
suggestion_status: SuggestionStatus, | ||
suggestion_user_id: int, | ||
guild_id: int, | ||
) -> Suggestion: | ||
await self.ensure_guild_exists(guild_id) | ||
|
||
return await self.table.create( | ||
data={ | ||
"suggestion_title": suggestion_title, | ||
"suggestion_description": suggestion_description, | ||
"suggestion_status": suggestion_status, | ||
"suggestion_user_id": suggestion_user_id, | ||
"guild_id": guild_id, | ||
}, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import discord | ||
from discord.ext import commands | ||
from loguru import logger | ||
|
||
from tux.database.controllers import DatabaseController | ||
from tux.utils import checks | ||
from tux.utils.constants import Constants as CONST | ||
from tux.utils.embeds import create_embed_footer | ||
|
||
|
||
class ButtonView(discord.ui.View): | ||
def __init__(self, embed: discord.Embed): | ||
super().__init__(timeout=None) | ||
self.embed = embed | ||
|
||
@discord.ui.button(label="Accept", style=discord.ButtonStyle.green, custom_id="accept_suggestion_button") | ||
@checks.has_pl(5) | ||
async def green_button(self, interaction: discord.Interaction, button: discord.ui.Button[discord.ui.View]): | ||
self.embed.set_author(name="Suggestion Status: Accepted") | ||
self.embed.add_field(name="Reviewed by", value=interaction.user.name, inline=False) | ||
self.embed.color = discord.Color.green() | ||
await interaction.response.edit_message(embed=self.embed, view=self) | ||
|
||
@discord.ui.button(label="Deny", style=discord.ButtonStyle.red, custom_id="deny_suggestion_button") | ||
@checks.has_pl(5) | ||
async def red_button(self, interaction: discord.Interaction, button: discord.ui.Button[discord.ui.View]): | ||
self.embed.set_author(name="Suggestion Status: Rejected") | ||
self.embed.add_field(name="Reviewed by", value=interaction.user.name, inline=False) | ||
self.embed.color = discord.Color.red() | ||
await interaction.response.edit_message(embed=self.embed, view=self) | ||
|
||
|
||
class SuggestionModal(discord.ui.Modal): | ||
def __init__(self, *, title: str = "Submit a suggestion", bot: commands.Bot) -> None: | ||
super().__init__(title=title) | ||
self.bot = bot | ||
self.config = DatabaseController().guild_config | ||
|
||
suggestion_title = discord.ui.TextInput( # type: ignore | ||
label="Suggestion Summary", | ||
style=discord.TextStyle.short, | ||
required=True, | ||
max_length=100, | ||
placeholder="Summarise your suggestion briefly", | ||
) | ||
|
||
suggestion_description = discord.ui.TextInput( # type: ignore | ||
style=discord.TextStyle.long, | ||
label="Suggestion Description", | ||
required=True, | ||
max_length=4000, | ||
placeholder="Please provide as much detail as possible on your suggestion", | ||
) | ||
|
||
async def on_submit(self, interaction: discord.Interaction) -> None: | ||
""" | ||
Sends the suggestion to dedicated channel. | ||
|
||
Parameters | ||
---------- | ||
interaction : discord.Interaction | ||
The interaction that triggered the command. | ||
""" | ||
|
||
if not interaction.guild: | ||
logger.error("Guild is None") | ||
return | ||
|
||
embed = discord.Embed( | ||
title=self.suggestion_title.value, # type: ignore | ||
description=self.suggestion_description.value, # type: ignore | ||
color=CONST.EMBED_COLORS["DEFAULT"], | ||
) | ||
embed.set_author(name="Suggestion Status: Under Review") | ||
""" Commented until I finish the rest of the code | ||
embed.add_field( | ||
name="Review Info", | ||
value="No review has been submitted", | ||
inline=False, | ||
) | ||
""" | ||
footer_text, footer_icon_url = create_embed_footer(interaction=interaction) | ||
embed.set_footer(text=footer_text, icon_url=footer_icon_url) | ||
|
||
try: | ||
suggestion_log_channel_id = await self.config.get_suggestion_log_channel(interaction.guild.id) | ||
except Exception as e: | ||
logger.error(f"Failed to get suggestion log channel for guild {interaction.guild.id}. {e}") | ||
await interaction.response.send_message( | ||
"Failed to submit your suggestion. Please try again later.", | ||
ephemeral=True, | ||
delete_after=30, | ||
) | ||
return | ||
|
||
if not suggestion_log_channel_id: | ||
logger.error(f"Suggestion log channel not set for guild {interaction.guild.id}") | ||
await interaction.response.send_message( | ||
"The suggestion channel has not been set up. Please contact an administrator.", | ||
ephemeral=True, | ||
delete_after=30, | ||
) | ||
return | ||
|
||
suggestion_log_channel = interaction.guild.get_channel(suggestion_log_channel_id) | ||
if not suggestion_log_channel or not isinstance(suggestion_log_channel, discord.TextChannel): | ||
logger.error(f"Failed to get suggestion log channel for guild {interaction.guild.id}") | ||
await interaction.response.send_message( | ||
"Failed to submit your suggestion. Please try again later.", | ||
ephemeral=True, | ||
delete_after=30, | ||
) | ||
return | ||
|
||
await interaction.response.send_message( | ||
"Your suggestion has been submitted.", | ||
ephemeral=True, | ||
delete_after=30, | ||
) | ||
|
||
view = ButtonView(embed=embed) | ||
message = await suggestion_log_channel.send(embed=embed, view=view) | ||
|
||
await suggestion_log_channel.create_thread( | ||
name=f"Suggestion: {self.suggestion_title.value}", # type: ignore | ||
message=message, | ||
auto_archive_duration=1440, | ||
) | ||
|
||
reactions = ["👍", "👎"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this to constants file, or ideally make configurable. |
||
for reaction in reactions: | ||
await message.add_reaction(reaction) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this need to be in bot.py?