Social Auto Poster
Cross-platform publishing skill for LinkedIn, X/Twitter, Facebook, WordPress, and Substack. Covers one-time setup, image creation, ARM upload, text entry, and post verification for each platform.
Prerequisites — One-Time Setup
Complete these steps once before using this skill for the first time.
1. Browser Login (LinkedIn, X, Facebook, Substack)
These four platforms use browser session authentication. You must log in manually once and keep the browser session alive.
CODEBLOCK0
Important rules:
- - Never run
pkill -f "Google Chrome" or kill the browser process manually — this destroys all saved sessions and forces you to log in again - Only use
browser stop / browser start via OpenClaw to restart the browser safely - Sessions persist in the browser profile directory and survive restarts as long as you use INLINECODE3
2. WordPress Credentials
WordPress uses REST API with Application Password — no browser needed.
Create a credentials file at ~/.openclaw/.wp-[yoursite].env:
CODEBLOCK1
To generate an Application Password:
- 1. Go to WordPress Admin → Users → Profile
- Scroll to "Application Passwords"
- Enter a name (e.g., "OpenClaw") → click Add New
- Copy the generated password into the
.env file
3. Image Overlay Script
This skill uses a shell script to generate quote images from your photo library. Locate or install it at:
CODEBLOCK2
Verify it works:
CODEBLOCK3
Expected output: a PNG file with your quote overlaid on a background photo.
4. Upload Directory
Ensure the uploads directory exists:
CODEBLOCK4
Note: /tmp/ is cleared on system reboot. Recreate images if needed after restart.
Workflow Overview
For each post, follow this sequence:
CODEBLOCK5
Always create both images first. Opening and closing the browser repeatedly wastes time and risks losing sessions.
Step 1 — Create Images
Run both image commands before opening the browser:
CODEBLOCK6
Verify both files exist and are non-empty before continuing:
ls -lh /tmp/openclaw/uploads/[topic]-en.png /tmp/openclaw/uploads/[topic]-vi.png
Step 2 — LinkedIn
Key constraint: Upload dialog lives inside a Shadow DOM (div.theme--light). ARM + Playwright native click is required. Never use evaluate input.click().
CODEBLOCK8
Step 3 — X / Twitter
Key constraint: ARM interceptor only fires on Playwright native click (click ref). Never use evaluate btn.click() to trigger the file picker — it breaks ARM and causes the image and text to post as two separate tweets.
280-character limit: Count characters before typing. If over, shorten the text first.
CODEBLOCK9
Tip: Include the LinkedIn post URL in the X text to drive cross-platform traffic.
Step 4 — Facebook
Key constraint: After the image uploads, Facebook switches to a new screen and the textbox count drops from 3 to 1. Always re-query the textbox AFTER blob=1 is confirmed — typing before that goes into the wrong element.
CODEBLOCK10
Step 5 — WordPress (REST API)
No browser needed. Load credentials first:
CODEBLOCK11
5a. Upload featured image:
CODEBLOCK12
5b. Build post JSON (always use Python — never heredoc, heredoc breaks UTF-8):
CODEBLOCK13
5c. Publish:
CODEBLOCK14
5d. Patch Yoast SEO OG image (required — skipping this causes avatar.jpg to show when the post is shared):
CODEBLOCK15
5e. Verify OG image:
CODEBLOCK16
Default category IDs (adjust per site): 8=Business, 6=Marketing, 14=Skills, 10=Lifestyle, 5=Other
Step 6 — Substack
Key constraint: Insert the image into the body FIRST, then type text. Opposite of Facebook. Text typed before image insertion gets wiped when ProseMirror re-renders.
CODEBLOCK17
Verify: Navigate to [publication].substack.com — latest post should appear at the top.
Step 7 — Verification Checklist
After posting to all platforms:
| Platform | Verify by |
|---|
| LinkedIn | recent-activity page — post text visible + image attached |
| X |
x.com/home feed — tweet with image visible |
| Facebook | Profile or Page feed — post with image and full text visible |
| WordPress | Visit post URL — featured image correct, OG image not avatar |
| Substack | Publication homepage — post appears at top |
Error Reference
| Symptom | Cause | Fix |
|---|
| X: two posts (1 image-only, 1 text-only) | Used evaluate btn.click() for Add photos | Must use click ref from snapshot |
| X: Post button stays disabled |
Text over 280 chars | Check
tweetTextarea_0.innerText.length, trim and retype |
| FB: image uploaded but no text in post | Typed before blob=1 | Re-query textbox after blob=1 confirmed |
| FB: blob=0 after 6s | ARM missed the file input | ARM again + click Ảnh/video again (max 2 retries) |
| LinkedIn: "N of M" in Editor | Leftover image from previous session | Delete extras until "1 of 1" |
| LinkedIn: Post button stays grey | Text not in shadow DOM editor | Use
type ref not
type selector |
| WP: avatar shows on social share | Yoast OG image not patched | Run Step 5d and verify Step 5e |
| Substack: text missing after publish | Text typed before image insertion | Always insert image first, then type |
| Substack: page not found | Wrong URL format | Use
[pub].substack.com/publish/post/new not
substack.com/new |
| All platforms: session lost after restart | Browser was killed manually | Never
pkill Chrome; always use
browser stop |
Platform Summary
| Platform | Image variant | Auth method | Tool |
|---|
| LinkedIn | EN | Browser session | INLINECODE22 + ARM |
| X/Twitter |
EN | Browser session |
browser + ARM (native click only) |
| Facebook | Local/VI | Browser session |
browser + ARM + re-query textbox |
| WordPress | Local/VI | App password in
.env |
curl REST API |
| Substack | Local/VI | Browser session |
browser + ARM (image before text) |
Social Auto Poster
跨平台发布技能,支持LinkedIn、X/Twitter、Facebook、WordPress和Substack。涵盖一次性设置、图片创建、ARM上传、文本输入以及每个平台的发布验证。
前提条件 — 一次性设置
首次使用此技能前,请完成以下步骤。
1. 浏览器登录(LinkedIn、X、Facebook、Substack)
这四个平台使用浏览器会话认证。您需要手动登录一次并保持浏览器会话活跃。
browser start
navigate linkedin.com → 如提示则登录
navigate x.com → 如提示则登录
navigate facebook.com → 如提示则登录
navigate [publication].substack.com → 如提示则登录
browser stop
重要规则:
- - 切勿运行 pkill -f Google Chrome 或手动终止浏览器进程——这会销毁所有已保存的会话,迫使您重新登录
- 仅通过OpenClaw使用 browser stop / browser start 安全重启浏览器
- 只要使用 browser stop,会话会保留在浏览器配置文件目录中,并在重启后持续存在
2. WordPress凭据
WordPress使用带应用密码的REST API——无需浏览器。
在 ~/.openclaw/.wp-[yoursite].env 创建凭据文件:
bash
WP_URL=https://yoursite.com
WPUSER=yourusername
WPAPPPASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx
生成应用密码:
- 1. 前往WordPress管理后台 → 用户 → 个人资料
- 滚动至应用密码
- 输入名称(例如OpenClaw)→ 点击新增
- 将生成的密码复制到 .env 文件中
3. 图片叠加脚本
此技能使用shell脚本从您的照片库生成引用图片。请定位或安装至:
~/workspace/linkedin-assets/create-overlay-image.sh
验证其工作正常:
bash
bash ~/workspace/linkedin-assets/create-overlay-image.sh 测试引用 /tmp/test-output.png
ls -lh /tmp/test-output.png
预期输出:一个PNG文件,包含叠加在背景照片上的引用文字。
4. 上传目录
确保上传目录存在:
bash
mkdir -p /tmp/openclaw/uploads
注意:系统重启时 /tmp/ 会被清空。重启后如需请重新创建图片。
工作流程概览
对于每篇帖子,按以下顺序操作:
步骤1:创建图片(bash脚本)—— 在打开任何浏览器之前
步骤2:发布到LinkedIn
步骤3:发布到X/Twitter(包含LinkedIn链接)
步骤4:发布到Facebook
步骤5:发布到WordPress(API,无需浏览器)
步骤6:发布到Substack(浏览器)
步骤7:验证所有平台
始终先创建两张图片。反复打开和关闭浏览器会浪费时间并存在丢失会话的风险。
步骤1 — 创建图片
在打开浏览器前运行两个图片命令:
bash
英文图片 — 用于LinkedIn和X
bash ~/workspace/linkedin-assets/create-overlay-image.sh \
您的英文引用文字 \
/tmp/openclaw/uploads/[topic]-en.png
本地语言图片 — 用于Facebook、WordPress、Substack
bash ~/workspace/linkedin-assets/create-overlay-image.sh \
您的本地语言引用文字 \
/tmp/openclaw/uploads/[topic]-vi.png
继续前验证两个文件均存在且非空:
bash
ls -lh /tmp/openclaw/uploads/[topic]-en.png /tmp/openclaw/uploads/[topic]-vi.png
步骤2 — LinkedIn
关键约束: 上传对话框位于Shadow DOM(div.theme--light)内部。必须使用ARM + Playwright原生点击。切勿使用 evaluate input.click()。
browser start → navigate linkedin.com/feed/ → 等待Photo文字出现
ARM上传:
browser upload paths=[/tmp/openclaw/uploads/[topic]-en.png]
(无选择器 — ARM将文件排队等待下一个打开的文件选择器)
snapshot → 获取Photo按钮的ref(一个指向 /preload/sharebox/?detourType=IMAGE 的链接)
click ref [Photo] → 等待编辑器对话框打开
检查:如果对话框显示N of M(例如1 of 2)→ 上次会话遗留
→ 点击Delete按钮直到计数器显示1 of 1
click Next → 等待Create post modal出现
snapshot → 获取标记为Text editor for creating content的文本框ref
click ref [textbox] → type ref [完整帖子文本,包含话题标签]
snapshot → 验证Post按钮处于活动状态(未禁用)
click ref [Post] → 等待8秒
验证:
navigate linkedin.com/in/[username]/recent-activity/all/
evaluate: document.querySelectorAll([data-urn])[0].innerText → 应包含您的文本
browser close tab → browser stop
步骤3 — X / Twitter
关键约束: ARM拦截器仅在Playwright原生点击(click ref)时触发。切勿使用 evaluate btn.click() 触发文件选择器——这会破坏ARM,导致图片和文本作为两条独立推文发布。
280字符限制: 输入前计算字符数。如果超出,先缩短文本。
browser start → navigate x.com/compose/post → 等待3秒
ARM上传:
browser upload paths=[/tmp/openclaw/uploads/[topic]-en.png]
snapshot depth=6 → 获取Add photos or video按钮的ref
(需要depth=6才能暴露嵌套在内部的按钮)
click ref [Add photos or video] ← Playwright原生点击,非evaluate
等待5秒 → evaluate: document.querySelectorAll(img[src*=blob:]).length
→ 必须等于1才能继续
snapshot → 获取文本框Post text的ref(图片附加后ref会变化——始终重新snapshot)
click ref [textbox] → type ref [文本,最多280字符]
evaluate: 发布前验证
document.querySelector([data-testid=tweetButton]).disabled === false
document.querySelector([data-testid=tweetTextarea_0]).innerText.length ≤ 280
evaluate: document.querySelector([data-testid=tweetButton]).click()
等待10秒 → evaluate: location.href → 应为 https://x.com/home
browser close tab → browser stop
提示: 在X文本中包含LinkedIn帖子URL,以驱动跨平台流量。
步骤4 — Facebook
关键约束: 图片上传后,Facebook会切换到新屏幕,文本框数量从3个减少到1个。始终在确认 blob=1 后重新查询文本框——在此之前输入会进入错误元素。
browser start → navigate facebook.com → 等待5秒
evaluate: 点击Whats on your mind? / Bạn đang nghĩ gì?按钮
[...document.querySelectorAll([role=button])]
.find(b => b.textContent.includes(đang nghĩ gì) || b.textContent.includes(on your mind))
.click()
等待2秒 → 验证Create post / Tạo bài viết对话框已打开
ARM上传:
browser upload paths=[/tmp/openclaw/uploads/[topic]-vi.png]
evaluate: 在Tạo bài viết对话框内,点击照片按钮:
dialog.querySelector([aria-label=Ảnh/video]).click()
(英文界面使用[aria-label=Photo/video])
等待6秒 → evaluate: document.querySelectorAll(img[src*=blob:]).length
如果 blob=0:再次ARM + 再次点击照片按钮(最多重试2次)
一旦 blob=1:
evaluate: 重新查询文本框 — 图片加载后恰好有1个:
document.querySelector([role=dialog] [role=textbox][contenteditable=true])
evaluate: tb.click(); tb.focus()
browser act type selector=[role=dialog] [role=textbox][contenteditable=true]
text=[完整帖子文本,包含话题标签]
evaluate: 验证 blob=1 且 textbox.innerText.length > 0
evaluate: 点击Next / Tiếp按钮
等待5秒
evaluate: 点击Post / Đăng按钮
等待8秒
evaluate: 验证撰写对话框已消失:
!document.querySelectorAll([role=dialog]).some(d => d.innerText.includes(Tạo bài viết))
browser close tab → browser stop
步骤5 — WordPress(REST API)
无需浏览器。首先加载凭据:
bash
source ~/.openclaw/.wp-[yoursite].env
5a.