← Back to Plugins
Tools

Yeizi Openclaw

chungeplus By chungeplus ⭐ 5 stars 👁 83 views ▲ 0 votes

yeizi-openclaw-plugin

GitHub

Install

npm install
npm

Configuration Example

{
    "type": "message",
    "text": "用户消息内容",
    "from": "user_xxx",
    "to": "yeizi",
    "messageId": "1700000000000",
    "chatType": "dm"
}

README

# Yeizi Channel 插件开发需求文档

## 1. 项目概述

### 1.1 项目名称

Yeizi Channel 插件

### 1.2 项目目标

开发一个基于 Web 的 OpenClaw 插件系统,包含:

- **Web 端**:提供用户对话界面和后端服务(鉴权、WebSocket 通信)
- **OpenClaw 插件**:作为 OpenClaw 的 channel 插件,与 Web 后端建立连接并处理消息

### 1.3 项目范围

**当前版本:单智能体、单账户、单用户**

- **单智能体**:只支持一个智能体(默认 `main`)
- **单账户**:只支持一个账户(默认 `default`)
- **单用户**:用户无需登录,前端自动生成用户标识

### 1.4 核心功能

用户通过 Web 界面输入消息,消息通过 WebSocket 传递给 OpenClaw 插件,插件再与 OpenClaw 进行通信,最终将 AI 回复返回给用户。

## 2. 系统架构

### 2.1 整体架构图

```
┌────────────────────────────────────────────────────────────────┐
│                         用户浏览器                               │
├────────────────────────────────────────────────────────────────┤
│  ┌──────────────────────────────────────────────────────────┐  │
│  │                    Web 前端(对话页面)                      │  │
│  │  • 用户输入消息                                             │  │
│  │  • 显示 AI 回复                                             │  │
│  │  • WebSocket 实时通信                                        │  │
│  │  • 插件配置显示                                             │  │
│  └──────────────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────────────┘
           ▲                                    │
           │                                    │ WebSocket
     AI 回复                                    │ 用户消息
           │                                    ▼
           └────────────────────────────────────────────────►
                                 │
                                 ▼
┌────────────────────────────────────────────────────────────────┐
│                          Web 后端服务                            │
├────────────────────────────────────────────────────────────────┤
│  • 鉴权服务(AppKey + AppSecret)                               │
│  • WebSocket 服务                                              │
│  • 消息路由                                                    │
│  • 配置查询 API (/api/config)                                   │
└────────────────────────────────────────────────────────────────┘
           ▲                                    │
           │                                    │ WebSocket
     AI 回复                                    │ 消息转发
           │                                    ▼
           └────────────────────────────────────────────────►
                                 │
                                 ▼
┌────────────────────────────────────────────────────────────────┐
│                        OpenClaw 插件                            │
├────────────────────────────────────────────────────────────────┤
│  • WebSocket 客户端(接收/发送消息)                             │
│  • dispatchReplyWithBufferedBlockDispatcher                     │
│  • ChannelDock 配置                                            │
│  • YeiziDock 定义插件能力                                       │
└────────────────────────────────────────────────────────────────┘
                                 │
                            AI 回复
                         (OpenClaw API)
```

### 2.2 消息流程

#### 消息发送流程(用户 → OpenClaw)

```
用户输入消息
    ↓
前端通过 WebSocket 发送消息到后端
    ↓
后端转发消息到 OpenClaw 插件
    ↓
插件构建 ctxPayload
    ↓
调用 finalizeInboundContext
    ↓
调用 dispatchReplyWithBufferedBlockDispatcher 触发 AI 处理
```

#### 消息回复流程(OpenClaw → 用户)

```
OpenClaw AI 处理完成
    ↓
dispatchReplyWithBufferedBlockDispatcher 的 deliver 回调触发
    ↓
插件通过 WebSocket 发送回复到后端
    ↓
后端通过 WebSocket 推送到前端
    ↓
前端显示 AI 回复
```

## 3. 安装与配置

### 3.1 环境要求

- **Node.js**:>= 18.0.0
- **OpenClaw**:>= 2026.3.12

### 3.2 安装步骤

#### 步骤 1:构建插件

```bash
cd yeizi-plugin
npm install
npm run build
```

#### 步骤 2:安装插件

```bash
cd yeizi-plugin/scripts
node setup.mjs <app_key> <app_secret> [base_url] [websocket_url]
```

示例:

```bash
node setup.mjs yeizi-app-key-2026 yeizi-app-secret-2026 http://localhost:3000 ws://localhost:3000
```

#### 步骤 3:重启 OpenClaw

安装完成后需要重启 OpenClaw Gateway 使配置生效。

### 3.3 后端配置

后端使用环境变量配置(.env 文件):

```bash
# 应用配置
APP_KEY=yeizi-app-key-2026
APP_SECRET=yeizi-app-secret-2026

# 服务地址配置
HOST=localhost
PORT=3000
PROTOCOL=http

# 服务地址
BASE_URL=http://localhost:3000
WEBSOCKET_URL=ws://localhost:3000
```

## 4. 项目结构

```
yeizi/
├── web-channel/                    # Web 端项目
│   ├── frontend/                   # 前端项目(Vue 3 + TypeScript)
│   │   ├── src/
│   │   │   ├── components/       # Vue 组件
│   │   │   │   ├── ChatInput.vue       # 消息输入组件
│   │   │   │   ├── ChatMessage.vue     # 消息显示组件
│   │   │   │   ├── ChatWindow.vue     # 聊天窗口组件
│   │   │   │   ├── ConnectionStatus.vue # 连接状态组件
│   │   │   │   └── SettingsPanel.vue   # 插件配置页面
│   │   │   ├── composables/
│   │   │   │   └── useWebSocket.ts     # WebSocket 钩子
│   │   │   ├── stores/
│   │   │   │   └── chat.ts             # 聊天状态管理
│   │   │   └── App.vue                 # 主应用组件
│   │   └── package.json
│   ├── backend/                   # 后端项目(Express.js)
│   │   ├── src/
│   │   │   ├── routes/
│   │   │   │   ├── auth.ts            # 鉴权路由
│   │   │   │   └── config.ts          # 配置查询路由
│   │   │   ├── services/
│   │   │   │   ├── auth.ts            # 鉴权服务
│   │   │   │   ├── config.ts          # 配置服务
│   │   │   │   └── websocket.ts       # WebSocket 管理
│   │   │   └── index.ts               # 服务入口
│   │   ├── .env                      # 环境变量配置
│   │   └── package.json
│   └── package.json
│
└── yeizi-plugin/                  # OpenClaw 插件项目
    ├── src/
    │   ├── accounts.ts              # 账户管理工具
    │   ├── channel.ts              # Channel Plugin 实现
    │   ├── config-schema.ts        # 配置 Schema 定义
    │   ├── runtime.ts              # 运行时存储管理
    │   ├── types.ts                # 类型定义
    │   └── websocket-client.ts     # WebSocket 客户端封装
    ├── scripts/
    │   ├── setup.mjs              # 安装脚本
    │   └── README.md               # 安装说明
    ├── index.ts                   # 插件运行时入口
    ├── openclaw.plugin.json       # 插件元数据
    ├── package.json
    └── tsconfig.json
```

## 5. 插件核心实现

### 5.1 插件入口 (index.ts)

```typescript
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { plugin, yeiziDock } from "./src/channel.js";
import { setRuntime } from "./src/runtime.js";

export { plugin, yeiziDock } from "./src/channel.js";

const yeizi: any = {
    id: "yeizi",
    name: "Yeizi",
    description: "Yeizi Channel 插件",
    configSchema: emptyPluginConfigSchema(),
    register(api: OpenClawPluginApi) {
        setRuntime(api.runtime);
        api.registerChannel({ plugin, dock: yeiziDock });
    },
};

export function register(api: OpenClawPluginApi) {
    yeizi.register(api);
}

export function activate(api: OpenClawPluginApi) {
    register(api);
}

export default yeizi;
```

### 5.2 ChannelDock 定义

```typescript
export const yeiziDock: ChannelDock = {
    id: "yeizi",
    capabilities: {
        chatTypes: ["direct"],
        blockStreaming: true,
    },
};
```

### 5.3 消息处理流程

```typescript
// 1. 启动账户时建立 WebSocket 连接
startAccount: async (ctx) => {
    // HTTP 鉴权获取 token
    const authResponse = await fetch(`${account.config.baseUrl}/api/auth/token`, {
        method: 'POST',
        body: JSON.stringify({
            appKey: account.config.appKey,
            appSecret: account.config.appSecret,
        }),
    });
    
    // 建立 WebSocket 连接
    const wsClient = new WebSocketClient({
        url: `${account.config.websocketUrl}/ws/plugin`,
        token: authData.token,
        onMessage: handleMessage,
    });
    
    wsClient.connect();
    
    // 等待 abort 信号
    return new Promise((resolve) => {
        ctx.abortSignal?.addEventListener('abort', () => {
            wsClient.disconnect();
            resolve();
        });
    });
}

// 2. 收到消息时处理
onMessage: (message: WebSocketMessage) => {
    // 构建 ctxPayload
    const ctxPayload = {
        Body: message.text,
        BodyForAgent: message.text,
        RawBody: JSON.stringify(message),
        From: message.from ?? 'unknown',
        To: message.to ?? 'yeizi',
        ChatType: 'dm',
        Provider: 'yeizi',
        Surface: 'yeizi',
        AgentId: agentId,
        Timestamp: Date.now(),
        AccountId: accountId,
        MessageSid: message.messageId ?? Date.now().toString(),
        OriginatingChannel: 'yeizi',
        OriginatingTo: message.to ?? 'yeizi',
    };
    
    const finalized = runtime.channel.reply.finalizeInboundContext(ctxPayload);
    
    // 分发消息到 AI 处理
    await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
        ctx: finalized,
        cfg,
        dispatcherOptions: {
            deliver: async (payload) => {
                const textOut = String(payload.text ?? payload.body ?? "");
                const target = message.from;
                
                if (!target || !textOut.trim()) {
                    return;
                }
                
                wsClient.send({
                    type: 'response',
                    payload: {
                        content: textOut,
                        messageId: message.messageId,
                        to: target,
                    },
                });
            },
        },
    });
}
```

### 5.4 出站消息配置

```typescript
outbound: {
    deliveryMode: 'direct',
    chunker: (text: string, _limit: number) => [text],
    textChunkLimit: 4096,
    sendText: async ({ to, text, accountId }) => {
        const wsClient = accountConnections.get(accountId ?? 'default');
        const messageId = Date.now().toString();
        
        if (!wsClient || !wsClient.isConnected()) {
            return { channel: 'yeizi', ok: false, messageId };
        }

        const sent = wsClient.send({
            type: 'response',
            payload: { content: text, messageId, to },
        });

        return { channel: 'yeizi', ok: sent, messageId };
    },
},
```

## 6. WebSocket 消息格式

### 6.1 前端 → 插件(用户消息)

```json
{
    "type": "message",
    "text": "用户消息内容",
    "from": "user_xxx",
    "to": "yeizi",
    "messageId": "17000000

... (truncated)
tools

Comments

Sign in to leave a comment

Loading comments...