@aka_openclaw_plugin/mychat 0.1.3 → 0.1.4

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.
@@ -1,7 +1,4 @@
1
- import { i as setMychatRuntime, n as init_runtime, o as __toCommonJS, r as runtime_exports, t as getMychatRuntime } from "./runtime-7z_VfQ27.js";
2
- import { buildChannelConfigSchema, createChannelPluginBase, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
3
- import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-lifecycle";
4
- import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/channel-plugin-common";
1
+ import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-core";
5
2
  //#region ../../node_modules/zod/v4/core/core.js
6
3
  var _a$1;
7
4
  function $constructor(name, initializer, params) {
@@ -2798,7 +2795,7 @@ function initializeContext(params) {
2798
2795
  external: params?.external ?? void 0
2799
2796
  };
2800
2797
  }
2801
- function process$1(schema, ctx, _params = {
2798
+ function process(schema, ctx, _params = {
2802
2799
  path: [],
2803
2800
  schemaPath: []
2804
2801
  }) {
@@ -2835,7 +2832,7 @@ function process$1(schema, ctx, _params = {
2835
2832
  const parent = schema._zod.parent;
2836
2833
  if (parent) {
2837
2834
  if (!result.ref) result.ref = parent;
2838
- process$1(parent, ctx, params);
2835
+ process(parent, ctx, params);
2839
2836
  ctx.seen.get(parent).isParent = true;
2840
2837
  }
2841
2838
  }
@@ -3055,7 +3052,7 @@ const createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
3055
3052
  ...params,
3056
3053
  processors
3057
3054
  });
3058
- process$1(schema, ctx);
3055
+ process(schema, ctx);
3059
3056
  extractDefs(ctx, schema);
3060
3057
  return finalize(ctx, schema);
3061
3058
  };
@@ -3067,7 +3064,7 @@ const createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params)
3067
3064
  io,
3068
3065
  processors
3069
3066
  });
3070
- process$1(schema, ctx);
3067
+ process(schema, ctx);
3071
3068
  extractDefs(ctx, schema);
3072
3069
  return finalize(ctx, schema);
3073
3070
  };
@@ -3147,7 +3144,7 @@ const arrayProcessor = (schema, ctx, _json, params) => {
3147
3144
  if (typeof minimum === "number") json.minItems = minimum;
3148
3145
  if (typeof maximum === "number") json.maxItems = maximum;
3149
3146
  json.type = "array";
3150
- json.items = process$1(def.element, ctx, {
3147
+ json.items = process(def.element, ctx, {
3151
3148
  ...params,
3152
3149
  path: [...params.path, "items"]
3153
3150
  });
@@ -3158,7 +3155,7 @@ const objectProcessor = (schema, ctx, _json, params) => {
3158
3155
  json.type = "object";
3159
3156
  json.properties = {};
3160
3157
  const shape = def.shape;
3161
- for (const key in shape) json.properties[key] = process$1(shape[key], ctx, {
3158
+ for (const key in shape) json.properties[key] = process(shape[key], ctx, {
3162
3159
  ...params,
3163
3160
  path: [
3164
3161
  ...params.path,
@@ -3176,7 +3173,7 @@ const objectProcessor = (schema, ctx, _json, params) => {
3176
3173
  if (def.catchall?._zod.def.type === "never") json.additionalProperties = false;
3177
3174
  else if (!def.catchall) {
3178
3175
  if (ctx.io === "output") json.additionalProperties = false;
3179
- } else if (def.catchall) json.additionalProperties = process$1(def.catchall, ctx, {
3176
+ } else if (def.catchall) json.additionalProperties = process(def.catchall, ctx, {
3180
3177
  ...params,
3181
3178
  path: [...params.path, "additionalProperties"]
3182
3179
  });
@@ -3184,7 +3181,7 @@ const objectProcessor = (schema, ctx, _json, params) => {
3184
3181
  const unionProcessor = (schema, ctx, json, params) => {
3185
3182
  const def = schema._zod.def;
3186
3183
  const isExclusive = def.inclusive === false;
3187
- const options = def.options.map((x, i) => process$1(x, ctx, {
3184
+ const options = def.options.map((x, i) => process(x, ctx, {
3188
3185
  ...params,
3189
3186
  path: [
3190
3187
  ...params.path,
@@ -3197,7 +3194,7 @@ const unionProcessor = (schema, ctx, json, params) => {
3197
3194
  };
3198
3195
  const intersectionProcessor = (schema, ctx, json, params) => {
3199
3196
  const def = schema._zod.def;
3200
- const a = process$1(def.left, ctx, {
3197
+ const a = process(def.left, ctx, {
3201
3198
  ...params,
3202
3199
  path: [
3203
3200
  ...params.path,
@@ -3205,7 +3202,7 @@ const intersectionProcessor = (schema, ctx, json, params) => {
3205
3202
  0
3206
3203
  ]
3207
3204
  });
3208
- const b = process$1(def.right, ctx, {
3205
+ const b = process(def.right, ctx, {
3209
3206
  ...params,
3210
3207
  path: [
3211
3208
  ...params.path,
@@ -3223,7 +3220,7 @@ const recordProcessor = (schema, ctx, _json, params) => {
3223
3220
  const keyType = def.keyType;
3224
3221
  const patterns = keyType._zod.bag?.patterns;
3225
3222
  if (def.mode === "loose" && patterns && patterns.size > 0) {
3226
- const valueSchema = process$1(def.valueType, ctx, {
3223
+ const valueSchema = process(def.valueType, ctx, {
3227
3224
  ...params,
3228
3225
  path: [
3229
3226
  ...params.path,
@@ -3234,11 +3231,11 @@ const recordProcessor = (schema, ctx, _json, params) => {
3234
3231
  json.patternProperties = {};
3235
3232
  for (const pattern of patterns) json.patternProperties[pattern.source] = valueSchema;
3236
3233
  } else {
3237
- if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") json.propertyNames = process$1(def.keyType, ctx, {
3234
+ if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") json.propertyNames = process(def.keyType, ctx, {
3238
3235
  ...params,
3239
3236
  path: [...params.path, "propertyNames"]
3240
3237
  });
3241
- json.additionalProperties = process$1(def.valueType, ctx, {
3238
+ json.additionalProperties = process(def.valueType, ctx, {
3242
3239
  ...params,
3243
3240
  path: [...params.path, "additionalProperties"]
3244
3241
  });
@@ -3251,7 +3248,7 @@ const recordProcessor = (schema, ctx, _json, params) => {
3251
3248
  };
3252
3249
  const nullableProcessor = (schema, ctx, json, params) => {
3253
3250
  const def = schema._zod.def;
3254
- const inner = process$1(def.innerType, ctx, params);
3251
+ const inner = process(def.innerType, ctx, params);
3255
3252
  const seen = ctx.seen.get(schema);
3256
3253
  if (ctx.target === "openapi-3.0") {
3257
3254
  seen.ref = def.innerType;
@@ -3260,27 +3257,27 @@ const nullableProcessor = (schema, ctx, json, params) => {
3260
3257
  };
3261
3258
  const nonoptionalProcessor = (schema, ctx, _json, params) => {
3262
3259
  const def = schema._zod.def;
3263
- process$1(def.innerType, ctx, params);
3260
+ process(def.innerType, ctx, params);
3264
3261
  const seen = ctx.seen.get(schema);
3265
3262
  seen.ref = def.innerType;
3266
3263
  };
3267
3264
  const defaultProcessor = (schema, ctx, json, params) => {
3268
3265
  const def = schema._zod.def;
3269
- process$1(def.innerType, ctx, params);
3266
+ process(def.innerType, ctx, params);
3270
3267
  const seen = ctx.seen.get(schema);
3271
3268
  seen.ref = def.innerType;
3272
3269
  json.default = JSON.parse(JSON.stringify(def.defaultValue));
3273
3270
  };
3274
3271
  const prefaultProcessor = (schema, ctx, json, params) => {
3275
3272
  const def = schema._zod.def;
3276
- process$1(def.innerType, ctx, params);
3273
+ process(def.innerType, ctx, params);
3277
3274
  const seen = ctx.seen.get(schema);
3278
3275
  seen.ref = def.innerType;
3279
3276
  if (ctx.io === "input") json._prefault = JSON.parse(JSON.stringify(def.defaultValue));
3280
3277
  };
3281
3278
  const catchProcessor = (schema, ctx, json, params) => {
3282
3279
  const def = schema._zod.def;
3283
- process$1(def.innerType, ctx, params);
3280
+ process(def.innerType, ctx, params);
3284
3281
  const seen = ctx.seen.get(schema);
3285
3282
  seen.ref = def.innerType;
3286
3283
  let catchValue;
@@ -3295,20 +3292,20 @@ const pipeProcessor = (schema, ctx, _json, params) => {
3295
3292
  const def = schema._zod.def;
3296
3293
  const inIsTransform = def.in._zod.traits.has("$ZodTransform");
3297
3294
  const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
3298
- process$1(innerType, ctx, params);
3295
+ process(innerType, ctx, params);
3299
3296
  const seen = ctx.seen.get(schema);
3300
3297
  seen.ref = innerType;
3301
3298
  };
3302
3299
  const readonlyProcessor = (schema, ctx, json, params) => {
3303
3300
  const def = schema._zod.def;
3304
- process$1(def.innerType, ctx, params);
3301
+ process(def.innerType, ctx, params);
3305
3302
  const seen = ctx.seen.get(schema);
3306
3303
  seen.ref = def.innerType;
3307
3304
  json.readOnly = true;
3308
3305
  };
3309
3306
  const optionalProcessor = (schema, ctx, _json, params) => {
3310
3307
  const def = schema._zod.def;
3311
- process$1(def.innerType, ctx, params);
3308
+ process(def.innerType, ctx, params);
3312
3309
  const seen = ctx.seen.get(schema);
3313
3310
  seen.ref = def.innerType;
3314
3311
  };
@@ -4234,944 +4231,4 @@ const mychatConfigSchema = buildChannelConfigSchema(object({
4234
4231
  name: { label: "Display name" }
4235
4232
  } });
4236
4233
  //#endregion
4237
- //#region src/accounts.ts
4238
- const DEFAULT_DM_POLICY = "open";
4239
- const DEFAULT_GROUP_POLICY = "open";
4240
- const DEFAULT_HISTORY_LIMIT = 50;
4241
- const DEFAULT_MEDIA_MAX_MB = 10;
4242
- function resolveWsUrl(baseUrl) {
4243
- const url = new URL(baseUrl);
4244
- url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
4245
- url.pathname = url.pathname.replace(/\/+$/, "") + "/ws/bot";
4246
- return url.toString();
4247
- }
4248
- function mergeAccountConfig(channel, account) {
4249
- if (!account) return channel;
4250
- return {
4251
- ...channel,
4252
- ...account,
4253
- actions: {
4254
- messages: account.actions?.messages ?? channel.actions?.messages ?? true,
4255
- threads: account.actions?.threads ?? channel.actions?.threads ?? true,
4256
- reactions: account.actions?.reactions ?? channel.actions?.reactions ?? true
4257
- },
4258
- groups: {
4259
- ...channel.groups ?? {},
4260
- ...account.groups ?? {}
4261
- },
4262
- reconnect: account.reconnect ?? channel.reconnect
4263
- };
4264
- }
4265
- function resolveMychatAccounts(cfg) {
4266
- const mychatConfig = cfg.channels?.mychat;
4267
- if (!mychatConfig) return [];
4268
- const accounts = mychatConfig.accounts ?? {};
4269
- const accountIds = Object.keys(accounts);
4270
- if (accountIds.length === 0) {
4271
- const resolved = resolveSingleAccount({
4272
- accountId: DEFAULT_ACCOUNT_ID,
4273
- config: mychatConfig
4274
- });
4275
- return resolved ? [resolved] : [];
4276
- }
4277
- return accountIds.map((id) => {
4278
- return resolveSingleAccount({
4279
- accountId: id,
4280
- config: mergeAccountConfig(mychatConfig, accounts[id])
4281
- });
4282
- }).filter(Boolean);
4283
- }
4284
- function resolveSingleAccount(params) {
4285
- const { accountId, config } = params;
4286
- const enabled = config.enabled ?? true;
4287
- if (!enabled) return null;
4288
- const token = resolveToken(config);
4289
- if (!token) return null;
4290
- const baseUrl = config.baseUrl.replace(/\/+$/, "");
4291
- const wsUrl = config.wsUrl ?? resolveWsUrl(baseUrl);
4292
- return {
4293
- accountId: normalizeAccountId(accountId),
4294
- enabled,
4295
- name: config.name,
4296
- baseUrl,
4297
- wsUrl,
4298
- token,
4299
- defaultTo: config.defaultTo,
4300
- dmPolicy: config.dmPolicy ?? DEFAULT_DM_POLICY,
4301
- groupPolicy: config.groupPolicy ?? DEFAULT_GROUP_POLICY,
4302
- allowFrom: config.allowFrom ?? [],
4303
- groupAllowFrom: config.groupAllowFrom ?? [],
4304
- requireMention: config.requireMention ?? false,
4305
- historyLimit: config.historyLimit ?? DEFAULT_HISTORY_LIMIT,
4306
- mediaMaxMb: config.mediaMaxMb ?? DEFAULT_MEDIA_MAX_MB,
4307
- actions: {
4308
- messages: config.actions?.messages ?? true,
4309
- threads: config.actions?.threads ?? true,
4310
- reactions: config.actions?.reactions ?? true
4311
- },
4312
- groups: config.groups ?? {},
4313
- reconnect: config.reconnect ?? {}
4314
- };
4315
- }
4316
- function resolveToken(config) {
4317
- if (config.token) return config.token;
4318
- if (typeof process !== "undefined") {
4319
- const envToken = process.env.MYCHAT_BOT_TOKEN;
4320
- if (envToken) return envToken;
4321
- }
4322
- return null;
4323
- }
4324
- function resolveMychatAccount(params) {
4325
- const accounts = resolveMychatAccounts(params.cfg);
4326
- if (accounts.length === 0) return null;
4327
- if (!params.accountId) return accounts[0];
4328
- return accounts.find((a) => a.accountId === params.accountId) ?? accounts[0] ?? null;
4329
- }
4330
- //#endregion
4331
- //#region src/client/http-client.ts
4332
- function createMychatHttpClient(params) {
4333
- const { baseUrl, token } = params;
4334
- const base = baseUrl.replace(/\/+$/, "");
4335
- async function request(method, path, body) {
4336
- try {
4337
- const headers = { authorization: `Bearer ${token}` };
4338
- if (body !== void 0) headers["content-type"] = "application/json";
4339
- const response = await fetch(`${base}${path}`, {
4340
- method,
4341
- headers,
4342
- body: body !== void 0 ? JSON.stringify(body) : void 0
4343
- });
4344
- if (!response.ok) {
4345
- console.error(`[mychat] HTTP ${method} ${path} failed: ${response.status} ${response.statusText}`);
4346
- return null;
4347
- }
4348
- return await response.json();
4349
- } catch (err) {
4350
- console.error(`[mychat] HTTP ${method} ${path} error:`, err);
4351
- return null;
4352
- }
4353
- }
4354
- return {
4355
- async getSelf() {
4356
- return request("GET", "/api/bot/self");
4357
- },
4358
- async getMe() {
4359
- return request("GET", "/api/auth/me");
4360
- },
4361
- async sendMessage(body) {
4362
- return request("POST", "/api/bot/messages", body);
4363
- },
4364
- async listMessages(params) {
4365
- const searchParams = new URLSearchParams();
4366
- if (params.conversationId) searchParams.set("conversationId", params.conversationId);
4367
- if (params.limit) searchParams.set("limit", String(params.limit));
4368
- if (params.after) searchParams.set("after", String(params.after));
4369
- const qs = searchParams.toString();
4370
- return (await request("GET", `/api/bot/messages${qs ? `?${qs}` : ""}`))?.messages ?? [];
4371
- },
4372
- async addReaction(messageId, body) {
4373
- return request("POST", `/api/bot/messages/${messageId}/reactions`, body);
4374
- },
4375
- async removeReaction(messageId, emoji) {
4376
- return request("POST", `/api/bot/messages/${messageId}/reactions`, {
4377
- emoji,
4378
- action: "remove"
4379
- });
4380
- },
4381
- async uploadFile(params) {
4382
- try {
4383
- const formData = new FormData();
4384
- let blob;
4385
- if (params.file instanceof Blob) blob = params.file;
4386
- else {
4387
- const bytes = new Uint8Array(params.file.buffer, params.file.byteOffset, params.file.byteLength);
4388
- blob = new Blob([bytes], { type: params.mimeType });
4389
- }
4390
- formData.append("file", blob, params.filename);
4391
- if (params.scopeKind) formData.append("scopeKind", params.scopeKind);
4392
- if (params.scopeId) formData.append("scopeId", params.scopeId);
4393
- const response = await fetch(`${base}/api/bot/uploads`, {
4394
- method: "POST",
4395
- headers: { authorization: `Bearer ${token}` },
4396
- body: formData
4397
- });
4398
- if (!response.ok) return null;
4399
- return await response.json();
4400
- } catch {
4401
- return null;
4402
- }
4403
- },
4404
- async healthCheck() {
4405
- const start = Date.now();
4406
- try {
4407
- const response = await fetch(`${base}/health`);
4408
- const latencyMs = Date.now() - start;
4409
- return {
4410
- ok: response.ok,
4411
- latencyMs
4412
- };
4413
- } catch {
4414
- return {
4415
- ok: false,
4416
- latencyMs: Date.now() - start
4417
- };
4418
- }
4419
- }
4420
- };
4421
- }
4422
- //#endregion
4423
- //#region src/client/ws-client.ts
4424
- init_runtime();
4425
- const DEFAULT_INITIAL_DELAY_MS = 1e3;
4426
- const DEFAULT_MAX_DELAY_MS = 3e4;
4427
- const DEFAULT_BACKOFF_MULTIPLIER = 2;
4428
- function createMychatWsClient(params) {
4429
- const { wsUrl, token, botSelfId, reconnect } = params;
4430
- const initialDelay = reconnect?.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
4431
- const maxDelay = reconnect?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
4432
- const backoffMultiplier = reconnect?.backoffMultiplier ?? DEFAULT_BACKOFF_MULTIPLIER;
4433
- let ws = null;
4434
- let connected = false;
4435
- let reconnectTimer = null;
4436
- let currentDelay = initialDelay;
4437
- const handlers = /* @__PURE__ */ new Set();
4438
- function buildUrl() {
4439
- const url = new URL(wsUrl);
4440
- if (!url.pathname.includes("/ws/bot")) url.pathname = url.pathname.replace(/\/+$/, "") + "/ws/bot";
4441
- return url.toString();
4442
- }
4443
- function scheduleReconnect() {
4444
- if (reconnectTimer) return;
4445
- reconnectTimer = setTimeout(() => {
4446
- reconnectTimer = null;
4447
- currentDelay = Math.min(currentDelay * backoffMultiplier, maxDelay);
4448
- connect();
4449
- }, currentDelay);
4450
- }
4451
- function connect() {
4452
- if (ws) return;
4453
- try {
4454
- const url = buildUrl();
4455
- const urlWithToken = new URL(url);
4456
- urlWithToken.searchParams.set("token", token);
4457
- console.log(`[mychat] WS connecting to ${urlWithToken.origin}${urlWithToken.pathname}`);
4458
- ws = new WebSocket(urlWithToken.toString());
4459
- ws.onopen = () => {
4460
- connected = true;
4461
- currentDelay = initialDelay;
4462
- if (botSelfId) ws?.send(JSON.stringify({
4463
- type: "subscribe",
4464
- body: { subscribe: { conversationId: botSelfId } }
4465
- }));
4466
- };
4467
- ws.onmessage = (event) => {
4468
- try {
4469
- const message = JSON.parse(event.data);
4470
- for (const handler of handlers) handler(message);
4471
- } catch {}
4472
- };
4473
- ws.onclose = () => {
4474
- connected = false;
4475
- ws = null;
4476
- scheduleReconnect();
4477
- };
4478
- ws.onerror = (event) => {
4479
- console.error(`[mychat] WS error:`, event);
4480
- ws?.close();
4481
- };
4482
- } catch {
4483
- scheduleReconnect();
4484
- }
4485
- }
4486
- function disconnect() {
4487
- if (reconnectTimer) {
4488
- clearTimeout(reconnectTimer);
4489
- reconnectTimer = null;
4490
- }
4491
- connected = false;
4492
- ws?.close();
4493
- ws = null;
4494
- }
4495
- return {
4496
- connect,
4497
- disconnect,
4498
- onMessage(handler) {
4499
- handlers.add(handler);
4500
- return () => {
4501
- handlers.delete(handler);
4502
- };
4503
- },
4504
- isConnected() {
4505
- return connected;
4506
- }
4507
- };
4508
- }
4509
- //#endregion
4510
- //#region src/logger.ts
4511
- const PREFIX = "mychat";
4512
- function mychatLogPrefix(feature) {
4513
- return `[${PREFIX}:${feature}]`;
4514
- }
4515
- function getMychatLogger() {
4516
- try {
4517
- const logger = ((init_runtime(), __toCommonJS(runtime_exports)).tryGetMychatRuntime?.())?.logger;
4518
- if (!logger) return null;
4519
- return {
4520
- debug: (m) => logger.debug?.(m),
4521
- info: (m) => logger.info(m),
4522
- warn: (m) => logger.warn(m),
4523
- error: (m) => logger.error(m)
4524
- };
4525
- } catch {
4526
- return null;
4527
- }
4528
- }
4529
- //#endregion
4530
- //#region src/monitor/listeners.ts
4531
- function createMychatMessageListener(params) {
4532
- const { handler } = params;
4533
- return { handle(message) {
4534
- handler.handleMessage(message).catch(() => {});
4535
- } };
4536
- }
4537
- function createMychatReactionListener(params) {
4538
- const logger = getMychatLogger();
4539
- const prefix = mychatLogPrefix("reaction");
4540
- return { handle(message) {
4541
- const emoji = message.body?.reaction;
4542
- const action = message.body?.action ?? "add";
4543
- const target = message.body?.target;
4544
- const senderId = message.from?.id;
4545
- if (logger) logger.debug(`${prefix} ${action} ${emoji} on ${target} by ${senderId}`);
4546
- } };
4547
- }
4548
- function createMychatTypingListener(params) {
4549
- const logger = getMychatLogger();
4550
- const prefix = mychatLogPrefix("typing");
4551
- return { handle(message) {
4552
- const senderId = message.from?.id;
4553
- if (senderId === params.account.accountId) return;
4554
- if (logger) logger.debug(`${prefix} from ${senderId}`);
4555
- } };
4556
- }
4557
- //#endregion
4558
- //#region src/monitor/provider.startup.ts
4559
- function createMychatProviderClients(account, botSelfId) {
4560
- return { wsClient: createMychatWsClient({
4561
- wsUrl: account.wsUrl,
4562
- token: account.token,
4563
- botSelfId,
4564
- reconnect: account.reconnect
4565
- }) };
4566
- }
4567
- function registerMychatMonitorListeners(params) {
4568
- const { account, handler, wsClient } = params;
4569
- const messageListener = createMychatMessageListener({
4570
- account,
4571
- handler
4572
- });
4573
- const reactionListener = createMychatReactionListener({ account });
4574
- const typingListener = createMychatTypingListener({ account });
4575
- wsClient.onMessage((message) => {
4576
- switch (message.type) {
4577
- case "message":
4578
- case "stream":
4579
- messageListener.handle(message);
4580
- break;
4581
- case "reaction":
4582
- reactionListener.handle(message);
4583
- break;
4584
- case "typing":
4585
- typingListener.handle(message);
4586
- break;
4587
- default: break;
4588
- }
4589
- });
4590
- }
4591
- //#endregion
4592
- //#region src/monitor/provider.lifecycle.ts
4593
- const DEFAULT_READY_TIMEOUT_MS = 15e3;
4594
- function createMychatProviderLifecycle(params) {
4595
- const { httpClient, wsClient, readyTimeoutMs = DEFAULT_READY_TIMEOUT_MS } = params;
4596
- const logger = getMychatLogger();
4597
- const prefix = mychatLogPrefix("lifecycle");
4598
- return {
4599
- async waitForReady() {
4600
- const start = Date.now();
4601
- wsClient.connect();
4602
- while (Date.now() - start < readyTimeoutMs) {
4603
- if (wsClient.isConnected()) {
4604
- const botSelf = await httpClient.getSelf();
4605
- if (botSelf) {
4606
- logger?.info(`${prefix} bot connected botId=${botSelf.botId}`);
4607
- return botSelf;
4608
- }
4609
- logger?.warn(`${prefix} ws connected but getSelf() failed`);
4610
- }
4611
- await new Promise((r) => setTimeout(r, 500));
4612
- }
4613
- const wsConnected = wsClient.isConnected();
4614
- logger?.warn(`${prefix} ready timeout wsConnected=${wsConnected} elapsedMs=${Date.now() - start}`);
4615
- return null;
4616
- },
4617
- shutdown() {
4618
- wsClient.disconnect();
4619
- logger?.info(`${prefix} shutdown`);
4620
- }
4621
- };
4622
- }
4623
- //#endregion
4624
- //#region src/monitor/inbound-dedupe.ts
4625
- const MAX_CACHE_SIZE = 500;
4626
- function createMychatInboundDedupe() {
4627
- const seen = /* @__PURE__ */ new Set();
4628
- return { isDuplicate(messageId) {
4629
- if (seen.has(messageId)) return true;
4630
- seen.add(messageId);
4631
- if (seen.size > MAX_CACHE_SIZE) {
4632
- const first = seen.values().next().value;
4633
- if (first !== void 0) seen.delete(first);
4634
- }
4635
- return false;
4636
- } };
4637
- }
4638
- //#endregion
4639
- //#region src/monitor/message-text.ts
4640
- /** Extract plain text from a WebSocket message body. */
4641
- function extractMychatMessageText(message) {
4642
- return message.body?.text ?? "";
4643
- }
4644
- /** Extract mention user IDs from a message. */
4645
- function extractMentionUserIds(message) {
4646
- return (message.body?.mentions ?? []).map((m) => m.userId).filter(Boolean);
4647
- }
4648
- /** Check if the bot is mentioned in a message. */
4649
- function isBotMentioned(message, botUserId) {
4650
- if (!botUserId) return false;
4651
- if (extractMentionUserIds(message).includes(botUserId)) return true;
4652
- return (message.body?.text ?? "").includes(`@${botUserId}`);
4653
- }
4654
- //#endregion
4655
- //#region src/monitor/message-handler.preflight.ts
4656
- /** Check if an inbound message should be accepted. */
4657
- function mychatPreflight(message, account) {
4658
- const senderId = message.from?.id;
4659
- if (!senderId) return {
4660
- allowed: false,
4661
- reason: "no-sender"
4662
- };
4663
- if (senderId === account.accountId) return {
4664
- allowed: false,
4665
- reason: "self"
4666
- };
4667
- const toKind = message.to?.kind ?? "direct";
4668
- if (toKind === "direct" || toKind === "channel") {
4669
- if (account.dmPolicy === "disabled") return {
4670
- allowed: false,
4671
- reason: "dm-disabled"
4672
- };
4673
- if (account.dmPolicy === "allowlist" && account.allowFrom.length > 0) {
4674
- if (!account.allowFrom.includes(senderId)) return {
4675
- allowed: false,
4676
- reason: "dm-not-allowed"
4677
- };
4678
- }
4679
- }
4680
- if (toKind === "group") {
4681
- if (account.groupPolicy === "disabled") return {
4682
- allowed: false,
4683
- reason: "group-disabled"
4684
- };
4685
- if (account.groupPolicy === "allowlist" && account.groupAllowFrom.length > 0) {
4686
- if (!account.groupAllowFrom.includes(senderId)) return {
4687
- allowed: false,
4688
- reason: "group-not-allowed"
4689
- };
4690
- }
4691
- const groupId = message.to?.id;
4692
- if (groupId && account.groups[groupId]) {
4693
- const groupConfig = account.groups[groupId];
4694
- if (groupConfig.enabled === false) return {
4695
- allowed: false,
4696
- reason: "group-disabled"
4697
- };
4698
- if (groupConfig.requireMention && !isBotMentioned(message, account.accountId)) return {
4699
- allowed: false,
4700
- reason: "mention-required"
4701
- };
4702
- }
4703
- if (account.requireMention && !isBotMentioned(message, account.accountId)) return {
4704
- allowed: false,
4705
- reason: "mention-required"
4706
- };
4707
- }
4708
- return { allowed: true };
4709
- }
4710
- //#endregion
4711
- //#region src/monitor/inbound-context.ts
4712
- function buildMychatInboundContext(message, account) {
4713
- if (message.type !== "message" && message.type !== "stream") return null;
4714
- const senderId = message.from?.id;
4715
- if (!senderId) return null;
4716
- const text = message.body?.text ?? "";
4717
- const toKind = message.to?.kind ?? "direct";
4718
- const chatType = toKind === "group" || toKind === "channel" ? "group" : "direct";
4719
- return {
4720
- messageId: message.id,
4721
- text,
4722
- senderId,
4723
- senderName: message.from?.name,
4724
- chatType,
4725
- conversationId: message.to?.id ?? message.body?.conversationId,
4726
- threadId: message.body?.parentId,
4727
- replyTo: message.body?.parentId,
4728
- mentions: message.body?.mentions ?? [],
4729
- attachments: message.body?.attachments ?? [],
4730
- timestamp: message.timestamp
4731
- };
4732
- }
4733
- //#endregion
4734
- //#region src/monitor/message-media.ts
4735
- /** Resolve media attachments from an inbound message. */
4736
- function resolveMychatMediaAttachments(message) {
4737
- return (message.body?.attachments ?? []).filter((a) => a.url && (a.mimeType.startsWith("image/") || a.mimeType.startsWith("video/") || a.mimeType.startsWith("audio/") || a.mimeType === "application/pdf"));
4738
- }
4739
- //#endregion
4740
- //#region src/monitor/message-handler.process.ts
4741
- /** Process an inbound message into a structured result. */
4742
- function processMychatInbound(message, account) {
4743
- const context = buildMychatInboundContext(message, account);
4744
- if (!context) return null;
4745
- return {
4746
- context,
4747
- text: extractMychatMessageText(message),
4748
- media: resolveMychatMediaAttachments(message)
4749
- };
4750
- }
4751
- //#endregion
4752
- //#region src/monitor/message-handler.ts
4753
- function createMychatMessageHandler(params) {
4754
- const { account, onInbound } = params;
4755
- const dedupe = createMychatInboundDedupe();
4756
- const logger = getMychatLogger();
4757
- const prefix = mychatLogPrefix("handler");
4758
- return { async handleMessage(message) {
4759
- if (dedupe.isDuplicate(message.id)) return;
4760
- const preflight = mychatPreflight(message, account);
4761
- if (!preflight.allowed) {
4762
- if (logger) logger.debug(`${prefix} message rejected reason=${preflight.reason} messageId=${message.id}`);
4763
- return;
4764
- }
4765
- const result = processMychatInbound(message, account);
4766
- if (!result) return;
4767
- if (logger) logger.debug(`${prefix} message accepted messageId=${result.context.messageId} chatType=${result.context.chatType}`);
4768
- await onInbound(result);
4769
- } };
4770
- }
4771
- //#endregion
4772
- //#region src/monitor/provider.ts
4773
- async function monitorMychatProvider(ctx) {
4774
- const { account, setStatus } = ctx;
4775
- const logger = getMychatLogger();
4776
- const prefix = mychatLogPrefix("provider");
4777
- if (logger) logger.info(`${prefix} starting accountId=${account.accountId}`);
4778
- setStatus({
4779
- connected: false,
4780
- mode: "websocket"
4781
- });
4782
- const httpClient = createMychatHttpClient({
4783
- baseUrl: account.baseUrl,
4784
- token: account.token
4785
- });
4786
- const botSelf = await httpClient.getSelf();
4787
- if (!botSelf) {
4788
- const error = "MyChat provider failed to get bot identity";
4789
- if (logger) logger.error(`${prefix} ${error}`);
4790
- setStatus({
4791
- connected: false,
4792
- lastError: error
4793
- });
4794
- throw new Error(error);
4795
- }
4796
- const health = await httpClient.healthCheck();
4797
- if (!health.ok) {
4798
- if (logger) logger.warn(`${prefix} health check failed latencyMs=${health.latencyMs}`);
4799
- }
4800
- const { wsClient } = createMychatProviderClients(account, botSelf.botId);
4801
- const rt = ctx.runtime;
4802
- rt.mychat = {
4803
- accountId: account.accountId,
4804
- httpClient,
4805
- wsClient,
4806
- botSelf
4807
- };
4808
- try {
4809
- setMychatRuntime(ctx.runtime);
4810
- } catch {}
4811
- registerMychatMonitorListeners({
4812
- account,
4813
- handler: createMychatMessageHandler({
4814
- account,
4815
- onInbound: async (result) => {
4816
- await dispatchMychatInbound(ctx, httpClient, result);
4817
- }
4818
- }),
4819
- wsClient
4820
- });
4821
- const lifecycle = createMychatProviderLifecycle({
4822
- httpClient,
4823
- wsClient
4824
- });
4825
- const readyBotSelf = await lifecycle.waitForReady();
4826
- if (!readyBotSelf) {
4827
- const error = "MyChat provider failed to connect";
4828
- if (logger) logger.error(`${prefix} ${error}`);
4829
- setStatus({
4830
- connected: false,
4831
- lastError: error
4832
- });
4833
- lifecycle.shutdown();
4834
- throw new Error(error);
4835
- }
4836
- setStatus({
4837
- connected: true,
4838
- lastConnectedAt: Date.now(),
4839
- lastEventAt: Date.now(),
4840
- mode: "websocket",
4841
- lastError: null
4842
- });
4843
- if (logger) logger.info(`${prefix} bot connected botId=${readyBotSelf.botId}`);
4844
- ctx.abortSignal?.addEventListener("abort", () => {
4845
- if (logger) logger.info(`${prefix} abort signal received, shutting down`);
4846
- lifecycle.shutdown();
4847
- setStatus({ connected: false });
4848
- });
4849
- return { unsubscribe() {
4850
- lifecycle.shutdown();
4851
- setStatus({ connected: false });
4852
- } };
4853
- }
4854
- /** Dispatch an inbound message through the OpenClaw turn runtime. */
4855
- async function dispatchMychatInbound(ctx, httpClient, result) {
4856
- const { runtime, cfg, account } = ctx;
4857
- const logger = getMychatLogger();
4858
- const prefix = mychatLogPrefix("dispatch");
4859
- const rt = runtime;
4860
- if (!rt.channel?.turn?.run) {
4861
- if (logger) logger.warn(`${prefix} channel turn runtime not available, skipping inbound`);
4862
- return;
4863
- }
4864
- const conversationId = result.context.conversationId ?? result.context.senderId;
4865
- const senderId = result.context.senderId;
4866
- const chatType = result.context.chatType;
4867
- const messageId = result.context.messageId;
4868
- await rt.channel.turn.run({
4869
- channel: "mychat",
4870
- accountId: account.accountId,
4871
- raw: result,
4872
- adapter: {
4873
- ingest: () => ({
4874
- id: messageId,
4875
- timestamp: result.context.timestamp,
4876
- rawText: result.text,
4877
- textForAgent: result.text,
4878
- textForCommands: result.text,
4879
- raw: result
4880
- }),
4881
- resolveTurn: (input) => {
4882
- const msgCtx = rt.channel.turn.buildContext({
4883
- channel: "mychat",
4884
- accountId: account.accountId,
4885
- timestamp: input.timestamp,
4886
- from: `mychat:user:${senderId}`,
4887
- sender: {
4888
- id: senderId,
4889
- name: result.context.senderName
4890
- },
4891
- conversation: {
4892
- kind: chatType,
4893
- id: conversationId,
4894
- label: result.context.senderName ?? senderId,
4895
- routePeer: {
4896
- kind: chatType,
4897
- id: conversationId
4898
- }
4899
- },
4900
- route: {
4901
- agentId: void 0,
4902
- accountId: account.accountId
4903
- },
4904
- reply: {
4905
- to: `mychat:${conversationId}`,
4906
- originatingTo: `mychat:${conversationId}`
4907
- },
4908
- message: {
4909
- rawBody: input.rawText,
4910
- commandBody: input.textForCommands,
4911
- bodyForAgent: input.textForAgent,
4912
- envelopeFrom: result.context.senderName
4913
- }
4914
- });
4915
- return {
4916
- cfg,
4917
- channel: "mychat",
4918
- accountId: account.accountId,
4919
- ctxPayload: msgCtx,
4920
- dispatchReplyWithBufferedBlockDispatcher: rt.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
4921
- delivery: { deliver: async (payload) => {
4922
- const text = payload.text?.trim();
4923
- if (!text) return;
4924
- const sendResult = await httpClient.sendMessage({
4925
- to: conversationId,
4926
- text,
4927
- type: chatType === "group" ? "group" : "direct"
4928
- });
4929
- if (logger) logger.debug(`${prefix} reply sent to=${conversationId} ok=${Boolean(sendResult)}`);
4930
- } }
4931
- };
4932
- }
4933
- }
4934
- });
4935
- }
4936
- //#endregion
4937
- //#region src/shared.ts
4938
- function createMychatPluginBase() {
4939
- return {
4940
- ...createChannelPluginBase({
4941
- id: "mychat",
4942
- setup: { applyAccountConfig(params) {
4943
- return params.cfg;
4944
- } },
4945
- meta: {
4946
- label: "MyChat",
4947
- docsPath: "docs/plugins/mychat",
4948
- blurb: "Connect to MyChat server via bot token",
4949
- aliases: ["mychat"]
4950
- },
4951
- capabilities: {
4952
- chatTypes: [
4953
- "direct",
4954
- "group",
4955
- "thread"
4956
- ],
4957
- reactions: true,
4958
- media: true,
4959
- threads: true
4960
- },
4961
- reload: { configPrefixes: ["channels.mychat"] },
4962
- configSchema: mychatConfigSchema,
4963
- config: {
4964
- listAccountIds(cfg) {
4965
- return resolveMychatAccounts(cfg).map((a) => a.accountId);
4966
- },
4967
- resolveAccount(cfg, accountId) {
4968
- return resolveMychatAccount({
4969
- cfg,
4970
- accountId
4971
- });
4972
- },
4973
- async inspectAccount(account) {
4974
- return {
4975
- accountId: account.accountId,
4976
- enabled: account.enabled,
4977
- label: account.name ?? account.baseUrl
4978
- };
4979
- }
4980
- }
4981
- }),
4982
- gateway: { async startAccount(ctx) {
4983
- const setStatus = createAccountStatusSink({
4984
- accountId: ctx.accountId,
4985
- setStatus: ctx.setStatus
4986
- });
4987
- return { unsubscribe: (await monitorMychatProvider({
4988
- cfg: ctx.cfg,
4989
- runtime: ctx.runtime,
4990
- account: ctx.account,
4991
- setStatus,
4992
- abortSignal: ctx.abortSignal,
4993
- log: ctx.log
4994
- })).unsubscribe };
4995
- } }
4996
- };
4997
- }
4998
- //#endregion
4999
- //#region src/outbound/outbound-payload.ts
5000
- async function sendMychatOutboundPayload(ctx, body) {
5001
- const logger = getMychatLogger();
5002
- const prefix = mychatLogPrefix("outbound");
5003
- try {
5004
- const result = await ctx.httpClient.sendMessage(body);
5005
- if (result) {
5006
- if (logger) logger.debug(`${prefix} message sent id=${result.id} status=${result.status}`);
5007
- return {
5008
- messageId: result.id,
5009
- ok: true
5010
- };
5011
- }
5012
- return { ok: false };
5013
- } catch (error) {
5014
- if (logger) logger.error(`${prefix} send failed error=${error instanceof Error ? error.message : String(error)}`);
5015
- return { ok: false };
5016
- }
5017
- }
5018
- //#endregion
5019
- //#region src/outbound/outbound-send-context.ts
5020
- function createMychatOutboundSendContext(params) {
5021
- return {
5022
- account: params.account,
5023
- httpClient: params.httpClient
5024
- };
5025
- }
5026
- //#endregion
5027
- //#region src/outbound/outbound-adapter.ts
5028
- init_runtime();
5029
- const TEXT_CHUNK_LIMIT = 4e3;
5030
- function chunkText(text, limit) {
5031
- if (text.length <= limit) return [text];
5032
- const chunks = [];
5033
- let remaining = text;
5034
- while (remaining.length > 0) {
5035
- chunks.push(remaining.slice(0, limit));
5036
- remaining = remaining.slice(limit);
5037
- }
5038
- return chunks;
5039
- }
5040
- const mychatOutbound = {
5041
- deliveryMode: "direct",
5042
- textChunkLimit: TEXT_CHUNK_LIMIT,
5043
- async sendText(params) {
5044
- const logger = getMychatLogger();
5045
- const account = getMychatRuntime().mychat;
5046
- if (!account) {
5047
- if (logger) logger.error(`${mychatLogPrefix("outbound")} no runtime account`);
5048
- return {
5049
- channel: "mychat",
5050
- messageId: "",
5051
- ok: false
5052
- };
5053
- }
5054
- const ctx = createMychatOutboundSendContext({
5055
- account: params.account ?? {},
5056
- httpClient: account.httpClient
5057
- });
5058
- const chunks = chunkText(params.text, TEXT_CHUNK_LIMIT);
5059
- let lastOk = false;
5060
- let lastMessageId = "";
5061
- for (const chunk of chunks) {
5062
- const result = await sendMychatOutboundPayload(ctx, {
5063
- to: params.target ?? "",
5064
- text: chunk,
5065
- type: params.chatType,
5066
- replyTo: params.replyTo
5067
- });
5068
- lastOk = result.ok;
5069
- if (result.messageId) lastMessageId = result.messageId;
5070
- }
5071
- return {
5072
- channel: "mychat",
5073
- messageId: lastMessageId,
5074
- ok: lastOk
5075
- };
5076
- },
5077
- async sendMedia(params) {
5078
- const logger = getMychatLogger();
5079
- const account = getMychatRuntime().mychat;
5080
- if (!account) {
5081
- if (logger) logger.error(`${mychatLogPrefix("outbound")} no runtime account`);
5082
- return {
5083
- channel: "mychat",
5084
- messageId: "",
5085
- ok: false
5086
- };
5087
- }
5088
- if (params.media) {
5089
- const uploadResult = await account.httpClient.uploadFile({
5090
- file: params.media,
5091
- filename: params.filename ?? "upload",
5092
- mimeType: params.mimeType ?? "application/octet-stream"
5093
- });
5094
- if (uploadResult) {
5095
- const result = await sendMychatOutboundPayload(createMychatOutboundSendContext({
5096
- account: params.account ?? {},
5097
- httpClient: account.httpClient
5098
- }), {
5099
- to: params.target ?? "",
5100
- text: params.text ?? "",
5101
- type: params.chatType,
5102
- attachments: [uploadResult.attachment]
5103
- });
5104
- return {
5105
- channel: "mychat",
5106
- messageId: result.messageId ?? "",
5107
- ok: result.ok
5108
- };
5109
- }
5110
- }
5111
- return {
5112
- channel: "mychat",
5113
- messageId: "",
5114
- ok: false
5115
- };
5116
- },
5117
- async sendPayload(params) {
5118
- const logger = getMychatLogger();
5119
- const account = getMychatRuntime().mychat;
5120
- if (!account) {
5121
- if (logger) logger.error(`${mychatLogPrefix("outbound")} no runtime account`);
5122
- return {
5123
- channel: "mychat",
5124
- messageId: "",
5125
- ok: false
5126
- };
5127
- }
5128
- const result = await sendMychatOutboundPayload(createMychatOutboundSendContext({
5129
- account: params.account ?? {},
5130
- httpClient: account.httpClient
5131
- }), {
5132
- to: params.target ?? "",
5133
- text: params.text ?? "",
5134
- type: params.chatType,
5135
- replyTo: params.replyTo
5136
- });
5137
- return {
5138
- channel: "mychat",
5139
- messageId: result.messageId ?? "",
5140
- ok: result.ok
5141
- };
5142
- }
5143
- };
5144
- //#endregion
5145
- //#region src/channel.ts
5146
- const mychatPlugin = createChatChannelPlugin({
5147
- base: createMychatPluginBase(),
5148
- security: { dm: {
5149
- channelKey: "dmPolicy",
5150
- resolvePolicy(account) {
5151
- return account.dmPolicy ?? "open";
5152
- },
5153
- resolveAllowFrom(account) {
5154
- return account.allowFrom;
5155
- }
5156
- } },
5157
- pairing: { text: {
5158
- idLabel: "MyChat Username",
5159
- message: "To connect to MyChat, configure your bot token and server URL in the channel config.",
5160
- notify() {}
5161
- } },
5162
- threading: {
5163
- replyToMode: "always",
5164
- resolveReplyTo(params) {
5165
- return {
5166
- threadId: params.inbound?.threadId ?? params.inbound?.conversationId,
5167
- replyTo: params.inbound?.messageId
5168
- };
5169
- },
5170
- sanitizeThreadName(name) {
5171
- return name.slice(0, 100).replace(/[^\w\s-]/g, "");
5172
- }
5173
- },
5174
- outbound: mychatOutbound
5175
- });
5176
- //#endregion
5177
- export { mychatPlugin as t };
4234
+ export { mychatConfigSchema as t };