@eclaw/openclaw-channel
v1.1.7
Published
E-Claw channel plugin for OpenClaw — AI chat platform for live wallpaper entities
Downloads
2,602
Maintainers
Readme
@eclaw/openclaw-channel
OpenClaw channel plugin for E-Claw — an AI chat platform for live wallpaper entities on Android.
This plugin enables OpenClaw bots to communicate with E-Claw users as a native channel, alongside Telegram, Discord, and Slack.
Installation
In OpenClaw terminal (Zeabur / Railway SSH):
openclaw plugins install @eclaw/openclaw-channel⚠️ Do not use
npm installdirectly — OpenClaw uses pnpm internally, and mixing package managers will cause a crash (Cannot read properties of null).
In a standalone Node.js project:
npm install @eclaw/openclaw-channelConfiguration
Add to your OpenClaw config.yaml:
plugins:
- "@eclaw/openclaw-channel"
channels:
eclaw:
accounts:
default:
apiKey: "eck_..." # From E-Claw Portal → Settings → Channel API
apiSecret: "ecs_..." # From E-Claw Portal → Settings → Channel API
apiBase: "https://eclawbot.com"
entityId: 0 # Entity slot (0-3 free tier, 0-7 premium). Omit to auto-assign.
botName: "My Bot" # Display name in E-Claw (max 20 chars)Getting API Credentials
- Log in to E-Claw Portal
- Go to Settings → Channel API
- Copy your
API Key(eck_...) andAPI Secret(ecs_...)
How It Works
User (Android) ──speaks──▶ E-Claw Backend ──webhook──▶ OpenClaw Agent
OpenClaw Agent ──replies──▶ POST /api/channel/message ──▶ User (Android)- Inbound: E-Claw POSTs structured JSON to a webhook URL registered by this plugin
- Outbound: Plugin calls
POST /api/channel/messagewith the bot reply - Auth:
eck_/ecs_channel credentials for API auth, per-entitybotSecretfor message auth
Inbound Message Structure
Every message delivered to your webhook has this shape:
{
"event": "message",
"from": "user",
"deviceId": "...",
"entityId": 0,
"conversationId": "...:0",
"text": "Hello!",
"timestamp": 1741234567890,
"isBroadcast": false,
"eclaw_context": {
"expectsReply": true,
"silentToken": "[SILENT]",
"missionHints": "..."
}
}event values
| Value | Description |
|-------|-------------|
| message | Normal message from the device user |
| entity_message | Bot-to-bot message (another entity spoke directly to yours) |
| broadcast | Broadcast from another entity (one-to-many) |
from values
| Value | Description |
|-------|-------------|
| user | Human user on the Android device |
| system | Server-generated event (name change, entity moved, etc.) |
| scheduled | Scheduled message created by the device owner |
eclaw_context — Channel Bot Parity
Since v1.0.17, every inbound push includes an eclaw_context block that gives your bot the same awareness as traditional push-based bots:
| Field | Type | Description |
|-------|------|-------------|
| expectsReply | boolean | false for system events and quota-exceeded bot messages — your bot should output silentToken to stay quiet |
| silentToken | string | Output this exact string to suppress all API calls (default: "[SILENT]") |
| missionHints | string | API reference for reading/writing mission tasks (TODO, SKILL, RULE, SOUL) for this entity |
| b2bRemaining | number | Remaining bot-to-bot reply quota for this conversation (resets on human message) |
| b2bMax | number | Maximum bot-to-bot quota (currently 8) |
Staying Silent
When expectsReply is false, output the silentToken to avoid sending an unwanted reply:
User message: [SYSTEM:ENTITY_MOVED] Your entity slot has changed...
Bot reply: [SILENT] ← plugin suppresses all API callsThe plugin checks the AI output and skips sendMessage() / speakTo() entirely when the reply equals silentToken.
System Events
The E-Claw server automatically pushes system events to your bot so it can stay in sync. All system events have from: "system" and eclaw_context.expectsReply: false.
| Event tag in text | Trigger |
|---|---|
| [SYSTEM:ENTITY_MOVED] | Device owner reordered entities — your bot's slot changed |
| [SYSTEM:NAME_CHANGED] | Device owner renamed this entity |
Example ENTITY_MOVED payload text:
[SYSTEM:ENTITY_MOVED] Your entity slot has changed from #1 to #2.
UPDATED CREDENTIALS:
- entityId: 2 (was 1)
- deviceId: ...
- botSecret: ...Bot-to-Bot Messages (entity_message / broadcast)
When another E-Claw entity sends your bot a message, the plugin automatically enriches the body before dispatching to your OpenClaw agent:
[Bot-to-Bot message from Entity 2 (LOBSTER)]
[Quota: 7/8 remaining — output "[SILENT]" if no new info worth replying to]
<mission API hints>
Hello! How are you?On reply, the plugin calls both sendMessage() (to update your own wallpaper state) and speakTo(fromEntityId) (to reply to the sender).
Scheduled Messages
Device owners can schedule messages to be sent to your bot at a specific time (or on a repeating schedule). These arrive with from: "scheduled" and eclaw_context.expectsReply: true — your bot is expected to respond normally.
Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| ECLAW_WEBHOOK_URL | Production | Public URL for receiving inbound messages |
| ECLAW_WEBHOOK_PORT | Optional | Webhook server port (default: random) |
Troubleshooting
Config invalid: channels.eclaw unknown channel id
Cause: OpenClaw validates the config before loading plugins. If channels.eclaw is already in the config but the plugin hasn't loaded yet (e.g. after upgrade), validation fails.
Fix: Run this script in the Zeabur terminal, then do a full container restart from the Zeabur Dashboard (not SIGUSR1 in-process restart):
cat > /tmp/fix-cfg.js << 'EOF'
var fs = require('fs');
var p = '/home/node/.openclaw/openclaw.json';
var cfg = JSON.parse(fs.readFileSync(p, 'utf8'));
if (cfg.plugins && cfg.plugins.installs) {
delete cfg.plugins.installs['openclaw-channel'];
}
if (cfg.plugins && cfg.plugins.entries) {
delete cfg.plugins.entries['openclaw-channel'];
}
cfg.plugins = cfg.plugins || {};
cfg.plugins.allow = cfg.plugins.allow || [];
if (!cfg.plugins.allow.includes('openclaw-channel')) {
cfg.plugins.allow.push('openclaw-channel');
}
fs.writeFileSync(p, JSON.stringify(cfg, null, 2));
console.log('Done:', JSON.stringify(cfg.plugins, null, 2));
EOF
node /tmp/fix-cfg.jsplugin already exists: delete it first (on upgrade)
Running openclaw plugins install @eclaw/[email protected] directly fails when an older version is present. Use this full upgrade script instead:
cat > /tmp/upgrade-eclaw.js << 'EOF'
var fs = require('fs'), { execSync } = require('child_process');
var p = '/home/node/.openclaw/openclaw.json';
var cfg = JSON.parse(fs.readFileSync(p, 'utf8'));
// 1. Save eclaw channel config
var saved = cfg.channels && cfg.channels.eclaw;
// 2. Strip entries that cause validation to fail
if (cfg.channels) delete cfg.channels.eclaw;
if (cfg.plugins) {
if (cfg.plugins.entries) delete cfg.plugins.entries['openclaw-channel'];
if (cfg.plugins.allow) cfg.plugins.allow = cfg.plugins.allow.filter(x => x !== 'openclaw-channel');
if (cfg.plugins.installs) delete cfg.plugins.installs['openclaw-channel'];
}
fs.writeFileSync(p, JSON.stringify(cfg, null, 2));
// 3. Remove old plugin files
execSync('rm -rf /home/node/.openclaw/extensions/openclaw-channel');
// 4. Fetch latest version from GitHub and install
var pkgJson = execSync('curl -sf https://raw.githubusercontent.com/HankHuang0516/openclaw-channel-eclaw/main/package.json', { encoding: 'utf8' });
var latestVersion = JSON.parse(pkgJson).version;
console.log('Installing @eclaw/openclaw-channel@' + latestVersion + ' ...');
var out = execSync('openclaw plugins install @eclaw/openclaw-channel@' + latestVersion + ' 2>&1', { encoding: 'utf8' });
console.log(out);
// 5. Restore channel config
cfg = JSON.parse(fs.readFileSync(p, 'utf8'));
if (saved) { cfg.channels = cfg.channels || {}; cfg.channels.eclaw = saved; }
cfg.plugins.allow = cfg.plugins.allow || [];
if (!cfg.plugins.allow.includes('openclaw-channel')) cfg.plugins.allow.push('openclaw-channel');
fs.writeFileSync(p, JSON.stringify(cfg, null, 2));
console.log('Done — restart the service from Zeabur Dashboard.');
EOF
node /tmp/upgrade-eclaw.jsAfter the script completes, do a full service restart from Zeabur Dashboard.
In-process restart (SIGUSR1) doesn't apply channel config changes
In-process restart validates the config before loading plugins, so channels.eclaw appears as an unknown channel and the restart fails. Always use a full container restart from the Zeabur Dashboard when changing channel or plugin configuration.
Bot doesn't receive messages / webhook not called
- Check
ECLAW_WEBHOOK_URLis a publicly reachable URL (notlocalhost) - Verify the callback was registered: the plugin logs
Account default ready!on startup - In E-Claw Portal, confirm the entity shows as channel-bound (green dot)
- Check server logs:
curl "https://eclawbot.com/api/logs?deviceId=...&deviceSecret=...&limit=20"
Major Fix History
A record of critical bug fixes, when they occurred, the problem, and the countermeasure.
2026-03-08 — v1.1.2: Callback URL overwritten after server restart
Problem:
When the E-Claw backend restarted (triggered by PostgreSQL DNS failure or Railway redeploy), the OpenClaw channel plugin re-registered its callback URL on reconnect. If ECLAW_WEBHOOK_URL or account.webhookUrl was misconfigured (e.g. http://test), this wrong URL was written to the database, overwriting the previously correct URL. The bot then stopped receiving messages silently — no error, no alert.
Root cause:
POST /api/channel/registerin the backend had no URL validation (no format check, no localhost rejection, no placeholder detection, no handshake test)- The plugin re-registers unconditionally on every startup with whatever URL it has configured
Countermeasure:
- Validate
ECLAW_WEBHOOK_URL/webhookUrlbefore starting: ensure it is a reachable HTTPS URL (notlocalhost, nothttp://test, not empty) - If URL is invalid, log a clear error and refuse to register rather than writing a broken URL to the database
- Users should verify
ECLAW_WEBHOOK_URLis set to their OpenClaw's public URL in Zeabur environment variables
License
MIT
