원클릭으로
gzh-publisher
微信公众号统一发布技能:通过 OpenClaw Browser 自动化完成登录、写文章、一键排版、封面、存草稿。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
微信公众号统一发布技能:通过 OpenClaw Browser 自动化完成登录、写文章、一键排版、封面、存草稿。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
霸下销售总监大师技能包 — 全球顶尖B2B销售能力。对标华为铁三角+Salesforce+麦肯锡谈判法。
墨白首席美学官大师技能包 — 全球顶级视觉战略能力。对标伦敦皇家艺术学院 + 奥美全球 + 苹果Jony Ive设计标准。
轩辕AI编码舰队——OpenClaw作为大脑,指挥Claude Code/Cursor/Windsurf/Trae/Codex CLI集群并行开发。覆盖:ACP配置、IDE接入、权限管理、任务分发、windsurf+cursor双IDE调度、worktree隔离、舰队看板。触发词:IDE舰队、编码集群、AI IDE、多工具编排、ACP配置、Cursor舰队、Windsurf集群、Trae接入、IDE调度、并行编码、AI编码引擎、IDE编排、coding fleet
项目进度追踪与SDLC全周期管理——ETA承诺、实时看板、风险预警、多项目并行调度。覆盖:任务拆解协议、ETA估算模型、进度追踪机制、日报/周报模板、风险预警系统、承诺驱动开发。触发词:项目进度、SDLC、ETA、甘特图、看板、燃尽图、里程碑、进度追踪、project management、进度汇报、阻塞告警
2025年最前沿的AI原生开发范式——VibeCoding。覆盖:Prompt-to-Product工作流、多轮对话管理、AI编码助手协作策略、代码质量保障、v0.dev/Lovable/Claude Code/Replit Agent工具链集成。触发词:vibecoding、VibeCoding、AI原生开发、提示即产品、AI pair programming、conversation-driven coding、prompt engineering for products
飞书云文档内容优化与格式美化。当用户需要优化飞书文档的排版、结构、格式、美观度时使用此技能。支持:(1) 读取飞书文档内容 (2) 优化文档结构和层次 (3) 清空并替换文档内容 (4) 通过浏览器自动化编辑文档。触发词:优化飞书文档、美化文档、整理文档格式、文档排版。
| name | gzh-publisher |
| description | 微信公众号统一发布技能:通过 OpenClaw Browser 自动化完成登录、写文章、一键排版、封面、存草稿。 |
| metadata | {"version":"3.2.0","author":"Daniel Li","domains":["content","publishing","automation","wechat"],"type":"automation"} |
唯一官方微信公众号发布方式。基于 OpenClaw Browser 自动化。
# 推荐:自动扫描 gzh/ 目录图片
python3 scripts/publish.py --article gzh/article.md --author "Daniel"
# 跳过一键排版
python3 scripts/publish.py --article gzh/article.md --author "Daniel" --no-format
# 跳过封面
python3 scripts/publish.py --article gzh/article.md --author "Daniel" --no-cover
需要 OpenClaw Browser 运行中(openclaw browser start)。
📋 发布前确认内容合规:
~/clawd/projects/MediaClaw/references/platforms/weixin-mp.md
┌─────────────────────────────────────────────────────┐
│ Step 1: 检查登录状态 │
│ browser → navigate(mp.weixin.qq.com) │
├─────────────────────────────────────────────────────┤
│ Step 2: 新建文章(新标签页 appmsg_edit) │
├─────────────────────────────────────────────────────┤
│ Step 3: 填标题(JS evaluate, textarea) │
├─────────────────────────────────────────────────────┤
│ Step 4: 填作者(JS evaluate, input) │
├─────────────────────────────────────────────────────┤
│ Step 5: 粘贴素材图片(clipboard + Ctrl+V) │
│ → 每张等8秒 CDN 转换 → 收集 mmbiz URL │
├─────────────────────────────────────────────────────┤
│ Step 6: 设封面「从正文选择」← 必须在 HTML 注入前! │
├─────────────────────────────────────────────────────┤
│ Step 7: 注入正文 HTML(CDP WebSocket, 含 CDN 图片) │
├─────────────────────────────────────────────────────┤
│ Step 8: 一键排版(新标签页 articlestruct) │
│ → 点"使用此排版" → 图片不会丢失 ✅ │
├─────────────────────────────────────────────────────┤
│ Step 9: 保存草稿 │
└─────────────────────────────────────────────────────┘
innerHTML 注入 <img> → 图片不加载DOM.setFileInputFiles → 上传但不插入编辑器browser upload → 同上Type not supportedarticlestruct,需切换 targetId 后点"使用此排版"fill() 在微信 textarea 上会超时ta.value = ... + dispatchEvent('input') via browser evaluateevaluate 的 fn 有字符长度限制,>2000字会被截断/tmp/wechat-article.html → CDP WebSocket Runtime.evaluate 注入browser(action="navigate", profile="openclaw", url="https://mp.weixin.qq.com")
browser(action="snapshot", profile="openclaw")
// 点"新的创作"
browser(action="act", kind="evaluate", fn="""
() => {
const h2s = document.querySelectorAll('h2');
for (const h of h2s) {
if (h.textContent.trim() === '新的创作') { h.click(); return 'clicked'; }
}
}
""")
// 等1秒 → 点"文章"(y>300 的第一个文本="文章"的元素)
// 等待6秒 → 用 tabs 找 appmsg_edit 标签页
browser(action="tabs", profile="openclaw")
browser(action="act", kind="evaluate", targetId=编辑器, fn="""
() => {
const ta = document.querySelector('textarea[placeholder*="标题"]');
ta.focus(); ta.value = '文章标题';
ta.dispatchEvent(new Event('input', {bubbles:true}));
return 'filled: ' + ta.value;
}
""")
browser(action="act", kind="evaluate", targetId=编辑器, fn="""
() => {
const inp = document.querySelector('input[placeholder*="作者"]');
inp.focus(); inp.value = 'Daniel';
inp.dispatchEvent(new Event('input', {bubbles:true}));
return 'filled';
}
""")
必须用 CDP WebSocket 方式(browser 工具的 evaluate 传 base64 太大):
# 写成独立脚本 /tmp/paste_images.py,exec 运行
import asyncio, json, base64, httpx, websockets, os
async def paste_all(images, target_id):
async with httpx.AsyncClient() as c:
r = await c.get("http://127.0.0.1:18800/json/list")
ws_url = next(t["webSocketDebuggerUrl"] for t in r.json() if target_id in t["id"])
async with websockets.connect(ws_url, max_size=50*1024*1024) as ws:
# Grant clipboard
await ws.send(json.dumps({"id":0,"method":"Browser.grantPermissions",
"params":{"permissions":["clipboard-read","clipboard-write"],"origin":"https://mp.weixin.qq.com"}}))
await ws.recv()
for img_path in images:
with open(img_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
# Write to clipboard
await ws.send(json.dumps({"id":1,"method":"Runtime.evaluate",
"params":{"expression":f"""
(async () => {{
const b = await fetch('data:image/png;base64,{b64}').then(r=>r.blob());
await navigator.clipboard.write([new ClipboardItem({{'image/png': b}})]);
}})()
"""}}))
await ws.recv()
await asyncio.sleep(0.5)
# Focus + Ctrl+V
await ws.send(json.dumps({"id":2,"method":"Runtime.evaluate",
"params":{"expression":"document.querySelector('.ProseMirror').focus()"}}))
await ws.recv()
for ev in ["keyDown","keyUp"]:
await ws.send(json.dumps({"id":3,"method":"Input.dispatchKeyEvent",
"params":{"type":ev,"modifiers":2,"key":"v","code":"KeyV","windowsVirtualKeyCode":86}}))
await ws.recv()
await asyncio.sleep(8) # Wait for CDN conversion
JPG 必须先转 PNG(Pillow):Image.open(p).save(p.with_suffix('.png'), 'PNG')
收集 CDN URL:
browser(action="act", kind="evaluate", targetId=编辑器, fn="""
() => JSON.stringify(
Array.from(document.querySelectorAll('.ProseMirror img'))
.filter(i => i.src.includes('mmbiz')).map(i => i.src)
)
""")
⚠️ 必须在 Step 7 HTML 注入之前做!
// 1. 点"从正文选择"
browser(action="act", kind="evaluate", targetId=编辑器, fn="""
() => {
const items = document.querySelectorAll('#js_cover_description_area a, a');
for (const el of items) {
if (el.textContent.includes('从正文选择')) { el.click(); return 'clicked'; }
}
return 'not found';
}
""")
// 2. 选第一张图
browser(action="act", kind="evaluate", targetId=编辑器, fn="""
() => {
const items = document.querySelectorAll('.appmsg_content_img_item');
if (items.length > 0) { items[0].click(); return 'selected'; }
return 'empty';
}
""")
// 3. 下一步 → 确认
长 HTML 必须用 CDP(先写文件再注入):
# 1. 生成 HTML 写入文件(图片 URL 替换为 CDN)
write(path="/tmp/wechat-article.html", content=生成的HTML)
# 2. CDP 注入
python3 << 'EOF'
import json, asyncio, httpx, websockets
async def inject():
async with httpx.AsyncClient() as c:
r = await c.get("http://127.0.0.1:18800/json/list")
ws_url = next(t["webSocketDebuggerUrl"] for t in r.json() if TARGET in t["id"])
with open("/tmp/wechat-article.html") as f:
html = f.read()
async with websockets.connect(ws_url) as ws:
await ws.send(json.dumps({"id":1,"method":"Runtime.evaluate","params":{
"expression": f"(() => {{ const e = document.querySelector('.ProseMirror'); e.innerHTML = {json.dumps(html)}; e.dispatchEvent(new Event('input', {{bubbles:true}})); return 'ok'; }})()",
"returnByValue": True
}}))
print(json.loads(await ws.recv())["result"]["result"]["value"])
asyncio.run(inject())
EOF
// 1. 在编辑器页点"一键排版"
browser(action="act", kind="evaluate", targetId=编辑器, fn="""
() => {
const all = document.querySelectorAll('span, div, button, a');
for (const el of all) {
if (el.textContent.trim() === '一键排版' && el.offsetParent !== null) {
el.click(); return 'clicked';
}
}
}
""")
// 2. 等待 → 用 tabs 找 articlestruct 标签页
// 3. 在排版页点"使用此排版"
browser(action="act", kind="click", ref=使用此排版ref, targetId=排版页)
// 4. 排版自动应用,回到编辑器
browser(action="act", kind="evaluate", targetId=编辑器, fn="""
() => {
const btns = document.querySelectorAll('button');
for (const b of btns) {
if (b.textContent.includes('保存为草稿') && b.offsetParent !== null) {
b.click(); return 'clicked';
}
}
}
""")
| Markdown | HTML 样式 |
|---|---|
# H1 | 22px bold, text-align center |
## H2 | 18px bold, border-left 4px #1a73e8 |
### H3 | 16px bold |
**bold** | <strong> |
*italic* | <em> |
`code` | bg #f6f8fa, padding 2px 6px |
> quote | border-left 3px #ddd, color #666 |
 | 居中, max-width 100% |
正文段落:font-size:15px; color:#3f3f3f; line-height:1.75; margin:8px 0
输入:
article_path: string # Markdown 文章路径(必需)
author: string # 作者名(可选)
image_paths: list # 素材图片(可选,默认自动扫描 gzh/ 目录)
输出:
status: ok | error
draft_saved: true/false
appmsgid: string
title: string
images_uploaded: number
format_applied: boolean
cover_set: boolean
| Skill | 关系 |
|---|---|
wemp-operator | 全 API(统计/评论/粉丝/素材/群发) |
daily-gzh-content | 公众号内容生成 |
wechat-toolkit | 文章下载工具 |
v3.2.0 (2026-04-22): 修复图片位置bug
v3.1.0 (2026-04-18): 实战验证固化
v3.0.0 (2026-04-18): 大重写