diff --git a/api_v1_rss.php b/api_v1_rss.php
index 5871032..497df23 100644
--- a/api_v1_rss.php
+++ b/api_v1_rss.php
@@ -40,11 +40,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
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 < 300) { // 5 minutes
+ 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']);
@@ -66,31 +84,62 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$link = (string)($item->link['href'] ?? $item->link);
$description = strip_tags((string)($item->description ?? $item->summary));
+ // 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
$stmt_check = db()->prepare("SELECT id FROM messages WHERE channel_id = ? AND rss_guid = ?");
$stmt_check->execute([$channel_id, $guid]);
if ($stmt_check->fetch()) continue;
// Insert as message from a special "RSS Bot" user or system
- // Let's find or create an RSS Bot user
$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, is_bot, status) VALUES ('RSS Bot', 1, 'online')");
+ $stmt_create_bot = db()->prepare("INSERT INTO users (username, is_bot, status, avatar_url, email, password_hash) VALUES ('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'];
}
- $content = "**" . $title . "**\n" . $description . "\n" . $link;
+ // 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) . "\n";
+ }
+ $content .= $description;
- // For announcements, we might want to use metadata for a rich embed
$metadata = json_encode([
'title' => $title,
- 'description' => mb_substr($description, 0, 200) . (mb_strlen($description) > 200 ? '...' : ''),
+ '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)
]);
@@ -103,7 +152,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt_update_feed->execute([$feed['id']]);
}
- echo json_encode(['success' => true, 'new_items' => $new_items_count]);
+ echo json_encode(['success' => true, 'new_items' => $new_items_count, 'channel_id' => $channel_id]);
exit;
}
} else {
diff --git a/assets/js/main.js b/assets/js/main.js
index 053a8e4..a531286 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -1408,6 +1408,37 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
+ // Auto-sync for announcement channels
+ if (window.activeChannelId) {
+ const autoSyncRss = async () => {
+ // Check if we are in an announcement channel
+ // We can look for the bullhorn icon in the header
+ const headerIcon = document.querySelector('.main-header i.fa-bullhorn');
+ if (headerIcon) {
+ const formData = new FormData();
+ formData.append('action', 'sync');
+ formData.append('channel_id', window.activeChannelId);
+ formData.append('auto', '1');
+ try {
+ const resp = await fetch('api_v1_rss.php', { method: 'POST', body: formData });
+ const data = await resp.json();
+ if (data.success && data.new_items > 0) {
+ // If new items were found, we might want to refresh the message list
+ // but only if we are still on the same channel
+ if (window.activeChannelId == data.channel_id) {
+ // For now, we don't reload automatically to avoid interrupting the user
+ // The new messages will appear on next reload or polling if implemented
+ }
+ }
+ } catch (e) { }
+ }
+ };
+ // Initial sync
+ setTimeout(autoSyncRss, 2000);
+ // Periodic sync every 2 minutes
+ setInterval(autoSyncRss, 120000);
+ }
+
// Clear Channel History
const clearHistoryBtn = document.getElementById('clear-channel-history-btn');
clearHistoryBtn?.addEventListener('click', async () => {
diff --git a/debug_reorder.log b/debug_reorder.log
index 3ad33af..586fe4a 100644
--- a/debug_reorder.log
+++ b/debug_reorder.log
@@ -13,3 +13,5 @@
2026-02-16 03:08:33 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"17","position":1,"category_id":null},{"id":"12","position":2,"category_id":null},{"id":"10","position":3,"category_id":null},{"id":"1","position":4,"category_id":"10"},{"id":"6","position":5,"category_id":"10"},{"id":"15","position":6,"category_id":"10"},{"id":"2","position":7,"category_id":"10"},{"id":"14","position":8,"category_id":null},{"id":"13","position":9,"category_id":null},{"id":"9","position":10,"category_id":null},{"id":"3","position":11,"category_id":null}]
2026-02-16 03:09:18 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"17","position":1,"category_id":null},{"id":"12","position":2,"category_id":null},{"id":"10","position":3,"category_id":null},{"id":"1","position":4,"category_id":"10"},{"id":"6","position":5,"category_id":"10"},{"id":"15","position":6,"category_id":"10"},{"id":"2","position":7,"category_id":"10"},{"id":"18","position":8,"category_id":"10"},{"id":"14","position":9,"category_id":null},{"id":"13","position":10,"category_id":null},{"id":"9","position":11,"category_id":null},{"id":"3","position":12,"category_id":null}]
2026-02-16 18:43:44 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"17","position":1,"category_id":null},{"id":"12","position":2,"category_id":null},{"id":"19","position":3,"category_id":null},{"id":"10","position":4,"category_id":null},{"id":"1","position":5,"category_id":"10"},{"id":"6","position":6,"category_id":"10"},{"id":"15","position":7,"category_id":"10"},{"id":"2","position":8,"category_id":"10"},{"id":"18","position":9,"category_id":"10"},{"id":"14","position":10,"category_id":null},{"id":"13","position":11,"category_id":null},{"id":"9","position":12,"category_id":null},{"id":"3","position":13,"category_id":null}]
+2026-02-16 23:37:00 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"12","position":1,"category_id":null},{"id":"17","position":2,"category_id":null},{"id":"19","position":3,"category_id":null},{"id":"10","position":4,"category_id":null},{"id":"1","position":5,"category_id":"10"},{"id":"6","position":6,"category_id":"10"},{"id":"15","position":7,"category_id":"10"},{"id":"2","position":8,"category_id":"10"},{"id":"18","position":9,"category_id":"10"},{"id":"14","position":10,"category_id":null},{"id":"13","position":11,"category_id":null},{"id":"9","position":12,"category_id":null},{"id":"3","position":13,"category_id":null}]
+2026-02-16 23:37:02 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"17","position":1,"category_id":null},{"id":"12","position":2,"category_id":null},{"id":"19","position":3,"category_id":null},{"id":"10","position":4,"category_id":null},{"id":"1","position":5,"category_id":"10"},{"id":"6","position":6,"category_id":"10"},{"id":"15","position":7,"category_id":"10"},{"id":"2","position":8,"category_id":"10"},{"id":"18","position":9,"category_id":"10"},{"id":"14","position":10,"category_id":null},{"id":"13","position":11,"category_id":null},{"id":"9","position":12,"category_id":null},{"id":"3","position":13,"category_id":null}]
diff --git a/index.php b/index.php
index d4fcae6..b263d37 100644
--- a/index.php
+++ b/index.php
@@ -867,11 +867,22 @@ $emote_html = '
-
+