190 lines
7.9 KiB
PHP
190 lines
7.9 KiB
PHP
<?php
|
|
require_once 'auth/session.php';
|
|
require_once 'includes/permissions.php';
|
|
requireLogin();
|
|
|
|
$user = getCurrentUser();
|
|
$action = $_REQUEST['action'] ?? '';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$channel_id = $_POST['channel_id'] ?? 0;
|
|
|
|
// Permission check: must have manage channels permission
|
|
$stmt = db()->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;
|
|
}
|