Back to skills
SkillHub ClubShip Full StackFull StackBackend

rss-to-wechat

将 RSS 文章转换为微信公众号格式。适用于:发布 RSS/博客文章到微信、格式化内容以符合微信 API、自动化微信内容发布。

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,106
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
C62.8

Install command

npx @skill-hub/cli install openclaw-skills-rss-to-wechat

Repository

openclaw/skills

Skill path: skills/huangbaixun/rss-to-wechat

将 RSS 文章转换为微信公众号格式。适用于:发布 RSS/博客文章到微信、格式化内容以符合微信 API、自动化微信内容发布。

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack, Backend.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install rss-to-wechat into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding rss-to-wechat to shared team environments
  • Use rss-to-wechat for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: rss-to-wechat
description: 将 RSS 文章转换为微信公众号格式。适用于:发布 RSS/博客文章到微信、格式化内容以符合微信 API、自动化微信内容发布。
---

# RSS to WeChat | RSS 转微信公众号

将 RSS 文章或任何网页内容转换为微信公众号兼容的 HTML 格式。

## 快速开始

```bash
# 检查配置和依赖
bash scripts/rss-to-wechat.sh --check

# 处理文章
bash scripts/rss-to-wechat.sh --url "https://example.com/article"

# 自动选择最新文章(需要 blogwatcher)
bash scripts/rss-to-wechat.sh --auto
```

## 配置

首次使用:

```bash
# 复制配置示例
cp references/config.example.sh config.local.sh

# 编辑配置
nano config.local.sh
```

最小配置:

```bash
WECHAT_APPID="你的AppID"
WECHAT_APPSECRET="你的AppSecret"
BRAND_NAME="你的品牌名称"
```

## 工作流程

1. **数据准备**(自动)
   - 脚本获取并解析文章
   - 提取标题、作者、内容
   - 保存为 JSON

2. **HTML 生成**(AI 辅助)
   - AI 助手生成微信兼容的 HTML
   - 使用品牌配置
   - 遵循严格的格式要求(见 `references/html-template.md`)

3. **封面生成**(可选)
   - 如果配置了 `COVER_SKILL`
   - 生成 1283×383 封面图

4. **发布**(可选)
   - 如果配置了微信凭证
   - 通过 API 上传到草稿箱

## 微信 HTML 格式要求

微信 API 对 HTML 格式有严格要求:

**必须使用:**
- `<section>` 和 `<p>` 标签(不用 `<div>`)
- 内联样式 `style="..."`
- `<strong>` 和 `<em>` 标签
- 完整 URL(不用相对链接)

**禁止使用:**
- `class` 或 `id` 属性
- 外部 CSS
- JavaScript
- 相对链接

详见 `references/html-template.md` 查看完整模板和示例。

## 脚本

所有脚本位于 `scripts/` 目录:

- `rss-to-wechat.sh` - 主入口
- `parse-article.sh` - 文章内容提取
- `format-wechat.sh` - HTML 格式化(已弃用,使用 AI 生成)
- `config.sh` - 默认配置
- `test.sh` - 依赖检查

## 参考文档

- `references/USER_GUIDE.md` - 完整用户文档
- `references/html-template.md` - 微信 HTML 模板和样式指南
- `references/config.example.sh` - 配置示例(包含所有选项)

## 依赖

**必需:**
- `curl` - HTTP 请求
- `jq` - JSON 处理
- `pandoc` - 格式转换

**可选:**
- `blogwatcher` - RSS 订阅管理(用于 --auto 模式)
- 自定义封面生成脚本
- 自定义发布脚本

## 配置选项

查看 `references/config.example.sh` 了解所有可用选项:

- RSS 源和过滤条件
- 品牌定制(名称、标语、颜色)
- 路径配置
- 外部工具集成
- 关键词过滤

## 故障排除

**错误 45166(invalid content)**
- 检查 HTML 格式是否符合要求
- 确保所有样式都是内联的
- 移除 class/id 属性
- 参考成功案例

**文章解析失败**
- 检查 URL 是否可访问
- 确认没有反爬虫措施
- 尝试手动提取内容

**配置问题**
- 运行 `bash scripts/rss-to-wechat.sh --check`
- 确认所有必需工具已安装
- 检查微信凭证

## 注意事项

- 此 skill 提供数据准备和指导
- AI 助手根据模板生成最终 HTML
- 手动 HTML 生成已弃用(因微信格式要求严格)
- 本地配置(`config.local.sh`)不会提交到 git


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### scripts/rss-to-wechat.sh

```bash
#!/bin/bash
# RSS 文章转微信公众号 - 主脚本

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

# 使用方法
usage() {
  cat << EOF
用法: $0 [选项]

选项:
  --url <URL>        指定文章 URL
  --auto             自动选择最新文章
  --config           显示配置帮助
  --check            检查配置和依赖
  --help             显示帮助信息

功能:
  1. 从 RSS 或指定 URL 获取文章
  2. 解析文章内容(标题、作者、正文)
  3. 提供 AI 助手生成微信格式 HTML 的指导
  4. 生成封面图(如果配置了 COVER_SKILL)
  5. 上传到微信草稿箱(如果配置了微信凭证)

示例:
  # 首次使用:检查配置
  $0 --check
  
  # 指定文章 URL
  $0 --url "https://simonwillison.net/2026/Mar/6/agentic-patterns/"
  
  # 自动选择最新文章(需要 blogwatcher)
  $0 --auto

配置:
  复制 config.example.sh 为 config.local.sh 并编辑你的配置。
  运行 $0 --config 查看详细说明。

EOF
  exit 1
}

# 解析参数
URL=""
AUTO=false
CHECK_ONLY=false

while [[ $# -gt 0 ]]; do
  case $1 in
    --url)
      URL="$2"
      shift 2
      ;;
    --auto)
      AUTO=true
      shift
      ;;
    --config)
      show_config_help
      exit 0
      ;;
    --check)
      CHECK_ONLY=true
      shift
      ;;
    --help)
      usage
      ;;
    *)
      error "未知参数: $1"
      usage
      ;;
  esac
done

# 检查配置和依赖
if [ "$CHECK_ONLY" = true ]; then
  log "检查配置和依赖..."
  echo ""
  
  # 检查配置文件
  if [ -f "$SCRIPT_DIR/config.local.sh" ]; then
    echo "✅ 本地配置文件存在: config.local.sh"
  else
    echo "⚠️  本地配置文件不存在"
    echo "   运行: cp config.example.sh config.local.sh"
    echo "   然后编辑 config.local.sh 设置你的配置"
  fi
  
  echo ""
  
  # 检查必需工具
  echo "必需工具:"
  for cmd in curl jq pandoc; do
    if command -v "$cmd" &> /dev/null; then
      echo "  ✅ $cmd"
    else
      echo "  ❌ $cmd (未安装)"
    fi
  done
  
  echo ""
  
  # 检查可选工具
  echo "可选工具:"
  if command -v blogwatcher &> /dev/null; then
    echo "  ✅ blogwatcher (支持 --auto 模式)"
  else
    echo "  ⚠️  blogwatcher (未安装,--auto 模式不可用)"
  fi
  
  echo ""
  
  # 检查微信配置
  echo "微信公众号配置:"
  if [ -n "$WECHAT_APPID" ] && [ "$WECHAT_APPID" != "your_appid_here" ]; then
    echo "  ✅ WECHAT_APPID: $WECHAT_APPID"
  else
    echo "  ⚠️  WECHAT_APPID 未配置"
  fi
  
  if [ -n "$WECHAT_APPSECRET" ] && [ "$WECHAT_APPSECRET" != "your_secret_here" ]; then
    echo "  ✅ WECHAT_APPSECRET: ********"
  else
    echo "  ⚠️  WECHAT_APPSECRET 未配置"
  fi
  
  if [ -n "$WECHAT_PUBLISH_SCRIPT" ] && [ -f "$WECHAT_PUBLISH_SCRIPT" ]; then
    echo "  ✅ 发布脚本: $WECHAT_PUBLISH_SCRIPT"
  else
    echo "  ⚠️  发布脚本未配置或不存在"
  fi
  
  echo ""
  
  # 检查封面生成
  echo "封面生成:"
  if [ -n "$COVER_SKILL" ] && [ -f "$COVER_SKILL" ]; then
    echo "  ✅ 封面生成脚本: $COVER_SKILL"
  else
    echo "  ⚠️  封面生成脚本未配置(将跳过封面生成)"
  fi
  
  echo ""
  
  # 检查目录
  echo "工作目录:"
  echo "  输出目录: $OUTPUT_DIR"
  echo "  草稿目录: $DRAFTS_DIR"
  
  if [ -d "$OUTPUT_DIR" ] && [ -d "$DRAFTS_DIR" ]; then
    echo "  ✅ 目录已创建"
  fi
  
  echo ""
  
  # 验证配置
  if validate_config; then
    echo "✅ 配置检查通过!"
    exit 0
  else
    echo "❌ 配置检查失败,请修复上述问题"
    exit 1
  fi
fi

# 获取北京时间
DATE=$(TZ=Asia/Shanghai date "+%Y年%m月%d日")
WEEKDAY=$(TZ=Asia/Shanghai date "+星期%u" | sed 's/1/一/;s/2/二/;s/3/三/;s/4/四/;s/5/五/;s/6/六/;s/7/日/')
TIMESTAMP=$(TZ=Asia/Shanghai date "+%Y-%m-%d")

log "📰 RSS to WeChat - $DATE $WEEKDAY"
echo ""

# 1. 获取文章
if [ "$AUTO" = true ]; then
  log "1️⃣ 自动选择最新文章..."
  # 获取最新的未读文章
  LATEST=$(blogwatcher articles | grep "^\[" | grep "\[new\]" | head -1)
  if [ -z "$LATEST" ]; then
    error "没有找到未读文章"
    exit 1
  fi
  
  # 提取 URL
  URL=$(echo "$LATEST" | grep -oE 'https?://[^ ]+')
  TITLE=$(echo "$LATEST" | sed 's/^\[[0-9]*\] \[new\] //' | sed 's/ *$//')
  
  log "选中文章: $TITLE"
  log "URL: $URL"
elif [ -n "$URL" ]; then
  log "1️⃣ 解析指定文章..."
  log "URL: $URL"
else
  error "请指定 --url 或 --auto"
  usage
fi

echo ""

# 2. 解析文章内容
log "2️⃣ 解析文章内容..."
ARTICLE_JSON="$OUTPUT_DIR/rss-article-$TIMESTAMP.json"
bash "$SCRIPT_DIR/parse-article.sh" "$URL" "$ARTICLE_JSON"

# 读取解析结果
ARTICLE_TITLE=$(jq -r '.title' "$ARTICLE_JSON")
ARTICLE_AUTHOR=$(jq -r '.author' "$ARTICLE_JSON")
ARTICLE_PUBLISHED=$(jq -r '.published' "$ARTICLE_JSON")
ARTICLE_CONTENT=$(jq -r '.content' "$ARTICLE_JSON")

log "标题: $ARTICLE_TITLE"
log "作者: $ARTICLE_AUTHOR"
log "发布: $ARTICLE_PUBLISHED"
log "内容长度: $(echo "$ARTICLE_CONTENT" | wc -c) 字符"

echo ""

# 3. 提供 AI 助手指导
log "3️⃣ 数据已准备,请使用 AI 助手生成微信格式 HTML"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 文章数据"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📄 数据文件: $ARTICLE_JSON"
echo ""
echo "📝 文章信息:"
echo "   标题: $ARTICLE_TITLE"
echo "   作者: $ARTICLE_AUTHOR"
echo "   发布: $ARTICLE_PUBLISHED"
echo "   URL: $URL"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎨 生成要求"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "1. HTML 格式要求:"
echo "   ✓ 使用 <section> 和 <p> 标签(不用 <div>)"
echo "   ✓ 所有样式内联(style=\"...\")"
echo "   ✓ 不使用 class 和 id 属性"
echo "   ✓ 不使用相对链接(<a> 标签)"
echo "   ✓ 使用 <strong> 和 <em> 而非 <span>"
echo "   ✓ 使用 <br/> 换行"
echo ""
echo "2. 内容结构:"
echo "   ✓ 头部品牌 Logo(内联 SVG)"
echo "   ✓ 日期栏($DATE $WEEKDAY)"
echo "   ✓ 标题"
echo "   ✓ 核心观点(TL;DR)"
echo "   ✓ 详细内容(分段落)"
echo "   ✓ 原文链接"
echo ""
echo "3. 品牌配置:"
echo "   名称: $BRAND_NAME"
echo "   Slogan: $BRAND_SLOGAN"
echo "   主色: $BRAND_COLOR"
echo ""
echo "4. 参考模板:"
echo "   $SCRIPT_DIR/html-template.md"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "💾 输出文件"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "HTML: $DRAFTS_DIR/rss-$TIMESTAMP.html"
if [ -n "$COVER_SKILL" ]; then
  echo "封面: $DRAFTS_DIR/rss-$TIMESTAMP-cover.png"
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 后续步骤"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "1. 生成 HTML(AI 助手):"
echo "   根据上述要求和参考模板,生成微信格式 HTML"
echo "   保存到: $DRAFTS_DIR/rss-$TIMESTAMP.html"
echo ""

# 封面生成步骤(如果配置了)
if [ -n "$COVER_SKILL" ] && [ -f "$COVER_SKILL" ]; then
  echo "2. 生成封面:"
  echo "   bash \"$COVER_SKILL\" \\"
  echo "     \"$ARTICLE_TITLE\" \\"
  echo "     \"$DRAFTS_DIR/rss-$TIMESTAMP-cover.png\""
  echo ""
  STEP_NUM=3
else
  warn "封面生成脚本未配置,跳过封面生成步骤"
  echo "2. 封面生成: 跳过(未配置 COVER_SKILL)"
  echo ""
  STEP_NUM=3
fi

# 发布步骤(如果配置了)
if [ -n "$WECHAT_PUBLISH_SCRIPT" ] && [ -f "$WECHAT_PUBLISH_SCRIPT" ]; then
  if [ -n "$WECHAT_APPID" ] && [ -n "$WECHAT_APPSECRET" ]; then
    echo "$STEP_NUM. 上传草稿:"
    echo "   bash \"$WECHAT_PUBLISH_SCRIPT\" \\"
    echo "     \"$ARTICLE_TITLE\" \\"
    echo "     \"$DRAFTS_DIR/rss-$TIMESTAMP.html\" \\"
    if [ -n "$COVER_SKILL" ]; then
      echo "     \"$DRAFTS_DIR/rss-$TIMESTAMP-cover.png\""
    else
      echo "     # 封面路径(如果有)"
    fi
    echo ""
  else
    warn "微信公众号配置不完整,跳过发布步骤"
    echo "$STEP_NUM. 上传草稿: 跳过(微信配置不完整)"
    echo "   请在 config.local.sh 中设置 WECHAT_APPID 和 WECHAT_APPSECRET"
    echo ""
  fi
else
  echo "$STEP_NUM. 上传草稿: 手动发布"
  echo "   登录微信公众平台: https://mp.weixin.qq.com"
  echo "   内容管理 → 草稿箱 → 新建图文 → 粘贴 HTML"
  echo ""
fi

echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "✅ 数据准备完成!"

```

### references/config.example.sh

```bash
#!/bin/bash
# 配置示例文件
# 复制此文件为 config.local.sh 并修改为你的配置

# ============ 微信公众号配置(必需)============
# 从微信公众平台获取: https://mp.weixin.qq.com
# 设置与开发 → 基本配置 → 开发者ID(AppID) 和 开发者密码(AppSecret)
WECHAT_APPID="your_appid_here"
WECHAT_APPSECRET="your_secret_here"

# ============ 品牌配置 ============
BRAND_NAME="你的公众号名称"
BRAND_SLOGAN="你的 Slogan"
BRAND_COLOR="#c41e3a"  # 主色调(十六进制颜色)

# ============ RSS 订阅源(可选)============
# 你关注的博客或网站
RSS_SOURCES=(
  "example.com"
  "another-blog.com"
)

# ============ 筛选条件(可选)============
MIN_VIEWS_DAILY=10000       # 日报最低观看量
MAX_VIEWS_DAILY=100000      # 日报最高观看量
MIN_VIEWS_FEATURED=100000   # 精选最低观看量

# ============ 主题关键词(可选)============
# 优先级关键词(匹配这些的文章优先推荐)
PRIORITY_KEYWORDS=(
  "AI"
  "Machine Learning"
  "Technology"
)

# 通用关键词
GENERAL_KEYWORDS=(
  "programming"
  "software"
  "development"
)

# 排除关键词(包含这些的文章会被过滤)
EXCLUDE_KEYWORDS=(
  "crypto"
  "blockchain"
)

# ============ 路径配置(可选)============
# 如果不设置,将使用默认路径
# WORKSPACE="$HOME/my-workspace"
# OUTPUT_DIR="$WORKSPACE/output"
# DRAFTS_DIR="$WORKSPACE/drafts"

# ============ 外部工具(可选)============
# 如果你有自己的封面生成脚本
# COVER_SKILL="/path/to/your/cover-generator.sh"

# 如果你有自己的发布脚本
# WECHAT_PUBLISH_SCRIPT="/path/to/your/publish-script.sh"

# ============ 时区(可选)============
# TZ="Asia/Shanghai"  # 默认:Asia/Shanghai

# ============ 调试模式(可选)============
# DEBUG=1  # 启用详细日志

```

### references/html-template.md

```markdown
# RSS to WeChat HTML 模板

## 基础结构

```html
<section style="background:#f5f5f0;padding:20px 15px;">

<!-- 头部品牌 Logo -->
<section style="background:#c41e3a;padding:20px 25px;margin-bottom:15px;border-radius:10px;text-align:center;">
<p style="display:flex;align-items:center;justify-content:center;gap:15px;margin-bottom:8px;">
<svg width="50" height="50" viewBox="0 0 140 140" style="display:inline-block;">
<circle cx="70" cy="70" r="70" fill="rgba(255,255,255,0.15)"/>
<path d="M40 110 L70 20 L100 110 M48 85 L92 85" stroke="#fff" stroke-width="8" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="70" cy="25" r="6" fill="#fff"/>
<line x1="70" y1="40" x2="70" y2="110" stroke="#fff" stroke-width="8" stroke-linecap="round"/>
</svg>
<span style="font-size:32px;font-weight:900;color:#fff;letter-spacing:4px;">AI晚知道</span>
</p>
<p style="font-size:13px;color:rgba(255,255,255,0.85);font-style:italic;font-family:Georgia,serif;">"All the AI News That's Fit to Read"</p>
</section>

<!-- 日期栏 -->
<section style="background:#fff;padding:15px 20px;margin-bottom:15px;box-shadow:0 1px 3px rgba(0,0,0,0.1);text-align:center;">
<p style="font-size:14px;color:#666;border-bottom:1px solid #eee;padding-bottom:10px;">{{DATE}}</p>
<p style="font-size:12px;color:#999;margin-top:8px;">精选</p>
</section>

<!-- 标题 -->
<section style="background:#fff;padding:25px 20px;margin-bottom:15px;box-shadow:0 1px 3px rgba(0,0,0,0.1);">
<p style="font-size:24px;font-weight:bold;color:#c41e3a;line-height:1.4;margin-bottom:12px;">{{TITLE}}</p>
<p style="font-size:13px;color:#999;">来源:{{AUTHOR}} | 发布时间:{{PUBLISHED}}</p>
</section>

<!-- 核心观点 -->
<section style="background:#fff;padding:20px;margin-bottom:15px;box-shadow:0 1px 3px rgba(0,0,0,0.1);">
<p style="font-size:11px;color:#c41e3a;letter-spacing:2px;border-bottom:1px solid #eee;padding-bottom:8px;margin-bottom:12px;">核心观点 · TL;DR</p>
<p style="font-size:14px;color:#444;line-height:1.8;border-left:3px solid #c41e3a;padding-left:12px;">
{{SUMMARY}}
</p>
</section>

<!-- 详细内容 -->
<section style="background:#fff;padding:20px;margin-bottom:15px;box-shadow:0 1px 3px rgba(0,0,0,0.1);">
<p style="font-size:11px;color:#c41e3a;letter-spacing:2px;border-bottom:1px solid #eee;padding-bottom:8px;margin-bottom:12px;">详细内容 · DETAILS</p>

{{CONTENT_SECTIONS}}

</section>

<!-- 原文链接 -->
<section style="background:#fff;padding:15px 20px;margin-bottom:15px;box-shadow:0 1px 3px rgba(0,0,0,0.1);text-align:center;">
<p style="font-size:12px;color:#999;">原文链接</p>
<p style="font-size:11px;color:#1a5fb4;word-break:break-all;margin-top:5px;">{{URL}}</p>
</section>

</section>
```

## 内容段落模板

### 标题 + 段落
```html
<p style="font-size:18px;font-weight:bold;color:#1a1a1a;margin-bottom:10px;margin-top:20px;">{{SECTION_TITLE}}</p>

<p style="font-size:14px;color:#444;line-height:1.8;margin-bottom:15px;">
{{PARAGRAPH_TEXT}}
</p>
```

### 要点列表
```html
<p style="background:#fafafa;padding:10px;border-radius:4px;margin-bottom:8px;">
<span style="font-size:13px;color:#666;">• {{POINT_TEXT}}</span>
</p>
```

### 高亮框
```html
<p style="background:#f0f7ff;padding:12px;border-radius:4px;margin-bottom:10px;border-left:3px solid #1a5fb4;">
<span style="font-size:14px;font-weight:bold;color:#1a5fb4;">{{HIGHLIGHT_TITLE}}</span><br/>
<span style="font-size:13px;color:#1a5fb4;">{{HIGHLIGHT_TEXT}}</span>
</p>
```

### 警告框
```html
<p style="background:#fff8f8;border:1px solid #ffe0e0;padding:12px;border-radius:4px;margin-bottom:15px;">
<span style="color:#c41e3a;font-weight:bold;">⚠️ {{WARNING_TITLE}}</span><br/>
<span style="font-size:13px;color:#666;">{{WARNING_TEXT}}</span>
</p>
```

### 引用
```html
<p style="background:#fafafa;padding:15px;border-left:3px solid #c41e3a;margin:15px 0;font-style:italic;color:#666;">
"{{QUOTE_TEXT}}"
</p>
```

## 样式规范

### 颜色
- 主色:`#c41e3a`(红色)
- 背景:`#f5f5f0`(米色)
- 文字:`#444`(深灰)
- 次要文字:`#666`、`#999`
- 高亮:`#1a5fb4`(蓝色)

### 字号
- 大标题:24px
- 章节标题:18px
- 小标题:16px
- 正文:14px
- 辅助文字:13px、12px、11px

### 间距
- 段落间距:`margin-bottom:15px`
- 小间距:`margin-bottom:10px`
- 大间距:`margin-top:20px`

## 注意事项

1. **禁止使用**:
   - `<div>` 标签(用 `<section>` 或 `<p>`)
   - `class` 和 `id` 属性
   - 相对链接 `<a href="/...">`
   - 外部 CSS

2. **必须使用**:
   - 内联样式 `style="..."`
   - `<strong>` 和 `<em>` 标签
   - `<br/>` 换行
   - 完整 URL

3. **内容要求**:
   - 核心观点简洁明了(1-2 句话)
   - 详细内容分段落,每段不超过 3-4 句
   - 使用要点列表增强可读性
   - 适当使用高亮框和警告框

```

### references/USER_GUIDE.md

```markdown
# RSS to WeChat Skill

将 RSS 文章转换为微信公众号格式并发布的通用工具。

## 特性

- ✅ **通用性**:支持任何 RSS 源或 URL
- ✅ **可配置**:品牌、样式、关键词完全可定制
- ✅ **模块化**:数据获取、内容生成、发布分离
- ✅ **灵活性**:AI 助手可根据内容调整结构
- ✅ **安全性**:本地配置不会被提交到 git

## 快速开始

### 1. 安装依赖

```bash
# 必需工具
brew install curl jq pandoc  # macOS
# 或
apt-get install curl jq pandoc  # Linux

# 可选工具(用于 --auto 模式)
npm install -g blogwatcher
```

### 2. 配置

```bash
# 复制配置示例
cd ~/clawd/skills/rss-to-wechat
cp config.example.sh config.local.sh

# 编辑配置
nano config.local.sh
```

最小配置:

```bash
# 微信公众号配置
WECHAT_APPID="your_appid"
WECHAT_APPSECRET="your_secret"

# 品牌配置
BRAND_NAME="你的公众号名称"
BRAND_SLOGAN="你的 Slogan"
```

### 3. 检查配置

```bash
bash rss-to-wechat.sh --check
```

### 4. 使用

```bash
# 指定文章 URL
bash rss-to-wechat.sh --url "https://example.com/article"

# 自动选择最新文章(需要 blogwatcher)
bash rss-to-wechat.sh --auto
```

## 工作流程

1. **数据准备**(自动)
   - 解析文章内容
   - 提取标题、作者、正文
   - 保存到 JSON 文件

2. **HTML 生成**(AI 助手)
   - 根据模板和指导生成微信格式 HTML
   - 使用你的品牌配置
   - 保存到草稿目录

3. **封面生成**(可选)
   - 如果配置了 `COVER_SKILL`
   - 自动生成 1283×383 封面图

4. **发布**(可选)
   - 如果配置了微信凭证
   - 自动上传到草稿箱
   - 或手动发布

## 配置选项

### 必需配置

```bash
# 微信公众号
WECHAT_APPID="your_appid"
WECHAT_APPSECRET="your_secret"

# 品牌
BRAND_NAME="你的公众号名称"
BRAND_SLOGAN="你的 Slogan"
```

### 可选配置

```bash
# RSS 订阅源
RSS_SOURCES=(
  "example.com"
  "another-blog.com"
)

# 筛选条件
MIN_VIEWS_DAILY=10000
MAX_VIEWS_DAILY=100000

# 关键词
PRIORITY_KEYWORDS=(
  "AI"
  "Technology"
)

EXCLUDE_KEYWORDS=(
  "crypto"
  "blockchain"
)

# 路径
WORKSPACE="$HOME/my-workspace"
OUTPUT_DIR="$WORKSPACE/output"
DRAFTS_DIR="$WORKSPACE/drafts"

# 外部工具
COVER_SKILL="/path/to/cover-generator.sh"
WECHAT_PUBLISH_SCRIPT="/path/to/publish-script.sh"

# 品牌样式
BRAND_COLOR="#c41e3a"

# 时区
TZ="Asia/Shanghai"

# 调试
DEBUG=1
```

## HTML 格式要求

微信公众号对 HTML 格式要求严格,必须遵循:

### 必须使用
- `<section>` 和 `<p>` 标签
- 内联样式 `style="..."`
- `<strong>` 和 `<em>` 标签
- `<br/>` 换行
- 完整 URL

### 禁止使用
- `<div>` 标签
- `class` 和 `id` 属性
- 相对链接 `<a href="/...">`
- 外部 CSS
- JavaScript

### 内容结构

1. 头部品牌 Logo(内联 SVG)
2. 日期栏
3. 标题
4. 核心观点(TL;DR)
5. 详细内容(分段落)
6. 原文链接

参考 `html-template.md` 查看完整模板。

## 文件结构

```
rss-to-wechat/
├── README.md              # 本文件
├── SKILL.md               # Skill 描述
├── config.sh              # 默认配置
├── config.example.sh      # 配置示例
├── config.local.sh        # 本地配置(不提交)
├── .gitignore             # Git 忽略规则
├── rss-to-wechat.sh       # 主脚本
├── parse-article.sh       # 文章解析
├── html-template.md       # HTML 模板
└── test.sh                # 测试脚本
```

## 示例

### 完整流程

```bash
# 1. 获取文章数据
bash rss-to-wechat.sh --url "https://simonwillison.net/..."

# 2. AI 助手生成 HTML(根据提示)
# 保存到: ~/clawd/drafts/rss-2026-03-06.html

# 3. 生成封面(如果配置了)
bash "$COVER_SKILL" \
  "文章标题" \
  ~/clawd/drafts/rss-2026-03-06-cover.png

# 4. 上传草稿(如果配置了)
bash "$WECHAT_PUBLISH_SCRIPT" \
  "文章标题" \
  ~/clawd/drafts/rss-2026-03-06.html \
  ~/clawd/drafts/rss-2026-03-06-cover.png
```

### 自动化

```bash
# 每天自动获取最新文章
0 7 * * * cd ~/clawd/skills/rss-to-wechat && bash rss-to-wechat.sh --auto
```

## 故障排除

### 错误码 45166: invalid content

检查 HTML 格式:
- 是否包含不支持的标签?
- 所有样式都是内联的吗?
- 是否有 `class`、`id` 属性?
- 参考成功案例的格式

### 文章解析失败

- 检查 URL 是否可访问
- 确认网站没有反爬虫限制
- 尝试手动复制内容

### 配置检查失败

```bash
bash rss-to-wechat.sh --check
```

查看详细错误信息。

## 进阶使用

### 自定义 HTML 模板

编辑 `html-template.md`,添加你的自定义样式和结构。

### 集成其他工具

在 `config.local.sh` 中设置:

```bash
# 自定义封面生成
COVER_SKILL="/path/to/your/cover-generator.sh"

# 自定义发布脚本
WECHAT_PUBLISH_SCRIPT="/path/to/your/publish-script.sh"
```

### 批量处理

```bash
# 处理多篇文章
for url in $(cat urls.txt); do
  bash rss-to-wechat.sh --url "$url"
done
```

## 贡献

欢迎提交 Issue 和 Pull Request!

## 许可

MIT License

## 相关 Skills

- `wechat-daily` - 日报生成
- `wechat-featured` - 精选文章
- `wechat-cover` - 封面生成
- `wechat-publish` - 草稿上传

## 支持

- 文档:查看 `SKILL.md` 和 `html-template.md`
- 配置帮助:`bash rss-to-wechat.sh --config`
- 检查配置:`bash rss-to-wechat.sh --check`

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### README.md

```markdown
# RSS to WeChat | RSS 转微信公众号

中文 | [English](README.en.md)

OpenClaw skill,用于将 RSS 文章转换为微信公众号格式。

## 特性

- ✅ 解析 RSS 订阅和网页文章
- ✅ 生成微信兼容的 HTML 格式
- ✅ 可配置品牌和样式
- ✅ 可选封面图生成
- ✅ 可选自动发布

## 安装

### 方式 1:通过 ClawHub 安装(推荐)

```bash
# 安装
clawhub install rss-to-wechat

# 配置
cd rss-to-wechat
cp references/config.example.sh config.local.sh
nano config.local.sh  # 编辑配置
```

### 方式 2:手动安装

```bash
# 克隆仓库
git clone https://github.com/huangbaixun/rss-to-wechat.git
cd rss-to-wechat

# 配置
cp references/config.example.sh config.local.sh
nano config.local.sh  # 编辑配置
```

## 快速开始

### 1. 配置

```bash
# 复制配置示例
cp references/config.example.sh config.local.sh

# 编辑配置文件
nano config.local.sh
```

最小配置:

```bash
# 微信公众号配置(必需)
WECHAT_APPID="你的AppID"
WECHAT_APPSECRET="你的AppSecret"

# 品牌配置
BRAND_NAME="你的公众号名称"
BRAND_SLOGAN="你的 Slogan"
```

### 2. 检查配置

```bash
bash scripts/rss-to-wechat.sh --check
```

### 3. 使用

```bash
# 处理指定文章
bash scripts/rss-to-wechat.sh --url "https://example.com/article"

# 自动选择最新文章(需要 blogwatcher)
bash scripts/rss-to-wechat.sh --auto
```

## 工作流程

1. **数据准备**(自动)
   - 脚本获取并解析文章
   - 提取标题、作者、内容
   - 保存为 JSON

2. **HTML 生成**(AI 辅助)
   - AI 助手生成微信兼容的 HTML
   - 使用你的品牌配置
   - 遵循严格的格式要求

3. **封面生成**(可选)
   - 如果配置了 `COVER_SKILL`
   - 生成 1283×383 封面图

4. **发布**(可选)
   - 如果配置了微信凭证
   - 通过 API 上传到草稿箱

## 微信 HTML 格式要求

微信公众号 API 对 HTML 格式有严格要求:

**必须使用:**
- `<section>` 和 `<p>` 标签(不用 `<div>`)
- 内联样式 `style="..."`
- `<strong>` 和 `<em>` 标签
- 完整 URL(不用相对链接)

**禁止使用:**
- `class` 或 `id` 属性
- 外部 CSS
- JavaScript
- 相对链接

详见 `references/html-template.md` 查看完整模板和示例。

## 文档

- **[SKILL.md](SKILL.md)** - Skill 主文档(英文)
- **[用户指南](references/USER_GUIDE.md)** - 完整使用指南(英文)
- **[HTML 模板](references/html-template.md)** - 微信 HTML 格式参考(英文)
- **[配置示例](references/config.example.sh)** - 配置选项说明

## 依赖

**必需:**
- `curl` - HTTP 请求
- `jq` - JSON 处理
- `pandoc` - 格式转换

**可选:**
- `blogwatcher` - RSS 订阅管理(用于 --auto 模式)
- 自定义封面生成脚本
- 自定义发布脚本

## 目录结构

```
rss-to-wechat/
├── SKILL.md              # Skill 主文档
├── README.md             # 英文说明
├── README.zh-CN.md       # 中文说明(本文件)
├── scripts/              # 可执行脚本
│   ├── rss-to-wechat.sh # 主入口
│   ├── parse-article.sh # 文章解析器
│   └── config.sh        # 默认配置
├── references/           # 文档
│   ├── USER_GUIDE.md    # 完整指南
│   ├── html-template.md # HTML 格式参考
│   └── config.example.sh# 配置示例
└── assets/              # 输出资源(空)
```

## 配置选项

查看 `references/config.example.sh` 了解所有可用选项:

- RSS 源和过滤条件
- 品牌定制(名称、标语、颜色)
- 路径配置
- 外部工具集成
- 关键词过滤

## 故障排除

**错误 45166(invalid content)**
- 检查 HTML 格式是否符合要求
- 确保所有样式都是内联的
- 移除 class/id 属性
- 参考成功案例

**文章解析失败**
- 检查 URL 是否可访问
- 确认没有反爬虫措施
- 尝试手动提取内容

**配置问题**
- 运行 `bash scripts/rss-to-wechat.sh --check`
- 确认所有必需工具已安装
- 检查微信凭证

## OpenClaw 集成

### Skill 触发条件

当你在 OpenClaw 中提到以下内容时,此 skill 会自动激活:

- "RSS 转微信"
- "发布文章到公众号"
- "微信公众号格式"
- "从 RSS 获取文章"
- "WeChat Official Account"

### 使用示例

在 OpenClaw 中,你可以直接说:

```
"帮我把这篇文章转换为微信公众号格式:https://example.com/article"

"从 RSS 订阅中选择最新文章发布到公众号"

"生成一篇微信公众号文章,内容来自 Simon Willison 的博客"
```

AI 助手会自动:
1. 加载此 skill
2. 解析文章内容
3. 生成微信格式 HTML
4. 创建封面图
5. 上传到草稿箱

## 许可证

MIT License - 查看 [LICENSE](LICENSE)

## 贡献

欢迎提交 Issue 和 Pull Request!

## 相关项目

- [OpenClaw](https://openclaw.ai) - AI 助手框架
- [ClawHub](https://clawhub.com) - OpenClaw Skill 市场

## 作者

黄百训 ([@huangbaixun](https://github.com/huangbaixun))

## 致谢

感谢 OpenClaw 社区的支持和反馈。

```

### _meta.json

```json
{
  "owner": "huangbaixun",
  "slug": "rss-to-wechat",
  "displayName": "Rss To Wechat",
  "latest": {
    "version": "1.0.1",
    "publishedAt": 1772830578689,
    "commit": "https://github.com/openclaw/skills/commit/5a65f301c0d90cd8d4db6ab30e80fbe59653df5b"
  },
  "history": []
}

```

### scripts/config.sh

```bash
#!/bin/bash
# RSS to WeChat - Configuration

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
WORKSPACE="${WORKSPACE:-$SKILL_DIR}"
OUTPUT_DIR="${OUTPUT_DIR:-$WORKSPACE/output}"
DRAFTS_DIR="${DRAFTS_DIR:-$WORKSPACE/drafts}"

mkdir -p "$OUTPUT_DIR" "$DRAFTS_DIR"

if [ ${#RSS_SOURCES[@]} -eq 0 ]; then
  RSS_SOURCES=("simonwillison.net" "paulgraham.com")
fi

MIN_VIEWS_DAILY=${MIN_VIEWS_DAILY:-10000}
MAX_VIEWS_DAILY=${MAX_VIEWS_DAILY:-100000}
MIN_VIEWS_FEATURED=${MIN_VIEWS_FEATURED:-100000}

if [ ${#PRIORITY_KEYWORDS[@]} -eq 0 ]; then
  PRIORITY_KEYWORDS=("AI" "Artificial Intelligence" "Machine Learning")
fi

if [ ${#GENERAL_KEYWORDS[@]} -eq 0 ]; then
  GENERAL_KEYWORDS=("technology" "programming" "software")
fi

if [ ${#EXCLUDE_KEYWORDS[@]} -eq 0 ]; then
  EXCLUDE_KEYWORDS=("crypto" "blockchain")
fi

WECHAT_APPID="${WECHAT_APPID:-}"
WECHAT_APPSECRET="${WECHAT_APPSECRET:-}"
WECHAT_PUBLISH_SCRIPT="${WECHAT_PUBLISH_SCRIPT:-}"

COVER_WIDTH=${COVER_WIDTH:-1283}
COVER_HEIGHT=${COVER_HEIGHT:-383}
COVER_SKILL="${COVER_SKILL:-}"

BRAND_NAME="${BRAND_NAME:-AI News}"
BRAND_SLOGAN="${BRAND_SLOGAN:-All the AI News That is Fit to Read}"
BRAND_COLOR="${BRAND_COLOR:-#c41e3a}"

export TZ="${TZ:-Asia/Shanghai}"
DEBUG=${DEBUG:-0}

log() {
  echo "[$(date "+%Y-%m-%d %H:%M:%S")] $*"
}

debug() {
  [ "$DEBUG" = "1" ] && echo "[DEBUG] $*" >&2
}

error() {
  echo "[ERROR] $*" >&2
}

warn() {
  echo "[WARN] $*" >&2
}

LOCAL_CONFIG="$SKILL_DIR/config.local.sh"
[ -f "$LOCAL_CONFIG" ] && source "$LOCAL_CONFIG"

validate_config() {
  local errors=0
  for cmd in curl jq pandoc; do
    if ! command -v "$cmd" &> /dev/null; then
      error "Missing required tool: $cmd"
      errors=$((errors + 1))
    fi
  done
  if [ -n "$WECHAT_PUBLISH_SCRIPT" ]; then
    if [ -z "$WECHAT_APPID" ] || [ -z "$WECHAT_APPSECRET" ]; then
      warn "WeChat config incomplete, will skip publishing"
    fi
  fi
  return $errors
}

show_config_help() {
  echo "RSS to WeChat - Configuration Help"
  echo ""
  echo "1. Copy config.example.sh to config.local.sh"
  echo "2. Edit config.local.sh with your settings"
  echo "3. Run: bash rss-to-wechat.sh --check"
}

```

### scripts/daily-featured.sh

```bash
#!/bin/bash
# 每日精选 - 自动筛选并发布高质量文章

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

log "开始每日精选..."

# 1. 获取今天的新文章
log "获取今天的新文章..."
ARTICLES=$(bash "$SCRIPT_DIR/fetch-rss.sh" -d 1)

if [ "$ARTICLES" = "[]" ]; then
  log "没有新文章"
  exit 0
fi

# 2. 筛选高质量文章(10万+ 观看量)
log "筛选高质量文章..."

# 注意:blogwatcher 可能没有观看量数据,这里简化为按来源优先级筛选
FEATURED=$(echo "$ARTICLES" | jq '[.[] | select(.feed | test("simonwillison|xeiaso|mitchellh"))]')

COUNT=$(echo "$FEATURED" | jq 'length')
log "找到 $COUNT 篇候选文章"

if [ "$COUNT" -eq 0 ]; then
  log "没有符合条件的文章"
  exit 0
fi

# 3. 选择第一篇文章
ARTICLE=$(echo "$FEATURED" | jq '.[0]')
URL=$(echo "$ARTICLE" | jq -r '.url')
TITLE=$(echo "$ARTICLE" | jq -r '.title')

log "选择文章: $TITLE"
log "URL: $URL"

# 4. 发布文章
log "发布文章..."
bash "$SCRIPT_DIR/publish-article.sh" "$URL"

log "✅ 每日精选完成!"

```

### scripts/fetch-rss.sh

```bash
#!/bin/bash
# 获取 RSS 新文章列表

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

# 使用方法
usage() {
  cat << EOF
用法: $0 [选项]

选项:
  -d, --days N        获取最近 N 天的文章(默认:1)
  -s, --source NAME   只获取指定来源的文章
  -k, --keyword WORD  按关键词筛选
  -h, --help          显示帮助信息

示例:
  $0                           # 获取今天的新文章
  $0 -d 7                      # 获取最近 7 天的文章
  $0 -s simonwillison.net      # 只获取 Simon Willison 的文章
  $0 -k "AI Coding"            # 筛选包含 "AI Coding" 的文章
EOF
  exit 1
}

# 参数解析
DAYS=1
SOURCE=""
KEYWORD=""

while [[ $# -gt 0 ]]; do
  case $1 in
    -d|--days)
      DAYS="$2"
      shift 2
      ;;
    -s|--source)
      SOURCE="$2"
      shift 2
      ;;
    -k|--keyword)
      KEYWORD="$2"
      shift 2
      ;;
    -h|--help)
      usage
      ;;
    *)
      error "未知选项: $1"
      usage
      ;;
  esac
done

log "扫描 RSS 订阅更新..."

# 扫描 blogwatcher
blogwatcher scan > /dev/null 2>&1 || true

log "获取文章列表..."

# 获取文章列表(JSON 格式)
ARTICLES=$(blogwatcher articles --format json --unread-only)

if [ -z "$ARTICLES" ] || [ "$ARTICLES" = "[]" ]; then
  log "没有新文章"
  echo "[]"
  exit 0
fi

# 筛选条件
FILTERED="$ARTICLES"

# 按来源筛选
if [ -n "$SOURCE" ]; then
  debug "筛选来源: $SOURCE"
  FILTERED=$(echo "$FILTERED" | jq --arg source "$SOURCE" '[.[] | select(.feed | contains($source))]')
fi

# 按关键词筛选
if [ -n "$KEYWORD" ]; then
  debug "筛选关键词: $KEYWORD"
  FILTERED=$(echo "$FILTERED" | jq --arg keyword "$KEYWORD" '[.[] | select(.title | test($keyword; "i"))]')
fi

# 按时间筛选(最近 N 天)
CUTOFF_DATE=$(date -v-${DAYS}d '+%Y-%m-%d' 2>/dev/null || date -d "${DAYS} days ago" '+%Y-%m-%d')
debug "截止日期: $CUTOFF_DATE"
FILTERED=$(echo "$FILTERED" | jq --arg cutoff "$CUTOFF_DATE" '[.[] | select(.published >= $cutoff)]')

# 按优先级关键词排序
for keyword in "${PRIORITY_KEYWORDS[@]}"; do
  debug "优先级关键词: $keyword"
done

# 输出结果
COUNT=$(echo "$FILTERED" | jq 'length')
log "找到 $COUNT 篇新文章"

echo "$FILTERED" | jq '.'

```

### scripts/format-wechat.sh

```bash
#!/bin/bash
# 格式化为微信公众号 HTML(内联样式,无外层标签)

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

# 使用方法
usage() {
  cat << EOF
用法: $0 <文章JSON> <输出文件>

参数:
  <文章JSON>   parse-article.sh 输出的 JSON 文件
  <输出文件>   输出的 HTML 文件路径

示例:
  $0 article.json output.html
EOF
  exit 1
}

if [ $# -lt 2 ]; then
  usage
fi

ARTICLE_JSON="$1"
OUTPUT_HTML="$2"

if [ ! -f "$ARTICLE_JSON" ]; then
  error "文章 JSON 文件不存在: $ARTICLE_JSON"
  exit 1
fi

log "格式化为公众号 HTML..."

# 读取文章数据
TITLE=$(jq -r '.title' "$ARTICLE_JSON")
AUTHOR=$(jq -r '.author' "$ARTICLE_JSON")
PUBLISHED=$(jq -r '.published' "$ARTICLE_JSON")
CONTENT=$(jq -r '.content' "$ARTICLE_JSON")
URL=$(jq -r '.url' "$ARTICLE_JSON")

# 转换 Markdown 为 HTML
CONTENT_HTML=$(echo "$CONTENT" | pandoc -f markdown -t html)

# 获取当前日期和星期
CURRENT_DATE=$(TZ=Asia/Shanghai date "+%Y年%m月%d日 星期%u" | sed 's/星期1/星期一/;s/星期2/星期二/;s/星期3/星期三/;s/星期4/星期四/;s/星期5/星期五/;s/星期6/星期六/;s/星期7/星期日/')

# 生成微信公众号兼容的 HTML(纯 section 标签 + 内联样式)
cat > "$OUTPUT_HTML" << EOF
<section style="background:#f5f5f0;padding:20px 15px;">

<!-- 头部品牌 Logo -->
<section style="background:#c41e3a;padding:20px 25px;margin-bottom:15px;border-radius:10px;text-align:center;">
<p style="display:flex;align-items:center;justify-content:center;gap:15px;margin-bottom:8px;">
<svg width="50" height="50" viewBox="0 0 140 140" style="display:inline-block;">
<circle cx="70" cy="70" r="70" fill="rgba(255,255,255,0.15)"/>
<path d="M40 110 L70 20 L100 110 M48 85 L92 85" stroke="#fff" stroke-width="8" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="70" cy="25" r="6" fill="#fff"/>
<line x1="70" y1="40" x2="70" y2="110" stroke="#fff" stroke-width="8" stroke-linecap="round"/>
</svg>
<span style="font-size:32px;font-weight:900;color:#fff;letter-spacing:4px;">AI晚知道</span>
</p>
<p style="font-size:13px;color:rgba(255,255,255,0.85);font-style:italic;font-family:Georgia,serif;">"All the AI News That's Fit to Read"</p>
</section>

<!-- 日期栏 -->
<section style="background:#fff;padding:15px 20px;margin-bottom:15px;box-shadow:0 1px 3px rgba(0,0,0,0.1);text-align:center;">
<p style="font-size:14px;color:#666;border-bottom:1px solid #eee;padding-bottom:10px;">$CURRENT_DATE</p>
<p style="font-size:12px;color:#999;margin-top:8px;">精选</p>
</section>

<!-- 标题 -->
<section style="background:#fff;padding:25px 20px;margin-bottom:15px;box-shadow:0 1px 3px rgba(0,0,0,0.1);">
<p style="font-size:24px;font-weight:bold;color:#c41e3a;line-height:1.4;margin-bottom:12px;">$TITLE</p>
<p style="font-size:13px;color:#999;">来源:$AUTHOR | 发布时间:$PUBLISHED</p>
</section>

<!-- 文章内容 -->
<section style="background:#fff;padding:20px;margin-bottom:15px;box-shadow:0 1px 3px rgba(0,0,0,0.1);">
EOF

# 将 Markdown HTML 转换为内联样式,并移除不兼容的元素
echo "$CONTENT_HTML" | sed \
  -e 's|<h1[^>]*>|<p style="font-size:22px;font-weight:bold;color:#1a1a1a;margin:20px 0 12px 0;">|g' \
  -e 's|</h1>|</p>|g' \
  -e 's|<h2[^>]*>|<p style="font-size:20px;font-weight:bold;color:#c41e3a;margin:18px 0 10px 0;">|g' \
  -e 's|</h2>|</p>|g' \
  -e 's|<h3[^>]*>|<p style="font-size:18px;font-weight:bold;color:#555;margin:16px 0 8px 0;">|g' \
  -e 's|</h3>|</p>|g' \
  -e 's|<p[^>]*>|<p style="font-size:14px;color:#444;line-height:1.8;margin-bottom:12px;">|g' \
  -e 's|<blockquote>|<section style="border-left:3px solid #c41e3a;padding-left:12px;margin:15px 0;color:#666;font-style:italic;">|g' \
  -e 's|</blockquote>|</section>|g' \
  -e 's|<code>|<span style="background:#f5f5f5;padding:2px 6px;border-radius:3px;font-family:monospace;font-size:13px;">|g' \
  -e 's|</code>|</span>|g' \
  -e 's|<pre>|<section style="background:#f5f5f5;padding:12px;border-radius:4px;overflow-x:auto;margin:12px 0;">|g' \
  -e 's|</pre>|</section>|g' \
  -e 's|<ul>|<section style="margin:10px 0;padding-left:20px;">|g' \
  -e 's|</ul>|</section>|g' \
  -e 's|<ol>|<section style="margin:10px 0;padding-left:20px;">|g' \
  -e 's|</ol>|</section>|g' \
  -e 's|<li>|<p style="font-size:14px;color:#444;line-height:1.8;margin-bottom:8px;">• |g' \
  -e 's|</li>|</p>|g' \
  -e 's|<strong>|<span style="font-weight:bold;color:#1a1a1a;">|g' \
  -e 's|</strong>|</span>|g' \
  -e 's|<em>|<span style="font-style:italic;color:#666;">|g' \
  -e 's|</em>|</span>|g' \
  -e 's|<a href="[^"]*">||g' \
  -e 's|</a>||g' \
  -e 's|<div[^>]*>||g' \
  -e 's|</div>||g' \
  >> "$OUTPUT_HTML"

cat >> "$OUTPUT_HTML" << EOF
</section>

<!-- 原文链接 -->
<section style="background:#fff;padding:15px 20px;margin-bottom:15px;box-shadow:0 1px 3px rgba(0,0,0,0.1);text-align:center;">
<p style="font-size:12px;color:#999;">原文链接</p>
<p style="font-size:11px;color:#1a5fb4;word-break:break-all;margin-top:5px;">$URL</p>
</section>

</section>
EOF

log "HTML 生成完成: $OUTPUT_HTML"

```

### scripts/parse-article.sh

```bash
#!/bin/bash
# 解析文章内容

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

# 使用方法
usage() {
  cat << EOF
用法: $0 <文章URL> [输出JSON文件]

参数:
  <文章URL>      要解析的文章 URL
  [输出JSON文件]  可选,输出 JSON 文件路径(默认输出到 stdout)

输出:
  JSON 格式的文章内容:
  {
    "title": "文章标题",
    "author": "作者",
    "published": "发布时间",
    "url": "文章URL",
    "content": "文章内容(Markdown)",
    "summary": "摘要"
  }

示例:
  $0 "https://simonwillison.net/2026/Mar/5/agentic-patterns/" article.json
EOF
  exit 1
}

if [ $# -lt 1 ]; then
  usage
fi

URL="$1"
OUTPUT_FILE="${2:-}"

log "解析文章: $URL"

# 使用 curl 获取内容(绕过 web_fetch 的 SSRF 限制)
HTML=$(curl -s -L "$URL" || {
  error "无法获取文章内容"
  exit 1
})

# 提取标题
TITLE=$(echo "$HTML" | grep -o '<title>[^<]*</title>' | sed 's/<title>\(.*\)<\/title>/\1/' | head -1)
# 清理标题(移除网站名称后缀)
TITLE=$(echo "$TITLE" | sed 's/ - Simon Willison.*$//' | sed 's/ - Agentic Engineering Patterns.*$//')
debug "标题: $TITLE"

# 提取作者(尝试多种方式)
AUTHOR=$(echo "$HTML" | grep -o '<meta name="author" content="[^"]*"' | sed 's/.*content="\([^"]*\)".*/\1/' | head -1)
if [ -z "$AUTHOR" ]; then
  AUTHOR=$(echo "$HTML" | grep -o '<meta property="article:author" content="[^"]*"' | sed 's/.*content="\([^"]*\)".*/\1/' | head -1)
fi
if [ -z "$AUTHOR" ]; then
  # 默认作者(Simon Willison 的博客)
  AUTHOR="Simon Willison"
fi
debug "作者: $AUTHOR"

# 提取发布时间
PUBLISHED=$(echo "$HTML" | grep -o '<meta property="article:published_time" content="[^"]*"' | sed 's/.*content="\([^"]*\)".*/\1/' | head -1)
if [ -z "$PUBLISHED" ]; then
  PUBLISHED=$(echo "$HTML" | grep -o '<time datetime="[^"]*"' | sed 's/.*datetime="\([^"]*\)".*/\1/' | head -1)
fi
if [ -z "$PUBLISHED" ]; then
  # 从 URL 提取日期(Simon Willison 的博客格式)
  PUBLISHED=$(echo "$URL" | grep -oE '[0-9]{4}/[A-Za-z]{3}/[0-9]{1,2}' | sed 's|/|-|g')
fi
debug "发布时间: $PUBLISHED"

# 提取正文内容(只提取 article 或 main 标签内的内容)
ARTICLE_HTML=$(echo "$HTML" | sed -n '/<article/,/<\/article>/p' | head -1000)
if [ -z "$ARTICLE_HTML" ]; then
  ARTICLE_HTML=$(echo "$HTML" | sed -n '/<main/,/<\/main>/p' | head -1000)
fi
if [ -z "$ARTICLE_HTML" ]; then
  # 如果没有 article/main 标签,尝试提取 id="primary" 的内容
  ARTICLE_HTML=$(echo "$HTML" | sed -n '/<div id="primary"/,/<\/div>/p' | head -1000)
fi

# 移除导航、侧边栏、页脚等无关内容
ARTICLE_HTML=$(echo "$ARTICLE_HTML" | \
  sed '/<nav/,/<\/nav>/d' | \
  sed '/<aside/,/<\/aside>/d' | \
  sed '/<footer/,/<\/footer>/d' | \
  sed '/<div id="secondary"/,/<\/div>/d' | \
  sed '/<div class="metabox"/,/<\/div>/d')

# 转换为 Markdown
CONTENT=$(echo "$ARTICLE_HTML" | pandoc -f html -t markdown 2>/dev/null || echo "")

# 如果内容为空,回退到全页面转换
if [ -z "$CONTENT" ] || [ $(echo "$CONTENT" | wc -c) -lt 100 ]; then
  warn "正文提取失败,使用全页面内容"
  CONTENT=$(echo "$HTML" | pandoc -f html -t markdown 2>/dev/null || echo "$HTML")
fi

# 清理内容(移除多余的空行和特殊字符)
CONTENT=$(echo "$CONTENT" | \
  sed '/^:::/d' | \
  sed '/^{#/d' | \
  sed '/^{\..*}$/d' | \
  sed 's/\\//g' | \
  awk 'NF {p=1} p' | \
  head -500)

# 生成摘要(前 300 字)
SUMMARY=$(echo "$CONTENT" | head -c 300)

# 生成 JSON
JSON_OUTPUT=$(jq -n \
  --arg title "$TITLE" \
  --arg author "$AUTHOR" \
  --arg published "$PUBLISHED" \
  --arg url "$URL" \
  --arg content "$CONTENT" \
  --arg summary "$SUMMARY" \
  '{
    title: $title,
    author: $author,
    published: $published,
    url: $url,
    content: $content,
    summary: $summary
  }')

# 输出到文件或 stdout
if [ -n "$OUTPUT_FILE" ]; then
  echo "$JSON_OUTPUT" > "$OUTPUT_FILE"
  log "解析完成: $OUTPUT_FILE"
else
  echo "$JSON_OUTPUT"
  log "解析完成"
fi

```

### scripts/publish-article.sh

```bash
#!/bin/bash
# 发布单篇文章到微信公众号

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

# 使用方法
usage() {
  cat << EOF
用法: $0 <文章URL> [标题]

参数:
  <文章URL>  要发布的文章 URL(必需)
  [标题]     自定义标题(可选,默认使用原标题)

示例:
  $0 "https://simonwillison.net/2026/Mar/5/agentic-patterns/"
  $0 "https://simonwillison.net/2026/Mar/5/agentic-patterns/" "AI Agent 工程模式"

流程:
  1. 解析文章内容
  2. 生成公众号 HTML
  3. 生成封面图
  4. 上传到草稿箱
  5. 更新发布历史
EOF
  exit 1
}

if [ $# -lt 1 ]; then
  usage
fi

URL="$1"
CUSTOM_TITLE="$2"

# 临时文件
TMP_DIR=$(mktemp -d)
ARTICLE_JSON="$TMP_DIR/article.json"
ARTICLE_HTML="$TMP_DIR/article.html"
COVER_IMAGE="$TMP_DIR/cover.png"

cleanup() {
  rm -rf "$TMP_DIR"
}
trap cleanup EXIT

log "开始处理文章: $URL"

# 1. 检查是否已发布
if [ -f "$PUBLISH_HISTORY" ]; then
  if grep -q "$URL" "$PUBLISH_HISTORY"; then
    error "文章已发布过: $URL"
    exit 1
  fi
fi

# 2. 解析文章内容
log "解析文章内容..."
bash "$SCRIPT_DIR/parse-article.sh" "$URL" > "$ARTICLE_JSON"

if [ ! -s "$ARTICLE_JSON" ]; then
  error "文章解析失败"
  exit 1
fi

TITLE=$(jq -r '.title' "$ARTICLE_JSON")
AUTHOR=$(jq -r '.author' "$ARTICLE_JSON")

log "标题: $TITLE"
log "作者: $AUTHOR"

# 使用自定义标题(如果提供)
if [ -n "$CUSTOM_TITLE" ]; then
  TITLE="$CUSTOM_TITLE"
  log "使用自定义标题: $TITLE"
fi

# 3. 生成公众号 HTML
log "生成公众号 HTML..."
bash "$SCRIPT_DIR/format-wechat.sh" "$ARTICLE_JSON" "$ARTICLE_HTML"

# 4. 生成封面图
log "生成封面图..."
bash "$COVER_SKILL" "$TITLE" "$COVER_IMAGE"

if [ ! -f "$COVER_IMAGE" ]; then
  error "封面生成失败"
  exit 1
fi

# 5. 上传到草稿箱
log "上传到草稿箱..."
DRAFT_ID=$(bash "$WECHAT_PUBLISH_SCRIPT" "$ARTICLE_HTML" "$COVER_IMAGE" "$TITLE" | grep -o 'draft_id=[^&]*' | cut -d= -f2)

if [ -z "$DRAFT_ID" ]; then
  error "上传失败"
  exit 1
fi

log "草稿 ID: $DRAFT_ID"

# 6. 更新发布历史
log "更新发布历史..."
CURRENT_DATE=$(TZ=Asia/Shanghai date "+%Y-%m-%d %H:%M:%S")

mkdir -p "$(dirname "$PUBLISH_HISTORY")"
cat >> "$PUBLISH_HISTORY" << EOF

## $CURRENT_DATE - $TITLE

- **来源**: $URL
- **作者**: $AUTHOR
- **草稿 ID**: $DRAFT_ID
- **类型**: RSS 精选

EOF

log "✅ 发布完成!"
log "草稿链接: https://mp.weixin.qq.com/cgi-bin/appmsg?t=media/appmsg_edit&action=edit&type=10&isMul=1&isNew=1&token=&lang=zh_CN&draft_id=$DRAFT_ID"

```

### scripts/test.sh

```bash
#!/bin/bash
# 测试 RSS to WeChat Skill

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

echo "=== RSS to WeChat Skill 测试 ==="
echo ""

# 1. 检查依赖
echo "1. 检查依赖..."
echo -n "  blogwatcher: "
if command -v blogwatcher &> /dev/null; then
  echo "✅"
else
  echo "❌ 未安装"
fi

echo -n "  curl: "
if command -v curl &> /dev/null; then
  echo "✅"
else
  echo "❌ 未安装"
fi

echo -n "  jq: "
if command -v jq &> /dev/null; then
  echo "✅"
else
  echo "❌ 未安装"
fi

echo -n "  pandoc: "
if command -v pandoc &> /dev/null; then
  echo "✅"
else
  echo "❌ 未安装"
fi

echo ""

# 2. 检查配置文件
echo "2. 检查配置文件..."
if [ -f "$SCRIPT_DIR/config.sh" ]; then
  echo "  ✅ config.sh"
else
  echo "  ❌ config.sh 不存在"
fi

echo ""

# 3. 检查脚本文件
echo "3. 检查脚本文件..."
for script in fetch-rss.sh parse-article.sh format-wechat.sh publish-article.sh daily-featured.sh; do
  if [ -f "$SCRIPT_DIR/$script" ] && [ -x "$SCRIPT_DIR/$script" ]; then
    echo "  ✅ $script"
  else
    echo "  ❌ $script (不存在或无执行权限)"
  fi
done

echo ""

# 4. 检查依赖 skills
echo "4. 检查依赖 skills..."
echo -n "  wechat-cover: "
if [ -f "$HOME/clawd/skills/wechat-cover/generate-cover.sh" ]; then
  echo "✅"
else
  echo "❌"
fi

echo -n "  wechat-publish: "
if [ -f "$HOME/clawd/scripts/wechat-publish.sh" ]; then
  echo "✅"
else
  echo "❌"
fi

echo ""

# 5. 测试示例
echo "5. 测试示例..."
echo ""
echo "获取新文章:"
echo "  bash $SCRIPT_DIR/fetch-rss.sh"
echo ""
echo "发布文章:"
echo "  bash $SCRIPT_DIR/publish-article.sh 'https://simonwillison.net/...'"
echo ""
echo "每日精选:"
echo "  bash $SCRIPT_DIR/daily-featured.sh"
echo ""

echo "=== 测试完成 ==="

```

rss-to-wechat | SkillHub