feat(ticket): add claim, add member, persistent management view
All checks were successful
ci / ci (push) Successful in 47s

- ClaimButton: staff claim/unclaim with log
- AddMemberButton: UserSelect to add member to ticket channel
- TicketManagementView: persistent LayoutView with all 3 buttons
- claimed_by field in TicketData
- Buttons registered as persistent views for bot restart survival
This commit is contained in:
Mathis 2026-09-25 19:28:15 +02:00
parent 80daeae55c
commit 4d1e265848
3 changed files with 135 additions and 2 deletions

View file

@ -8,7 +8,7 @@ from discord.ext import commands
from tickets import storage from tickets import storage
from tickets.data import TicketCategory from tickets.data import TicketCategory
from tickets.ui import PanelCreateButton from tickets.ui import AddMemberButton, ClaimButton, CloseButton, PanelCreateButton
logger = logging.getLogger("vision") logger = logging.getLogger("vision")
@ -130,12 +130,21 @@ class ConfigSetLogButton(discord.ui.Button):
await interaction.response.send_message("Selectionnez le canal de logs :", view=view, ephemeral=True) await interaction.response.send_message("Selectionnez le canal de logs :", view=view, ephemeral=True)
class TicketManagementView(discord.ui.LayoutView):
def __init__(self):
super().__init__(timeout=None)
container = discord.ui.Container()
container.add_item(discord.ui.ActionRow(ClaimButton(), AddMemberButton(), CloseButton()))
self.add_item(container)
class TicketSystem(commands.Cog): class TicketSystem(commands.Cog):
ticket = app_commands.Group(name="ticket", description="Systeme de tickets") ticket = app_commands.Group(name="ticket", description="Systeme de tickets")
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.bot.add_view(PanelView()) self.bot.add_view(PanelView())
self.bot.add_view(TicketManagementView())
@commands.Cog.listener() @commands.Cog.listener()
async def on_ready(self): async def on_ready(self):

View file

@ -53,6 +53,7 @@ class TicketData:
created_at: str = "" created_at: str = ""
closed_at: str | None = None closed_at: str | None = None
closed_by: int | None = None closed_by: int | None = None
claimed_by: int | None = None
messages: list[TicketMessage] = field(default_factory=list) messages: list[TicketMessage] = field(default_factory=list)
reason: str = "" reason: str = ""
@ -66,6 +67,7 @@ class TicketData:
"created_at": self.created_at, "created_at": self.created_at,
"closed_at": self.closed_at, "closed_at": self.closed_at,
"closed_by": self.closed_by, "closed_by": self.closed_by,
"claimed_by": self.claimed_by,
"messages": [ "messages": [
{ {
"author_id": m.author_id, "author_id": m.author_id,

View file

@ -185,7 +185,13 @@ class TicketModal(discord.ui.Modal, title="Nouveau Ticket"):
) )
) )
container.add_item(discord.ui.Separator()) container.add_item(discord.ui.Separator())
container.add_item(discord.ui.ActionRow(CloseButton())) container.add_item(
discord.ui.ActionRow(
ClaimButton(),
AddMemberButton(),
CloseButton(),
)
)
view = discord.ui.LayoutView() view = discord.ui.LayoutView()
view.add_item(container) view.add_item(container)
@ -208,6 +214,122 @@ class TicketModal(discord.ui.Modal, title="Nouveau Ticket"):
await _send_log(guild, config, embed) await _send_log(guild, config, embed)
class ClaimButton(discord.ui.Button):
def __init__(self):
super().__init__(
label="Claim",
style=discord.ButtonStyle.success,
custom_id="ticket_claim",
emoji="Claim",
)
async def callback(self, interaction: discord.Interaction):
config = storage.get_config(interaction.guild_id)
ticket = storage.get_ticket(interaction.channel.id)
if not ticket:
await interaction.response.send_message("Ce n'est pas un ticket.", ephemeral=True)
return
member = interaction.guild.get_member(interaction.user.id)
if not member or not _is_staff(member, config):
await interaction.response.send_message("Seul le staff peut claim un ticket.", ephemeral=True)
return
if ticket.claimed_by:
if ticket.claimed_by == interaction.user.id:
ticket.claimed_by = None
storage.save_ticket(ticket)
await interaction.response.send_message(
f"{interaction.user.mention} a retire son claim.", ephemeral=True
)
else:
await interaction.response.send_message(
f"Ce ticket est deja claim par <@{ticket.claimed_by}>.", ephemeral=True
)
return
ticket.claimed_by = interaction.user.id
storage.save_ticket(ticket)
embed = discord.Embed(
title="Ticket claim",
description=f"Claim par {interaction.user.mention}",
color=discord.Color.green(),
timestamp=datetime.now(timezone.utc),
)
await _send_log(interaction.guild, config, embed)
container = discord.ui.Container()
container.add_item(discord.ui.TextDisplay(f"## Ticket claim\nClaim par {interaction.user.mention}"))
view = discord.ui.LayoutView()
view.add_item(container)
await interaction.response.send_message(view=view)
class AddMemberButton(discord.ui.Button):
def __init__(self):
super().__init__(
label="Ajouter un membre",
style=discord.ButtonStyle.secondary,
custom_id="ticket_add_member",
emoji="Ajouter",
)
async def callback(self, interaction: discord.Interaction):
config = storage.get_config(interaction.guild_id)
ticket = storage.get_ticket(interaction.channel.id)
if not ticket:
await interaction.response.send_message("Ce n'est pas un ticket.", ephemeral=True)
return
member = interaction.guild.get_member(interaction.user.id)
if not member or not _is_staff(member, config):
await interaction.response.send_message("Seul le staff peut ajouter des membres.", ephemeral=True)
return
await interaction.response.send_message(
"Selectionnez le membre a ajouter :",
view=AddMemberSelectView(),
ephemeral=True,
)
class AddMemberSelect(discord.ui.UserSelect):
def __init__(self):
super().__init__(
placeholder="Selectionnez un membre...",
custom_id="ticket_add_member_select",
min_values=1,
max_values=1,
)
async def callback(self, interaction: discord.Interaction):
user = self.values[0]
await interaction.channel.set_permissions(
user,
view_channel=True,
send_messages=True,
read_message_history=True,
)
await interaction.response.edit_message(
content=f"{user.mention} a ete ajoute au ticket.",
view=None,
)
embed = discord.Embed(
title="Membre ajoute",
description=f"{user.mention} a ete ajoute par {interaction.user.mention}",
color=discord.Color.blue(),
timestamp=datetime.now(timezone.utc),
)
await _send_log(interaction.guild, storage.get_config(interaction.guild_id), embed)
class AddMemberSelectView(discord.ui.View):
def __init__(self):
super().__init__(timeout=300)
self.add_item(AddMemberSelect())
class CloseButton(discord.ui.Button): class CloseButton(discord.ui.Button):
def __init__(self): def __init__(self):
super().__init__( super().__init__(