Skill: misakiga/wechat-mp-publisher (version 2.0.2)

--- name: wechat-mp-publisher version: 2.0.2 description: 远程微信公众号发布技能 (合规优化版)。通过 HTTP MCP 解决家用宽带 IP 变动问题，支持安全凭证隔离与依赖检查。 homepage: https://github.com/caol64/wenyan-mcp metadata: openclaw: emoji: "🚀" category: publishing clawdbot: emoji: "🚀" requires: bins: ["mcporter", "curl", "jq"] install: - id: "node" kind: "node" package: "mcporter" bins: ["mcporter"] label: "安装 MCP 客户端 (mcporter)" --- # 微信公众号远程发布 (Remote Publisher - Compliance Optimized) **核心痛点解决**：家用宽带 IP 频繁变动，无法固定添加到公众号白名单？ 本技能通过远程 `wenyan-mcp` 服务中转，让你的本地 OpenClaw 也能稳定发布文章，无需本地 IP 权限！ ## 🌟 架构优势 - **IP 漫游无忧**：仅需将远程 MCP 服务器 IP 加入白名单，无论你在家里、咖啡厅还是 4G 热点，都能随时发布。 - **合规隔离**：凭证与系统配置分离，避免污染全局 `TOOLS.md`。 - **依赖自检**：脚本运行时自动检查 `jq`、`mcporter` 和 `wenyan-cli`。 - **灵活配置**：支持自定义 MCP 配置文件路径。 ## ⚙️ 快速配置 ### 1. 准备凭证 (wechat.env) 在技能根目录下复制 `wechat.env.example` 为 `wechat.env` 并填入公众号凭证： ```bash cp wechat.env.example wechat.env nano wechat.env ``` 内容示例： ```bash export WECHAT_APP_ID="wx..." export WECHAT_APP_SECRET="cx..." # Optional: 指定 MCP 配置文件 (默认 $HOME/.openclaw/mcp.json) # export MCP_CONFIG_FILE="/path/to/your/mcp.json" ``` ### 2. 连接远程服务 (mcp.json) 确保你的 `mcp.json` 指向远程 MCP 实例： ```json { "mcpServers": { "wenyan-mcp": { "name": "公众号远程助手", "transport": "sse", "url": "http://<your-remote-server-ip>:3000/sse", "headers": { "X-API-Key": "<optional-api-key>" } } } } ``` ## 🚀 使用指南 ### 方式 A: 智能助手 (推荐) 直接对我说： > "帮我把 `path/to/article.md` 发布到公众号，使用默认主题。" 我会自动： 1. 读取 `wechat.env` 获取凭证 2. 检查本地环境 (`mcporter`, `jq`) 3. 调用远程 MCP 完成发布 ### 方式 B: 命令行脚本 (高级) 我们提供了封装好的脚本 `scripts/publish-remote.sh`，体验与本地 CLI 一致： ```bash # 赋予执行权限 chmod +x scripts/publish-remote.sh # 发布文章 (自动加载 wechat.env) ./scripts/publish-remote.sh ./my-post.md # 指定主题 (lapis) ./scripts/publish-remote.sh ./my-post.md lapis ``` ## 📝 Markdown 规范 与标准 wenyan-cli 一致，头部必须包含元数据： ```markdown --- title: 我的精彩文章 cover: https://example.com/cover.jpg --- # 正文开始 ... ``` *提示：`cover` 推荐使用图床链接，以确保远程服务器能正确下载封面。* ## 🛠️ 故障排查 | 现象 | 原因 | 解决方案 | | :--- | :--- | :--- | | **Dependencies Missing** | 缺少 `jq` 或 `mcporter` | 请确保系统已安装这些工具 | | **Config Not Found** | 未找到 `wechat.env` | 请按照步骤 1 创建并配置 | | **IP not in whitelist** | 远程服务器 IP 未加白 | 登录公众号后台 -> 基本配置 -> IP 白名单，添加 **MCP 服务器的公网 IP** |

path: README.md
content: # wechat-mp-publisher

**Remote WeChat Official Account Publisher via HTTP MCP**

OpenClaw 技能，用于将 Markdown 文章一键发布到微信公众号草稿箱。
专为解决家用宽带动态 IP 痛点而生，通过远程 `wenyan-mcp` 服务中转，让你的本地环境无需繁琐配置即可稳定发文。

## ✨ 核心特性

- **IP 漫游**: 仅需一次配置远程 MCP 服务器 IP 白名单，无论在哪都能发。
- **大文件传输**: 优化的 `upload_file` 协议，稳定支持长图文。
- **完全兼容**: 支持 wenyan-cli 所有排版主题与 Frontmatter 语法。
- **安全隔离**: 敏感凭证 (AppID/Secret) 仅在运行时传递，不落地存储。

## 📦 快速安装

1. 下载并解压 `wechat-mp-publisher.zip` 到 OpenClaw `skills/` 目录。
2. 配置 `/root/.openclaw/mcp.json` 指向远程服务。
3. 运行 `./scripts/publish-remote.sh` 或直接与 Agent 对话发布。

## 📚 文档索引

- [使用说明 (SKILL.md)](SKILL.md)
- [排版主题预览](references/themes.md)
- [故障排查指南](references/troubleshooting.md)

## 🔗 相关项目

- [wenyan-mcp](https://github.com/caol64/wenyan-mcp): 本技能依赖的后端服务
- [wenyan-cli](https://github.com/caol64/wenyan-cli): 核心排版引擎


path: example.md
content: ---
title: wechat-publisher 测试文章
cover: ./assets/default-cover.jpg
---

# wechat-publisher Skill 测试

欢迎使用 **wechat-publisher** skill！

## 核心功能

### 1. Markdown 自动转换
- 标准 Markdown 转换为微信公众号格式
- 自动处理样式和排版
- 支持多种主题

### 2. 图片自动上传
- 本地图片自动上传
- 网络图片自动处理
- 无需手动操作

### 3. 一键发布
```bash
./scripts/publish.sh example.md
```

## 代码示例

```python
def publish_article(file):
    """发布到微信公众号"""
    return wenyan.publish(file)
```

## 使用步骤

1. 配置凭证（TOOLS.md）
2. 准备 Markdown 文件
3. 运行发布脚本

## 封面图说明

本测试文章使用相对路径引用封面：
```markdown
cover: ./assets/default-cover.jpg
```

这样发布到 GitHub 后，其他用户克隆 skill 也能直接使用！

## 测试总结

如果看到这篇文章，说明 skill 工作正常！

---

*由 wechat-publisher 自动发布*


path: references/themes.md
content: # wenyan-cli 主题列表

wenyan-cli 支持多种内置主题，也支持自定义主题。

## 内置主题

查看所有内置主题：
```bash
wenyan theme -l
```

**常用主题：**

1. **default** - 默认主题
   - 简洁、通用
   - 适合大部分文章

2. **lapis** - 青金石（推荐）
   - 优雅的蓝色调
   - 适合技术文章

3. **phycat** - 物理猫
   - 轻量、现代
   - 适合科技类内容

**完整主题列表：** https://github.com/caol64/wenyan-core/tree/main/src/assets/themes

## 代码高亮主题

### 亮色主题
- `atom-one-light` - Atom 编辑器亮色
- `github` - GitHub 风格
- `solarized-light` - Solarized 亮色（推荐）
- `xcode` - Xcode 默认

### 暗色主题
- `atom-one-dark` - Atom 编辑器暗色
- `dracula` - Dracula 主题
- `github-dark` - GitHub 暗色
- `monokai` - Monokai 经典
- `solarized-dark` - Solarized 暗色

## 自定义主题

### 临时使用
```bash
wenyan publish -f article.md -c /path/to/theme.css
```

### 永久安装
```bash
# 从本地文件
wenyan theme --add --name my-theme --path /path/to/theme.css

# 从网络
wenyan theme --add --name my-theme --path https://example.com/theme.css
```

### 使用已安装主题
```bash
wenyan publish -f article.md -t my-theme
```

### 删除主题
```bash
wenyan theme --rm my-theme
```

## 主题定制

如果你想创建自己的主题，可以参考：

1. **查看现有主题源码：** https://github.com/caol64/wenyan-core/tree/main/src/assets/themes
2. **CSS 变量参考：** wenyan 使用 CSS 变量定制样式
3. **测试主题：** 使用 `wenyan render` 命令仅渲染不发布

**示例：**
```bash
# 渲染测试（不发布）
wenyan render -f article.md -t my-theme -h github
```

## 推荐组合

### 技术文章
```bash
wenyan publish -f article.md -t lapis -h solarized-light
```

### 深色风格
```bash
wenyan publish -f article.md -t phycat -h dracula
```

### 简洁风格
```bash
wenyan publish -f article.md -t default -h github
```

## 更多选项

### 关闭 Mac 风格代码块
```bash
wenyan publish -f article.md -t lapis --no-mac-style
```

### 关闭链接转脚注
```bash
wenyan publish -f article.md -t lapis --no-footnote
```

### 组合所有选项
```bash
wenyan publish -f article.md \
  -t lapis \
  -h solarized-light \
  --no-mac-style \
  --no-footnote
```


path: references/troubleshooting.md
content: # 微信公众号发布常见问题 (Remote MCP 版)

## 1. 核心误区：IP 白名单配置

使用本远程技能时，**必须将 MCP 服务所在公网 IP** 加入白名单，而**不是**你当前操作机器的 IP。

**为什么？**
- 所有的 API 请求（包括获取 Access Token、上传图文、上传素材）都是由 **远程 MCP 服务器** 发起的。
- 你本地的 OpenClaw 只负责发指令给 MCP，不直接与微信交互。
- 即使你在家里用动态 IP 也没关系，只要 MCP 服务器 IP 固定即可。

## 2. 封面图 (Cover) 加载失败

**错误信息**: `cover not found` 或 `upload image failed`

**常见原因**:
1. **相对路径失效**: 远程 MCP 无法访问你本地磁盘上的相对路径 `./assets/cover.jpg`。
2. **图床防盗链**: 使用了某些开启防盗链的图床链接。

**解决方案**:
- **推荐**: 使用公网可访问的 URL（如 GitHub Pages, S3, OSS 等）。
- **高级**: 如果必须用本地图片，需搭建文件服务让 MCP 可访问，或者直接将图片转为 Base64 嵌入（不推荐，体积过大）。

## 3. 文章排版错乱

**现象**: 发布成功，但在手机预览时样式丢失或错位。

**原因**:
- 微信编辑器对某些 CSS 属性支持不佳。
- 远程 MCP 上的 `wenyan-cli` 版本过旧。

**检查**:
- 确认远程服务器安装了最新版 `wenyan-cli`。
- 尝试更换主题 (`lapis`, `phycat` 等标准主题兼容性最好)。


path: scripts/publish-remote.sh
content: #!/bin/bash
# 远程发布文章到微信公众号 (Optimized for Compliance)
# 基于 wenyan-mcp HTTP Stateless 模式

set -e

# Get script directory for relative path resolution
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"

# Configuration
CONFIG_FILE="${SKILL_ROOT}/wechat.env"
MCP_CONFIG_FILE="${MCP_CONFIG_FILE:-$HOME/.openclaw/mcp.json}"

# Load Configuration
if [ -f "$CONFIG_FILE" ]; then
    source "$CONFIG_FILE"
fi

# Dependencies Check
check_deps() {
    local missing=0
    for cmd in jq mcporter curl; do
        if ! command -v "$cmd" &> /dev/null; then
            echo "❌ Error: Required command '$cmd' not found."
            missing=1
        fi
    done
    
    if [ $missing -eq 1 ]; then
        echo "Please install missing dependencies."
        exit 1
    fi
}

check_deps

# Check Config Files
if [ ! -f "$MCP_CONFIG_FILE" ]; then
    echo "❌ Error: MCP config file not found at: $MCP_CONFIG_FILE"
    echo "Please create it or set MCP_CONFIG_FILE env var."
    exit 1
fi

# Check Credentials
if [ -z "$WECHAT_APP_ID" ] || [ -z "$WECHAT_APP_SECRET" ]; then
    echo "❌ Error: WECHAT_APP_ID or WECHAT_APP_SECRET not set."
    echo "Please configure '$CONFIG_FILE' based on 'wechat.env.example'."
    exit 1
fi

# Usage
FILE_PATH="$1"
THEME_ID="${2:-default}"

if [ -z "$FILE_PATH" ]; then
  echo "Usage: $(basename "$0") <path/to/article.md> [theme_id]"
  echo "Example: ./publish-remote.sh ./my-post.md lapis"
  exit 1
fi

if [ ! -f "$FILE_PATH" ]; then
  echo "❌ Error: File '$FILE_PATH' not found."
  exit 1
fi

# Upload File
echo "🚀 Uploading file to wenyan-mcp..."
FILENAME=$(basename "$FILE_PATH")
CONTENT=$(cat "$FILE_PATH")

# Safe JSON construction with jq
UPLOAD_ARGS=$(jq -n --arg content "$CONTENT" --arg filename "$FILENAME" '{content: $content, filename: $filename}')

# Call remote MCP
UPLOAD_RES=$(mcporter call wenyan-mcp.upload_file --config "$MCP_CONFIG_FILE" --args "$UPLOAD_ARGS" 2>/dev/null)

# Parse Upload Result
FILE_ID=$(echo "$UPLOAD_RES" | jq -r '.file_id // empty')
ERROR_MSG=$(echo "$UPLOAD_RES" | jq -r '.error // empty')

if [ -n "$ERROR_MSG" ]; then
  echo "❌ Upload failed: $ERROR_MSG"
  exit 1
fi

if [ -z "$FILE_ID" ] || [ "$FILE_ID" == "null" ]; then
  echo "❌ Upload failed: Could not parse file_id from response."
  echo "Response: $UPLOAD_RES"
  exit 1
fi

echo "✅ File uploaded! ID: $FILE_ID"
echo "⏳ Publishing to WeChat draft box..."

# Construct Publish Arguments
PUBLISH_ARGS=$(jq -n \
  --arg file_id "$FILE_ID" \
  --arg theme_id "$THEME_ID" \
  --arg app_id "$WECHAT_APP_ID" \
  --arg app_secret "$WECHAT_APP_SECRET" \
  '{file_id: $file_id, theme_id: $theme_id, wechat_app_id: $app_id, wechat_app_secret: $app_secret}')

# Call remote MCP
PUBLISH_RES=$(mcporter call wenyan-mcp.publish_article --config "$MCP_CONFIG_FILE" --args "$PUBLISH_ARGS" 2>/dev/null)

# Parse Publish Result
MEDIA_ID=$(echo "$PUBLISH_RES" | jq -r '.media_id // empty')
PUBLISH_ERR=$(echo "$PUBLISH_RES" | jq -r '.error // empty')

if [ -n "$PUBLISH_ERR" ]; then
  echo "❌ Publish failed: $PUBLISH_ERR"
  echo "Tip: Check if remote server IP is whitelisted in WeChat MP backend."
  exit 1
fi

if [ -z "$MEDIA_ID" ] || [ "$MEDIA_ID" == "null" ]; then
  echo "❌ Publish failed: Unknown response."
  echo "Response: $PUBLISH_RES"
  exit 1
fi

echo "🎉 Success! Media ID: $MEDIA_ID"
echo "Please check your WeChat Official Account draft box."


path: scripts/publish.sh
content: #!/usr/bin/env bash
# wechat-publisher: 发布 Markdown 到微信公众号草稿箱
# Usage: ./publish.sh <markdown-file> [theme] [highlight]

set -e

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 默认配置
DEFAULT_THEME="lapis"
DEFAULT_HIGHLIGHT="solarized-light"

# 获取脚本目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="${SKILL_ROOT}/wechat.env"

# 检查 wenyan-cli 是否安装
check_wenyan() {
    if command -v wenyan &> /dev/null; then
        WENYAN_CMD="wenyan"
    elif command -v npx &> /dev/null; then
        echo -e "${YELLOW}wenyan-cli 未安装，使用 npx 运行...${NC}"
        WENYAN_CMD="npx @wenyan-md/cli"
    else
        echo -e "${RED}❌ wenyan-cli 和 npx 均未安装！${NC}"
        echo -e "${YELLOW}请安装 Node.js 或手动运行: npm install -g @wenyan-md/cli${NC}"
        exit 1
    fi
}

# 从 wechat.env 读取环境变量
load_credentials() {
    if [ -f "$CONFIG_FILE" ]; then
        echo -e "${YELLOW}📖 从 wechat.env 读取凭证...${NC}"
        # Source only specific vars to avoid polluting env
        export WECHAT_APP_ID=$(grep "^export WECHAT_APP_ID=" "$CONFIG_FILE" | cut -d'"' -f2)
        export WECHAT_APP_SECRET=$(grep "^export WECHAT_APP_SECRET=" "$CONFIG_FILE" | cut -d'"' -f2)
    fi
}

# 检查环境变量
check_env() {
    load_credentials
    
    if [ -z "$WECHAT_APP_ID" ] || [ -z "$WECHAT_APP_SECRET" ]; then
        echo -e "${RED}❌ 环境变量未设置！${NC}"
        echo -e "${YELLOW}请在 wechat.env 中配置微信公众号凭证：${NC}"
        echo ""
        echo "  export WECHAT_APP_ID=your_app_id"
        echo "  export WECHAT_APP_SECRET=your_app_secret"
        echo ""
        exit 1
    fi
}

# 检查文件是否存在
check_file() {
    local file="$1"
    if [ ! -f "$file" ]; then
        echo -e "${RED}❌ 文件不存在: $file${NC}"
        exit 1
    fi
}

# 发布函数
publish() {
    local file="$1"
    local theme="${2:-$DEFAULT_THEME}"
    local highlight="${3:-$DEFAULT_HIGHLIGHT}"
    
    echo -e "${GREEN}📝 准备发布文章...${NC}"
    echo "  文件: $file"
    echo "  主题: $theme"
    echo "  代码高亮: $highlight"
    echo ""
    
    # 执行发布
    $WENYAN_CMD publish -f "$file" -t "$theme" -h "$highlight"
    
    if [ $? -eq 0 ]; then
        echo ""
        echo -e "${GREEN}✅ 发布成功！${NC}"
        echo -e "${YELLOW}📱 请前往微信公众号后台草稿箱查看：${NC}"
        echo "  https://mp.weixin.qq.com/"
    else
        echo ""
        echo -e "${RED}❌ 发布失败！${NC}"
        echo -e "${YELLOW}💡 常见问题：${NC}"
        echo "  1. IP 未在白名单 → 添加到公众号后台"
        echo "  2. Frontmatter 缺失 → 文件顶部添加 title + cover"
        echo "  3. API 凭证错误 → 检查 wechat.env 中的凭证"
        echo "  4. 封面尺寸错误 → 需要 1080×864 像素"
        exit 1
    fi
}

# 显示帮助
show_help() {
    echo "Usage: $0 <markdown-file> [theme] [highlight]"
    echo ""
    echo "Examples:"
    echo "  $0 article.md"
    echo "  $0 article.md lapis"
    echo "  $0 article.md lapis solarized-light"
    echo ""
    echo "Available themes:"
    echo "  default, lapis, phycat, ..."
    echo "  Run 'wenyan theme -l' to see all themes"
    echo ""
    echo "Available highlights:"
    echo "  atom-one-dark, atom-one-light, dracula, github-dark, github,"
    echo "  monokai, solarized-dark, solarized-light, xcode"
}

# 主函数
main() {
    # 检查参数
    if [ $# -eq 0 ] || [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
        show_help
        exit 0
    fi
    
    local file="$1"
    local theme="$2"
    local highlight="$3"
    
    # 执行检查
    check_wenyan
    check_env
    check_file "$file"
    
    # 发布文章
    publish "$file" "$theme" "$highlight"
}

# 运行
main "$@"


path: scripts/setup.sh
content: #!/usr/bin/env bash
# setup.sh - 从 TOOLS.md 读取微信公众号环境变量
# Usage: source ./setup.sh

TOOLS_MD="$HOME/.openclaw/workspace/TOOLS.md"

# 检查 TOOLS.md 是否存在
if [ ! -f "$TOOLS_MD" ]; then
    echo "❌ 找不到 TOOLS.md 文件: $TOOLS_MD"
    echo ""
    echo "请在 TOOLS.md 中添加微信公众号凭证："
    echo ""
    echo "## 🔐 WeChat Official Account (微信公众号)"
    echo ""
    echo "export WECHAT_APP_ID=your_app_id"
    echo "export WECHAT_APP_SECRET=your_app_secret"
    exit 1
fi

# 从 TOOLS.md 提取凭证
WECHAT_APP_ID=$(grep "export WECHAT_APP_ID=" "$TOOLS_MD" | head -1 | sed 's/.*export WECHAT_APP_ID=//' | tr -d ' ')
WECHAT_APP_SECRET=$(grep "export WECHAT_APP_SECRET=" "$TOOLS_MD" | head -1 | sed 's/.*export WECHAT_APP_SECRET=//' | tr -d ' ')

# 检查是否成功提取
if [ -z "$WECHAT_APP_ID" ] || [ -z "$WECHAT_APP_SECRET" ]; then
    echo "❌ 无法从 TOOLS.md 读取凭证！"
    echo ""
    echo "请确保 TOOLS.md 包含以下格式："
    echo ""
    echo "export WECHAT_APP_ID=your_app_id"
    echo "export WECHAT_APP_SECRET=your_app_secret"
    exit 1
fi

# 设置环境变量
export WECHAT_APP_ID
export WECHAT_APP_SECRET

echo "✅ 微信公众号环境变量已从 TOOLS.md 加载！"
echo ""
echo "  WECHAT_APP_ID=${WECHAT_APP_ID:0:10}..."
echo "  WECHAT_APP_SECRET=****** (已隐藏)"
echo ""
echo "💡 提示：这些变量仅在当前 shell 会话有效"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)