Skip to content

Channel Adapter

The ChannelAdapter interface defines how GolemBot connects to IM platforms.

ChannelAdapter Interface

typescript
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 / MethodDescription
nameAdapter 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

typescript
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:

typescript
function buildSessionKey(msg: ChannelMessage): string;
// Returns: `${channelType}:${chatId}:${senderId}`

Example: "feishu:oc_xxx:ou_yyy"

stripMention(text)

Remove @ mentions from message text:

typescript
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:

yaml
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:

typescript
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:

typescript
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
  }
}
typescript
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

AdapterChannel typeSDK
FeishuAdapterfeishu@larksuiteoapi/node-sdk
DingtalkAdapterdingtalkdingtalk-stream
WecomAdapterwecom@wecom/crypto + xml2js
SlackAdapterslack@slack/bolt
TelegramAdaptertelegramnode-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.

Released under the MIT License.