koishi-plugin-adapter-onebot-multi 0.0.16 → 0.0.18

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/lib/index.js CHANGED
@@ -894,6 +894,13 @@ var WsClient = class extends import_koishi5.Adapter.WsClient {
894
894
  static {
895
895
  __name(this, "WsClient");
896
896
  }
897
+ constructor(ctx, bot) {
898
+ super(ctx, bot);
899
+ ctx.on("http/websocket-init", (url, options) => {
900
+ ;
901
+ options.maxPayload = 500 * 1024 * 1024;
902
+ });
903
+ }
897
904
  accept(socket) {
898
905
  accept(socket, this.bot);
899
906
  }
@@ -975,9 +982,18 @@ function accept(socket, bot) {
975
982
  delete listeners[parsed.echo];
976
983
  }
977
984
  });
978
- socket.addEventListener("close", () => {
985
+ socket.addEventListener("error", (event) => {
986
+ bot.logger.warn(`WebSocket 错误: ${event.message || "未知错误"}`);
987
+ });
988
+ socket.addEventListener("close", (event) => {
979
989
  delete bot.internal._request;
980
- bot.offline();
990
+ const closeInfo = `code: ${event.code}, reason: ${event.reason || "无"}`;
991
+ if (event.code === 1009) {
992
+ bot.logger.error(`WebSocket 因消息过大断开 (${closeInfo}),协议端可能发送了超大消息`);
993
+ } else if (event.code !== 1e3 && event.code !== 1001) {
994
+ bot.logger.warn(`WebSocket 连接关闭 (${closeInfo})`);
995
+ }
996
+ ;
981
997
  bot.ctx.emit("onebot-multi/bot-offline", bot);
982
998
  });
983
999
  bot.internal._request = (action, params) => {
@@ -1051,7 +1067,7 @@ var HeartbeatMonitor = class {
1051
1067
  }
1052
1068
  } else {
1053
1069
  if (this.bot.status === import_koishi6.Universal.Status.ONLINE) {
1054
- this.bot.offline();
1070
+ this.logger.warn(`Bot ${this.bot.selfId} 协议端报告离线,等待恢复...`);
1055
1071
  this.ctx.emit("onebot-multi/bot-offline", this.bot);
1056
1072
  }
1057
1073
  }
@@ -1064,10 +1080,7 @@ var HeartbeatMonitor = class {
1064
1080
  }
1065
1081
  if (this.lastOnlineState !== false) {
1066
1082
  this.lastOnlineState = false;
1067
- if (this.bot.status === import_koishi6.Universal.Status.ONLINE) {
1068
- this.bot.offline(error);
1069
- this.ctx.emit("onebot-multi/bot-offline", this.bot);
1070
- }
1083
+ this.ctx.emit("onebot-multi/bot-offline", this.bot);
1071
1084
  }
1072
1085
  }
1073
1086
  }
@@ -3353,6 +3366,54 @@ var LoadBalancer = class {
3353
3366
  botStats
3354
3367
  };
3355
3368
  }
3369
+ /**
3370
+ * 获取详细的负载均衡状态(供指令使用)
3371
+ */
3372
+ getDetailedStatus() {
3373
+ const onlineBots = this.getOnlineBots();
3374
+ const unassignedValue = this.config.unassignedValue || "";
3375
+ let assignedChannels = 0;
3376
+ let unassignedChannels = 0;
3377
+ for (const [, assignee] of this.lastAssignees.entries()) {
3378
+ if (assignee && assignee !== unassignedValue) {
3379
+ assignedChannels++;
3380
+ } else {
3381
+ unassignedChannels++;
3382
+ }
3383
+ }
3384
+ const bots = [];
3385
+ for (const botId of onlineBots) {
3386
+ const channels = this.botChannels.get(botId) || /* @__PURE__ */ new Set();
3387
+ let assignedCount = 0;
3388
+ for (const [, assignee] of this.lastAssignees.entries()) {
3389
+ if (assignee === botId) {
3390
+ assignedCount++;
3391
+ }
3392
+ }
3393
+ const maxLoad = this.getBotMaxLoad(botId);
3394
+ const remaining = maxLoad === 0 ? -1 : Math.max(0, maxLoad - assignedCount);
3395
+ const botInstance = this.ctx.bots.find((b) => b.selfId === botId && b.platform === "onebot");
3396
+ const nickname = botInstance?.user?.name || botId;
3397
+ const avatarUrl = `http://q.qlogo.cn/headimg_dl?dst_uin=${botId}&spec=640`;
3398
+ bots.push({
3399
+ selfId: botId,
3400
+ nickname,
3401
+ avatarUrl,
3402
+ channelCount: channels.size,
3403
+ assignedCount,
3404
+ maxLoad,
3405
+ remaining
3406
+ });
3407
+ }
3408
+ bots.sort((a, b) => b.assignedCount - a.assignedCount);
3409
+ return {
3410
+ onlineBots: onlineBots.length,
3411
+ totalChannels: this.allChannels.size,
3412
+ assignedChannels,
3413
+ unassignedChannels,
3414
+ bots
3415
+ };
3416
+ }
3356
3417
  };
3357
3418
 
3358
3419
  // src/index.ts
@@ -3402,10 +3463,51 @@ function apply(ctx, config) {
3402
3463
  if (config.panel?.enabled) {
3403
3464
  new StatusPanel(ctx, config.panel, statusManager, configManager);
3404
3465
  }
3466
+ let loadBalancer = null;
3405
3467
  if (config.loadBalance?.enabled) {
3406
- new LoadBalancer(ctx, config.loadBalance, statusManager);
3468
+ loadBalancer = new LoadBalancer(ctx, config.loadBalance, statusManager);
3407
3469
  logger.info("负载均衡已启用");
3408
3470
  }
3471
+ ctx.command("onebot.balance", "查看 OneBot 负载均衡状态").alias("ob.balance").alias("负载均衡").action(async () => {
3472
+ if (!loadBalancer || !config.loadBalance?.enabled) {
3473
+ return "负载均衡未启用。";
3474
+ }
3475
+ const status = loadBalancer.getDetailedStatus();
3476
+ const elements = [];
3477
+ elements.push(import_koishi12.h.text("═══ OneBot 负载均衡状态 ═══\n\n"));
3478
+ elements.push(import_koishi12.h.text(`📊 总览
3479
+ `));
3480
+ elements.push(import_koishi12.h.text(` 在线 Bot: ${status.onlineBots} 个
3481
+ `));
3482
+ elements.push(import_koishi12.h.text(` 管理群数: ${status.totalChannels} 个
3483
+ `));
3484
+ elements.push(import_koishi12.h.text(` 已分配群: ${status.assignedChannels} 个
3485
+ `));
3486
+ elements.push(import_koishi12.h.text(` 未分配群: ${status.unassignedChannels} 个
3487
+
3488
+ `));
3489
+ elements.push(import_koishi12.h.text(`🤖 Bot 负载详情
3490
+ `));
3491
+ for (const bot of status.bots) {
3492
+ const maxLoadStr = bot.maxLoad === 0 ? "无限制" : `${bot.maxLoad}`;
3493
+ const remainingStr = bot.maxLoad === 0 ? "∞" : `${bot.remaining}`;
3494
+ const barLength = 20;
3495
+ const usedLength = bot.maxLoad === 0 ? Math.min(Math.round(bot.assignedCount / Math.max(bot.channelCount, 1) * barLength), barLength) : Math.round(bot.assignedCount / bot.maxLoad * barLength);
3496
+ const bar = "█".repeat(usedLength) + "░".repeat(barLength - usedLength);
3497
+ elements.push(import_koishi12.h.image(bot.avatarUrl));
3498
+ elements.push(import_koishi12.h.text("\n"));
3499
+ elements.push(import_koishi12.h.text(` ${bot.nickname} (${bot.selfId})
3500
+ `));
3501
+ elements.push(import_koishi12.h.text(` 所在群: ${bot.channelCount} | 负责群: ${bot.assignedCount} | 上限: ${maxLoadStr}
3502
+ `));
3503
+ elements.push(import_koishi12.h.text(` 剩余容量: ${remainingStr}
3504
+ `));
3505
+ elements.push(import_koishi12.h.text(` [${bar}]
3506
+
3507
+ `));
3508
+ }
3509
+ return elements;
3510
+ });
3409
3511
  ctx.inject(["console"], (ctx2) => {
3410
3512
  ctx2.console.addEntry({
3411
3513
  dev: (0, import_path.resolve)(__dirname, "../client/index.ts"),
@@ -85,4 +85,22 @@ export declare class LoadBalancer {
85
85
  assignedCount: number;
86
86
  }>;
87
87
  };
88
+ /**
89
+ * 获取详细的负载均衡状态(供指令使用)
90
+ */
91
+ getDetailedStatus(): {
92
+ onlineBots: number;
93
+ totalChannels: number;
94
+ assignedChannels: number;
95
+ unassignedChannels: number;
96
+ bots: {
97
+ selfId: string;
98
+ nickname: string;
99
+ avatarUrl: string;
100
+ channelCount: number;
101
+ assignedCount: number;
102
+ maxLoad: number;
103
+ remaining: number;
104
+ }[];
105
+ };
88
106
  }
package/lib/ws.d.ts CHANGED
@@ -6,6 +6,7 @@ export interface SharedConfig<T = 'ws' | 'ws-reverse'> {
6
6
  responseTimeout?: number;
7
7
  }
8
8
  export declare class WsClient<C extends Context = Context> extends Adapter.WsClient<C, OneBotBot<C, any>> {
9
+ constructor(ctx: C, bot: OneBotBot<C>);
9
10
  accept(socket: Universal.WebSocket): void;
10
11
  prepare(): any;
11
12
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-adapter-onebot-multi",
3
3
  "description": "奶龙bot定制版onebot适配器,支持自动负载均衡,适配器级黑名单/白名单,提供webui,可指定端口",
4
- "version": "0.0.16",
4
+ "version": "0.0.18",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [