Fix: système de ticket & arriver-départ
This commit is contained in:
parent
d9afa32828
commit
a169449390
9 changed files with 61 additions and 69 deletions
BIN
EXEMPLE_ARRIVER.png
Normal file
BIN
EXEMPLE_ARRIVER.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 315 KiB |
|
|
@ -185,7 +185,7 @@ class TicketPanelView(disnake.ui.View):
|
||||||
accessory=disnake.ui.Button(
|
accessory=disnake.ui.Button(
|
||||||
label=cat.name,
|
label=cat.name,
|
||||||
style=disnake.ButtonStyle.primary,
|
style=disnake.ButtonStyle.primary,
|
||||||
custom_id=f"cat_{cat.id}_{int(disnake.utils.time_snowflake(disnake.utils.utcnow()) / 1000)}"
|
custom_id=f"catv2_{cat.id}_{int(disnake.utils.time_snowflake(disnake.utils.utcnow()) / 1000)}"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -200,8 +200,13 @@ class TicketPanelView(disnake.ui.View):
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
# Fallback: texte simple avec view legacy
|
# Fallback: texte simple avec view legacy
|
||||||
cat_list = "\n".join(f"{cat.emoji} **{cat.name}** — {cat.description or 'Aucune description'}" for cat in self.config.categories[:5])
|
if interaction.response.is_done():
|
||||||
if not interaction.response.is_done():
|
await interaction.followup.send(
|
||||||
|
f"**🎫 Créer un Ticket**\n\nSélectionnez une catégorie ci-dessous:",
|
||||||
|
view=CategorySelectionView(self.bot, self.config),
|
||||||
|
ephemeral=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
f"**🎫 Créer un Ticket**\n\nSélectionnez une catégorie ci-dessous:",
|
f"**🎫 Créer un Ticket**\n\nSélectionnez une catégorie ci-dessous:",
|
||||||
view=CategorySelectionView(self.bot, self.config),
|
view=CategorySelectionView(self.bot, self.config),
|
||||||
|
|
@ -2239,11 +2244,15 @@ class CategoryManagerView(disnake.ui.View):
|
||||||
async def callback(self, interaction: disnake.ModalInteraction):
|
async def callback(self, interaction: disnake.ModalInteraction):
|
||||||
import uuid
|
import uuid
|
||||||
cat_id = str(uuid.uuid4())[:8]
|
cat_id = str(uuid.uuid4())[:8]
|
||||||
|
name_val = interaction.text_values.get("add_cat_name", "Nouvelle Catégorie")
|
||||||
|
desc_val = interaction.text_values.get("add_cat_desc", "")
|
||||||
|
emoji_val = interaction.text_values.get("add_cat_emoji", "🎫")
|
||||||
|
|
||||||
category = TicketCategory(
|
category = TicketCategory(
|
||||||
id=cat_id,
|
id=cat_id,
|
||||||
name=self.name.value,
|
name=name_val,
|
||||||
description=self.description.value or "",
|
description=desc_val,
|
||||||
emoji=self.emoji.value or "🎫"
|
emoji=emoji_val
|
||||||
)
|
)
|
||||||
self.config.categories.append(category)
|
self.config.categories.append(category)
|
||||||
self.storage.save_config(interaction.guild_id, self.config)
|
self.storage.save_config(interaction.guild_id, self.config)
|
||||||
|
|
@ -2847,10 +2856,10 @@ class TicketCommands(commands.Cog):
|
||||||
if not custom_id:
|
if not custom_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Accept both ticket_* / cat_* / close_* custom_ids
|
# Accept both ticket_* / catv2_* / close_* custom_ids
|
||||||
if not (
|
if not (
|
||||||
str(custom_id).startswith("ticket_")
|
str(custom_id).startswith("ticket_")
|
||||||
or str(custom_id).startswith("cat_")
|
or str(custom_id).startswith("catv2_")
|
||||||
or str(custom_id) in {"close_confirm", "close_cancel", "delay_cancel"}
|
or str(custom_id) in {"close_confirm", "close_cancel", "delay_cancel"}
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
@ -2875,20 +2884,10 @@ class TicketCommands(commands.Cog):
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# Routage manuel
|
# Routage manuel
|
||||||
if custom_id == "ticket_create":
|
# ✅ BUGFIX: Sélection de catégorie en V2 => custom_id = catv2_{cat.id}_{snowflake}
|
||||||
view = TicketPanelView(self.bot, config, self.storage)
|
if custom_id.startswith("catv2_"):
|
||||||
await view._create_callback(inter)
|
|
||||||
elif custom_id == "ticket_my_tickets":
|
|
||||||
view = TicketPanelView(self.bot, config, self.storage)
|
|
||||||
await view._my_tickets_callback(inter)
|
|
||||||
elif custom_id == "ticket_analytics":
|
|
||||||
view = TicketPanelView(self.bot, config, self.storage)
|
|
||||||
await view._analytics_callback(inter)
|
|
||||||
|
|
||||||
# ✅ BUGFIX: Sélection de catégorie en V2 => custom_id = cat_{cat.id}_{snowflake}
|
|
||||||
elif custom_id.startswith("cat_"):
|
|
||||||
try:
|
try:
|
||||||
# format: cat_<catid>_<suffix>
|
# format: catv2_<catid>_<suffix>
|
||||||
parts = custom_id.split("_")
|
parts = custom_id.split("_")
|
||||||
if len(parts) >= 3:
|
if len(parts) >= 3:
|
||||||
cat_id = "_".join(parts[1:-1])
|
cat_id = "_".join(parts[1:-1])
|
||||||
|
|
@ -2904,7 +2903,7 @@ class TicketCommands(commands.Cog):
|
||||||
modal = TicketPriorityModal(self.bot, config, category)
|
modal = TicketPriorityModal(self.bot, config, category)
|
||||||
await inter.response.send_modal(modal)
|
await inter.response.send_modal(modal)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
kuby_logger.error(f"[TicketSystem] cat_ routing error: {e}", exc_info=True)
|
kuby_logger.error(f"[TicketSystem] catv2_ routing error: {e}", exc_info=True)
|
||||||
if not inter.response.is_done():
|
if not inter.response.is_done():
|
||||||
await inter.response.send_message(f"Erreur interne: {e}", ephemeral=True)
|
await inter.response.send_message(f"Erreur interne: {e}", ephemeral=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class TicketCategory:
|
||||||
self.description = description
|
self.description = description
|
||||||
self.emoji = emoji
|
self.emoji = emoji
|
||||||
self.color = color
|
self.color = color
|
||||||
self.staff_roles = staff_roles or []
|
self.staff_roles = list(staff_roles) if staff_roles is not None else []
|
||||||
self.welcome_message = welcome_message or "Merci d'avoir créé un ticket. Notre équipe va vous répondre sous peu."
|
self.welcome_message = welcome_message or "Merci d'avoir créé un ticket. Notre équipe va vous répondre sous peu."
|
||||||
self.require_reason = require_reason
|
self.require_reason = require_reason
|
||||||
self.max_tickets_per_user = max_tickets_per_user
|
self.max_tickets_per_user = max_tickets_per_user
|
||||||
|
|
|
||||||
|
|
@ -93,63 +93,56 @@ class Welcome(commands.Cog):
|
||||||
background = Image.open(bg_path)
|
background = Image.open(bg_path)
|
||||||
draw = ImageDraw.Draw(background)
|
draw = ImageDraw.Draw(background)
|
||||||
|
|
||||||
# Charger la police avec support des accents
|
# Charger les polices custom (comme sur EXEMPLE_ARRIVER.png)
|
||||||
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSansCondensed-Bold.ttf"
|
font_large_path = os.path.join(self.base_dir, 'custom_font_large.otf')
|
||||||
if not os.path.exists(font_path):
|
font_small_path = os.path.join(self.base_dir, 'custom_font_small.otf')
|
||||||
kuby_logger.warning(f"[Welcome] Police DejaVu introuvable, fallback sur police custom")
|
|
||||||
font_path = os.path.join(self.base_dir, 'custom_font_large.otf')
|
|
||||||
|
|
||||||
font = ImageFont.truetype(font_path, 55)
|
# Les 3 lignes de texte à afficher (format identique à l'exemple)
|
||||||
|
line1_text = "BIENVENUE"
|
||||||
# Construire le message à afficher sur la bannière
|
line2_text = username.upper()
|
||||||
display_message = custom_message if custom_message else "Bienvenue {membre} chez {server}"
|
line3_text = (server_name or "").upper()
|
||||||
|
|
||||||
# Remplacer les placeholders
|
|
||||||
try:
|
|
||||||
display_message = display_message.format(
|
|
||||||
membre=username,
|
|
||||||
member=username,
|
|
||||||
server=server_name or "",
|
|
||||||
serveur=server_name or "",
|
|
||||||
username=username,
|
|
||||||
servername=server_name or "",
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
pass # Si placeholders inconnus, garder tel quel
|
|
||||||
|
|
||||||
# Zone de texte disponible (à droite de l'avatar)
|
# Zone de texte disponible (à droite de l'avatar)
|
||||||
text_area_left = 280
|
text_area_left = 280
|
||||||
text_area_right = background.width - 20
|
text_area_right = background.width - 20
|
||||||
text_area_width = text_area_right - text_area_left
|
text_area_width = text_area_right - text_area_left
|
||||||
|
text_area_center_x = text_area_left + text_area_width // 2
|
||||||
|
|
||||||
# Découper le texte en lignes qui tiennent dans la zone
|
# Fonction pour trouver la taille de police optimale
|
||||||
words = display_message.split()
|
def fit_font(font_path, text, max_size, max_width):
|
||||||
lines = []
|
size = max_size
|
||||||
current_line = ""
|
while size > 10:
|
||||||
for word in words:
|
font = ImageFont.truetype(font_path, size)
|
||||||
test_line = f"{current_line} {word}".strip()
|
bbox = draw.textbbox((0, 0), text, font=font)
|
||||||
bbox = draw.textbbox((0, 0), test_line, font=font)
|
w = bbox[2] - bbox[0]
|
||||||
if bbox[2] - bbox[0] <= text_area_width:
|
if w <= max_width:
|
||||||
current_line = test_line
|
h = bbox[3] - bbox[1]
|
||||||
else:
|
return font, w, h
|
||||||
if current_line:
|
size -= 2
|
||||||
lines.append(current_line)
|
font = ImageFont.truetype(font_path, 10)
|
||||||
current_line = word
|
bbox = draw.textbbox((0, 0), text, font=font)
|
||||||
if current_line:
|
return font, bbox[2] - bbox[0], bbox[3] - bbox[1]
|
||||||
lines.append(current_line)
|
|
||||||
|
|
||||||
wrapped_text = "\n".join(lines)
|
# Adapter chaque ligne à la zone disponible
|
||||||
|
font1, w1, h1 = fit_font(font_large_path, line1_text, 65, text_area_width)
|
||||||
|
font2, w2, h2 = fit_font(font_small_path, line2_text, 35, text_area_width)
|
||||||
|
font3, w3, h3 = fit_font(font_large_path, line3_text, 65, text_area_width)
|
||||||
|
|
||||||
# Mesurer le bloc de texte complet
|
# Espacement entre les lignes
|
||||||
bbox = draw.textbbox((0, 0), wrapped_text, font=font)
|
spacing_1_2 = 15 # entre BIENVENUE et username
|
||||||
text_width = bbox[2] - bbox[0]
|
spacing_2_3 = 20 # entre username et nom du serveur
|
||||||
text_height = bbox[3] - bbox[1]
|
total_height = h1 + spacing_1_2 + h2 + spacing_2_3 + h3
|
||||||
|
|
||||||
# Centrer le bloc dans la zone disponible
|
# Centrer verticalement le bloc de 3 lignes
|
||||||
text_x = text_area_left + (text_area_width - text_width) // 2
|
start_y = (background.height - total_height) // 2
|
||||||
text_y = (background.height - text_height) // 2
|
|
||||||
|
|
||||||
draw.text((text_x, text_y), wrapped_text, font=font, fill=(255, 255, 255), align="center")
|
# Dessiner chaque ligne centrée horizontalement dans la zone
|
||||||
|
y = start_y
|
||||||
|
draw.text((text_area_center_x - w1 // 2, y), line1_text, font=font1, fill=(255, 255, 255))
|
||||||
|
y += h1 + spacing_1_2
|
||||||
|
draw.text((text_area_center_x - w2 // 2, y), line2_text, font=font2, fill=(255, 255, 255))
|
||||||
|
y += h2 + spacing_2_3
|
||||||
|
draw.text((text_area_center_x - w3 // 2, y), line3_text, font=font3, fill=(255, 255, 255))
|
||||||
|
|
||||||
|
|
||||||
# Ajouter l'avatar
|
# Ajouter l'avatar
|
||||||
|
|
|
||||||
BIN
test_welcome_default_bg.png
Normal file
BIN
test_welcome_default_bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
test_welcome_final.png
Normal file
BIN
test_welcome_final.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
test_welcome_fixed.png
Normal file
BIN
test_welcome_fixed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 196 KiB |
BIN
test_welcome_output.png
Normal file
BIN
test_welcome_output.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
test_welcome_real.png
Normal file
BIN
test_welcome_real.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 195 KiB |
Loading…
Add table
Add a link
Reference in a new issue