FYKANG/dsh-rss-reader ↗★ 0

dsh-rss-reader

DSH plugin: an in-harness RSS/Atom reader — subscribe to feeds, browse items in a dedicated panel, refresh on demand, read with the agent 适合需要在DSH内部订阅、浏览和让智能体阅读RSS源的用户。

Package
dsh-rss-reader
Compatibility
Unverified
Harness peer range
^0.1.5-rc.1
Cordis peer range
^4.0.2
Version
0.1.0
Last updated
Sep 24, 2026

Install

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:FYKANG/dsh-rss-reader

使用

界面

中间主区域(三栏)

  1. 点击左侧栏 RSS 图标打开阅读面板。
  2. 首次使用点 + 添加订阅源,粘贴订阅源地址(例如 https://www.ithome.com/rss/)或网站首页(例如 https://github.blog/),可选填分组,点「添加并获取」。
  3. 左栏选择订阅源或「全部订阅源」,中间点选条目,右侧阅读正文(Markdown 排版)。
  4. 顶栏可搜索、筛选未读 / 收藏,或「⟳ 刷新」。

右侧边栏(单栏)

  1. 点左侧栏底部的 RSS 阅读器(或右侧边栏 + 里的同名项)打开。
  2. 顶部一排是订阅源 横条,每个胶囊显示源名与未读数;下面就是条目列表。
  3. 点某条 → 列表就地换成该条详情,← 返回列表 固定在顶部随读随在(不跟随滚动条跑掉);返回后筛选与搜索都保持原样,没有跳转、没有新标签页。
  4. 读的时候想换个源?直接点顶部的订阅源胶囊——会立刻回到列表(不会停在上一条的详情里让你再点一次返回)。「全部」同理。
  5. 悬浮条贴住阅读区顶边、下方没有空隙:阅读区本身没有上内边距(有的话会落在悬浮条上方,正文会从缝里透出来),正文的首个元素上外边距也用一条只作用于该面板的规则清掉了——外边距塌陷没法用行内样式表达,这是唯一一处注入的 CSS。
  6. 顶栏按钮在窄栏里收成图标(○ 未读、★ 收藏、⟳ 刷新、+ 添加),鼠标悬停有完整说明。

取消订阅(右键 + 二次确认)

订阅源行上没有删除按钮。取消订阅走两步:

  1. 在订阅源上右键(或点行尾的 ⋯)→ 弹出操作菜单:刷新此源 / 全部标为已读 / 取消订阅…;
  2. 选「取消订阅…」后弹出确认框,写清将要删除的条目数,点 确定取消订阅 才真正执行。

菜单和确认框都可以随时放弃,期间不会发出任何请求。

设置页(设置 → 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 上别人整理好的那些订阅源,有什么值得我订?

点顶栏 🧭 探索(窄栏里只显示图标):

  1. 搜索:一个输入框同时搜站点名、路由名、路径。输入 热搜 会列出「B 站热搜 / 百度热搜榜 / 微博热搜榜」;输入 bilibili 会把这些站点的路由都排前面。
  2. 分类:一排分类胶囊(编程、社交媒体、大学、传统媒体、财经、政务……),每个后面是它覆盖的站点数。选中即过滤,再点一次取消。
  3. 站点:默认列出站点(名称 + 域名 + 路由条数),按路由条数从多到少。点进去就是该站点的所有路由,左上角有 ← 返回。
  4. 订阅:在一条路由上点 添加,展开参数表单——参数已经用 RSSHub 官方示例填好,可以直接改,也可以留空可选项(留空的可选参数会从地址里省略)。点 订阅 即完成,对话框不会关闭,方便接着订第二条。

每条路由会带上它自己声明的事实,避免「订了却取不到」:

标记含义
需配置这条路由需要实例侧配置(Cookie / Token)。公共实例上通常取不到,需要自建实例。鼠标悬停会列出缺哪些变量名。
反爬站点有反爬措施,公共实例可能被拦。
需浏览器需要实例开启 Playwright / Puppeteer。
示例 …RSSHub 文档里给出的可运行示例地址。

订下去之后,标题由订阅源自己决定:像 36kr 那条多用途路由(资讯 / 快讯 / 用户文章……)在参数为 newsflashes 时,最终订阅名是「36氪 - 快讯」而不是路由表里的长名字。

首次打开会慢几秒:宿主要先把实例的整张路由表(约 3 MB)拉下来并投影成紧凑结构(实测约 1 秒),之后 12 小时内都走内存缓存(实测 30 ms 左右)。缓存窗口可用 rsshubCatalogMinutes 调整。

抓取更早的文章

订阅源是窗口,不是档案。阮一峰的 atom.xml 里就只有 3 条(实测 44319 字节、3 个 ``),所以无论刷新多少次都到不了第 400 期——不是插件截断了,是源里没有。

往前的文章仍在他自己的归档页上,插件可以按需取回:

  1. 在订阅源横条上选中一个源(「全部」时这个入口不出现,一次抓所有源对人家服务器不礼貌);
  2. 点顶栏 ⇤ 更早,对话框里填归档页地址(默认按该源已有条目的目录猜一个,通常直接确认即可)和篇数(默认 20,上限 50);
  3. 抓完列表里就多了这些文章,按日期插在正确的位置,可以正常阅读、翻译、收藏。

它的行为是刻意保守的:

  • 只挑"像文章"的链接:拿已订阅条目的地址推出形态(目录形态 + 扩展名,或整条路径形态),归档页里的导航、标签页、月度索引都会被滤掉;
  • 不重复抓:已订阅过的地址会跳过,所以连点两次不会重复下载(http / https 两种写法视为同一条);
  • 请求之间有停顿(默认 300ms,historyDelayMs 可调),一篇失败不影响其余各篇,失败原因会在对话框里列出;
  • 每源保留上限仍然生效(maxItemsPerFeed,默认 100):抓回的旧文按日期排序,超限时丢的是最老的,不会挤掉订阅源自己给的那几条。

配置项:

history: true          # 设为 false 关闭这个功能(接口会返回 503)
historyMaxPerRun: 50   # 单次上限,无论对话框里填多少
historyDelayMs: 300    # 每篇之间的间隔

抓取的是别人服务器上的页面,一次 50 篇就是 50 个请求。默认值是按"够用且克制"选的;要一次拉完几百期,请把篇数调大并自己确认对方站点能接受,或者干脆分批抓。

阅读正文

  • 图片留在原位:图片不再被搬到文章结尾,而是留在原文给它的位置——段与段之间的图就是段与段之间的一个折叠条,连续的图合成一组(▸ 图片 3 张),单张图显示它的配文(▸ 图片:配图说明)。
    • 折叠时不会加载任何图片(连 `` 都不渲染);点一下展开缩略图,再点某张图放大看原图(Esc 或点背景关闭,可点「原图 ↗」在新标签打开)。
    • 展开后每张图先显示「载入中…」;加载成功即显示图片,加载失败则在该位置显示「图片加载失败」,不会一直停在载入中。
    • 句子里的图(文字 ![说明](https://github.com/FYKANG/dsh-rss-reader/blob/53550e1190fde41d8033e23482a1bb13d34fa916/%E5%9B%BE) 文字)折成一枚 🖼 说明 小标签留在句子中,点开就地显示,不把一句话拆成三段。
    • 想一进来就看到图,打开 设置 → 通用 → 「正文图片直接展开」;那之后图片仍在原位,只是不再折叠(依旧按需加载),而且单独点一条仍可以只把那条折回去。
  • 一键翻译:点顶栏 译 按钮,把当前条目翻译成左下角「翻译为」所选的语言。
    • 首次翻译会调用模型(通常几秒到二十几秒,按钮显示「翻译中…」)。
    • 翻好后按钮变为 显示译文 / 显示原文,可随时切换;译文同样按 Markdown 排版,链接与代码原样保留。
    • 对照:把原文与译文逐段穿插显示——每一段原文下面紧跟它自己的译文(译文左侧有一条竖线区分)。这是默认推荐的读法:既能核对译法,也不会丢掉原文的措辞。
      • 逐段对照靠"按段翻译"实现:插件把正文切成段落(代码块内的空行不算切分),逐段编号交给模型,要求一一对应地返回,再按编号配对。
      • 只有完全对齐时才显示对照;模型少给、多给或编号重复时,宁可退回整篇译文,也不会把某段的译文错配到另一段下面(那时页面会说明并提示点 ↻ 重译)。
      • 只有图片或只有代码的段落没有可译内容,会原样保留在原文一侧、不重复显示。
      • 旧版本缓存下来的译文没有分段信息,点 对照 会自动重译一次以获得对齐结果。
    • 旁边的 ↻ 强制重译,✕ 删除已缓存的译文。
    • 换一个目标语言会自动重新翻译(不会拿中文译文冒充日文)。
    • 若当前 profile 没有可用模型,按钮会直接不显示。

提示条怎么消失

  • 成功 / 状态提示(「已刷新 3/4 个源,新增 12 条」「订阅成功」「翻译完成」「已显示缓存的翻译」)出现 2 秒后自动消失,不必手动点关闭;连续两条提示时,计时从最新那条重新开始。
  • 异常提示(刷新失败、翻译失败、宿主返回错误等)不会自动消失,必须点「关闭」——这类信息你可能还要照着处理,不该在读完之前自己跑掉。
  • 设置页里的输入校验提示(实例地址、翻译模型)是常驻说明,不是一次性提示,不参与这个规则。

RSSHub 订阅源发现

很多站点自己根本不提供 RSS。在「+ 添加订阅源」对话框里粘贴任意网页地址,点 🔍 查找,插件会分两路找出可用订阅源:

  1. 页面自身声明的 `` —— 权威、不依赖第三方,排在最前;
  2. 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 实例 可以随时改,改完立即生效(两个客户端一起换,并丢掉旧实例的缓存)。用户没改过就跟着这个配置走;「恢复默认」会删掉用户的选择,回到这里配置的值。