Channel Adapter
The ChannelAdapter interface defines how GolemBot connects to IM platforms.
ChannelAdapter Interface
interface ChannelAdapter {
readonly name: string;
/** Optional: override the default 4000-char message split limit for this channel. */
readonly maxMessageLength?: number;
start(onMessage: (msg: ChannelMessage) => void | Promise<void>): Promise<void>;
reply(msg: ChannelMessage, text: string): Promise<void>;
stop(): Promise<void>;
}| Property / Method | Description |
|---|---|
name | Adapter name (e.g., 'feishu', 'dingtalk', 'my-email') |
maxMessageLength | (optional) Override the default 4000-char split limit for long replies |
start(onMessage) | Connect to the platform and begin listening. Call onMessage for each incoming message. |
reply(msg, text) | Send a text reply to the original message |
stop() | Gracefully disconnect |
ChannelMessage Type
interface ChannelMessage {
channelType: string; // 'feishu' | 'dingtalk' | 'wecom'
senderId: string; // User ID on the platform
senderName?: string; // Display name (if available)
chatId: string; // Chat/conversation ID
chatType: 'dm' | 'group';
text: string; // Message text content
raw: unknown; // Raw SDK event object
}Helper Functions
buildSessionKey(msg)
Generate a session key from a channel message:
function buildSessionKey(msg: ChannelMessage): string;
// Returns: `${channelType}:${chatId}:${senderId}`Example: "feishu:oc_xxx:ou_yyy"
stripMention(text)
Remove @ mentions from message text:
function stripMention(text: string): string;Handles:
- XML-style:
<at user_id="xxx">BotName</at> - Plain text:
@BotName
Custom Adapters via golem.yaml
You can plug any message source into GolemBot — email, GitHub Issues, Discord, cron triggers, or anything else — without touching the framework code. Declare a custom channel in golem.yaml with an _adapter field pointing to your adapter file or npm package:
name: my-assistant
engine: claude-code
channels:
# Built-in channel (unchanged)
slack:
botToken: ${SLACK_BOT_TOKEN}
appToken: ${SLACK_APP_TOKEN}
# Custom channel — local file (relative to the assistant directory)
my-email:
_adapter: ./adapters/email-adapter.js
host: imap.gmail.com
token: ${EMAIL_TOKEN}
# Custom channel — npm package
discord:
_adapter: golembot-discord-adapter
token: ${DISCORD_TOKEN}Path resolution rules:
- Starts with
.or/→ resolved relative to the assistant directory - Anything else → treated as an npm package name (resolved by Node.js module resolution)
Writing an Adapter
Your adapter file must export a default class that implements the ChannelAdapter interface. All config fields from golem.yaml are passed to the constructor:
import type { ChannelAdapter, ChannelMessage } from 'golembot';
export default class EmailAdapter implements ChannelAdapter {
readonly name: string;
readonly maxMessageLength = 10000; // optional — overrides the default 4000
constructor(private config: Record<string, unknown>) {
this.name = (config.channelName as string) ?? 'email';
}
async start(onMessage: (msg: ChannelMessage) => void | Promise<void>): Promise<void> {
// Start listening (IMAP, webhook, polling, etc.)
// Call onMessage() for each incoming message:
onMessage({
channelType: 'email',
senderId: email.from,
senderName: email.fromName,
chatId: email.threadId,
chatType: 'dm',
text: email.body,
raw: email,
});
}
async reply(msg: ChannelMessage, text: string): Promise<void> {
// Send the reply (SMTP, API call, etc.)
}
async stop(): Promise<void> {
// Clean up connections
}
}GolemBot handles all message routing, session management, and reply splitting automatically once your adapter is loaded.
Implementing a Custom Adapter Programmatically
If you're embedding GolemBot in your own application and want to wire up a channel manually (without golem.yaml), implement the interface and integrate with createAssistant() directly:
import type { ChannelAdapter, ChannelMessage } from 'golembot';
class MyAdapter implements ChannelAdapter {
readonly name = 'my-channel';
async start(onMessage: (msg: ChannelMessage) => void | Promise<void>) {
// Connect and call onMessage for each incoming message
}
async reply(msg: ChannelMessage, text: string) {
// Send reply
}
async stop() {
// Disconnect
}
}import { createAssistant, buildSessionKey, stripMention } from 'golembot';
const assistant = createAssistant({ dir: './my-bot' });
const adapter = new MyAdapter();
await adapter.start(async (msg) => {
const sessionKey = buildSessionKey(msg);
const text = stripMention(msg.text);
let reply = '';
for await (const event of assistant.chat(text, { sessionKey })) {
if (event.type === 'text') reply += event.content;
}
await adapter.reply(msg, reply);
});Built-in Adapters
| Adapter | Channel type | SDK |
|---|---|---|
FeishuAdapter | feishu | @larksuiteoapi/node-sdk |
DingtalkAdapter | dingtalk | dingtalk-stream |
WecomAdapter | wecom | @wecom/crypto + xml2js |
SlackAdapter | slack | @slack/bolt |
TelegramAdapter | telegram | node-telegram-bot-api |
These are used internally by the gateway service. To use them, configure the corresponding channel type in golem.yaml — no _adapter field needed.