OpenClaw plugin for conservative, self-contained WeCom sticker delivery.
The primary path is agentic: the assistant may call the send_sticker tool to queue one sticker for the current reply. The plugin then runs in reply_payload_sending, syncs packaged PNG assets, and resolves the queued sticker into payload mediaUrls for WeCom.
Format guard support for [sticker:name] and MEDIA: remains as a compatibility fallback, not the preferred model-facing interface.
- WeCom only by default:
channels: ["wecom"] - Registers
send_stickerfor the assistant to queuehappy,love,confused,sigh,awkward,nervous, orcool - Syncs packaged assets from
resources/into{workspaceDir}/stickersunlessassetSync.enabledis disabled - Format guard still fixes
📎, Markdown image syntax, inlineMEDIA:, leading spaces, absolute sticker paths, and[sticker:name] - Auto append is disabled by default; when enabled it supports:
- success:
happy/love - minor failure:
sigh/awkward - uncertainty:
confused
- success:
- serious, long, code-heavy, apology, incident, complaint, legal, medical, HR, and security contexts are blocked
Run unit tests:
pnpm testRun typecheck:
pnpm typecheckBuild the plugin runtime:
pnpm buildSmoke test the built tool-to-hook path without starting Gateway:
node -e "import('./dist/index.js').then(async ({default: entry}) => { let h, f; const api={rootDir:'/tmp/openclaw-state/extensions/claw-sticker',pluginConfig:{},logger:{info(){},warn(){},error(){},debug(){}},registerHostedMediaResolver(){},registerTool(factory){f=factory},on(name,handler){ if(name==='reply_payload_sending') h=handler; }}; entry.register(api); await f({sessionKey:'room',messageChannel:'wecom'}).execute('tool-1',{name:'happy'}); console.log(await h({payload:{text:'已完成。'},channel:'wecom',sessionKey:'room'}, {channelId:'wecom',conversationId:'room'})); })"Expected output contains:
mediaUrls: [ '<resolved OpenClaw state dir>/workspace/stickers/happy.png' ]
- Build this package with
pnpm build. - Install or copy the plugin so OpenClaw can load
openclaw.plugin.jsonanddist/index.js. - Enable the plugin:
{
"plugins": {
"entries": {
"claw-sticker": {
"enabled": true,
"config": {
"channels": ["wecom"],
"mediaBasePath": "{workspaceDir}/stickers",
"assetSync": { "enabled": true },
"tool": { "enabled": true },
"formatGuard": { "enabled": true },
"autoAppend": { "enabled": false }
}
}
},
"allow": ["claw-sticker"]
}
}If your plugins.allow is absent or empty, no allowlist change is needed. If it is restrictive, add claw-sticker.
- Restart Gateway or reload plugins according to your OpenClaw runtime.
- The normal path is tool-driven. The assistant should call:
{
"name": "happy",
"reason": "task_success"
}with the send_sticker tool when a small sticker fits the final reply.
For manual smoke testing, send this through a WeCom-bound session:
请只回复下面内容,不要解释:
[sticker:happy]
Expected outbound content:
搞定了
mediaUrls: ["<resolved OpenClaw state dir>/workspace/stickers/happy.png"]
mediaBasePath is optional. By default, the plugin resolves {workspaceDir} from the active OpenClaw state directory, copies packaged PNG assets from resources/ into {workspaceDir}/stickers, then sends those absolute local files through WeCom.
The tool currently exposes fixed semantic sticker names:
happy, love, confused, sigh, awkward, nervous, cool
To customize the images without changing code, keep those file names and replace the PNG files in a directory you control.
Recommended setup:
- Create a custom sticker directory under the OpenClaw state directory:
mkdir -p ~/.openclaw/custom-stickers- Put PNG files with these exact names in that directory:
happy.png
love.png
confused.png
sigh.png
awkward.png
nervous.png
cool.png
- Point the plugin to that directory and disable packaged asset sync:
{
"plugins": {
"entries": {
"claw-sticker": {
"enabled": true,
"config": {
"mediaBasePath": "{stateDir}/custom-stickers",
"assetSync": { "enabled": false }
}
}
}
}
}Do not manually edit {workspaceDir}/stickers while assetSync.enabled is true; the plugin may copy packaged assets there again on startup. For WeCom, use normal PNG files, preferably square, transparent, and reasonably small.
Adding new sticker names beyond the seven listed above requires a code change because the tool schema intentionally restricts the model to known names.
For the optional auto-append fallback, explicitly enable it and use a simple completion reply such as:
已完成,测试通过了。
Because auto append is probabilistic and cooled down, it may not append every time. To validate decision logic without sending stickers, set:
{
"autoAppend": {
"enabled": true,
"dryRun": true
}
}Then check Gateway logs for claw-sticker dry-run decisions.