Migration de Kuby vers disnake
This commit is contained in:
parent
dc6e235f27
commit
c8c579eefe
36 changed files with 1205 additions and 1253 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -16,9 +16,8 @@ from PIL import Image, ImageDraw, ImageFont
|
|||
# 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
|
||||
from discord import app_commands
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
|
||||
from .data.storage import get_storage
|
||||
from .data.models import TicketData, TicketStatus, GuildTicketConfig
|
||||
|
|
@ -31,7 +30,7 @@ class TicketAnalytics(commands.Cog):
|
|||
self.bot = bot
|
||||
self.storage = get_storage()
|
||||
|
||||
def _is_staff(self, interaction: discord.Interaction) -> bool:
|
||||
def _is_staff(self, interaction: disnake.Interaction) -> bool:
|
||||
"""Check if user is staff"""
|
||||
guild = interaction.guild
|
||||
user = interaction.user
|
||||
|
|
@ -363,9 +362,8 @@ class TicketAnalytics(commands.Cog):
|
|||
buf.seek(0)
|
||||
return buf
|
||||
|
||||
@app_commands.command(name="ticket_analytics", description="Affiche les statistiques détaillées des tickets (Staff uniquement)")
|
||||
@app_commands.describe(period="Période d'analyse (7d, 30d, 90d, all)")
|
||||
async def ticket_analytics(self, interaction: discord.Interaction, period: str = "30d"):
|
||||
@commands.slash_command(name="ticket_analytics", description="Affiche les statistiques détaillées des tickets (Staff uniquement)")
|
||||
async def ticket_analytics(self, interaction: disnake.Interaction, period: str = commands.Param(default="30d", choices=["7d", "30d", "90d", "all"], description="Période d'analyse")):
|
||||
"""Display comprehensive ticket analytics for staff"""
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
|
||||
|
|
@ -405,10 +403,10 @@ class TicketAnalytics(commands.Cog):
|
|||
t.created_at >= cutoff_date for t in filtered_tickets if hasattr(t, 'response_time') and t.response_time == rt)]
|
||||
|
||||
# Create main embed
|
||||
embed = discord.Embed(
|
||||
embed = disnake.Embed(
|
||||
title="📊 Analytics des Tickets",
|
||||
description=f"Statistiques pour la période: **{period}**",
|
||||
color=discord.Color.blue(),
|
||||
color=disnake.Color.blue(),
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
|
||||
|
|
@ -444,7 +442,7 @@ class TicketAnalytics(commands.Cog):
|
|||
rating_chart = self._create_rating_chart(stats['ratings'])
|
||||
await interaction.followup.send(
|
||||
"📊 **Distribution des Notes Clients**",
|
||||
file=discord.File(rating_chart, 'rating_distribution.png'),
|
||||
file=disnake.File(rating_chart, 'rating_distribution.png'),
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
|
|
@ -453,7 +451,7 @@ class TicketAnalytics(commands.Cog):
|
|||
response_chart = self._create_response_time_chart(stats['response_times'])
|
||||
await interaction.followup.send(
|
||||
"⏱️ **Temps de Réponse**",
|
||||
file=discord.File(response_chart, 'response_times.png'),
|
||||
file=disnake.File(response_chart, 'response_times.png'),
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
|
|
@ -462,7 +460,7 @@ class TicketAnalytics(commands.Cog):
|
|||
trend_chart = self._create_trend_chart(stats['tickets_by_day'])
|
||||
await interaction.followup.send(
|
||||
"📈 **Évolution des Tickets**",
|
||||
file=discord.File(trend_chart, 'ticket_trends.png'),
|
||||
file=disnake.File(trend_chart, 'ticket_trends.png'),
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
|
|
@ -471,10 +469,10 @@ class TicketAnalytics(commands.Cog):
|
|||
|
||||
# Send feedback summary if available
|
||||
if stats['feedbacks']:
|
||||
feedback_embed = discord.Embed(
|
||||
feedback_embed = disnake.Embed(
|
||||
title="💬 Commentaires Clients",
|
||||
description=f"Derniers commentaires ({min(5, len(stats['feedbacks']))} affichés)",
|
||||
color=discord.Color.green()
|
||||
color=disnake.Color.green()
|
||||
)
|
||||
|
||||
# Sort by rating (lowest first to show areas for improvement)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import discord
|
||||
import disnake
|
||||
from .data.models import TicketData
|
||||
|
||||
class FeedbackView(discord.ui.View):
|
||||
class FeedbackView(disnake.ui.View):
|
||||
def __init__(self, bot, storage, ticket: TicketData, staff_rating_only: bool = False):
|
||||
super().__init__(timeout=86400) # 24h timeout
|
||||
self.bot = bot
|
||||
|
|
@ -14,12 +14,12 @@ class FeedbackView(discord.ui.View):
|
|||
# Add Select Menu for Rating
|
||||
self.add_item(RatingSelect())
|
||||
|
||||
@discord.ui.button(label="📝 Laisser un commentaire", style=discord.ButtonStyle.secondary, custom_id="feedback_comment", row=1)
|
||||
async def comment_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
@disnake.ui.button(label="📝 Laisser un commentaire", style=disnake.ButtonStyle.secondary, custom_id="feedback_comment", row=1)
|
||||
async def comment_button(self, interaction: disnake.Interaction, button: disnake.ui.Button):
|
||||
await interaction.response.send_modal(FeedbackModal(self))
|
||||
|
||||
@discord.ui.button(label="✅ Envoyer", style=discord.ButtonStyle.green, custom_id="feedback_submit", row=1)
|
||||
async def submit_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
@disnake.ui.button(label="✅ Envoyer", style=disnake.ButtonStyle.green, custom_id="feedback_submit", row=1)
|
||||
async def submit_button(self, interaction: disnake.Interaction, button: disnake.ui.Button):
|
||||
if self.rating == 0:
|
||||
await interaction.response.send_message("❌ Veuillez sélectionner une note avant d'envoyer.", ephemeral=True)
|
||||
return
|
||||
|
|
@ -66,11 +66,11 @@ class FeedbackView(discord.ui.View):
|
|||
|
||||
if staff_user:
|
||||
# Create feedback embed
|
||||
embed = discord.Embed(
|
||||
embed = disnake.Embed(
|
||||
title="📝 Nouveau Commentaire Reçu",
|
||||
description=f"Un utilisateur a laissé un avis sur le ticket #{self.ticket.channel_id}",
|
||||
color=discord.Color.gold() if self.rating < 3 else discord.Color.green(),
|
||||
timestamp=discord.utils.utcnow()
|
||||
color=disnake.Color.gold() if self.rating < 3 else disnake.Color.green(),
|
||||
timestamp=disnake.utils.utcnow()
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
|
|
@ -99,7 +99,7 @@ class FeedbackView(discord.ui.View):
|
|||
try:
|
||||
await staff_user.send(embed=embed)
|
||||
print(f"DEBUG: DM sent successfully to {staff_user}", flush=True)
|
||||
except discord.Forbidden:
|
||||
except disnake.Forbidden:
|
||||
print(f"DEBUG: DM failed - Forbidden (DMs closed)", flush=True)
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error sending feedback DM: {e}", flush=True)
|
||||
|
|
@ -111,34 +111,34 @@ class FeedbackView(discord.ui.View):
|
|||
except Exception as e:
|
||||
print(f"Error in feedback notification: {e}", flush=True)
|
||||
|
||||
class RatingSelect(discord.ui.Select):
|
||||
class RatingSelect(disnake.ui.Select):
|
||||
def __init__(self):
|
||||
options = [
|
||||
discord.SelectOption(label="⭐⭐⭐⭐⭐ (Excellent)", value="5", description="Service parfait !"),
|
||||
discord.SelectOption(label="⭐⭐⭐⭐ (Très bien)", value="4", description="Très bon service"),
|
||||
discord.SelectOption(label="⭐⭐⭐ (Bien)", value="3", description="Correct"),
|
||||
discord.SelectOption(label="⭐⭐ (Moyen)", value="2", description="Peut mieux faire"),
|
||||
discord.SelectOption(label="⭐ (Mauvais)", value="1", description="Insatisfaisant")
|
||||
disnake.SelectOption(label="⭐⭐⭐⭐⭐ (Excellent)", value="5", description="Service parfait !"),
|
||||
disnake.SelectOption(label="⭐⭐⭐⭐ (Très bien)", value="4", description="Très bon service"),
|
||||
disnake.SelectOption(label="⭐⭐⭐ (Bien)", value="3", description="Correct"),
|
||||
disnake.SelectOption(label="⭐⭐ (Moyen)", value="2", description="Peut mieux faire"),
|
||||
disnake.SelectOption(label="⭐ (Mauvais)", value="1", description="Insatisfaisant")
|
||||
]
|
||||
super().__init__(placeholder="Notez le service...", min_values=1, max_values=1, options=options, row=0)
|
||||
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
async def callback(self, interaction: disnake.Interaction):
|
||||
self.view.rating = int(self.values[0])
|
||||
await interaction.response.defer()
|
||||
|
||||
class FeedbackModal(discord.ui.Modal):
|
||||
class FeedbackModal(disnake.ui.Modal):
|
||||
def __init__(self, view):
|
||||
super().__init__(title="Votre avis")
|
||||
self.view = view
|
||||
self.comment = discord.ui.TextInput(
|
||||
self.comment = disnake.ui.TextInput(
|
||||
label="Commentaire",
|
||||
style=discord.TextStyle.paragraph,
|
||||
style=disnake.TextStyle.paragraph,
|
||||
placeholder="Dites-nous ce que vous avez pensé du support...",
|
||||
required=True,
|
||||
max_length=1000
|
||||
)
|
||||
self.add_item(self.comment)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
async def on_submit(self, interaction: disnake.Interaction):
|
||||
self.view.comment = self.comment.value
|
||||
await interaction.response.send_message("Commentaire enregistré ! N'oubliez pas de valider avec 'Envoyer'.", ephemeral=True)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from datetime import datetime, timedelta
|
|||
from collections import defaultdict
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
import discord
|
||||
import disnake
|
||||
import matplotlib.pyplot as plt
|
||||
import plotly.graph_objects as go
|
||||
import plotly.express as px
|
||||
|
|
@ -254,7 +254,7 @@ class StaffAnalytics:
|
|||
return buf
|
||||
|
||||
|
||||
class StaffStatsView(discord.ui.View):
|
||||
class StaffStatsView(disnake.ui.View):
|
||||
"""View for selecting individual staff member stats"""
|
||||
|
||||
def __init__(self, bot, staff_list: List[Dict[str, Any]], analytics: StaffAnalytics):
|
||||
|
|
@ -272,7 +272,7 @@ class StaffStatsView(discord.ui.View):
|
|||
description = f"Note: {staff['average_rating']:.1f}⭐" if staff['total_ratings'] > 0 else "Aucune évaluation"
|
||||
|
||||
options.append(
|
||||
discord.SelectOption(
|
||||
disnake.SelectOption(
|
||||
label=label,
|
||||
value=str(staff['user_id']),
|
||||
description=description[:50]
|
||||
|
|
@ -280,14 +280,14 @@ class StaffStatsView(discord.ui.View):
|
|||
)
|
||||
|
||||
if options:
|
||||
select = discord.ui.Select(
|
||||
select = disnake.ui.Select(
|
||||
placeholder="Sélectionnez un membre du staff...",
|
||||
options=options
|
||||
)
|
||||
select.callback = self._staff_selected
|
||||
self.add_item(select)
|
||||
|
||||
async def _staff_selected(self, interaction: discord.Interaction):
|
||||
async def _staff_selected(self, interaction: disnake.Interaction):
|
||||
"""Handle staff selection"""
|
||||
staff_id = int(interaction.data["values"][0])
|
||||
staff_data = next((s for s in self.staff_list if s['user_id'] == staff_id), None)
|
||||
|
|
@ -301,10 +301,10 @@ class StaffStatsView(discord.ui.View):
|
|||
# Create individual chart
|
||||
chart = self.analytics.create_individual_staff_chart(staff_data)
|
||||
|
||||
embed = discord.Embed(
|
||||
embed = disnake.Embed(
|
||||
title=f"📊 Statistiques de {staff_data['name']}",
|
||||
description="Performances détaillées du membre du staff",
|
||||
color=discord.Color.blue(),
|
||||
color=disnake.Color.blue(),
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
|
||||
|
|
@ -331,4 +331,4 @@ class StaffStatsView(discord.ui.View):
|
|||
inline=True
|
||||
)
|
||||
|
||||
await interaction.followup.send(embed=embed, file=discord.File(chart, 'staff_stats.png'), ephemeral=True)
|
||||
await interaction.followup.send(embed=embed, file=disnake.File(chart, 'staff_stats.png'), ephemeral=True)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Beautiful embed and message formatters for the ticket system.
|
|||
"""
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Dict, Any
|
||||
from discord import Embed, Color, User, Member, Role
|
||||
from disnake import Embed, Color, User, Member, Role
|
||||
|
||||
from ..data.models import (
|
||||
TicketData,
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ Ticket Permissions Utility
|
|||
==========================
|
||||
Centralized permission logic for the ticket system.
|
||||
"""
|
||||
import discord
|
||||
import disnake
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..data.models import GuildTicketConfig, TicketCategory
|
||||
|
||||
def is_staff(interaction: discord.Interaction, config: 'GuildTicketConfig', category: Optional['TicketCategory'] = None) -> bool:
|
||||
def is_staff(interaction: disnake.Interaction, config: 'GuildTicketConfig', category: Optional['TicketCategory'] = None) -> bool:
|
||||
"""
|
||||
Check if the user has staff permissions.
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ def is_staff(interaction: discord.Interaction, config: 'GuildTicketConfig', cate
|
|||
|
||||
return False
|
||||
|
||||
def can_access_config(interaction: discord.Interaction, config: 'GuildTicketConfig') -> bool:
|
||||
def can_access_config(interaction: disnake.Interaction, config: 'GuildTicketConfig') -> bool:
|
||||
"""
|
||||
Check if the user can access the configuration.
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ def can_access_config(interaction: discord.Interaction, config: 'GuildTicketConf
|
|||
|
||||
return False
|
||||
|
||||
def is_whitelisted(interaction: discord.Interaction, config: 'GuildTicketConfig', bot) -> bool:
|
||||
def is_whitelisted(interaction: disnake.Interaction, config: 'GuildTicketConfig', bot) -> bool:
|
||||
"""
|
||||
Check if user is whitelisted or has staff/config permissions.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import json
|
|||
import os
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Dict, Any
|
||||
from discord import User, Member
|
||||
from disnake import User, Member
|
||||
|
||||
from ..data.models import TicketData, TicketMessage
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue