dsh-rss-reader
在侧边栏提供内置的RSS/Atom订阅阅读器 适合需要在DSH内部订阅、浏览和让智能体阅读RSS源的用户。
安裝
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:FYKANG/dsh-rss-reader說明文件
閱讀完整 README ↗使用
界面
中间主区域(三栏)
- 点击左侧栏 RSS 图标打开阅读面板。
- 首次使用点 + 添加订阅源,粘贴订阅源地址(例如
https://www.ithome.com/rss/)或网站首页(例如https://github.blog/),可选填分组,点「添加并获取」。 - 左栏选择订阅源或「全部订阅源」,中间点选条目,右侧阅读正文(Markdown 排版)。
- 顶栏可搜索、筛选未读 / 收藏,或「⟳ 刷新」。
右侧边栏(单栏)
- 点左侧栏底部的 RSS 阅读器(或右侧边栏 + 里的同名项)打开。
- 顶部一排是订阅源 横条,每个胶囊显示源名与未读数;下面就是条目列表。
- 点某条 → 列表就地换成该条详情,← 返回列表 固定在顶部随读随在(不跟随滚动条跑掉);返回后筛选与搜索都保持原样,没有跳转、没有新标签页。
- 读的时候想换个源?直接点顶部的订阅源胶囊——会立刻回到列表(不会停在上一条的详情里让你再点一次返回)。「全部」同理。
- 悬浮条贴住阅读区顶边、下方没有空隙:阅读区本身没有上内边距(有的话会落在悬浮条上方,正文会从缝里透出来),正文的首个元素上外边距也用一条只作用于该面板的规则清掉了——外边距塌陷没法用行内样式表达,这是唯一一处注入的 CSS。
- 顶栏按钮在窄栏里收成图标(
○未读、★收藏、⟳刷新、+添加),鼠标悬停有完整说明。
取消订阅(右键 + 二次确认)
订阅源行上没有删除按钮。取消订阅走两步:
- 在订阅源上右键(或点行尾的 ⋯)→ 弹出操作菜单:
刷新此源/全部标为已读/取消订阅…; - 选「取消订阅…」后弹出确认框,写清将要删除的条目数,点 确定取消订阅 才真正执行。
菜单和确认框都可以随时放弃,期间不会发出任何请求。
设置页(设置 → RSS 阅读器)
RSS 的设置不混在「通用」里,而是自己一页(导航里叫 RSS 阅读器,排在 通用 / 模型 / 插件 之后):
「左侧栏入口」
- 打开(默认):左侧栏显示 RSS 图标,点击在中间主区域打开三栏面板;
- 关闭:左侧栏不再显示该图标。面板本身没有消失——仍可从左侧栏底部的「RSS 阅读器」按钮或右侧边栏打开。
「正文图片直接展开」
- 关闭(默认):正文里的图片折成一条可展开的提示(
▸ 图片:配图说明/▸ 图片 3 张),位置与原文一致,点开才加载; - 打开:图片直接显示在原位(仍然
loading="lazy"按需加载),最接近原文观感。
「收起订阅源栏」
- 关闭(默认):右侧边栏面板顶部保留订阅源那一行胶囊(
全部/ 各订阅源 + 未读数); - 打开:那一行折起来,把纵向空间让给条目。折叠后图钉位置改由表头的 ▸ 承担——它会写清当前筛的是哪个源(没筛就是「订阅源 N」),点它即可展开。
只影响右侧边栏那个窄面板。中间主区域的三栏布局里,左栏订阅源列表同时也是重排顺序、右键管理订阅源的地方,折掉它会拿走能力而不只是腾地方,所以那里不跟着折;面板表头上的折叠按钮也只在窄栏出现。
「翻译模型」(仅当这个 profile 真的能翻译时出现)
三个下拉 + 恢复默认:模型服务、模型、思考强度(只有当所选模型声明了可选强度时才出现)。下面一行写清当前生效的是哪个路由(服务 / 模型,或「跟随 DSH 默认模型」)以及它是「自定义」还是「默认」;换模型服务会同时清掉模型选择,避免留下半套路由。
- 清单来自宿主自己的模型目录,所以每个模型能选的思考强度也会一并列出;
- 选了当前 profile 没挂载的服务,这里会直接写明「没有挂载,翻译会失败」,而不是等你点了「译」才发现;
- 恢复默认一次清掉三项,回到插件配置 / 会话默认模型;
- 长文翻译失败最常见的原因就在这里:推理型模型会把输出额度先花在「思考」上,换一个不做长推理的模型,或把思考强度调到最低,比调大
translateMaxTokens更有效。
「订阅源顺序」
带编号的订阅源列表,每行 ↑ / ↓ 两个按钮,首行的 ↑ 与末行的 ↓ 是禁用的(到顶到底了)。面板里拖出来的顺序也会实时反映在这里。
「RSSHub 实例」(仅当这个 profile 真的启用了 RSSHub 时出现)
一个地址输入框 + 保存 / 测试连接 / 恢复默认:
- 框里显示的是当前生效的实例,下面一行写清它是「自定义实例」还是「默认实例」;
- 保存后立即生效(不用重启),并自动探一次可达性,直接告诉你「已保存。实例可达(HTTP 200)」或「已保存。实例没有响应:…」;
- 测试连接不改地址,只探当前这个实例;
- 地址填错会被拒绝保存(不是存下来等下次启动才炸),错误就显示在框下面;
- 恢复默认清掉自定义地址,回到插件配置里的
rsshubBase(没配置就是官方https://rsshub.app)。
这些开关都点下即生效,不需要重启,也不需要刷新页面(关掉入口后左侧栏那一行立刻消失,打开后立刻回来;切换图片偏好后正文立刻重排)。选择会写进订阅数据文件(prefs.showSidebarEntry / prefs.expandImages / prefs.collapseFeeds),重启后依然有效;写入失败时开关会弹回原位并就地报错,不会假装已保存。
窄栏表头的折叠按钮和这一页的开关写的是同一个偏好,所以从哪里折、从哪里开都一样,折叠状态也会跟着重启保留(这正是「右侧边栏面板重新打开时不该又弹回来」所要求的)。
之所以把这些开关放进 DSH 自己的设置、而不是放进 RSS 面板:入口那个开关决定的是面板有没有那个入口,如果只能从那个入口进去关它,就是个死循环。而图片偏好与排序都是阅读偏好,和「左侧栏长什么样」一样属于 DSH 层面的设置。
给订阅源排序
三种操作,写的是同一份顺序(存在宿主的状态文件里,所以换浏览器、换面板、Agent 看到的都是同一个顺序):
| 方式 | 位置 | 适合 |
|---|---|---|
| 拖动 | 面板左栏的订阅源行(窄栏是顶部胶囊) | 一眼看清、一次挪到位 |
| 右键 →「上移 / 下移」 | 订阅源行右键菜单的最上面 | 挪一位、不想拖;窄栏里尤其好用 |
| ↑ / ↓ 按钮 | 设置 → RSS 阅读器 → 订阅源顺序 | 精确调整、触屏、一次挪很多位 |
细节:
- 行首有 ⠿ 记号、鼠标是抓手,提示这一行可以拖。
- 拖到某一行的位置上就占它的位,中间的行依次让位——不需要瞄准缝隙。
- 菜单里没有「上移」就是已经在最上面(而不是给一个点了没反应的灰项)。
- 新订阅的源排在末尾,不会插进你已经排好的顺序中间;删掉的源会从顺序里消失,不留空洞。
- 排序立即生效(先动屏幕、再落盘),宿主拒绝时会把顺序退回并提示,不会留一个「屏幕上是这样、文件里不是」的状态。
探索 RSSHub 订阅源
「🔍 查找」解决的是「我已经知道那个地址」;探索解决的是另一半问题:RSSHub 上别人整理好的那些订阅源,有什么值得我订?
点顶栏 🧭 探索(窄栏里只显示图标):
- 搜索:一个输入框同时搜站点名、路由名、路径。输入
热搜会列出「B 站热搜 / 百度热搜榜 / 微博热搜榜」;输入bilibili会把这些站点的路由都排前面。 - 分类:一排分类胶囊(编程、社交媒体、大学、传统媒体、财经、政务……),每个后面是它覆盖的站点数。选中即过滤,再点一次取消。
- 站点:默认列出站点(名称 + 域名 + 路由条数),按路由条数从多到少。点进去就是该站点的所有路由,左上角有 ← 返回。
- 订阅:在一条路由上点 添加,展开参数表单——参数已经用 RSSHub 官方示例填好,可以直接改,也可以留空可选项(留空的可选参数会从地址里省略)。点 订阅 即完成,对话框不会关闭,方便接着订第二条。
每条路由会带上它自己声明的事实,避免「订了却取不到」:
| 标记 | 含义 |
|---|---|
| 需配置 | 这条路由需要实例侧配置(Cookie / Token)。公共实例上通常取不到,需要自建实例。鼠标悬停会列出缺哪些变量名。 |
| 反爬 | 站点有反爬措施,公共实例可能被拦。 |
| 需浏览器 | 需要实例开启 Playwright / Puppeteer。 |
| 示例 … | RSSHub 文档里给出的可运行示例地址。 |
订下去之后,标题由订阅源自己决定:像 36kr 那条多用途路由(资讯 / 快讯 / 用户文章……)在参数为 newsflashes 时,最终订阅名是「36氪 - 快讯」而不是路由表里的长名字。
首次打开会慢几秒:宿主要先把实例的整张路由表(约 3 MB)拉下来并投影成紧凑结构(实测约 1 秒),之后 12 小时内都走内存缓存(实测 30 ms 左右)。缓存窗口可用
rsshubCatalogMinutes调整。
抓取更早的文章
订阅源是窗口,不是档案。阮一峰的 atom.xml 里就只有 3 条(实测 44319 字节、3 个 ``),所以无论刷新多少次都到不了第 400 期——不是插件截断了,是源里没有。
往前的文章仍在他自己的归档页上,插件可以按需取回:
- 在订阅源横条上选中一个源(「全部」时这个入口不出现,一次抓所有源对人家服务器不礼貌);
- 点顶栏 ⇤ 更早,对话框里填归档页地址(默认按该源已有条目的目录猜一个,通常直接确认即可)和篇数(默认 20,上限 50);
- 抓完列表里就多了这些文章,按日期插在正确的位置,可以正常阅读、翻译、收藏。
它的行为是刻意保守的:
- 只挑"像文章"的链接:拿已订阅条目的地址推出形态(目录形态 + 扩展名,或整条路径形态),归档页里的导航、标签页、月度索引都会被滤掉;
- 不重复抓:已订阅过的地址会跳过,所以连点两次不会重复下载(
http/https两种写法视为同一条); - 请求之间有停顿(默认 300ms,
historyDelayMs可调),一篇失败不影响其余各篇,失败原因会在对话框里列出; - 每源保留上限仍然生效(
maxItemsPerFeed,默认 100):抓回的旧文按日期排序,超限时丢的是最老的,不会挤掉订阅源自己给的那几条。
配置项:
history: true # 设为 false 关闭这个功能(接口会返回 503)
historyMaxPerRun: 50 # 单次上限,无论对话框里填多少
historyDelayMs: 300 # 每篇之间的间隔
抓取的是别人服务器上的页面,一次 50 篇就是 50 个请求。默认值是按"够用且克制"选的;要一次拉完几百期,请把篇数调大并自己确认对方站点能接受,或者干脆分批抓。
阅读正文
- 图片留在原位:图片不再被搬到文章结尾,而是留在原文给它的位置——段与段之间的图就是段与段之间的一个折叠条,连续的图合成一组(
▸ 图片 3 张),单张图显示它的配文(▸ 图片:配图说明)。- 折叠时不会加载任何图片(连 `` 都不渲染);点一下展开缩略图,再点某张图放大看原图(
Esc或点背景关闭,可点「原图 ↗」在新标签打开)。 - 展开后每张图先显示「载入中…」;加载成功即显示图片,加载失败则在该位置显示「图片加载失败」,不会一直停在载入中。
- 句子里的图(
文字  文字)折成一枚🖼 说明小标签留在句子中,点开就地显示,不把一句话拆成三段。 - 想一进来就看到图,打开 设置 → 通用 → 「正文图片直接展开」;那之后图片仍在原位,只是不再折叠(依旧按需加载),而且单独点一条仍可以只把那条折回去。
- 折叠时不会加载任何图片(连 `` 都不渲染);点一下展开缩略图,再点某张图放大看原图(
- 一键翻译:点顶栏 译 按钮,把当前条目翻译成左下角「翻译为」所选的语言。
- 首次翻译会调用模型(通常几秒到二十几秒,按钮显示「翻译中…」)。
- 翻好后按钮变为 显示译文 / 显示原文,可随时切换;译文同样按 Markdown 排版,链接与代码原样保留。
- 对照:把原文与译文逐段穿插显示——每一段原文下面紧跟它自己的译文(译文左侧有一条竖线区分)。这是默认推荐的读法:既能核对译法,也不会丢掉原文的措辞。
- 逐段对照靠"按段翻译"实现:插件把正文切成段落(代码块内的空行不算切分),逐段编号交给模型,要求一一对应地返回,再按编号配对。
- 只有完全对齐时才显示对照;模型少给、多给或编号重复时,宁可退回整篇译文,也不会把某段的译文错配到另一段下面(那时页面会说明并提示点 ↻ 重译)。
- 只有图片或只有代码的段落没有可译内容,会原样保留在原文一侧、不重复显示。
- 旧版本缓存下来的译文没有分段信息,点 对照 会自动重译一次以获得对齐结果。
- 旁边的 ↻ 强制重译,✕ 删除已缓存的译文。
- 换一个目标语言会自动重新翻译(不会拿中文译文冒充日文)。
- 若当前 profile 没有可用模型,按钮会直接不显示。
提示条怎么消失
- 成功 / 状态提示(「已刷新 3/4 个源,新增 12 条」「订阅成功」「翻译完成」「已显示缓存的翻译」)出现 2 秒后自动消失,不必手动点关闭;连续两条提示时,计时从最新那条重新开始。
- 异常提示(刷新失败、翻译失败、宿主返回错误等)不会自动消失,必须点「关闭」——这类信息你可能还要照着处理,不该在读完之前自己跑掉。
- 设置页里的输入校验提示(实例地址、翻译模型)是常驻说明,不是一次性提示,不参与这个规则。
RSSHub 订阅源发现
很多站点自己根本不提供 RSS。在「+ 添加订阅源」对话框里粘贴任意网页地址,点 🔍 查找,插件会分两路找出可用订阅源:
- 页面自身声明的 `` —— 权威、不依赖第三方,排在最前;
- RSSHub 的 Radar 规则 —— 把页面 URL 匹配成一条 RSSHub 路由,为没有 RSS 的站点生成订阅源。
结果会带来源标签(站点 / RSSHub),点任意一条即可直接订阅,订阅名用路由标题而不是裸 URL。
粘贴 https://space.bilibili.com/2267573 →
[RSSHub] UP 主图文 → rsshub…/bilibili/user/article/2267573
[RSSHub] UP 主投币视频 → rsshub…/bilibili/user/coin/2267573
[RSSHub] UP 主动态 → rsshub…/bilibili/user/dynamic/2267573
如果只粘贴了首页(如 github.com)而没有具体路径,通常匹配不到路由。此时插件不会留下一片空白,而是提示该站点在 RSSHub 中共有多少条路由、并举例说明——让你知道该换成哪种页面地址(github.com/作者/仓库/issues 之类)再去查找。
关于 RSSHub 实例:默认使用官方公共实例 https://rsshub.app。公共实例常有速率限制或需要 key,自建实例通常更稳定,可在配置里指定:
config:
rsshubBase: 'https://rsshub.example.com' # 自建或偏好的公共实例
rsshub: true # 设为 false 可关闭该功能
rsshubTimeoutMs: 15000
rsshubCacheMinutes: 360
- 若实例地址填错,插件不会启动失败,只是关闭该功能并在日志里说明原因。
- 规则按域名缓存(默认 6 小时)。若某条路由取不到内容,多半是实例限流或该路由需要配置
key/Cookie,与插件无关——可换实例试试。
注意:
rsshub.app在部分网络环境下不可达。发现功能依赖能否访问你配置的实例;实例不可用时插件仍可正常订阅页面自身声明的 RSS。
命令
/rss # 列出订阅源与最近条目
/rss refresh # 先刷新再列出
/rss refresh 掘金 # 只刷新并查看指定源
工具
Agent 可调用 rss_read:
| 参数 | 说明 |
|---|---|
feedId | 只看某个源(id 或标题),省略为全部 |
limit | 最多列出多少条,默认 30 |
unreadOnly | 只看未读 |
refresh | 先联网刷新再读取 |
配置
在插入条目里写 config(或使用 dsh plugin 安装后编辑 profile 的 cordis.patch.yml):
- insert:
- id: rss-reader
name: 'dsh-rss-reader'
config:
refreshMinutes: 30 # 后台自动刷新间隔(分钟),0 = 关闭(默认)
refreshOnStart: true # 启动时刷新一次,默认 false
timeoutMs: 20000 # 单次请求超时
maxBytes: 8388608 # 单个响应大小上限(8 MiB)
concurrency: 4 # 并发抓取的源数量
maxItemsPerFeed: 100 # 每个源保留的条目数
maxFeeds: 200 # 订阅源数量上限
storeFile: '' # 状态文件路径,默认 $DSH_HOME/rss-reader/feeds.json
tool: true # 注册 rss_read 工具与提示词说明
webApi: true # 提供 Web 面板与 HTTP API
showSidebarEntry: true # 左侧栏是否显示 RSS 入口(用户可在 DSH 设置里覆盖,见上)
# ── 翻译 ──────────────────────────────────────────────
translate: true # 开启「译」按钮
translateTarget: zh-CN # 默认目标语言
translateProvider: '' # 留空 = 复用当前会话的默认模型;用户可在 设置 → RSS 阅读器 里覆盖
translateModel: ''
translateEffort: '' # 思考强度(如 low / high);留空 = 模型自己的默认
# ── 抓取更早的文章 ──────────────────────────────────────
history: true # 允许从站点归档页按需抓取更早的文章
historyMaxPerRun: 50 # 单次抓取上限(对话框里填再多也不会超过)
historyDelayMs: 300 # 每篇之间的间隔,对别人服务器客气一点
translateTimeoutMs: 90000
translateMaxTokens: 4000 # 输出预算的**下限**,长文会自动提高(见下)
# ── RSSHub 订阅源发现 ───────────────────────────────────
rsshub: true # 开启「🔍 查找」发现功能
rsshubExplore: true # 开启「🧭 探索」路由表浏览
rsshubBase: '' # 默认实例;留空 = https://rsshub.app。用户可在 设置 → RSS 阅读器 里覆盖
rsshubTimeoutMs: 15000
rsshubCacheMinutes: 360 # Radar 规则缓存(按域名)
rsshubCatalogMinutes: 720 # 路由表缓存(整张表,默认 12 小时)
关于翻译用的模型:translateProvider / translateModel / translateEffort 是默认值,用户可以在 设置 → RSS 阅读器 → 翻译模型 里改(三个下拉:模型服务、模型、思考强度),改完立即生效、不用重启。三层优先级是:用户在设置里选的 → 插件配置 → 会话默认模型(agentDefaultModel,也就是你在 DSH 里选的那个),所以什么都不配也能用,不需要额外 API Key。如果当前 profile 没有挂载 LLM 服务或没有可用模型,插件仍能正常阅读,只是隐藏「译」按钮(而不是给你一个点了必失败的按钮)。
模型清单来自宿主自己的模型目录(sessionController.modelCatalog()),因此每个模型可选的思考强度也一并列出;选了一个当前 profile 没有挂载的服务时,设置页会直接说它没有挂载、翻译会失败,而不是让你点了才发现。
关于 translateMaxTokens:它是输出预算的下限,不是硬上限。正文越长,插件要求的预算越高(按正文字符数估算,上限 64000),因为一篇长文的译文本身就和原文差不多长,而推理型模型还会先花掉同一份额度去思考——额度在思考阶段就用光时,模型一个文字块都没吐出来。这也是长文翻译失败最常见的原因:与其调大额度,不如在设置里换一个不做长推理的模型,或把思考强度调到最低。真的被截断时报错会说明「模型在输出 N tokens 时被截断」并指向设置页,而不是含糊地说「模型没有返回文本」。要控成本就把这个值设小(长文可能因此失败);translate: false 则整体关掉翻译(面板上不会出现「译」按钮)。
关于 showSidebarEntry:它是默认值,不是开关锁。用户在 DSH 设置里选过之后,以用户的选择为准;没选过的人才跟着这个配置走。所以把它设成 false 可以得到「出厂即隐藏左侧栏入口」的效果,而用户仍能在设置里把它打开。要回到配置默认值,删掉数据文件里的 prefs 段即可。
关于 rsshubBase:同理,它是默认实例,不是锁。官方实例在部分网络下不可达,而找到可用实例不该意味着改配置 + 重启——所以 设置 → RSS 阅读器 → RSSHub 实例 可以随时改,改完立即生效(两个客户端一起换,并丢掉旧实例的缓存)。用户没改过就跟着这个配置走;「恢复默认」会删掉用户的选择,回到这里配置的值。