@zhin.js/plugin-rss 1.1.0 → 1.1.2
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/commands/rss-add/{[url].js → [url]/index.js} +9 -7
- package/commands/rss-add/{[url].ts → [url]/index.ts} +9 -7
- package/commands/rss-check/{[[url]].js → [[url]]/index.js} +9 -8
- package/commands/rss-check/{[[url]].ts → [[url]]/index.ts} +9 -8
- package/commands/{rss-list.js → rss-list/index.js} +6 -5
- package/commands/{rss-list.ts → rss-list/index.ts} +6 -5
- package/commands/rss-preview/{[url].js → [url]/index.js} +1 -1
- package/commands/rss-preview/{[url].ts → [url]/index.ts} +1 -1
- package/commands/rss-remove/{[url].js → [url]/index.js} +5 -4
- package/commands/rss-remove/{[url].ts → [url]/index.ts} +6 -5
- package/lib/db-store.d.ts +2 -13
- package/lib/db-store.d.ts.map +1 -1
- package/lib/db-store.js +5 -36
- package/lib/db-store.js.map +1 -1
- package/lib/index.d.ts +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -2
- package/lib/index.js.map +1 -1
- package/lib/memory-store.d.ts +1 -1
- package/lib/memory-store.js +1 -1
- package/lib/poll.d.ts +6 -15
- package/lib/poll.d.ts.map +1 -1
- package/lib/poll.js +18 -25
- package/lib/poll.js.map +1 -1
- package/lib/runtime.d.ts +16 -0
- package/lib/runtime.d.ts.map +1 -0
- package/lib/runtime.js +3 -0
- package/lib/runtime.js.map +1 -0
- package/package.json +17 -11
- package/plugin.js +17 -22
- package/skills/rss/tools/rss_list_subscriptions/index.js +32 -0
- package/{agent/tools/rss_list_subscriptions.ts → skills/rss/tools/rss_list_subscriptions/index.ts} +5 -5
- package/skills/rss/tools/rss_preview_feed/index.js +26 -0
- package/{agent/tools/rss_preview_feed.ts → skills/rss/tools/rss_preview_feed/index.ts} +6 -5
- package/src/db-store.ts +4 -41
- package/src/index.ts +4 -11
- package/src/memory-store.ts +1 -1
- package/src/poll.ts +20 -38
- package/src/runtime.ts +22 -0
- package/lib/rss-agent-deps.d.ts +0 -15
- package/lib/rss-agent-deps.d.ts.map +0 -1
- package/lib/rss-agent-deps.js +0 -10
- package/lib/rss-agent-deps.js.map +0 -1
- package/src/rss-agent-deps.ts +0 -15
- /package/{agent/skills/rss.md → skills/rss/SKILL.md} +0 -0
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import { extractChannelInfo } from "
|
|
4
|
-
import { getRssSeen, getRssSubs } from "
|
|
5
|
-
import { fetchFeed, resolveRssConfig } from "
|
|
6
|
-
import { markItemsSeen } from "
|
|
3
|
+
import { extractChannelInfo } from "../../../lib/channel.js";
|
|
4
|
+
import { getRssSeen, getRssSubs } from "../../../lib/db-store.js";
|
|
5
|
+
import { fetchFeed, resolveRssConfig } from "../../../lib/feed.js";
|
|
6
|
+
import { markItemsSeen } from "../../../lib/poll.js";
|
|
7
|
+
import { rssRuntimeToken } from "../../../lib/runtime.js";
|
|
7
8
|
export default defineCommand({
|
|
8
9
|
description: '订阅一个 RSS/Atom 源',
|
|
9
10
|
params: { url: { type: 'string' } },
|
|
10
|
-
async execute({ params, config, input }) {
|
|
11
|
-
const
|
|
11
|
+
async execute({ params, config, input, use }) {
|
|
12
|
+
const runtime = use(rssRuntimeToken);
|
|
13
|
+
const Subs = getRssSubs(runtime.db);
|
|
12
14
|
if (!Subs)
|
|
13
15
|
return 'RSS 数据库尚未就绪,请稍后重试';
|
|
14
16
|
const url = String(params.url ?? '').trim();
|
|
@@ -38,7 +40,7 @@ export default defineCommand({
|
|
|
38
40
|
try {
|
|
39
41
|
const { title, items } = await fetchFeed(url, cfg.timeout);
|
|
40
42
|
feedTitle = title;
|
|
41
|
-
await markItemsSeen(url, items);
|
|
43
|
+
await markItemsSeen(runtime, url, items);
|
|
42
44
|
}
|
|
43
45
|
catch (e) {
|
|
44
46
|
return `无法解析该地址: ${e.message}\n请确认是有效的 RSS/Atom 源`;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { defineCommand } from 'zhin.js/command';
|
|
2
|
-
import { extractChannelInfo } from '
|
|
3
|
-
import { getRssSeen, getRssSubs } from '
|
|
4
|
-
import { fetchFeed, resolveRssConfig, type RssConfig } from '
|
|
5
|
-
import { markItemsSeen } from '
|
|
2
|
+
import { extractChannelInfo } from '../../../src/channel.js';
|
|
3
|
+
import { getRssSeen, getRssSubs } from '../../../src/db-store.js';
|
|
4
|
+
import { fetchFeed, resolveRssConfig, type RssConfig } from '../../../src/feed.js';
|
|
5
|
+
import { markItemsSeen } from '../../../src/poll.js';
|
|
6
|
+
import { rssRuntimeToken } from '../../../src/runtime.js';
|
|
6
7
|
|
|
7
8
|
export default defineCommand<RssConfig>({
|
|
8
9
|
description: '订阅一个 RSS/Atom 源',
|
|
9
10
|
params: { url: { type: 'string' } },
|
|
10
|
-
async execute({ params, config, input }) {
|
|
11
|
-
const
|
|
11
|
+
async execute({ params, config, input, use }) {
|
|
12
|
+
const runtime = use(rssRuntimeToken);
|
|
13
|
+
const Subs = getRssSubs(runtime.db);
|
|
12
14
|
if (!Subs) return 'RSS 数据库尚未就绪,请稍后重试';
|
|
13
15
|
|
|
14
16
|
const url = String(params.url ?? '').trim();
|
|
@@ -39,7 +41,7 @@ export default defineCommand<RssConfig>({
|
|
|
39
41
|
try {
|
|
40
42
|
const { title, items } = await fetchFeed(url, cfg.timeout);
|
|
41
43
|
feedTitle = title;
|
|
42
|
-
await markItemsSeen(url, items);
|
|
44
|
+
await markItemsSeen(runtime, url, items);
|
|
43
45
|
} catch (e) {
|
|
44
46
|
return `无法解析该地址: ${(e as Error).message}\n请确认是有效的 RSS/Atom 源`;
|
|
45
47
|
}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import { extractChannelInfo } from "
|
|
4
|
-
import { getRssSubs } from "
|
|
5
|
-
import { resolveRssConfig } from "
|
|
6
|
-
import { checkSubscriptions } from "
|
|
3
|
+
import { extractChannelInfo } from "../../../lib/channel.js";
|
|
4
|
+
import { getRssSubs } from "../../../lib/db-store.js";
|
|
5
|
+
import { resolveRssConfig } from "../../../lib/feed.js";
|
|
6
|
+
import { checkSubscriptions } from "../../../lib/poll.js";
|
|
7
|
+
import { rssRuntimeToken } from "../../../lib/runtime.js";
|
|
7
8
|
export default defineCommand({
|
|
8
9
|
description: '手动触发检查 RSS 更新',
|
|
9
10
|
params: { url: { type: 'string', default: '' } },
|
|
10
|
-
async execute({ params,
|
|
11
|
-
const
|
|
11
|
+
async execute({ params, input, use }) {
|
|
12
|
+
const runtime = use(rssRuntimeToken);
|
|
13
|
+
const Subs = getRssSubs(runtime.db);
|
|
12
14
|
if (!Subs)
|
|
13
15
|
return 'RSS 数据库尚未就绪';
|
|
14
16
|
const channel = extractChannelInfo(input);
|
|
15
|
-
const cfg = resolveRssConfig(config);
|
|
16
17
|
const targetUrl = String(params.url ?? '').trim();
|
|
17
18
|
let urls;
|
|
18
19
|
if (targetUrl) {
|
|
@@ -36,7 +37,7 @@ export default defineCommand({
|
|
|
36
37
|
return '当前会话没有任何订阅';
|
|
37
38
|
urls = [...new Set(rows.map((r) => String(r.url ?? '')).filter(Boolean))];
|
|
38
39
|
}
|
|
39
|
-
const result = await checkSubscriptions({ urls
|
|
40
|
+
const result = await checkSubscriptions(runtime, { urls });
|
|
40
41
|
return result.text;
|
|
41
42
|
},
|
|
42
43
|
});
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { defineCommand } from 'zhin.js/command';
|
|
2
|
-
import { extractChannelInfo } from '
|
|
3
|
-
import { getRssSubs } from '
|
|
4
|
-
import { resolveRssConfig, type RssConfig } from '
|
|
5
|
-
import { checkSubscriptions } from '
|
|
2
|
+
import { extractChannelInfo } from '../../../src/channel.js';
|
|
3
|
+
import { getRssSubs } from '../../../src/db-store.js';
|
|
4
|
+
import { resolveRssConfig, type RssConfig } from '../../../src/feed.js';
|
|
5
|
+
import { checkSubscriptions } from '../../../src/poll.js';
|
|
6
|
+
import { rssRuntimeToken } from '../../../src/runtime.js';
|
|
6
7
|
|
|
7
8
|
export default defineCommand<RssConfig>({
|
|
8
9
|
description: '手动触发检查 RSS 更新',
|
|
9
10
|
params: { url: { type: 'string', default: '' } },
|
|
10
|
-
async execute({ params,
|
|
11
|
-
const
|
|
11
|
+
async execute({ params, input, use }) {
|
|
12
|
+
const runtime = use(rssRuntimeToken);
|
|
13
|
+
const Subs = getRssSubs(runtime.db);
|
|
12
14
|
if (!Subs) return 'RSS 数据库尚未就绪';
|
|
13
15
|
|
|
14
16
|
const channel = extractChannelInfo(input);
|
|
15
|
-
const cfg = resolveRssConfig(config);
|
|
16
17
|
const targetUrl = String(params.url ?? '').trim();
|
|
17
18
|
|
|
18
19
|
let urls: string[];
|
|
@@ -35,7 +36,7 @@ export default defineCommand<RssConfig>({
|
|
|
35
36
|
urls = [...new Set(rows.map((r) => String(r.url ?? '')).filter(Boolean))];
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
const result = await checkSubscriptions({ urls
|
|
39
|
+
const result = await checkSubscriptions(runtime, { urls });
|
|
39
40
|
return result.text;
|
|
40
41
|
},
|
|
41
42
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import { extractChannelInfo } from "
|
|
4
|
-
import { getRssSubs } from "
|
|
5
|
-
import { resolveRssConfig } from "
|
|
3
|
+
import { extractChannelInfo } from "../../lib/channel.js";
|
|
4
|
+
import { getRssSubs } from "../../lib/db-store.js";
|
|
5
|
+
import { resolveRssConfig } from "../../lib/feed.js";
|
|
6
|
+
import { rssRuntimeToken } from "../../lib/runtime.js";
|
|
6
7
|
export default defineCommand({
|
|
7
8
|
description: '查看当前会话的所有 RSS 订阅',
|
|
8
|
-
async execute({ config, input }) {
|
|
9
|
-
const Subs = getRssSubs();
|
|
9
|
+
async execute({ config, input, use }) {
|
|
10
|
+
const Subs = getRssSubs(use(rssRuntimeToken).db);
|
|
10
11
|
if (!Subs)
|
|
11
12
|
return 'RSS 数据库尚未就绪';
|
|
12
13
|
const channel = extractChannelInfo(input);
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { defineCommand } from 'zhin.js/command';
|
|
2
|
-
import { extractChannelInfo } from '
|
|
3
|
-
import { getRssSubs } from '
|
|
4
|
-
import { resolveRssConfig, type RssConfig } from '
|
|
2
|
+
import { extractChannelInfo } from '../../src/channel.js';
|
|
3
|
+
import { getRssSubs } from '../../src/db-store.js';
|
|
4
|
+
import { resolveRssConfig, type RssConfig } from '../../src/feed.js';
|
|
5
|
+
import { rssRuntimeToken } from '../../src/runtime.js';
|
|
5
6
|
|
|
6
7
|
export default defineCommand<RssConfig>({
|
|
7
8
|
description: '查看当前会话的所有 RSS 订阅',
|
|
8
|
-
async execute({ config, input }) {
|
|
9
|
-
const Subs = getRssSubs();
|
|
9
|
+
async execute({ config, input, use }) {
|
|
10
|
+
const Subs = getRssSubs(use(rssRuntimeToken).db);
|
|
10
11
|
if (!Subs) return 'RSS 数据库尚未就绪';
|
|
11
12
|
|
|
12
13
|
const channel = extractChannelInfo(input);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import { fetchFeed, formatFeedPreview, resolveRssConfig } from "
|
|
3
|
+
import { fetchFeed, formatFeedPreview, resolveRssConfig } from "../../../lib/feed.js";
|
|
4
4
|
export default defineCommand({
|
|
5
5
|
description: '预览一个 RSS 源的最新内容(不订阅)',
|
|
6
6
|
params: { url: { type: 'string' } },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineCommand } from 'zhin.js/command';
|
|
2
|
-
import { fetchFeed, formatFeedPreview, resolveRssConfig, type RssConfig } from '
|
|
2
|
+
import { fetchFeed, formatFeedPreview, resolveRssConfig, type RssConfig } from '../../../src/feed.js';
|
|
3
3
|
|
|
4
4
|
export default defineCommand<RssConfig>({
|
|
5
5
|
description: '预览一个 RSS 源的最新内容(不订阅)',
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import { extractChannelInfo } from "
|
|
4
|
-
import { getRssSubs } from "
|
|
3
|
+
import { extractChannelInfo } from "../../../lib/channel.js";
|
|
4
|
+
import { getRssSubs } from "../../../lib/db-store.js";
|
|
5
|
+
import { rssRuntimeToken } from "../../../lib/runtime.js";
|
|
5
6
|
export default defineCommand({
|
|
6
7
|
description: '取消一个 RSS 订阅',
|
|
7
8
|
params: { url: { type: 'string' } },
|
|
8
|
-
async execute({ params, input }) {
|
|
9
|
-
const Subs = getRssSubs();
|
|
9
|
+
async execute({ params, input, use }) {
|
|
10
|
+
const Subs = getRssSubs(use(rssRuntimeToken).db);
|
|
10
11
|
if (!Subs)
|
|
11
12
|
return 'RSS 数据库尚未就绪';
|
|
12
13
|
const url = String(params.url ?? '').trim();
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { defineCommand } from 'zhin.js/command';
|
|
2
|
-
import { extractChannelInfo } from '
|
|
3
|
-
import { getRssSubs } from '
|
|
4
|
-
import type { RssConfig } from '
|
|
2
|
+
import { extractChannelInfo } from '../../../src/channel.js';
|
|
3
|
+
import { getRssSubs } from '../../../src/db-store.js';
|
|
4
|
+
import type { RssConfig } from '../../../src/feed.js';
|
|
5
|
+
import { rssRuntimeToken } from '../../../src/runtime.js';
|
|
5
6
|
|
|
6
7
|
export default defineCommand<RssConfig>({
|
|
7
8
|
description: '取消一个 RSS 订阅',
|
|
8
9
|
params: { url: { type: 'string' } },
|
|
9
|
-
async execute({ params, input }) {
|
|
10
|
-
const Subs = getRssSubs();
|
|
10
|
+
async execute({ params, input, use }) {
|
|
11
|
+
const Subs = getRssSubs(use(rssRuntimeToken).db);
|
|
11
12
|
if (!Subs) return 'RSS 数据库尚未就绪';
|
|
12
13
|
|
|
13
14
|
const url = String(params.url ?? '').trim();
|
package/lib/db-store.d.ts
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
export { createInMemoryRssDb, RSS_SEEN_TABLE, RSS_SUBS_TABLE, type RssMemoryDb, type RssModel, } from './memory-store.js';
|
|
2
|
-
import { type Dispose, type GenerationStoreContext } from 'zhin.js';
|
|
3
2
|
import { type RssMemoryDb, type RssModel } from './memory-store.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* Auto-unregisters when the generation lifecycle disposes, so no stale
|
|
7
|
-
* reference to the previous generation's host survives a reload.
|
|
8
|
-
*/
|
|
9
|
-
export declare function provideRssDb(context: GenerationStoreContext, db: RssMemoryDb): Dispose;
|
|
10
|
-
export declare function ensureRssMemoryDb(): RssMemoryDb;
|
|
11
|
-
export declare function getRssDb(): RssMemoryDb | null;
|
|
12
|
-
export declare function setRssDb(db: RssMemoryDb | null): void;
|
|
13
|
-
export declare function resetRssDb(): void;
|
|
14
|
-
export declare function getRssSubs(): RssModel | null;
|
|
15
|
-
export declare function getRssSeen(): RssModel | null;
|
|
3
|
+
export declare function getRssSubs(db: RssMemoryDb): RssModel | null;
|
|
4
|
+
export declare function getRssSeen(db: RssMemoryDb): RssModel | null;
|
|
16
5
|
//# 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,
|
|
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"}
|
package/lib/db-store.js
CHANGED
|
@@ -1,40 +1,9 @@
|
|
|
1
1
|
export { createInMemoryRssDb, RSS_SEEN_TABLE, RSS_SUBS_TABLE, } from './memory-store.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
let _db = null;
|
|
6
|
-
let _memoryDb = null;
|
|
7
|
-
/**
|
|
8
|
-
* Generation-owned database binding used by Plugin Runtime setup().
|
|
9
|
-
* Auto-unregisters when the generation lifecycle disposes, so no stale
|
|
10
|
-
* reference to the previous generation's host survives a reload.
|
|
11
|
-
*/
|
|
12
|
-
export function provideRssDb(context, db) {
|
|
13
|
-
return rssDbStore.provide(context, db);
|
|
2
|
+
import { RSS_SEEN_TABLE, RSS_SUBS_TABLE, } from './memory-store.js';
|
|
3
|
+
export function getRssSubs(db) {
|
|
4
|
+
return db.models.get(RSS_SUBS_TABLE) ?? null;
|
|
14
5
|
}
|
|
15
|
-
export function
|
|
16
|
-
|
|
17
|
-
if (provided)
|
|
18
|
-
return provided;
|
|
19
|
-
if (!_memoryDb)
|
|
20
|
-
_memoryDb = createInMemoryRssDb();
|
|
21
|
-
return _memoryDb;
|
|
22
|
-
}
|
|
23
|
-
export function getRssDb() {
|
|
24
|
-
return ensureRssMemoryDb();
|
|
25
|
-
}
|
|
26
|
-
export function setRssDb(db) {
|
|
27
|
-
_db = db;
|
|
28
|
-
}
|
|
29
|
-
export function resetRssDb() {
|
|
30
|
-
rssDbStore.clear();
|
|
31
|
-
_db = null;
|
|
32
|
-
_memoryDb = null;
|
|
33
|
-
}
|
|
34
|
-
export function getRssSubs() {
|
|
35
|
-
return ensureRssMemoryDb().models.get(RSS_SUBS_TABLE) ?? null;
|
|
36
|
-
}
|
|
37
|
-
export function getRssSeen() {
|
|
38
|
-
return ensureRssMemoryDb().models.get(RSS_SEEN_TABLE) ?? null;
|
|
6
|
+
export function getRssSeen(db) {
|
|
7
|
+
return db.models.get(RSS_SEEN_TABLE) ?? null;
|
|
39
8
|
}
|
|
40
9
|
//# 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,
|
|
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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { DEFAULT_RSS_CONFIG, fetchFeed, formatFeedPreview, resolveRssConfig, stripHtml, } from './feed.js';
|
|
2
2
|
export type { FeedItem, RssConfig } from './feed.js';
|
|
3
|
-
export {
|
|
3
|
+
export { getRssSeen, getRssSubs } from './db-store.js';
|
|
4
|
+
export { rssRuntimeToken, type RssRuntime, type RssOutboundPush } from './runtime.js';
|
|
4
5
|
export { extractChannelInfo, SMOKE_CHANNEL } from './channel.js';
|
|
5
|
-
export { checkSubscriptions, pollAllFeeds, formatNewItems
|
|
6
|
-
export type {
|
|
6
|
+
export { checkSubscriptions, pollAllFeeds, formatNewItems } from './poll.js';
|
|
7
|
+
export type { CheckResult } from './poll.js';
|
|
7
8
|
//# 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,
|
|
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;AACvD,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC7E,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { DEFAULT_RSS_CONFIG, fetchFeed, formatFeedPreview, resolveRssConfig, stripHtml, } from './feed.js';
|
|
2
|
-
export {
|
|
2
|
+
export { getRssSeen, getRssSubs } from './db-store.js';
|
|
3
|
+
export { rssRuntimeToken } from './runtime.js';
|
|
3
4
|
export { extractChannelInfo, SMOKE_CHANNEL } from './channel.js';
|
|
4
|
-
export { checkSubscriptions, pollAllFeeds, formatNewItems
|
|
5
|
+
export { checkSubscriptions, pollAllFeeds, formatNewItems } from './poll.js';
|
|
5
6
|
//# 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,
|
|
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;AACvD,OAAO,EAAE,eAAe,EAAyC,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/lib/memory-store.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* In-memory RSS models
|
|
2
|
+
* In-memory RSS models used when no DatabaseHost is available.
|
|
3
3
|
*
|
|
4
4
|
* 模型表面与 `@zhin.js/plugin-runtime` 的 DatabaseHostModel 结构兼容:
|
|
5
5
|
* select 的 where 返回 PromiseLike(host 返回链式 Selection,memory 返回 Promise),
|
package/lib/memory-store.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* In-memory RSS models
|
|
2
|
+
* In-memory RSS models used when no DatabaseHost is available.
|
|
3
3
|
*
|
|
4
4
|
* 模型表面与 `@zhin.js/plugin-runtime` 的 DatabaseHostModel 结构兼容:
|
|
5
5
|
* select 的 where 返回 PromiseLike(host 返回链式 Selection,memory 返回 Promise),
|
package/lib/poll.d.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Feed poll helpers: detect new items; optional OutboundHost push to subscribers.
|
|
3
3
|
*/
|
|
4
|
-
import { type FeedItem
|
|
5
|
-
|
|
6
|
-
readonly adapterName: string;
|
|
7
|
-
readonly endpointKey: string;
|
|
8
|
-
readonly channelType: string;
|
|
9
|
-
readonly channelId: string;
|
|
10
|
-
readonly content: string;
|
|
11
|
-
}) => Promise<void>;
|
|
12
|
-
export declare function setRssOutboundPush(push: RssOutboundPush | null): void;
|
|
13
|
-
export declare function getRssOutboundPush(): RssOutboundPush | null;
|
|
4
|
+
import { type FeedItem } from './feed.js';
|
|
5
|
+
import type { RssRuntime } from './runtime.js';
|
|
14
6
|
export declare function formatNewItems(feedTitle: string, items: FeedItem[], maxItems: number): string;
|
|
15
|
-
export declare function markItemsSeen(feedUrl: string, items: FeedItem[]): Promise<void>;
|
|
7
|
+
export declare function markItemsSeen(runtime: RssRuntime, feedUrl: string, items: FeedItem[]): Promise<void>;
|
|
16
8
|
export interface CheckResult {
|
|
17
9
|
readonly text: string;
|
|
18
10
|
readonly totalNew: number;
|
|
@@ -22,16 +14,15 @@ export interface CheckResult {
|
|
|
22
14
|
* Check subscribed feeds for new items and mark them seen.
|
|
23
15
|
* When OutboundHost is wired, pushes formatted text to each subscriber channel.
|
|
24
16
|
*/
|
|
25
|
-
export declare function checkSubscriptions(options: {
|
|
17
|
+
export declare function checkSubscriptions(runtime: RssRuntime, options: {
|
|
26
18
|
urls: readonly string[];
|
|
27
|
-
config: RssConfig;
|
|
28
19
|
}): Promise<CheckResult>;
|
|
29
20
|
/**
|
|
30
21
|
* Periodically clean seen records older than 7 days.
|
|
31
22
|
*/
|
|
32
|
-
export declare function cleanOldSeen(): Promise<void>;
|
|
23
|
+
export declare function cleanOldSeen(runtime: RssRuntime): Promise<void>;
|
|
33
24
|
/**
|
|
34
25
|
* Poll all unique feed URLs across subscriptions.
|
|
35
26
|
*/
|
|
36
|
-
export declare function pollAllFeeds(
|
|
27
|
+
export declare function pollAllFeeds(runtime: RssRuntime): Promise<CheckResult>;
|
|
37
28
|
//# sourceMappingURL=poll.d.ts.map
|
package/lib/poll.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../src/poll.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAa,KAAK,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../src/poll.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM/C,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY7F;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAc1G;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AA4BD;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE;IACrE,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB,GAAG,OAAO,CAAC,WAAW,CAAC,CAuCvB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBrE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAO5E"}
|
package/lib/poll.js
CHANGED
|
@@ -3,13 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { fetchFeed } from './feed.js';
|
|
5
5
|
import { getRssSeen, getRssSubs } from './db-store.js';
|
|
6
|
-
let _outboundPush = null;
|
|
7
|
-
export function setRssOutboundPush(push) {
|
|
8
|
-
_outboundPush = push;
|
|
9
|
-
}
|
|
10
|
-
export function getRssOutboundPush() {
|
|
11
|
-
return _outboundPush;
|
|
12
|
-
}
|
|
13
6
|
function ts() {
|
|
14
7
|
return new Date().toISOString();
|
|
15
8
|
}
|
|
@@ -28,8 +21,8 @@ export function formatNewItems(feedTitle, items, maxItems) {
|
|
|
28
21
|
}
|
|
29
22
|
return lines.join('\n');
|
|
30
23
|
}
|
|
31
|
-
export async function markItemsSeen(feedUrl, items) {
|
|
32
|
-
const Seen = getRssSeen();
|
|
24
|
+
export async function markItemsSeen(runtime, feedUrl, items) {
|
|
25
|
+
const Seen = getRssSeen(runtime.db);
|
|
33
26
|
if (!Seen)
|
|
34
27
|
return;
|
|
35
28
|
for (const item of items) {
|
|
@@ -44,9 +37,9 @@ export async function markItemsSeen(feedUrl, items) {
|
|
|
44
37
|
}
|
|
45
38
|
}
|
|
46
39
|
}
|
|
47
|
-
async function pushToSubscribers(feedUrl, content) {
|
|
48
|
-
const push =
|
|
49
|
-
const Subs = getRssSubs();
|
|
40
|
+
async function pushToSubscribers(runtime, feedUrl, content) {
|
|
41
|
+
const push = runtime.outbound;
|
|
42
|
+
const Subs = getRssSubs(runtime.db);
|
|
50
43
|
if (!push || !Subs)
|
|
51
44
|
return 0;
|
|
52
45
|
const subscribers = await Subs.select().where({ url: feedUrl });
|
|
@@ -76,9 +69,9 @@ async function pushToSubscribers(feedUrl, content) {
|
|
|
76
69
|
* Check subscribed feeds for new items and mark them seen.
|
|
77
70
|
* When OutboundHost is wired, pushes formatted text to each subscriber channel.
|
|
78
71
|
*/
|
|
79
|
-
export async function checkSubscriptions(options) {
|
|
80
|
-
const Seen = getRssSeen();
|
|
81
|
-
const Subs = getRssSubs();
|
|
72
|
+
export async function checkSubscriptions(runtime, options) {
|
|
73
|
+
const Seen = getRssSeen(runtime.db);
|
|
74
|
+
const Subs = getRssSubs(runtime.db);
|
|
82
75
|
if (!Seen || !Subs) {
|
|
83
76
|
return { text: 'RSS 数据库尚未就绪', totalNew: 0, pushed: 0 };
|
|
84
77
|
}
|
|
@@ -87,7 +80,7 @@ export async function checkSubscriptions(options) {
|
|
|
87
80
|
let pushed = 0;
|
|
88
81
|
for (const url of options.urls) {
|
|
89
82
|
try {
|
|
90
|
-
const { title, items } = await fetchFeed(url,
|
|
83
|
+
const { title, items } = await fetchFeed(url, runtime.config.timeout);
|
|
91
84
|
const seenRows = await Seen.select().where({ feed_url: url });
|
|
92
85
|
const seenGuids = new Set(seenRows.map((r) => String(r.item_guid ?? '')));
|
|
93
86
|
const newItems = items.filter((item) => item.guid && !seenGuids.has(item.guid));
|
|
@@ -95,11 +88,11 @@ export async function checkSubscriptions(options) {
|
|
|
95
88
|
parts.push(`${title || url}: 无新内容`);
|
|
96
89
|
continue;
|
|
97
90
|
}
|
|
98
|
-
await markItemsSeen(url, newItems);
|
|
91
|
+
await markItemsSeen(runtime, url, newItems);
|
|
99
92
|
totalNew += newItems.length;
|
|
100
|
-
const body = formatNewItems(title || url, newItems,
|
|
93
|
+
const body = formatNewItems(title || url, newItems, runtime.config.maxItems);
|
|
101
94
|
parts.push(body);
|
|
102
|
-
pushed += await pushToSubscribers(url, body);
|
|
95
|
+
pushed += await pushToSubscribers(runtime, url, body);
|
|
103
96
|
}
|
|
104
97
|
catch (e) {
|
|
105
98
|
parts.push(`${url}: 检查失败 — ${e.message}`);
|
|
@@ -107,7 +100,7 @@ export async function checkSubscriptions(options) {
|
|
|
107
100
|
}
|
|
108
101
|
if (parts.length === 0)
|
|
109
102
|
return { text: '没有可检查的订阅', totalNew: 0, pushed: 0 };
|
|
110
|
-
const pushNote =
|
|
103
|
+
const pushNote = runtime.outbound
|
|
111
104
|
? (pushed > 0 ? `,已推送 ${pushed} 个会话` : ',无出站推送')
|
|
112
105
|
: '(未装配 OutboundHost,仅本地标记)';
|
|
113
106
|
const header = totalNew > 0
|
|
@@ -118,8 +111,8 @@ export async function checkSubscriptions(options) {
|
|
|
118
111
|
/**
|
|
119
112
|
* Periodically clean seen records older than 7 days.
|
|
120
113
|
*/
|
|
121
|
-
export async function cleanOldSeen() {
|
|
122
|
-
const Seen = getRssSeen();
|
|
114
|
+
export async function cleanOldSeen(runtime) {
|
|
115
|
+
const Seen = getRssSeen(runtime.db);
|
|
123
116
|
if (!Seen)
|
|
124
117
|
return;
|
|
125
118
|
try {
|
|
@@ -141,14 +134,14 @@ export async function cleanOldSeen() {
|
|
|
141
134
|
/**
|
|
142
135
|
* Poll all unique feed URLs across subscriptions.
|
|
143
136
|
*/
|
|
144
|
-
export async function pollAllFeeds(
|
|
145
|
-
const Subs = getRssSubs();
|
|
137
|
+
export async function pollAllFeeds(runtime) {
|
|
138
|
+
const Subs = getRssSubs(runtime.db);
|
|
146
139
|
if (!Subs)
|
|
147
140
|
return { text: 'RSS 数据库尚未就绪', totalNew: 0, pushed: 0 };
|
|
148
141
|
const all = await Subs.select();
|
|
149
142
|
const urls = [...new Set(all.map((s) => String(s.url ?? '')).filter(Boolean))];
|
|
150
143
|
if (urls.length === 0)
|
|
151
144
|
return { text: '暂无任何 RSS 订阅', totalNew: 0, pushed: 0 };
|
|
152
|
-
return checkSubscriptions({ urls
|
|
145
|
+
return checkSubscriptions(runtime, { urls });
|
|
153
146
|
}
|
|
154
147
|
//# sourceMappingURL=poll.js.map
|
package/lib/poll.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poll.js","sourceRoot":"","sources":["../src/poll.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"poll.js","sourceRoot":"","sources":["../src/poll.ts"],"names":[],"mappings":"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"}
|
package/lib/runtime.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RssConfig } from './feed.js';
|
|
2
|
+
import type { RssMemoryDb } from './memory-store.js';
|
|
3
|
+
export type RssOutboundPush = (input: {
|
|
4
|
+
readonly adapterName: string;
|
|
5
|
+
readonly endpointKey: string;
|
|
6
|
+
readonly channelType: string;
|
|
7
|
+
readonly channelId: string;
|
|
8
|
+
readonly content: string;
|
|
9
|
+
}) => Promise<void>;
|
|
10
|
+
export interface RssRuntime {
|
|
11
|
+
readonly db: RssMemoryDb;
|
|
12
|
+
readonly config: Readonly<RssConfig>;
|
|
13
|
+
readonly outbound: RssOutboundPush | null;
|
|
14
|
+
}
|
|
15
|
+
export declare const rssRuntimeToken: import("zhin.js").Token<RssRuntime>;
|
|
16
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEpB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;CAC3C;AAED,eAAO,MAAM,eAAe,qCAG3B,CAAC"}
|
package/lib/runtime.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAkBtC,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CACxC,kBAAkB,EAClB,0BAA0B,CAC3B,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.2",
|
|
4
4
|
"description": "RSS/Atom feed subscription plugin for Zhin.js (Plugin Runtime)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"commands",
|
|
20
20
|
"src",
|
|
21
21
|
"lib",
|
|
22
|
-
"
|
|
22
|
+
"tools",
|
|
23
|
+
"skills",
|
|
23
24
|
"README.md"
|
|
24
25
|
],
|
|
25
26
|
"keywords": [
|
|
@@ -40,25 +41,22 @@
|
|
|
40
41
|
},
|
|
41
42
|
"license": "MIT",
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"rss-parser": "^3.13.0"
|
|
44
|
+
"rss-parser": "^3.13.0",
|
|
45
|
+
"@zhin.js/tool": "1.1.2",
|
|
46
|
+
"@zhin.js/skill": "1.1.2"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
49
|
"typescript": "^6.0.3",
|
|
47
50
|
"vitest": "^4.1.10",
|
|
48
51
|
"zod": "^4.4.3",
|
|
49
|
-
"
|
|
50
|
-
"zhin.js": "1.1.0"
|
|
52
|
+
"zhin.js": "1.1.3"
|
|
51
53
|
},
|
|
52
54
|
"peerDependencies": {
|
|
53
55
|
"zod": "^4.0.0",
|
|
54
|
-
"@zhin.js/
|
|
55
|
-
"
|
|
56
|
-
"zhin.js": "^1.1.0"
|
|
56
|
+
"@zhin.js/command": "^1.1.2",
|
|
57
|
+
"zhin.js": "^1.1.3"
|
|
57
58
|
},
|
|
58
59
|
"peerDependenciesMeta": {
|
|
59
|
-
"@zhin.js/agent": {
|
|
60
|
-
"optional": true
|
|
61
|
-
},
|
|
62
60
|
"@zhin.js/command": {
|
|
63
61
|
"optional": true
|
|
64
62
|
},
|
|
@@ -88,6 +86,14 @@
|
|
|
88
86
|
{
|
|
89
87
|
"package": "@zhin.js/command",
|
|
90
88
|
"api": "^1.0.0"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"package": "@zhin.js/tool",
|
|
92
|
+
"api": "^1.0.0"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"package": "@zhin.js/skill",
|
|
96
|
+
"api": "^1.0.0"
|
|
91
97
|
}
|
|
92
98
|
],
|
|
93
99
|
"plugins": []
|
package/plugin.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { definePlugin, databaseHostToken, outboundHostToken, scheduleHostToken, } from 'zhin.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { resolveRssConfig } from "./lib/feed.js";
|
|
4
|
+
import { createInMemoryRssDb, RSS_SEEN_TABLE, RSS_SUBS_TABLE, } from "./lib/db-store.js";
|
|
5
|
+
import { cleanOldSeen, pollAllFeeds } from "./lib/poll.js";
|
|
6
|
+
import { rssRuntimeToken } from "./lib/runtime.js";
|
|
7
7
|
function defineRssTables(db) {
|
|
8
8
|
db.define(RSS_SUBS_TABLE, {
|
|
9
9
|
url: { type: 'text', nullable: false },
|
|
@@ -37,18 +37,15 @@ export default definePlugin({
|
|
|
37
37
|
},
|
|
38
38
|
setup(context) {
|
|
39
39
|
const config = resolveRssConfig(context.config.get());
|
|
40
|
-
|
|
40
|
+
const db = context.resources.has(databaseHostToken) ? (() => {
|
|
41
41
|
const host = context.resources.use(databaseHostToken);
|
|
42
42
|
defineRssTables(host);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
else {
|
|
47
|
-
ensureRssMemoryDb();
|
|
48
|
-
}
|
|
43
|
+
return host;
|
|
44
|
+
})() : createInMemoryRssDb();
|
|
45
|
+
let outboundPush = null;
|
|
49
46
|
if (context.resources.has(outboundHostToken)) {
|
|
50
47
|
const outbound = context.resources.use(outboundHostToken);
|
|
51
|
-
|
|
48
|
+
outboundPush = async (input) => {
|
|
52
49
|
await outbound.send({
|
|
53
50
|
adapter: input.adapterName,
|
|
54
51
|
endpointKey: input.endpointKey,
|
|
@@ -58,16 +55,14 @@ export default definePlugin({
|
|
|
58
55
|
},
|
|
59
56
|
content: input.content,
|
|
60
57
|
});
|
|
61
|
-
}
|
|
62
|
-
context.lifecycle.add(() => setRssOutboundPush(null));
|
|
58
|
+
};
|
|
63
59
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
items: f.items.map((i) => ({ title: i.title, link: i.link })),
|
|
69
|
-
})),
|
|
60
|
+
const runtime = Object.freeze({
|
|
61
|
+
db,
|
|
62
|
+
config: Object.freeze({ ...config }),
|
|
63
|
+
outbound: outboundPush,
|
|
70
64
|
});
|
|
65
|
+
context.resources.provide(rssRuntimeToken, runtime);
|
|
71
66
|
if (!context.resources.has(scheduleHostToken))
|
|
72
67
|
return;
|
|
73
68
|
const schedule = context.resources.use(scheduleHostToken);
|
|
@@ -76,7 +71,7 @@ export default definePlugin({
|
|
|
76
71
|
cron: config.pollCron,
|
|
77
72
|
description: 'Poll RSS subscriptions',
|
|
78
73
|
async execute() {
|
|
79
|
-
await pollAllFeeds(
|
|
74
|
+
await pollAllFeeds(runtime);
|
|
80
75
|
},
|
|
81
76
|
});
|
|
82
77
|
const disposeClean = schedule.register({
|
|
@@ -84,7 +79,7 @@ export default definePlugin({
|
|
|
84
79
|
cron: '0 0 4 * * *',
|
|
85
80
|
description: 'Clean seen records older than 7 days',
|
|
86
81
|
async execute() {
|
|
87
|
-
await cleanOldSeen();
|
|
82
|
+
await cleanOldSeen(runtime);
|
|
88
83
|
},
|
|
89
84
|
});
|
|
90
85
|
context.lifecycle.add(dispose);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { getRssSubs } from "../../../../lib/db-store.js";
|
|
5
|
+
import { rssRuntimeToken } from "../../../../lib/runtime.js";
|
|
6
|
+
export default defineAgentTool({
|
|
7
|
+
description: '查询当前所有 RSS 订阅',
|
|
8
|
+
inputSchema: z.object({}),
|
|
9
|
+
async execute(_input, context) {
|
|
10
|
+
const Subs = getRssSubs(context.use(rssRuntimeToken).db);
|
|
11
|
+
if (!Subs)
|
|
12
|
+
return 'RSS 数据库尚未就绪';
|
|
13
|
+
const all = (await Subs.select());
|
|
14
|
+
if (all.length === 0)
|
|
15
|
+
return '暂无任何 RSS 订阅';
|
|
16
|
+
const grouped = new Map();
|
|
17
|
+
for (const sub of all) {
|
|
18
|
+
const key = `${sub.channel_type}:${sub.channel_id}`;
|
|
19
|
+
const list = grouped.get(key) || [];
|
|
20
|
+
list.push(sub);
|
|
21
|
+
grouped.set(key, list);
|
|
22
|
+
}
|
|
23
|
+
const lines = [];
|
|
24
|
+
for (const [channel, subs] of grouped) {
|
|
25
|
+
lines.push(`[${channel}]`);
|
|
26
|
+
for (const s of subs) {
|
|
27
|
+
lines.push(` - ${s.feed_title || s.url}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return `RSS 订阅总览 (${all.length} 条):\n${lines.join('\n')}`;
|
|
31
|
+
},
|
|
32
|
+
});
|
package/{agent/tools/rss_list_subscriptions.ts → skills/rss/tools/rss_list_subscriptions/index.ts}
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { defineAgentTool } from '@zhin.js/
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
3
|
+
import { getRssSubs } from '../../../../src/db-store.js';
|
|
4
|
+
import { rssRuntimeToken } from '../../../../src/runtime.js';
|
|
4
5
|
|
|
5
6
|
export default defineAgentTool({
|
|
6
7
|
description: '查询当前所有 RSS 订阅',
|
|
7
8
|
inputSchema: z.object({}),
|
|
8
|
-
async execute() {
|
|
9
|
-
const
|
|
10
|
-
const Subs = getSubs();
|
|
9
|
+
async execute(_input, context) {
|
|
10
|
+
const Subs = getRssSubs(context.use(rssRuntimeToken).db);
|
|
11
11
|
if (!Subs) return 'RSS 数据库尚未就绪';
|
|
12
12
|
|
|
13
13
|
const all = (await Subs.select()) as Array<{
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { fetchFeed } from "../../../../lib/feed.js";
|
|
5
|
+
import { rssRuntimeToken } from "../../../../lib/runtime.js";
|
|
6
|
+
export default defineAgentTool({
|
|
7
|
+
description: '预览一个 RSS 源的最新内容',
|
|
8
|
+
inputSchema: z.object({ url: z.string().min(1) }),
|
|
9
|
+
async execute({ url }, context) {
|
|
10
|
+
if (!url)
|
|
11
|
+
return '请提供 RSS 源 URL';
|
|
12
|
+
try {
|
|
13
|
+
const runtime = context.use(rssRuntimeToken);
|
|
14
|
+
const { title, items } = await fetchFeed(url, runtime.config.timeout);
|
|
15
|
+
if (items.length === 0)
|
|
16
|
+
return `${title}: 无内容`;
|
|
17
|
+
return items
|
|
18
|
+
.slice(0, 5)
|
|
19
|
+
.map((item, i) => `${i + 1}. ${item.title}\n ${item.link}`)
|
|
20
|
+
.join('\n');
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
return `解析失败: ${e.message}`;
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { defineAgentTool } from '@zhin.js/
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
3
|
+
import { fetchFeed } from '../../../../src/feed.js';
|
|
4
|
+
import { rssRuntimeToken } from '../../../../src/runtime.js';
|
|
4
5
|
|
|
5
6
|
export default defineAgentTool<{ url: string }>({
|
|
6
7
|
description: '预览一个 RSS 源的最新内容',
|
|
7
8
|
inputSchema: z.object({ url: z.string().min(1) }),
|
|
8
|
-
async execute({ url }) {
|
|
9
|
+
async execute({ url }, context) {
|
|
9
10
|
if (!url) return '请提供 RSS 源 URL';
|
|
10
11
|
try {
|
|
11
|
-
const
|
|
12
|
-
const { title, items } = await fetchFeed(url);
|
|
12
|
+
const runtime = context.use(rssRuntimeToken);
|
|
13
|
+
const { title, items } = await fetchFeed(url, runtime.config.timeout);
|
|
13
14
|
if (items.length === 0) return `${title}: 无内容`;
|
|
14
15
|
return items
|
|
15
16
|
.slice(0, 5)
|
package/src/db-store.ts
CHANGED
|
@@ -6,54 +6,17 @@ export {
|
|
|
6
6
|
type RssModel,
|
|
7
7
|
} from './memory-store.js';
|
|
8
8
|
|
|
9
|
-
import { createGenerationStore, type Dispose, type GenerationStoreContext } from 'zhin.js';
|
|
10
9
|
import {
|
|
11
|
-
createInMemoryRssDb,
|
|
12
10
|
RSS_SEEN_TABLE,
|
|
13
11
|
RSS_SUBS_TABLE,
|
|
14
12
|
type RssMemoryDb,
|
|
15
13
|
type RssModel,
|
|
16
14
|
} from './memory-store.js';
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let _db: RssMemoryDb | null = null;
|
|
21
|
-
let _memoryDb: RssMemoryDb | null = null;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Generation-owned database binding used by Plugin Runtime setup().
|
|
25
|
-
* Auto-unregisters when the generation lifecycle disposes, so no stale
|
|
26
|
-
* reference to the previous generation's host survives a reload.
|
|
27
|
-
*/
|
|
28
|
-
export function provideRssDb(context: GenerationStoreContext, db: RssMemoryDb): Dispose {
|
|
29
|
-
return rssDbStore.provide(context, db);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function ensureRssMemoryDb(): RssMemoryDb {
|
|
33
|
-
const provided = rssDbStore.tryUse() ?? _db;
|
|
34
|
-
if (provided) return provided;
|
|
35
|
-
if (!_memoryDb) _memoryDb = createInMemoryRssDb();
|
|
36
|
-
return _memoryDb;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function getRssDb(): RssMemoryDb | null {
|
|
40
|
-
return ensureRssMemoryDb();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function setRssDb(db: RssMemoryDb | null): void {
|
|
44
|
-
_db = db;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function resetRssDb(): void {
|
|
48
|
-
rssDbStore.clear();
|
|
49
|
-
_db = null;
|
|
50
|
-
_memoryDb = null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function getRssSubs(): RssModel | null {
|
|
54
|
-
return ensureRssMemoryDb().models.get(RSS_SUBS_TABLE) ?? null;
|
|
16
|
+
export function getRssSubs(db: RssMemoryDb): RssModel | null {
|
|
17
|
+
return db.models.get(RSS_SUBS_TABLE) ?? null;
|
|
55
18
|
}
|
|
56
19
|
|
|
57
|
-
export function getRssSeen(): RssModel | null {
|
|
58
|
-
return
|
|
20
|
+
export function getRssSeen(db: RssMemoryDb): RssModel | null {
|
|
21
|
+
return db.models.get(RSS_SEEN_TABLE) ?? null;
|
|
59
22
|
}
|
package/src/index.ts
CHANGED
|
@@ -6,15 +6,8 @@ export {
|
|
|
6
6
|
stripHtml,
|
|
7
7
|
} from './feed.js';
|
|
8
8
|
export type { FeedItem, RssConfig } from './feed.js';
|
|
9
|
-
export {
|
|
10
|
-
|
|
11
|
-
getRssSeen,
|
|
12
|
-
getRssSubs,
|
|
13
|
-
provideRssDb,
|
|
14
|
-
setRssDb,
|
|
15
|
-
ensureRssMemoryDb,
|
|
16
|
-
resetRssDb,
|
|
17
|
-
} from './db-store.js';
|
|
9
|
+
export { getRssSeen, getRssSubs } from './db-store.js';
|
|
10
|
+
export { rssRuntimeToken, type RssRuntime, type RssOutboundPush } from './runtime.js';
|
|
18
11
|
export { extractChannelInfo, SMOKE_CHANNEL } from './channel.js';
|
|
19
|
-
export { checkSubscriptions, pollAllFeeds, formatNewItems
|
|
20
|
-
export type {
|
|
12
|
+
export { checkSubscriptions, pollAllFeeds, formatNewItems } from './poll.js';
|
|
13
|
+
export type { CheckResult } from './poll.js';
|
package/src/memory-store.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* In-memory RSS models
|
|
2
|
+
* In-memory RSS models used when no DatabaseHost is available.
|
|
3
3
|
*
|
|
4
4
|
* 模型表面与 `@zhin.js/plugin-runtime` 的 DatabaseHostModel 结构兼容:
|
|
5
5
|
* select 的 where 返回 PromiseLike(host 返回链式 Selection,memory 返回 Promise),
|
package/src/poll.ts
CHANGED
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Feed poll helpers: detect new items; optional OutboundHost push to subscribers.
|
|
3
3
|
*/
|
|
4
|
-
import { fetchFeed, type FeedItem
|
|
4
|
+
import { fetchFeed, type FeedItem } from './feed.js';
|
|
5
5
|
import { getRssSeen, getRssSubs } from './db-store.js';
|
|
6
|
-
|
|
7
|
-
export type RssOutboundPush = (input: {
|
|
8
|
-
readonly adapterName: string;
|
|
9
|
-
readonly endpointKey: string;
|
|
10
|
-
readonly channelType: string;
|
|
11
|
-
readonly channelId: string;
|
|
12
|
-
readonly content: string;
|
|
13
|
-
}) => Promise<void>;
|
|
14
|
-
|
|
15
|
-
let _outboundPush: RssOutboundPush | null = null;
|
|
16
|
-
|
|
17
|
-
export function setRssOutboundPush(push: RssOutboundPush | null): void {
|
|
18
|
-
_outboundPush = push;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function getRssOutboundPush(): RssOutboundPush | null {
|
|
22
|
-
return _outboundPush;
|
|
23
|
-
}
|
|
6
|
+
import type { RssRuntime } from './runtime.js';
|
|
24
7
|
|
|
25
8
|
function ts(): string {
|
|
26
9
|
return new Date().toISOString();
|
|
@@ -40,8 +23,8 @@ export function formatNewItems(feedTitle: string, items: FeedItem[], maxItems: n
|
|
|
40
23
|
return lines.join('\n');
|
|
41
24
|
}
|
|
42
25
|
|
|
43
|
-
export async function markItemsSeen(feedUrl: string, items: FeedItem[]): Promise<void> {
|
|
44
|
-
const Seen = getRssSeen();
|
|
26
|
+
export async function markItemsSeen(runtime: RssRuntime, feedUrl: string, items: FeedItem[]): Promise<void> {
|
|
27
|
+
const Seen = getRssSeen(runtime.db);
|
|
45
28
|
if (!Seen) return;
|
|
46
29
|
for (const item of items) {
|
|
47
30
|
const dup = await Seen.select().where({ feed_url: feedUrl, item_guid: item.guid });
|
|
@@ -62,9 +45,9 @@ export interface CheckResult {
|
|
|
62
45
|
readonly pushed: number;
|
|
63
46
|
}
|
|
64
47
|
|
|
65
|
-
async function pushToSubscribers(feedUrl: string, content: string): Promise<number> {
|
|
66
|
-
const push =
|
|
67
|
-
const Subs = getRssSubs();
|
|
48
|
+
async function pushToSubscribers(runtime: RssRuntime, feedUrl: string, content: string): Promise<number> {
|
|
49
|
+
const push = runtime.outbound;
|
|
50
|
+
const Subs = getRssSubs(runtime.db);
|
|
68
51
|
if (!push || !Subs) return 0;
|
|
69
52
|
const subscribers = await Subs.select().where({ url: feedUrl });
|
|
70
53
|
let pushed = 0;
|
|
@@ -92,12 +75,11 @@ async function pushToSubscribers(feedUrl: string, content: string): Promise<numb
|
|
|
92
75
|
* Check subscribed feeds for new items and mark them seen.
|
|
93
76
|
* When OutboundHost is wired, pushes formatted text to each subscriber channel.
|
|
94
77
|
*/
|
|
95
|
-
export async function checkSubscriptions(options: {
|
|
78
|
+
export async function checkSubscriptions(runtime: RssRuntime, options: {
|
|
96
79
|
urls: readonly string[];
|
|
97
|
-
config: RssConfig;
|
|
98
80
|
}): Promise<CheckResult> {
|
|
99
|
-
const Seen = getRssSeen();
|
|
100
|
-
const Subs = getRssSubs();
|
|
81
|
+
const Seen = getRssSeen(runtime.db);
|
|
82
|
+
const Subs = getRssSubs(runtime.db);
|
|
101
83
|
if (!Seen || !Subs) {
|
|
102
84
|
return { text: 'RSS 数据库尚未就绪', totalNew: 0, pushed: 0 };
|
|
103
85
|
}
|
|
@@ -108,7 +90,7 @@ export async function checkSubscriptions(options: {
|
|
|
108
90
|
|
|
109
91
|
for (const url of options.urls) {
|
|
110
92
|
try {
|
|
111
|
-
const { title, items } = await fetchFeed(url,
|
|
93
|
+
const { title, items } = await fetchFeed(url, runtime.config.timeout);
|
|
112
94
|
const seenRows = await Seen.select().where({ feed_url: url });
|
|
113
95
|
const seenGuids = new Set(seenRows.map((r) => String(r.item_guid ?? '')));
|
|
114
96
|
const newItems = items.filter((item) => item.guid && !seenGuids.has(item.guid));
|
|
@@ -116,18 +98,18 @@ export async function checkSubscriptions(options: {
|
|
|
116
98
|
parts.push(`${title || url}: 无新内容`);
|
|
117
99
|
continue;
|
|
118
100
|
}
|
|
119
|
-
await markItemsSeen(url, newItems);
|
|
101
|
+
await markItemsSeen(runtime, url, newItems);
|
|
120
102
|
totalNew += newItems.length;
|
|
121
|
-
const body = formatNewItems(title || url, newItems,
|
|
103
|
+
const body = formatNewItems(title || url, newItems, runtime.config.maxItems);
|
|
122
104
|
parts.push(body);
|
|
123
|
-
pushed += await pushToSubscribers(url, body);
|
|
105
|
+
pushed += await pushToSubscribers(runtime, url, body);
|
|
124
106
|
} catch (e) {
|
|
125
107
|
parts.push(`${url}: 检查失败 — ${(e as Error).message}`);
|
|
126
108
|
}
|
|
127
109
|
}
|
|
128
110
|
|
|
129
111
|
if (parts.length === 0) return { text: '没有可检查的订阅', totalNew: 0, pushed: 0 };
|
|
130
|
-
const pushNote =
|
|
112
|
+
const pushNote = runtime.outbound
|
|
131
113
|
? (pushed > 0 ? `,已推送 ${pushed} 个会话` : ',无出站推送')
|
|
132
114
|
: '(未装配 OutboundHost,仅本地标记)';
|
|
133
115
|
const header = totalNew > 0
|
|
@@ -139,8 +121,8 @@ export async function checkSubscriptions(options: {
|
|
|
139
121
|
/**
|
|
140
122
|
* Periodically clean seen records older than 7 days.
|
|
141
123
|
*/
|
|
142
|
-
export async function cleanOldSeen(): Promise<void> {
|
|
143
|
-
const Seen = getRssSeen();
|
|
124
|
+
export async function cleanOldSeen(runtime: RssRuntime): Promise<void> {
|
|
125
|
+
const Seen = getRssSeen(runtime.db);
|
|
144
126
|
if (!Seen) return;
|
|
145
127
|
try {
|
|
146
128
|
const cutoff = new Date();
|
|
@@ -161,11 +143,11 @@ export async function cleanOldSeen(): Promise<void> {
|
|
|
161
143
|
/**
|
|
162
144
|
* Poll all unique feed URLs across subscriptions.
|
|
163
145
|
*/
|
|
164
|
-
export async function pollAllFeeds(
|
|
165
|
-
const Subs = getRssSubs();
|
|
146
|
+
export async function pollAllFeeds(runtime: RssRuntime): Promise<CheckResult> {
|
|
147
|
+
const Subs = getRssSubs(runtime.db);
|
|
166
148
|
if (!Subs) return { text: 'RSS 数据库尚未就绪', totalNew: 0, pushed: 0 };
|
|
167
149
|
const all = await Subs.select();
|
|
168
150
|
const urls = [...new Set(all.map((s) => String(s.url ?? '')).filter(Boolean))];
|
|
169
151
|
if (urls.length === 0) return { text: '暂无任何 RSS 订阅', totalNew: 0, pushed: 0 };
|
|
170
|
-
return checkSubscriptions({ urls
|
|
152
|
+
return checkSubscriptions(runtime, { urls });
|
|
171
153
|
}
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createToken } from 'zhin.js';
|
|
2
|
+
import type { RssConfig } from './feed.js';
|
|
3
|
+
import type { RssMemoryDb } from './memory-store.js';
|
|
4
|
+
|
|
5
|
+
export type RssOutboundPush = (input: {
|
|
6
|
+
readonly adapterName: string;
|
|
7
|
+
readonly endpointKey: string;
|
|
8
|
+
readonly channelType: string;
|
|
9
|
+
readonly channelId: string;
|
|
10
|
+
readonly content: string;
|
|
11
|
+
}) => Promise<void>;
|
|
12
|
+
|
|
13
|
+
export interface RssRuntime {
|
|
14
|
+
readonly db: RssMemoryDb;
|
|
15
|
+
readonly config: Readonly<RssConfig>;
|
|
16
|
+
readonly outbound: RssOutboundPush | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const rssRuntimeToken = createToken<RssRuntime>(
|
|
20
|
+
'zhin.rss.runtime',
|
|
21
|
+
'Owner-scoped RSS runtime',
|
|
22
|
+
);
|
package/lib/rss-agent-deps.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface RssAgentDeps {
|
|
2
|
-
getSubs: () => {
|
|
3
|
-
select: () => Promise<unknown[]>;
|
|
4
|
-
} | null;
|
|
5
|
-
fetchFeed: (url: string) => Promise<{
|
|
6
|
-
title: string;
|
|
7
|
-
items: Array<{
|
|
8
|
-
title: string;
|
|
9
|
-
link: string;
|
|
10
|
-
}>;
|
|
11
|
-
}>;
|
|
12
|
-
}
|
|
13
|
-
export declare function setRssAgentDeps(next: RssAgentDeps): void;
|
|
14
|
-
export declare function getRssAgentDeps(): RssAgentDeps;
|
|
15
|
-
//# sourceMappingURL=rss-agent-deps.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rss-agent-deps.d.ts","sourceRoot":"","sources":["../src/rss-agent-deps.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM;QAAE,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3D,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CACvG;AAID,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAExD;AAED,wBAAgB,eAAe,IAAI,YAAY,CAG9C"}
|
package/lib/rss-agent-deps.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rss-agent-deps.js","sourceRoot":"","sources":["../src/rss-agent-deps.ts"],"names":[],"mappings":"AAKA,IAAI,IAAI,GAAwB,IAAI,CAAC;AAErC,MAAM,UAAU,eAAe,CAAC,IAAkB;IAChD,IAAI,GAAG,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAC7D,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/src/rss-agent-deps.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface RssAgentDeps {
|
|
2
|
-
getSubs: () => { select: () => Promise<unknown[]> } | null;
|
|
3
|
-
fetchFeed: (url: string) => Promise<{ title: string; items: Array<{ title: string; link: string }> }>;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
let deps: RssAgentDeps | null = null;
|
|
7
|
-
|
|
8
|
-
export function setRssAgentDeps(next: RssAgentDeps): void {
|
|
9
|
-
deps = next;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function getRssAgentDeps(): RssAgentDeps {
|
|
13
|
-
if (!deps) throw new Error('rss agent deps not initialized');
|
|
14
|
-
return deps;
|
|
15
|
-
}
|
|
File without changes
|