1. 项目概述一个为OpenClaw打造的RSS摘要生成技能如果你和我一样每天需要关注几十个甚至上百个RSS订阅源从技术博客、行业动态到个人随笔那么信息过载和重复阅读绝对是最大的痛点。手动打开每个阅读器或者依赖一些通用聚合工具要么效率低下要么无法按照自己的逻辑进行归类整理。今天要聊的这个项目——openclaw-skill-rss-digest就是为解决这个问题而生的。它本质上是一个脚本“技能”围绕blogwatcher这个命令行RSS阅读器构建核心任务就一个自动抓取你配置的所有订阅源然后智能地按类别整理、去重最后生成一份干净、可读性强的每日或每周摘要。这个项目特别适合那些已经习惯使用命令行工具、追求自动化工作流的开发者或技术爱好者。它不是一个独立的桌面应用而是一个“胶水”脚本完美地嵌入了OpenClaw这个开源AI智能体生态。你可以把它想象成给你的数字助理OpenClaw增加了一个“读报”技能让它能定时为你抓取、消化网络信息并以你喜欢的格式比如带分类表情的Markdown或结构化的JSON呈现出来。对于需要追踪特定领域如AI、加密货币、Web开发动态又不想被海量未读条目淹没的人来说这是一个极佳的解决方案。2. 核心设计思路与架构解析2.1 为什么选择 blogwatcher jq 的组合这个项目的设计非常“Unix哲学”一个工具只做好一件事然后通过管道组合起来完成复杂任务。它没有重新发明轮子去解析RSS而是选择了blogwatcher作为底层引擎。选择 blogwatcher 的理由专注与稳定blogwatcher本身就是一个成熟的、专注于抓取和初步解析RSS/Atom的命令行工具。它处理了网络请求、缓存、遵循网站robots.txt、解析不同Feed格式等脏活累活提供了一个干净的JSON输出接口。这比我们自己用curl和正则表达式去抓取要可靠得多。配置化blogwatcher使用TOML配置文件可以非常方便地管理大量的订阅源feeds并支持分组、设置抓取间隔等。这为后续的分类整理提供了天然的元数据基础。生态契合作为命令行工具它的输出JSON极易被其他工具如jq处理完美契合自动化管道。而jq则是处理JSON的“瑞士军刀”。项目的核心逻辑——去重、分类、格式化、截取——几乎全部通过精心设计的jq过滤器管道完成。这种设计的好处是性能高纯C编写、依赖少且逻辑清晰所有数据转换规则都体现在jq脚本中易于维护和调试。2.2 数据流转与核心模块拆解整个技能的工作流程可以清晰地分为几个阶段理解这个流程对自定义和排错至关重要数据采集层由blogwatcher命令执行。它根据~/.config/blogwatcher/config.toml中的配置抓取所有订阅源的最新内容并将每个条目转换为结构化的JSON对象数组输出。每个条目通常包含title、link、published发布时间、feed_title来源名称、categories标签等字段。数据处理管道这是脚本的核心。原始JSON数据通过管道|送入一系列jq命令。去重这是关键且曾存在性能瓶颈的一步。早期版本可能使用了嵌套循环对比link或title导致时间复杂度为O(n²)。优化后的方案利用jq的INDEX函数以条目唯一标识如link为键快速构建哈希映射实现近似O(1)的查找将整体去重复杂度降至O(n)。分类打标脚本会扫描条目的title、categories甚至feed_title根据预定义或可配置的关键词规则为其分配一个类别如“ 开发”、“ AI”、“ 加密货币”等并添加相应的表情符号前缀。排序与截取通常按发布时间published降序排列确保最新内容在前。然后根据MAX_ITEMS配置默认20截取指定数量的条目防止摘要过长。格式化输出层处理后的数据被格式化成两种主要形式。人类可读文本用于直接阅读或发送到即时通讯软件如Telegram、Slack。格式通常是“[类别表情] 标题 - 来源”的列表。机器可读JSON用于进一步的自动化处理。例如另一个脚本可以读取这个JSON将其发布到博客的“每日链接”板块或者生成数据可视化图表。状态管理与持久化为了实现跨次运行的去重避免每次摘要都包含上次已读过的条目脚本需要记录已处理条目的状态。一个常见的做法是将本次输出条目的唯一ID如link追加到一个状态文件如~/.cache/rss-digest/seen.log中。下次运行时先读取这个文件过滤掉已记录的ID。项目文档中提到的“state tracking”很可能就是指这类机制。2.3 性能优化深度剖析项目文档中特别提到了2026-02-18的优化这非常值得展开讲讲因为它体现了处理大数据集时的经典优化思路。优化前的问题O(n²)复杂度假设最初的脚本使用了一个简单的双重循环来去重。伪代码如下# 伪代码示意O(n²)逻辑 for item in all_items; do is_duplicatefalse for seen_item in unique_items; do if item.link seen_item.link; then is_duplicatetrue break fi done if not is_duplicate; then unique_items.append(item) fi done当有N个条目时最坏情况下需要进行大约 N*(N-1)/2 次比较。对于数百个条目开销已经不小。优化后的方案O(n)复杂度优化利用了jq内置的高效数据结构操作。核心是INDEX和GROUP_BY这类函数。# 利用 INDEX 构建查找表然后取唯一值 # 假设以 .link 作为唯一键 map(.) | group_by(.link) | map(.[0])group_by(.link)会将所有条目按link分组相同link的条目会放在同一个子数组里。然后map(.[0])从每个分组中取出第一个即代表该链接的一个条目。group_by的实现通常是哈希映射平均复杂度接近O(n)。减少子进程创建 另一个优化点是“Consolidated multiplejqfilter passes into a single pipeline”。早期版本可能像这样blogwatcher | jq ...filter1... | jq ...filter2... | jq ...filter3...每多一个| jq就多启动一个进程进程间通信管道也有开销。优化后将多个过滤、转换操作合并到一个jq脚本中blogwatcher | jq -f comprehensive_filter.jq这显著减少了系统调用和内存复制在处理大量数据时性能提升明显。实操心得这种优化思路具有普适性。在编写Shell数据处理脚本时应尽量避免在循环内调用外部命令并尽可能使用工具如jq、awk的单次复杂操作替代多次简单操作。对于需要持久化的状态使用基于文件的哈希集如sqlite3或简单的grep -f也比在内存中做线性查找要高效得多尤其是在脚本多次运行的场景下。3. 从零开始部署与配置实战3.1 环境准备与依赖安装首先你需要一个Linux或macOS环境Windows可通过WSL获得完美体验。确保已安装git和bash。第一步安装核心依赖blogwatcherblogwatcher是项目的基石。通常可以通过系统的包管理器或从源码安装。# 对于 macOS (使用 Homebrew) brew install blogwatcher # 对于 Arch Linux (使用 AUR) yay -S blogwatcher # 对于其他 Linux 发行版或需要最新版建议从源码安装 git clone https://github.com/nichochar/blogwatcher.git cd blogwatcher cargo install --path . # 需要 Rust 工具链安装完成后在终端输入blogwatcher --version验证是否成功。第二步安装 JSON 处理利器jqjq的安装更为普遍。# macOS brew install jq # Ubuntu/Debian sudo apt-get update sudo apt-get install jq # CentOS/RHEL sudo yum install jq # 或从官网下载二进制使用jq --version验证。第三步获取 openclaw-skill-rss-digest 脚本git clone https://github.com/manthis/openclaw-skill-rss-digest.git cd openclaw-skill-rss-digest此时项目目录下最重要的文件是scripts/rss-digest.sh这就是主脚本。3.2 配置 blogwatcher 订阅源这是整个流程中最个性化的一步。blogwatcher的配置文件默认位于~/.config/blogwatcher/config.toml。如果不存在可以创建它。一个典型的配置示例如下# ~/.config/blogwatcher/config.toml # 全局设置 [global] interval 3600 # 抓取间隔秒3600秒1小时 user_agent blogwatcher/1.0 (https://myblog.example.com) # 定义订阅源组 (groups) [[groups]] name tech_blogs interval 1800 # 这个组每30分钟抓取一次 # 在这个组下添加具体的订阅源 (feeds) [[groups.feeds]] url https://blog.example.com/feed.xml name Example Tech Blog # 自定义名称用于输出 [[groups.feeds]] url https://github.blog/feed/ name GitHub Blog [[groups]] name ai_news # 使用全局interval [[groups.feeds]] url https://openai.com/blog/rss/ name OpenAI Blog categories [AI, Research] # 可以为feed预设分类标签 [[groups.feeds]] url https://www.deepmind.com/blog/rss.xml name DeepMind Blog categories [AI, Reinforcement Learning]关键配置解析interval抓取频率。设置过短可能对目标网站不友好建议对于新闻类设为1-2小时技术博客设为6-12小时。groups和feeds组织订阅源的好方法。name字段很重要它会在摘要中显示为来源。categories在feed级别预设分类可以被rss-digest.sh脚本利用作为分类打标的第一优先级依据。注意事项配置文件里包含了所有你订阅的URL务必不要将其提交到公开的版本控制系统。项目README中的安全警告正是针对此。建议将~/.config/blogwatcher/目录添加到你的.gitignore全局忽略列表中。3.3 首次运行与脚本配置在运行脚本前可以先了解一下它的参数和环境变量。直接运行./scripts/rss-digest.sh --help可能会显示帮助如果脚本实现了该功能。根据项目描述主要配置通过环境变量进行# 指定 blogwatcher 命令的路径如果它不在你的 PATH 环境变量中 export BLOGWATCHER_CMD/usr/local/bin/blogwatcher # 指定 blogwatcher 的配置文件路径 export BLOGWATCHER_CONFIG$HOME/.config/blogwatcher/my-config.toml # 设置摘要中最多显示多少条条目 export MAX_ITEMS30 # 最重要的先进行一次干跑dry-run不保存任何状态只查看输出 ./scripts/rss-digest.sh --dry-run--dry-run参数非常有用它让脚本执行完整的抓取、处理、格式化流程但可能跳过写入状态文件等持久化操作让你能预览摘要内容确认分类和去重逻辑是否符合预期。如果一切正常你会看到类似这样的输出 AI - OpenAI 发布新模型 GPT-4.5 - OpenAI Blog - DeepMind 在蛋白质折叠领域取得新突破 - DeepMind Blog 开发 - GitHub Actions 新功能复合 Actions - GitHub Blog - 深入理解 Rust 所有权系统 - Example Tech Blog 加密货币 - 比特币减半周期分析 - Crypto News Blog这表明脚本成功运行并按照预设或自动识别的规则进行了分类。3.4 集成到 OpenClaw 或自动化任务作为OpenClaw的一个“技能”你需要将其集成到OpenClaw的技能配置中。具体方式取决于OpenClaw的架构通常需要在OpenClaw的配置目录如~/.config/openclaw/skills/下创建一个指向该技能的链接或配置文件并定义触发词如“生成今日摘要”、“fetch news”。更通用的做法是将其设置为定时任务Cron Job这是独立于OpenClaw的自动化方式。例如设置每天上午9点生成摘要并发送到Telegram创建最终执行脚本在~/bin/或任意你喜欢的位置创建一个包装脚本比如~/bin/daily-rss-digest。#!/bin/bash # ~/bin/daily-rss-digest export PATH$PATH:/usr/local/bin export BLOGWATCHER_CMDblogwatcher export MAX_ITEMS25 cd /path/to/openclaw-skill-rss-digest # 运行脚本并将纯文本输出保存到临时文件 ./scripts/rss-digest.sh --text /tmp/today-rss-digest.txt # 使用 curl 发送到 Telegram Bot (需要提前配置好BOT_TOKEN和CHAT_ID) BOT_TOKENYOUR_BOT_TOKEN CHAT_IDYOUR_CHAT_ID MESSAGE$(cat /tmp/today-rss-digest.txt | sed :a;N;$!ba;s/\n/%0A/g) # 将换行符转换为URL编码 curl -s -X POST https://api.telegram.org/bot${BOT_TOKEN}/sendMessage \ -d chat_id${CHAT_ID} \ -d text${MESSAGE} \ -d disable_web_page_previewtrue /dev/null记得给脚本加执行权限chmod x ~/bin/daily-rss-digest。添加到 Crontab运行crontab -e编辑定时任务。# 每天工作日早上9点15分执行 15 9 * * 1-5 /home/yourusername/bin/daily-rss-digest # 或者每周一早上9点生成一周摘要假设脚本支持--since 1week参数 # 0 9 * * 1 /home/yourusername/bin/weekly-rss-digest4. 高级定制与问题排查指南4.1 自定义分类规则与表情符号项目默认的分类规则如识别“AI”、“Crypto”等关键词很可能内嵌在rss-digest.sh脚本的jq过滤器中。要自定义你需要找到脚本中负责分类的部分。通常这会是一个大的jq函数可能叫做categorize或包含一系列if-else逻辑。例如def categorize: if (.title (.categories // [] | join( )) .feed_title) | ascii_downcase | test(\\b(ai|machine learning|deep learning|llm)\\b) then AI elif test(\\b(crypto|bitcoin|ethereum|blockchain|web3)\\b) then Crypto elif test(\\b(dev|programming|rust|python|javascript|web\\s*dev)\\b) then Dev else General end;自定义步骤打开scripts/rss-digest.sh搜索jq命令段。找到类似上面的分类函数定义。修改或添加elif分支。例如想增加一个“ 科学”类别elif test(\\b(science|physics|biology|research paper|nature)\\b) then Science调整关键词和表情符号。注意test内是正则表达式\\b表示单词边界确保匹配的是独立单词。实操心得分类规则的设计是平衡的艺术。规则太宽泛会导致大量条目被归入某一类规则太严格又会有很多条目落入“General”。建议先从几个核心领域开始运行几天后观察分类结果再逐步调整关键词。也可以考虑为某些重要的特定Feed如“Hacker News”设置固定分类覆盖自动规则。4.2 状态管理与去重逻辑的调整去重是保证摘要质量的关键。默认逻辑很可能基于条目的link字段。但有时同一篇文章可能被多个网站转载链接不同但内容高度相似。或者某些网站的链接包含会话ID或时间戳导致每次抓取链接都不同。排查与调整去重策略检查去重键在脚本中找到jq的group_by或INDEX函数看它基于哪个字段。通常是link或id。# 在脚本中搜索类似这样的模式 jq [...] | group_by(.link) | map(.[0]) | ...如果基于link去重不理想可以考虑使用更稳定的标识符或者组合多个字段。使用title但标题可能被修改如添加“- 更新”。使用link的主机名和路径部分用jq的split和sub函数清洗掉查询参数。def clean_link: .link | split(?)[0]; # 去掉?后面的查询参数 map(.) | group_by(clean_link) | map(.[0])使用title和feed_title的组合作为内容的指纹。def dedupe_key: .feed_title :: .title; map(.) | group_by(dedupe_key) | map(.[0])状态文件如果脚本使用状态文件记录已读条目请确认其位置如~/.cache/rss-digest/seen.log。如果发现摘要不再更新新内容可能是状态文件过大或逻辑有误。可以尝试临时删除状态文件或使用--dry-run忽略它来测试。4.3 常见问题与解决方案速查表问题现象可能原因排查步骤与解决方案运行脚本报错blogwatcher: command not found1.blogwatcher未安装。2. 未正确设置PATH或BLOGWATCHER_CMD环境变量。1. 终端执行which blogwatcher确认安装位置。2. 在脚本中或执行前通过export BLOGWATCHER_CMD/full/path/to/blogwatcher指定绝对路径。脚本执行无输出或输出为空1.blogwatcher配置错误或订阅源无更新。2.jq过滤器过于严格过滤掉了所有条目。3. 网络问题导致抓取失败。1. 单独运行blogwatcher看是否有JSON输出。2. 逐步调试先运行blogwatcher | jq .[0]查看单个条目原始数据。3. 检查网络连接和订阅源URL是否有效。分类结果不准确大量条目归为“General”分类规则关键词未覆盖你的订阅内容。1. 查看原始条目数据中的title、categories字段提取高频词。2. 按照4.1节方法在脚本的jq分类函数中添加或修改关键词规则。摘要中出现重复条目去重逻辑失效。可能因为1. 去重键如link不稳定。2. 状态文件损坏或未生效。1. 使用--dry-run模式运行检查去重前的原始数据量。2. 检查脚本中的去重jq逻辑确认分组字段。3. 检查状态文件路径和读写权限。性能慢处理大量订阅源时卡顿1. 未使用优化后的脚本版本。2. 订阅源过多或单个源条目过多。3. 系统资源不足。1. 确保使用的是最新版脚本利用了INDEX等优化。2. 在blogwatcher配置中调整interval或使用MAX_ITEMS限制。3. 考虑分批处理将订阅源分到多个blogwatcher配置文件中分别生成摘要。输出格式不符合预期脚本的文本格式化模板被修改或损坏。1. 查看脚本中负责生成最终文本输出的jq或awk命令段。2. 对比项目仓库的原始版本恢复格式化逻辑。集成到OpenClaw后不触发OpenClaw技能配置错误。1. 确认技能路径在OpenClaw的扫描范围内。2. 检查OpenClaw的技能配置文件确保触发器intent和脚本执行命令正确匹配。3. 查看OpenClaw的日志文件获取错误信息。4.4 扩展功能思路基础功能稳定后你可以考虑以下扩展让这个摘要工具更加强大摘要内容提炼集成AI摘要功能。将抓取到的文章链接和正文如果blogwatcher支持获取全文或摘要发送到本地或云端的LLM API如Ollama、OpenAI API生成一段简要总结附在条目后面。多输出格式除了文本和JSON可以增加Markdown文件输出方便直接粘贴到笔记软件如Obsidian、Logseq中。甚至可以生成HTML片段嵌入个人仪表盘。智能优先级排序不仅仅是按时间排序。可以结合来源网站的信誉度、历史点击率如果记录的话、关键词匹配度你特别关注的领域进行加权排序把最重要的信息排在最前面。离线阅读包生成配合pandoc或readability类工具将摘要中的链接对应的文章正文抓取下来生成为一个EPUB或PDF文件供通勤时离线阅读。健康度监控在脚本中添加一个环节检查各个订阅源的可用性是否返回错误、是否长期未更新并生成一个报告提醒你清理“僵尸”订阅。这个项目的魅力在于其简洁性和可扩展性。它用最小的依赖blogwatcherjqbash解决了一个实际痛点并且每一个环节都清晰可见、易于修改。你可以根据自己的需求把它打磨成完全符合个人信息摄入习惯的利器。从我自己的使用经验来看一旦配置妥当它就像是一个默默工作的信息管家每天准时把精选、去重、分类好的内容推送到你面前极大地提升了信息获取的效率和质量。