Première version non terminer du nouveau système de ticket § ajout du système de boutons persistant (non tester)
This commit is contained in:
parent
10173132a2
commit
93e2f9bb44
15 changed files with 3788 additions and 209 deletions
661
commandes/ticket/__init__.py
Normal file
661
commandes/ticket/__init__.py
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
"""
|
||||
Modular Ticket System
|
||||
=====================
|
||||
A complete, beautiful, and modular ticket system for Discord bots.
|
||||
|
||||
Features:
|
||||
- Multiple ticket categories with custom settings
|
||||
- Priority levels (Low/Normal/High/Urgent)
|
||||
- Claim system for staff
|
||||
- Transcript generation (TXT, JSON, HTML)
|
||||
- Satisfaction surveys
|
||||
- Statistics and reporting
|
||||
- Bulk management
|
||||
- Auto-close inactive tickets
|
||||
- Rich embeds and beautiful UI
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
import discord
|
||||
from discord.ext import commands, tasks
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from .data.storage import get_storage
|
||||
from .data.models import GuildTicketConfig, TicketCategory, TicketStatus, Priority
|
||||
from .utils.formatters import TicketEmbedFormatter
|
||||
from .utils.transcript import get_transcript_generator
|
||||
from .views.creation import (
|
||||
TicketCreationView,
|
||||
TicketManagementView,
|
||||
SurveyView,
|
||||
)
|
||||
from .views.admin import (
|
||||
AdminPanelView,
|
||||
ConfigPanelView,
|
||||
RolesManagementView,
|
||||
)
|
||||
|
||||
|
||||
class Ticket(commands.Cog):
|
||||
"""
|
||||
Main ticket cog with all functionality.
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.storage = get_storage()
|
||||
self._register_views.start()
|
||||
self._auto_close_check.start()
|
||||
|
||||
def cog_unload(self):
|
||||
self._register_views.cancel()
|
||||
self._auto_close_check.cancel()
|
||||
|
||||
async def cog_load(self):
|
||||
"""Register persistent views when cog is loaded"""
|
||||
await self._register_all_views()
|
||||
|
||||
async def _register_all_views(self):
|
||||
"""Register all persistent views for all configured guilds"""
|
||||
try:
|
||||
for guild_id in self.storage.get_all_guild_ids():
|
||||
try:
|
||||
config = self.storage.load_config(guild_id)
|
||||
if config and config.enabled:
|
||||
# Register creation view (with categories)
|
||||
creation_view = TicketCreationView(self.bot, config)
|
||||
self.bot.add_view(creation_view)
|
||||
|
||||
# Register management view (no config needed for persistence)
|
||||
management_view = TicketManagementView(self.bot, self.storage, config)
|
||||
self.bot.add_view(management_view)
|
||||
|
||||
# Register admin views
|
||||
admin_view = AdminPanelView(self.bot, config)
|
||||
self.bot.add_view(admin_view)
|
||||
|
||||
config_view = ConfigPanelView(self.bot, config)
|
||||
self.bot.add_view(config_view)
|
||||
|
||||
roles_view = RolesManagementView(self.bot, config)
|
||||
self.bot.add_view(roles_view)
|
||||
|
||||
print(f"[Ticket] Views registered for guild {guild_id}")
|
||||
except Exception as e:
|
||||
print(f"[Ticket] Error registering views for guild {guild_id}: {e}")
|
||||
except Exception as e:
|
||||
print(f"[Ticket] Error in _register_all_views: {e}")
|
||||
|
||||
@tasks.loop(seconds=30)
|
||||
async def _register_views(self):
|
||||
"""Register persistent views periodically"""
|
||||
await self._register_all_views()
|
||||
|
||||
@tasks.loop(minutes=5)
|
||||
async def _auto_close_check(self):
|
||||
"""Check for tickets to auto-close"""
|
||||
try:
|
||||
for guild_id in self.storage.get_all_guild_ids():
|
||||
config = self.storage.load_config(guild_id)
|
||||
if not config.auto_close_enabled:
|
||||
continue
|
||||
|
||||
auto_close_days = config.auto_close_days or 7
|
||||
auto_close_threshold = datetime.now().timestamp() - (auto_close_days * 86400)
|
||||
|
||||
tickets = self.storage.get_guild_tickets(guild_id, TicketStatus.OPEN)
|
||||
for ticket in tickets:
|
||||
if ticket.created_at.timestamp() < auto_close_threshold:
|
||||
# Auto-close ticket
|
||||
ticket.status = TicketStatus.CLOSED
|
||||
ticket.closed_at = datetime.now()
|
||||
self.storage.save_ticket(ticket)
|
||||
|
||||
# Log if configured
|
||||
if config.log_channel_id:
|
||||
log_channel = self.bot.get_guild(guild_id).get_channel(config.log_channel_id)
|
||||
if log_channel:
|
||||
await log_channel.send(
|
||||
f"🔒 Ticket auto-fermé (inactif depuis {auto_close_days} jours)"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"[Ticket] Auto-close error: {e}")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
print("🎫 Module Ticket chargé avec succès!")
|
||||
|
||||
# --- Main Commands ---
|
||||
|
||||
@commands.hybrid_group(name="ticket", aliases=["tickets", "ticketing"])
|
||||
async def ticket_group(self, ctx):
|
||||
"""Système de tickets complet"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
# --- Setup Commands ---
|
||||
|
||||
@ticket_group.command(name="setup", aliases=["configurer"])
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def ticket_setup(self, ctx):
|
||||
"""Configurer le système de tickets"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
config = self.storage.load_config(ctx.guild.id)
|
||||
|
||||
# Send setup panel
|
||||
embed = discord.Embed(
|
||||
title="⚙️ Configuration des Tickets",
|
||||
description="Bienvenue dans le panneau de configuration des tickets!",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
embed.add_field(name="📁 Catégories", value=str(len(config.categories)), inline=True)
|
||||
embed.add_field(name="👥 Rôles Staff", value=str(len(config.staff_roles)), inline=True)
|
||||
embed.add_field(name="📊 Canal Logs", value=f"<#{config.log_channel_id}>" if config.log_channel_id else "Non configuré", inline=True)
|
||||
|
||||
view = TicketSetupView(self.bot, self.storage, config)
|
||||
await ctx.send(embed=embed, view=view, ephemeral=True)
|
||||
|
||||
@ticket_group.command(name="panel", aliases=["panneau"])
|
||||
async def ticket_panel(self, ctx, channel: discord.TextChannel = None):
|
||||
"""Créer un panneau de création de tickets"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
config = self.storage.load_config(ctx.guild.id)
|
||||
|
||||
if not config.enabled:
|
||||
await ctx.send("Le système de tickets est désactivé.", ephemeral=True)
|
||||
return
|
||||
|
||||
if not config.categories:
|
||||
await ctx.send("Aucune catégorie configurée. Utilisez `/ticket setup` d'abord.", ephemeral=True)
|
||||
return
|
||||
|
||||
target = channel or ctx.channel
|
||||
|
||||
embed = TicketEmbedFormatter.panel_embed(config, ctx.guild.name)
|
||||
view = TicketCreationView(self.bot, config)
|
||||
|
||||
await target.send(embed=embed, view=view)
|
||||
await ctx.send(f"Panneau envoyé dans {target.mention}", ephemeral=True)
|
||||
|
||||
# --- Info Commands ---
|
||||
|
||||
@ticket_group.command(name="info", aliases=["informations"])
|
||||
async def ticket_info(self, ctx, channel: discord.TextChannel = None):
|
||||
"""Voir les informations d'un ticket"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
target = channel or ctx.channel
|
||||
|
||||
storage = get_storage()
|
||||
ticket = storage.load_ticket(target.id)
|
||||
|
||||
if not ticket:
|
||||
await ctx.send("Ce salon n'est pas un ticket.", ephemeral=True)
|
||||
return
|
||||
|
||||
user = self.bot.get_user(ticket.user_id)
|
||||
category = storage.load_config(ctx.guild.id).get_category(ticket.category_id)
|
||||
|
||||
embed = TicketEmbedFormatter.ticket_info_embed(ticket, user, category)
|
||||
|
||||
await ctx.send(embed=embed, ephemeral=True)
|
||||
|
||||
@ticket_group.command(name="list", aliases=["liste"])
|
||||
async def ticket_list(self, ctx, user: discord.Member = None):
|
||||
"""Liste des tickets"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
storage = get_storage()
|
||||
|
||||
if user:
|
||||
tickets = storage.get_user_tickets(ctx.guild.id, user.id, open_only=False)
|
||||
else:
|
||||
tickets = storage.get_guild_tickets(ctx.guild.id)
|
||||
|
||||
if not tickets:
|
||||
await ctx.send("Aucun ticket trouvé.", ephemeral=True)
|
||||
return
|
||||
|
||||
embed = TicketEmbedFormatter.ticket_list_embed(tickets, user)
|
||||
|
||||
await ctx.send(embed=embed, ephemeral=True)
|
||||
|
||||
# --- Stats Command ---
|
||||
|
||||
@ticket_group.command(name="stats", aliases=["statistiques"])
|
||||
async def ticket_stats(self, ctx):
|
||||
"""Voir les statistiques des tickets"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
config = self.storage.load_config(ctx.guild.id)
|
||||
|
||||
stats = self._calculate_stats(ctx.guild.id)
|
||||
embed = TicketEmbedFormatter.stats_embed(stats, ctx.guild.name)
|
||||
|
||||
await ctx.send(embed=embed, ephemeral=True)
|
||||
|
||||
def _calculate_stats(self, guild_id):
|
||||
"""Calculate ticket statistics"""
|
||||
from .data.models import TicketStats
|
||||
|
||||
storage = get_storage()
|
||||
tickets = storage.get_guild_tickets(guild_id)
|
||||
|
||||
stats = TicketStats()
|
||||
stats.total_tickets = len(tickets)
|
||||
|
||||
total_duration = 0
|
||||
closed_count = 0
|
||||
rating_sum = 0
|
||||
rating_count = 0
|
||||
|
||||
for ticket in tickets:
|
||||
if ticket.status == TicketStatus.OPEN:
|
||||
stats.open_tickets += 1
|
||||
elif ticket.status == TicketStatus.CLAIMED:
|
||||
stats.claimed_tickets += 1
|
||||
stats.open_tickets += 1
|
||||
elif ticket.status == TicketStatus.CLOSED:
|
||||
stats.closed_tickets += 1
|
||||
closed_count += 1
|
||||
|
||||
stats.tickets_by_category[ticket.category_id] = \
|
||||
stats.tickets_by_category.get(ticket.category_id, 0) + 1
|
||||
|
||||
stats.tickets_by_priority[ticket.priority.value] = \
|
||||
stats.tickets_by_priority.get(ticket.priority.value, 0) + 1
|
||||
|
||||
if ticket.duration_minutes:
|
||||
total_duration += ticket.duration_minutes
|
||||
|
||||
if ticket.rating:
|
||||
rating_sum += ticket.rating
|
||||
rating_count += 1
|
||||
|
||||
if closed_count > 0:
|
||||
stats.average_duration_minutes = total_duration / closed_count
|
||||
|
||||
if rating_count > 0:
|
||||
stats.average_rating = rating_sum / rating_count
|
||||
|
||||
return stats
|
||||
|
||||
# --- Admin Commands ---
|
||||
|
||||
@ticket_group.command(name="admin", aliases=["administration"])
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def ticket_admin(self, ctx):
|
||||
"""Panneau d'administration des tickets"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
config = self.storage.load_config(ctx.guild.id)
|
||||
view = AdminPanelView(self.bot, config)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="🎛️ Administration des Tickets",
|
||||
description="Gérez votre système de tickets ici:",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
embed.add_field(name="📊 Statistiques", value="Voir les stats", inline=True)
|
||||
embed.add_field(name="🔴 Fermeture Massive", value="Fermer plusieurs tickets", inline=True)
|
||||
embed.add_field(name="⚙️ Configuration", value="Configurer le système", inline=True)
|
||||
embed.add_field(name="🧹 Nettoyer", value="Supprimer les vieux tickets", inline=True)
|
||||
|
||||
await ctx.send(embed=embed, view=view, ephemeral=True)
|
||||
|
||||
@ticket_group.command(name="transcript", aliases=["transcription"])
|
||||
async def ticket_transcript(self, ctx, channel: discord.TextChannel = None):
|
||||
"""Générer un transcript pour un ticket"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
target = channel or ctx.channel
|
||||
|
||||
storage = get_storage()
|
||||
ticket = storage.load_ticket(target.id)
|
||||
|
||||
if not ticket:
|
||||
await ctx.send("Ce salon n'est pas un ticket.", ephemeral=True)
|
||||
return
|
||||
|
||||
# Collect messages
|
||||
messages = []
|
||||
async for msg in target.history(limit=1000, oldest_first=True):
|
||||
if msg.author.bot:
|
||||
continue
|
||||
messages.append({
|
||||
"id": str(msg.id),
|
||||
"author_id": msg.author.id,
|
||||
"author_name": str(msg.author),
|
||||
"content": msg.content,
|
||||
"timestamp": msg.created_at.isoformat(),
|
||||
"attachments": [a.url for a in msg.attachments],
|
||||
"is_staff": self._is_staff(msg.author, ctx.guild)
|
||||
})
|
||||
|
||||
# Generate transcript
|
||||
transcript_gen = get_transcript_generator()
|
||||
filepath = transcript_gen.save_transcript(ticket, messages, format="html")
|
||||
|
||||
if filepath:
|
||||
await ctx.send(f"✅ Transcript sauvegardé: `{filepath}`", ephemeral=True)
|
||||
else:
|
||||
await ctx.send("❌ Erreur lors de la génération.", ephemeral=True)
|
||||
|
||||
def _is_staff(self, member, guild) -> bool:
|
||||
"""Check if member is staff"""
|
||||
config = self.storage.load_config(guild.id)
|
||||
for role_id in config.staff_roles:
|
||||
role = guild.get_role(int(role_id))
|
||||
if role and role in member.roles:
|
||||
return True
|
||||
return False
|
||||
|
||||
# --- Category Management ---
|
||||
|
||||
@ticket_group.command(name="category", aliases=["categorie"])
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def ticket_category(self, ctx, action: str, category_name: str = None):
|
||||
"""Gérer les catégories de tickets (add/remove/list)"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
config = self.storage.load_config(ctx.guild.id)
|
||||
|
||||
if action == "list":
|
||||
if not config.categories:
|
||||
await ctx.send("Aucune catégorie configurée.", ephemeral=True)
|
||||
return
|
||||
|
||||
embed = discord.Embed(
|
||||
title="📁 Catégories de Tickets",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
|
||||
for cat in config.categories:
|
||||
embed.add_field(
|
||||
name=f"{cat.emoji} {cat.name}",
|
||||
value=f"ID: `{cat.id}`\n{cat.description or 'Aucune description'}",
|
||||
inline=False
|
||||
)
|
||||
|
||||
await ctx.send(embed=embed, ephemeral=True)
|
||||
|
||||
elif action == "add":
|
||||
if not category_name:
|
||||
await ctx.send("Nom de catégorie requis.", ephemeral=True)
|
||||
return
|
||||
|
||||
import uuid
|
||||
cat_id = str(uuid.uuid4())[:8]
|
||||
|
||||
category = TicketCategory(
|
||||
id=cat_id,
|
||||
name=category_name,
|
||||
description="Nouvelle catégorie",
|
||||
emoji="🎫"
|
||||
)
|
||||
|
||||
config.categories.append(category)
|
||||
self.storage.save_config(ctx.guild.id, config)
|
||||
|
||||
await ctx.send(f"Catégorie **{category_name}** créée!", ephemeral=True)
|
||||
|
||||
elif action == "remove":
|
||||
if not category_name:
|
||||
await ctx.send("Nom de catégorie requis.", ephemeral=True)
|
||||
return
|
||||
|
||||
config.categories = [c for c in config.categories if c.name.lower() != category_name.lower()]
|
||||
self.storage.save_config(ctx.guild.id, config)
|
||||
|
||||
await ctx.send(f"Catégorie **{category_name}** supprimée.", ephemeral=True)
|
||||
|
||||
else:
|
||||
await ctx.send("Action invalide. Utilisez: list, add, remove", ephemeral=True)
|
||||
|
||||
# --- Close/Reopen ---
|
||||
|
||||
@ticket_group.command(name="close", aliases=["fermer"])
|
||||
async def ticket_close(self, ctx, channel: discord.TextChannel = None, *, reason: str = None):
|
||||
"""Fermer un ticket"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
target = channel or ctx.channel
|
||||
|
||||
storage = get_storage()
|
||||
ticket = storage.load_ticket(target.id)
|
||||
|
||||
if not ticket:
|
||||
await ctx.send("Ce salon n'est pas un ticket.", ephemeral=True)
|
||||
return
|
||||
|
||||
# Check permissions
|
||||
can_close = (
|
||||
ctx.author.id == ticket.user_id or
|
||||
self._is_staff(ctx.author, ctx.guild)
|
||||
)
|
||||
|
||||
if not can_close:
|
||||
await ctx.send("Vous ne pouvez pas fermer ce ticket.", ephemeral=True)
|
||||
return
|
||||
|
||||
# Close ticket
|
||||
ticket.status = TicketStatus.CLOSED
|
||||
ticket.closed_at = datetime.now()
|
||||
storage.save_ticket(ticket)
|
||||
|
||||
# Update channel
|
||||
overwrites = target.overwrites.copy()
|
||||
for target_perm, perms in overwrites.items():
|
||||
if isinstance(target_perm, discord.Member) and target_perm.id != ctx.guild.me.id:
|
||||
overwrites[target_perm] = discord.PermissionOverwrite(
|
||||
read_messages=True,
|
||||
send_messages=False
|
||||
)
|
||||
|
||||
await target.edit(
|
||||
name=f"closed-{target.name}",
|
||||
overwrites=overwrites
|
||||
)
|
||||
|
||||
embed = TicketEmbedFormatter.ticket_closed(ticket, ctx.author, ticket.duration_minutes, reason)
|
||||
await target.send(embed=embed)
|
||||
|
||||
await ctx.send("Ticket fermé!", ephemeral=True)
|
||||
|
||||
@ticket_group.command(name="reopen", aliases=["rouvrir"])
|
||||
async def ticket_reopen(self, ctx, channel: discord.TextChannel = None):
|
||||
"""Rouvrir un ticket fermé"""
|
||||
await ctx.defer(ephemeral=True)
|
||||
|
||||
target = channel or ctx.channel
|
||||
|
||||
storage = get_storage()
|
||||
ticket = storage.load_ticket(target.id)
|
||||
|
||||
if not ticket:
|
||||
await ctx.send("Ce salon n'est pas un ticket.", ephemeral=True)
|
||||
return
|
||||
|
||||
if ticket.status != TicketStatus.CLOSED:
|
||||
await ctx.send("Ce ticket n'est pas fermé.", ephemeral=True)
|
||||
return
|
||||
|
||||
# Check permissions
|
||||
can_reopen = (
|
||||
ctx.author.id == ticket.user_id or
|
||||
self._is_staff(ctx.author, ctx.guild)
|
||||
)
|
||||
|
||||
if not can_reopen:
|
||||
await ctx.send("Vous ne pouvez pas rouvrir ce ticket.", ephemeral=True)
|
||||
return
|
||||
|
||||
# Reopen ticket
|
||||
ticket.status = TicketStatus.OPEN
|
||||
storage.save_ticket(ticket)
|
||||
|
||||
# Update channel name
|
||||
new_name = target.name.replace("closed-", "")
|
||||
await target.edit(name=new_name)
|
||||
|
||||
embed = TicketEmbedFormatter.ticket_reopened(ticket, ctx.author)
|
||||
await target.send(embed=embed)
|
||||
|
||||
await ctx.send("Ticket rouvert!", ephemeral=True)
|
||||
|
||||
|
||||
class TicketSetupView(discord.ui.View):
|
||||
"""View for initial ticket setup"""
|
||||
|
||||
def __init__(self, bot, storage, config: GuildTicketConfig):
|
||||
super().__init__(timeout=None) # Persistent view
|
||||
self.bot = bot
|
||||
self.storage = storage
|
||||
self.config = config
|
||||
|
||||
@discord.ui.button(label="➕ Ajouter Catégorie", style=discord.ButtonStyle.blurple, custom_id="ticket_setup_add_category")
|
||||
async def add_category(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
modal = AddCategoryModal(self.bot, self.storage, self.config)
|
||||
await interaction.response.send_modal(modal)
|
||||
|
||||
@discord.ui.button(label="👥 Configurer Rôles Staff", style=discord.ButtonStyle.secondary, custom_id="ticket_setup_config_roles")
|
||||
async def config_roles(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
from .views.admin import RolesManagementView
|
||||
|
||||
view = RolesManagementView(self.bot, self.config)
|
||||
|
||||
role_mentions = []
|
||||
for role_id in self.config.staff_roles:
|
||||
role = interaction.guild.get_role(int(role_id))
|
||||
if role:
|
||||
role_mentions.append(role.mention)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="👥 Gestion des Rôles Staff",
|
||||
description="Sélectionnez les rôles qui auront accès aux tickets:",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
embed.add_field(
|
||||
name="Rôles actuels",
|
||||
value=", ".join(role_mentions) if role_mentions else "Aucun",
|
||||
inline=False
|
||||
)
|
||||
|
||||
await interaction.response.send_message(embed=embed, view=view, ephemeral=True)
|
||||
|
||||
@discord.ui.button(label="📊 Définir Canal Logs", style=discord.ButtonStyle.secondary, custom_id="ticket_setup_config_logs")
|
||||
async def config_logs(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
modal = LogChannelModal(self.storage, self.config)
|
||||
await interaction.response.send_modal(modal)
|
||||
|
||||
@discord.ui.button(label="✅ Système Activé", style=discord.ButtonStyle.green, custom_id="ticket_setup_toggle_system")
|
||||
async def enable_system(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
self.config.enabled = not self.config.enabled
|
||||
self.storage.save_config(interaction.guild.id, self.config)
|
||||
|
||||
if self.config.enabled:
|
||||
button.style = discord.ButtonStyle.green
|
||||
button.label = "✅ Système Activé"
|
||||
status_msg = "Système de tickets activé!"
|
||||
else:
|
||||
button.style = discord.ButtonStyle.red
|
||||
button.label = "❌ Système Désactivé"
|
||||
status_msg = "Système de tickets désactivé!"
|
||||
|
||||
await interaction.response.edit_message(view=self)
|
||||
await interaction.followup.send(status_msg, ephemeral=True)
|
||||
|
||||
|
||||
class AddCategoryModal(discord.ui.Modal):
|
||||
"""Modal for adding a category"""
|
||||
|
||||
def __init__(self, bot, storage, config: GuildTicketConfig):
|
||||
super().__init__(title="➕ Nouvelle Catégorie")
|
||||
self.bot = bot
|
||||
self.storage = storage
|
||||
self.config = config
|
||||
|
||||
self.name = discord.ui.TextInput(
|
||||
label="Nom",
|
||||
placeholder="Ex: Support",
|
||||
required=True
|
||||
)
|
||||
self.add_item(self.name)
|
||||
|
||||
self.description = discord.ui.TextInput(
|
||||
label="Description",
|
||||
placeholder="Description de la catégorie...",
|
||||
required=False,
|
||||
style=discord.TextStyle.paragraph
|
||||
)
|
||||
self.add_item(self.description)
|
||||
|
||||
self.emoji = discord.ui.TextInput(
|
||||
label="Emoji",
|
||||
placeholder="🎫",
|
||||
required=False,
|
||||
max_length=5
|
||||
)
|
||||
self.add_item(self.emoji)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
import uuid
|
||||
|
||||
cat_id = str(uuid.uuid4())[:8]
|
||||
|
||||
category = TicketCategory(
|
||||
id=cat_id,
|
||||
name=self.name.value,
|
||||
description=self.description.value or "",
|
||||
emoji=self.emoji.value or "🎫"
|
||||
)
|
||||
|
||||
self.config.categories.append(category)
|
||||
|
||||
if not self.config.default_category:
|
||||
self.config.default_category = cat_id
|
||||
|
||||
self.storage.save_config(interaction.guild.id, self.config)
|
||||
|
||||
await interaction.response.send_message(
|
||||
f"Catégorie **{self.name.value}** créée avec succès!",
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
|
||||
class LogChannelModal(discord.ui.Modal):
|
||||
"""Modal for setting log channel"""
|
||||
|
||||
def __init__(self, storage, config: GuildTicketConfig):
|
||||
super().__init__(title="📊 Canal Logs")
|
||||
self.storage = storage
|
||||
self.config = config
|
||||
|
||||
self.channel_id = discord.ui.TextInput(
|
||||
label="ID du Canal",
|
||||
placeholder="Ex: 123456789012345678",
|
||||
required=True
|
||||
)
|
||||
self.add_item(self.channel_id)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
try:
|
||||
channel_id = int(self.channel_id.value)
|
||||
self.config.log_channel_id = channel_id
|
||||
self.storage.save_config(interaction.guild.id, self.config)
|
||||
await interaction.response.send_message(f"Canal logs défini: <#{channel_id}>", ephemeral=True)
|
||||
except ValueError:
|
||||
await interaction.response.send_message("ID de canal invalide.", ephemeral=True)
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
"""Setup function for the cog"""
|
||||
await bot.add_cog(Ticket(bot))
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue