添加订阅分类,支持分类导出

This commit is contained in:
2026-09-01 19:11:13 +08:00
parent ed5340d7e0
commit a07b9302ab
3 changed files with 270 additions and 91 deletions

View File

@@ -1,12 +1,12 @@
---
name: blogwatcher-daily
description: RSS 订阅监控 + 文章管理 + 导出。使用 RSSHub + feedparser 抓取订阅,自动去重入库 SQLite;支持按单个订阅更新/查看/标记已读/删除文章,可将文章按 txt/markdown/html 导出。附「自然语言操作指南」,AI 智能体可直接把用户口语映射到 CLI 命令。
version: 1.5
description: RSS 订阅监控 + 文章管理 + 分类导出。使用 RSSHub + feedparser 抓取订阅,自动去重入库 SQLite;支持按单个订阅或分类更新/查看/标记已读/删除文章,可将文章按 txt/markdown/html 导出。附「自然语言操作指南」,AI 智能体可直接把用户口语映射到 CLI 命令。
version: 1.6
category: custom
tags: [rss, blog, monitoring, automation, export]
tags: [rss, blog, monitoring, automation, export, category]
metadata:
author: Hermes Agent
last_updated: 2026-08-30
last_updated: 2026-09-01
platform: macos, ubuntu
custom_skill_path: /Users/weishen/.hermes/skills/custom/
installation_note: "blogwatcher-daily 脚本在 Mac mini 本地运行(依赖 feedparser)。需要 RSSHub 服务(http://192.168.3.45:1200)访问 YouTube/Bilibili 等被墙源。"
@@ -55,10 +55,34 @@ curl → 目标网站
├── SKILL.md # 本文件
├── scripts/
│ └── blogwatcher-daily.py # 扫描入库 + 订阅管理 + 文章导出(单文件)
├── subscriptions.txt # 订阅列表(name|URL)
├── subscriptions.txt # 订阅列表(分类|name|URL)
└── blogwatcher.db # SQLite 数据库(自动创建)
```
## 订阅文件格式(subscriptions.txt)
```
分类|频道名|URL[|enabled]
```
- **新格式(推荐)**:`AI技术|Tech With Tim|http://192.168.3.45:1200/youtube/channel/UC4JX40jDee_tINbkjycV4Sg`
- **旧格式(仍兼容)**:`频道名|URL`(读入时自动归为 `未分类`,无需手动迁移)
- `#` 开头的行视为注释
- 判定规则:若 `parts[1]` 以 `http://` / `https://` 开头,视为旧格式;否则新格式
示例(同一文件可混用):
```
# 新格式
AI技术|Tech With Tim|http://192.168.3.45:1200/youtube/channel/UC4JX40jDee_tINbkjycV4Sg
AI技术|灵姐说AI|http://192.168.3.45:1200/youtube/channel/UCMenHpvUet8myDntrTh7Ckw
电子书|SaltTiger|https://salttiger.com/feed/
新闻热点|BBC中文网|http://192.168.3.45:1200/youtube/channel/UCb3TZ4SD_Ys3j4z0-8o6auA
# 旧格式(自动归为「未分类」)
Jon Law|http://192.168.3.45:1200/youtube/channel/UCQM_HxoKmza1simMUchYfVA
```
## 使用方法
### 扫描订阅(默认)
@@ -74,17 +98,20 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py
```bash
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
--add "频道名" "URL"
--add "频道名" "URL" [--category "分类"]
```
- 省略 `--category` 时,分类默认为 `未分类`
- 分类是任意字符串(中英文皆可),后续可通过手工编辑 `subscriptions.txt` 调整
**YouTube 频道自动转换**:直接贴 YouTube 频道 URL 或 feed URL,
脚本自动识别并转为 RSSHub 格式,无需手动拼接。
```bash
# 以下三种方式效果相同:
--add "Tech With Tim" "https://www.youtube.com/channel/UC4JX40jDee_tINbkjycV4Sg"
--add "Tech With Tim" "https://www.youtube.com/feeds/videos.xml?channel_id=UC4JX40jDee_tINbkjycV4Sg"
--add "Tech With Tim" "http://192.168.3.45:1200/youtube/channel/UC4JX40jDee_tINbkjycV4Sg"
--add "Tech With Tim" "https://www.youtube.com/channel/UC4JX40jDee_tINbkjycV4Sg" --category "AI技术"
--add "Tech With Tim" "https://www.youtube.com/feeds/videos.xml?channel_id=UC4JX40jDee_tINbkjycV4Sg" --category "AI技术"
--add "Tech With Tim" "http://192.168.3.45:1200/youtube/channel/UC4JX40jDee_tINbkjycV4Sg" --category "AI技术"
```
### 列出订阅
@@ -93,6 +120,22 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py --list
```
输出**按分类分组**,序号 `[N]` 是**全局 1-based**(跨分类连续),可直接用于 `--update N` / `--articles N` / `--sub N` 等命令:
```
📡 当前订阅 (37 个 · 9 个分类):
── 【AI技术】(7 个) ──
[1] Tech With Tim
http://192.168.3.45:1200/youtube/channel/UC4JX40jDee_tINbkjycV4Sg
[3] 小白AI笔记
...
── 【电子书】(1 个) ──
[25] SaltTiger
https://salttiger.com/feed/
```
### 强制回扫(`--all`)
```bash
@@ -223,9 +266,21 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
# 单订阅最新 20 篇 → Markdown(按订阅名或 --list 序号)
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
--export markdown --sub "异次元软件世界" --limit 20
# 【分类导出】某分类下所有订阅的全部文章
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
--export markdown --category "AI技术"
# 【分类导出】某分类下所有订阅今日文章
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
--export markdown --category "AI技术" --date $(date +%F)
# 【分类导出】某分类下所有订阅最新 30 篇
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
--export html --category "新闻热点" --limit 30 > news.html
```
**过滤规则**(`--date` / `--limit` / `--sub` 的组合语义)
**过滤规则**(`--date` / `--limit` / `--sub` / `--category` 的组合语义)
| 参数组合 | 行为 |
|---------|------|
@@ -237,8 +292,16 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
| `--export FMT --sub SUB --limit N` | 该订阅最新 N 篇 |
| `--export FMT --sub SUB --date X` | 该订阅在日期 X 的文章 |
| `--export FMT --sub SUB --date X --limit N` | 该订阅在日期 X 的最新 N 篇 |
| `--export FMT --category C` | 该分类下**所有订阅的全部**文章(分类过滤同样让默认日期失效) |
| `--export FMT --category C --date X` | 该分类下所有订阅在日期 X 的文章 |
| `--export FMT --category C --limit N` | 该分类下所有订阅的最新 N 篇 |
| `--export FMT --category C --date X --limit N` | 该分类下在日期 X 的最新 N 篇 |
> `--sub` 的 SUB 参数解析规则与 `--articles / --update / --mark-read / --delete` 完全一致(序号 → 精确 → 忽略大小写精确 → 忽略大小写子串),详见「订阅标识(SUB)」。
> `--sub` 和 `--category` **互斥**(前者指定单个订阅,后者指定一组)。
>
> `--sub` 的 SUB 参数解析规则:序号 → 精确 → 忽略大小写精确 → 忽略大小写子串,详见「订阅标识(SUB)」。
>
> `--category` 的分类参数解析规则:精确 → 忽略大小写精确 → 忽略大小写子串(子串匹配跨多个分类时会打印警告,但仍会返回全部命中的订阅)。
**参数速查**
@@ -248,6 +311,7 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
| `--date YYYY-MM-DD` | 目标日期(本地时区,默认今天) |
| `--limit N` | 文章数上限;单独使用时忽略日期 |
| `--sub SUB` | 只导出该订阅的文章(SUB=`--list` 序号或名称) |
| `--category CATEGORY` | 只导出该分类下所有订阅的文章(与 `--sub` 互斥) |
**输出去向**
@@ -316,8 +380,10 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py
| 用户可能说 | 命令 |
|---|---|
| "订阅 X"、"添加频道 X"、"加个 RSS"、"帮我关注 X"(附 URL) | `--add "NAME" "URL"` |
| "订阅 X 到 AI 分类"、"把 X 加到新闻热点"、"关注 X(分类 = Y)" | `--add "NAME" "URL" --category "Y"` |
- YouTube URL、RSSHub URL、原生 RSS URL 都直接传,脚本内部自动路由。
- 未指定分类时默认归为 `未分类`,事后可手工编辑 `subscriptions.txt` 调整。
### 7. 导出
@@ -330,6 +396,13 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py
| "导出异次元最新 20 篇 md"、"给我 Slashdot 最近 20 条 html" | `--export markdown --sub "SUB" --limit 20` |
| "把 X 昨天的文章导出成 html" | `--export html --sub "SUB" --date YYYY-MM-DD` |
| "今天的文章导 html 到桌面" | `--export html > ~/Desktop/$(date +%Y-%m-%d).html` |
| "导出所有 AI 技术频道的文章"、"AI 分类全部导出"、"给我看看 AI 分类的东西" | `--export markdown --category "AI技术"` |
| "导出所有 AI 技术频道今日的文章"、"AI 分类今天的" | `--export markdown --category "AI技术" --date $(date +%F)` |
| "AI 分类最新 30 篇"、"电子书类最新 10 篇" | `--export markdown --category "AI技术" --limit 30` |
| "把新闻热点导成 html 存桌面" | `--export html --category "新闻热点" > ~/Desktop/news-$(date +%Y-%m-%d).html` |
> AI 智能体判断"分类"意图的关键词:**分类 / 类别 / 类型 / 归类 / 一类 / 那类 / category**。
> 常见分类举例:`AI技术` `软件工具` `新闻热点` `科技资讯` `技术博客` `电子书` `学习教育` `生活方式` `未分类`。用户口语中说"AI 类"通常指分类名 `AI技术`,说"新闻类"通常指 `新闻热点`。若不确定,先跑一次 `--list` 让用户/自己看清可用分类,再定夺。
### 订阅标识(SUB)如何解析
@@ -350,10 +423,11 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py
1. **看/查询类**(无副作用):`--list`、`--articles`。可直接执行。
2. **抓取类**(可能改 DB):`--update`、无参扫描、`--all`。安全,直接跑。
3. **写入类**(会改 DB):`--mark-read`、`--add`。可直接执行,但结束后简报结果。
3. **写入类**(会改 DB):`--mark-read`、`--add`(含 `--category`)。可直接执行,但结束后简报结果。
4. **删除类**(不可逆):`--delete`。**先展示要删的内容再执行**,除非用户在同一轮明确肯定。
5. **导出类**(只读):`--export`。stdout 输出,AI 应帮用户决定重定向到哪个文件。
5. **导出类**(只读):`--export`(含 `--sub` / `--category`)。stdout 输出,AI 应帮用户决定重定向到哪个文件。
6. **SUB 消歧**:能用序号就优先用序号(`--list` 是廉价的),避免多义。
7. **分类消歧**:用户说"AI 类的"、"新闻类的"这种口语,先用 `--list` 拉一下可用分类,再选最匹配的作为 `--category` 值;不确定就回问用户。
## 添加订阅示例
@@ -361,23 +435,28 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py
```bash
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
--add "Tech With Tim" "http://192.168.3.45:1200/youtube/channel/UC4JX40jDee_tINbkjycV4Sg"
--add "Tech With Tim" "http://192.168.3.45:1200/youtube/channel/UC4JX40jDee_tINbkjycV4Sg" \
--category "AI技术"
```
### Bilibili
```bash
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
--add "B站频道" "http://192.168.3.45:1200/bilibili/user/{uid}"
--add "B站频道" "http://192.168.3.45:1200/bilibili/user/{uid}" \
--category "生活方式"
```
### 普通 RSS
```bash
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
--add "博客名" "http://192.168.3.45:1200/rss/https://example.com/feed.xml"
--add "博客名" "http://192.168.3.45:1200/rss/https://example.com/feed.xml" \
--category "技术博客"
```
> 省略 `--category` 时新订阅会归入「未分类」,之后可手工编辑 `subscriptions.txt` 调整分类。
## RSSHub vs 直接 RSS
脚本自动判断路由,不需要手动选择:
@@ -428,7 +507,7 @@ cronjob --create \
### 扫描入库(默认命令)
1. **加载订阅**:从 `subscriptions.txt` 读取所有 name|URL 对
1. **加载订阅**:从 `subscriptions.txt` 读取所有条目(`分类|name|URL` 新格式,或 `name|URL` 旧格式→归为「未分类」)
2. **URL 路由**:`build_fetch_url()` 自动判断:
- YouTube → 转为 `http://192.168.3.45:1200/youtube/channel/{id}`
- RSSHub URL → 直接使用
@@ -441,7 +520,7 @@ cronjob --create \
### 导出(`--export`)
1. **查询**:`fetch_articles()` 按 `date(fetched_at, 'localtime')` 过滤 + 可选 `feed_url` 按订阅过滤(`--sub`)+ 可选 `LIMIT N`
1. **查询**:`fetch_articles()` 按 `date(fetched_at, 'localtime')` 过滤 + 可选 `feed_url` 按单订阅过滤(`--sub`)+ 可选 `feed_url IN (...)` 按分类内多订阅过滤(`--category`)+ 可选 `LIMIT N`
2. **分组**:`_group_by_channel()` 按 `channel_title` 分桶
3. **格式化**:`FORMATTERS[fmt]` 分发到 `format_txt` / `format_markdown` / `format_html`
4. **输出**:正文写 stdout,`📊 N 篇文章 → FMT` 进度写 stderr