add role panel

This commit is contained in:
Arthur Puechberty
2026-01-18 17:07:07 +01:00
parent 494015c7b1
commit 0ba91ac116
7 changed files with 1783 additions and 0 deletions
@@ -0,0 +1,382 @@
const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType } = require('discord.js');
const addCommand = require('../../fonctions/addCommand');
const db = require('../../db');
const buttonStyles = {
'primary': ButtonStyle.Primary,
'secondary': ButtonStyle.Secondary,
'success': ButtonStyle.Success,
'danger': ButtonStyle.Danger
};
// Fonction pour créer/mettre à jour le message du panel
async function updatePanelMessage(client, panel, buttons) {
try {
const guild = client.guilds.cache.get(panel.guild_id);
if (!guild) return null;
const channel = guild.channels.cache.get(panel.channel_id);
if (!channel) return null;
// Créer l'embed
const embed = new EmbedBuilder()
.setColor(panel.color || '#5865F2')
.setTitle(panel.title || '🎭 Choisissez vos rôles')
.setDescription(panel.description || 'Cliquez sur les boutons ci-dessous pour obtenir ou retirer des rôles.');
if (panel.image_url) embed.setImage(panel.image_url);
if (panel.thumbnail_url) embed.setThumbnail(panel.thumbnail_url);
// Infos sur le mode
const modeText = panel.exclusive
? '⚠️ *Un seul rôle possible à la fois*'
: (panel.mode === 'toggle' ? '💡 *Cliquez à nouveau pour retirer un rôle*' : '');
if (modeText) {
embed.setFooter({ text: modeText });
}
// Créer les boutons (max 5 par ligne, max 5 lignes)
const rows = [];
const enabledButtons = buttons.filter(b => b.enabled).sort((a, b) => a.position - b.position);
for (let i = 0; i < enabledButtons.length && rows.length < 5; i += 5) {
const row = new ActionRowBuilder();
const rowButtons = enabledButtons.slice(i, i + 5);
for (const btn of rowButtons) {
const button = new ButtonBuilder()
.setCustomId(`role_panel_${btn.id}`)
.setLabel(btn.label)
.setStyle(buttonStyles[btn.style] || ButtonStyle.Primary);
if (btn.emoji) {
// Vérifier si c'est un emoji custom ou unicode
if (btn.emoji.match(/^\d+$/)) {
button.setEmoji({ id: btn.emoji });
} else {
button.setEmoji(btn.emoji);
}
}
row.addComponents(button);
}
if (row.components.length > 0) {
rows.push(row);
}
}
// Mettre à jour ou créer le message
if (panel.message_id) {
try {
const message = await channel.messages.fetch(panel.message_id);
await message.edit({ embeds: [embed], components: rows });
return panel.message_id;
} catch {
// Message introuvable, en créer un nouveau
}
}
const message = await channel.send({ embeds: [embed], components: rows });
// Sauvegarder l'ID du message
await new Promise((resolve, reject) => {
db.run("UPDATE role_panels SET message_id = ? WHERE id = ?", [message.id, panel.id], (err) => {
if (err) reject(err);
else resolve();
});
});
return message.id;
} catch (err) {
console.error('Erreur mise à jour panel:', err);
return null;
}
}
module.exports = addCommand({
name: 'rolepanel',
description: 'Créer un panneau de rôles interactif',
aliases: ['rp', 'rolebuttons', 'reactionroles'],
permissions: ['ManageRoles', 'ManageMessages'],
botOwnerOnly: false,
dm: false,
scope: 'global',
slashOptions: [
{ type: 'STRING', name: 'action', description: 'Action à effectuer', required: true, choices: [
{ name: 'Créer un panel', value: 'create' },
{ name: 'Ajouter un bouton', value: 'addbutton' },
{ name: 'Supprimer un panel', value: 'delete' },
{ name: 'Liste des panels', value: 'list' },
{ name: 'Actualiser un panel', value: 'refresh' }
]},
{ type: 'STRING', name: 'nom', description: 'Nom du panel', required: false },
{ type: 'CHANNEL', name: 'salon', description: 'Salon où envoyer le panel', required: false },
{ type: 'ROLE', name: 'role', description: 'Rôle à associer (pour addbutton)', required: false },
{ type: 'STRING', name: 'label', description: 'Texte du bouton', required: false },
{ type: 'STRING', name: 'emoji', description: 'Emoji du bouton', required: false }
],
executeSlash: async (client, interaction) => {
const action = interaction.options.getString('action');
const guildId = interaction.guild.id;
switch (action) {
case 'create': {
const name = interaction.options.getString('nom');
const channel = interaction.options.getChannel('salon');
if (!name || !channel) {
return interaction.reply({
content: '❌ Vous devez spécifier un nom et un salon.\nUsage: `/rolepanel create nom:MonPanel salon:#roles`',
ephemeral: true
});
}
if (channel.type !== ChannelType.GuildText) {
return interaction.reply({
content: '❌ Le salon doit être un salon textuel.',
ephemeral: true
});
}
// Vérifier si un panel avec ce nom existe déjà
const existing = await db.getAsync(
"SELECT id FROM role_panels WHERE guild_id = ? AND name = ?",
[guildId, name]
);
if (existing) {
return interaction.reply({
content: '❌ Un panel avec ce nom existe déjà.',
ephemeral: true
});
}
// Créer le panel
const panelId = await new Promise((resolve, reject) => {
db.run(
"INSERT INTO role_panels (guild_id, channel_id, name, title, description) VALUES (?, ?, ?, ?, ?)",
[guildId, channel.id, name, `🎭 ${name}`, 'Cliquez sur les boutons ci-dessous pour obtenir vos rôles.'],
function(err) {
if (err) reject(err);
else resolve(this.lastID);
}
);
});
const embed = new EmbedBuilder()
.setColor(0x57F287)
.setTitle('✅ Panel créé')
.setDescription(`Le panel **${name}** a été créé.`)
.addFields(
{ name: '📁 Salon', value: `${channel}`, inline: true },
{ name: '🆔 ID', value: `${panelId}`, inline: true }
)
.setFooter({ text: 'Ajoutez des boutons avec /rolepanel addbutton' });
return interaction.reply({ embeds: [embed], ephemeral: true });
}
case 'addbutton': {
const name = interaction.options.getString('nom');
const role = interaction.options.getRole('role');
const label = interaction.options.getString('label');
const emoji = interaction.options.getString('emoji');
if (!name || !role) {
return interaction.reply({
content: '❌ Vous devez spécifier le nom du panel et un rôle.\nUsage: `/rolepanel addbutton nom:MonPanel role:@MonRole label:Mon Rôle emoji:🎮`',
ephemeral: true
});
}
// Trouver le panel
const panel = await db.getAsync(
"SELECT * FROM role_panels WHERE guild_id = ? AND name = ?",
[guildId, name]
);
if (!panel) {
return interaction.reply({
content: `❌ Panel "${name}" non trouvé. Utilisez \`/rolepanel list\` pour voir les panels existants.`,
ephemeral: true
});
}
// Vérifier le nombre de boutons (max 25)
const buttonCount = await db.getAsync(
"SELECT COUNT(*) as count FROM role_panel_buttons WHERE panel_id = ?",
[panel.id]
);
if (buttonCount.count >= 25) {
return interaction.reply({
content: '❌ Ce panel a atteint la limite de 25 boutons.',
ephemeral: true
});
}
// Ajouter le bouton
await new Promise((resolve, reject) => {
db.run(
"INSERT INTO role_panel_buttons (panel_id, role_id, label, emoji, position) VALUES (?, ?, ?, ?, ?)",
[panel.id, role.id, label || role.name, emoji || null, buttonCount.count],
(err) => {
if (err) reject(err);
else resolve();
}
);
});
// Mettre à jour le message
const buttons = await db.allAsync(
"SELECT * FROM role_panel_buttons WHERE panel_id = ?",
[panel.id]
);
await updatePanelMessage(client, panel, buttons);
const embed = new EmbedBuilder()
.setColor(0x57F287)
.setTitle('✅ Bouton ajouté')
.addFields(
{ name: '📋 Panel', value: name, inline: true },
{ name: '🎭 Rôle', value: `${role}`, inline: true },
{ name: '🏷️ Label', value: label || role.name, inline: true }
);
if (emoji) embed.addFields({ name: '😀 Emoji', value: emoji, inline: true });
return interaction.reply({ embeds: [embed], ephemeral: true });
}
case 'delete': {
const name = interaction.options.getString('nom');
if (!name) {
return interaction.reply({
content: '❌ Vous devez spécifier le nom du panel à supprimer.',
ephemeral: true
});
}
const panel = await db.getAsync(
"SELECT * FROM role_panels WHERE guild_id = ? AND name = ?",
[guildId, name]
);
if (!panel) {
return interaction.reply({
content: `❌ Panel "${name}" non trouvé.`,
ephemeral: true
});
}
// Supprimer le message
try {
const channel = interaction.guild.channels.cache.get(panel.channel_id);
if (channel && panel.message_id) {
const message = await channel.messages.fetch(panel.message_id).catch(() => null);
if (message) await message.delete();
}
} catch {}
// Supprimer de la DB
await new Promise((resolve, reject) => {
db.run("DELETE FROM role_panel_buttons WHERE panel_id = ?", [panel.id], (err) => {
if (err) reject(err);
else resolve();
});
});
await new Promise((resolve, reject) => {
db.run("DELETE FROM role_panels WHERE id = ?", [panel.id], (err) => {
if (err) reject(err);
else resolve();
});
});
return interaction.reply({
content: `✅ Panel **${name}** supprimé avec succès.`,
ephemeral: true
});
}
case 'list': {
const panels = await db.allAsync(
"SELECT rp.*, COUNT(rpb.id) as button_count FROM role_panels rp LEFT JOIN role_panel_buttons rpb ON rp.id = rpb.panel_id WHERE rp.guild_id = ? GROUP BY rp.id",
[guildId]
);
if (!panels || panels.length === 0) {
return interaction.reply({
content: '️ Aucun panel de rôles configuré. Créez-en un avec `/rolepanel create`.',
ephemeral: true
});
}
const embed = new EmbedBuilder()
.setColor(0x5865F2)
.setTitle('📋 Panels de rôles')
.setDescription(panels.map(p => {
const status = p.enabled ? '🟢' : '🔴';
return `${status} **${p.name}** - ${p.button_count} boutons - <#${p.channel_id}>`;
}).join('\n'));
return interaction.reply({ embeds: [embed], ephemeral: true });
}
case 'refresh': {
const name = interaction.options.getString('nom');
if (!name) {
return interaction.reply({
content: '❌ Vous devez spécifier le nom du panel à actualiser.',
ephemeral: true
});
}
const panel = await db.getAsync(
"SELECT * FROM role_panels WHERE guild_id = ? AND name = ?",
[guildId, name]
);
if (!panel) {
return interaction.reply({
content: `❌ Panel "${name}" non trouvé.`,
ephemeral: true
});
}
const buttons = await db.allAsync(
"SELECT * FROM role_panel_buttons WHERE panel_id = ?",
[panel.id]
);
const messageId = await updatePanelMessage(client, panel, buttons);
if (messageId) {
return interaction.reply({
content: `✅ Panel **${name}** actualisé !`,
ephemeral: true
});
} else {
return interaction.reply({
content: '❌ Erreur lors de l\'actualisation du panel.',
ephemeral: true
});
}
}
}
},
executePrefix: async (client, message, args) => {
return message.reply('❌ Cette commande est disponible uniquement en slash command. Utilisez `/rolepanel`.');
}
});
// Exporter la fonction de mise à jour pour l'API
module.exports.updatePanelMessage = updatePanelMessage;
+31
View File
@@ -410,6 +410,37 @@ db.exec(`
last_channel_activity INTEGER,
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
);
-- Système de rôles par boutons
CREATE TABLE IF NOT EXISTS role_panels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
message_id TEXT,
name TEXT NOT NULL,
title TEXT,
description TEXT,
color TEXT DEFAULT '#5865F2',
image_url TEXT,
thumbnail_url TEXT,
mode TEXT NOT NULL DEFAULT 'toggle',
exclusive INTEGER NOT NULL DEFAULT 0,
required_role_id TEXT,
enabled INTEGER NOT NULL DEFAULT 1,
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
);
CREATE TABLE IF NOT EXISTS role_panel_buttons (
id INTEGER PRIMARY KEY AUTOINCREMENT,
panel_id INTEGER NOT NULL,
role_id TEXT NOT NULL,
label TEXT NOT NULL,
emoji TEXT,
style TEXT NOT NULL DEFAULT 'primary',
position INTEGER NOT NULL DEFAULT 0,
enabled INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY (panel_id) REFERENCES role_panels(id) ON DELETE CASCADE
);
`);
module.exports = db;
+109
View File
@@ -0,0 +1,109 @@
const { Events, EmbedBuilder } = require("discord.js");
const db = require("../db");
module.exports = {
name: Events.InteractionCreate,
async execute(client, interaction) {
// Gérer uniquement les boutons de rôles
if (!interaction.isButton()) return;
if (!interaction.customId.startsWith('role_panel_')) return;
const buttonId = interaction.customId.replace('role_panel_', '');
try {
// Récupérer les infos du bouton
const button = await db.getAsync(
"SELECT rpb.*, rp.mode, rp.exclusive, rp.required_role_id, rp.enabled as panel_enabled FROM role_panel_buttons rpb JOIN role_panels rp ON rpb.panel_id = rp.id WHERE rpb.id = ?",
[buttonId]
);
if (!button) {
return interaction.reply({
content: '❌ Ce bouton n\'est plus valide.',
ephemeral: true
});
}
if (!button.panel_enabled || !button.enabled) {
return interaction.reply({
content: '❌ Ce système de rôles est actuellement désactivé.',
ephemeral: true
});
}
// Vérifier le rôle requis
if (button.required_role_id) {
if (!interaction.member.roles.cache.has(button.required_role_id)) {
return interaction.reply({
content: `❌ Vous devez avoir le rôle <@&${button.required_role_id}> pour utiliser ce menu.`,
ephemeral: true
});
}
}
const role = interaction.guild.roles.cache.get(button.role_id);
if (!role) {
return interaction.reply({
content: '❌ Le rôle associé à ce bouton n\'existe plus.',
ephemeral: true
});
}
// Vérifier que le bot peut gérer ce rôle
if (role.position >= interaction.guild.members.me.roles.highest.position) {
return interaction.reply({
content: '❌ Je ne peux pas gérer ce rôle car il est au-dessus de mon rôle le plus élevé.',
ephemeral: true
});
}
const hasRole = interaction.member.roles.cache.has(role.id);
// Mode exclusif : retirer les autres rôles du même panel
if (button.exclusive && !hasRole) {
const panelButtons = await db.allAsync(
"SELECT role_id FROM role_panel_buttons WHERE panel_id = ? AND id != ?",
[button.panel_id, buttonId]
);
for (const btn of panelButtons) {
if (interaction.member.roles.cache.has(btn.role_id)) {
await interaction.member.roles.remove(btn.role_id).catch(() => {});
}
}
}
// Gérer le rôle selon le mode
if (hasRole) {
// Mode toggle : retirer le rôle
if (button.mode === 'toggle') {
await interaction.member.roles.remove(role);
return interaction.reply({
content: `✅ Le rôle ${role} vous a été retiré.`,
ephemeral: true
});
} else {
// Mode add-only : ne pas retirer
return interaction.reply({
content: `️ Vous avez déjà le rôle ${role}.`,
ephemeral: true
});
}
} else {
// Ajouter le rôle
await interaction.member.roles.add(role);
return interaction.reply({
content: `✅ Le rôle ${role} vous a été attribué !`,
ephemeral: true
});
}
} catch (err) {
console.error('Erreur interaction role panel:', err);
return interaction.reply({
content: '❌ Une erreur est survenue.',
ephemeral: true
});
}
}
};
+242
View File
@@ -1286,3 +1286,245 @@ body {
color: var(--error-color);
background: rgba(237, 66, 69, 0.1);
}
/* ===== Modal ===== */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: var(--spacing-lg);
}
.modal-content {
background: var(--bg-card);
border-radius: var(--radius-lg);
width: 100%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
}
.modal-content.modal-lg {
max-width: 700px;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-lg);
border-bottom: 1px solid var(--border-color);
}
.modal-header h3 {
margin: 0;
font-size: 1.2rem;
}
.modal-close {
background: none;
border: none;
color: var(--text-muted);
font-size: 1.5rem;
cursor: pointer;
padding: 0;
line-height: 1;
}
.modal-close:hover {
color: var(--text-primary);
}
.modal-body {
padding: var(--spacing-lg);
}
.modal-footer {
padding: var(--spacing-lg);
border-top: 1px solid var(--border-color);
display: flex;
justify-content: flex-end;
gap: var(--spacing-sm);
align-items: center;
}
/* ===== Role Panels ===== */
.panels-list {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}
.panel-item {
background: var(--bg-secondary);
border-radius: var(--radius-md);
overflow: hidden;
}
.panel-item-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-md);
border-bottom: 1px solid var(--border-color);
}
.panel-item-info {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.panel-item-info h4 {
margin: 0;
font-size: 1rem;
}
.panel-status {
width: 10px;
height: 10px;
border-radius: 50%;
}
.panel-status.status-active {
background: #57F287;
}
.panel-status.status-inactive {
background: #ED4245;
}
.panel-meta {
color: var(--text-muted);
font-size: 0.85rem;
}
.panel-item-actions {
display: flex;
gap: var(--spacing-xs);
}
.panel-item-preview {
padding: var(--spacing-md);
}
.preview-embed {
background: var(--bg-main);
border-left: 4px solid #5865F2;
border-radius: var(--radius-sm);
padding: var(--spacing-md);
}
.preview-title {
font-weight: 600;
margin-bottom: var(--spacing-xs);
}
.preview-desc {
color: var(--text-muted);
font-size: 0.9rem;
margin-bottom: var(--spacing-sm);
}
.preview-buttons {
display: flex;
flex-wrap: wrap;
gap: var(--spacing-xs);
}
.preview-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 12px;
border-radius: 4px;
font-size: 0.85rem;
font-weight: 500;
}
.preview-btn-primary {
background: #5865F2;
color: white;
}
.preview-btn-secondary {
background: #4f545c;
color: white;
}
.preview-btn-success {
background: #57F287;
color: black;
}
.preview-btn-danger {
background: #ED4245;
color: white;
}
.preview-more {
color: var(--text-muted);
font-size: 0.85rem;
padding: 4px 8px;
}
/* Buttons list in modal */
.buttons-list {
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
}
.button-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spacing-md);
padding: var(--spacing-sm) var(--spacing-md);
background: var(--bg-secondary);
border-radius: var(--radius-sm);
}
.button-item.button-disabled {
opacity: 0.5;
}
.button-preview {
flex-shrink: 0;
}
.button-info {
flex: 1;
color: var(--text-muted);
font-size: 0.9rem;
}
.button-actions {
display: flex;
gap: var(--spacing-xs);
}
.btn-xs {
padding: 4px 8px;
font-size: 0.75rem;
}
.btn-sm {
padding: 6px 12px;
font-size: 0.85rem;
}
.empty-message, .loading-message {
text-align: center;
color: var(--text-muted);
padding: var(--spacing-lg);
}
+163
View File
@@ -82,6 +82,10 @@
<span class="nav-item-icon">🛡️</span>
Anti-Raid
</a>
<a class="nav-item" data-section="rolepanels">
<span class="nav-item-icon">🎭</span>
Rôles par boutons
</a>
</div>
</nav>
@@ -1898,6 +1902,164 @@
</div>
</section>
<!-- Section Role Panels -->
<section id="section-rolepanels" class="config-section">
<div class="config-card">
<div class="config-card-header">
<div class="config-card-title">
<span class="icon">🎭</span>
<h2>Rôles par boutons</h2>
</div>
</div>
<div class="config-card-body">
<p class="text-muted">
Créez des panneaux de rôles interactifs. Les utilisateurs pourront cliquer sur des boutons pour obtenir ou retirer des rôles.
</p>
<!-- Liste des panels -->
<div id="role-panels-list" class="panels-list">
<div class="loading-message">Chargement des panels...</div>
</div>
<!-- Bouton pour créer un nouveau panel -->
<button type="button" class="btn btn-primary" id="create-panel-btn" style="margin-top: 1rem;">
Créer un nouveau panel
</button>
</div>
</div>
<!-- Modal création/édition de panel -->
<div id="panel-modal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3 id="panel-modal-title">Créer un panel</h3>
<button class="modal-close" id="close-panel-modal">&times;</button>
</div>
<div class="modal-body">
<input type="hidden" id="edit-panel-id">
<div class="form-group">
<label class="form-label">📝 Nom du panel (identifiant unique)</label>
<input type="text" class="form-input" id="panel-name" placeholder="ex: couleurs, jeux, notifications">
</div>
<div class="form-group">
<label class="form-label">📁 Salon</label>
<select class="form-select" id="panel-channel"></select>
</div>
<div class="form-group">
<label class="form-label">🏷️ Titre de l'embed</label>
<input type="text" class="form-input" id="panel-title" placeholder="🎭 Choisissez vos rôles">
</div>
<div class="form-group">
<label class="form-label">📄 Description</label>
<textarea class="form-input" id="panel-description" rows="3" placeholder="Cliquez sur les boutons ci-dessous pour obtenir vos rôles."></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">🎨 Couleur</label>
<input type="color" class="form-input" id="panel-color" value="#5865F2">
</div>
<div class="form-group">
<label class="form-label">⚙️ Mode</label>
<select class="form-select" id="panel-mode">
<option value="toggle">Toggle (ajouter/retirer)</option>
<option value="add">Ajouter uniquement</option>
</select>
</div>
</div>
<div class="form-group">
<div class="checkbox-group">
<label class="checkbox-label">
<input type="checkbox" id="panel-exclusive">
Rôles exclusifs (un seul à la fois)
</label>
</div>
</div>
<div class="form-group">
<label class="form-label">🔒 Rôle requis (optionnel)</label>
<select class="form-select" id="panel-required-role">
<option value="">Aucun</option>
</select>
<small class="text-muted">Seuls les membres avec ce rôle pourront utiliser ce panel.</small>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">🖼️ Image URL (optionnel)</label>
<input type="text" class="form-input" id="panel-image" placeholder="https://...">
</div>
<div class="form-group">
<label class="form-label">🔲 Thumbnail URL (optionnel)</label>
<input type="text" class="form-input" id="panel-thumbnail" placeholder="https://...">
</div>
</div>
</div>
<div class="modal-footer">
<div id="status-panel-form" class="status-message"></div>
<button type="button" class="btn btn-secondary" id="cancel-panel-btn">Annuler</button>
<button type="button" class="btn btn-primary" id="save-panel-btn">💾 Sauvegarder</button>
</div>
</div>
</div>
<!-- Modal édition des boutons -->
<div id="buttons-modal" class="modal" style="display: none;">
<div class="modal-content modal-lg">
<div class="modal-header">
<h3 id="buttons-modal-title">Boutons du panel</h3>
<button class="modal-close" id="close-buttons-modal">&times;</button>
</div>
<div class="modal-body">
<input type="hidden" id="buttons-panel-id">
<!-- Liste des boutons existants -->
<div id="panel-buttons-list" class="buttons-list"></div>
<!-- Formulaire d'ajout de bouton -->
<div class="add-button-form" style="margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--border-color);">
<h4> Ajouter un bouton</h4>
<div class="form-row">
<div class="form-group">
<label class="form-label">🎭 Rôle</label>
<select class="form-select" id="new-button-role"></select>
</div>
<div class="form-group">
<label class="form-label">🏷️ Label</label>
<input type="text" class="form-input" id="new-button-label" placeholder="Mon rôle">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">😀 Emoji (optionnel)</label>
<input type="text" class="form-input" id="new-button-emoji" placeholder="🎮">
</div>
<div class="form-group">
<label class="form-label">🎨 Style</label>
<select class="form-select" id="new-button-style">
<option value="primary">Bleu (Primary)</option>
<option value="secondary">Gris (Secondary)</option>
<option value="success">Vert (Success)</option>
<option value="danger">Rouge (Danger)</option>
</select>
</div>
</div>
<button type="button" class="btn btn-success" id="add-button-btn"> Ajouter le bouton</button>
</div>
</div>
<div class="modal-footer">
<div id="status-buttons-form" class="status-message"></div>
<button type="button" class="btn btn-primary" id="publish-panel-btn">🚀 Publier / Actualiser le panel</button>
</div>
</div>
</div>
</section>
</div>
</main>
@@ -1920,5 +2082,6 @@
<script src="/guild/botAppearanceForm.js"></script>
<script src="/guild/logsForm.js"></script>
<script src="/guild/antiraidForm.js"></script>
<script src="/guild/rolePanelsForm.js"></script>
</body>
</html>
+448
View File
@@ -0,0 +1,448 @@
// =============================================
// ========== ROLE PANELS FORM =================
// =============================================
(function() {
let panels = [];
let channels = [];
let roles = [];
let currentPanelId = null;
// Charger les panels
async function loadRolePanels() {
try {
const res = await fetch(`/api/bot/get-role-panels?guildId=${guildId}`);
const data = await res.json();
if (data.success) {
panels = data.panels || [];
channels = data.channels || [];
roles = data.roles || [];
renderPanelsList();
populateSelects();
}
} catch (err) {
console.error('Erreur chargement panels:', err);
}
}
// Remplir les selects
function populateSelects() {
// Salon du panel
const channelSelect = document.getElementById('panel-channel');
if (channelSelect) {
channelSelect.innerHTML = channels.map(c => `<option value="${c.id}"># ${c.name}</option>`).join('');
}
// Rôle requis
const requiredRoleSelect = document.getElementById('panel-required-role');
if (requiredRoleSelect) {
requiredRoleSelect.innerHTML = '<option value="">Aucun</option>' +
roles.map(r => `<option value="${r.id}">${r.name}</option>`).join('');
}
// Rôle pour nouveau bouton
const newButtonRoleSelect = document.getElementById('new-button-role');
if (newButtonRoleSelect) {
newButtonRoleSelect.innerHTML = roles.map(r => `<option value="${r.id}">${r.name}</option>`).join('');
}
}
// Afficher la liste des panels
function renderPanelsList() {
const container = document.getElementById('role-panels-list');
if (!container) return;
if (panels.length === 0) {
container.innerHTML = '<div class="empty-message">Aucun panel de rôles configuré.</div>';
return;
}
container.innerHTML = panels.map(panel => {
const statusClass = panel.enabled ? 'status-active' : 'status-inactive';
const statusText = panel.enabled ? 'Actif' : 'Inactif';
const buttonCount = panel.buttons ? panel.buttons.length : 0;
const channel = channels.find(c => c.id === panel.channel_id);
return `
<div class="panel-item" data-panel-id="${panel.id}">
<div class="panel-item-header">
<div class="panel-item-info">
<span class="panel-status ${statusClass}"></span>
<h4>${panel.name}</h4>
<span class="panel-meta">${buttonCount} boutons #${channel?.name || 'inconnu'}</span>
</div>
<div class="panel-item-actions">
<button class="btn btn-sm btn-secondary" onclick="editPanelButtons(${panel.id})">🔘 Boutons</button>
<button class="btn btn-sm btn-secondary" onclick="editPanel(${panel.id})"> Modifier</button>
<button class="btn btn-sm btn-danger" onclick="deletePanel(${panel.id})">🗑</button>
</div>
</div>
<div class="panel-item-preview">
<div class="preview-embed" style="border-left-color: ${panel.color || '#5865F2'}">
<div class="preview-title">${panel.title || '🎭 Choisissez vos rôles'}</div>
<div class="preview-desc">${panel.description || ''}</div>
<div class="preview-buttons">
${(panel.buttons || []).slice(0, 5).map(btn => `
<span class="preview-btn preview-btn-${btn.style || 'primary'}">${btn.emoji || ''} ${btn.label}</span>
`).join('')}
${buttonCount > 5 ? `<span class="preview-more">+${buttonCount - 5} autres</span>` : ''}
</div>
</div>
</div>
</div>
`;
}).join('');
}
// Ouvrir le modal de création
function openCreateModal() {
document.getElementById('panel-modal-title').textContent = 'Créer un panel';
document.getElementById('edit-panel-id').value = '';
document.getElementById('panel-name').value = '';
document.getElementById('panel-name').disabled = false;
document.getElementById('panel-title').value = '';
document.getElementById('panel-description').value = '';
document.getElementById('panel-color').value = '#5865F2';
document.getElementById('panel-mode').value = 'toggle';
document.getElementById('panel-exclusive').checked = false;
document.getElementById('panel-required-role').value = '';
document.getElementById('panel-image').value = '';
document.getElementById('panel-thumbnail').value = '';
document.getElementById('panel-modal').style.display = 'flex';
}
// Ouvrir le modal d'édition
window.editPanel = function(panelId) {
const panel = panels.find(p => p.id === panelId);
if (!panel) return;
document.getElementById('panel-modal-title').textContent = 'Modifier le panel';
document.getElementById('edit-panel-id').value = panelId;
document.getElementById('panel-name').value = panel.name;
document.getElementById('panel-name').disabled = true;
document.getElementById('panel-channel').value = panel.channel_id;
document.getElementById('panel-title').value = panel.title || '';
document.getElementById('panel-description').value = panel.description || '';
document.getElementById('panel-color').value = panel.color || '#5865F2';
document.getElementById('panel-mode').value = panel.mode || 'toggle';
document.getElementById('panel-exclusive').checked = panel.exclusive;
document.getElementById('panel-required-role').value = panel.required_role_id || '';
document.getElementById('panel-image').value = panel.image_url || '';
document.getElementById('panel-thumbnail').value = panel.thumbnail_url || '';
document.getElementById('panel-modal').style.display = 'flex';
};
// Fermer le modal
function closeModal(modalId) {
document.getElementById(modalId).style.display = 'none';
}
// Sauvegarder le panel
async function savePanel() {
const panelId = document.getElementById('edit-panel-id').value;
const isEdit = !!panelId;
const data = {
guildId,
name: document.getElementById('panel-name').value,
channelId: document.getElementById('panel-channel').value,
title: document.getElementById('panel-title').value,
description: document.getElementById('panel-description').value,
color: document.getElementById('panel-color').value,
mode: document.getElementById('panel-mode').value,
exclusive: document.getElementById('panel-exclusive').checked,
requiredRoleId: document.getElementById('panel-required-role').value || null,
imageUrl: document.getElementById('panel-image').value || null,
thumbnailUrl: document.getElementById('panel-thumbnail').value || null,
enabled: true
};
if (!data.name) {
showStatus('status-panel-form', '❌ Le nom est requis', 'error');
return;
}
const statusEl = document.getElementById('status-panel-form');
statusEl.textContent = 'Sauvegarde en cours...';
try {
let res;
if (isEdit) {
data.panelId = panelId;
res = await fetch('/api/bot/update-role-panel', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
} else {
res = await fetch('/api/bot/create-role-panel', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
}
const result = await res.json();
if (result.success) {
showStatus('status-panel-form', '✅ Panel sauvegardé !', 'success');
setTimeout(() => {
closeModal('panel-modal');
loadRolePanels();
}, 1000);
} else {
showStatus('status-panel-form', '❌ ' + (result.error || 'Erreur'), 'error');
}
} catch (err) {
console.error('Erreur sauvegarde panel:', err);
showStatus('status-panel-form', '❌ Erreur de connexion', 'error');
}
}
// Supprimer un panel
window.deletePanel = async function(panelId) {
if (!confirm('Êtes-vous sûr de vouloir supprimer ce panel ? Le message sera également supprimé.')) {
return;
}
try {
const res = await fetch('/api/bot/delete-role-panel', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ guildId, panelId })
});
const result = await res.json();
if (result.success) {
loadRolePanels();
} else {
alert('Erreur: ' + (result.error || 'Erreur inconnue'));
}
} catch (err) {
console.error('Erreur suppression panel:', err);
alert('Erreur de connexion');
}
};
// Ouvrir le modal des boutons
window.editPanelButtons = function(panelId) {
const panel = panels.find(p => p.id === panelId);
if (!panel) return;
currentPanelId = panelId;
document.getElementById('buttons-modal-title').textContent = `Boutons: ${panel.name}`;
document.getElementById('buttons-panel-id').value = panelId;
renderButtonsList(panel.buttons || []);
document.getElementById('buttons-modal').style.display = 'flex';
};
// Afficher la liste des boutons
function renderButtonsList(buttons) {
const container = document.getElementById('panel-buttons-list');
if (buttons.length === 0) {
container.innerHTML = '<div class="empty-message">Aucun bouton configuré. Ajoutez-en un ci-dessous.</div>';
return;
}
container.innerHTML = buttons.map((btn, index) => {
const role = roles.find(r => r.id === btn.role_id);
const statusClass = btn.enabled ? '' : 'button-disabled';
return `
<div class="button-item ${statusClass}" data-button-id="${btn.id}">
<div class="button-preview">
<span class="preview-btn preview-btn-${btn.style || 'primary'}">${btn.emoji || ''} ${btn.label}</span>
</div>
<div class="button-info">
<span class="button-role"> @${role?.name || 'Rôle inconnu'}</span>
</div>
<div class="button-actions">
<button class="btn btn-xs btn-secondary" onclick="toggleButton(${btn.id}, ${!btn.enabled})">${btn.enabled ? '🔴 Désactiver' : '🟢 Activer'}</button>
<button class="btn btn-xs btn-danger" onclick="deleteButton(${btn.id})">🗑</button>
</div>
</div>
`;
}).join('');
}
// Ajouter un bouton
async function addButton() {
const panelId = document.getElementById('buttons-panel-id').value;
const roleId = document.getElementById('new-button-role').value;
const label = document.getElementById('new-button-label').value;
const emoji = document.getElementById('new-button-emoji').value;
const style = document.getElementById('new-button-style').value;
if (!roleId) {
showStatus('status-buttons-form', '❌ Sélectionnez un rôle', 'error');
return;
}
const role = roles.find(r => r.id === roleId);
const finalLabel = label || role?.name || 'Rôle';
try {
const res = await fetch('/api/bot/add-panel-button', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
guildId,
panelId,
roleId,
label: finalLabel,
emoji: emoji || null,
style
})
});
const result = await res.json();
if (result.success) {
showStatus('status-buttons-form', '✅ Bouton ajouté !', 'success');
// Reset le formulaire
document.getElementById('new-button-label').value = '';
document.getElementById('new-button-emoji').value = '';
// Recharger les panels
await loadRolePanels();
const panel = panels.find(p => p.id == panelId);
if (panel) renderButtonsList(panel.buttons || []);
} else {
showStatus('status-buttons-form', '❌ ' + (result.error || 'Erreur'), 'error');
}
} catch (err) {
console.error('Erreur ajout bouton:', err);
showStatus('status-buttons-form', '❌ Erreur de connexion', 'error');
}
}
// Activer/désactiver un bouton
window.toggleButton = async function(buttonId, enabled) {
try {
const res = await fetch('/api/bot/update-panel-button', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
guildId,
buttonId,
enabled,
// On garde les autres valeurs
roleId: panels.flatMap(p => p.buttons || []).find(b => b.id === buttonId)?.role_id,
label: panels.flatMap(p => p.buttons || []).find(b => b.id === buttonId)?.label,
emoji: panels.flatMap(p => p.buttons || []).find(b => b.id === buttonId)?.emoji,
style: panels.flatMap(p => p.buttons || []).find(b => b.id === buttonId)?.style,
position: panels.flatMap(p => p.buttons || []).find(b => b.id === buttonId)?.position
})
});
const result = await res.json();
if (result.success) {
await loadRolePanels();
const panelId = document.getElementById('buttons-panel-id').value;
const panel = panels.find(p => p.id == panelId);
if (panel) renderButtonsList(panel.buttons || []);
}
} catch (err) {
console.error('Erreur toggle bouton:', err);
}
};
// Supprimer un bouton
window.deleteButton = async function(buttonId) {
if (!confirm('Supprimer ce bouton ?')) return;
try {
const res = await fetch('/api/bot/delete-panel-button', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ guildId, buttonId })
});
const result = await res.json();
if (result.success) {
await loadRolePanels();
const panelId = document.getElementById('buttons-panel-id').value;
const panel = panels.find(p => p.id == panelId);
if (panel) renderButtonsList(panel.buttons || []);
}
} catch (err) {
console.error('Erreur suppression bouton:', err);
}
};
// Publier/actualiser le panel
async function publishPanel() {
const panelId = document.getElementById('buttons-panel-id').value;
try {
const res = await fetch('/api/bot/publish-role-panel', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ guildId, panelId })
});
const result = await res.json();
if (result.success) {
showStatus('status-buttons-form', '✅ Panel publié/actualisé !', 'success');
} else {
showStatus('status-buttons-form', '❌ ' + (result.error || 'Erreur'), 'error');
}
} catch (err) {
console.error('Erreur publication panel:', err);
showStatus('status-buttons-form', '❌ Erreur de connexion', 'error');
}
}
// Afficher un message de statut
function showStatus(elementId, message, type) {
const el = document.getElementById(elementId);
if (!el) return;
el.textContent = message;
el.className = 'status-message ' + (type || '');
setTimeout(() => {
el.textContent = '';
el.className = 'status-message';
}, 3000);
}
// Event Listeners (avec vérification d'existence)
const createPanelBtn = document.getElementById('create-panel-btn');
const closePanelModalBtn = document.getElementById('close-panel-modal');
const cancelPanelBtn = document.getElementById('cancel-panel-btn');
const savePanelBtn = document.getElementById('save-panel-btn');
const closeButtonsModalBtn = document.getElementById('close-buttons-modal');
const addButtonBtn = document.getElementById('add-button-btn');
const publishPanelBtn = document.getElementById('publish-panel-btn');
const panelModal = document.getElementById('panel-modal');
const buttonsModal = document.getElementById('buttons-modal');
if (createPanelBtn) createPanelBtn.addEventListener('click', openCreateModal);
if (closePanelModalBtn) closePanelModalBtn.addEventListener('click', () => closeModal('panel-modal'));
if (cancelPanelBtn) cancelPanelBtn.addEventListener('click', () => closeModal('panel-modal'));
if (savePanelBtn) savePanelBtn.addEventListener('click', savePanel);
if (closeButtonsModalBtn) closeButtonsModalBtn.addEventListener('click', () => closeModal('buttons-modal'));
if (addButtonBtn) addButtonBtn.addEventListener('click', addButton);
if (publishPanelBtn) publishPanelBtn.addEventListener('click', publishPanel);
// Fermer les modals en cliquant à l'extérieur
if (panelModal) {
panelModal.addEventListener('click', (e) => {
if (e.target === e.currentTarget) closeModal('panel-modal');
});
}
if (buttonsModal) {
buttonsModal.addEventListener('click', (e) => {
if (e.target === e.currentTarget) closeModal('buttons-modal');
});
}
// Charger au démarrage
loadRolePanels();
})();
+408
View File
@@ -2037,5 +2037,413 @@ module.exports = (app, db, client) => {
}
});
// =============================================
// ========== ROLE PANELS API ==================
// =============================================
// Récupérer tous les panels d'un serveur
router.get("/bot/get-role-panels", async (req, res) => {
const { guildId } = req.query;
if (!req.session.guilds) {
return res.status(401).json({ success: false, error: "Non connecté" });
}
const isAdmin = req.session.guilds.find(
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
);
if (!isAdmin) {
return res.status(403).json({ success: false, error: "Permission refusée" });
}
try {
const panels = await db.allAsync(
"SELECT * FROM role_panels WHERE guild_id = ? ORDER BY created_at DESC",
[guildId]
);
// Récupérer les boutons pour chaque panel
for (const panel of panels) {
panel.buttons = await db.allAsync(
"SELECT * FROM role_panel_buttons WHERE panel_id = ? ORDER BY position",
[panel.id]
);
}
// Récupérer les salons et rôles
const guild = client.guilds.cache.get(guildId);
const channels = guild?.channels.cache
.filter(c => c.type === 0)
.map(c => ({ id: c.id, name: c.name })) || [];
const roles = guild?.roles.cache
.filter(r => r.id !== guildId && !r.managed)
.sort((a, b) => b.position - a.position)
.map(r => ({ id: r.id, name: r.name, color: r.hexColor })) || [];
res.json({ success: true, panels, channels, roles });
} catch (err) {
console.error("Erreur get role panels:", err);
res.status(500).json({ success: false, error: err.message });
}
});
// Créer un nouveau panel
router.post("/bot/create-role-panel", express.json(), async (req, res) => {
const { guildId, name, channelId, title, description, color, mode, exclusive, requiredRoleId } = req.body;
if (!req.session.guilds) {
return res.status(401).json({ success: false, error: "Non connecté" });
}
const isAdmin = req.session.guilds.find(
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
);
if (!isAdmin) {
return res.status(403).json({ success: false, error: "Permission refusée" });
}
try {
// Vérifier si un panel avec ce nom existe
const existing = await db.getAsync(
"SELECT id FROM role_panels WHERE guild_id = ? AND name = ?",
[guildId, name]
);
if (existing) {
return res.json({ success: false, error: "Un panel avec ce nom existe déjà" });
}
const panelId = await new Promise((resolve, reject) => {
db.run(
`INSERT INTO role_panels (guild_id, channel_id, name, title, description, color, mode, exclusive, required_role_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[guildId, channelId, name, title, description, color || '#5865F2', mode || 'toggle', exclusive ? 1 : 0, requiredRoleId || null],
function(err) {
if (err) reject(err);
else resolve(this.lastID);
}
);
});
res.json({ success: true, panelId });
} catch (err) {
console.error("Erreur create role panel:", err);
res.status(500).json({ success: false, error: err.message });
}
});
// Mettre à jour un panel
router.post("/bot/update-role-panel", express.json(), async (req, res) => {
const { guildId, panelId, channelId, title, description, color, imageUrl, thumbnailUrl, mode, exclusive, requiredRoleId, enabled } = req.body;
if (!req.session.guilds) {
return res.status(401).json({ success: false, error: "Non connecté" });
}
const isAdmin = req.session.guilds.find(
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
);
if (!isAdmin) {
return res.status(403).json({ success: false, error: "Permission refusée" });
}
try {
await new Promise((resolve, reject) => {
db.run(
`UPDATE role_panels SET
channel_id = ?,
title = ?,
description = ?,
color = ?,
image_url = ?,
thumbnail_url = ?,
mode = ?,
exclusive = ?,
required_role_id = ?,
enabled = ?
WHERE id = ? AND guild_id = ?`,
[channelId, title, description, color, imageUrl || null, thumbnailUrl || null, mode, exclusive ? 1 : 0, requiredRoleId || null, enabled ? 1 : 0, panelId, guildId],
(err) => {
if (err) reject(err);
else resolve();
}
);
});
// Mettre à jour le message Discord
const panel = await db.getAsync("SELECT * FROM role_panels WHERE id = ?", [panelId]);
const buttons = await db.allAsync("SELECT * FROM role_panel_buttons WHERE panel_id = ?", [panelId]);
if (panel && enabled) {
const { updatePanelMessage } = require('../commands/🔧 Administration/rolepanel');
await updatePanelMessage(client, panel, buttons);
}
res.json({ success: true });
} catch (err) {
console.error("Erreur update role panel:", err);
res.status(500).json({ success: false, error: err.message });
}
});
// Supprimer un panel
router.post("/bot/delete-role-panel", express.json(), async (req, res) => {
const { guildId, panelId } = req.body;
if (!req.session.guilds) {
return res.status(401).json({ success: false, error: "Non connecté" });
}
const isAdmin = req.session.guilds.find(
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
);
if (!isAdmin) {
return res.status(403).json({ success: false, error: "Permission refusée" });
}
try {
// Récupérer le panel pour supprimer le message
const panel = await db.getAsync("SELECT * FROM role_panels WHERE id = ? AND guild_id = ?", [panelId, guildId]);
if (panel && panel.message_id) {
try {
const guild = client.guilds.cache.get(guildId);
const channel = guild?.channels.cache.get(panel.channel_id);
if (channel) {
const message = await channel.messages.fetch(panel.message_id).catch(() => null);
if (message) await message.delete();
}
} catch {}
}
// Supprimer les boutons puis le panel
await new Promise((resolve, reject) => {
db.run("DELETE FROM role_panel_buttons WHERE panel_id = ?", [panelId], (err) => {
if (err) reject(err);
else resolve();
});
});
await new Promise((resolve, reject) => {
db.run("DELETE FROM role_panels WHERE id = ? AND guild_id = ?", [panelId, guildId], (err) => {
if (err) reject(err);
else resolve();
});
});
res.json({ success: true });
} catch (err) {
console.error("Erreur delete role panel:", err);
res.status(500).json({ success: false, error: err.message });
}
});
// Ajouter un bouton à un panel
router.post("/bot/add-panel-button", express.json(), async (req, res) => {
const { guildId, panelId, roleId, label, emoji, style } = req.body;
if (!req.session.guilds) {
return res.status(401).json({ success: false, error: "Non connecté" });
}
const isAdmin = req.session.guilds.find(
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
);
if (!isAdmin) {
return res.status(403).json({ success: false, error: "Permission refusée" });
}
try {
// Vérifier le panel
const panel = await db.getAsync("SELECT * FROM role_panels WHERE id = ? AND guild_id = ?", [panelId, guildId]);
if (!panel) {
return res.json({ success: false, error: "Panel non trouvé" });
}
// Vérifier le nombre de boutons
const count = await db.getAsync("SELECT COUNT(*) as count FROM role_panel_buttons WHERE panel_id = ?", [panelId]);
if (count.count >= 25) {
return res.json({ success: false, error: "Limite de 25 boutons atteinte" });
}
// Ajouter le bouton
const buttonId = await new Promise((resolve, reject) => {
db.run(
"INSERT INTO role_panel_buttons (panel_id, role_id, label, emoji, style, position) VALUES (?, ?, ?, ?, ?, ?)",
[panelId, roleId, label, emoji || null, style || 'primary', count.count],
function(err) {
if (err) reject(err);
else resolve(this.lastID);
}
);
});
// Mettre à jour le message
const buttons = await db.allAsync("SELECT * FROM role_panel_buttons WHERE panel_id = ?", [panelId]);
const { updatePanelMessage } = require('../commands/🔧 Administration/rolepanel');
await updatePanelMessage(client, panel, buttons);
res.json({ success: true, buttonId });
} catch (err) {
console.error("Erreur add panel button:", err);
res.status(500).json({ success: false, error: err.message });
}
});
// Mettre à jour un bouton
router.post("/bot/update-panel-button", express.json(), async (req, res) => {
const { guildId, buttonId, roleId, label, emoji, style, enabled, position } = req.body;
if (!req.session.guilds) {
return res.status(401).json({ success: false, error: "Non connecté" });
}
const isAdmin = req.session.guilds.find(
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
);
if (!isAdmin) {
return res.status(403).json({ success: false, error: "Permission refusée" });
}
try {
// Vérifier que le bouton appartient bien à un panel de ce serveur
const button = await db.getAsync(
`SELECT rpb.*, rp.guild_id FROM role_panel_buttons rpb
JOIN role_panels rp ON rpb.panel_id = rp.id
WHERE rpb.id = ? AND rp.guild_id = ?`,
[buttonId, guildId]
);
if (!button) {
return res.json({ success: false, error: "Bouton non trouvé" });
}
await new Promise((resolve, reject) => {
db.run(
"UPDATE role_panel_buttons SET role_id = ?, label = ?, emoji = ?, style = ?, enabled = ?, position = ? WHERE id = ?",
[roleId, label, emoji || null, style, enabled ? 1 : 0, position, buttonId],
(err) => {
if (err) reject(err);
else resolve();
}
);
});
// Mettre à jour le message
const panel = await db.getAsync("SELECT * FROM role_panels WHERE id = ?", [button.panel_id]);
const buttons = await db.allAsync("SELECT * FROM role_panel_buttons WHERE panel_id = ?", [button.panel_id]);
const { updatePanelMessage } = require('../commands/🔧 Administration/rolepanel');
await updatePanelMessage(client, panel, buttons);
res.json({ success: true });
} catch (err) {
console.error("Erreur update panel button:", err);
res.status(500).json({ success: false, error: err.message });
}
});
// Supprimer un bouton
router.post("/bot/delete-panel-button", express.json(), async (req, res) => {
const { guildId, buttonId } = req.body;
if (!req.session.guilds) {
return res.status(401).json({ success: false, error: "Non connecté" });
}
const isAdmin = req.session.guilds.find(
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
);
if (!isAdmin) {
return res.status(403).json({ success: false, error: "Permission refusée" });
}
try {
// Vérifier que le bouton appartient à un panel de ce serveur
const button = await db.getAsync(
`SELECT rpb.*, rp.guild_id FROM role_panel_buttons rpb
JOIN role_panels rp ON rpb.panel_id = rp.id
WHERE rpb.id = ? AND rp.guild_id = ?`,
[buttonId, guildId]
);
if (!button) {
return res.json({ success: false, error: "Bouton non trouvé" });
}
const panelId = button.panel_id;
await new Promise((resolve, reject) => {
db.run("DELETE FROM role_panel_buttons WHERE id = ?", [buttonId], (err) => {
if (err) reject(err);
else resolve();
});
});
// Mettre à jour le message
const panel = await db.getAsync("SELECT * FROM role_panels WHERE id = ?", [panelId]);
const buttons = await db.allAsync("SELECT * FROM role_panel_buttons WHERE panel_id = ?", [panelId]);
const { updatePanelMessage } = require('../commands/🔧 Administration/rolepanel');
await updatePanelMessage(client, panel, buttons);
res.json({ success: true });
} catch (err) {
console.error("Erreur delete panel button:", err);
res.status(500).json({ success: false, error: err.message });
}
});
// Publier/actualiser un panel
router.post("/bot/publish-role-panel", express.json(), async (req, res) => {
const { guildId, panelId } = req.body;
if (!req.session.guilds) {
return res.status(401).json({ success: false, error: "Non connecté" });
}
const isAdmin = req.session.guilds.find(
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
);
if (!isAdmin) {
return res.status(403).json({ success: false, error: "Permission refusée" });
}
try {
const panel = await db.getAsync("SELECT * FROM role_panels WHERE id = ? AND guild_id = ?", [panelId, guildId]);
if (!panel) {
return res.json({ success: false, error: "Panel non trouvé" });
}
const buttons = await db.allAsync("SELECT * FROM role_panel_buttons WHERE panel_id = ?", [panelId]);
const { updatePanelMessage } = require('../commands/🔧 Administration/rolepanel');
const messageId = await updatePanelMessage(client, panel, buttons);
if (messageId) {
res.json({ success: true, messageId });
} else {
res.json({ success: false, error: "Erreur lors de la publication" });
}
} catch (err) {
console.error("Erreur publish role panel:", err);
res.status(500).json({ success: false, error: err.message });
}
});
app.use("/api", router);
};