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 +110 -8
- package/lib/load-balancer.d.ts +18 -0
- package/lib/ws.d.ts +1 -0
- package/package.json +1 -1
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("
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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"),
|
package/lib/load-balancer.d.ts
CHANGED
|
@@ -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