添加按订阅导出文章的参数设置
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: blogwatcher-daily
|
name: blogwatcher-daily
|
||||||
description: RSS 订阅监控 + 文章管理 + 导出。使用 RSSHub + feedparser 抓取订阅,自动去重入库 SQLite;支持按单个订阅更新/查看/标记已读/删除文章,可将文章按 txt/markdown/html 导出。附「自然语言操作指南」,AI 智能体可直接把用户口语映射到 CLI 命令。
|
description: RSS 订阅监控 + 文章管理 + 导出。使用 RSSHub + feedparser 抓取订阅,自动去重入库 SQLite;支持按单个订阅更新/查看/标记已读/删除文章,可将文章按 txt/markdown/html 导出。附「自然语言操作指南」,AI 智能体可直接把用户口语映射到 CLI 命令。
|
||||||
version: 1.4
|
version: 1.5
|
||||||
category: custom
|
category: custom
|
||||||
tags: [rss, blog, monitoring, automation, export]
|
tags: [rss, blog, monitoring, automation, export]
|
||||||
metadata:
|
metadata:
|
||||||
@@ -219,9 +219,13 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
|
|||||||
# 全库最新 20 篇 → 纯文本
|
# 全库最新 20 篇 → 纯文本
|
||||||
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
|
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
|
||||||
--export txt --limit 20
|
--export txt --limit 20
|
||||||
|
|
||||||
|
# 单订阅最新 20 篇 → Markdown(按订阅名或 --list 序号)
|
||||||
|
python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
|
||||||
|
--export markdown --sub "异次元软件世界" --limit 20
|
||||||
```
|
```
|
||||||
|
|
||||||
**过滤规则**(`--date` 与 `--limit` 的组合语义)
|
**过滤规则**(`--date` / `--limit` / `--sub` 的组合语义)
|
||||||
|
|
||||||
| 参数组合 | 行为 |
|
| 参数组合 | 行为 |
|
||||||
|---------|------|
|
|---------|------|
|
||||||
@@ -229,6 +233,12 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
|
|||||||
| `--export FMT --date X` | 指定日期 X 的所有文章 |
|
| `--export FMT --date X` | 指定日期 X 的所有文章 |
|
||||||
| `--export FMT --limit N` | 忽略日期,取**全库最新 N 篇** |
|
| `--export FMT --limit N` | 忽略日期,取**全库最新 N 篇** |
|
||||||
| `--export FMT --date X --limit N` | 日期 X 的最新 N 篇 |
|
| `--export FMT --date X --limit N` | 日期 X 的最新 N 篇 |
|
||||||
|
| `--export FMT --sub SUB` | 该订阅**全部**文章(`--sub` 也会让默认日期失效) |
|
||||||
|
| `--export FMT --sub SUB --limit N` | 该订阅最新 N 篇 |
|
||||||
|
| `--export FMT --sub SUB --date X` | 该订阅在日期 X 的文章 |
|
||||||
|
| `--export FMT --sub SUB --date X --limit N` | 该订阅在日期 X 的最新 N 篇 |
|
||||||
|
|
||||||
|
> `--sub` 的 SUB 参数解析规则与 `--articles / --update / --mark-read / --delete` 完全一致(序号 → 精确 → 忽略大小写精确 → 忽略大小写子串),详见「订阅标识(SUB)」。
|
||||||
|
|
||||||
**参数速查**
|
**参数速查**
|
||||||
|
|
||||||
@@ -237,6 +247,7 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py \
|
|||||||
| `--export {txt,markdown,html}` | 触发导出模式并指定格式 |
|
| `--export {txt,markdown,html}` | 触发导出模式并指定格式 |
|
||||||
| `--date YYYY-MM-DD` | 目标日期(本地时区,默认今天) |
|
| `--date YYYY-MM-DD` | 目标日期(本地时区,默认今天) |
|
||||||
| `--limit N` | 文章数上限;单独使用时忽略日期 |
|
| `--limit N` | 文章数上限;单独使用时忽略日期 |
|
||||||
|
| `--sub SUB` | 只导出该订阅的文章(SUB=`--list` 序号或名称) |
|
||||||
|
|
||||||
**输出去向**
|
**输出去向**
|
||||||
|
|
||||||
@@ -315,6 +326,9 @@ python3 ~/.hermes/skills/custom/blogwatcher-daily/scripts/blogwatcher-daily.py
|
|||||||
| "把今天的文章导出成 markdown"、"生成今日摘要 md" | `--export markdown` |
|
| "把今天的文章导出成 markdown"、"生成今日摘要 md" | `--export markdown` |
|
||||||
| "导出 2026-08-28 的 HTML"、"我要那天的网页版" | `--export html --date 2026-08-28` |
|
| "导出 2026-08-28 的 HTML"、"我要那天的网页版" | `--export html --date 2026-08-28` |
|
||||||
| "最新 20 篇导出为 txt"、"给我全库最新 20 篇" | `--export txt --limit 20` |
|
| "最新 20 篇导出为 txt"、"给我全库最新 20 篇" | `--export txt --limit 20` |
|
||||||
|
| "把异次元软件世界的文章导成 markdown"、"只导 Engadget 的" | `--export markdown --sub "SUB"` |
|
||||||
|
| "导出异次元最新 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` |
|
| "今天的文章导 html 到桌面" | `--export html > ~/Desktop/$(date +%Y-%m-%d).html` |
|
||||||
|
|
||||||
### 订阅标识(SUB)如何解析
|
### 订阅标识(SUB)如何解析
|
||||||
@@ -427,7 +441,7 @@ cronjob --create \
|
|||||||
|
|
||||||
### 导出(`--export`)
|
### 导出(`--export`)
|
||||||
|
|
||||||
1. **查询**:`fetch_articles()` 按 `date(fetched_at, 'localtime')` 过滤 + 可选 `LIMIT N`
|
1. **查询**:`fetch_articles()` 按 `date(fetched_at, 'localtime')` 过滤 + 可选 `feed_url` 按订阅过滤(`--sub`)+ 可选 `LIMIT N`
|
||||||
2. **分组**:`_group_by_channel()` 按 `channel_title` 分桶
|
2. **分组**:`_group_by_channel()` 按 `channel_title` 分桶
|
||||||
3. **格式化**:`FORMATTERS[fmt]` 分发到 `format_txt` / `format_markdown` / `format_html`
|
3. **格式化**:`FORMATTERS[fmt]` 分发到 `format_txt` / `format_markdown` / `format_html`
|
||||||
4. **输出**:正文写 stdout,`📊 N 篇文章 → FMT` 进度写 stderr
|
4. **输出**:正文写 stdout,`📊 N 篇文章 → FMT` 进度写 stderr
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ Usage:
|
|||||||
python3 blogwatcher-daily.py --export markdown # 把今天的文章导出为 Markdown
|
python3 blogwatcher-daily.py --export markdown # 把今天的文章导出为 Markdown
|
||||||
python3 blogwatcher-daily.py --export html --date 2026-08-28
|
python3 blogwatcher-daily.py --export html --date 2026-08-28
|
||||||
python3 blogwatcher-daily.py --export txt --limit 20
|
python3 blogwatcher-daily.py --export txt --limit 20
|
||||||
|
python3 blogwatcher-daily.py --export markdown --sub "异次元软件世界" --limit 20 # 单订阅最新 20 篇
|
||||||
|
|
||||||
订阅标识(SUB):
|
订阅标识(SUB):
|
||||||
可传 --list 输出中的整数序号(如 3),也可传订阅名(精确 → 忽略大小写精确 → 忽略大小写包含)。
|
可传 --list 输出中的整数序号(如 3),也可传订阅名(精确 → 忽略大小写精确 → 忽略大小写包含)。
|
||||||
@@ -437,8 +438,9 @@ def delete_articles_for_subscription(sub, article_index=None):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# ========== 导出 ==========
|
# ========== 导出 ==========
|
||||||
def fetch_articles(target_date=None, limit=None):
|
def fetch_articles(target_date=None, limit=None, feed_url=None):
|
||||||
"""从 articles 表读取;可选按本地日期过滤(date(fetched_at,'localtime'))+ 可选限制数量"""
|
"""从 articles 表读取;可选按本地日期过滤(date(fetched_at,'localtime'))
|
||||||
|
+ 可选按订阅 feed_url 过滤 + 可选限制数量。"""
|
||||||
if not os.path.exists(DB_PATH):
|
if not os.path.exists(DB_PATH):
|
||||||
print(f"❌ 数据库不存在: {DB_PATH}", file=sys.stderr)
|
print(f"❌ 数据库不存在: {DB_PATH}", file=sys.stderr)
|
||||||
return []
|
return []
|
||||||
@@ -446,11 +448,15 @@ def fetch_articles(target_date=None, limit=None):
|
|||||||
conn = sqlite3.connect(DB_PATH)
|
conn = sqlite3.connect(DB_PATH)
|
||||||
conn.row_factory = sqlite3.Row
|
conn.row_factory = sqlite3.Row
|
||||||
|
|
||||||
where = ""
|
conditions = []
|
||||||
params = []
|
params = []
|
||||||
if target_date:
|
if target_date:
|
||||||
where = "WHERE date(fetched_at, 'localtime') = ?"
|
conditions.append("date(fetched_at, 'localtime') = ?")
|
||||||
params.append(target_date)
|
params.append(target_date)
|
||||||
|
if feed_url:
|
||||||
|
conditions.append("feed_url = ?")
|
||||||
|
params.append(feed_url)
|
||||||
|
where = ("WHERE " + " AND ".join(conditions)) if conditions else ""
|
||||||
|
|
||||||
query = f"""
|
query = f"""
|
||||||
SELECT channel_title, title, link, description, pub_date, fetched_at
|
SELECT channel_title, title, link, description, pub_date, fetched_at
|
||||||
@@ -550,14 +556,21 @@ FORMATTERS = {
|
|||||||
"html": format_html,
|
"html": format_html,
|
||||||
}
|
}
|
||||||
|
|
||||||
def export_articles(fmt, target_date, limit):
|
def export_articles(fmt, target_date, limit, sub=None):
|
||||||
articles = fetch_articles(target_date, limit)
|
feed_url = sub['url'] if sub else None
|
||||||
|
articles = fetch_articles(target_date, limit, feed_url=feed_url)
|
||||||
|
|
||||||
|
label_parts = []
|
||||||
|
if sub:
|
||||||
|
label_parts.append(sub['name'])
|
||||||
if target_date:
|
if target_date:
|
||||||
label = target_date
|
label_parts.append(target_date)
|
||||||
elif limit:
|
if limit:
|
||||||
label = f"最新 {limit} 篇"
|
label_parts.append(f"最新 {limit} 篇")
|
||||||
else:
|
if not label_parts:
|
||||||
label = "全部"
|
label_parts.append("全部")
|
||||||
|
label = " — ".join(label_parts)
|
||||||
|
|
||||||
print(f"📊 {len(articles)} 篇文章 → {fmt}", file=sys.stderr)
|
print(f"📊 {len(articles)} 篇文章 → {fmt}", file=sys.stderr)
|
||||||
sys.stdout.write(FORMATTERS[fmt](articles, label))
|
sys.stdout.write(FORMATTERS[fmt](articles, label))
|
||||||
|
|
||||||
@@ -585,6 +598,8 @@ def main():
|
|||||||
parser.add_argument('--date', help='导出的目标日期 YYYY-MM-DD(默认今天,仅 --export 有效)')
|
parser.add_argument('--date', help='导出的目标日期 YYYY-MM-DD(默认今天,仅 --export 有效)')
|
||||||
parser.add_argument('--limit', type=int,
|
parser.add_argument('--limit', type=int,
|
||||||
help='导出的文章数上限;单独使用时忽略日期取全库最新 N 篇(仅 --export 有效)')
|
help='导出的文章数上限;单独使用时忽略日期取全库最新 N 篇(仅 --export 有效)')
|
||||||
|
parser.add_argument('--sub', metavar='SUB',
|
||||||
|
help='与 --export 搭配:只导出指定订阅的文章(SUB=--list 序号或名称)')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -592,6 +607,8 @@ def main():
|
|||||||
parser.error("--unread 必须与 --articles 一起使用")
|
parser.error("--unread 必须与 --articles 一起使用")
|
||||||
if args.article_id is not None and not args.delete:
|
if args.article_id is not None and not args.delete:
|
||||||
parser.error("--article-id 必须与 --delete 一起使用")
|
parser.error("--article-id 必须与 --delete 一起使用")
|
||||||
|
if args.sub and not args.export:
|
||||||
|
parser.error("--sub 必须与 --export 一起使用")
|
||||||
|
|
||||||
global RSSHUB_BASE
|
global RSSHUB_BASE
|
||||||
if args.rsshub:
|
if args.rsshub:
|
||||||
@@ -642,13 +659,18 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
elif args.export:
|
elif args.export:
|
||||||
|
sub = None
|
||||||
|
if args.sub:
|
||||||
|
sub = find_subscription(args.sub)
|
||||||
|
if not sub:
|
||||||
|
sys.exit(1)
|
||||||
if args.date:
|
if args.date:
|
||||||
target_date = args.date
|
target_date = args.date
|
||||||
elif args.limit:
|
elif args.limit or sub:
|
||||||
target_date = None
|
target_date = None
|
||||||
else:
|
else:
|
||||||
target_date = date.today().isoformat()
|
target_date = date.today().isoformat()
|
||||||
export_articles(args.export, target_date, args.limit)
|
export_articles(args.export, target_date, args.limit, sub=sub)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
scan_all(force_all=args.all)
|
scan_all(force_all=args.all)
|
||||||
|
|||||||
Reference in New Issue
Block a user