207 lines
No EOL
9 KiB
Python
207 lines
No EOL
9 KiB
Python
import discord
|
|
from discord.ext import commands, tasks
|
|
from discord.ui import View, Button, Modal, TextInput
|
|
import json
|
|
import os
|
|
from datetime import datetime
|
|
import asyncio
|
|
|
|
# --- Fonctions de gestion de données ---
|
|
|
|
def load_config():
|
|
"""Charge la configuration des tickets"""
|
|
path = "data/tickets/config.json"
|
|
if not os.path.exists("data/tickets"):
|
|
os.makedirs("data/tickets")
|
|
if not os.path.exists(path):
|
|
with open(path, "w") as f:
|
|
json.dump({}, f, indent=4)
|
|
with open(path, "r") as f:
|
|
return json.load(f)
|
|
|
|
def save_config(config):
|
|
"""Sauvegarde la configuration des tickets"""
|
|
path = "data/tickets/config.json"
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
json.dump(config, f, indent=4, ensure_ascii=False)
|
|
|
|
# --- Vues (Interactions) ---
|
|
|
|
class TicketView(View):
|
|
def __init__(self, bot, config):
|
|
super().__init__(timeout=None)
|
|
self.bot = bot
|
|
self.config = config
|
|
|
|
@discord.ui.button(label="Créer un ticket", style=discord.ButtonStyle.blurple, custom_id="create_ticket")
|
|
async def create_ticket(self, interaction: discord.Interaction, button: Button):
|
|
await interaction.response.defer(ephemeral=True)
|
|
guild_id = str(interaction.guild_id)
|
|
|
|
if guild_id not in self.config:
|
|
return await interaction.followup.send("Le système n'est pas configuré.", ephemeral=True)
|
|
|
|
guild_config = self.config[guild_id]
|
|
|
|
# Vérifier si l'utilisateur a déjà un ticket
|
|
for channel in interaction.guild.text_channels:
|
|
if channel.name.startswith(f"ticket-{interaction.user.id}"):
|
|
return await interaction.followup.send(f"Vous avez déjà un ticket : {channel.mention}", ephemeral=True)
|
|
|
|
# Configuration des permissions
|
|
category = interaction.guild.get_channel(int(guild_config["category_id"]))
|
|
overwrites = {
|
|
interaction.guild.default_role: discord.PermissionOverwrite(read_messages=False),
|
|
interaction.user: discord.PermissionOverwrite(read_messages=True, send_messages=True, attach_files=True),
|
|
interaction.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True)
|
|
}
|
|
|
|
for role_id in guild_config["staff_roles"]:
|
|
role = interaction.guild.get_role(int(role_id))
|
|
if role:
|
|
overwrites[role] = discord.PermissionOverwrite(read_messages=True, send_messages=True)
|
|
|
|
# Création du salon
|
|
channel_name = f"ticket-{interaction.user.id}-{datetime.now().strftime('%m%d%H%M')}"
|
|
ticket_channel = await interaction.guild.create_text_channel(
|
|
name=channel_name,
|
|
category=category,
|
|
overwrites=overwrites,
|
|
topic=f"Ticket de {interaction.user} ({interaction.user.id})"
|
|
)
|
|
|
|
embed = discord.Embed(
|
|
title="Nouveau Ticket",
|
|
description=guild_config["ticket_message"],
|
|
color=discord.Color.blue(),
|
|
timestamp=datetime.now()
|
|
)
|
|
embed.set_author(name=interaction.user.display_name, icon_url=interaction.user.display_avatar.url)
|
|
|
|
await ticket_channel.send(embed=embed, view=TicketCloseView(self.bot, self.config))
|
|
|
|
# Logs
|
|
if guild_config.get("log_channel"):
|
|
log_ch = self.bot.get_channel(int(guild_config["log_channel"]))
|
|
if log_ch:
|
|
log_embed = discord.Embed(title="Ticket Créé", color=discord.Color.green())
|
|
log_embed.add_field(name="Auteur", value=interaction.user.mention)
|
|
log_embed.add_field(name="Salon", value=ticket_channel.mention)
|
|
await log_ch.send(embed=log_embed)
|
|
|
|
await interaction.followup.send(f"Ticket créé : {ticket_channel.mention}", ephemeral=True)
|
|
|
|
class TicketCloseView(View):
|
|
def __init__(self, bot, config):
|
|
super().__init__(timeout=None)
|
|
self.bot = bot
|
|
self.config = config
|
|
|
|
@discord.ui.button(label="Fermer le ticket", style=discord.ButtonStyle.red, custom_id="close_ticket")
|
|
async def close_ticket(self, interaction: discord.Interaction, button: Button):
|
|
await interaction.response.defer(ephemeral=True)
|
|
guild_config = self.config.get(str(interaction.guild_id))
|
|
|
|
# Modifier permissions (bloquer l'écriture)
|
|
overwrites = interaction.channel.overwrites
|
|
for target in overwrites:
|
|
if isinstance(target, discord.Member) and target != interaction.guild.me:
|
|
overwrites[target] = discord.PermissionOverwrite(read_messages=True, send_messages=False)
|
|
|
|
await interaction.channel.edit(name=f"closed-{interaction.channel.name}", overwrites=overwrites)
|
|
|
|
embed = discord.Embed(title="Ticket Fermé", description=f"Fermé par {interaction.user.mention}", color=discord.Color.red())
|
|
await interaction.channel.send(embed=embed, view=TicketReopenView(self.bot, self.config))
|
|
await interaction.followup.send("Ticket fermé.", ephemeral=True)
|
|
|
|
class TicketReopenView(View):
|
|
def __init__(self, bot, config):
|
|
super().__init__(timeout=None)
|
|
self.bot = bot
|
|
self.config = config
|
|
|
|
@discord.ui.button(label="Rouvrir", style=discord.ButtonStyle.green, custom_id="reopen_ticket")
|
|
async def reopen(self, interaction: discord.Interaction, button: Button):
|
|
await interaction.response.defer(ephemeral=True)
|
|
new_name = interaction.channel.name.replace("closed-", "")
|
|
await interaction.channel.edit(name=new_name)
|
|
await interaction.channel.send(f"Ticket rouvert par {interaction.user.mention}", view=TicketCloseView(self.bot, self.config))
|
|
|
|
@discord.ui.button(label="Supprimer", style=discord.ButtonStyle.grey, custom_id="delete_ticket")
|
|
async def delete(self, interaction: discord.Interaction, button: Button):
|
|
await interaction.response.send_message("Suppression dans 3s...")
|
|
await asyncio.sleep(3)
|
|
await interaction.channel.delete()
|
|
|
|
# --- Modale de Configuration ---
|
|
|
|
class TicketConfigModal(Modal):
|
|
def __init__(self, bot, config, guild_id):
|
|
super().__init__(title="Configuration Tickets")
|
|
self.bot = bot
|
|
self.config = config
|
|
self.guild_id = str(guild_id)
|
|
|
|
self.category_id = TextInput(label="ID Catégorie", placeholder="Ex: 123456789", required=True)
|
|
self.staff_roles = TextInput(label="IDs Rôles Staff (virgule)", placeholder="ID1, ID2", required=True)
|
|
self.log_channel = TextInput(label="ID Salon Logs", required=False)
|
|
self.ticket_msg = TextInput(label="Message Ticket", style=discord.TextStyle.paragraph, required=True)
|
|
self.welcome_msg = TextInput(label="Message Panel", required=True)
|
|
|
|
for item in [self.category_id, self.staff_roles, self.log_channel, self.ticket_msg, self.welcome_msg]:
|
|
self.add_item(item)
|
|
|
|
async def on_submit(self, interaction: discord.Interaction):
|
|
role_ids = [r.strip() for r in self.staff_roles.value.split(",")]
|
|
self.config[self.guild_id] = {
|
|
"category_id": self.category_id.value,
|
|
"staff_roles": role_ids,
|
|
"log_channel": self.log_channel.value,
|
|
"ticket_message": self.ticket_msg.value,
|
|
"welcome_message": self.welcome_msg.value
|
|
}
|
|
save_config(self.config)
|
|
await interaction.response.send_message("Configuration sauvegardée !", ephemeral=True)
|
|
|
|
# --- Le Cog Principal ---
|
|
|
|
class Ticket(commands.Cog):
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
self.config = load_config()
|
|
|
|
@commands.Cog.listener()
|
|
async def on_ready(self):
|
|
# Enregistrer les vues persistantes (pour qu'elles marchent après reboot)
|
|
self.bot.add_view(TicketView(self.bot, self.config))
|
|
self.bot.add_view(TicketCloseView(self.bot, self.config))
|
|
self.bot.add_view(TicketReopenView(self.bot, self.config))
|
|
print("Module Ticket prêt")
|
|
|
|
@commands.hybrid_group(name="ticket")
|
|
@commands.has_permissions(administrator=True)
|
|
async def ticket(self, ctx):
|
|
if ctx.invoked_subcommand is None:
|
|
await ctx.send("Utilisez `/ticket setup` ou `/ticket panel`", ephemeral=True)
|
|
|
|
@ticket.command(name="setup")
|
|
async def setup_cmd(self, ctx):
|
|
await ctx.interaction.response.send_modal(TicketConfigModal(self.bot, self.config, ctx.guild.id))
|
|
|
|
@ticket.command(name="panel")
|
|
async def panel_cmd(self, ctx, channel: discord.TextChannel = None):
|
|
guild_id = str(ctx.guild.id)
|
|
if guild_id not in self.config:
|
|
return await ctx.send("Configurez d'abord le système via `/ticket setup`.")
|
|
|
|
target = channel or ctx.channel
|
|
embed = discord.Embed(
|
|
title="Support",
|
|
description=self.config[guild_id]["welcome_message"],
|
|
color=discord.Color.blue()
|
|
)
|
|
await target.send(embed=embed, view=TicketView(self.bot, self.config))
|
|
await ctx.send("Panel envoyé !", ephemeral=True)
|
|
|
|
async def setup(bot):
|
|
await bot.add_cog(Ticket(bot)) |