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/src/api.ts
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
export type MochatAuthorInfo = {
|
|
2
|
+
userId?: string;
|
|
3
|
+
agentId?: string | null;
|
|
4
|
+
nickname?: string | null;
|
|
5
|
+
email?: string | null;
|
|
6
|
+
avatar?: string | null;
|
|
7
|
+
type?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type MochatEvent = {
|
|
11
|
+
seq: number;
|
|
12
|
+
sessionId: string;
|
|
13
|
+
type: string;
|
|
14
|
+
timestamp?: string;
|
|
15
|
+
payload?: {
|
|
16
|
+
messageId?: string;
|
|
17
|
+
author?: string;
|
|
18
|
+
authorInfo?: MochatAuthorInfo | null;
|
|
19
|
+
content?: unknown;
|
|
20
|
+
meta?: Record<string, unknown>;
|
|
21
|
+
groupId?: string;
|
|
22
|
+
converseId?: string;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type MochatParticipantInput = {
|
|
29
|
+
type: "agent" | "user";
|
|
30
|
+
id?: string;
|
|
31
|
+
uniqueName?: string;
|
|
32
|
+
name?: string;
|
|
33
|
+
avatar?: string;
|
|
34
|
+
metadata?: Record<string, unknown>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type MochatWatchResponse = {
|
|
38
|
+
sessionId: string;
|
|
39
|
+
cursor: number;
|
|
40
|
+
events: MochatEvent[];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type MochatSendResponse = {
|
|
44
|
+
sessionId: string;
|
|
45
|
+
status?: string;
|
|
46
|
+
[key: string]: unknown;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type MochatCreateSessionResponse = {
|
|
50
|
+
sessionId: string;
|
|
51
|
+
workspaceId: string;
|
|
52
|
+
converseId: string;
|
|
53
|
+
participants: string[];
|
|
54
|
+
visibility: string;
|
|
55
|
+
status: string;
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type MochatSessionResponse = {
|
|
60
|
+
sessionId: string;
|
|
61
|
+
workspaceId?: string;
|
|
62
|
+
converseId?: string;
|
|
63
|
+
participants?: string[];
|
|
64
|
+
visibility?: string;
|
|
65
|
+
status?: string;
|
|
66
|
+
metadata?: Record<string, unknown>;
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type MochatMessagesResponse = {
|
|
71
|
+
sessionId: string;
|
|
72
|
+
messages?: unknown[];
|
|
73
|
+
[key: string]: unknown;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type MochatSessionListResponse = {
|
|
77
|
+
sessions?: MochatSessionResponse[];
|
|
78
|
+
[key: string]: unknown;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type MochatGroupResponse = {
|
|
82
|
+
_id?: string;
|
|
83
|
+
id?: string;
|
|
84
|
+
panels?: Array<{
|
|
85
|
+
id?: string;
|
|
86
|
+
_id?: string;
|
|
87
|
+
name?: string;
|
|
88
|
+
type?: number;
|
|
89
|
+
provider?: string;
|
|
90
|
+
pluginPanelName?: string;
|
|
91
|
+
meta?: Record<string, unknown>;
|
|
92
|
+
}>;
|
|
93
|
+
[key: string]: unknown;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export type MochatPanelMessagesResponse = {
|
|
97
|
+
groupId?: string;
|
|
98
|
+
panelId: string;
|
|
99
|
+
messages?: unknown[];
|
|
100
|
+
[key: string]: unknown;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type MochatRequestOptions = {
|
|
104
|
+
baseUrl: string;
|
|
105
|
+
clawToken: string;
|
|
106
|
+
signal?: AbortSignal;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
type ClawWrapped<T> = {
|
|
110
|
+
code?: number;
|
|
111
|
+
data?: T;
|
|
112
|
+
name?: string;
|
|
113
|
+
message?: string;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const DEFAULT_HEADERS = {
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
function resolveClawUrl(baseUrl: string, path: string): string {
|
|
121
|
+
const trimmed = baseUrl.trim();
|
|
122
|
+
const normalizedBase = trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
|
|
123
|
+
return new URL(path.startsWith("/") ? path : `/${path}`, normalizedBase).toString();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function postJson<T>(
|
|
127
|
+
opts: MochatRequestOptions,
|
|
128
|
+
path: string,
|
|
129
|
+
payload: Record<string, unknown>,
|
|
130
|
+
): Promise<T> {
|
|
131
|
+
const url = resolveClawUrl(opts.baseUrl, path);
|
|
132
|
+
const response = await fetch(url, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: {
|
|
135
|
+
...DEFAULT_HEADERS,
|
|
136
|
+
"X-Claw-Token": opts.clawToken,
|
|
137
|
+
},
|
|
138
|
+
body: JSON.stringify(payload),
|
|
139
|
+
signal: opts.signal,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const rawText = await response.text().catch(() => "");
|
|
143
|
+
if (!response.ok) {
|
|
144
|
+
throw new Error(`Claw IM request failed (${response.status}): ${rawText || response.statusText}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let parsed: unknown = rawText;
|
|
148
|
+
if (rawText) {
|
|
149
|
+
try {
|
|
150
|
+
parsed = JSON.parse(rawText) as unknown;
|
|
151
|
+
} catch {
|
|
152
|
+
parsed = rawText;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (parsed && typeof parsed === "object") {
|
|
157
|
+
const wrapped = parsed as ClawWrapped<T>;
|
|
158
|
+
if (typeof wrapped.code === "number") {
|
|
159
|
+
if (wrapped.code !== 200) {
|
|
160
|
+
const errMessage = wrapped.message || wrapped.name || "Claw IM request failed";
|
|
161
|
+
throw new Error(`${errMessage} (code=${wrapped.code})`);
|
|
162
|
+
}
|
|
163
|
+
return (wrapped.data ?? ({} as T)) as T;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return parsed as T;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function watchSession(params: {
|
|
171
|
+
baseUrl: string;
|
|
172
|
+
clawToken: string;
|
|
173
|
+
sessionId: string;
|
|
174
|
+
cursor: number;
|
|
175
|
+
timeoutMs: number;
|
|
176
|
+
limit: number;
|
|
177
|
+
signal?: AbortSignal;
|
|
178
|
+
}): Promise<MochatWatchResponse> {
|
|
179
|
+
return await postJson<MochatWatchResponse>(
|
|
180
|
+
{
|
|
181
|
+
baseUrl: params.baseUrl,
|
|
182
|
+
clawToken: params.clawToken,
|
|
183
|
+
signal: params.signal,
|
|
184
|
+
},
|
|
185
|
+
"/api/claw/sessions/watch",
|
|
186
|
+
{
|
|
187
|
+
sessionId: params.sessionId,
|
|
188
|
+
cursor: params.cursor,
|
|
189
|
+
timeoutMs: params.timeoutMs,
|
|
190
|
+
limit: params.limit,
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export async function sendSessionMessage(params: {
|
|
196
|
+
baseUrl: string;
|
|
197
|
+
clawToken: string;
|
|
198
|
+
sessionId: string;
|
|
199
|
+
content: string;
|
|
200
|
+
replyTo?: string | null;
|
|
201
|
+
signal?: AbortSignal;
|
|
202
|
+
}): Promise<MochatSendResponse> {
|
|
203
|
+
return await postJson<MochatSendResponse>(
|
|
204
|
+
{
|
|
205
|
+
baseUrl: params.baseUrl,
|
|
206
|
+
clawToken: params.clawToken,
|
|
207
|
+
signal: params.signal,
|
|
208
|
+
},
|
|
209
|
+
"/api/claw/sessions/send",
|
|
210
|
+
{
|
|
211
|
+
sessionId: params.sessionId,
|
|
212
|
+
content: params.content,
|
|
213
|
+
...(params.replyTo ? { replyTo: params.replyTo } : {}),
|
|
214
|
+
},
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export async function sendPanelMessage(params: {
|
|
219
|
+
baseUrl: string;
|
|
220
|
+
clawToken: string;
|
|
221
|
+
panelId: string;
|
|
222
|
+
content: string;
|
|
223
|
+
replyTo?: string | null;
|
|
224
|
+
signal?: AbortSignal;
|
|
225
|
+
groupId?: string;
|
|
226
|
+
}): Promise<MochatSendResponse> {
|
|
227
|
+
return await postJson<MochatSendResponse>(
|
|
228
|
+
{
|
|
229
|
+
baseUrl: params.baseUrl,
|
|
230
|
+
clawToken: params.clawToken,
|
|
231
|
+
signal: params.signal,
|
|
232
|
+
},
|
|
233
|
+
"/api/claw/groups/panels/send",
|
|
234
|
+
{
|
|
235
|
+
panelId: params.panelId,
|
|
236
|
+
content: params.content,
|
|
237
|
+
...(params.replyTo ? { replyTo: params.replyTo } : {}),
|
|
238
|
+
...(params.groupId ? { groupId: params.groupId } : {}),
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export async function createSession(params: {
|
|
244
|
+
baseUrl: string;
|
|
245
|
+
clawToken: string;
|
|
246
|
+
participants: MochatParticipantInput[];
|
|
247
|
+
visibility?: string;
|
|
248
|
+
metadata?: Record<string, unknown>;
|
|
249
|
+
signal?: AbortSignal;
|
|
250
|
+
}): Promise<MochatCreateSessionResponse> {
|
|
251
|
+
return await postJson<MochatCreateSessionResponse>(
|
|
252
|
+
{
|
|
253
|
+
baseUrl: params.baseUrl,
|
|
254
|
+
clawToken: params.clawToken,
|
|
255
|
+
signal: params.signal,
|
|
256
|
+
},
|
|
257
|
+
"/api/claw/sessions/create",
|
|
258
|
+
{
|
|
259
|
+
participants: params.participants,
|
|
260
|
+
...(params.visibility ? { visibility: params.visibility } : {}),
|
|
261
|
+
...(params.metadata ? { metadata: params.metadata } : {}),
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function addParticipants(params: {
|
|
267
|
+
baseUrl: string;
|
|
268
|
+
clawToken: string;
|
|
269
|
+
sessionId: string;
|
|
270
|
+
participants: MochatParticipantInput[];
|
|
271
|
+
signal?: AbortSignal;
|
|
272
|
+
}): Promise<MochatSessionResponse> {
|
|
273
|
+
return await postJson<MochatSessionResponse>(
|
|
274
|
+
{
|
|
275
|
+
baseUrl: params.baseUrl,
|
|
276
|
+
clawToken: params.clawToken,
|
|
277
|
+
signal: params.signal,
|
|
278
|
+
},
|
|
279
|
+
"/api/claw/sessions/addParticipants",
|
|
280
|
+
{
|
|
281
|
+
sessionId: params.sessionId,
|
|
282
|
+
participants: params.participants,
|
|
283
|
+
},
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export async function removeParticipants(params: {
|
|
288
|
+
baseUrl: string;
|
|
289
|
+
clawToken: string;
|
|
290
|
+
sessionId: string;
|
|
291
|
+
participants: MochatParticipantInput[];
|
|
292
|
+
signal?: AbortSignal;
|
|
293
|
+
}): Promise<MochatSessionResponse> {
|
|
294
|
+
return await postJson<MochatSessionResponse>(
|
|
295
|
+
{
|
|
296
|
+
baseUrl: params.baseUrl,
|
|
297
|
+
clawToken: params.clawToken,
|
|
298
|
+
signal: params.signal,
|
|
299
|
+
},
|
|
300
|
+
"/api/claw/sessions/removeParticipants",
|
|
301
|
+
{
|
|
302
|
+
sessionId: params.sessionId,
|
|
303
|
+
participants: params.participants,
|
|
304
|
+
},
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export async function closeSession(params: {
|
|
309
|
+
baseUrl: string;
|
|
310
|
+
clawToken: string;
|
|
311
|
+
sessionId: string;
|
|
312
|
+
policy?: string;
|
|
313
|
+
signal?: AbortSignal;
|
|
314
|
+
}): Promise<MochatSessionResponse> {
|
|
315
|
+
return await postJson<MochatSessionResponse>(
|
|
316
|
+
{
|
|
317
|
+
baseUrl: params.baseUrl,
|
|
318
|
+
clawToken: params.clawToken,
|
|
319
|
+
signal: params.signal,
|
|
320
|
+
},
|
|
321
|
+
"/api/claw/sessions/close",
|
|
322
|
+
{
|
|
323
|
+
sessionId: params.sessionId,
|
|
324
|
+
...(params.policy ? { policy: params.policy } : {}),
|
|
325
|
+
},
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export async function getSession(params: {
|
|
330
|
+
baseUrl: string;
|
|
331
|
+
clawToken: string;
|
|
332
|
+
sessionId: string;
|
|
333
|
+
signal?: AbortSignal;
|
|
334
|
+
}): Promise<MochatSessionResponse> {
|
|
335
|
+
return await postJson<MochatSessionResponse>(
|
|
336
|
+
{
|
|
337
|
+
baseUrl: params.baseUrl,
|
|
338
|
+
clawToken: params.clawToken,
|
|
339
|
+
signal: params.signal,
|
|
340
|
+
},
|
|
341
|
+
"/api/claw/sessions/get",
|
|
342
|
+
{
|
|
343
|
+
sessionId: params.sessionId,
|
|
344
|
+
},
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export async function getSessionDetail(params: {
|
|
349
|
+
baseUrl: string;
|
|
350
|
+
clawToken: string;
|
|
351
|
+
sessionId: string;
|
|
352
|
+
signal?: AbortSignal;
|
|
353
|
+
}): Promise<MochatSessionResponse> {
|
|
354
|
+
return await postJson<MochatSessionResponse>(
|
|
355
|
+
{
|
|
356
|
+
baseUrl: params.baseUrl,
|
|
357
|
+
clawToken: params.clawToken,
|
|
358
|
+
signal: params.signal,
|
|
359
|
+
},
|
|
360
|
+
"/api/claw/sessions/detail",
|
|
361
|
+
{
|
|
362
|
+
sessionId: params.sessionId,
|
|
363
|
+
},
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export async function listSessionMessages(params: {
|
|
368
|
+
baseUrl: string;
|
|
369
|
+
clawToken: string;
|
|
370
|
+
sessionId: string;
|
|
371
|
+
beforeMessageId?: string;
|
|
372
|
+
limit?: number;
|
|
373
|
+
signal?: AbortSignal;
|
|
374
|
+
}): Promise<MochatMessagesResponse> {
|
|
375
|
+
return await postJson<MochatMessagesResponse>(
|
|
376
|
+
{
|
|
377
|
+
baseUrl: params.baseUrl,
|
|
378
|
+
clawToken: params.clawToken,
|
|
379
|
+
signal: params.signal,
|
|
380
|
+
},
|
|
381
|
+
"/api/claw/sessions/messages",
|
|
382
|
+
{
|
|
383
|
+
sessionId: params.sessionId,
|
|
384
|
+
...(params.beforeMessageId ? { beforeMessageId: params.beforeMessageId } : {}),
|
|
385
|
+
...(typeof params.limit === "number" ? { limit: params.limit } : {}),
|
|
386
|
+
},
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export async function listSessions(params: {
|
|
391
|
+
baseUrl: string;
|
|
392
|
+
clawToken: string;
|
|
393
|
+
updatedAfter?: string;
|
|
394
|
+
limit?: number;
|
|
395
|
+
signal?: AbortSignal;
|
|
396
|
+
}): Promise<MochatSessionListResponse> {
|
|
397
|
+
return await postJson<MochatSessionListResponse>(
|
|
398
|
+
{
|
|
399
|
+
baseUrl: params.baseUrl,
|
|
400
|
+
clawToken: params.clawToken,
|
|
401
|
+
signal: params.signal,
|
|
402
|
+
},
|
|
403
|
+
"/api/claw/sessions/list",
|
|
404
|
+
{
|
|
405
|
+
...(params.updatedAfter ? { updatedAfter: params.updatedAfter } : {}),
|
|
406
|
+
...(typeof params.limit === "number" ? { limit: params.limit } : {}),
|
|
407
|
+
},
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export async function getWorkspaceGroup(params: {
|
|
412
|
+
baseUrl: string;
|
|
413
|
+
clawToken: string;
|
|
414
|
+
groupId?: string;
|
|
415
|
+
signal?: AbortSignal;
|
|
416
|
+
}): Promise<MochatGroupResponse> {
|
|
417
|
+
return await postJson<MochatGroupResponse>(
|
|
418
|
+
{
|
|
419
|
+
baseUrl: params.baseUrl,
|
|
420
|
+
clawToken: params.clawToken,
|
|
421
|
+
signal: params.signal,
|
|
422
|
+
},
|
|
423
|
+
"/api/claw/groups/get",
|
|
424
|
+
{
|
|
425
|
+
...(params.groupId ? { groupId: params.groupId } : {}),
|
|
426
|
+
},
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export async function listPanelMessages(params: {
|
|
431
|
+
baseUrl: string;
|
|
432
|
+
clawToken: string;
|
|
433
|
+
panelId: string;
|
|
434
|
+
groupId?: string;
|
|
435
|
+
beforeMessageId?: string;
|
|
436
|
+
limit?: number;
|
|
437
|
+
signal?: AbortSignal;
|
|
438
|
+
}): Promise<MochatPanelMessagesResponse> {
|
|
439
|
+
return await postJson<MochatPanelMessagesResponse>(
|
|
440
|
+
{
|
|
441
|
+
baseUrl: params.baseUrl,
|
|
442
|
+
clawToken: params.clawToken,
|
|
443
|
+
signal: params.signal,
|
|
444
|
+
},
|
|
445
|
+
"/api/claw/groups/panels/messages",
|
|
446
|
+
{
|
|
447
|
+
panelId: params.panelId,
|
|
448
|
+
...(params.groupId ? { groupId: params.groupId } : {}),
|
|
449
|
+
...(params.beforeMessageId ? { beforeMessageId: params.beforeMessageId } : {}),
|
|
450
|
+
...(typeof params.limit === "number" ? { limit: params.limit } : {}),
|
|
451
|
+
},
|
|
452
|
+
);
|
|
453
|
+
}
|
package/src/channel.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildChannelConfigSchema,
|
|
3
|
+
DEFAULT_ACCOUNT_ID,
|
|
4
|
+
type ChannelOutboundContext,
|
|
5
|
+
type ChannelPlugin,
|
|
6
|
+
} from "openclaw/plugin-sdk";
|
|
7
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
8
|
+
import { MochatConfigSchema } from "./config-schema.js";
|
|
9
|
+
import {
|
|
10
|
+
listMochatAccountIds,
|
|
11
|
+
resolveDefaultMochatAccountId,
|
|
12
|
+
resolveMochatAccount,
|
|
13
|
+
type ResolvedMochatAccount,
|
|
14
|
+
} from "./accounts.js";
|
|
15
|
+
import { sendPanelMessage, sendSessionMessage } from "./api.js";
|
|
16
|
+
import { startMochatSocketClient } from "./socket.js";
|
|
17
|
+
|
|
18
|
+
const meta = {
|
|
19
|
+
id: "mochat",
|
|
20
|
+
label: "Mochat",
|
|
21
|
+
selectionLabel: "Mochat (Claw IM)",
|
|
22
|
+
docsPath: "/channels/mochat",
|
|
23
|
+
docsLabel: "mochat",
|
|
24
|
+
blurb: "Claw IM gateway for Mochat/Tailchat",
|
|
25
|
+
order: 95,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function resolveMochatTarget(raw: string): { id: string; isPanel: boolean } {
|
|
29
|
+
const trimmed = raw.trim();
|
|
30
|
+
if (!trimmed) {
|
|
31
|
+
return { id: "", isPanel: false };
|
|
32
|
+
}
|
|
33
|
+
const lower = trimmed.toLowerCase();
|
|
34
|
+
let id = trimmed;
|
|
35
|
+
let forcePanel = false;
|
|
36
|
+
const prefixes = ["mochat:", "group:", "channel:", "panel:"];
|
|
37
|
+
for (const prefix of prefixes) {
|
|
38
|
+
if (lower.startsWith(prefix)) {
|
|
39
|
+
id = trimmed.slice(prefix.length).trim();
|
|
40
|
+
if (prefix !== "mochat:") {
|
|
41
|
+
forcePanel = true;
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const isSessionId = id.startsWith("session_");
|
|
47
|
+
return { id, isPanel: forcePanel || !isSessionId };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildOutboundContent(ctx: ChannelOutboundContext, mediaUrls?: string[]): string {
|
|
51
|
+
const contentParts: string[] = [];
|
|
52
|
+
if (ctx.text?.trim()) {
|
|
53
|
+
contentParts.push(ctx.text.trim());
|
|
54
|
+
}
|
|
55
|
+
if (mediaUrls && mediaUrls.length > 0) {
|
|
56
|
+
contentParts.push(...mediaUrls);
|
|
57
|
+
}
|
|
58
|
+
return contentParts.join("\n").trim();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const mochatPlugin: ChannelPlugin<ResolvedMochatAccount> = {
|
|
62
|
+
id: "mochat",
|
|
63
|
+
meta,
|
|
64
|
+
capabilities: {
|
|
65
|
+
chatTypes: ["direct", "group"],
|
|
66
|
+
media: true,
|
|
67
|
+
reactions: false,
|
|
68
|
+
threads: false,
|
|
69
|
+
},
|
|
70
|
+
reload: { configPrefixes: ["channels.mochat"] },
|
|
71
|
+
configSchema: buildChannelConfigSchema(MochatConfigSchema),
|
|
72
|
+
config: {
|
|
73
|
+
listAccountIds: (cfg) => listMochatAccountIds(cfg),
|
|
74
|
+
resolveAccount: (cfg, accountId) => resolveMochatAccount({ cfg, accountId }),
|
|
75
|
+
defaultAccountId: (cfg) => resolveDefaultMochatAccountId(cfg),
|
|
76
|
+
isEnabled: (account) => account.enabled,
|
|
77
|
+
isConfigured: (account) => account.configured,
|
|
78
|
+
describeAccount: (account) => ({
|
|
79
|
+
accountId: account.accountId,
|
|
80
|
+
name: account.name,
|
|
81
|
+
enabled: account.enabled,
|
|
82
|
+
configured: account.configured,
|
|
83
|
+
baseUrl: account.config.baseUrl,
|
|
84
|
+
sessions: account.config.sessions ?? [],
|
|
85
|
+
panels: account.config.panels ?? [],
|
|
86
|
+
autoDiscoverSessions: account.config.autoDiscoverSessions,
|
|
87
|
+
autoDiscoverPanels: account.config.autoDiscoverPanels,
|
|
88
|
+
agentUserId: account.config.agentUserId ? "[set]" : "[missing]",
|
|
89
|
+
clawToken: account.config.clawToken ? "[set]" : "[missing]",
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
messaging: {
|
|
93
|
+
normalizeTarget: (target) => resolveMochatTarget(target).id || target.trim(),
|
|
94
|
+
targetResolver: {
|
|
95
|
+
looksLikeId: (input) => Boolean(input.trim()),
|
|
96
|
+
hint: "<sessionId>",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
outbound: {
|
|
100
|
+
deliveryMode: "direct",
|
|
101
|
+
sendText: async (ctx) => {
|
|
102
|
+
const account = resolveMochatAccount({
|
|
103
|
+
cfg: ctx.cfg as OpenClawConfig,
|
|
104
|
+
accountId: ctx.accountId ?? DEFAULT_ACCOUNT_ID,
|
|
105
|
+
});
|
|
106
|
+
if (!account.config.clawToken) {
|
|
107
|
+
throw new Error("Mochat clawToken is not configured");
|
|
108
|
+
}
|
|
109
|
+
const content = buildOutboundContent(ctx, []);
|
|
110
|
+
if (!content) {
|
|
111
|
+
return { channel: "mochat", to: ctx.to };
|
|
112
|
+
}
|
|
113
|
+
const target = resolveMochatTarget(ctx.to);
|
|
114
|
+
if (!target.id) {
|
|
115
|
+
return { channel: "mochat", to: ctx.to };
|
|
116
|
+
}
|
|
117
|
+
const isPanel =
|
|
118
|
+
target.isPanel || (account.config.panels ?? []).includes(target.id);
|
|
119
|
+
if (isPanel) {
|
|
120
|
+
await sendPanelMessage({
|
|
121
|
+
baseUrl: account.config.baseUrl,
|
|
122
|
+
clawToken: account.config.clawToken,
|
|
123
|
+
panelId: target.id,
|
|
124
|
+
content,
|
|
125
|
+
replyTo: ctx.replyToId ?? null,
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
await sendSessionMessage({
|
|
129
|
+
baseUrl: account.config.baseUrl,
|
|
130
|
+
clawToken: account.config.clawToken,
|
|
131
|
+
sessionId: target.id,
|
|
132
|
+
content,
|
|
133
|
+
replyTo: ctx.replyToId ?? null,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return { channel: "mochat", to: ctx.to };
|
|
137
|
+
},
|
|
138
|
+
sendMedia: async (ctx) => {
|
|
139
|
+
const account = resolveMochatAccount({
|
|
140
|
+
cfg: ctx.cfg as OpenClawConfig,
|
|
141
|
+
accountId: ctx.accountId ?? DEFAULT_ACCOUNT_ID,
|
|
142
|
+
});
|
|
143
|
+
if (!account.config.clawToken) {
|
|
144
|
+
throw new Error("Mochat clawToken is not configured");
|
|
145
|
+
}
|
|
146
|
+
const mediaUrls = ctx.mediaUrl ? [ctx.mediaUrl] : [];
|
|
147
|
+
const content = buildOutboundContent(ctx, mediaUrls);
|
|
148
|
+
if (!content) {
|
|
149
|
+
return { channel: "mochat", to: ctx.to };
|
|
150
|
+
}
|
|
151
|
+
const target = resolveMochatTarget(ctx.to);
|
|
152
|
+
if (!target.id) {
|
|
153
|
+
return { channel: "mochat", to: ctx.to };
|
|
154
|
+
}
|
|
155
|
+
const isPanel =
|
|
156
|
+
target.isPanel || (account.config.panels ?? []).includes(target.id);
|
|
157
|
+
if (isPanel) {
|
|
158
|
+
await sendPanelMessage({
|
|
159
|
+
baseUrl: account.config.baseUrl,
|
|
160
|
+
clawToken: account.config.clawToken,
|
|
161
|
+
panelId: target.id,
|
|
162
|
+
content,
|
|
163
|
+
replyTo: ctx.replyToId ?? null,
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
await sendSessionMessage({
|
|
167
|
+
baseUrl: account.config.baseUrl,
|
|
168
|
+
clawToken: account.config.clawToken,
|
|
169
|
+
sessionId: target.id,
|
|
170
|
+
content,
|
|
171
|
+
replyTo: ctx.replyToId ?? null,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return { channel: "mochat", to: ctx.to };
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
status: {
|
|
178
|
+
defaultRuntime: {
|
|
179
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
180
|
+
running: false,
|
|
181
|
+
lastStartAt: null,
|
|
182
|
+
lastStopAt: null,
|
|
183
|
+
lastError: null,
|
|
184
|
+
},
|
|
185
|
+
buildAccountSnapshot: ({ account, runtime }) => ({
|
|
186
|
+
accountId: account.accountId,
|
|
187
|
+
name: account.name,
|
|
188
|
+
enabled: account.enabled,
|
|
189
|
+
configured: account.configured,
|
|
190
|
+
baseUrl: account.config.baseUrl,
|
|
191
|
+
sessions: account.config.sessions ?? [],
|
|
192
|
+
panels: account.config.panels ?? [],
|
|
193
|
+
running: runtime?.running ?? false,
|
|
194
|
+
lastStartAt: runtime?.lastStartAt ?? null,
|
|
195
|
+
lastStopAt: runtime?.lastStopAt ?? null,
|
|
196
|
+
lastError: runtime?.lastError ?? null,
|
|
197
|
+
lastInboundAt: runtime?.lastInboundAt ?? null,
|
|
198
|
+
lastOutboundAt: runtime?.lastOutboundAt ?? null,
|
|
199
|
+
}),
|
|
200
|
+
},
|
|
201
|
+
gateway: {
|
|
202
|
+
startAccount: async (ctx) => {
|
|
203
|
+
const account = ctx.account;
|
|
204
|
+
if (!account.configured) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Mochat not configured for account "${account.accountId}" (missing clawToken, agentUserId, or sessions)`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const sessions = account.config.sessions ?? [];
|
|
211
|
+
const panels = account.config.panels ?? [];
|
|
212
|
+
if (
|
|
213
|
+
sessions.length === 0 &&
|
|
214
|
+
panels.length === 0 &&
|
|
215
|
+
!account.config.autoDiscoverSessions &&
|
|
216
|
+
!account.config.autoDiscoverPanels
|
|
217
|
+
) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
`Mochat account "${account.accountId}" has no sessions or panels configured`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
ctx.log?.info(
|
|
224
|
+
`[${account.accountId}] starting Mochat socket (${sessions.length} sessions, ${panels.length} panels)`,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
ctx.setStatus({
|
|
228
|
+
accountId: account.accountId,
|
|
229
|
+
running: true,
|
|
230
|
+
lastStartAt: Date.now(),
|
|
231
|
+
lastError: null,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const socketClient = startMochatSocketClient({
|
|
235
|
+
account,
|
|
236
|
+
log: ctx.log,
|
|
237
|
+
abortSignal: ctx.abortSignal,
|
|
238
|
+
statusSink: (patch) => ctx.setStatus({ accountId: account.accountId, ...patch }),
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
stop: () => {
|
|
243
|
+
socketClient.stop();
|
|
244
|
+
ctx.setStatus({
|
|
245
|
+
accountId: account.accountId,
|
|
246
|
+
running: false,
|
|
247
|
+
lastStopAt: Date.now(),
|
|
248
|
+
});
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
};
|