prepare("SELECT server_id FROM channels WHERE id = ?"); $stmt->execute([$channel_id]); $chan = $stmt->fetch(); if (!$chan || !Permissions::hasPermission($user['id'], $chan['server_id'], Permissions::MANAGE_CHANNELS)) { echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit; } if ($action === 'add') { $url = $_POST['url'] ?? ''; if (!filter_var($url, FILTER_VALIDATE_URL)) { echo json_encode(['success' => false, 'error' => 'Invalid URL']); exit; } $stmt = db()->prepare("INSERT INTO channel_rss_feeds (channel_id, url) VALUES (?, ?)"); $stmt->execute([$channel_id, $url]); echo json_encode(['success' => true]); exit; } if ($action === 'delete') { $feed_id = $_POST['feed_id'] ?? 0; $stmt = db()->prepare("DELETE FROM channel_rss_feeds WHERE id = ? AND channel_id = ?"); $stmt->execute([$feed_id, $channel_id]); echo json_encode(['success' => true]); exit; } if ($action === 'sync') { // Cooldown check: only sync if last sync was > 5 minutes ago // Or if it's a forced sync from the settings UI $is_auto = isset($_POST['auto']) && $_POST['auto'] == 1; // Fetch feeds for this channel $stmt = db()->prepare("SELECT * FROM channel_rss_feeds WHERE channel_id = ?"); $stmt->execute([$channel_id]); $feeds = $stmt->fetchAll(); if ($is_auto) { $last_fetch = 0; foreach ($feeds as $f) { if ($f['last_fetched_at']) { $ts = strtotime($f['last_fetched_at']); if ($ts > $last_fetch) $last_fetch = $ts; } } if (time() - $last_fetch < 120) { // 2 minutes to match JS echo json_encode(['success' => true, 'new_items' => 0, 'skipped' => true]); exit; } } $new_items_count = 0; foreach ($feeds as $feed) { $rss_content = @file_get_contents($feed['url']); if (!$rss_content) continue; $xml = @simplexml_load_string($rss_content); if (!$xml) continue; $items = []; if (isset($xml->channel->item)) { // RSS 2.0 $items = $xml->channel->item; } elseif (isset($xml->entry)) { // Atom $items = $xml->entry; } $feed_items = []; foreach ($items as $item) { $feed_items[] = $item; } $feed_items = array_reverse($feed_items); foreach ($feed_items as $item) { $guid = (string)($item->guid ?? ($item->id ?? $item->link)); if (empty($guid) && isset($item->link['href'])) { $guid = (string)$item->link['href']; } $title = (string)$item->title; $link = (string)($item->link['href'] ?? $item->link); if (empty($link) && isset($item->link)) { foreach($item->link as $l) { if ($l['rel'] == 'alternate' || !$l['rel']) { $link = (string)$l['href']; break; } } } $description = (string)($item->description ?? ($item->summary ?? $item->content)); $description = strip_tags($description); // Extract additional fields $category = (string)($item->category ?? ($item->category['term'] ?? '')); $pubDate = (string)($item->pubDate ?? ($item->published ?? ($item->updated ?? ''))); // Format date nicely if possible if ($pubDate) { $timestamp = strtotime($pubDate); if ($timestamp) { $pubDate = date('d/m/Y H:i', $timestamp); } } $author = (string)($item->author->name ?? ($item->author ?? '')); if (!$author) { $dc = $item->children('http://purl.org/dc/elements/1.1/'); if (isset($dc->creator)) { $author = (string)$dc->creator; } } // Check if already exists in processed items (prevents re-posting if deleted by retention policy) $stmt_check = db()->prepare("SELECT id FROM rss_processed_items WHERE channel_id = ? AND rss_guid = ?"); $stmt_check->execute([$channel_id, $guid]); if ($stmt_check->fetch()) continue; // Insert into processed items first to be safe $stmt_processed = db()->prepare("INSERT IGNORE INTO rss_processed_items (channel_id, rss_guid) VALUES (?, ?)"); $stmt_processed->execute([$channel_id, $guid]); // Insert as message from a special "RSS Bot" user or system $stmt_bot = db()->prepare("SELECT id FROM users WHERE username = 'RSS Bot' AND is_bot = 1"); $stmt_bot->execute(); $bot = $stmt_bot->fetch(); if (!$bot) { $stmt_create_bot = db()->prepare("INSERT INTO users (username, display_name, is_bot, status, avatar_url, email, password_hash) VALUES ('RSS Bot', 'RSS Bot', 1, 'online', 'https://cdn-icons-png.flaticon.com/512/3607/3607436.png', 'rss-bot@system.internal', 'bot-no-password')"); $stmt_create_bot->execute(); $bot_id = db()->lastInsertId(); } else { $bot_id = $bot['id']; } // Format content for traditional view $content = "[" . $title . "](" . $link . ")\n"; if ($category || $pubDate || $author) { $parts = []; if ($category) $parts[] = $category; if ($pubDate) $parts[] = $pubDate; if ($author) $parts[] = $author; $content .= implode(" ยท ", $parts); } $metadata = json_encode([ 'title' => $title, 'description' => mb_substr($description, 0, 500) . (mb_strlen($description) > 500 ? '...' : ''), 'url' => $link, 'category' => $category, 'date' => $pubDate, 'author' => $author, 'is_rss' => true, 'site_name' => parse_url($feed['url'], PHP_URL_HOST) ]); $stmt_msg = db()->prepare("INSERT INTO messages (channel_id, user_id, content, metadata, rss_guid) VALUES (?, ?, ?, ?, ?)"); $stmt_msg->execute([$channel_id, $bot_id, $content, $metadata, $guid]); enforceChannelLimit($channel_id); $new_items_count++; } $stmt_update_feed = db()->prepare("UPDATE channel_rss_feeds SET last_fetched_at = CURRENT_TIMESTAMP WHERE id = ?"); $stmt_update_feed->execute([$feed['id']]); } echo json_encode(['success' => true, 'new_items' => $new_items_count, 'channel_id' => $channel_id]); exit; } } else { // GET: List feeds $channel_id = $_GET['channel_id'] ?? 0; $stmt = db()->prepare("SELECT * FROM channel_rss_feeds WHERE channel_id = ? ORDER BY created_at DESC"); $stmt->execute([$channel_id]); echo json_encode(['success' => true, 'feeds' => $stmt->fetchAll()]); exit; }