diff --git a/api_v1_channels.php b/api_v1_channels.php index b521ca6..2eca980 100644 --- a/api_v1_channels.php +++ b/api_v1_channels.php @@ -62,6 +62,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($icon === '') $icon = null; $category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null; $rules_role_id = !empty($_POST['rules_role_id']) ? (int)$_POST['rules_role_id'] : null; + $ai_moderation_enabled = isset($_POST['ai_moderation_enabled']) ? 1 : 0; // Check if user has permission to manage channels $stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?"); @@ -73,8 +74,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Allow spaces, accents and mixed case $name = trim($name); // Explicitly exclude position from update to prevent jumping to bottom - $stmt = db()->prepare("UPDATE channels SET name = ?, type = ?, status = ?, allow_file_sharing = ?, message_limit = ?, icon = ?, category_id = ?, rules_role_id = ? WHERE id = ?"); - $stmt->execute([$name, $type, $status, $allow_file_sharing, $message_limit, $icon, $category_id, $rules_role_id, $channel_id]); + $stmt = db()->prepare("UPDATE channels SET name = ?, type = ?, status = ?, allow_file_sharing = ?, ai_moderation_enabled = ?, message_limit = ?, icon = ?, category_id = ?, rules_role_id = ? WHERE id = ?"); + $stmt->execute([$name, $type, $status, $allow_file_sharing, $ai_moderation_enabled, $message_limit, $icon, $category_id, $rules_role_id, $channel_id]); if ($message_limit !== null) { require_once 'db/config.php'; @@ -115,6 +116,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($icon === '') $icon = null; $category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null; $rules_role_id = !empty($_POST['rules_role_id']) ? (int)$_POST['rules_role_id'] : null; + $ai_moderation_enabled = isset($_POST['ai_moderation_enabled']) ? 1 : 0; // Get next position $stmtPos = db()->prepare("SELECT MAX(position) as max_pos FROM channels WHERE server_id = ?"); @@ -122,8 +124,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $maxPos = $stmtPos->fetch(); $nextPos = ($maxPos['max_pos'] ?? -1) + 1; - $stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing, message_limit, icon, category_id, position, rules_role_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); - $stmt->execute([$server_id, $name, $type, $allow_file_sharing, $message_limit, $icon, $category_id, $nextPos, $rules_role_id]); + $stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing, ai_moderation_enabled, message_limit, icon, category_id, position, rules_role_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + $stmt->execute([$server_id, $name, $type, $allow_file_sharing, $ai_moderation_enabled, $message_limit, $icon, $category_id, $nextPos, $rules_role_id]); $channel_id = db()->lastInsertId(); header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id); diff --git a/api_v1_messages.php b/api_v1_messages.php index 5c1891a..d102102 100644 --- a/api_v1_messages.php +++ b/api_v1_messages.php @@ -270,6 +270,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'DELETE') { } $content = ''; + $channel_id = 0; $thread_id = null; $attachment_url = null; @@ -313,6 +314,11 @@ if (empty($content) && empty($attachment_url)) { } // Check granular permissions +// Check if AI moderation is enabled for this channel +$stmtMod = db()->prepare("SELECT ai_moderation_enabled FROM channels WHERE id = ?"); +$stmtMod->execute([$channel_id]); +$channelMod = $stmtMod->fetch(); +$ai_moderation_enabled = $channelMod ? (bool)$channelMod["ai_moderation_enabled"] : true; $can_send = Permissions::canSendInChannel($user_id, $channel_id); if ($thread_id) { // For threads, we check the specific thread permission instead of the general channel permission @@ -336,7 +342,7 @@ if ($thread_id) { } } -if (!empty($content)) { +if (!empty($content) && $ai_moderation_enabled) { $moderation = moderateContent($content); if (!$moderation['is_safe']) { echo json_encode(['success' => false, 'error' => 'Message flagged as inappropriate: ' . ($moderation['reason'] ?? 'Violation of community standards')]); @@ -380,7 +386,7 @@ if (isset($_POST['is_announcement']) && $_POST['is_announcement'] == '1') { 'poll_end_date' => $end_date, 'poll_color' => $_POST['poll_color'] ?? '#3c6f7c' ]); -} elseif (!empty($content)) { +} elseif (!empty($content) && $ai_moderation_enabled) { $urls = extractUrls($content); if (!empty($urls)) { // Fetch OG data for the first URL diff --git a/assets/js/main.js b/assets/js/main.js index ae8d2f1..3f22b38 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1563,6 +1563,7 @@ document.addEventListener('DOMContentLoaded', () => { } modal.querySelector('#edit-channel-files').checked = btn.dataset.files == '1'; + modal.querySelector('#edit-channel-ai-moderation').checked = btn.dataset.aiModeration == '1'; modal.querySelector('#edit-channel-limit').value = btn.dataset.limit || ''; modal.querySelector('#edit-channel-status').value = btn.dataset.status || ''; modal.querySelector('#edit-channel-icon').value = btn.dataset.icon || ''; @@ -1595,6 +1596,8 @@ document.addEventListener('DOMContentLoaded', () => { } if (editFilesContainer) { editFilesContainer.style.display = (channelType === 'rules' || channelType === 'autorole' || channelType === 'poll' || isRoleChannel) ? 'none' : 'block'; + const aiModContainer = document.getElementById('edit-channel-ai-moderation-container'); + if (aiModContainer) aiModContainer.style.display = (channelType === 'chat' || channelType === 'announcement' || channelType === 'forum') ? 'block' : 'none'; } if (clearChatBtn) clearChatBtn.style.display = (channelType === 'rules' || channelType === 'poll') ? 'none' : 'inline-block'; @@ -3005,6 +3008,8 @@ document.addEventListener('DOMContentLoaded', () => { } } if (filesContainer) filesContainer.style.display = (type === 'rules' || type === 'autorole' || type === 'poll') ? 'none' : 'block'; + const aiModContainer = document.getElementById('add-channel-ai-moderation-container'); + if (aiModContainer) aiModContainer.style.display = (type === 'chat' || type === 'announcement' || type === 'forum') ? 'block' : 'none'; }); // User Settings - Avatar Search diff --git a/assets/pasted-20260313-233133-648dd6ef.png b/assets/pasted-20260313-233133-648dd6ef.png new file mode 100644 index 0000000..83e1c65 Binary files /dev/null and b/assets/pasted-20260313-233133-648dd6ef.png differ diff --git a/database/schema.sql b/database/schema.sql index ee3f0d3..e573414 100644 --- a/database/schema.sql +++ b/database/schema.sql @@ -186,6 +186,7 @@ CREATE TABLE `channels` ( `position` int(11) DEFAULT 0, `category_id` int(11) DEFAULT NULL, `rules_role_id` int(11) DEFAULT NULL, + `ai_moderation_enabled` tinyint(1) DEFAULT 1, PRIMARY KEY (`id`), KEY `server_id` (`server_id`), CONSTRAINT `channels_ibfk_1` FOREIGN KEY (`server_id`) REFERENCES `servers` (`id`) ON DELETE CASCADE diff --git a/db/migrations/20260313_add_ai_moderation_to_channels.sql b/db/migrations/20260313_add_ai_moderation_to_channels.sql new file mode 100644 index 0000000..9b29039 --- /dev/null +++ b/db/migrations/20260313_add_ai_moderation_to_channels.sql @@ -0,0 +1,2 @@ +-- Add ai_moderation_enabled column to channels table +ALTER TABLE `channels` ADD COLUMN `ai_moderation_enabled` TINYINT(1) DEFAULT 1; diff --git a/index.php b/index.php index cf71b37..4eb1ba3 100644 --- a/index.php +++ b/index.php @@ -668,7 +668,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; data-status="" data-icon="" data-rules-role="" - data-category=""> + data-ai-moderation="" data-category=""> @@ -719,7 +719,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; data-status="" data-icon="" data-rules-role="" - data-category=""> + data-ai-moderation="" data-category=""> @@ -776,7 +776,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; data-status="" data-icon="" data-rules-role="" - data-category="" + data-category="" data-ai-moderation="" data-theme=""> @@ -2781,6 +2781,13 @@ document.addEventListener('DOMContentLoaded', () => {
Conserve automatiquement seulement les X derniers messages de ce salon.
+
+
+ + +
Vérifie automatiquement les messages inappropriés via l'IA. Peut ralentir l'envoi.
+
+
@@ -3101,6 +3108,13 @@ document.addEventListener('DOMContentLoaded', () => { +
+
+ + +
Vérifie automatiquement les messages inappropriés via l'IA. Peut ralentir l'envoi.
+
+