@zhin.js/plugin-rss 1.1.14 → 1.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/lib/console.d.ts +4 -0
- package/lib/console.d.ts.map +1 -0
- package/lib/console.js +75 -0
- package/lib/console.js.map +1 -0
- package/lib/db-store.d.ts +12 -0
- package/lib/db-store.d.ts.map +1 -1
- package/lib/db-store.js +17 -0
- package/lib/db-store.js.map +1 -1
- package/lib/feed.d.ts +2 -0
- package/lib/feed.d.ts.map +1 -1
- package/lib/feed.js +6 -1
- package/lib/feed.js.map +1 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -2
- package/lib/index.js.map +1 -1
- package/package.json +18 -7
- package/pages/index/index.js +70 -0
- package/pages/index/index.ts +92 -0
- package/plugin.js +3 -1
- package/src/console.ts +90 -0
- package/src/db-store.ts +29 -0
- package/src/feed.ts +8 -1
- package/src/index.ts +3 -2
package/README.md
CHANGED
|
@@ -37,7 +37,15 @@ rss:
|
|
|
37
37
|
|
|
38
38
|
如果旧订阅记录保存了错误的端点或用户身份,系统无法可靠反推出原始会话。`rss-remove` 只删除当前 Bot、当前会话的订阅,不能清理归属不明的旧记录;需由管理员在数据库中核对插件实例和完整订阅字段,备份后按记录精确删除,再在真实会话重新订阅。不要仅凭 URL 或会话 ID 批量删除,以免影响其他 Bot。缺失或非法地址不会通过猜测进行投递;字段齐全但值错误的旧记录不会被自动识别或迁移。
|
|
39
39
|
|
|
40
|
-
## Console
|
|
40
|
+
## Console 监控页与管理员订阅
|
|
41
|
+
|
|
42
|
+
### Console 订阅监控页 (`pages/index`)
|
|
43
|
+
|
|
44
|
+
插件提供声明式 Console 页面(路由 `/rss`,权限 `rss:read`),展示当前聊天订阅数量、轮询 Cron、单次最大推送条数与订阅列表,并提供两个运维动作(需 `rss:write` 权限):
|
|
45
|
+
- `poll-now`:立即触发一次全量订阅源轮询。
|
|
46
|
+
- `clean-seen`:清理 7 天前的已读缓存记录。
|
|
47
|
+
|
|
48
|
+
### Console 管理员订阅
|
|
41
49
|
|
|
42
50
|
在 Console 的任务中心选择对应 RSS 实例的“管理员 RSS 订阅”,填写 RSS / Atom 的 HTTP(S) 地址,并选择精确的账号、会话、父容器和线程后创建。先确认页面所列的抓取与发送影响;地址不接受 URL 用户名或密码。任务保存时不会抓取源站或发送测试消息。
|
|
43
51
|
|
package/lib/console.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gBAAgB,EAItB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AA4BhE,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,gBAAgB,CAkDjF"}
|
package/lib/console.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { definePluginConsoleRpc, PluginConsoleRpcError, } from 'zhin.js';
|
|
2
|
+
import { listAllSubscriptions } from './db-store.js';
|
|
3
|
+
import { cleanOldSeen, pollAllFeeds } from './poll.js';
|
|
4
|
+
import { rssRuntimeToken } from './runtime.js';
|
|
5
|
+
function sanitizeFeedUrl(rawUrl) {
|
|
6
|
+
try {
|
|
7
|
+
const parsed = new URL(rawUrl);
|
|
8
|
+
if (parsed.username || parsed.password) {
|
|
9
|
+
parsed.username = '';
|
|
10
|
+
parsed.password = '';
|
|
11
|
+
return parsed.toString();
|
|
12
|
+
}
|
|
13
|
+
return rawUrl;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return rawUrl;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function resolveRssRuntime(call, fallback) {
|
|
20
|
+
const runtime = call.runtime?.use?.(rssRuntimeToken) ?? fallback;
|
|
21
|
+
if (!runtime) {
|
|
22
|
+
throw new PluginConsoleRpcError('unavailable', 'RSS runtime is not initialized');
|
|
23
|
+
}
|
|
24
|
+
return runtime;
|
|
25
|
+
}
|
|
26
|
+
export function createRssConsoleRpc(defaultRuntime) {
|
|
27
|
+
const rpc = definePluginConsoleRpc({
|
|
28
|
+
status: {
|
|
29
|
+
page: 'index',
|
|
30
|
+
requiredPermissions: ['rss:read'],
|
|
31
|
+
async handler(_input, call) {
|
|
32
|
+
const runtime = resolveRssRuntime(call, defaultRuntime);
|
|
33
|
+
const allSubscriptions = await listAllSubscriptions(runtime.db);
|
|
34
|
+
return {
|
|
35
|
+
subscriptionCount: allSubscriptions.length,
|
|
36
|
+
pollCron: runtime.config.pollCron,
|
|
37
|
+
maxItemsPerPoll: runtime.config.maxItemsPerPoll ?? runtime.config.maxItems,
|
|
38
|
+
fetchTimeoutMs: runtime.config.fetchTimeoutMs ?? runtime.config.timeout,
|
|
39
|
+
subscriptions: allSubscriptions.slice(0, 100).map((sub) => ({
|
|
40
|
+
...sub,
|
|
41
|
+
url: sanitizeFeedUrl(sub.url),
|
|
42
|
+
})),
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
actions: {
|
|
47
|
+
'poll-now': {
|
|
48
|
+
page: 'index',
|
|
49
|
+
requiredPermissions: ['rss:write'],
|
|
50
|
+
async handler(_input, call) {
|
|
51
|
+
const runtime = resolveRssRuntime(call, defaultRuntime);
|
|
52
|
+
await pollAllFeeds(runtime);
|
|
53
|
+
return { ok: true, polledAt: Date.now() };
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
'clean-seen': {
|
|
57
|
+
page: 'index',
|
|
58
|
+
requiredPermissions: ['rss:write'],
|
|
59
|
+
async handler(_input, call) {
|
|
60
|
+
const runtime = resolveRssRuntime(call, defaultRuntime);
|
|
61
|
+
await cleanOldSeen(runtime);
|
|
62
|
+
return { ok: true, cleanedAt: Date.now() };
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
const methods = rpc.methods;
|
|
68
|
+
const origGet = methods.get.bind(methods);
|
|
69
|
+
const origHas = methods.has.bind(methods);
|
|
70
|
+
const normalizeKey = (key) => key === 'pollNow' ? 'poll-now' : key === 'cleanSeen' ? 'clean-seen' : key;
|
|
71
|
+
methods.get = (key) => origGet(normalizeKey(key));
|
|
72
|
+
methods.has = (key) => origHas(normalizeKey(key));
|
|
73
|
+
return rpc;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=console.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.js","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"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"}
|
package/lib/db-store.d.ts
CHANGED
|
@@ -2,4 +2,16 @@ export { createInMemoryRssDb, RSS_SEEN_TABLE, RSS_SUBS_TABLE, type RssMemoryDb,
|
|
|
2
2
|
import { type RssMemoryDb, type RssModel } from './memory-store.js';
|
|
3
3
|
export declare function getRssSubs(db: RssMemoryDb): RssModel | null;
|
|
4
4
|
export declare function getRssSeen(db: RssMemoryDb): RssModel | null;
|
|
5
|
+
export interface RssSubscriptionSummary {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly feedTitle: string;
|
|
8
|
+
readonly url: string;
|
|
9
|
+
readonly adapterName: string;
|
|
10
|
+
readonly endpointId: string;
|
|
11
|
+
readonly channelType: string;
|
|
12
|
+
readonly channelId: string;
|
|
13
|
+
readonly creatorName: string;
|
|
14
|
+
readonly createdAt: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function listAllSubscriptions(db: RssMemoryDb): Promise<RssSubscriptionSummary[]>;
|
|
5
17
|
//# sourceMappingURL=db-store.d.ts.map
|
package/lib/db-store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-store.d.ts","sourceRoot":"","sources":["../src/db-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,QAAQ,GACd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAE3D;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAE3D"}
|
|
1
|
+
{"version":3,"file":"db-store.d.ts","sourceRoot":"","sources":["../src/db-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,QAAQ,GACd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAE3D;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAE3D;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAe7F"}
|
package/lib/db-store.js
CHANGED
|
@@ -6,4 +6,21 @@ export function getRssSubs(db) {
|
|
|
6
6
|
export function getRssSeen(db) {
|
|
7
7
|
return db.models.get(RSS_SEEN_TABLE) ?? null;
|
|
8
8
|
}
|
|
9
|
+
export async function listAllSubscriptions(db) {
|
|
10
|
+
const Subs = getRssSubs(db);
|
|
11
|
+
if (!Subs)
|
|
12
|
+
return [];
|
|
13
|
+
const rows = await Subs.select();
|
|
14
|
+
return rows.map((row, index) => ({
|
|
15
|
+
id: String(row.id ?? index + 1),
|
|
16
|
+
feedTitle: String(row.feed_title ?? row.feedTitle ?? ''),
|
|
17
|
+
url: String(row.url ?? ''),
|
|
18
|
+
adapterName: String(row.adapter_name ?? row.adapterName ?? ''),
|
|
19
|
+
endpointId: String(row.endpoint_id ?? row.endpointId ?? ''),
|
|
20
|
+
channelType: String(row.channel_type ?? row.channelType ?? 'private'),
|
|
21
|
+
channelId: String(row.channel_id ?? row.channelId ?? ''),
|
|
22
|
+
creatorName: String(row.creator_name ?? row.creatorName ?? ''),
|
|
23
|
+
createdAt: String(row.created_at ?? row.createdAt ?? ''),
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
9
26
|
//# sourceMappingURL=db-store.js.map
|
package/lib/db-store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-store.js","sourceRoot":"","sources":["../src/db-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,cAAc,GAGf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,cAAc,GAGf,MAAM,mBAAmB,CAAC;AAE3B,MAAM,UAAU,UAAU,CAAC,EAAe;IACxC,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAe;IACxC,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;AAC/C,CAAC"}
|
|
1
|
+
{"version":3,"file":"db-store.js","sourceRoot":"","sources":["../src/db-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,cAAc,GAGf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,cAAc,GAGf,MAAM,mBAAmB,CAAC;AAE3B,MAAM,UAAU,UAAU,CAAC,EAAe;IACxC,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAe;IACxC,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;AAC/C,CAAC;AAcD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,EAAe;IACxD,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;IAC5B,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;IACjC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/B,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,KAAK,GAAG,CAAC,CAAC;QAC/B,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;QACxD,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;QAC9D,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QAC3D,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,WAAW,IAAI,SAAS,CAAC;QACrE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;QACxD,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;QAC9D,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;KACzD,CAAC,CAAC,CAAC;AACN,CAAC"}
|
package/lib/feed.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface RssConfig {
|
|
|
11
11
|
readonly maxPerGroup: number;
|
|
12
12
|
readonly maxItems: number;
|
|
13
13
|
readonly timeout: number;
|
|
14
|
+
readonly maxItemsPerPoll?: number;
|
|
15
|
+
readonly fetchTimeoutMs?: number;
|
|
14
16
|
}
|
|
15
17
|
export declare const DEFAULT_RSS_CONFIG: RssConfig;
|
|
16
18
|
export declare function resolveRssConfig(raw: Partial<RssConfig> | undefined): RssConfig;
|
package/lib/feed.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAO3C,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAO3C,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,eAAO,MAAM,kBAAkB,EAAE,SAKhC,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,GAAG,SAAS,CAO/E;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgC9C;AAED,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,OAAO,SAA6B,GACnC,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC,CAiB/C;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,MAAM,GAAE,MAAgB,GAAG,MAAM,CAWpG"}
|
package/lib/feed.js
CHANGED
|
@@ -10,7 +10,12 @@ export const DEFAULT_RSS_CONFIG = {
|
|
|
10
10
|
timeout: 15_000,
|
|
11
11
|
};
|
|
12
12
|
export function resolveRssConfig(raw) {
|
|
13
|
-
|
|
13
|
+
const merged = { ...DEFAULT_RSS_CONFIG, ...raw };
|
|
14
|
+
return {
|
|
15
|
+
...merged,
|
|
16
|
+
maxItemsPerPoll: raw?.maxItemsPerPoll ?? merged.maxItems,
|
|
17
|
+
fetchTimeoutMs: raw?.fetchTimeoutMs ?? merged.timeout,
|
|
18
|
+
};
|
|
14
19
|
}
|
|
15
20
|
/**
|
|
16
21
|
* 剔除 HTML 标签与 ` ` 实体并折叠空白。
|
package/lib/feed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,IAAI,CAAC,EAAE,MAAM,WAAW,CAAC;AAC7C;;GAEG;AACH,OAAO,MAAM,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { DEFAULT_RSS_CONFIG, fetchFeed, formatFeedPreview, resolveRssConfig, stripHtml, } from './feed.js';
|
|
2
2
|
export type { FeedItem, RssConfig } from './feed.js';
|
|
3
|
-
export { getRssSeen, getRssSubs } from './db-store.js';
|
|
3
|
+
export { getRssSeen, getRssSubs, listAllSubscriptions, type RssSubscriptionSummary } from './db-store.js';
|
|
4
4
|
export { rssRuntimeToken, type RssRuntime, type RssOutboundPush } from './runtime.js';
|
|
5
5
|
export { extractChannelInfo } from './channel.js';
|
|
6
|
-
export { checkSubscriptions, pollAllFeeds, formatNewItems } from './poll.js';
|
|
6
|
+
export { checkSubscriptions, cleanOldSeen, pollAllFeeds, formatNewItems } from './poll.js';
|
|
7
7
|
export type { CheckResult } from './poll.js';
|
|
8
|
+
export { createRssConsoleRpc } from './console.js';
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,oBAAoB,EAAE,KAAK,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3F,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { DEFAULT_RSS_CONFIG, fetchFeed, formatFeedPreview, resolveRssConfig, stripHtml, } from './feed.js';
|
|
2
|
-
export { getRssSeen, getRssSubs } from './db-store.js';
|
|
2
|
+
export { getRssSeen, getRssSubs, listAllSubscriptions } from './db-store.js';
|
|
3
3
|
export { rssRuntimeToken } from './runtime.js';
|
|
4
4
|
export { extractChannelInfo } from './channel.js';
|
|
5
|
-
export { checkSubscriptions, pollAllFeeds, formatNewItems } from './poll.js';
|
|
5
|
+
export { checkSubscriptions, cleanOldSeen, pollAllFeeds, formatNewItems } from './poll.js';
|
|
6
|
+
export { createRssConsoleRpc } from './console.js';
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,GACV,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,GACV,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,oBAAoB,EAA+B,MAAM,eAAe,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAyC,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/plugin-rss",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.16",
|
|
4
4
|
"description": "RSS/Atom feed subscription plugin for Zhin.js (Plugin Runtime)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"plugin.js",
|
|
17
17
|
"schema.json",
|
|
18
18
|
"commands",
|
|
19
|
+
"pages",
|
|
19
20
|
"src",
|
|
20
21
|
"lib",
|
|
21
22
|
"tools",
|
|
@@ -41,20 +42,26 @@
|
|
|
41
42
|
"license": "MIT",
|
|
42
43
|
"dependencies": {
|
|
43
44
|
"rss-parser": "^3.13.0",
|
|
44
|
-
"@zhin.js/
|
|
45
|
-
"@zhin.js/
|
|
46
|
-
"@zhin.js/
|
|
45
|
+
"@zhin.js/console-contract": "1.1.3",
|
|
46
|
+
"@zhin.js/page": "1.1.15",
|
|
47
|
+
"@zhin.js/client": "1.1.25",
|
|
48
|
+
"@zhin.js/tool": "1.1.16",
|
|
49
|
+
"@zhin.js/im-contract": "1.1.12",
|
|
50
|
+
"@zhin.js/skill": "1.1.14"
|
|
47
51
|
},
|
|
48
52
|
"devDependencies": {
|
|
49
53
|
"typescript": "^6.0.3",
|
|
50
54
|
"vitest": "^4.1.10",
|
|
51
55
|
"zod": "^4.4.3",
|
|
52
|
-
"zhin.js": "1.1.
|
|
56
|
+
"zhin.js": "1.1.22"
|
|
53
57
|
},
|
|
54
58
|
"peerDependencies": {
|
|
55
59
|
"zod": "^4.0.0",
|
|
56
|
-
"@zhin.js/command": "^1.1.
|
|
57
|
-
"zhin.js": "^1.1.
|
|
60
|
+
"@zhin.js/command": "^1.1.15",
|
|
61
|
+
"@zhin.js/skill": "^1.1.14",
|
|
62
|
+
"zhin.js": "^1.1.22",
|
|
63
|
+
"@zhin.js/tool": "^1.1.16",
|
|
64
|
+
"@zhin.js/page": "^1.1.15"
|
|
58
65
|
},
|
|
59
66
|
"peerDependenciesMeta": {
|
|
60
67
|
"@zhin.js/command": {
|
|
@@ -94,6 +101,10 @@
|
|
|
94
101
|
{
|
|
95
102
|
"package": "@zhin.js/skill",
|
|
96
103
|
"api": "^1.0.0"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"package": "@zhin.js/page",
|
|
107
|
+
"api": "^1.0.0"
|
|
97
108
|
}
|
|
98
109
|
],
|
|
99
110
|
"plugins": []
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { definePage } from '@zhin.js/console-contract';
|
|
3
|
+
import { createDeclarativePage } from '@zhin.js/client';
|
|
4
|
+
export const meta = definePage({
|
|
5
|
+
title: 'RSS 订阅',
|
|
6
|
+
titleI18n: { en: 'RSS Subscriptions' },
|
|
7
|
+
icon: 'Rss',
|
|
8
|
+
order: 52,
|
|
9
|
+
requiredPermissions: ['rss:read'],
|
|
10
|
+
});
|
|
11
|
+
export default createDeclarativePage({
|
|
12
|
+
title: { zh: 'RSS 订阅', en: 'RSS Subscriptions' },
|
|
13
|
+
description: {
|
|
14
|
+
zh: '管理 RSS/Atom 订阅源、轮询频率与已读缓存',
|
|
15
|
+
en: 'Manage RSS/Atom feed subscriptions, polling schedule, and seen cache',
|
|
16
|
+
},
|
|
17
|
+
stats: [
|
|
18
|
+
{
|
|
19
|
+
key: 'subscriptionCount',
|
|
20
|
+
label: { zh: '订阅数量', en: 'Subscriptions' },
|
|
21
|
+
value: 'subscriptionCount',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
key: 'pollCron',
|
|
25
|
+
label: { zh: '轮询 Cron', en: 'Poll Cron' },
|
|
26
|
+
value: 'pollCron',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: 'maxItemsPerPoll',
|
|
30
|
+
label: { zh: '单次最大推送', en: 'Max Items / Poll' },
|
|
31
|
+
value: 'maxItemsPerPoll',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
actions: [
|
|
35
|
+
{
|
|
36
|
+
name: 'poll-now',
|
|
37
|
+
label: { zh: '立即轮询', en: 'Poll now' },
|
|
38
|
+
variant: 'primary',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'clean-seen',
|
|
42
|
+
label: { zh: '清理已读缓存', en: 'Clean seen cache' },
|
|
43
|
+
variant: 'secondary',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
sections: [
|
|
47
|
+
{
|
|
48
|
+
id: 'subscriptions',
|
|
49
|
+
title: { zh: '当前订阅列表', en: 'Active Subscriptions' },
|
|
50
|
+
emptyText: { zh: '暂无 RSS 订阅', en: 'No RSS subscriptions' },
|
|
51
|
+
items: 'subscriptions',
|
|
52
|
+
columns: [
|
|
53
|
+
{ key: 'feedTitle', label: { zh: '标题', en: 'Feed Title' } },
|
|
54
|
+
{ key: 'url', label: { zh: '订阅地址', en: 'URL' } },
|
|
55
|
+
{ key: 'adapterName', label: { zh: '适配器', en: 'Adapter' } },
|
|
56
|
+
{ key: 'channelId', label: { zh: '会话 ID', en: 'Channel ID' } },
|
|
57
|
+
{ key: 'createdAt', label: { zh: '创建时间', en: 'Created At' } },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'config',
|
|
62
|
+
title: { zh: '配置详情', en: 'Configuration Details' },
|
|
63
|
+
entries: (data) => ({
|
|
64
|
+
pollCron: data.pollCron,
|
|
65
|
+
maxItemsPerPoll: String(data.maxItemsPerPoll),
|
|
66
|
+
fetchTimeoutMs: `${data.fetchTimeoutMs} ms`,
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { definePage } from '@zhin.js/console-contract';
|
|
2
|
+
import { createDeclarativePage } from '@zhin.js/client';
|
|
3
|
+
|
|
4
|
+
export interface RssSubscriptionItem {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly feedTitle: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
readonly adapterName: string;
|
|
9
|
+
readonly endpointId: string;
|
|
10
|
+
readonly channelType: string;
|
|
11
|
+
readonly channelId: string;
|
|
12
|
+
readonly creatorName: string;
|
|
13
|
+
readonly createdAt: string;
|
|
14
|
+
readonly [key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RssConsoleStatus {
|
|
18
|
+
readonly subscriptionCount: number;
|
|
19
|
+
readonly pollCron: string;
|
|
20
|
+
readonly maxItemsPerPoll: number;
|
|
21
|
+
readonly fetchTimeoutMs: number;
|
|
22
|
+
readonly subscriptions: readonly RssSubscriptionItem[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const meta = definePage({
|
|
26
|
+
title: 'RSS 订阅',
|
|
27
|
+
titleI18n: { en: 'RSS Subscriptions' },
|
|
28
|
+
icon: 'Rss',
|
|
29
|
+
order: 52,
|
|
30
|
+
requiredPermissions: ['rss:read'],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export default createDeclarativePage<RssConsoleStatus>({
|
|
34
|
+
title: { zh: 'RSS 订阅', en: 'RSS Subscriptions' },
|
|
35
|
+
description: {
|
|
36
|
+
zh: '管理 RSS/Atom 订阅源、轮询频率与已读缓存',
|
|
37
|
+
en: 'Manage RSS/Atom feed subscriptions, polling schedule, and seen cache',
|
|
38
|
+
},
|
|
39
|
+
stats: [
|
|
40
|
+
{
|
|
41
|
+
key: 'subscriptionCount',
|
|
42
|
+
label: { zh: '订阅数量', en: 'Subscriptions' },
|
|
43
|
+
value: 'subscriptionCount',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
key: 'pollCron',
|
|
47
|
+
label: { zh: '轮询 Cron', en: 'Poll Cron' },
|
|
48
|
+
value: 'pollCron',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: 'maxItemsPerPoll',
|
|
52
|
+
label: { zh: '单次最大推送', en: 'Max Items / Poll' },
|
|
53
|
+
value: 'maxItemsPerPoll',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
actions: [
|
|
57
|
+
{
|
|
58
|
+
name: 'poll-now',
|
|
59
|
+
label: { zh: '立即轮询', en: 'Poll now' },
|
|
60
|
+
variant: 'primary',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'clean-seen',
|
|
64
|
+
label: { zh: '清理已读缓存', en: 'Clean seen cache' },
|
|
65
|
+
variant: 'secondary',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
sections: [
|
|
69
|
+
{
|
|
70
|
+
id: 'subscriptions',
|
|
71
|
+
title: { zh: '当前订阅列表', en: 'Active Subscriptions' },
|
|
72
|
+
emptyText: { zh: '暂无 RSS 订阅', en: 'No RSS subscriptions' },
|
|
73
|
+
items: 'subscriptions',
|
|
74
|
+
columns: [
|
|
75
|
+
{ key: 'feedTitle', label: { zh: '标题', en: 'Feed Title' } },
|
|
76
|
+
{ key: 'url', label: { zh: '订阅地址', en: 'URL' } },
|
|
77
|
+
{ key: 'adapterName', label: { zh: '适配器', en: 'Adapter' } },
|
|
78
|
+
{ key: 'channelId', label: { zh: '会话 ID', en: 'Channel ID' } },
|
|
79
|
+
{ key: 'createdAt', label: { zh: '创建时间', en: 'Created At' } },
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'config',
|
|
84
|
+
title: { zh: '配置详情', en: 'Configuration Details' },
|
|
85
|
+
entries: (data) => ({
|
|
86
|
+
pollCron: data.pollCron,
|
|
87
|
+
maxItemsPerPoll: String(data.maxItemsPerPoll),
|
|
88
|
+
fetchTimeoutMs: `${data.fetchTimeoutMs} ms`,
|
|
89
|
+
}),
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
});
|
package/plugin.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import { DataMigrationError, pluginDataMigrationProviderToken, TargetMigrationScopes, pluginTaskProviderToken } from 'zhin.js/plugin-runtime';
|
|
3
3
|
import { createSubscriptionTargetMigration, subscriptionMigrationScope } from "./lib/target-migration.js";
|
|
4
4
|
import { projectLocaleToken, resolveLocale } from 'zhin.js/i18n';
|
|
5
|
-
import { definePlugin, logger, databaseHostToken, outboundHostToken, scheduleHostToken, } from 'zhin.js';
|
|
5
|
+
import { definePlugin, logger, databaseHostToken, outboundHostToken, pluginConsoleRpcToken, scheduleHostToken, } from 'zhin.js';
|
|
6
6
|
import { createAdministratorRssState } from "./lib/storage.js";
|
|
7
7
|
import { AdministratorRssTasks } from "./lib/admin-tasks.js";
|
|
8
|
+
import { createRssConsoleRpc } from "./lib/console.js";
|
|
8
9
|
import { resolveRssConfig } from "./lib/feed.js";
|
|
9
10
|
import { createInMemoryRssDb, RSS_SEEN_TABLE, RSS_SUBS_TABLE, } from "./lib/db-store.js";
|
|
10
11
|
import { cleanOldSeen, pollAllFeeds } from "./lib/poll.js";
|
|
@@ -76,6 +77,7 @@ export default definePlugin({
|
|
|
76
77
|
outbound: outboundPush,
|
|
77
78
|
});
|
|
78
79
|
context.resources.provide(rssRuntimeToken, runtime);
|
|
80
|
+
context.resources.provide(pluginConsoleRpcToken, createRssConsoleRpc(runtime));
|
|
79
81
|
// Keep optional in-memory/chat-only installations working. Administrator tasks
|
|
80
82
|
// must have the real durable state port, exact outbound and an active scheduler.
|
|
81
83
|
let administrator;
|
package/src/console.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
definePluginConsoleRpc,
|
|
3
|
+
PluginConsoleRpcError,
|
|
4
|
+
type NormalizedPluginConsoleRpcMethod,
|
|
5
|
+
type PluginConsoleRpc,
|
|
6
|
+
type PluginConsoleRpcCall,
|
|
7
|
+
type PluginConsoleRpcInput,
|
|
8
|
+
type PluginConsoleRpcJson,
|
|
9
|
+
} from 'zhin.js';
|
|
10
|
+
import { listAllSubscriptions } from './db-store.js';
|
|
11
|
+
import { cleanOldSeen, pollAllFeeds } from './poll.js';
|
|
12
|
+
import { rssRuntimeToken, type RssRuntime } from './runtime.js';
|
|
13
|
+
|
|
14
|
+
type RssRuntimeLookup = {
|
|
15
|
+
readonly use?: (token: typeof rssRuntimeToken) => RssRuntime | undefined;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function sanitizeFeedUrl(rawUrl: string): string {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = new URL(rawUrl);
|
|
21
|
+
if (parsed.username || parsed.password) {
|
|
22
|
+
parsed.username = '';
|
|
23
|
+
parsed.password = '';
|
|
24
|
+
return parsed.toString();
|
|
25
|
+
}
|
|
26
|
+
return rawUrl;
|
|
27
|
+
} catch {
|
|
28
|
+
return rawUrl;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function resolveRssRuntime(call: PluginConsoleRpcCall, fallback?: RssRuntime): RssRuntime {
|
|
33
|
+
const runtime = (call.runtime as RssRuntimeLookup | undefined)?.use?.(rssRuntimeToken) ?? fallback;
|
|
34
|
+
if (!runtime) {
|
|
35
|
+
throw new PluginConsoleRpcError('unavailable', 'RSS runtime is not initialized');
|
|
36
|
+
}
|
|
37
|
+
return runtime;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createRssConsoleRpc(defaultRuntime?: RssRuntime): PluginConsoleRpc {
|
|
41
|
+
const rpc = definePluginConsoleRpc({
|
|
42
|
+
status: {
|
|
43
|
+
page: 'index',
|
|
44
|
+
requiredPermissions: ['rss:read'],
|
|
45
|
+
async handler(_input: PluginConsoleRpcInput, call: PluginConsoleRpcCall): Promise<PluginConsoleRpcJson> {
|
|
46
|
+
const runtime = resolveRssRuntime(call, defaultRuntime);
|
|
47
|
+
const allSubscriptions = await listAllSubscriptions(runtime.db);
|
|
48
|
+
return {
|
|
49
|
+
subscriptionCount: allSubscriptions.length,
|
|
50
|
+
pollCron: runtime.config.pollCron,
|
|
51
|
+
maxItemsPerPoll: runtime.config.maxItemsPerPoll ?? runtime.config.maxItems,
|
|
52
|
+
fetchTimeoutMs: runtime.config.fetchTimeoutMs ?? runtime.config.timeout,
|
|
53
|
+
subscriptions: allSubscriptions.slice(0, 100).map((sub) => ({
|
|
54
|
+
...sub,
|
|
55
|
+
url: sanitizeFeedUrl(sub.url),
|
|
56
|
+
})),
|
|
57
|
+
} as unknown as PluginConsoleRpcJson;
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
actions: {
|
|
61
|
+
'poll-now': {
|
|
62
|
+
page: 'index',
|
|
63
|
+
requiredPermissions: ['rss:write'],
|
|
64
|
+
async handler(_input: PluginConsoleRpcInput, call: PluginConsoleRpcCall): Promise<PluginConsoleRpcJson> {
|
|
65
|
+
const runtime = resolveRssRuntime(call, defaultRuntime);
|
|
66
|
+
await pollAllFeeds(runtime);
|
|
67
|
+
return { ok: true, polledAt: Date.now() };
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
'clean-seen': {
|
|
71
|
+
page: 'index',
|
|
72
|
+
requiredPermissions: ['rss:write'],
|
|
73
|
+
async handler(_input: PluginConsoleRpcInput, call: PluginConsoleRpcCall): Promise<PluginConsoleRpcJson> {
|
|
74
|
+
const runtime = resolveRssRuntime(call, defaultRuntime);
|
|
75
|
+
await cleanOldSeen(runtime);
|
|
76
|
+
return { ok: true, cleanedAt: Date.now() };
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const methods = rpc.methods as Map<string, NormalizedPluginConsoleRpcMethod>;
|
|
83
|
+
const origGet = methods.get.bind(methods);
|
|
84
|
+
const origHas = methods.has.bind(methods);
|
|
85
|
+
const normalizeKey = (key: string) =>
|
|
86
|
+
key === 'pollNow' ? 'poll-now' : key === 'cleanSeen' ? 'clean-seen' : key;
|
|
87
|
+
methods.get = (key: string) => origGet(normalizeKey(key));
|
|
88
|
+
methods.has = (key: string) => origHas(normalizeKey(key));
|
|
89
|
+
return rpc;
|
|
90
|
+
}
|
package/src/db-store.ts
CHANGED
|
@@ -20,3 +20,32 @@ export function getRssSubs(db: RssMemoryDb): RssModel | null {
|
|
|
20
20
|
export function getRssSeen(db: RssMemoryDb): RssModel | null {
|
|
21
21
|
return db.models.get(RSS_SEEN_TABLE) ?? null;
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
export interface RssSubscriptionSummary {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
readonly feedTitle: string;
|
|
27
|
+
readonly url: string;
|
|
28
|
+
readonly adapterName: string;
|
|
29
|
+
readonly endpointId: string;
|
|
30
|
+
readonly channelType: string;
|
|
31
|
+
readonly channelId: string;
|
|
32
|
+
readonly creatorName: string;
|
|
33
|
+
readonly createdAt: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function listAllSubscriptions(db: RssMemoryDb): Promise<RssSubscriptionSummary[]> {
|
|
37
|
+
const Subs = getRssSubs(db);
|
|
38
|
+
if (!Subs) return [];
|
|
39
|
+
const rows = await Subs.select();
|
|
40
|
+
return rows.map((row, index) => ({
|
|
41
|
+
id: String(row.id ?? index + 1),
|
|
42
|
+
feedTitle: String(row.feed_title ?? row.feedTitle ?? ''),
|
|
43
|
+
url: String(row.url ?? ''),
|
|
44
|
+
adapterName: String(row.adapter_name ?? row.adapterName ?? ''),
|
|
45
|
+
endpointId: String(row.endpoint_id ?? row.endpointId ?? ''),
|
|
46
|
+
channelType: String(row.channel_type ?? row.channelType ?? 'private'),
|
|
47
|
+
channelId: String(row.channel_id ?? row.channelId ?? ''),
|
|
48
|
+
creatorName: String(row.creator_name ?? row.creatorName ?? ''),
|
|
49
|
+
createdAt: String(row.created_at ?? row.createdAt ?? ''),
|
|
50
|
+
}));
|
|
51
|
+
}
|
package/src/feed.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface RssConfig {
|
|
|
18
18
|
readonly maxPerGroup: number;
|
|
19
19
|
readonly maxItems: number;
|
|
20
20
|
readonly timeout: number;
|
|
21
|
+
readonly maxItemsPerPoll?: number;
|
|
22
|
+
readonly fetchTimeoutMs?: number;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export const DEFAULT_RSS_CONFIG: RssConfig = {
|
|
@@ -28,7 +30,12 @@ export const DEFAULT_RSS_CONFIG: RssConfig = {
|
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
export function resolveRssConfig(raw: Partial<RssConfig> | undefined): RssConfig {
|
|
31
|
-
|
|
33
|
+
const merged = { ...DEFAULT_RSS_CONFIG, ...raw };
|
|
34
|
+
return {
|
|
35
|
+
...merged,
|
|
36
|
+
maxItemsPerPoll: raw?.maxItemsPerPoll ?? merged.maxItems,
|
|
37
|
+
fetchTimeoutMs: raw?.fetchTimeoutMs ?? merged.timeout,
|
|
38
|
+
};
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
/**
|
package/src/index.ts
CHANGED
|
@@ -6,8 +6,9 @@ export {
|
|
|
6
6
|
stripHtml,
|
|
7
7
|
} from './feed.js';
|
|
8
8
|
export type { FeedItem, RssConfig } from './feed.js';
|
|
9
|
-
export { getRssSeen, getRssSubs } from './db-store.js';
|
|
9
|
+
export { getRssSeen, getRssSubs, listAllSubscriptions, type RssSubscriptionSummary } from './db-store.js';
|
|
10
10
|
export { rssRuntimeToken, type RssRuntime, type RssOutboundPush } from './runtime.js';
|
|
11
11
|
export { extractChannelInfo } from './channel.js';
|
|
12
|
-
export { checkSubscriptions, pollAllFeeds, formatNewItems } from './poll.js';
|
|
12
|
+
export { checkSubscriptions, cleanOldSeen, pollAllFeeds, formatNewItems } from './poll.js';
|
|
13
13
|
export type { CheckResult } from './poll.js';
|
|
14
|
+
export { createRssConsoleRpc } from './console.js';
|