🎨 chore(apps/rssPush.js): clean up import statements and improve logging messages for better clarity and consistency

This commit is contained in:
Jerry 2025-11-27 18:30:46 +08:00
parent b86743f885
commit d9c8da47c0

View File

@ -6,7 +6,6 @@ import fs from 'fs';
import rssCache from '../lib/rss/rssCache.js'; import rssCache from '../lib/rss/rssCache.js';
import schedule from 'node-schedule'; import schedule from 'node-schedule';
import tools from '../components/tool.js'; import tools from '../components/tool.js';
// 移除了重复的 import ConfigControl
export default class RssPlugin extends plugin { export default class RssPlugin extends plugin {
constructor() { constructor() {
@ -31,7 +30,6 @@ export default class RssPlugin extends plugin {
permission: 'master', permission: 'master',
priority: 100, priority: 100,
}, },
// 【新增】查看列表功能
{ {
reg: '^#rss列表$', reg: '^#rss列表$',
fnc: 'listFeeds', fnc: 'listFeeds',
@ -45,14 +43,11 @@ export default class RssPlugin extends plugin {
}, },
], ],
}); });
// 【修复】定时任务启动逻辑
// 只要插件加载,且未注册过任务,就启动定时器
if (!global.__rss_job_scheduled) { if (!global.__rss_job_scheduled) {
// 默认每10分钟执行一次 // 默认每10分钟执行一次
schedule.scheduleJob('*/10 * * * *', () => this.pushFeeds()); schedule.scheduleJob('*/10 * * * *', () => this.pushFeeds());
global.__rss_job_scheduled = true; global.__rss_job_scheduled = true;
logger.mark('[RSS] 定时检测任务已启动'); logger.mark('[crystelf-rss] 定时检测任务已启动');
} }
} }
@ -95,23 +90,21 @@ export default class RssPlugin extends plugin {
} }
/** /**
* 新增查看当前群组订阅列表 * 查看当前群组订阅列表
*/ */
async listFeeds(e) { async listFeeds(e) {
const feeds = configControl.get('feeds') || []; const feeds = configControl.get('feeds') || [];
const groupId = e.group_id; const groupId = e.group_id;
// 筛选当前群组的订阅,保留全局索引以便移除
const currentGroupFeeds = feeds const currentGroupFeeds = feeds
.map((feed, index) => ({ index, ...feed })) .map((feed, index) => ({ index, ...feed }))
.filter((feed) => feed.targetGroups.includes(groupId)); .filter((feed) => feed.targetGroups.includes(groupId));
if (currentGroupFeeds.length === 0) { if (currentGroupFeeds.length === 0) {
return e.reply('当前群组暂无任何RSS订阅', true); return e.reply('当前群组暂无任何RSS订阅..', true);
} }
const msg = [ const msg = [
`当前群组订阅列表 (${currentGroupFeeds.length})`, `当前群组订阅列表 (${currentGroupFeeds.length})`,
...currentGroupFeeds.map((f) => `[${f.index}] ${f.url}`), ...currentGroupFeeds.map((f) => `[${f.index}] ${f.url}`),
'----------------', '----------------',
'提示: 使用 #rss移除+索引号 取消订阅' '提示: 使用 #rss移除+索引号 取消订阅'
@ -121,47 +114,35 @@ export default class RssPlugin extends plugin {
} }
/** /**
* 修复移除rss (增强健壮性) * 移除rss
*/ */
async removeFeed(e) { async removeFeed(e) {
// 提取数字
const match = e.msg.match(/#rss移除\s*(\d+)/); const match = e.msg.match(/#rss移除\s*(\d+)/);
if (!match || !match[1]) { if (!match || !match[1]) {
return e.reply('请指定要移除的订阅索引例如:#rss移除0', true); return e.reply('请指定要移除的订阅索引,例如:#rss移除0', true);
} }
const index = parseInt(match[1], 10); const index = parseInt(match[1], 10);
const feeds = configControl.get('feeds') || []; const feeds = configControl.get('feeds') || [];
const groupId = e.group_id; const groupId = e.group_id;
// 越界与有效性检查
if (isNaN(index) || index < 0 || index >= feeds.length) { if (isNaN(index) || index < 0 || index >= feeds.length) {
return e.reply('索引无效,请发送 #rss列表 查看正确索引。', true); return e.reply('索引无效,请发送 #rss列表 查看正确索引..', true);
} }
const targetFeed = feeds[index]; const targetFeed = feeds[index];
if (!targetFeed) return e.reply('未找到该配置。', true); if (!targetFeed) return e.reply('未找到该配置..', true);
// 兼容性处理
if (!Array.isArray(targetFeed.targetGroups)) { if (!Array.isArray(targetFeed.targetGroups)) {
targetFeed.targetGroups = []; targetFeed.targetGroups = [];
} }
// 检查本群是否已订阅
if (!targetFeed.targetGroups.includes(groupId)) { if (!targetFeed.targetGroups.includes(groupId)) {
return e.reply('当前群组未订阅此源,无需移除。', true); return e.reply('当前群组未订阅此源,无需移除..', true);
} }
// 执行移除
targetFeed.targetGroups = targetFeed.targetGroups.filter((id) => id !== groupId); targetFeed.targetGroups = targetFeed.targetGroups.filter((id) => id !== groupId);
await configControl.set('feeds', feeds); await configControl.set('feeds', feeds);
return await e.reply(`已取消订阅:${targetFeed.title || targetFeed.url}`); return await e.reply(`已取消订阅:${targetFeed.title || targetFeed.url}`);
} }
/**
* 修复手动拉取 (增加错误处理)
*/
async pullFeedNow(e) { async pullFeedNow(e) {
const url = e.msg.replace(/^#rss拉取/, '').trim(); const url = e.msg.replace(/^#rss拉取/, '').trim();
if (!url) return e.reply('请提供RSS链接', true); if (!url) return e.reply('请提供RSS链接', true);
@ -170,7 +151,7 @@ export default class RssPlugin extends plugin {
try { try {
latest = await rssTools.fetchFeed(url); latest = await rssTools.fetchFeed(url);
} catch (err) { } catch (err) {
logger.error(`[RSS] 手动拉取失败: ${err.message}`); logger.error(`[crystelf-rss] 手动拉取失败: ${err.message}`);
return await e.reply(`拉取失败: ${err.message}`, true); return await e.reply(`拉取失败: ${err.message}`, true);
} }
@ -186,20 +167,14 @@ export default class RssPlugin extends plugin {
await screenshot.generateScreenshot(post, tempPath); await screenshot.generateScreenshot(post, tempPath);
await e.reply([segment.image(tempPath)]); await e.reply([segment.image(tempPath)]);
} catch (err) { } catch (err) {
logger.error(`[RSS] 截图失败: ${err}`); logger.error(`[crystelf-rss] 截图失败: ${err}`);
await e.reply('生成预览图失败..', true); await e.reply('生成预览图失败..', true);
} finally { } finally {
if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath); if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath);
} }
} }
/**
* 修复核心逻辑检查rss更新
* 修复了时区问题逻辑死循环问题和报错导致插件停止的问题
*/
async pushFeeds() { async pushFeeds() {
const feeds = configControl.get('feeds') || []; const feeds = configControl.get('feeds') || [];
// logger.mark(`正在检查rss流更新..`); // 调试时可解开
for (const feed of feeds) { for (const feed of feeds) {
let latest; let latest;
@ -213,21 +188,15 @@ export default class RssPlugin extends plugin {
if (!latest || !latest.length) continue; if (!latest || !latest.length) continue;
const newItems = []; const newItems = [];
// 限制只检查前3条避免刷屏
const checkLimit = Math.min(latest.length, 3); const checkLimit = Math.min(latest.length, 3);
for (let i = 0; i < checkLimit; i++) { for (let i = 0; i < checkLimit; i++) {
const item = latest[i]; const item = latest[i];
if (!item.link) continue; if (!item.link) continue;
// 【核心修复】不再强制对比 todayStr (解决时区问题)
// 而是依赖 rssCache 判断是否推送过
const isCached = await rssCache.has(feed.url, item.link); const isCached = await rssCache.has(feed.url, item.link);
if (!isCached) { if (!isCached) {
// 增加一个宽松的时间检查 (48小时内),防止第一次添加把几年前的文章全发出来
const pubDate = item.date ? new Date(item.date).getTime() : Date.now(); const pubDate = item.date ? new Date(item.date).getTime() : Date.now();
// 如果文章没有时间默认允许推送如果有时间则必须在48小时内
if (!item.date || (Date.now() - pubDate < 172800000)) { if (!item.date || (Date.now() - pubDate < 172800000)) {
newItems.push(item); newItems.push(item);
} }
@ -235,21 +204,18 @@ export default class RssPlugin extends plugin {
} }
if (newItems.length > 0) { if (newItems.length > 0) {
// 【优化】倒序排列,先发旧的,后发新的
newItems.reverse(); newItems.reverse();
for (const post of newItems) { for (const post of newItems) {
// 写入缓存 // 写入缓存
await rssCache.set(feed.url, post.link); await rssCache.set(feed.url, post.link);
for (const groupId of feed.targetGroups) { for (const groupId of feed.targetGroups) {
// 稍微延迟,防止风控
await tools.sleep(2000); await tools.sleep(2000);
const tempPath = path.join(process.cwd(), 'data', `rss-${Date.now()}.png`); const tempPath = path.join(process.cwd(), 'data', `rss-${Date.now()}.png`);
try { try {
if (feed.screenshot) { if (feed.screenshot) {
logger.info(`[RSS] 推送更新: ${post.title} -> 群 ${groupId}`); logger.info(`[crystelf-rss] 推送更新: ${post.title} -> 群 ${groupId}`);
// 先发个文字提示 // 先发个文字提示
await Bot.pickGroup(groupId)?.sendMsg( await Bot.pickGroup(groupId)?.sendMsg(
`[RSS] ${post.feedTitle || '订阅更新'}\n${post.title}` `[RSS] ${post.feedTitle || '订阅更新'}\n${post.title}`
@ -262,9 +228,8 @@ export default class RssPlugin extends plugin {
await Bot.pickGroup(groupId)?.sendMsg(`[RSS推送]\n${post.title}\n${post.link}`); await Bot.pickGroup(groupId)?.sendMsg(`[RSS推送]\n${post.title}\n${post.link}`);
} }
} catch (err) { } catch (err) {
logger.error(`[RSS] 推送消息异常: ${err.message}`); logger.error(`[crystelf-rss] 推送消息异常: ${err.message}`);
} finally { } finally {
// 确保删除临时文件
if (fs.existsSync(tempPath)) { if (fs.existsSync(tempPath)) {
fs.unlinkSync(tempPath); fs.unlinkSync(tempPath);
} }