rssany 0.1.1 → 0.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/.env.example +1 -1
- package/README.md +5 -1
- package/app/plugins/builtin/rss.rssany.js +107 -24
- package/app/plugins/site.rssany.js +1 -1
- package/dist/index.js +986 -266
- package/dist/index.js.map +1 -1
- package/{config.examples.json → init/config.json} +7 -1
- package/init/sources.json +353 -0
- package/package.json +4 -6
- package/statics/401.html +1 -1
- package/statics/README.md +1 -1
- package/webui/build/200.html +16 -18
- package/webui/build/_app/immutable/assets/0.C6Q_nuW9.css +1 -0
- package/webui/build/_app/immutable/assets/10.Dj8_pmut.css +1 -0
- package/webui/build/_app/immutable/assets/11.qYZMiTb0.css +1 -0
- package/webui/build/_app/immutable/assets/12.Ct59LCqW.css +1 -0
- package/webui/build/_app/immutable/assets/13.BhO9zvFi.css +1 -0
- package/webui/build/_app/immutable/assets/14.CujIhjQK.css +1 -0
- package/webui/build/_app/immutable/assets/15.nNGjXhCQ.css +1 -0
- package/webui/build/_app/immutable/assets/16.PP9XLDf7.css +1 -0
- package/webui/build/_app/immutable/assets/4.9wPHhVwv.css +1 -0
- package/webui/build/_app/immutable/assets/6.DSJfjJwx.css +1 -0
- package/webui/build/_app/immutable/assets/7.CrNxmd8B.css +1 -0
- package/webui/build/_app/immutable/assets/8.Ba5_jYIY.css +1 -0
- package/webui/build/_app/immutable/assets/{9.BZheTlzZ.css → 9.m-LCx_kl.css} +1 -1
- package/webui/build/_app/immutable/assets/BackToParentRoute.DGk-X5ow.css +1 -0
- package/webui/build/_app/immutable/assets/SourcesList.yTBBi3_m.css +1 -0
- package/webui/build/_app/immutable/assets/homeFeedPanelStore.BopJZtHu.css +1 -0
- package/webui/build/_app/immutable/chunks/{V2-VOe88.js → B-OsL1Ct.js} +1 -1
- package/webui/build/_app/immutable/chunks/B2Q1a1-H.js +2 -0
- package/webui/build/_app/immutable/chunks/BK3WtZwv.js +1 -0
- package/webui/build/_app/immutable/chunks/BQqoDzLx.js +1 -0
- package/webui/build/_app/immutable/chunks/BXCWEhUd.js +1 -0
- package/webui/build/_app/immutable/chunks/BbWUOQ_m.js +1 -0
- package/webui/build/_app/immutable/chunks/Bp63qm3L.js +1 -0
- package/webui/build/_app/immutable/chunks/CVzlFH44.js +1 -0
- package/webui/build/_app/immutable/chunks/CWNeClHp.js +6 -0
- package/webui/build/_app/immutable/chunks/Cihqbfi5.js +1 -0
- package/webui/build/_app/immutable/chunks/CkUAV0m0.js +41 -0
- package/webui/build/_app/immutable/chunks/CtijX1u3.js +31 -0
- package/webui/build/_app/immutable/chunks/D5GvRCv7.js +1 -0
- package/webui/build/_app/immutable/chunks/DEDI7Ecm.js +1 -0
- package/webui/build/_app/immutable/chunks/DFuhmi31.js +1 -0
- package/webui/build/_app/immutable/chunks/DMWEh-Ek.js +2 -0
- package/webui/build/_app/immutable/chunks/{CZDFXKiF.js → DcAshVxe.js} +1 -1
- package/webui/build/_app/immutable/chunks/DjNLq3TF.js +1 -0
- package/webui/build/_app/immutable/chunks/Dt2CddFe.js +1 -0
- package/webui/build/_app/immutable/chunks/Dw782Tjs.js +1 -0
- package/webui/build/_app/immutable/chunks/EIZIMsXK.js +1 -0
- package/webui/build/_app/immutable/chunks/Xy_fhzQq.js +1 -0
- package/webui/build/_app/immutable/chunks/lk5LaiqA.js +1 -0
- package/webui/build/_app/immutable/chunks/mW5RwvnK.js +13 -0
- package/webui/build/_app/immutable/chunks/{CtHRh_pJ.js → tB7QMF3U.js} +1 -1
- package/webui/build/_app/immutable/chunks/xtNWTdbD.js +1 -0
- package/webui/build/_app/immutable/entry/app.DdgnooOk.js +2 -0
- package/webui/build/_app/immutable/entry/start.DhJaJZhR.js +1 -0
- package/webui/build/_app/immutable/nodes/0.BE05Cuc4.js +11 -0
- package/webui/build/_app/immutable/nodes/1.5DFDaT4c.js +1 -0
- package/webui/build/_app/immutable/nodes/10.OVK4i9XE.js +1 -0
- package/webui/build/_app/immutable/nodes/11.Dhn_rO4A.js +1 -0
- package/webui/build/_app/immutable/nodes/12.Cg8AeCSH.js +1 -0
- package/webui/build/_app/immutable/nodes/13.nT3SOzEB.js +1 -0
- package/webui/build/_app/immutable/nodes/14.B_KpJLxn.js +1 -0
- package/webui/build/_app/immutable/nodes/15.RaWaA-0I.js +1 -0
- package/webui/build/_app/immutable/nodes/{12.CMcby_lY.js → 16.DSUgqolV.js} +15 -15
- package/webui/build/_app/immutable/nodes/2.BYWOpaxy.js +1 -0
- package/webui/build/_app/immutable/nodes/3.wQvGs9w-.js +1 -0
- package/webui/build/_app/immutable/nodes/4.DTSxpKm7.js +2 -0
- package/webui/build/_app/immutable/nodes/5.CCtn90c0.js +1 -0
- package/webui/build/_app/immutable/nodes/6.C2_mjW1u.js +1 -0
- package/webui/build/_app/immutable/nodes/7.Dwz6W7A1.js +1 -0
- package/webui/build/_app/immutable/nodes/8.DzkEw6rx.js +1 -0
- package/webui/build/_app/immutable/nodes/9.DtlXEwe1.js +1 -0
- package/webui/build/_app/version.json +1 -1
- package/sources.example.json +0 -562
- package/webui/build/_app/immutable/assets/0.BUAXpTm6.css +0 -1
- package/webui/build/_app/immutable/assets/10.I1OuCLrU.css +0 -1
- package/webui/build/_app/immutable/assets/11.CrO9xaki.css +0 -1
- package/webui/build/_app/immutable/assets/12.BEi6fInA.css +0 -1
- package/webui/build/_app/immutable/assets/14.Ctlgn1LZ.css +0 -1
- package/webui/build/_app/immutable/assets/2.eJ80XOGm.css +0 -1
- package/webui/build/_app/immutable/assets/4.B8-jYAVj.css +0 -1
- package/webui/build/_app/immutable/assets/6.Drn-0DON.css +0 -1
- package/webui/build/_app/immutable/assets/7.ms2diq_q.css +0 -1
- package/webui/build/_app/immutable/assets/8.DKymkjjs.css +0 -1
- package/webui/build/_app/immutable/assets/SourcesList.BhtYlRsQ.css +0 -1
- package/webui/build/_app/immutable/chunks/BUngiKFg.js +0 -1
- package/webui/build/_app/immutable/chunks/Bt0fzibd.js +0 -1
- package/webui/build/_app/immutable/chunks/BxHqDcpw.js +0 -1
- package/webui/build/_app/immutable/chunks/ByQRbEUX.js +0 -1
- package/webui/build/_app/immutable/chunks/C12mHcUp.js +0 -6
- package/webui/build/_app/immutable/chunks/C1kQ4pHy.js +0 -1
- package/webui/build/_app/immutable/chunks/C74gbb4Q.js +0 -1
- package/webui/build/_app/immutable/chunks/CAtemnMo.js +0 -1
- package/webui/build/_app/immutable/chunks/CVjCNJia.js +0 -1
- package/webui/build/_app/immutable/chunks/CjQQ9_Q2.js +0 -2
- package/webui/build/_app/immutable/chunks/D-6mYMI1.js +0 -1
- package/webui/build/_app/immutable/chunks/D1Gs8-g3.js +0 -1
- package/webui/build/_app/immutable/chunks/D9dRVKgL.js +0 -1
- package/webui/build/_app/immutable/chunks/DCEY1XiC.js +0 -1
- package/webui/build/_app/immutable/chunks/DI-t-G_K.js +0 -2
- package/webui/build/_app/immutable/chunks/DTUxjyWL.js +0 -1
- package/webui/build/_app/immutable/chunks/DWJZOHke.js +0 -1
- package/webui/build/_app/immutable/chunks/Dgs6d7X5.js +0 -1
- package/webui/build/_app/immutable/chunks/DjpPK99f.js +0 -71
- package/webui/build/_app/immutable/chunks/DjzVVxpy.js +0 -1
- package/webui/build/_app/immutable/chunks/DvtNA-3X.js +0 -1
- package/webui/build/_app/immutable/chunks/LQVMBmDN.js +0 -1
- package/webui/build/_app/immutable/chunks/Qw0Qgx6J.js +0 -1
- package/webui/build/_app/immutable/chunks/bohabpgg.js +0 -1
- package/webui/build/_app/immutable/chunks/c-YfbAB_.js +0 -8
- package/webui/build/_app/immutable/chunks/tpTQfoNn.js +0 -1
- package/webui/build/_app/immutable/entry/app.Cra5Zsz4.js +0 -2
- package/webui/build/_app/immutable/entry/start.ToY0Qh0_.js +0 -1
- package/webui/build/_app/immutable/nodes/0.D2-xzG_8.js +0 -11
- package/webui/build/_app/immutable/nodes/1.CFixzRR6.js +0 -1
- package/webui/build/_app/immutable/nodes/10.ayxWydPr.js +0 -1
- package/webui/build/_app/immutable/nodes/11.B0JS3E2j.js +0 -1
- package/webui/build/_app/immutable/nodes/13.DRpZV72T.js +0 -1
- package/webui/build/_app/immutable/nodes/14.DVeJW6bd.js +0 -1
- package/webui/build/_app/immutable/nodes/2.DIZ4IPNm.js +0 -1
- package/webui/build/_app/immutable/nodes/3.BFSNf0FK.js +0 -1
- package/webui/build/_app/immutable/nodes/4.BSsIjejE.js +0 -2
- package/webui/build/_app/immutable/nodes/5.COxRT9Oe.js +0 -1
- package/webui/build/_app/immutable/nodes/6.CBgQ4YzB.js +0 -1
- package/webui/build/_app/immutable/nodes/7.BbzWOL0V.js +0 -6
- package/webui/build/_app/immutable/nodes/8.C8120200.js +0 -1
- package/webui/build/_app/immutable/nodes/9.BH_BGQQ4.js +0 -1
- /package/webui/build/_app/immutable/nodes/{15.BtYZF6FM.js → 17.BtYZF6FM.js} +0 -0
- /package/webui/build/_app/immutable/nodes/{16.Ba_qJjp6.js → 18.Ba_qJjp6.js} +0 -0
package/.env.example
CHANGED
|
@@ -33,7 +33,7 @@ SMTP_PASS=your-smtp-password
|
|
|
33
33
|
# Resend API Key(driver=resend 时使用;https://resend.com/api-keys)
|
|
34
34
|
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx
|
|
35
35
|
|
|
36
|
-
# ─── LLM
|
|
36
|
+
# ─── LLM(解析 / Pipeline 等;也可在管理后台「设置 → LLM」写入 .rssany/config.json)──
|
|
37
37
|
OPENAI_API_KEY=sk-...
|
|
38
38
|
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
39
39
|
OPENAI_MODEL=gpt-4o-mini
|
package/README.md
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
**RSSAny** 是一套自托管的订阅管线:列表 URL → **抓取与解析**(规则 / LLM)→ **正文提取**(自定义 / Readability / LLM)→ **upsert 去重** → 固定 **pipeline**(打标签、翻译等)→ 对外提供 `**/rss`** 等输出。
|
|
7
7
|
|
|
8
|
+
## 界面预览
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
8
12
|
---
|
|
9
13
|
|
|
10
14
|
## 功能概览
|
|
@@ -56,7 +60,7 @@ pnpm run webui:install
|
|
|
56
60
|
```bash
|
|
57
61
|
cp .env.example .env
|
|
58
62
|
```
|
|
59
|
-
2. 信源与全局配置:首次启动会在 **`~/.rssany/`**(Windows:`%USERPROFILE%\.rssany
|
|
63
|
+
2. 信源与全局配置:首次启动会在 **`~/.rssany/`**(Windows:`%USERPROFILE%\.rssany\`)下自动从包内 **`init/`** 目录中的默认数据复制生成 `sources.json`、`config.json`(若已存在则不会覆盖)。也可手动复制仓库里的 `init/sources.json`、`init/config.json`。
|
|
60
64
|
3. (可选)LLM:在 `.env` 中设置 `OPENAI_API_KEY`、`OPENAI_BASE_URL`、`OPENAI_MODEL` 等。
|
|
61
65
|
|
|
62
66
|
### 运行
|
|
@@ -1,30 +1,95 @@
|
|
|
1
|
-
// 内置 RSS/Atom/JSON Feed
|
|
1
|
+
// 内置 RSS/Atom/JSON Feed:通过浏览器(Puppeteer)拉取 Feed URL,再用 rss-parser 解析;
|
|
2
|
+
// 与站点插件一致走 Chrome,便于应对需浏览器环境或代理的场景;XML 使用 HTTP 响应原文(useHttpResponseBody)。
|
|
2
3
|
|
|
3
4
|
const UA = "RssAny/1.0 (+https://github.com/joohw/rssany)";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
const IMAGE_TYPE_RE = /^image\//i;
|
|
7
|
+
const IMAGE_EXT_IN_PATH_RE = /\.(jpg|jpeg|png|gif|webp|avif|svg)(\?|#|$)/i;
|
|
8
|
+
|
|
9
|
+
function trimUrl(s) {
|
|
10
|
+
if (typeof s !== "string") return undefined;
|
|
11
|
+
const t = s.trim();
|
|
12
|
+
return t || undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 从 rss-parser 条目上尽量取出配图 URL(入库用 imageUrl,与 Gateway 的 cover_img 对齐)。 */
|
|
16
|
+
function extractItemImageUrl(item) {
|
|
17
|
+
const enc = item.enclosure;
|
|
18
|
+
if (enc && typeof enc.url === "string") {
|
|
19
|
+
const u = trimUrl(enc.url);
|
|
20
|
+
const t = typeof enc.type === "string" ? enc.type : "";
|
|
21
|
+
if (u && (IMAGE_TYPE_RE.test(t) || (!t && IMAGE_EXT_IN_PATH_RE.test(u)))) {
|
|
22
|
+
return u;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const itunesImg = item.itunes && typeof item.itunes.image === "string" ? item.itunes.image : undefined;
|
|
27
|
+
const fromItunes = trimUrl(itunesImg);
|
|
28
|
+
if (fromItunes) return fromItunes;
|
|
29
|
+
|
|
30
|
+
const thumbs = item.mediaThumbnail;
|
|
31
|
+
if (Array.isArray(thumbs) && thumbs[0]?.$) {
|
|
32
|
+
const u = trimUrl(thumbs[0].$.url ?? thumbs[0].$.href);
|
|
33
|
+
if (u) return u;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const mediaBlocks = item.mediaContent;
|
|
37
|
+
if (Array.isArray(mediaBlocks)) {
|
|
38
|
+
for (const block of mediaBlocks) {
|
|
39
|
+
const $ = block && block.$;
|
|
40
|
+
if (!$ || typeof $.url !== "string") continue;
|
|
41
|
+
const medium = $.medium;
|
|
42
|
+
const ctype = typeof $.type === "string" ? $.type : "";
|
|
43
|
+
if (medium === "image" || IMAGE_TYPE_RE.test(ctype)) {
|
|
44
|
+
const u = trimUrl($.url);
|
|
45
|
+
if (u) return u;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const atomLinks = item.atomLinks;
|
|
51
|
+
if (Array.isArray(atomLinks)) {
|
|
52
|
+
for (const l of atomLinks) {
|
|
53
|
+
const $ = l && l.$;
|
|
54
|
+
if (!$ || typeof $.href !== "string") continue;
|
|
55
|
+
const rel = String($.rel || "").toLowerCase();
|
|
56
|
+
const ctype = String($.type || "").toLowerCase();
|
|
57
|
+
if (rel === "enclosure" && ctype.startsWith("image/")) {
|
|
58
|
+
const u = trimUrl($.href);
|
|
59
|
+
if (u) return u;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
19
62
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
63
|
+
|
|
64
|
+
const fromHtml =
|
|
65
|
+
firstImgSrcFromHtml(item.content) ||
|
|
66
|
+
firstImgSrcFromHtml(item.summary) ||
|
|
67
|
+
firstImgSrcFromHtml(item["content:encoded"]) ||
|
|
68
|
+
firstImgSrcFromHtml(item.contentSnippet);
|
|
69
|
+
if (fromHtml && /^https?:\/\//i.test(fromHtml)) {
|
|
70
|
+
return fromHtml;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function firstImgSrcFromHtml(html) {
|
|
77
|
+
if (typeof html !== "string" || !html) return undefined;
|
|
78
|
+
const m = html.match(/<img[^>]+src\s*=\s*["']([^"']+)["']/i);
|
|
79
|
+
return m ? trimUrl(m[1]) : undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function fetchFeedXml(url, ctx) {
|
|
83
|
+
const fetchHtml = ctx.fetchHtml;
|
|
84
|
+
if (typeof fetchHtml !== "function") {
|
|
85
|
+
throw new Error("RSS 插件需要 ctx.fetchHtml(请通过 feeder / buildSourceContext 调用)");
|
|
86
|
+
}
|
|
87
|
+
const { html } = await fetchHtml(url, {
|
|
88
|
+
waitMs: 800,
|
|
89
|
+
purify: false,
|
|
90
|
+
useHttpResponseBody: true,
|
|
26
91
|
});
|
|
27
|
-
return
|
|
92
|
+
return html;
|
|
28
93
|
}
|
|
29
94
|
|
|
30
95
|
export default {
|
|
@@ -35,7 +100,22 @@ export default {
|
|
|
35
100
|
refreshInterval: "1h",
|
|
36
101
|
async fetchItems(sourceId, ctx) {
|
|
37
102
|
const { deps } = ctx;
|
|
38
|
-
const
|
|
103
|
+
const xml = await fetchFeedXml(sourceId, ctx);
|
|
104
|
+
const parser = new deps.RssParser({
|
|
105
|
+
timeout: 30_000,
|
|
106
|
+
headers: {
|
|
107
|
+
"User-Agent": UA,
|
|
108
|
+
Accept: "application/rss+xml,application/atom+xml,application/json,application/xml,text/xml,*/*",
|
|
109
|
+
},
|
|
110
|
+
customFields: {
|
|
111
|
+
item: [
|
|
112
|
+
["media:thumbnail", "mediaThumbnail", { keepArray: true }],
|
|
113
|
+
["media:content", "mediaContent", { keepArray: true }],
|
|
114
|
+
["link", "atomLinks", { keepArray: true }],
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
const feed = await parser.parseString(xml);
|
|
39
119
|
return (feed.items ?? []).map((item) => {
|
|
40
120
|
const link = item.link ?? item.guid ?? sourceId;
|
|
41
121
|
const guid = item.guid ?? deps.createHash("sha256").update(link).digest("hex");
|
|
@@ -52,7 +132,8 @@ export default {
|
|
|
52
132
|
typeof item.summary === "string" ? item.summary : typeof item.contentSnippet === "string" ? item.contentSnippet : undefined;
|
|
53
133
|
const content =
|
|
54
134
|
typeof item.content === "string" ? item.content : typeof item["content:encoded"] === "string" ? item["content:encoded"] : undefined;
|
|
55
|
-
|
|
135
|
+
const imageUrl = extractItemImageUrl(item);
|
|
136
|
+
const base = {
|
|
56
137
|
guid,
|
|
57
138
|
title: item.title ?? "",
|
|
58
139
|
link,
|
|
@@ -61,6 +142,8 @@ export default {
|
|
|
61
142
|
summary,
|
|
62
143
|
content,
|
|
63
144
|
};
|
|
145
|
+
if (!imageUrl) return base;
|
|
146
|
+
return { ...base, imageUrl, cover_img: imageUrl };
|
|
64
147
|
});
|
|
65
148
|
},
|
|
66
149
|
};
|