Writing a Channel Plugin ๐ด
This chapter walks through a complete channel plugin from scratch, showing how to add OpenClaw support for a new messaging platform.
I. Plugin Directory Structure
extensions/my-channel/
โโโ openclaw.plugin.json โ plugin manifest (required)
โโโ package.json
โโโ tsconfig.json
โโโ src/
โโโ index.ts โ plugin entry (exports definePlugin result)
โโโ channel.ts โ ChannelPlugin implementation (core)
โโโ client.ts โ HTTP client for platform API
โโโ config.ts โ config type definitions
II. openclaw.plugin.json
{
"id": "my-channel",
"channels": ["my-channel"],
"enabledByDefault": false,
"configSchema": {
"type": "object",
"properties": {
"accounts": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"token": { "type": "string" }
},
"required": ["token"]
}
}
}
}
}
III. Plugin Entry (src/index.ts)
id: 'my-channel',
setup(api) {
api.channel.register(createMyChannelPlugin(api));
}
});
IV. Channel Plugin Implementation
// src/channel.ts
return {
id: 'my-channel',
// Required: messaging adapter
messaging: {
async formatInboundMessage(raw) {
const msg = raw as { sender: string; body: string };
return `[${msg.sender}]\n${msg.body}`;
},
},
// Outbound sending
outbound: {
async send(params) {
const client = new MyPlatformClient(params.accountConfig.token);
await client.sendMessage({ chatId: params.peer.id, text: params.text });
return { ok: true };
},
},
// Lifecycle (start/stop)
lifecycle: {
async start(params) {
this._pollSession = startPollingSession({
client: new MyPlatformClient(params.accountConfig.token),
onMessage: async (rawMsg) => {
await params.ingest({
accountId: params.accountId,
peer: { kind: rawMsg.isPrivate ? 'dm' : 'group', id: String(rawMsg.chatId) },
rawMessage: rawMsg,
});
},
});
},
async stop() { this._pollSession?.stop(); },
},
// Auth check (for health monitoring)
auth: {
async checkToken(params) {
try {
const me = await new MyPlatformClient(params.token).getMe();
return { ok: true, botId: me.id, botName: me.name };
} catch {
return { ok: false, error: 'Invalid token' };
}
},
},
};
}
V. Enable in Config
plugins:
- path: ./extensions/my-channel # local dev plugin
channels:
my-channel:
accounts:
main-account:
token: "${env:MY_PLATFORM_BOT_TOKEN}"
bindings:
- agentId: main
match:
channel: my-channel
VI. Reference Implementation
The Telegram plugin is the most complete official reference:
| File | Size | Reference Value |
|---|---|---|
extensions/telegram/src/channel.ts | 37KB | Complete channel plugin |
extensions/telegram/src/bot.test.ts | 79KB | Comprehensive test suite |
extensions/discord/src/channel.ts | - | Another reference |
Summary
- Minimum channel plugin:
messagingadapter +openclaw.plugin.json. - Webhook over polling: less overhead, faster response.
- Reference Telegram plugin: most complete official implementation (37KB channel.ts).
- Local dev: use
path: ./extensions/my-channelwithout publishing to npm.