乐于分享
好东西不私藏

OpenClaw-渠道接入层 (Channel Layer)分析

OpenClaw-渠道接入层 (Channel Layer)分析

文档版本:2026.3.24  最后更新:2026-03-25

1. 层概述

职责: 连接各个消息平台,提供统一的消息收发接口

定位: Gateway 和外部消息平台之间的桥梁

核心特性:

  • • 多渠道支持 (9个内置聊天渠道 + 25+扩展平台)
  • • 统一消息抽象
  • • 插件化架构
  • • 独立故障隔离

内置聊天渠道 (9个,CHAT_CHANNEL_ORDER 顺序):
tg, whatsapp, discord, irc, googlechat, slack, signal, imessage, line

变更说明: IRC 和 Google Chat 自 v2026.3 起已升级为一级内置聊天渠道;
ACP(内部代理协议)和 Web(提供者接口)不再列于 CHAT_CHANNEL_META,各自以独立模块运行。

通道注册: src/channels/ids.ts (CHAT_CHANNEL_ORDER / CHANNEL_IDS常量)


2. 核心渠道实现 (extensions/)

2.1 WhatsApp (Baileys)

路径: extensions/whatsapp/ (插件通道)

: src/whatsapp/ 仅包含消息归一化工具,主要实现在extensions

协议: Baileys Web (开源 WhatsApp Web 协议实现)

功能:

  • • 扫码登录 (QR Code)
  • • 文本消息收发
  • • 媒体消息 (图片/音频/视频/文档)
  • • 群组消息
  • • 引用回复
  • • 状态更新

技术实现:

import makeWASocket from '@whiskeysockets/baileys'

export
 class WhatsAppChannel {
  private
 sock?: WASocket
  
  async
 start(): Promise<void> {
    const
 { state, saveCreds } = await useMultiFileAuthState('./auth')
    
    this
.sock = makeWASocket({
      auth
: state,
      printQRInTerminal
: true
    })
    
    this
.sock.ev.on('connection.update', this.handleConnection)
    this
.sock.ev.on('messages.upsert', this.handleMessage)
    this
.sock.ev.on('creds.update', saveCreds)
  }
  
  async
 sendMessage(to: string, content: string): Promise<void> {
    await
 this.sock!.sendMessage(to, { text: content })
  }
}

依赖: @whiskeysockets/baileys (7.0.0-rc.9)

2.2 TG (grammY)

路径: extensions/tele*/ (插件通道)

协议: TG Bot API

功能:

  • • Bot Token 认证
  • • 私聊和群组消息
  • • 内联键盘
  • • 媒体消息
  • • Webhook 模式
  • • 长轮询模式

技术实现:

import { Bot } from 'grammy'
import
 { run } from '@grammyjs/runner'

export
 class TelegramChannel {
  private
 bot: Bot
  
  async
 start(token: string): Promise<void> {
    this
.bot = new Bot(token)
    
    this
.bot.on('message', async (ctx) => {
      await
 this.handleMessage(ctx)
    })
    
    // 使用 runner 提高性能

    run
(this.bot)
  }
  
  async
 sendMessage(chatId: number, text: string): Promise<void> {
    await
 this.bot.api.sendMessage(chatId, text)
  }
}

依赖:

  • grammy (1.39.3)
  • @grammyjs/runner (2.0.3)
  • @grammyjs/transformer-throttler (1.2.1)

2.3 Slack (Bolt)

路径: extensions/slack/ (插件通道)

协议: Slack Bolt SDK

功能:

  • • Socket Mode (无需公网 IP)
  • • 事件订阅
  • • 斜杠命令
  • • 交互式消息
  • • 文件上传
  • • 线程回复

技术实现:

import { App } from '@slack/bolt'

export
 class SlackChannel {
  private
 app: App
  
  async
 start(token: string, appToken: string): Promise<void> {
    this
.app = new App({
      token,
      appToken,
      socketMode
: true
    })
    
    this
.app.message(async ({ message, say }) => {
      await
 this.handleMessage(message)
    })
    
    await
 this.app.start()
  }
  
  async
 sendMessage(channel: string, text: string): Promise<void> {
    await
 this.app.client.chat.postMessage({
      channel,
      text
    })
  }
}

依赖: @slack/bolt (4.6.0)

2.4 Discord

路径: extensions/discord/ (插件通道)

协议: Discord.js

功能:

  • • Bot Token 认证
  • • 文本频道消息
  • • 语音频道 (可选)
  • • 嵌入消息
  • • 按钮和选择菜单
  • • 斜杠命令

技术实现:

import { Client, GatewayIntentBits } from 'discord.js'

export
 class DiscordChannel {
  private
 client: Client
  
  async
 start(token: string): Promise<void> {
    this
.client = new Client({
      intents
: [
        GatewayIntentBits
.Guilds,
        GatewayIntentBits
.GuildMessages,
        GatewayIntentBits
.MessageContent
      ]
    })
    
    this
.client.on('messageCreate', async (message) => {
      await
 this.handleMessage(message)
    })
    
    await
 this.client.login(token)
  }
  
  async
 sendMessage(channelId: string, content: string): Promise<void> {
    const
 channel = await this.client.channels.fetch(channelId)
    if
 (channel?.isTextBased()) {
      await
 channel.send(content)
    }
  }
}

依赖: discord-api-types (0.38.40) + @snazzah/davey(DAVE 语音加密)

2.5 Google Chat

路径: src/channels/googlechat/, extensions/googlechat/

协议: Google Chat API

功能:

  • • Service Account 认证
  • • 空间消息
  • • 私聊消息
  • • 卡片消息
  • • 线程回复

技术实现:

import { google } from 'googleapis'

export
 class GoogleChatChannel {
  private
 chat: any
  
  async
 start(credentials: ServiceAccountCreds): Promise<void> {
    const
 auth = new google.auth.GoogleAuth({
      credentials,
      scopes
: ['https://www.googleapis.com/auth/chat.bot']
    })
    
    this
.chat = google.chat({ version: 'v1', auth })
  }
  
  async
 sendMessage(space: string, text: string): Promise<void> {
    await
 this.chat.spaces.messages.create({
      parent
: space,
      requestBody
: {
        text
      }
    })
  }
}

2.6 IRC

路径: extensions/irc/

协议: IRC(Internet Relay Chat)文本协议

特点: 自 v2026.3 起从扩展渠道升级为一级内置聊天渠道

功能:

  • • 传统 IRC 网络 DM / 频道路由
  • • Pairing 控制

2.7 LINE

路径: src/line/, extensions/line/

协议: LINE Messaging API

依赖: @line/bot-sdk (10.6.0)


3. 扩展渠道 (extensions/)

3.1 Signal

路径: extensions/signal/

协议: signal-cli

特点: 需要本地运行 signal-cli 守护进程

3.2 iMessage

路径: extensions/imessage/

协议: imsg (macOS only)

特点: 仅支持 macOS 系统

3.3 BlueBubbles

路径: extensions/bluebubbles/

协议: BlueBubbles Server API

特点: iMessage 服务器代理

3.4 Microsoft Teams

路径: extensions/msteams/

协议: Bot Framework

3.5 Matrix

路径: extensions/matrix/

协议: Matrix Client-Server API

3.6 Zalo / ZaloUser

路径: extensions/zalo/, extensions/zalouser/

协议: Zalo API

3.7 其他扩展

  • extensions/feishu/ - Feishu/Lark(飞书)
  • extensions/synology-chat/ - Synology Chat
  • extensions/tlon/ - Tlon (Urbit)
  • extensions/twitch/ - Twitch Chat
  • extensions/nostr/ - Nostr Protocol
  • extensions/nextcloud-talk/ - Nextcloud Talk
  • extensions/mattermost/ - Mattermost
  • extensions/voice-call/ - Voice Call(Telnyx 语音通话)
  • extensions/phone-control/ - 手机控制

3.8 新增扩展插件 (v2026.3)

Talk Voice (extensions/talk-voice/)

职责: 管理 Talk 语音选项(列出/设置语音角色),为语音通话渠道提供声音选择控制。

Thread Ownership (extensions/thread-ownership/)

职责: 防止多个代理同时在同一 Slack 线程中响应,通过调用 slack-forwarder 的 ownership API 实现线程级独占控制。

配置项:

       
                                           
字段说明
forwarderUrlslack-forwarder ownership API 基础 URL(默认 http://slack-forwarder:8750
abTestChannels实施线程所有权控制的 Slack 频道 ID 列表
       
     

4. 统一渠道接口

4.1 Channel 接口

// src/channels/channel.ts
export
 interface Channel {
  // 渠道元数据

  readonly
 id: string
  readonly
 name: string
  readonly
 type: ChannelType
  
  // 生命周期

  start
(config: ChannelConfig): Promise<void>
  stop
(): Promise<void>
  restart
(): Promise<void>
  
  // 消息收发

  sendMessage
(target: MessageTarget, content: MessageContent): Promise<void>
  onMessage
(handler: MessageHandler): void
  
  // 状态查询

  getStatus
(): ChannelStatus
  isConnected
(): boolean
  
  // 能力查询

  supports
(capability: Capability): boolean
}

export
 interface MessageTarget {
  type
: 'user' | 'group' | 'channel'
  id
: string
}

export
 interface MessageContent {
  text
?: string
  media
?: MediaAttachment[]
  replyTo
?: string
  metadata
?: Record<string, any>
}

export
 type MessageHandler = (message: InboundMessage) => Promise<void>

export
 interface InboundMessage {
  id
: string
  channel
: string
  from
: MessageTarget
  to
: MessageTarget
  content
: MessageContent
  timestamp
: Date
}

4.2 Channel Manager

实际路径: src/gateway/server-channels.ts (createChannelManager)

// 集成在Gateway Runtime中,不是独立类
export
 function createChannelManager(opts) {
  private
 channels: Map<string, Channel> = new Map()
  
  async
 register(channel: Channel): Promise<void> {
    this
.channels.set(channel.id, channel)
  }
  
  async
 startAll(): Promise<void> {
    for
 (const channel of this.channels.values()) {
      try
 {
        await
 channel.start(this.getConfig(channel.id))
        logger.info(`Started channel: ${channel.id}`)
      } catch (error) {
        logger.error(`Failed to start channel: ${channel.id}`, error)
      }
    }
  }
  
  async
 stopAll(): Promise<void> {
    for
 (const channel of this.channels.values()) {
      await
 channel.stop()
    }
  }
  
  async
 send(
    channelId
: string,
    target
: MessageTarget,
    content
: MessageContent
  ): Promise<void> {
    const
 channel = this.channels.get(channelId)
    if
 (!channel) {
      throw
 new Error(`Channel not found: ${channelId}`)
    }
    
    await
 channel.sendMessage(target, content)
  }
  
  getStatus
(): ChannelStatusMap {
    const
 status: ChannelStatusMap = {}
    
    for
 (const [id, channel] of this.channels) {
      status[id] = channel.getStatus()
    }
    
    return
 status
  }
}

5. 消息处理流程

5.1 入站消息流

外部平台 (WhatsApp/Telegram/etc.)
  ↓ Platform Event
渠道实现 (WhatsAppChannel/TGChannel/etc.)
  ↓ 转换为统一格式
ChannelManager
  ↓ 发布渠道事件
Gateway 事件总线
  ↓ 订阅者处理
消息路由层

5.2 出站消息流

AI 代理响应
  ↓
消息路由层
  ↓ 确定目标渠道
ChannelManager
  ↓ 查找渠道实例
渠道实现
  ↓ 平台特定格式
外部平台 API

6. 技术实现细节

6.1 渠道生命周期

// src/channels/base-channel.ts
export
 abstract class BaseChannel implements Channel {
  protected
 status: ChannelStatus = 'disconnected'
  protected
 messageHandlers: MessageHandler[] = []
  
  abstract
 async connect(): Promise<void>
  abstract
 async disconnect(): Promise<void>
  abstract
 async sendRaw(message: any): Promise<void>
  
  async
 start(config: ChannelConfig): Promise<void> {
    this
.config = config
    this
.status = 'connecting'
    
    try
 {
      await
 this.connect()
      this
.status = 'connected'
      this
.emit('connected')
    } catch (error) {
      this
.status = 'error'
      this
.emit('error', error)
      throw
 error
    }
  }
  
  async
 stop(): Promise<void> {
    this
.status = 'disconnecting'
    await
 this.disconnect()
    this
.status = 'disconnected'
    this
.emit('disconnected')
  }
  
  async
 restart(): Promise<void> {
    await
 this.stop()
    await
 new Promise(resolve => setTimeout(resolve, 1000))
    await
 this.start(this.config)
  }
  
  protected
 handleIncomingMessage(platformMessage: any): void {
    // 转换为统一格式

    const
 message = this.transformMessage(platformMessage)
    
    // 调用所有处理器

    for
 (const handler of this.messageHandlers) {
      handler
(message).catch(error => {
        logger.error('Message handler error', error)
      })
    }
  }
  
  abstract
 transformMessage(platformMessage: any): InboundMessage
}

6.2 媒体处理

// src/channels/media-handler.ts
export
 class MediaHandler {
  async
 downloadMedia(url: string): Promise<Buffer> {
    const
 response = await fetch(url)
    return
 Buffer.from(await response.arrayBuffer())
  }
  
  async
 uploadMedia(
    channel
: Channel,
    media
: Buffer,
    type
: MediaType
  ): Promise<string> {
    // 特定渠道的上传逻辑

    if
 (channel.type === 'telegram') {
      return
 await this.uploadToTelegram(media, type)
    }
    
    // 通用上传

    return
 await this.uploadToStorage(media, type)
  }
  
  async
 processMedia(
    media
: Buffer,
    options
: ProcessOptions
  ): Promise<Buffer> {
    // 使用媒体管道处理

    const
 pipeline = createMediaPipeline()
    return
 await pipeline.process(media, options)
  }
}

6.3 错误恢复

// src/channels/error-recovery.ts
export
 class ChannelRecovery {
  private
 retryCount: Map<string, number> = new Map()
  private
 maxRetries = 3
  private
 backoffMs = 5000
  
  async
 recover(channel: Channel, error: Error): Promise<void> {
    const
 retries = this.retryCount.get(channel.id) || 0
    
    if
 (retries >= this.maxRetries) {
      logger.error(`Max retries exceeded for channel: ${channel.id}`)
      this
.emit('recovery_failed', channel, error)
      return

    }
    
    // 指数退避

    const
 delay = this.backoffMs * Math.pow(2, retries)
    await
 new Promise(resolve => setTimeout(resolve, delay))
    
    try
 {
      await
 channel.restart()
      this
.retryCount.set(channel.id, 0)
      logger.info(`Successfully recovered channel: ${channel.id}`)
    } catch (retryError) {
      this
.retryCount.set(channel.id, retries + 1)
      await
 this.recover(channel, retryError)
    }
  }
}

7. 渠道配置

7.1 配置结构

channels:
  whatsapp:

    enabled:
 true
    sessionDir:
 ~/.openclaw/sessions/whatsapp
    autoReconnect:
 true
    
  slack:

    enabled:
 true
    token:
 env:SLACK_BOT_TOKEN
    appToken:
 env:SLACK_APP_TOKEN
    socketMode:
 true
    
  discord:

    enabled:
 true
    token:
 env:DISCORD_BOT_TOKEN
    intents:

      -
 guilds
      -
 guildMessages
      -
 messageContent

7.2 环境变量

# Telegram
export
 TELEGRAM_BOT_TOKEN="123456:ABC-DEF..."

# Slack

export
 SLACK_BOT_TOKEN="xoxb-..."
export
 SLACK_APP_TOKEN="xapp-..."

# Discord

export
 DISCORD_BOT_TOKEN="MTk..."

# Google Chat

export
 GOOGLE_CHAT_CREDENTIALS='{"type":"service_account",...}'

8. 安全特性

8.1 消息验证

// src/channels/message-validator.ts
export
 class MessageValidator {
  validate
(message: InboundMessage): ValidationResult {
    // 验证消息来源

    if
 (!this.isValidSender(message.from)) {
      return
 { valid: false, reason: 'Invalid sender' }
    }
    
    // 验证消息内容

    if
 (!this.isValidContent(message.content)) {
      return
 { valid: false, reason: 'Invalid content' }
    }
    
    // 验证消息大小

    if
 (this.isOversized(message)) {
      return
 { valid: false, reason: 'Message too large' }
    }
    
    return
 { valid: true }
  }
}

8.2 速率限制

// src/channels/rate-limiter.ts
export
 class ChannelRateLimiter {
  private
 limits: Map<string, RateLimit> = new Map()
  
  async
 checkLimit(channel: string, action: string): Promise<boolean> {
    const
 key = `${channel}:${action}`
    const
 limit = this.limits.get(key) || this.createLimit()
    
    return
 await limit.check()
  }
  
  private
 createLimit(): RateLimit {
    // Telegram: 30 messages per second

    // WhatsApp: Unlimited (但建议限制)

    // Slack: 1 message per second per channel

    return
 new RateLimit({
      maxRequests
: 30,
      windowMs
: 1000
    })
  }
}

9. 性能优化

9.1 连接复用

// 复用 WebSocket 连接
export
 class ConnectionPool {
  private
 connections: Map<string, WebSocket> = new Map()
  
  getConnection
(url: string): WebSocket {
    if
 (!this.connections.has(url)) {
      const
 ws = new WebSocket(url)
      this
.connections.set(url, ws)
    }
    
    return
 this.connections.get(url)!
  }
}

9.2 消息批处理

// 批量发送消息
export
 class MessageBatcher {
  private
 queue: OutboundMessage[] = []
  private
 flushInterval = 100
  
  enqueue
(message: OutboundMessage): void {
    this
.queue.push(message)
    
    if
 (this.queue.length >= 10) {
      this
.flush()
    }
  }
  
  private
 async flush(): Promise<void> {
    const
 batch = this.queue.splice(0)
    await
 this.sendBatch(batch)
  }
}

10. 总结

渠道接入层是 OpenClaw 的多平台桥梁,提供:

  • 广泛兼容: 25+ 消息平台支持
  • 统一抽象: 一致的消息接口
  • 插件化: 独立扩展包
  • 健壮性: 错误恢复和重连
  • 安全性: 消息验证和速率限制
  • 高性能: 连接复用和批处理

v2026.3 变更摘要:

  • • IRC 和 Google Chat 升级为一级内置聊天渠道(与 Telegram、Slack 等同级)
  • • 新增 Talk Voice 扩展插件(语音选择管理)
  • • 新增 Thread Ownership 扩展插件(Slack 多代理线程冲突防护)

使得 OpenClaw 能够跨越不同平台,为用户提供统一的 AI 助手体验。