Ruby-Cogs/appeals/db/tables.py
Valerie 477974d53c
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
Upload 2 Cogs & Update README
2025-05-23 01:30:53 -04:00

137 lines
5.6 KiB
Python

import typing as t
import discord
import orjson
from piccolo.columns import (
JSON,
Array,
BigInt,
Boolean,
ForeignKey,
Integer,
Serial,
Text,
Timestamptz,
)
from piccolo.columns.defaults.timestamptz import TimestamptzNow
from piccolo.table import Table, sort_table_classes
from redbot.core.bot import Red
class AppealGuild(Table):
id = BigInt(primary_key=True, index=True) # Guild ID
created_at = Timestamptz()
# Settings
target_guild_id = BigInt() # Target guild to be unbanned from
appeal_channel = BigInt() # Channel where the appeal message is located
appeal_message = BigInt() # Message ID of the appeal message with appeal button
pending_channel = BigInt() # Channel where pending appeals are stored
approved_channel = BigInt() # Channel where approved appeals are stored
denied_channel = BigInt() # Channel where denied appeals are stored
alert_roles = Array(BigInt()) # Roles to alert when a new appeal is submitted
alert_channel = BigInt() # Channel to alert when a new appeal is submitted
appeal_limit = Integer(default=1) # Maximum number of times a user can submit an appeal
# Appeal button
button_style = Text(default="primary") # can be `primary`, `secondary`, `success`, `danger`
button_label = Text(default="Submit Appeal")
button_emoji = Text(default=None, null=True) # int or string
def get_emoji(self, bot: Red) -> str | discord.Emoji | discord.PartialEmoji | None:
if not self.button_emoji:
return None
if self.button_emoji.isdigit():
return bot.get_emoji(int(self.button_emoji))
return self.button_emoji
class AppealQuestion(Table):
id: Serial
created_at = Timestamptz()
updated_on = Timestamptz(auto_update=TimestamptzNow().python)
guild = ForeignKey(references=AppealGuild, required=True)
question = Text(required=True) # Up to 256 characters
# menu based setup only
sort_order = Integer(default=0)
# Modal specific (menu based setup only)
required = Boolean(default=True)
default = Text(default=None, null=True)
placeholder = Text(default=None, null=True)
max_length = BigInt(default=None, null=True)
min_length = BigInt(default=None, null=True) # Up to 1024 characters
style = Text(default="long") # can be `short` or `long`
# Button specific (button based setup only)
button_style = Text(default="primary") # can be `primary`, `secondary`, `success`, `danger`
def embed(self, color: discord.Color = None) -> discord.Embed:
style_emojis = {
"danger": "🔴",
"success": "🟢",
"primary": "🔵",
"secondary": "",
}
embed = discord.Embed(title=f"Question ID: {self.id}", description=self.question, color=color)
embed.add_field(name="Sort Order", value=self.sort_order)
embed.add_field(name="Required", value="Yes" if self.required else "No")
embed.add_field(name="Modal Style", value=self.style)
embed.add_field(
name="Button Style",
value=self.button_style + style_emojis[self.button_style],
)
embed.add_field(name="Placeholder", value=self.placeholder or "Not set")
embed.add_field(name="Default Answer", value=self.default or "Not set")
embed.add_field(name="Max Length", value=self.max_length or "Not set")
embed.add_field(name="Min Length", value=self.min_length or "Not set")
return embed
def created(self, type: t.Literal["t", "T", "d", "D", "f", "F", "R"]) -> str:
return f"<t:{int(self.created_at.timestamp())}:{type}>"
def modified(self, type: t.Literal["t", "T", "d", "D", "f", "F", "R"]) -> str:
return f"<t:{int(self.updated_on.timestamp())}:{type}>"
class AppealSubmission(Table):
id: Serial
created_at = Timestamptz()
guild = ForeignKey(references=AppealGuild)
user_id = BigInt() # Person who submitted the appeal
answers = JSON() # {question: answer}
status = Text(default="pending") # can be `pending`, `approved`, `denied`
message_id = BigInt() # Message ID of the submission message
reason = Text() # Reason for denial
def created(self, type: t.Literal["t", "T", "d", "D", "f", "F", "R"]) -> str:
return f"<t:{int(self.created_at.timestamp())}:{type}>"
def embed(self, user: discord.Member | discord.User = None) -> discord.Embed:
colors = {
"pending": discord.Color.blurple(),
"approved": discord.Color.green(),
"denied": discord.Color.red(),
}
if user:
desc = f"Submitted by **{user.name}** ({user.id})\nMention: {user.mention}"
else:
desc = f"Submitted by **Unknown** ({self.user_id})"
embed = discord.Embed(
description=desc,
color=colors[self.status],
timestamp=self.created_at,
)
embed.set_author(
name=f"{self.status.capitalize()} Submission",
icon_url=user.display_avatar if user else None,
)
embed.set_footer(text=f"Submission ID: {self.id}")
answers = orjson.loads(self.answers) if isinstance(self.answers, str) else self.answers
for question, answer in answers.items():
embed.add_field(name=question, value=answer, inline=False)
if self.reason and self.status == "denied":
embed.add_field(name="Reason for Denial", value=self.reason)
elif self.reason and self.status == "approved":
embed.add_field(name="Reason for Approval", value=self.reason)
return embed
TABLES: list[Table] = sort_table_classes([AppealGuild, AppealQuestion, AppealSubmission])