openclaw-mochat 2026.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -0
- package/index.ts +19 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +41 -0
- package/src/accounts.ts +180 -0
- package/src/api.ts +453 -0
- package/src/channel.ts +253 -0
- package/src/config-schema.ts +46 -0
- package/src/delay-buffer.test.ts +81 -0
- package/src/delay-buffer.ts +123 -0
- package/src/event-store.ts +56 -0
- package/src/inbound.ts +402 -0
- package/src/poller.ts +116 -0
- package/src/runtime.ts +14 -0
- package/src/socket.ts +439 -0
- package/src/tool.ts +252 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Mochat 插件(OpenClaw)
|
|
2
|
+
|
|
3
|
+
这是一个 OpenClaw 的渠道插件,用于对接 Mochat/Tailchat(Claw IM)。
|
|
4
|
+
|
|
5
|
+
## 打包/分发方式
|
|
6
|
+
|
|
7
|
+
插件目录需要包含:
|
|
8
|
+
|
|
9
|
+
- `openclaw.plugin.json`
|
|
10
|
+
- `package.json`(含 `openclaw.extensions`)
|
|
11
|
+
- 入口文件(如 `index.ts` 或 `dist/index.js`)
|
|
12
|
+
|
|
13
|
+
常见分发方式:
|
|
14
|
+
|
|
15
|
+
1) **本地目录/压缩包**
|
|
16
|
+
- 直接把插件目录发送给用户。
|
|
17
|
+
- 用户用 `openclaw plugins install /path/to/mochat` 安装。
|
|
18
|
+
|
|
19
|
+
2) **发布到 npm(推荐)**
|
|
20
|
+
- `package.json` 中配置:
|
|
21
|
+
- `openclaw.extensions` 指向入口(`./index.ts` 或 `./dist/index.js`)
|
|
22
|
+
- 运行依赖放在插件自身 `dependencies`。
|
|
23
|
+
- 发布后,用户可用:
|
|
24
|
+
- `openclaw plugins install openclaw-mochat`
|
|
25
|
+
|
|
26
|
+
## 用户标准安装流程
|
|
27
|
+
|
|
28
|
+
1) **安装插件**
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# npm 安装
|
|
32
|
+
openclaw plugins install openclaw-mochat
|
|
33
|
+
|
|
34
|
+
# 或本地目录安装
|
|
35
|
+
openclaw plugins install /path/to/mochat
|
|
36
|
+
|
|
37
|
+
# 开发联调(软链接,不复制)
|
|
38
|
+
openclaw plugins install -l /path/to/mochat
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
2) **启用插件**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
openclaw plugins enable mochat
|
|
45
|
+
# 或
|
|
46
|
+
openclaw config set plugins.entries.mochat.enabled true
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
3) **配置渠道参数**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
openclaw config set channels.mochat.baseUrl "http://localhost:11000"
|
|
53
|
+
openclaw config set channels.mochat.socketUrl "http://localhost:11000"
|
|
54
|
+
openclaw config set channels.mochat.clawToken "claw_ef06c64c7589bb36d27e16ab8e337dded50529cfc3982d88"
|
|
55
|
+
openclaw config set channels.mochat.agentUserId "69820107a785110aea8b1069"
|
|
56
|
+
openclaw config set channels.mochat.sessions '["*"]'
|
|
57
|
+
openclaw config set channels.mochat.panels '["*"]'
|
|
58
|
+
openclaw config set channels.mochat.refreshIntervalMs 30000
|
|
59
|
+
openclaw config set channels.mochat.replyDelayMode "non-mention"
|
|
60
|
+
openclaw config set channels.mochat.replyDelayMs 120000
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`replyDelayMode=non-mention` 时:仅 **panel** 启用混合模式(被 @ 立即回复,未 @ 延迟合并)。Session 消息仍然立即回复,不受影响。
|
|
64
|
+
|
|
65
|
+
4) **重启网关**
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
openclaw gateway restart
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
5) **验证**
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
openclaw plugins list
|
|
75
|
+
openclaw channels status --probe
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 开发/调试建议
|
|
79
|
+
|
|
80
|
+
- 如果使用 `openclaw plugins install -l`,代码修改后无需重新安装。
|
|
81
|
+
- 依赖变更后需要在插件目录执行安装(例如 `pnpm install`)。
|
|
82
|
+
- 配置变更后需重启网关生效。
|
package/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
+
import { mochatPlugin } from "./src/channel.js";
|
|
4
|
+
import { setMochatRuntime } from "./src/runtime.js";
|
|
5
|
+
import { mochatTool } from "./src/tool.js";
|
|
6
|
+
|
|
7
|
+
const plugin = {
|
|
8
|
+
id: "mochat",
|
|
9
|
+
name: "Mochat",
|
|
10
|
+
description: "OpenClaw Mochat (Claw IM) channel plugin",
|
|
11
|
+
configSchema: emptyPluginConfigSchema(),
|
|
12
|
+
register(api: OpenClawPluginApi) {
|
|
13
|
+
setMochatRuntime(api.runtime);
|
|
14
|
+
api.registerChannel({ plugin: mochatPlugin });
|
|
15
|
+
api.registerTool(mochatTool, { optional: true });
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openclaw-mochat",
|
|
3
|
+
"version": "2026.2.3",
|
|
4
|
+
"description": "OpenClaw Mochat (Claw IM) channel plugin",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@sinclair/typebox": "0.34.48",
|
|
8
|
+
"socket.io-client": "^4.7.2",
|
|
9
|
+
"socket.io-msgpack-parser": "^3.0.2",
|
|
10
|
+
"zod": "^4.3.6"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"openclaw": "workspace:*"
|
|
14
|
+
},
|
|
15
|
+
"openclaw": {
|
|
16
|
+
"extensions": [
|
|
17
|
+
"./index.ts"
|
|
18
|
+
],
|
|
19
|
+
"channel": {
|
|
20
|
+
"id": "mochat",
|
|
21
|
+
"label": "Mochat",
|
|
22
|
+
"selectionLabel": "Mochat (Claw IM)",
|
|
23
|
+
"docsPath": "/channels/mochat",
|
|
24
|
+
"docsLabel": "mochat",
|
|
25
|
+
"blurb": "Claw IM gateway for Mochat/Tailchat",
|
|
26
|
+
"order": 95
|
|
27
|
+
},
|
|
28
|
+
"install": {
|
|
29
|
+
"npmSpec": "openclaw-mochat",
|
|
30
|
+
"localPath": "extensions/moltchat",
|
|
31
|
+
"defaultChoice": "npm"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"index.ts",
|
|
36
|
+
"src/**",
|
|
37
|
+
"openclaw.plugin.json",
|
|
38
|
+
"README.md",
|
|
39
|
+
"package.json"
|
|
40
|
+
]
|
|
41
|
+
}
|
package/src/accounts.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
3
|
+
import type { MochatConfig } from "./config-schema.js";
|
|
4
|
+
|
|
5
|
+
export type MochatAccountConfig = MochatConfig & {
|
|
6
|
+
accounts?: Record<string, MochatConfig | undefined>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type ResolvedMochatAccount = {
|
|
10
|
+
accountId: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
configured: boolean;
|
|
14
|
+
config: Required<
|
|
15
|
+
Pick<
|
|
16
|
+
MochatConfig,
|
|
17
|
+
| "baseUrl"
|
|
18
|
+
| "refreshIntervalMs"
|
|
19
|
+
| "watchTimeoutMs"
|
|
20
|
+
| "watchLimit"
|
|
21
|
+
| "retryDelayMs"
|
|
22
|
+
| "maxRetryAttempts"
|
|
23
|
+
| "socketPath"
|
|
24
|
+
| "socketDisableMsgpack"
|
|
25
|
+
| "socketReconnectDelayMs"
|
|
26
|
+
| "socketMaxReconnectDelayMs"
|
|
27
|
+
| "socketConnectTimeoutMs"
|
|
28
|
+
| "replyDelayMode"
|
|
29
|
+
| "replyDelayMs"
|
|
30
|
+
>
|
|
31
|
+
> &
|
|
32
|
+
Pick<
|
|
33
|
+
MochatConfig,
|
|
34
|
+
| "clawToken"
|
|
35
|
+
| "agentUserId"
|
|
36
|
+
| "sessions"
|
|
37
|
+
| "panels"
|
|
38
|
+
| "mention"
|
|
39
|
+
| "groups"
|
|
40
|
+
| "socketUrl"
|
|
41
|
+
> & {
|
|
42
|
+
autoDiscoverSessions: boolean;
|
|
43
|
+
autoDiscoverPanels: boolean;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const DEFAULT_BASE_URL = "http://localhost:11000";
|
|
48
|
+
const DEFAULT_WATCH_TIMEOUT_MS = 25000;
|
|
49
|
+
const DEFAULT_WATCH_LIMIT = 100;
|
|
50
|
+
const DEFAULT_RETRY_DELAY_MS = 200;
|
|
51
|
+
const DEFAULT_MAX_RETRY_ATTEMPTS = 3;
|
|
52
|
+
const DEFAULT_SOCKET_PATH = "/socket.io";
|
|
53
|
+
const DEFAULT_SOCKET_DISABLE_MSGPACK = false;
|
|
54
|
+
const DEFAULT_SOCKET_RECONNECT_DELAY_MS = 1000;
|
|
55
|
+
const DEFAULT_SOCKET_MAX_RECONNECT_DELAY_MS = 10000;
|
|
56
|
+
const DEFAULT_SOCKET_CONNECT_TIMEOUT_MS = 10000;
|
|
57
|
+
const DEFAULT_REFRESH_INTERVAL_MS = 30000;
|
|
58
|
+
const DEFAULT_REPLY_DELAY_MODE = "off";
|
|
59
|
+
const DEFAULT_REPLY_DELAY_MS = 120000;
|
|
60
|
+
|
|
61
|
+
type NormalizedList = {
|
|
62
|
+
items: string[];
|
|
63
|
+
hasWildcard: boolean;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function normalizeIdList(values?: string[]): NormalizedList {
|
|
67
|
+
const cleaned = (values ?? [])
|
|
68
|
+
.map((entry) => String(entry).trim())
|
|
69
|
+
.filter(Boolean);
|
|
70
|
+
const hasWildcard = cleaned.includes("*");
|
|
71
|
+
const items = Array.from(new Set(cleaned.filter((entry) => entry !== "*")));
|
|
72
|
+
return { items, hasWildcard };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function listMochatAccountIds(cfg: OpenClawConfig): string[] {
|
|
76
|
+
const channel = (cfg.channels?.mochat ?? {}) as MochatAccountConfig;
|
|
77
|
+
const accountIds = Object.keys(channel.accounts ?? {});
|
|
78
|
+
if (accountIds.length > 0) {
|
|
79
|
+
return accountIds;
|
|
80
|
+
}
|
|
81
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function resolveDefaultMochatAccountId(_cfg: OpenClawConfig): string {
|
|
85
|
+
return DEFAULT_ACCOUNT_ID;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function resolveMochatAccount(params: {
|
|
89
|
+
cfg: OpenClawConfig;
|
|
90
|
+
accountId?: string | null;
|
|
91
|
+
}): ResolvedMochatAccount {
|
|
92
|
+
const accountId = params.accountId ?? DEFAULT_ACCOUNT_ID;
|
|
93
|
+
const channel = (params.cfg.channels?.mochat ?? {}) as MochatAccountConfig;
|
|
94
|
+
const accountConfig = channel.accounts?.[accountId] ?? channel;
|
|
95
|
+
|
|
96
|
+
const baseUrl = accountConfig.baseUrl ?? channel.baseUrl ?? DEFAULT_BASE_URL;
|
|
97
|
+
const clawToken = accountConfig.clawToken ?? channel.clawToken;
|
|
98
|
+
const agentUserId = accountConfig.agentUserId ?? channel.agentUserId;
|
|
99
|
+
const sessionList = normalizeIdList(accountConfig.sessions ?? channel.sessions);
|
|
100
|
+
const panelList = normalizeIdList(accountConfig.panels ?? channel.panels);
|
|
101
|
+
const sessions = sessionList.items;
|
|
102
|
+
const panels = panelList.items;
|
|
103
|
+
const autoDiscoverSessions = sessionList.hasWildcard;
|
|
104
|
+
const autoDiscoverPanels = panelList.hasWildcard;
|
|
105
|
+
const mention = accountConfig.mention ?? channel.mention;
|
|
106
|
+
const groups = accountConfig.groups ?? channel.groups;
|
|
107
|
+
const socketUrl = accountConfig.socketUrl ?? channel.socketUrl ?? baseUrl;
|
|
108
|
+
const socketPath = accountConfig.socketPath ?? channel.socketPath ?? DEFAULT_SOCKET_PATH;
|
|
109
|
+
const socketDisableMsgpack =
|
|
110
|
+
accountConfig.socketDisableMsgpack ??
|
|
111
|
+
channel.socketDisableMsgpack ??
|
|
112
|
+
DEFAULT_SOCKET_DISABLE_MSGPACK;
|
|
113
|
+
const socketReconnectDelayMs =
|
|
114
|
+
accountConfig.socketReconnectDelayMs ??
|
|
115
|
+
channel.socketReconnectDelayMs ??
|
|
116
|
+
DEFAULT_SOCKET_RECONNECT_DELAY_MS;
|
|
117
|
+
const socketMaxReconnectDelayMs =
|
|
118
|
+
accountConfig.socketMaxReconnectDelayMs ??
|
|
119
|
+
channel.socketMaxReconnectDelayMs ??
|
|
120
|
+
DEFAULT_SOCKET_MAX_RECONNECT_DELAY_MS;
|
|
121
|
+
const socketConnectTimeoutMs =
|
|
122
|
+
accountConfig.socketConnectTimeoutMs ??
|
|
123
|
+
channel.socketConnectTimeoutMs ??
|
|
124
|
+
DEFAULT_SOCKET_CONNECT_TIMEOUT_MS;
|
|
125
|
+
const refreshIntervalMs =
|
|
126
|
+
accountConfig.refreshIntervalMs ??
|
|
127
|
+
channel.refreshIntervalMs ??
|
|
128
|
+
DEFAULT_REFRESH_INTERVAL_MS;
|
|
129
|
+
const watchTimeoutMs =
|
|
130
|
+
accountConfig.watchTimeoutMs ?? channel.watchTimeoutMs ?? DEFAULT_WATCH_TIMEOUT_MS;
|
|
131
|
+
const watchLimit = accountConfig.watchLimit ?? channel.watchLimit ?? DEFAULT_WATCH_LIMIT;
|
|
132
|
+
const retryDelayMs =
|
|
133
|
+
accountConfig.retryDelayMs ?? channel.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
134
|
+
const maxRetryAttempts =
|
|
135
|
+
accountConfig.maxRetryAttempts ?? channel.maxRetryAttempts ?? DEFAULT_MAX_RETRY_ATTEMPTS;
|
|
136
|
+
const replyDelayMode =
|
|
137
|
+
accountConfig.replyDelayMode ?? channel.replyDelayMode ?? DEFAULT_REPLY_DELAY_MODE;
|
|
138
|
+
const replyDelayMs =
|
|
139
|
+
accountConfig.replyDelayMs ?? channel.replyDelayMs ?? DEFAULT_REPLY_DELAY_MS;
|
|
140
|
+
|
|
141
|
+
const enabled = accountConfig.enabled ?? channel.enabled ?? true;
|
|
142
|
+
const configured =
|
|
143
|
+
Boolean(clawToken?.trim()) &&
|
|
144
|
+
Boolean(agentUserId?.trim()) &&
|
|
145
|
+
((Array.isArray(sessions) && sessions.length > 0) ||
|
|
146
|
+
(Array.isArray(panels) && panels.length > 0) ||
|
|
147
|
+
autoDiscoverSessions ||
|
|
148
|
+
autoDiscoverPanels);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
accountId,
|
|
152
|
+
name: accountConfig.name ?? channel.name,
|
|
153
|
+
enabled,
|
|
154
|
+
configured,
|
|
155
|
+
config: {
|
|
156
|
+
baseUrl,
|
|
157
|
+
clawToken,
|
|
158
|
+
agentUserId,
|
|
159
|
+
sessions,
|
|
160
|
+
panels,
|
|
161
|
+
autoDiscoverSessions,
|
|
162
|
+
autoDiscoverPanels,
|
|
163
|
+
mention,
|
|
164
|
+
groups,
|
|
165
|
+
socketUrl,
|
|
166
|
+
socketPath,
|
|
167
|
+
socketDisableMsgpack,
|
|
168
|
+
socketReconnectDelayMs,
|
|
169
|
+
socketMaxReconnectDelayMs,
|
|
170
|
+
socketConnectTimeoutMs,
|
|
171
|
+
refreshIntervalMs,
|
|
172
|
+
watchTimeoutMs,
|
|
173
|
+
watchLimit,
|
|
174
|
+
retryDelayMs,
|
|
175
|
+
maxRetryAttempts,
|
|
176
|
+
replyDelayMode,
|
|
177
|
+
replyDelayMs,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|