@aka_openclaw_plugin/mychat 0.1.1 → 0.1.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/api.js +5 -19
- package/channel-Dbv-np-3.js +5177 -0
- package/channel-plugin-api.js +2 -9
- package/index.js +16 -19
- package/package.json +7 -7
- package/runtime-7z_VfQ27.js +53 -0
- package/runtime-api.js +4 -14
- package/setup-entry.js +2 -9
- package/chunk-5KQQEVLS.js +0 -60
- package/chunk-PNKVD2UK.js +0 -26
- package/chunk-VJXCCUPC.js +0 -41
- package/chunk-ZECSAALT.js +0 -853
- package/config-api.js +0 -11
- package/runtime-setter-api.js +0 -11
package/chunk-ZECSAALT.js
DELETED
|
@@ -1,853 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
mychatConfigSchema
|
|
3
|
-
} from "./chunk-5KQQEVLS.js";
|
|
4
|
-
import {
|
|
5
|
-
getMychatRuntime,
|
|
6
|
-
init_runtime,
|
|
7
|
-
runtime_exports
|
|
8
|
-
} from "./chunk-VJXCCUPC.js";
|
|
9
|
-
import {
|
|
10
|
-
__esm,
|
|
11
|
-
__export,
|
|
12
|
-
__toCommonJS
|
|
13
|
-
} from "./chunk-PNKVD2UK.js";
|
|
14
|
-
|
|
15
|
-
// src/client/http-client.ts
|
|
16
|
-
var http_client_exports = {};
|
|
17
|
-
__export(http_client_exports, {
|
|
18
|
-
createMychatHttpClient: () => createMychatHttpClient
|
|
19
|
-
});
|
|
20
|
-
function createMychatHttpClient(params) {
|
|
21
|
-
const { baseUrl, token } = params;
|
|
22
|
-
const base = baseUrl.replace(/\/+$/, "");
|
|
23
|
-
async function request(method, path, body) {
|
|
24
|
-
try {
|
|
25
|
-
const headers = {
|
|
26
|
-
authorization: `Bearer ${token}`
|
|
27
|
-
};
|
|
28
|
-
if (body !== void 0) {
|
|
29
|
-
headers["content-type"] = "application/json";
|
|
30
|
-
}
|
|
31
|
-
const response = await fetch(`${base}${path}`, {
|
|
32
|
-
method,
|
|
33
|
-
headers,
|
|
34
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
35
|
-
});
|
|
36
|
-
if (!response.ok) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
return await response.json();
|
|
40
|
-
} catch {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return {
|
|
45
|
-
async getSelf() {
|
|
46
|
-
return request("GET", "/api/bot/self");
|
|
47
|
-
},
|
|
48
|
-
async getMe() {
|
|
49
|
-
return request("GET", "/api/auth/me");
|
|
50
|
-
},
|
|
51
|
-
async sendMessage(body) {
|
|
52
|
-
return request("POST", "/api/bot/messages", body);
|
|
53
|
-
},
|
|
54
|
-
async listMessages(params2) {
|
|
55
|
-
const searchParams = new URLSearchParams();
|
|
56
|
-
if (params2.conversationId) searchParams.set("conversationId", params2.conversationId);
|
|
57
|
-
if (params2.limit) searchParams.set("limit", String(params2.limit));
|
|
58
|
-
if (params2.after) searchParams.set("after", String(params2.after));
|
|
59
|
-
const qs = searchParams.toString();
|
|
60
|
-
const result = await request(
|
|
61
|
-
"GET",
|
|
62
|
-
`/api/bot/messages${qs ? `?${qs}` : ""}`
|
|
63
|
-
);
|
|
64
|
-
return result?.messages ?? [];
|
|
65
|
-
},
|
|
66
|
-
async addReaction(messageId, body) {
|
|
67
|
-
return request("POST", `/api/bot/messages/${messageId}/reactions`, body);
|
|
68
|
-
},
|
|
69
|
-
async removeReaction(messageId, emoji) {
|
|
70
|
-
return request("POST", `/api/bot/messages/${messageId}/reactions`, {
|
|
71
|
-
emoji,
|
|
72
|
-
action: "remove"
|
|
73
|
-
});
|
|
74
|
-
},
|
|
75
|
-
async uploadFile(params2) {
|
|
76
|
-
try {
|
|
77
|
-
const formData = new FormData();
|
|
78
|
-
let blob;
|
|
79
|
-
if (params2.file instanceof Blob) {
|
|
80
|
-
blob = params2.file;
|
|
81
|
-
} else {
|
|
82
|
-
const bytes = new Uint8Array(
|
|
83
|
-
params2.file.buffer,
|
|
84
|
-
params2.file.byteOffset,
|
|
85
|
-
params2.file.byteLength
|
|
86
|
-
);
|
|
87
|
-
blob = new Blob([bytes], { type: params2.mimeType });
|
|
88
|
-
}
|
|
89
|
-
formData.append("file", blob, params2.filename);
|
|
90
|
-
if (params2.scopeKind) formData.append("scopeKind", params2.scopeKind);
|
|
91
|
-
if (params2.scopeId) formData.append("scopeId", params2.scopeId);
|
|
92
|
-
const response = await fetch(`${base}/api/bot/uploads`, {
|
|
93
|
-
method: "POST",
|
|
94
|
-
headers: { authorization: `Bearer ${token}` },
|
|
95
|
-
body: formData
|
|
96
|
-
});
|
|
97
|
-
if (!response.ok) return null;
|
|
98
|
-
return await response.json();
|
|
99
|
-
} catch {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
async healthCheck() {
|
|
104
|
-
const start = Date.now();
|
|
105
|
-
try {
|
|
106
|
-
const response = await fetch(`${base}/health`);
|
|
107
|
-
const latencyMs = Date.now() - start;
|
|
108
|
-
return { ok: response.ok, latencyMs };
|
|
109
|
-
} catch {
|
|
110
|
-
return { ok: false, latencyMs: Date.now() - start };
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
var init_http_client = __esm({
|
|
116
|
-
"src/client/http-client.ts"() {
|
|
117
|
-
"use strict";
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// src/client/ws-client.ts
|
|
122
|
-
var ws_client_exports = {};
|
|
123
|
-
__export(ws_client_exports, {
|
|
124
|
-
createMychatWsClient: () => createMychatWsClient
|
|
125
|
-
});
|
|
126
|
-
function createMychatWsClient(params) {
|
|
127
|
-
const { wsUrl, token, botSelfId, reconnect } = params;
|
|
128
|
-
const initialDelay = reconnect?.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
|
|
129
|
-
const maxDelay = reconnect?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
130
|
-
const backoffMultiplier = reconnect?.backoffMultiplier ?? DEFAULT_BACKOFF_MULTIPLIER;
|
|
131
|
-
let ws = null;
|
|
132
|
-
let connected = false;
|
|
133
|
-
let reconnectTimer = null;
|
|
134
|
-
let currentDelay = initialDelay;
|
|
135
|
-
const handlers = /* @__PURE__ */ new Set();
|
|
136
|
-
function buildUrl() {
|
|
137
|
-
const url = new URL(wsUrl);
|
|
138
|
-
if (!url.pathname.includes("/ws/bot")) {
|
|
139
|
-
url.pathname = url.pathname.replace(/\/+$/, "") + "/ws/bot";
|
|
140
|
-
}
|
|
141
|
-
return url.toString();
|
|
142
|
-
}
|
|
143
|
-
function scheduleReconnect() {
|
|
144
|
-
if (reconnectTimer) return;
|
|
145
|
-
reconnectTimer = setTimeout(() => {
|
|
146
|
-
reconnectTimer = null;
|
|
147
|
-
currentDelay = Math.min(currentDelay * backoffMultiplier, maxDelay);
|
|
148
|
-
connect();
|
|
149
|
-
}, currentDelay);
|
|
150
|
-
}
|
|
151
|
-
function connect() {
|
|
152
|
-
if (ws) return;
|
|
153
|
-
try {
|
|
154
|
-
const url = buildUrl();
|
|
155
|
-
const urlWithToken = new URL(url);
|
|
156
|
-
urlWithToken.searchParams.set("token", token);
|
|
157
|
-
ws = new WebSocket(urlWithToken.toString());
|
|
158
|
-
ws.onopen = () => {
|
|
159
|
-
connected = true;
|
|
160
|
-
currentDelay = initialDelay;
|
|
161
|
-
if (botSelfId) {
|
|
162
|
-
ws?.send(
|
|
163
|
-
JSON.stringify({
|
|
164
|
-
type: "subscribe",
|
|
165
|
-
body: {
|
|
166
|
-
subscribe: {
|
|
167
|
-
conversationId: botSelfId
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
})
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
ws.onmessage = (event) => {
|
|
175
|
-
try {
|
|
176
|
-
const message = JSON.parse(event.data);
|
|
177
|
-
for (const handler of handlers) {
|
|
178
|
-
handler(message);
|
|
179
|
-
}
|
|
180
|
-
} catch {
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
ws.onclose = () => {
|
|
184
|
-
connected = false;
|
|
185
|
-
ws = null;
|
|
186
|
-
scheduleReconnect();
|
|
187
|
-
};
|
|
188
|
-
ws.onerror = () => {
|
|
189
|
-
ws?.close();
|
|
190
|
-
};
|
|
191
|
-
} catch {
|
|
192
|
-
scheduleReconnect();
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
function disconnect() {
|
|
196
|
-
if (reconnectTimer) {
|
|
197
|
-
clearTimeout(reconnectTimer);
|
|
198
|
-
reconnectTimer = null;
|
|
199
|
-
}
|
|
200
|
-
connected = false;
|
|
201
|
-
ws?.close();
|
|
202
|
-
ws = null;
|
|
203
|
-
}
|
|
204
|
-
return {
|
|
205
|
-
connect,
|
|
206
|
-
disconnect,
|
|
207
|
-
onMessage(handler) {
|
|
208
|
-
handlers.add(handler);
|
|
209
|
-
return () => {
|
|
210
|
-
handlers.delete(handler);
|
|
211
|
-
};
|
|
212
|
-
},
|
|
213
|
-
isConnected() {
|
|
214
|
-
return connected;
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
var DEFAULT_INITIAL_DELAY_MS, DEFAULT_MAX_DELAY_MS, DEFAULT_BACKOFF_MULTIPLIER;
|
|
219
|
-
var init_ws_client = __esm({
|
|
220
|
-
"src/client/ws-client.ts"() {
|
|
221
|
-
"use strict";
|
|
222
|
-
DEFAULT_INITIAL_DELAY_MS = 1e3;
|
|
223
|
-
DEFAULT_MAX_DELAY_MS = 3e4;
|
|
224
|
-
DEFAULT_BACKOFF_MULTIPLIER = 2;
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// src/channel.ts
|
|
229
|
-
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
230
|
-
|
|
231
|
-
// src/shared.ts
|
|
232
|
-
import { createChannelPluginBase } from "openclaw/plugin-sdk/channel-core";
|
|
233
|
-
|
|
234
|
-
// src/accounts.ts
|
|
235
|
-
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/channel-plugin-common";
|
|
236
|
-
var DEFAULT_DM_POLICY = "open";
|
|
237
|
-
var DEFAULT_GROUP_POLICY = "open";
|
|
238
|
-
var DEFAULT_HISTORY_LIMIT = 50;
|
|
239
|
-
var DEFAULT_MEDIA_MAX_MB = 10;
|
|
240
|
-
function resolveWsUrl(baseUrl) {
|
|
241
|
-
const url = new URL(baseUrl);
|
|
242
|
-
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
243
|
-
url.pathname = url.pathname.replace(/\/+$/, "") + "/ws/bot";
|
|
244
|
-
return url.toString();
|
|
245
|
-
}
|
|
246
|
-
function mergeAccountConfig(channel, account) {
|
|
247
|
-
if (!account) return channel;
|
|
248
|
-
return {
|
|
249
|
-
...channel,
|
|
250
|
-
...account,
|
|
251
|
-
actions: {
|
|
252
|
-
messages: account.actions?.messages ?? channel.actions?.messages ?? true,
|
|
253
|
-
threads: account.actions?.threads ?? channel.actions?.threads ?? true,
|
|
254
|
-
reactions: account.actions?.reactions ?? channel.actions?.reactions ?? true
|
|
255
|
-
},
|
|
256
|
-
groups: { ...channel.groups ?? {}, ...account.groups ?? {} },
|
|
257
|
-
reconnect: account.reconnect ?? channel.reconnect
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
function resolveMychatAccounts(cfg) {
|
|
261
|
-
const channelConfig = cfg.channels;
|
|
262
|
-
const mychatConfig = channelConfig?.mychat;
|
|
263
|
-
if (!mychatConfig) return [];
|
|
264
|
-
const accounts = mychatConfig.accounts ?? {};
|
|
265
|
-
const accountIds = Object.keys(accounts);
|
|
266
|
-
if (accountIds.length === 0) {
|
|
267
|
-
const resolved = resolveSingleAccount({
|
|
268
|
-
accountId: DEFAULT_ACCOUNT_ID,
|
|
269
|
-
config: mychatConfig
|
|
270
|
-
});
|
|
271
|
-
return resolved ? [resolved] : [];
|
|
272
|
-
}
|
|
273
|
-
return accountIds.map((id) => {
|
|
274
|
-
const merged = mergeAccountConfig(mychatConfig, accounts[id]);
|
|
275
|
-
return resolveSingleAccount({ accountId: id, config: merged });
|
|
276
|
-
}).filter(Boolean);
|
|
277
|
-
}
|
|
278
|
-
function resolveSingleAccount(params) {
|
|
279
|
-
const { accountId, config } = params;
|
|
280
|
-
const enabled = config.enabled ?? true;
|
|
281
|
-
if (!enabled) return null;
|
|
282
|
-
const token = resolveToken(config);
|
|
283
|
-
if (!token) return null;
|
|
284
|
-
const baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
285
|
-
const wsUrl = config.wsUrl ?? resolveWsUrl(baseUrl);
|
|
286
|
-
return {
|
|
287
|
-
accountId: normalizeAccountId(accountId),
|
|
288
|
-
enabled,
|
|
289
|
-
name: config.name,
|
|
290
|
-
baseUrl,
|
|
291
|
-
wsUrl,
|
|
292
|
-
token,
|
|
293
|
-
defaultTo: config.defaultTo,
|
|
294
|
-
dmPolicy: config.dmPolicy ?? DEFAULT_DM_POLICY,
|
|
295
|
-
groupPolicy: config.groupPolicy ?? DEFAULT_GROUP_POLICY,
|
|
296
|
-
allowFrom: config.allowFrom ?? [],
|
|
297
|
-
groupAllowFrom: config.groupAllowFrom ?? [],
|
|
298
|
-
requireMention: config.requireMention ?? false,
|
|
299
|
-
historyLimit: config.historyLimit ?? DEFAULT_HISTORY_LIMIT,
|
|
300
|
-
mediaMaxMb: config.mediaMaxMb ?? DEFAULT_MEDIA_MAX_MB,
|
|
301
|
-
actions: {
|
|
302
|
-
messages: config.actions?.messages ?? true,
|
|
303
|
-
threads: config.actions?.threads ?? true,
|
|
304
|
-
reactions: config.actions?.reactions ?? true
|
|
305
|
-
},
|
|
306
|
-
groups: config.groups ?? {},
|
|
307
|
-
reconnect: config.reconnect ?? {}
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
function resolveToken(config) {
|
|
311
|
-
if (config.token) return config.token;
|
|
312
|
-
if (typeof process !== "undefined") {
|
|
313
|
-
const envToken = process.env.MYCHAT_BOT_TOKEN;
|
|
314
|
-
if (envToken) return envToken;
|
|
315
|
-
}
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
function resolveMychatAccount(params) {
|
|
319
|
-
const accounts = resolveMychatAccounts(params.cfg);
|
|
320
|
-
if (accounts.length === 0) return null;
|
|
321
|
-
if (!params.accountId) return accounts[0];
|
|
322
|
-
return accounts.find((a) => a.accountId === params.accountId) ?? accounts[0] ?? null;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// src/logger.ts
|
|
326
|
-
var PREFIX = "mychat";
|
|
327
|
-
function mychatLogPrefix(feature) {
|
|
328
|
-
return `[${PREFIX}:${feature}]`;
|
|
329
|
-
}
|
|
330
|
-
function getMychatLogger() {
|
|
331
|
-
try {
|
|
332
|
-
const mod = (init_runtime(), __toCommonJS(runtime_exports));
|
|
333
|
-
const runtime = mod.tryGetMychatRuntime?.();
|
|
334
|
-
const logger = runtime?.logger;
|
|
335
|
-
if (!logger) return null;
|
|
336
|
-
return {
|
|
337
|
-
debug: (m) => logger.debug?.(m),
|
|
338
|
-
info: (m) => logger.info(m),
|
|
339
|
-
warn: (m) => logger.warn(m),
|
|
340
|
-
error: (m) => logger.error(m)
|
|
341
|
-
};
|
|
342
|
-
} catch {
|
|
343
|
-
return null;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// src/monitor/listeners.ts
|
|
348
|
-
function createMychatMessageListener(params) {
|
|
349
|
-
const { handler } = params;
|
|
350
|
-
return {
|
|
351
|
-
handle(message) {
|
|
352
|
-
handler.handleMessage(message).catch(() => {
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
function createMychatReactionListener(params) {
|
|
358
|
-
const logger = getMychatLogger();
|
|
359
|
-
const prefix = mychatLogPrefix("reaction");
|
|
360
|
-
return {
|
|
361
|
-
handle(message) {
|
|
362
|
-
const emoji = message.body?.reaction;
|
|
363
|
-
const action = message.body?.action ?? "add";
|
|
364
|
-
const target = message.body?.target;
|
|
365
|
-
const senderId = message.from?.id;
|
|
366
|
-
if (logger) logger.debug(`${prefix} ${action} ${emoji} on ${target} by ${senderId}`);
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
function createMychatTypingListener(params) {
|
|
371
|
-
const logger = getMychatLogger();
|
|
372
|
-
const prefix = mychatLogPrefix("typing");
|
|
373
|
-
return {
|
|
374
|
-
handle(message) {
|
|
375
|
-
const senderId = message.from?.id;
|
|
376
|
-
if (senderId === params.account.accountId) return;
|
|
377
|
-
if (logger) logger.debug(`${prefix} from ${senderId}`);
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// src/monitor/provider.startup.ts
|
|
383
|
-
function createMychatProviderClients(account, botSelfId) {
|
|
384
|
-
const { createMychatHttpClient: createMychatHttpClient2 } = (init_http_client(), __toCommonJS(http_client_exports));
|
|
385
|
-
const { createMychatWsClient: createMychatWsClient2 } = (init_ws_client(), __toCommonJS(ws_client_exports));
|
|
386
|
-
const httpClient = createMychatHttpClient2({
|
|
387
|
-
baseUrl: account.baseUrl,
|
|
388
|
-
token: account.token
|
|
389
|
-
});
|
|
390
|
-
const wsClient = createMychatWsClient2({
|
|
391
|
-
wsUrl: account.wsUrl,
|
|
392
|
-
token: account.token,
|
|
393
|
-
botSelfId,
|
|
394
|
-
reconnect: account.reconnect
|
|
395
|
-
});
|
|
396
|
-
return { httpClient, wsClient };
|
|
397
|
-
}
|
|
398
|
-
function registerMychatMonitorListeners(params) {
|
|
399
|
-
const { account, handler, wsClient } = params;
|
|
400
|
-
const messageListener = createMychatMessageListener({ account, handler });
|
|
401
|
-
const reactionListener = createMychatReactionListener({ account });
|
|
402
|
-
const typingListener = createMychatTypingListener({ account });
|
|
403
|
-
wsClient.onMessage((message) => {
|
|
404
|
-
switch (message.type) {
|
|
405
|
-
case "message":
|
|
406
|
-
case "stream":
|
|
407
|
-
messageListener.handle(message);
|
|
408
|
-
break;
|
|
409
|
-
case "reaction":
|
|
410
|
-
reactionListener.handle(message);
|
|
411
|
-
break;
|
|
412
|
-
case "typing":
|
|
413
|
-
typingListener.handle(message);
|
|
414
|
-
break;
|
|
415
|
-
default:
|
|
416
|
-
break;
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// src/monitor/provider.lifecycle.ts
|
|
422
|
-
var DEFAULT_READY_TIMEOUT_MS = 15e3;
|
|
423
|
-
function createMychatProviderLifecycle(params) {
|
|
424
|
-
const { httpClient, wsClient, readyTimeoutMs = DEFAULT_READY_TIMEOUT_MS } = params;
|
|
425
|
-
const logger = getMychatLogger();
|
|
426
|
-
const prefix = mychatLogPrefix("lifecycle");
|
|
427
|
-
return {
|
|
428
|
-
async waitForReady() {
|
|
429
|
-
const start = Date.now();
|
|
430
|
-
wsClient.connect();
|
|
431
|
-
while (Date.now() - start < readyTimeoutMs) {
|
|
432
|
-
if (wsClient.isConnected()) {
|
|
433
|
-
const botSelf = await httpClient.getSelf();
|
|
434
|
-
if (botSelf) {
|
|
435
|
-
logger?.info(`${prefix} bot connected botId=${botSelf.botId}`);
|
|
436
|
-
return botSelf;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
440
|
-
}
|
|
441
|
-
logger?.warn(`${prefix} ready timeout`);
|
|
442
|
-
return null;
|
|
443
|
-
},
|
|
444
|
-
shutdown() {
|
|
445
|
-
wsClient.disconnect();
|
|
446
|
-
logger?.info(`${prefix} shutdown`);
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// src/monitor/inbound-dedupe.ts
|
|
452
|
-
var MAX_CACHE_SIZE = 500;
|
|
453
|
-
function createMychatInboundDedupe() {
|
|
454
|
-
const seen = /* @__PURE__ */ new Set();
|
|
455
|
-
return {
|
|
456
|
-
isDuplicate(messageId) {
|
|
457
|
-
if (seen.has(messageId)) return true;
|
|
458
|
-
seen.add(messageId);
|
|
459
|
-
if (seen.size > MAX_CACHE_SIZE) {
|
|
460
|
-
const first = seen.values().next().value;
|
|
461
|
-
if (first !== void 0) seen.delete(first);
|
|
462
|
-
}
|
|
463
|
-
return false;
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// src/monitor/message-text.ts
|
|
469
|
-
function extractMychatMessageText(message) {
|
|
470
|
-
return message.body?.text ?? "";
|
|
471
|
-
}
|
|
472
|
-
function extractMentionUserIds(message) {
|
|
473
|
-
const mentions = message.body?.mentions ?? [];
|
|
474
|
-
return mentions.map((m) => m.userId).filter(Boolean);
|
|
475
|
-
}
|
|
476
|
-
function isBotMentioned(message, botUserId) {
|
|
477
|
-
if (!botUserId) return false;
|
|
478
|
-
const mentionIds = extractMentionUserIds(message);
|
|
479
|
-
if (mentionIds.includes(botUserId)) return true;
|
|
480
|
-
const text = message.body?.text ?? "";
|
|
481
|
-
return text.includes(`@${botUserId}`);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// src/monitor/message-handler.preflight.ts
|
|
485
|
-
function mychatPreflight(message, account) {
|
|
486
|
-
const senderId = message.from?.id;
|
|
487
|
-
if (!senderId) {
|
|
488
|
-
return { allowed: false, reason: "no-sender" };
|
|
489
|
-
}
|
|
490
|
-
if (senderId === account.accountId) {
|
|
491
|
-
return { allowed: false, reason: "self" };
|
|
492
|
-
}
|
|
493
|
-
const toKind = message.to?.kind ?? "direct";
|
|
494
|
-
if (toKind === "direct" || toKind === "channel") {
|
|
495
|
-
if (account.dmPolicy === "disabled") {
|
|
496
|
-
return { allowed: false, reason: "dm-disabled" };
|
|
497
|
-
}
|
|
498
|
-
if (account.dmPolicy === "allowlist" && account.allowFrom.length > 0) {
|
|
499
|
-
if (!account.allowFrom.includes(senderId)) {
|
|
500
|
-
return { allowed: false, reason: "dm-not-allowed" };
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
if (toKind === "group") {
|
|
505
|
-
if (account.groupPolicy === "disabled") {
|
|
506
|
-
return { allowed: false, reason: "group-disabled" };
|
|
507
|
-
}
|
|
508
|
-
if (account.groupPolicy === "allowlist" && account.groupAllowFrom.length > 0) {
|
|
509
|
-
if (!account.groupAllowFrom.includes(senderId)) {
|
|
510
|
-
return { allowed: false, reason: "group-not-allowed" };
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
const groupId = message.to?.id;
|
|
514
|
-
if (groupId && account.groups[groupId]) {
|
|
515
|
-
const groupConfig = account.groups[groupId];
|
|
516
|
-
if (groupConfig.enabled === false) {
|
|
517
|
-
return { allowed: false, reason: "group-disabled" };
|
|
518
|
-
}
|
|
519
|
-
if (groupConfig.requireMention && !isBotMentioned(message, account.accountId)) {
|
|
520
|
-
return { allowed: false, reason: "mention-required" };
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
if (account.requireMention && !isBotMentioned(message, account.accountId)) {
|
|
524
|
-
return { allowed: false, reason: "mention-required" };
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
return { allowed: true };
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// src/monitor/inbound-context.ts
|
|
531
|
-
function buildMychatInboundContext(message, account) {
|
|
532
|
-
if (message.type !== "message" && message.type !== "stream") return null;
|
|
533
|
-
const senderId = message.from?.id;
|
|
534
|
-
if (!senderId) return null;
|
|
535
|
-
const text = message.body?.text ?? "";
|
|
536
|
-
const toKind = message.to?.kind ?? "direct";
|
|
537
|
-
const chatType = toKind === "group" || toKind === "channel" ? "group" : "direct";
|
|
538
|
-
return {
|
|
539
|
-
messageId: message.id,
|
|
540
|
-
text,
|
|
541
|
-
senderId,
|
|
542
|
-
senderName: message.from?.name,
|
|
543
|
-
chatType,
|
|
544
|
-
conversationId: message.to?.id ?? message.body?.conversationId,
|
|
545
|
-
threadId: message.body?.parentId,
|
|
546
|
-
replyTo: message.body?.parentId,
|
|
547
|
-
mentions: message.body?.mentions ?? [],
|
|
548
|
-
attachments: message.body?.attachments ?? [],
|
|
549
|
-
timestamp: message.timestamp
|
|
550
|
-
};
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// src/monitor/message-media.ts
|
|
554
|
-
function resolveMychatMediaAttachments(message) {
|
|
555
|
-
const attachments = message.body?.attachments ?? [];
|
|
556
|
-
return attachments.filter(
|
|
557
|
-
(a) => a.url && (a.mimeType.startsWith("image/") || a.mimeType.startsWith("video/") || a.mimeType.startsWith("audio/") || a.mimeType === "application/pdf")
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// src/monitor/message-handler.process.ts
|
|
562
|
-
function processMychatInbound(message, account) {
|
|
563
|
-
const context = buildMychatInboundContext(message, account);
|
|
564
|
-
if (!context) return null;
|
|
565
|
-
const text = extractMychatMessageText(message);
|
|
566
|
-
const media = resolveMychatMediaAttachments(message);
|
|
567
|
-
return { context, text, media };
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// src/monitor/message-handler.ts
|
|
571
|
-
function createMychatMessageHandler(params) {
|
|
572
|
-
const { account, onInbound } = params;
|
|
573
|
-
const dedupe = createMychatInboundDedupe();
|
|
574
|
-
const logger = getMychatLogger();
|
|
575
|
-
const prefix = mychatLogPrefix("handler");
|
|
576
|
-
return {
|
|
577
|
-
async handleMessage(message) {
|
|
578
|
-
if (dedupe.isDuplicate(message.id)) return;
|
|
579
|
-
const preflight = mychatPreflight(message, account);
|
|
580
|
-
if (!preflight.allowed) {
|
|
581
|
-
if (logger) logger.debug(`${prefix} message rejected reason=${preflight.reason} messageId=${message.id}`);
|
|
582
|
-
return;
|
|
583
|
-
}
|
|
584
|
-
const result = processMychatInbound(message, account);
|
|
585
|
-
if (!result) return;
|
|
586
|
-
if (logger) logger.debug(`${prefix} message accepted messageId=${result.context.messageId} chatType=${result.context.chatType}`);
|
|
587
|
-
await onInbound(result);
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// src/monitor/provider.ts
|
|
593
|
-
async function monitorMychatProvider(ctx, account) {
|
|
594
|
-
const logger = getMychatLogger();
|
|
595
|
-
const prefix = mychatLogPrefix("provider");
|
|
596
|
-
if (logger) logger.info(`${prefix} starting accountId=${account.accountId}`);
|
|
597
|
-
const { createMychatHttpClient: createMychatHttpClient2 } = (init_http_client(), __toCommonJS(http_client_exports));
|
|
598
|
-
const httpClient = createMychatHttpClient2({
|
|
599
|
-
baseUrl: account.baseUrl,
|
|
600
|
-
token: account.token
|
|
601
|
-
});
|
|
602
|
-
const botSelf = await httpClient.getSelf();
|
|
603
|
-
if (!botSelf) {
|
|
604
|
-
if (logger) logger.error(`${prefix} failed to get bot identity`);
|
|
605
|
-
throw new Error("MyChat provider failed to get bot identity");
|
|
606
|
-
}
|
|
607
|
-
const health = await httpClient.healthCheck();
|
|
608
|
-
if (!health.ok) {
|
|
609
|
-
if (logger) logger.warn(`${prefix} health check failed latencyMs=${health.latencyMs}`);
|
|
610
|
-
}
|
|
611
|
-
const { wsClient } = createMychatProviderClients(account, botSelf.botId);
|
|
612
|
-
const handler = createMychatMessageHandler({
|
|
613
|
-
account,
|
|
614
|
-
onInbound: async (result) => {
|
|
615
|
-
ctx.runtime?.channel?.turn?.({
|
|
616
|
-
text: result.text,
|
|
617
|
-
context: result.context,
|
|
618
|
-
media: result.media
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
});
|
|
622
|
-
registerMychatMonitorListeners({ account, handler, wsClient });
|
|
623
|
-
const lifecycle = createMychatProviderLifecycle({ httpClient, wsClient });
|
|
624
|
-
const readyBotSelf = await lifecycle.waitForReady();
|
|
625
|
-
if (!readyBotSelf) {
|
|
626
|
-
if (logger) logger.error(`${prefix} failed to connect`);
|
|
627
|
-
lifecycle.shutdown();
|
|
628
|
-
throw new Error("MyChat provider failed to connect");
|
|
629
|
-
}
|
|
630
|
-
return {
|
|
631
|
-
unsubscribe() {
|
|
632
|
-
lifecycle.shutdown();
|
|
633
|
-
}
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// src/shared.ts
|
|
638
|
-
function createMychatPluginBase() {
|
|
639
|
-
const base = createChannelPluginBase({
|
|
640
|
-
id: "mychat",
|
|
641
|
-
setup: {
|
|
642
|
-
applyAccountConfig(params) {
|
|
643
|
-
return params.cfg;
|
|
644
|
-
}
|
|
645
|
-
},
|
|
646
|
-
meta: {
|
|
647
|
-
label: "MyChat",
|
|
648
|
-
docsPath: "docs/plugins/mychat",
|
|
649
|
-
blurb: "Connect to MyChat server via bot token",
|
|
650
|
-
aliases: ["mychat"]
|
|
651
|
-
},
|
|
652
|
-
capabilities: {
|
|
653
|
-
chatTypes: ["direct", "group", "thread"],
|
|
654
|
-
reactions: true,
|
|
655
|
-
media: true,
|
|
656
|
-
threads: true
|
|
657
|
-
},
|
|
658
|
-
reload: { configPrefixes: ["channels.mychat"] },
|
|
659
|
-
configSchema: mychatConfigSchema,
|
|
660
|
-
config: {
|
|
661
|
-
listAccountIds(cfg) {
|
|
662
|
-
return resolveMychatAccounts(cfg).map((a) => a.accountId);
|
|
663
|
-
},
|
|
664
|
-
resolveAccount(cfg, accountId) {
|
|
665
|
-
return resolveMychatAccount({ cfg, accountId });
|
|
666
|
-
},
|
|
667
|
-
async inspectAccount(account) {
|
|
668
|
-
return {
|
|
669
|
-
accountId: account.accountId,
|
|
670
|
-
enabled: account.enabled,
|
|
671
|
-
label: account.name ?? account.baseUrl
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
return {
|
|
677
|
-
...base,
|
|
678
|
-
gateway: {
|
|
679
|
-
async startAccount(ctx) {
|
|
680
|
-
const result = await monitorMychatProvider(ctx, ctx.account);
|
|
681
|
-
return {
|
|
682
|
-
unsubscribe: result.unsubscribe
|
|
683
|
-
};
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// src/outbound/outbound-adapter.ts
|
|
690
|
-
init_runtime();
|
|
691
|
-
|
|
692
|
-
// src/outbound/outbound-payload.ts
|
|
693
|
-
async function sendMychatOutboundPayload(ctx, body) {
|
|
694
|
-
const logger = getMychatLogger();
|
|
695
|
-
const prefix = mychatLogPrefix("outbound");
|
|
696
|
-
try {
|
|
697
|
-
const result = await ctx.httpClient.sendMessage(body);
|
|
698
|
-
if (result) {
|
|
699
|
-
if (logger) logger.debug(`${prefix} message sent id=${result.id} status=${result.status}`);
|
|
700
|
-
return { messageId: result.id, ok: true };
|
|
701
|
-
}
|
|
702
|
-
return { ok: false };
|
|
703
|
-
} catch (error) {
|
|
704
|
-
if (logger) logger.error(`${prefix} send failed error=${error instanceof Error ? error.message : String(error)}`);
|
|
705
|
-
return { ok: false };
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// src/outbound/outbound-send-context.ts
|
|
710
|
-
function createMychatOutboundSendContext(params) {
|
|
711
|
-
return {
|
|
712
|
-
account: params.account,
|
|
713
|
-
httpClient: params.httpClient
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// src/outbound/outbound-adapter.ts
|
|
718
|
-
var TEXT_CHUNK_LIMIT = 4e3;
|
|
719
|
-
function chunkText(text, limit) {
|
|
720
|
-
if (text.length <= limit) return [text];
|
|
721
|
-
const chunks = [];
|
|
722
|
-
let remaining = text;
|
|
723
|
-
while (remaining.length > 0) {
|
|
724
|
-
chunks.push(remaining.slice(0, limit));
|
|
725
|
-
remaining = remaining.slice(limit);
|
|
726
|
-
}
|
|
727
|
-
return chunks;
|
|
728
|
-
}
|
|
729
|
-
var mychatOutbound = {
|
|
730
|
-
deliveryMode: "direct",
|
|
731
|
-
textChunkLimit: TEXT_CHUNK_LIMIT,
|
|
732
|
-
async sendText(params) {
|
|
733
|
-
const logger = getMychatLogger();
|
|
734
|
-
const runtime = getMychatRuntime();
|
|
735
|
-
const account = runtime.mychat;
|
|
736
|
-
if (!account) {
|
|
737
|
-
if (logger) logger.error(`${mychatLogPrefix("outbound")} no runtime account`);
|
|
738
|
-
return { channel: "mychat", messageId: "", ok: false };
|
|
739
|
-
}
|
|
740
|
-
const ctx = createMychatOutboundSendContext({
|
|
741
|
-
account: params.account ?? {},
|
|
742
|
-
httpClient: account.httpClient
|
|
743
|
-
});
|
|
744
|
-
const chunks = chunkText(params.text, TEXT_CHUNK_LIMIT);
|
|
745
|
-
let lastOk = false;
|
|
746
|
-
let lastMessageId = "";
|
|
747
|
-
for (const chunk of chunks) {
|
|
748
|
-
const result = await sendMychatOutboundPayload(ctx, {
|
|
749
|
-
to: params.target ?? "",
|
|
750
|
-
text: chunk,
|
|
751
|
-
type: params.chatType,
|
|
752
|
-
replyTo: params.replyTo
|
|
753
|
-
});
|
|
754
|
-
lastOk = result.ok;
|
|
755
|
-
if (result.messageId) lastMessageId = result.messageId;
|
|
756
|
-
}
|
|
757
|
-
return { channel: "mychat", messageId: lastMessageId, ok: lastOk };
|
|
758
|
-
},
|
|
759
|
-
async sendMedia(params) {
|
|
760
|
-
const logger = getMychatLogger();
|
|
761
|
-
const runtime = getMychatRuntime();
|
|
762
|
-
const account = runtime.mychat;
|
|
763
|
-
if (!account) {
|
|
764
|
-
if (logger) logger.error(`${mychatLogPrefix("outbound")} no runtime account`);
|
|
765
|
-
return { channel: "mychat", messageId: "", ok: false };
|
|
766
|
-
}
|
|
767
|
-
if (params.media) {
|
|
768
|
-
const uploadResult = await account.httpClient.uploadFile({
|
|
769
|
-
file: params.media,
|
|
770
|
-
filename: params.filename ?? "upload",
|
|
771
|
-
mimeType: params.mimeType ?? "application/octet-stream"
|
|
772
|
-
});
|
|
773
|
-
if (uploadResult) {
|
|
774
|
-
const ctx = createMychatOutboundSendContext({
|
|
775
|
-
account: params.account ?? {},
|
|
776
|
-
httpClient: account.httpClient
|
|
777
|
-
});
|
|
778
|
-
const result = await sendMychatOutboundPayload(ctx, {
|
|
779
|
-
to: params.target ?? "",
|
|
780
|
-
text: params.text ?? "",
|
|
781
|
-
type: params.chatType,
|
|
782
|
-
attachments: [uploadResult.attachment]
|
|
783
|
-
});
|
|
784
|
-
return { channel: "mychat", messageId: result.messageId ?? "", ok: result.ok };
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
return { channel: "mychat", messageId: "", ok: false };
|
|
788
|
-
},
|
|
789
|
-
async sendPayload(params) {
|
|
790
|
-
const logger = getMychatLogger();
|
|
791
|
-
const runtime = getMychatRuntime();
|
|
792
|
-
const account = runtime.mychat;
|
|
793
|
-
if (!account) {
|
|
794
|
-
if (logger) logger.error(`${mychatLogPrefix("outbound")} no runtime account`);
|
|
795
|
-
return { channel: "mychat", messageId: "", ok: false };
|
|
796
|
-
}
|
|
797
|
-
const ctx = createMychatOutboundSendContext({
|
|
798
|
-
account: params.account ?? {},
|
|
799
|
-
httpClient: account.httpClient
|
|
800
|
-
});
|
|
801
|
-
const result = await sendMychatOutboundPayload(ctx, {
|
|
802
|
-
to: params.target ?? "",
|
|
803
|
-
text: params.text ?? "",
|
|
804
|
-
type: params.chatType,
|
|
805
|
-
replyTo: params.replyTo
|
|
806
|
-
});
|
|
807
|
-
return { channel: "mychat", messageId: result.messageId ?? "", ok: result.ok };
|
|
808
|
-
}
|
|
809
|
-
};
|
|
810
|
-
|
|
811
|
-
// src/threading/threading.ts
|
|
812
|
-
var mychatThreading = {
|
|
813
|
-
replyToMode: "always",
|
|
814
|
-
resolveReplyTo(params) {
|
|
815
|
-
return {
|
|
816
|
-
threadId: params.inbound?.threadId ?? params.inbound?.conversationId,
|
|
817
|
-
replyTo: params.inbound?.messageId
|
|
818
|
-
};
|
|
819
|
-
},
|
|
820
|
-
sanitizeThreadName(name) {
|
|
821
|
-
return name.slice(0, 100).replace(/[^\w\s-]/g, "");
|
|
822
|
-
}
|
|
823
|
-
};
|
|
824
|
-
|
|
825
|
-
// src/channel.ts
|
|
826
|
-
var mychatPlugin = createChatChannelPlugin({
|
|
827
|
-
base: createMychatPluginBase(),
|
|
828
|
-
security: {
|
|
829
|
-
dm: {
|
|
830
|
-
channelKey: "dmPolicy",
|
|
831
|
-
resolvePolicy(account) {
|
|
832
|
-
return account.dmPolicy ?? "open";
|
|
833
|
-
},
|
|
834
|
-
resolveAllowFrom(account) {
|
|
835
|
-
return account.allowFrom;
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
},
|
|
839
|
-
pairing: {
|
|
840
|
-
text: {
|
|
841
|
-
idLabel: "MyChat Username",
|
|
842
|
-
message: "To connect to MyChat, configure your bot token and server URL in the channel config.",
|
|
843
|
-
notify() {
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
},
|
|
847
|
-
threading: mychatThreading,
|
|
848
|
-
outbound: mychatOutbound
|
|
849
|
-
});
|
|
850
|
-
|
|
851
|
-
export {
|
|
852
|
-
mychatPlugin
|
|
853
|
-
};
|