diff --git a/api_v1_channels.php b/api_v1_channels.php index 133af40..4850062 100644 --- a/api_v1_channels.php +++ b/api_v1_channels.php @@ -1,24 +1,75 @@ prepare("SELECT * FROM channels WHERE server_id = ?"); + $stmt->execute([$server_id]); + echo json_encode($stmt->fetchAll()); + exit; +} + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $action = $_POST['action'] ?? 'create'; $server_id = $_POST['server_id'] ?? 0; + $user_id = $_SESSION['user_id']; + + if ($action === 'update') { + $channel_id = $_POST['channel_id'] ?? 0; + $name = $_POST['name'] ?? ''; + $allow_file_sharing = isset($_POST['allow_file_sharing']) ? 1 : 0; + + // Check if user is owner of the server + $stmt = db()->prepare("SELECT s.owner_id FROM servers s JOIN channels c ON s.id = c.server_id WHERE c.id = ?"); + $stmt->execute([$channel_id]); + $server = $stmt->fetch(); + + if ($server && $server['owner_id'] == $user_id) { + $name = strtolower(preg_replace('/[^a-zA-Z0-9\-]/', '-', $name)); + $stmt = db()->prepare("UPDATE channels SET name = ?, allow_file_sharing = ? WHERE id = ?"); + $stmt->execute([$name, $allow_file_sharing, $channel_id]); + } + header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id); + exit; + } + + if ($action === 'delete') { + $channel_id = $_POST['channel_id'] ?? 0; + // Check if user is owner + $stmt = db()->prepare("SELECT s.owner_id, s.id as server_id FROM servers s JOIN channels c ON s.id = c.server_id WHERE c.id = ?"); + $stmt->execute([$channel_id]); + $server = $stmt->fetch(); + + if ($server && $server['owner_id'] == $user_id) { + $stmt = db()->prepare("DELETE FROM channels WHERE id = ?"); + $stmt->execute([$channel_id]); + } + header('Location: index.php?server_id=' . ($server['server_id'] ?? '')); + exit; + } + $name = $_POST['name'] ?? ''; $type = $_POST['type'] ?? 'text'; $user_id = $_SESSION['user_id']; - // Check if user is owner of the server or has permissions (simplified check for now: user must be a member) + // Check if user is member of the server $stmt = db()->prepare("SELECT 1 FROM server_members WHERE server_id = ? AND user_id = ?"); $stmt->execute([$server_id, $user_id]); if ($stmt->fetch() && $name) { try { // Basic sanitization for channel name - $name = strtolower(preg_replace('/[^a-zA-Z0-3\-]/', '-', $name)); + $name = strtolower(preg_replace('/[^a-zA-Z0-9\-]/', '-', $name)); + $allow_file_sharing = isset($_POST['allow_file_sharing']) ? 1 : 0; - $stmt = db()->prepare("INSERT INTO channels (server_id, name, type) VALUES (?, ?, ?)"); - $stmt->execute([$server_id, $name, $type]); + $stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing) VALUES (?, ?, ?, ?)"); + $stmt->execute([$server_id, $name, $type, $allow_file_sharing]); $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 ba12ee9..07b3b7c 100644 --- a/api_v1_messages.php +++ b/api_v1_messages.php @@ -2,6 +2,7 @@ header('Content-Type: application/json'); require_once 'auth/session.php'; require_once 'includes/opengraph.php'; +require_once 'includes/ai_filtering.php'; // Check for Bot token in headers $headers = getallheaders(); @@ -86,7 +87,17 @@ if (strpos($_SERVER['CONTENT_TYPE'] ?? '', 'application/json') !== false) { $content = $_POST['content'] ?? ''; $channel_id = $_POST['channel_id'] ?? 0; + // Check if file sharing is allowed in this channel + $stmt = db()->prepare("SELECT allow_file_sharing FROM channels WHERE id = ?"); + $stmt->execute([$channel_id]); + $channel = $stmt->fetch(); + $can_share_files = $channel ? (bool)$channel['allow_file_sharing'] : true; + if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) { + if (!$can_share_files) { + echo json_encode(['success' => false, 'error' => 'File sharing is disabled in this channel.']); + exit; + } $upload_dir = 'assets/uploads/'; if (!is_dir($upload_dir)) mkdir($upload_dir, 0775, true); @@ -104,6 +115,14 @@ if (empty($content) && empty($attachment_url)) { exit; } +if (!empty($content)) { + $moderation = moderateContent($content); + if (!$moderation['is_safe']) { + echo json_encode(['success' => false, 'error' => 'Message flagged as inappropriate: ' . ($moderation['reason'] ?? 'Violation of community standards')]); + exit; + } +} + $metadata = null; if (!empty($content)) { $urls = extractUrls($content); diff --git a/api_v1_webhook.php b/api_v1_webhook.php index fc87466..c436214 100644 --- a/api_v1_webhook.php +++ b/api_v1_webhook.php @@ -1,44 +1,84 @@ false, 'error' => 'Missing token']); - exit; -} - -$stmt = db()->prepare("SELECT * FROM webhooks WHERE token = ?"); -$stmt->execute([$token]); -$webhook = $stmt->fetch(); - -if (!$webhook) { - http_response_code(401); - echo json_encode(['success' => false, 'error' => 'Invalid token']); - exit; -} - -if (empty($content)) { - http_response_code(400); - echo json_encode(['success' => false, 'error' => 'Empty content']); - exit; -} - -try { - // We'll use a special System user or a placeholder user_id for webhooks - // Or we could create a bot user for each webhook. - // For now, let's assume we use user_id 1 (System) but override the name if provided. +// Check for execution (no session needed, just token) +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['token'])) { + require_once 'db/config.php'; + $token = $_GET['token'] ?? ''; + $data = json_decode(file_get_contents('php://input'), true); + $content = $data['content'] ?? ''; - $stmt = db()->prepare("INSERT INTO messages (channel_id, user_id, content) VALUES (?, ?, ?)"); - $stmt->execute([$webhook['channel_id'], 1, $content]); + $stmt = db()->prepare("SELECT * FROM webhooks WHERE token = ?"); + $stmt->execute([$token]); + $webhook = $stmt->fetch(); - echo json_encode(['success' => true]); -} catch (Exception $e) { - http_response_code(500); - echo json_encode(['success' => false, 'error' => $e->getMessage()]); + if (!$webhook) { + http_response_code(401); + echo json_encode(['success' => false, 'error' => 'Invalid token']); + exit; + } + + if (empty($content)) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'Empty content']); + exit; + } + + try { + $stmt = db()->prepare("INSERT INTO messages (channel_id, user_id, content) VALUES (?, ?, ?)"); + $stmt->execute([$webhook['channel_id'], 1, $content]); // 1 is system/bot user + echo json_encode(['success' => true]); + } catch (Exception $e) { + http_response_code(500); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); + } + exit; +} + +// Manage webhooks (session needed) +requireLogin(); +$user_id = $_SESSION['user_id']; + +if ($_SERVER['REQUEST_METHOD'] === 'GET') { + $server_id = $_GET['server_id'] ?? 0; + $stmt = db()->prepare(" + SELECT w.*, c.name as channel_name + FROM webhooks w + JOIN channels c ON w.channel_id = c.id + WHERE c.server_id = ? + "); + $stmt->execute([$server_id]); + $webhooks = $stmt->fetchAll(); + echo json_encode(['success' => true, 'webhooks' => $webhooks]); + exit; +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $data = json_decode(file_get_contents('php://input'), true); + $channel_id = $data['channel_id'] ?? 0; + $name = $data['name'] ?? 'New Webhook'; + $token = bin2hex(random_bytes(16)); + + try { + $stmt = db()->prepare("INSERT INTO webhooks (channel_id, name, token) VALUES (?, ?, ?)"); + $stmt->execute([$channel_id, $name, $token]); + echo json_encode(['success' => true, 'webhook' => ['id' => db()->lastInsertId(), 'token' => $token]]); + } catch (Exception $e) { + echo json_encode(['success' => false, 'error' => $e->getMessage()]); + } + exit; +} + +if ($_SERVER['REQUEST_METHOD'] === 'DELETE') { + $data = json_decode(file_get_contents('php://input'), true); + $id = $data['id'] ?? 0; + try { + $stmt = db()->prepare("DELETE FROM webhooks WHERE id = ?"); + $stmt->execute([$id]); + echo json_encode(['success' => true]); + } catch (Exception $e) { + echo json_encode(['success' => false, 'error' => $e->getMessage()]); + } + exit; } diff --git a/assets/css/discord.css b/assets/css/discord.css index 1b3c936..b4cc879 100644 --- a/assets/css/discord.css +++ b/assets/css/discord.css @@ -585,6 +585,28 @@ body { border: 1px solid rgba(255,255,255,0.1); } +.video-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 10px; + padding: 10px; + background: #000; + width: 100%; +} + +.video-grid video { + width: 100%; + aspect-ratio: 16/9; + background: #2b2d31; + border-radius: 8px; + object-fit: cover; +} + +.voice-controls { + border-top: 1px solid rgba(255,255,255,0.05); + background-color: #232428 !important; +} + /* Roles Management */ #roles-list .list-group-item:hover { background-color: rgba(255, 255, 255, 0.05) !important; @@ -627,3 +649,17 @@ body { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } } + +.channel-item-container:hover .channel-settings-btn { + opacity: 1 !important; +} + +.channel-settings-btn { + opacity: 0; + transition: opacity 0.2s; + padding: 2px; +} + +.channel-settings-btn:hover { + color: var(--text-normal) !important; +} diff --git a/assets/js/main.js b/assets/js/main.js index 651f0cc..473a319 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -362,6 +362,18 @@ document.addEventListener('DOMContentLoaded', () => { } }); + // Roles Management + const channelSettingsBtns = document.querySelectorAll('.channel-settings-btn'); + channelSettingsBtns.forEach(btn => { + btn.addEventListener('click', () => { + const modal = document.getElementById('editChannelModal'); + modal.querySelector('#edit-channel-id').value = btn.dataset.id; + modal.querySelector('#edit-channel-name').value = btn.dataset.name; + modal.querySelector('#edit-channel-files').checked = btn.dataset.files == '1'; + modal.querySelector('#delete-channel-id').value = btn.dataset.id; + }); + }); + // Roles Management const rolesTabBtn = document.getElementById('roles-tab-btn'); const rolesList = document.getElementById('roles-list'); @@ -442,6 +454,84 @@ document.addEventListener('DOMContentLoaded', () => { } }); + // Webhooks Management + const webhooksTabBtn = document.getElementById('webhooks-tab-btn'); + const webhooksList = document.getElementById('webhooks-list'); + const addWebhookBtn = document.getElementById('add-webhook-btn'); + + webhooksTabBtn?.addEventListener('click', loadWebhooks); + + async function loadWebhooks() { + webhooksList.innerHTML = '