From 77842b3685caafbadb1661a63e39c1ecea4193b8 Mon Sep 17 00:00:00 2001 From: Arthur Puechberty Date: Sun, 18 Jan 2026 13:40:38 +0100 Subject: [PATCH] =?UTF-8?q?ajouter=20un=20syst=C3=A8me=20de=20messages=20p?= =?UTF-8?q?rogramm=C3=A9s=20avec=20options=20de=20planification=20et=20int?= =?UTF-8?q?erface=20utilisateur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/bot.js | 142 ++++++++ app/db.js | 23 ++ app/public/guild.css | 173 +++++++++ app/public/guild.html | 157 +++++++++ app/public/guild/scheduledMessagesForm.js | 412 ++++++++++++++++++++++ app/public/index.html | 5 + app/routes/api.js | 157 +++++++++ 7 files changed, 1069 insertions(+) create mode 100644 app/public/guild/scheduledMessagesForm.js diff --git a/app/bot.js b/app/bot.js index def4d6d..622e6cf 100644 --- a/app/bot.js +++ b/app/bot.js @@ -410,6 +410,148 @@ client.once("clientReady", async () => { }); +// ===== SCHEDULED MESSAGES SYSTEM ===== +const { EmbedBuilder } = require("discord.js"); + +// Track last channel activity +const channelLastActivity = new Map(); + +// Update last activity on message +client.on("messageCreate", (message) => { + if (!message.guild || message.author.bot) return; + channelLastActivity.set(message.channel.id, Date.now()); +}); + +// Process scheduled messages +async function processScheduledMessages() { + try { + const messages = await db.allAsync( + "SELECT * FROM scheduled_messages WHERE enabled = 1" + ); + + // Utiliser le fuseau horaire français + const now = new Date(); + const parisTime = new Date(now.toLocaleString("en-US", { timeZone: "Europe/Paris" })); + const currentDay = parisTime.getDay(); // 0-6 (Sunday-Saturday) + const currentHour = parisTime.getHours().toString().padStart(2, '0'); + const currentMinute = parisTime.getMinutes().toString().padStart(2, '0'); + const currentTime = `${currentHour}:${currentMinute}`; + const currentTimestamp = Date.now(); + + for (const msg of messages) { + try { + const guild = client.guilds.cache.get(msg.guild_id); + if (!guild) continue; + + const channel = guild.channels.cache.get(msg.channel_id); + if (!channel) continue; + + let shouldSend = false; + + if (msg.schedule_type === "weekly") { + // Check day and time + const days = JSON.parse(msg.days_of_week || "[]").map(d => parseInt(d)); + const times = JSON.parse(msg.times_of_day || "[]"); + + if (days.includes(currentDay) && times.includes(currentTime)) { + // Check if already sent this minute + const lastSent = msg.last_sent_at || 0; + const oneMinuteAgo = currentTimestamp - 60000; + + if (lastSent < oneMinuteAgo) { + shouldSend = true; + } + } + } else if (msg.schedule_type === "interval") { + // Check interval + const intervalMs = msg.interval_unit === "hours" + ? msg.interval_value * 60 * 60 * 1000 + : msg.interval_value * 60 * 1000; + + const lastSent = msg.last_sent_at || 0; + + if (currentTimestamp - lastSent >= intervalMs) { + shouldSend = true; + } + } + + if (!shouldSend) continue; + + // Check force_send option + if (!msg.force_send) { + const lastActivity = channelLastActivity.get(msg.channel_id) || msg.last_channel_activity || 0; + const lastSent = msg.last_sent_at || 0; + + // If no activity since last send, skip + if (lastActivity <= lastSent) { + continue; + } + } + + // Delete previous message if option enabled + if (msg.delete_previous && msg.last_message_id) { + try { + const oldMessage = await channel.messages.fetch(msg.last_message_id); + if (oldMessage) await oldMessage.delete(); + } catch (err) { + // Message may have been deleted already + } + } + + // Build message content + const messageOptions = {}; + + if (msg.message_content && msg.message_content.trim()) { + messageOptions.content = msg.message_content; + } + + if (msg.embed_enabled) { + const embed = new EmbedBuilder(); + + if (msg.embed_title) embed.setTitle(msg.embed_title); + if (msg.embed_description) embed.setDescription(msg.embed_description); + if (msg.embed_color) { + const color = msg.embed_color.startsWith('#') + ? parseInt(msg.embed_color.slice(1), 16) + : parseInt(msg.embed_color, 16); + embed.setColor(color); + } + embed.setTimestamp(); + + messageOptions.embeds = [embed]; + } + + // Send message + const sentMessage = await channel.send(messageOptions); + + // Update database + db.run( + `UPDATE scheduled_messages SET + last_sent_at = ?, + last_message_id = ?, + last_channel_activity = ? + WHERE id = ?`, + [currentTimestamp, sentMessage.id, channelLastActivity.get(msg.channel_id) || currentTimestamp, msg.id] + ); + + console.log(`📨 Message programmé envoyé: ${msg.id} dans ${channel.name}`); + + } catch (err) { + console.error(`Erreur envoi message programmé ${msg.id}:`, err); + } + } + } catch (err) { + console.error("Erreur processScheduledMessages:", err); + } +} + +// Run every minute to check scheduled messages +setInterval(processScheduledMessages, 60 * 1000); + +// Initial run after 10 seconds +setTimeout(processScheduledMessages, 10 * 1000); + + client.login(process.env.BOT_TOKEN); module.exports = client; diff --git a/app/db.js b/app/db.js index ce3b3a1..8f18bb0 100644 --- a/app/db.js +++ b/app/db.js @@ -213,6 +213,29 @@ db.exec(` ); CREATE INDEX IF NOT EXISTS idx_user_activity_stats_date ON user_activity_stats(guild_id, user_id, date); + + CREATE TABLE IF NOT EXISTS scheduled_messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + guild_id TEXT NOT NULL, + channel_id TEXT NOT NULL, + message_content TEXT NOT NULL, + embed_enabled INTEGER NOT NULL DEFAULT 0, + embed_title TEXT, + embed_description TEXT, + embed_color TEXT DEFAULT '#5865F2', + schedule_type TEXT NOT NULL DEFAULT 'weekly', + days_of_week TEXT DEFAULT '[]', + times_of_day TEXT DEFAULT '[]', + interval_value INTEGER DEFAULT 60, + interval_unit TEXT DEFAULT 'minutes', + force_send INTEGER NOT NULL DEFAULT 1, + delete_previous INTEGER NOT NULL DEFAULT 0, + enabled INTEGER NOT NULL DEFAULT 1, + last_sent_at INTEGER, + last_message_id TEXT, + last_channel_activity INTEGER, + created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) + ); `); module.exports = db; diff --git a/app/public/guild.css b/app/public/guild.css index 6b927ac..1f9661c 100644 --- a/app/public/guild.css +++ b/app/public/guild.css @@ -626,3 +626,176 @@ body { gap: var(--spacing-md); } } + +/* ===== Scheduled Messages ===== */ +.checkbox-group { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-sm); +} + +.checkbox-item { + display: flex; + align-items: center; + gap: var(--spacing-xs); + padding: var(--spacing-xs) var(--spacing-sm); + background: var(--bg-secondary); + border-radius: var(--radius-sm); + cursor: pointer; + user-select: none; +} + +.checkbox-item:hover { + background: var(--bg-card); +} + +.checkbox-item input { + cursor: pointer; +} + +.checkbox-group-vertical { + display: flex; + flex-direction: column; + gap: var(--spacing-sm); +} + +.checkbox-group-vertical .checkbox-item { + width: fit-content; +} + +.times-input-container { + display: flex; + gap: var(--spacing-sm); + align-items: center; + margin-bottom: var(--spacing-sm); +} + +.times-list { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-xs); + min-height: 32px; +} + +.time-tag { + display: inline-flex; + align-items: center; + gap: var(--spacing-xs); + padding: 4px 10px; + background: var(--primary-color); + color: white; + border-radius: var(--radius-sm); + font-size: 0.85rem; +} + +.time-remove { + background: none; + border: none; + color: white; + cursor: pointer; + font-size: 1.1rem; + line-height: 1; + opacity: 0.7; +} + +.time-remove:hover { + opacity: 1; +} + +.form-actions { + display: flex; + gap: var(--spacing-sm); + margin-top: var(--spacing-md); +} + +.scheduled-message-item { + background: var(--bg-secondary); + border-radius: var(--radius-md); + padding: var(--spacing-md); + margin-bottom: var(--spacing-sm); +} + +.scheduled-message-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: var(--spacing-sm); +} + +.scheduled-message-info strong { + color: var(--text-primary); + display: block; +} + +.scheduled-message-schedule { + font-size: 0.85rem; + color: var(--text-muted); +} + +.status-badge { + padding: 2px 8px; + border-radius: var(--radius-sm); + font-size: 0.75rem; + font-weight: 600; +} + +.status-active { + background: rgba(87, 242, 135, 0.2); + color: #57F287; +} + +.status-inactive { + background: rgba(237, 66, 69, 0.2); + color: #ED4245; +} + +.scheduled-message-preview { + background: var(--bg-card); + padding: var(--spacing-sm); + border-radius: var(--radius-sm); + font-size: 0.9rem; + color: var(--text-secondary); + margin-bottom: var(--spacing-sm); + word-break: break-word; +} + +.embed-badge { + display: inline-block; + padding: 2px 6px; + background: var(--primary-color); + color: white; + border-radius: var(--radius-sm); + font-size: 0.75rem; + margin-left: var(--spacing-xs); +} + +.scheduled-message-options { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-xs); + margin-bottom: var(--spacing-sm); +} + +.option-tag { + padding: 2px 8px; + background: var(--bg-card); + border-radius: var(--radius-sm); + font-size: 0.75rem; + color: var(--text-muted); +} + +.scheduled-message-actions { + display: flex; + gap: var(--spacing-sm); + flex-wrap: wrap; +} + +.btn-warning { + background: #F0B232; + color: #000; +} + +.btn-warning:hover { + background: #d9a02a; +} + diff --git a/app/public/guild.html b/app/public/guild.html index a00e743..4e4c04c 100644 --- a/app/public/guild.html +++ b/app/public/guild.html @@ -62,6 +62,10 @@ 📊 Salons de stats + + + Messages programmés + @@ -776,6 +780,158 @@ + +
+
+
+
+

⏰ Messages Programmés

+

Programmez l'envoi automatique de messages dans vos salons

+
+
+
+ + +
+

➕ Nouveau message programmé

+ + + +
+ + +
+ +
+ + +
+ + +
+ +
+ + + + +
+ + +
+ + +
+
+ +
+ + + + + + + +
+
+ +
+ +
+ + : + + +
+
+
+
+ + + + + +
+ +
+ + + +
+
+ +
+ + +
+
+ + +
+

📋 Messages programmés

+
+

Aucun message programmé.

+
+
+ +
+
+
+ @@ -793,5 +949,6 @@ + diff --git a/app/public/guild/scheduledMessagesForm.js b/app/public/guild/scheduledMessagesForm.js new file mode 100644 index 0000000..f6b73fd --- /dev/null +++ b/app/public/guild/scheduledMessagesForm.js @@ -0,0 +1,412 @@ +// ===== SCHEDULED MESSAGES FORM ===== +(async function () { + const channelSelect = document.getElementById("scheduled-channel-select"); + const messageContent = document.getElementById("scheduled-message-content"); + const embedEnabled = document.getElementById("scheduled-embed-enabled"); + const embedOptions = document.getElementById("scheduled-embed-options"); + const embedTitle = document.getElementById("scheduled-embed-title"); + const embedDescription = document.getElementById("scheduled-embed-description"); + const embedColor = document.getElementById("scheduled-embed-color"); + const scheduleType = document.getElementById("scheduled-type"); + const weeklyOptions = document.getElementById("scheduled-weekly-options"); + const intervalOptions = document.getElementById("scheduled-interval-options"); + const daysContainer = document.getElementById("scheduled-days"); + const hourSelect = document.getElementById("scheduled-hour-select"); + const minuteSelect = document.getElementById("scheduled-minute-select"); + const addTimeBtn = document.getElementById("scheduled-add-time"); + const timesList = document.getElementById("scheduled-times-list"); + const intervalValue = document.getElementById("scheduled-interval-value"); + const intervalUnit = document.getElementById("scheduled-interval-unit"); + const forceSend = document.getElementById("scheduled-force-send"); + const deletePrevious = document.getElementById("scheduled-delete-previous"); + const enabledCheckbox = document.getElementById("scheduled-enabled"); + const saveBtn = document.getElementById("scheduled-save-btn"); + const cancelBtn = document.getElementById("scheduled-cancel-btn"); + const editIdInput = document.getElementById("scheduled-edit-id"); + const formTitle = document.getElementById("scheduled-form-title"); + const listContainer = document.getElementById("scheduled-messages-list"); + + let selectedTimes = []; + + // Populate hour and minute selects + function initTimeSelects() { + const now = new Date(); + const currentHour = now.getHours().toString().padStart(2, "0"); + const currentMinute = now.getMinutes().toString().padStart(2, "0"); + + // Hours 00-23 + for (let h = 0; h < 24; h++) { + const opt = document.createElement("option"); + opt.value = h.toString().padStart(2, "0"); + opt.textContent = h.toString().padStart(2, "0"); + hourSelect.appendChild(opt); + } + // Minutes 00-59 + for (let m = 0; m < 60; m++) { + const opt = document.createElement("option"); + opt.value = m.toString().padStart(2, "0"); + opt.textContent = m.toString().padStart(2, "0"); + minuteSelect.appendChild(opt); + } + + // Set default to current time + hourSelect.value = currentHour; + minuteSelect.value = currentMinute; + } + + // Toggle embed options + embedEnabled.addEventListener("change", () => { + embedOptions.style.display = embedEnabled.checked ? "block" : "none"; + }); + + // Toggle schedule type options + scheduleType.addEventListener("change", () => { + if (scheduleType.value === "weekly") { + weeklyOptions.style.display = "block"; + intervalOptions.style.display = "none"; + } else { + weeklyOptions.style.display = "none"; + intervalOptions.style.display = "block"; + } + }); + + // Add time + addTimeBtn.addEventListener("click", () => { + const hour = hourSelect.value; + const minute = minuteSelect.value; + + if (!hour || !minute) { + alert("Veuillez sélectionner une heure et des minutes."); + return; + } + + const time = `${hour}:${minute}`; + if (selectedTimes.includes(time)) { + alert("Cette heure est déjà ajoutée."); + return; + } + + selectedTimes.push(time); + selectedTimes.sort(); + renderTimesList(); + hourSelect.value = ""; + minuteSelect.value = ""; + }); + + function renderTimesList() { + if (selectedTimes.length === 0) { + timesList.innerHTML = 'Aucune heure sélectionnée'; + return; + } + + timesList.innerHTML = selectedTimes.map(time => ` + + ${time} + + + `).join(""); + + document.querySelectorAll(".time-remove").forEach(btn => { + btn.addEventListener("click", () => { + selectedTimes = selectedTimes.filter(t => t !== btn.dataset.time); + renderTimesList(); + }); + }); + } + + // Load text channels + async function loadChannels() { + try { + const res = await fetch(`/api/bot/get-text-channels/${guildId}`); + const channels = await res.json(); + channelSelect.innerHTML = ''; + channels.forEach(ch => { + const opt = document.createElement("option"); + opt.value = ch.id; + opt.textContent = "# " + ch.name; + channelSelect.appendChild(opt); + }); + } catch (err) { + console.error("Erreur chargement salons:", err); + } + } + + // Get selected days + function getSelectedDays() { + const days = []; + daysContainer.querySelectorAll("input:checked").forEach(cb => { + days.push(parseInt(cb.value)); + }); + return days; + } + + // Set selected days + function setSelectedDays(days) { + daysContainer.querySelectorAll("input").forEach(cb => { + cb.checked = days.includes(parseInt(cb.value)); + }); + } + + // Reset form + function resetForm() { + editIdInput.value = ""; + formTitle.textContent = "➕ Nouveau message programmé"; + channelSelect.value = ""; + messageContent.value = ""; + embedEnabled.checked = false; + embedOptions.style.display = "none"; + embedTitle.value = ""; + embedDescription.value = ""; + embedColor.value = "#5865F2"; + scheduleType.value = "weekly"; + weeklyOptions.style.display = "block"; + intervalOptions.style.display = "none"; + setSelectedDays([]); + selectedTimes = []; + renderTimesList(); + intervalValue.value = 60; + intervalUnit.value = "minutes"; + forceSend.checked = true; + deletePrevious.checked = false; + enabledCheckbox.checked = true; + saveBtn.textContent = "💾 Enregistrer"; + cancelBtn.style.display = "none"; + } + + // Load scheduled messages list + async function loadScheduledMessages() { + try { + const res = await fetch(`/api/bot/get-scheduled-messages/${guildId}`); + const messages = await res.json(); + + if (messages.length === 0) { + listContainer.innerHTML = '

Aucun message programmé.

'; + return; + } + + // Get channels for names + const channelsRes = await fetch(`/api/bot/get-text-channels/${guildId}`); + const channels = await channelsRes.json(); + const channelMap = {}; + channels.forEach(ch => channelMap[ch.id] = ch.name); + + listContainer.innerHTML = messages.map(msg => { + const channelName = channelMap[msg.channel_id] || "Salon inconnu"; + const scheduleInfo = getScheduleInfo(msg); + const statusClass = msg.enabled ? "status-active" : "status-inactive"; + const statusText = msg.enabled ? "Actif" : "Inactif"; + + return ` +
+
+
+ # ${channelName} + ${scheduleInfo} +
+ ${statusText} +
+
+ ${msg.message_content ? escapeHtml(msg.message_content.substring(0, 100)) + (msg.message_content.length > 100 ? '...' : '') : ''} + ${msg.embed_enabled ? '📦 Embed' : ''} +
+
+ ${msg.force_send ? 'Force envoi' : 'Vérifie activité'} + ${msg.delete_previous ? 'Supprime précédent' : ''} +
+
+ + + +
+
+ `; + }).join(""); + + // Event listeners + document.querySelectorAll(".edit-scheduled").forEach(btn => { + btn.addEventListener("click", () => editMessage(messages.find(m => m.id == btn.dataset.id))); + }); + + document.querySelectorAll(".toggle-scheduled").forEach(btn => { + btn.addEventListener("click", () => toggleMessage(btn.dataset.id)); + }); + + document.querySelectorAll(".delete-scheduled").forEach(btn => { + btn.addEventListener("click", () => deleteMessage(btn.dataset.id)); + }); + + } catch (err) { + console.error("Erreur chargement messages:", err); + } + } + + function getScheduleInfo(msg) { + if (msg.schedule_type === "interval") { + return `Toutes les ${msg.interval_value} ${msg.interval_unit === 'hours' ? 'heures' : 'minutes'}`; + } else { + const days = JSON.parse(msg.days_of_week || "[]"); + const times = JSON.parse(msg.times_of_day || "[]"); + const dayNames = ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam']; + const dayStr = days.map(d => dayNames[d]).join(', ') || 'Aucun jour'; + const timeStr = times.join(', ') || 'Aucune heure'; + return `${dayStr} à ${timeStr}`; + } + } + + function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + + // Edit message + function editMessage(msg) { + editIdInput.value = msg.id; + formTitle.textContent = "✏️ Modifier le message programmé"; + channelSelect.value = msg.channel_id; + messageContent.value = msg.message_content || ""; + embedEnabled.checked = !!msg.embed_enabled; + embedOptions.style.display = msg.embed_enabled ? "block" : "none"; + embedTitle.value = msg.embed_title || ""; + embedDescription.value = msg.embed_description || ""; + embedColor.value = msg.embed_color || "#5865F2"; + scheduleType.value = msg.schedule_type || "weekly"; + + if (msg.schedule_type === "interval") { + weeklyOptions.style.display = "none"; + intervalOptions.style.display = "block"; + } else { + weeklyOptions.style.display = "block"; + intervalOptions.style.display = "none"; + } + + setSelectedDays(JSON.parse(msg.days_of_week || "[]")); + selectedTimes = JSON.parse(msg.times_of_day || "[]"); + renderTimesList(); + intervalValue.value = msg.interval_value || 60; + intervalUnit.value = msg.interval_unit || "minutes"; + forceSend.checked = !!msg.force_send; + deletePrevious.checked = !!msg.delete_previous; + enabledCheckbox.checked = !!msg.enabled; + saveBtn.textContent = "💾 Mettre à jour"; + cancelBtn.style.display = "inline-block"; + + // Scroll to form + document.getElementById("scheduled-form-section").scrollIntoView({ behavior: "smooth" }); + } + + // Toggle message + async function toggleMessage(id) { + try { + await fetch(`/api/bot/toggle-scheduled-message/${id}`, { method: "PATCH" }); + loadScheduledMessages(); + } catch (err) { + console.error("Erreur toggle:", err); + } + } + + // Delete message + async function deleteMessage(id) { + if (!confirm("Supprimer ce message programmé ?")) return; + + try { + await fetch(`/api/bot/delete-scheduled-message/${id}`, { method: "DELETE" }); + loadScheduledMessages(); + } catch (err) { + console.error("Erreur suppression:", err); + } + } + + // Save button + saveBtn.addEventListener("click", async () => { + const channel = channelSelect.value; + if (!channel) { + alert("Veuillez sélectionner un salon."); + return; + } + + const content = messageContent.value; + const hasEmbed = embedEnabled.checked && (embedTitle.value || embedDescription.value); + + if (!content && !hasEmbed) { + alert("Veuillez entrer un message ou configurer un embed."); + return; + } + + if (scheduleType.value === "weekly") { + if (getSelectedDays().length === 0) { + alert("Veuillez sélectionner au moins un jour."); + return; + } + if (selectedTimes.length === 0) { + alert("Veuillez ajouter au moins une heure."); + return; + } + } + + saveBtn.disabled = true; + saveBtn.textContent = "Enregistrement..."; + + const data = { + guildId, + channelId: channel, + messageContent: content, + embedEnabled: embedEnabled.checked, + embedTitle: embedTitle.value, + embedDescription: embedDescription.value, + embedColor: embedColor.value, + scheduleType: scheduleType.value, + daysOfWeek: getSelectedDays(), + timesOfDay: selectedTimes, + intervalValue: parseInt(intervalValue.value) || 60, + intervalUnit: intervalUnit.value, + forceSend: forceSend.checked, + deletePrevious: deletePrevious.checked, + enabled: enabledCheckbox.checked + }; + + try { + const editId = editIdInput.value; + let res; + + if (editId) { + res = await fetch(`/api/bot/update-scheduled-message/${editId}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data) + }); + } else { + res = await fetch("/api/bot/add-scheduled-message", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data) + }); + } + + const result = await res.json(); + if (result.success) { + resetForm(); + loadScheduledMessages(); + } else { + alert("Erreur lors de l'enregistrement."); + } + } catch (err) { + console.error("Erreur:", err); + alert("Erreur réseau."); + } + + saveBtn.disabled = false; + saveBtn.textContent = editIdInput.value ? "💾 Mettre à jour" : "💾 Enregistrer"; + }); + + // Cancel button + cancelBtn.addEventListener("click", resetForm); + + // Init + initTimeSelects(); + await loadChannels(); + await loadScheduledMessages(); + renderTimesList(); +})(); diff --git a/app/public/index.html b/app/public/index.html index ac178a8..2e6023e 100644 --- a/app/public/index.html +++ b/app/public/index.html @@ -92,6 +92,11 @@

Stats d'Activité

Suivez l'activité de vos membres avec des graphiques détaillés : messages envoyés, temps en vocal, par semaine, mois ou année.

+
+
+

Messages Programmés

+

Planifiez l'envoi automatique de messages à des jours et heures précis ou à intervalles réguliers. Options avancées incluses.

+
⚙️

Dashboard Intuitif

diff --git a/app/routes/api.js b/app/routes/api.js index 81c6b91..0ac6399 100644 --- a/app/routes/api.js +++ b/app/routes/api.js @@ -965,5 +965,162 @@ module.exports = (app, db, client) => { }); }); + // ===== SCHEDULED MESSAGES ===== + + // Récupérer tous les messages programmés d'un serveur + router.get("/bot/get-scheduled-messages/:guildId", (req, res) => { + const { guildId } = req.params; + + if (!req.session.guilds) { + return res.status(401).json([]); + } + + const isAdmin = req.session.guilds.find( + g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n + ); + + if (!isAdmin) { + return res.status(403).json([]); + } + + db.all( + "SELECT * FROM scheduled_messages WHERE guild_id = ? ORDER BY created_at DESC", + [guildId], + (err, rows) => { + if (err) { + console.error(err); + return res.status(500).json([]); + } + res.json(rows || []); + } + ); + }); + + // Ajouter un message programmé + router.post("/bot/add-scheduled-message", express.json(), (req, res) => { + const { + guildId, channelId, messageContent, + embedEnabled, embedTitle, embedDescription, embedColor, + scheduleType, daysOfWeek, timesOfDay, intervalValue, intervalUnit, + forceSend, deletePrevious, enabled + } = req.body; + + if (!req.session.guilds) { + return res.status(401).json({ success: false }); + } + + const isAdmin = req.session.guilds.find( + g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n + ); + + if (!isAdmin) { + return res.status(403).json({ success: false }); + } + + db.run( + `INSERT INTO scheduled_messages ( + guild_id, channel_id, message_content, + embed_enabled, embed_title, embed_description, embed_color, + schedule_type, days_of_week, times_of_day, interval_value, interval_unit, + force_send, delete_previous, enabled + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [ + guildId, channelId, messageContent || '', + embedEnabled ? 1 : 0, embedTitle || null, embedDescription || null, embedColor || '#5865F2', + scheduleType || 'weekly', + JSON.stringify(daysOfWeek || []), + JSON.stringify(timesOfDay || []), + intervalValue || 60, intervalUnit || 'minutes', + forceSend ? 1 : 0, deletePrevious ? 1 : 0, enabled ? 1 : 0 + ], + function(err) { + if (err) { + console.error(err); + return res.status(500).json({ success: false }); + } + res.json({ success: true, id: this.lastID }); + } + ); + }); + + // Modifier un message programmé + router.put("/bot/update-scheduled-message/:id", express.json(), (req, res) => { + const { id } = req.params; + const { + channelId, messageContent, + embedEnabled, embedTitle, embedDescription, embedColor, + scheduleType, daysOfWeek, timesOfDay, intervalValue, intervalUnit, + forceSend, deletePrevious, enabled + } = req.body; + + if (!req.session.guilds) { + return res.status(401).json({ success: false }); + } + + db.run( + `UPDATE scheduled_messages SET + channel_id = ?, message_content = ?, + embed_enabled = ?, embed_title = ?, embed_description = ?, embed_color = ?, + schedule_type = ?, days_of_week = ?, times_of_day = ?, interval_value = ?, interval_unit = ?, + force_send = ?, delete_previous = ?, enabled = ? + WHERE id = ?`, + [ + channelId, messageContent || '', + embedEnabled ? 1 : 0, embedTitle || null, embedDescription || null, embedColor || '#5865F2', + scheduleType || 'weekly', + JSON.stringify(daysOfWeek || []), + JSON.stringify(timesOfDay || []), + intervalValue || 60, intervalUnit || 'minutes', + forceSend ? 1 : 0, deletePrevious ? 1 : 0, enabled ? 1 : 0, + id + ], + (err) => { + if (err) { + console.error(err); + return res.status(500).json({ success: false }); + } + res.json({ success: true }); + } + ); + }); + + // Supprimer un message programmé + router.delete("/bot/delete-scheduled-message/:id", (req, res) => { + const { id } = req.params; + + if (!req.session.guilds) { + return res.status(401).json({ success: false }); + } + + db.run("DELETE FROM scheduled_messages WHERE id = ?", [id], (err) => { + if (err) { + console.error(err); + return res.status(500).json({ success: false }); + } + res.json({ success: true }); + }); + }); + + // Toggle enabled/disabled + router.patch("/bot/toggle-scheduled-message/:id", (req, res) => { + const { id } = req.params; + + if (!req.session.guilds) { + return res.status(401).json({ success: false }); + } + + db.run( + "UPDATE scheduled_messages SET enabled = NOT enabled WHERE id = ?", + [id], + (err) => { + if (err) { + console.error(err); + return res.status(500).json({ success: false }); + } + res.json({ success: true }); + } + ); + }); + app.use("/api", router); };