koishi-plugin-adapter-onebot-multi 1.0.6 → 2.0.1
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/client/components/BotEditorModal.vue +260 -0
- package/client/components/BotEditorModal.vue.bak +54 -0
- package/client/page.vue +605 -1071
- package/client/page.vue.bak +1083 -0
- package/dist/index.js +7 -7
- package/dist/style.css +1 -1
- package/lib/activation-store.d.ts +1 -0
- package/lib/index.js +186 -72
- package/lib/load-balancer.d.ts +1 -0
- package/lib/ws.d.ts +1 -2
- package/package.json +1 -1
- package/lib/admin.d.ts +0 -52
- package/lib/config-manager.d.ts +0 -83
- package/lib/csv-sync.d.ts +0 -19
- package/lib/panel.d.ts +0 -25
package/lib/index.js
CHANGED
|
@@ -920,65 +920,103 @@ var WsClient = class extends import_koishi5.Adapter.WsClient {
|
|
|
920
920
|
WsClient2.Options = import_koishi5.Schema.intersect([
|
|
921
921
|
import_koishi5.Schema.object({
|
|
922
922
|
protocol: import_koishi5.Schema.const("ws").required(process.env.KOISHI_ENV !== "browser"),
|
|
923
|
-
endpoint: import_koishi5.Schema.string().description("OneBot
|
|
924
|
-
responseTimeout: import_koishi5.Schema.natural().role("time").default(import_koishi5.Time.minute).description("
|
|
925
|
-
}).description("
|
|
923
|
+
endpoint: import_koishi5.Schema.string().description("OneBot ??? WebSocket ??????").required(),
|
|
924
|
+
responseTimeout: import_koishi5.Schema.natural().role("time").default(import_koishi5.Time.minute).description("??????? (?????)?")
|
|
925
|
+
}).description("????"),
|
|
926
926
|
import_koishi5.HTTP.createConfig(true),
|
|
927
927
|
import_koishi5.Adapter.WsClientConfig
|
|
928
928
|
]);
|
|
929
929
|
})(WsClient || (WsClient = {}));
|
|
930
930
|
var kSocket = Symbol("socket");
|
|
931
|
-
var
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
logger;
|
|
937
|
-
wsServer;
|
|
938
|
-
constructor(ctx, bot) {
|
|
939
|
-
super(ctx);
|
|
931
|
+
var reverseHubs = /* @__PURE__ */ new WeakMap();
|
|
932
|
+
var ReverseHub = class {
|
|
933
|
+
constructor(ctx, path2) {
|
|
934
|
+
this.ctx = ctx;
|
|
935
|
+
this.path = path2;
|
|
940
936
|
this.logger = ctx.logger("onebot");
|
|
941
|
-
const { path: path2 = "/onebot" } = bot.config;
|
|
942
937
|
this.wsServer = ctx.server.ws(path2, (socket, { headers }) => {
|
|
943
938
|
this.logger.debug("connected with", headers);
|
|
944
939
|
if (headers["x-client-role"] !== "Universal") {
|
|
945
940
|
return socket.close(1008, "invalid x-client-role");
|
|
946
941
|
}
|
|
947
942
|
const selfId = headers["x-self-id"]?.toString();
|
|
948
|
-
let
|
|
949
|
-
if (!
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
return managedKey.startsWith("pending_") || String(item.selfId || "").startsWith("pending_");
|
|
953
|
-
});
|
|
954
|
-
if (pending) {
|
|
955
|
-
this.logger.info(`Bot ${pending.selfId} 自动绑定真实 selfId: ${selfId}`);
|
|
956
|
-
pending.selfId = selfId;
|
|
957
|
-
pending.config.selfId = selfId;
|
|
958
|
-
bot2 = pending;
|
|
959
|
-
}
|
|
943
|
+
let bot = selfId ? this.findBot(selfId) : void 0;
|
|
944
|
+
if (!bot && selfId) {
|
|
945
|
+
const ensureReverseBot = this.ctx._onebotMultiEnsureReverseBot;
|
|
946
|
+
bot = ensureReverseBot?.(selfId, this.path);
|
|
960
947
|
}
|
|
961
|
-
if (!
|
|
962
|
-
this.logger.info(`Bot ${selfId}
|
|
963
|
-
|
|
964
|
-
accept(socket,
|
|
948
|
+
if (!bot) return socket.close(1008, "invalid x-self-id");
|
|
949
|
+
this.logger.info(`Bot ${selfId} ??? (ws-reverse)`);
|
|
950
|
+
bot[kSocket] = socket;
|
|
951
|
+
accept(socket, bot);
|
|
965
952
|
});
|
|
966
953
|
ctx.on("dispose", () => {
|
|
967
954
|
this.logger.debug("ws server closing");
|
|
968
|
-
this.wsServer
|
|
955
|
+
this.wsServer?.close();
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
static {
|
|
959
|
+
__name(this, "ReverseHub");
|
|
960
|
+
}
|
|
961
|
+
logger;
|
|
962
|
+
wsServer;
|
|
963
|
+
findBot(selfId) {
|
|
964
|
+
for (const bot of this.ctx.bots) {
|
|
965
|
+
if (bot.platform !== "onebot") continue;
|
|
966
|
+
if (String(bot.selfId) !== selfId) continue;
|
|
967
|
+
return bot;
|
|
968
|
+
}
|
|
969
|
+
return void 0;
|
|
970
|
+
}
|
|
971
|
+
register(_bot) {
|
|
972
|
+
}
|
|
973
|
+
unregister(_bot) {
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
function getReverseHub(ctx, path2) {
|
|
977
|
+
let byPath = reverseHubs.get(ctx);
|
|
978
|
+
if (!byPath) {
|
|
979
|
+
byPath = /* @__PURE__ */ new Map();
|
|
980
|
+
reverseHubs.set(ctx, byPath);
|
|
981
|
+
}
|
|
982
|
+
let hub = byPath.get(path2);
|
|
983
|
+
if (!hub) {
|
|
984
|
+
hub = new ReverseHub(ctx, path2);
|
|
985
|
+
byPath.set(path2, hub);
|
|
986
|
+
}
|
|
987
|
+
return hub;
|
|
988
|
+
}
|
|
989
|
+
__name(getReverseHub, "getReverseHub");
|
|
990
|
+
var WsServer = class extends import_koishi5.Adapter {
|
|
991
|
+
static {
|
|
992
|
+
__name(this, "WsServer");
|
|
993
|
+
}
|
|
994
|
+
static inject = ["server"];
|
|
995
|
+
logger;
|
|
996
|
+
hub;
|
|
997
|
+
constructor(ctx, bot) {
|
|
998
|
+
super(ctx);
|
|
999
|
+
this.logger = ctx.logger("onebot");
|
|
1000
|
+
const { path: path2 = "/onebot" } = bot.config;
|
|
1001
|
+
this.hub = getReverseHub(ctx, path2);
|
|
1002
|
+
this.hub.register(bot);
|
|
1003
|
+
ctx.on("dispose", () => {
|
|
1004
|
+
this.hub.unregister(bot);
|
|
969
1005
|
});
|
|
970
1006
|
}
|
|
971
1007
|
async disconnect(bot) {
|
|
1008
|
+
;
|
|
972
1009
|
bot[kSocket]?.close();
|
|
973
1010
|
bot[kSocket] = null;
|
|
1011
|
+
this.hub.unregister(bot);
|
|
974
1012
|
}
|
|
975
1013
|
};
|
|
976
1014
|
((WsServer2) => {
|
|
977
1015
|
WsServer2.Options = import_koishi5.Schema.object({
|
|
978
1016
|
protocol: import_koishi5.Schema.const("ws-reverse").required(process.env.KOISHI_ENV === "browser"),
|
|
979
|
-
path: import_koishi5.Schema.string().description("
|
|
980
|
-
responseTimeout: import_koishi5.Schema.natural().role("time").default(import_koishi5.Time.minute).description("
|
|
981
|
-
}).description("
|
|
1017
|
+
path: import_koishi5.Schema.string().description("?????????").default("/onebot"),
|
|
1018
|
+
responseTimeout: import_koishi5.Schema.natural().role("time").default(import_koishi5.Time.minute).description("??????? (?????)?")
|
|
1019
|
+
}).description("????");
|
|
982
1020
|
})(WsServer || (WsServer = {}));
|
|
983
1021
|
var counter = 0;
|
|
984
1022
|
var listeners = {};
|
|
@@ -1000,15 +1038,15 @@ function accept(socket, bot) {
|
|
|
1000
1038
|
}
|
|
1001
1039
|
});
|
|
1002
1040
|
socket.addEventListener("error", (event) => {
|
|
1003
|
-
bot.logger.warn(`WebSocket
|
|
1041
|
+
bot.logger.warn(`WebSocket ??: ${event.message || "????"}`);
|
|
1004
1042
|
});
|
|
1005
1043
|
socket.addEventListener("close", (event) => {
|
|
1006
1044
|
delete bot.internal._request;
|
|
1007
|
-
const closeInfo = `code: ${event.code}, reason: ${event.reason || "
|
|
1045
|
+
const closeInfo = `code: ${event.code}, reason: ${event.reason || "?"}`;
|
|
1008
1046
|
if (event.code === 1009) {
|
|
1009
|
-
bot.logger.error(`WebSocket
|
|
1047
|
+
bot.logger.error(`WebSocket ??????? (${closeInfo})?????????????`);
|
|
1010
1048
|
} else if (event.code !== 1e3 && event.code !== 1001) {
|
|
1011
|
-
bot.logger.warn(`WebSocket
|
|
1049
|
+
bot.logger.warn(`WebSocket ???? (${closeInfo})`);
|
|
1012
1050
|
}
|
|
1013
1051
|
;
|
|
1014
1052
|
bot.ctx.emit("onebot-multi/bot-offline", bot);
|
|
@@ -1453,6 +1491,9 @@ var LoadBalancer = class {
|
|
|
1453
1491
|
/**
|
|
1454
1492
|
* 检查 Bot 是否在线
|
|
1455
1493
|
*/
|
|
1494
|
+
getEffectivePriorityChannels() {
|
|
1495
|
+
return new Set(this.config.priorityChannels || []);
|
|
1496
|
+
}
|
|
1456
1497
|
isBotOnline(selfId) {
|
|
1457
1498
|
const bot = this.ctx.bots.find((b) => b.selfId === selfId && b.platform === "onebot");
|
|
1458
1499
|
return bot?.status === STATUS_ONLINE3;
|
|
@@ -1655,7 +1696,7 @@ var LoadBalancer = class {
|
|
|
1655
1696
|
return;
|
|
1656
1697
|
}
|
|
1657
1698
|
const channels = [];
|
|
1658
|
-
const prioritySet =
|
|
1699
|
+
const prioritySet = this.getEffectivePriorityChannels();
|
|
1659
1700
|
for (const [channelId, bots] of this.allChannels.entries()) {
|
|
1660
1701
|
channels.push({
|
|
1661
1702
|
channelId,
|
|
@@ -1730,13 +1771,8 @@ var LoadBalancer = class {
|
|
|
1730
1771
|
}
|
|
1731
1772
|
}
|
|
1732
1773
|
if (assignments.length > 0) {
|
|
1733
|
-
const
|
|
1734
|
-
|
|
1735
|
-
const slice = assignments.slice(i, i + batchSize);
|
|
1736
|
-
await Promise.all(slice.map(async ({ channelId, assignee }) => {
|
|
1737
|
-
await this.ctx.database.setChannel("onebot", channelId, { assignee });
|
|
1738
|
-
this.lastAssignees.set(channelId, assignee);
|
|
1739
|
-
}));
|
|
1774
|
+
for (const { channelId, assignee } of assignments) {
|
|
1775
|
+
this.lastAssignees.set(channelId, assignee);
|
|
1740
1776
|
}
|
|
1741
1777
|
}
|
|
1742
1778
|
const botsWithData = onlineBots.filter((id) => {
|
|
@@ -2138,7 +2174,7 @@ var ConfigStore = class {
|
|
|
2138
2174
|
var Config = import_koishi9.Schema.object({}).description("在侧边栏webui使用,配置文件在data/adapter-onebot-multi/config.yaml");
|
|
2139
2175
|
var name = "adapter-onebot-multi";
|
|
2140
2176
|
var inject = {
|
|
2141
|
-
required: ["server"
|
|
2177
|
+
required: ["server"],
|
|
2142
2178
|
optional: ["console"]
|
|
2143
2179
|
};
|
|
2144
2180
|
function apply(ctx, _config) {
|
|
@@ -2148,6 +2184,49 @@ function apply(ctx, _config) {
|
|
|
2148
2184
|
const managedBotIds = /* @__PURE__ */ new Set();
|
|
2149
2185
|
const botForks = /* @__PURE__ */ new Map();
|
|
2150
2186
|
const getRuntimeConfig = /* @__PURE__ */ __name(() => configStore.getRuntime(), "getRuntimeConfig");
|
|
2187
|
+
const createPendingSelfId = /* @__PURE__ */ __name(() => {
|
|
2188
|
+
let candidate = `pending_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
2189
|
+
const exists = /* @__PURE__ */ __name((id) => configStore.getBots().some((bot) => bot.selfId === id), "exists");
|
|
2190
|
+
while (exists(candidate)) {
|
|
2191
|
+
candidate = `pending_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
2192
|
+
}
|
|
2193
|
+
return candidate;
|
|
2194
|
+
}, "createPendingSelfId");
|
|
2195
|
+
const isReverseTemplateBot = /* @__PURE__ */ __name((bot) => {
|
|
2196
|
+
return (bot.protocol || "ws-reverse") === "ws-reverse" && String(bot.selfId || "").startsWith("pending_");
|
|
2197
|
+
}, "isReverseTemplateBot");
|
|
2198
|
+
const findReverseTemplate = /* @__PURE__ */ __name((path2) => {
|
|
2199
|
+
return configStore.getBots().find((bot) => {
|
|
2200
|
+
if (bot.enabled === false) return false;
|
|
2201
|
+
if (!isReverseTemplateBot(bot)) return false;
|
|
2202
|
+
return (bot.path || "/onebot") === path2;
|
|
2203
|
+
});
|
|
2204
|
+
}, "findReverseTemplate");
|
|
2205
|
+
const findReverseSeedBot = /* @__PURE__ */ __name((path2) => {
|
|
2206
|
+
const bots = configStore.getBots().filter((bot) => {
|
|
2207
|
+
if (bot.enabled === false) return false;
|
|
2208
|
+
return (bot.protocol || "ws-reverse") === "ws-reverse" && (bot.path || "/onebot") === path2;
|
|
2209
|
+
});
|
|
2210
|
+
return bots.find(isReverseTemplateBot) || bots[0];
|
|
2211
|
+
}, "findReverseSeedBot");
|
|
2212
|
+
const ensureReverseTemplateRecord = /* @__PURE__ */ __name((path2) => {
|
|
2213
|
+
const normalizedPath = path2 || "/onebot";
|
|
2214
|
+
if (findReverseTemplate(normalizedPath)) return;
|
|
2215
|
+
const seedBot = findReverseSeedBot(normalizedPath);
|
|
2216
|
+
if (!seedBot) return;
|
|
2217
|
+
configStore.setBots([
|
|
2218
|
+
...configStore.getBots(),
|
|
2219
|
+
{
|
|
2220
|
+
selfId: createPendingSelfId(),
|
|
2221
|
+
token: seedBot.token,
|
|
2222
|
+
protocol: "ws-reverse",
|
|
2223
|
+
path: normalizedPath,
|
|
2224
|
+
name: "服务器",
|
|
2225
|
+
enabled: true
|
|
2226
|
+
}
|
|
2227
|
+
]);
|
|
2228
|
+
logger.info(`已为 path=${normalizedPath} 自动补齐 ws 服务器模板记录`);
|
|
2229
|
+
}, "ensureReverseTemplateRecord");
|
|
2151
2230
|
const getGlobalConfig = /* @__PURE__ */ __name(() => {
|
|
2152
2231
|
const runtime = getRuntimeConfig();
|
|
2153
2232
|
return {
|
|
@@ -2246,6 +2325,42 @@ function apply(ctx, _config) {
|
|
|
2246
2325
|
botForks.set(selfId, fork);
|
|
2247
2326
|
managedBotIds.add(selfId);
|
|
2248
2327
|
}, "startBot");
|
|
2328
|
+
const ensureReverseBot = /* @__PURE__ */ __name((selfId, path2) => {
|
|
2329
|
+
const normalizedSelfId = String(selfId || "").trim();
|
|
2330
|
+
const normalizedPath = path2 || "/onebot";
|
|
2331
|
+
if (!normalizedSelfId) return;
|
|
2332
|
+
const live = ctx.bots.find((bot) => bot.selfId === normalizedSelfId && bot.platform === "onebot");
|
|
2333
|
+
if (live) return live;
|
|
2334
|
+
const existing = configStore.getBots().find((bot) => {
|
|
2335
|
+
return bot.selfId === normalizedSelfId && (bot.protocol || "ws-reverse") === "ws-reverse";
|
|
2336
|
+
});
|
|
2337
|
+
if (existing) {
|
|
2338
|
+
startBot(existing);
|
|
2339
|
+
return ctx.bots.find((bot) => bot.selfId === normalizedSelfId && bot.platform === "onebot");
|
|
2340
|
+
}
|
|
2341
|
+
const template = findReverseTemplate(normalizedPath);
|
|
2342
|
+
const seedBot = template || findReverseSeedBot(normalizedPath);
|
|
2343
|
+
if (!seedBot) {
|
|
2344
|
+
logger.warn(`未找到 path=${normalizedPath} 的 ws-reverse 配置,拒绝动态创建 Bot ${normalizedSelfId}`);
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
if (!template) {
|
|
2348
|
+
ensureReverseTemplateRecord(normalizedPath);
|
|
2349
|
+
}
|
|
2350
|
+
const dynamicBot = {
|
|
2351
|
+
selfId: normalizedSelfId,
|
|
2352
|
+
token: seedBot.token,
|
|
2353
|
+
protocol: "ws-reverse",
|
|
2354
|
+
path: normalizedPath,
|
|
2355
|
+
name: seedBot.name && seedBot.name !== "服务器" ? seedBot.name : void 0,
|
|
2356
|
+
enabled: true
|
|
2357
|
+
};
|
|
2358
|
+
configStore.setBots([...configStore.getBots(), dynamicBot]);
|
|
2359
|
+
startBot(dynamicBot);
|
|
2360
|
+
logger.info(`已为反向 WS 连接动态创建 Bot ${normalizedSelfId} (${normalizedPath})`);
|
|
2361
|
+
return ctx.bots.find((bot) => bot.selfId === normalizedSelfId && bot.platform === "onebot");
|
|
2362
|
+
}, "ensureReverseBot");
|
|
2363
|
+
ctx._onebotMultiEnsureReverseBot = ensureReverseBot;
|
|
2249
2364
|
const stopBot = /* @__PURE__ */ __name(async (selfId) => {
|
|
2250
2365
|
try {
|
|
2251
2366
|
const fork = botForks.get(selfId);
|
|
@@ -2269,6 +2384,13 @@ function apply(ctx, _config) {
|
|
|
2269
2384
|
startBot(target);
|
|
2270
2385
|
}, "restartBot");
|
|
2271
2386
|
const syncBots = /* @__PURE__ */ __name(async () => {
|
|
2387
|
+
const currentBots = configStore.getBots();
|
|
2388
|
+
const reversePaths = new Set(
|
|
2389
|
+
currentBots.filter((bot) => (bot.protocol || "ws-reverse") === "ws-reverse").map((bot) => bot.path || "/onebot")
|
|
2390
|
+
);
|
|
2391
|
+
for (const path2 of reversePaths) {
|
|
2392
|
+
ensureReverseTemplateRecord(path2);
|
|
2393
|
+
}
|
|
2272
2394
|
const bots = configStore.getBots();
|
|
2273
2395
|
const enabledIds = new Set(bots.filter((b) => b.enabled !== false).map((b) => b.selfId));
|
|
2274
2396
|
for (const selfId of Array.from(managedBotIds)) {
|
|
@@ -2284,14 +2406,6 @@ function apply(ctx, _config) {
|
|
|
2284
2406
|
}
|
|
2285
2407
|
}, "syncBots");
|
|
2286
2408
|
const upsertBot = /* @__PURE__ */ __name((nextBot, oldSelfId) => {
|
|
2287
|
-
const createPendingSelfId = /* @__PURE__ */ __name(() => {
|
|
2288
|
-
let candidate = `pending_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
2289
|
-
const exists = /* @__PURE__ */ __name((id) => configStore.getBots().some((bot) => bot.selfId === id), "exists");
|
|
2290
|
-
while (exists(candidate)) {
|
|
2291
|
-
candidate = `pending_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
2292
|
-
}
|
|
2293
|
-
return candidate;
|
|
2294
|
-
}, "createPendingSelfId");
|
|
2295
2409
|
const inputSelfId = String(nextBot.selfId || "").trim();
|
|
2296
2410
|
const nextSelfId = inputSelfId || oldSelfId || createPendingSelfId();
|
|
2297
2411
|
const all = configStore.getBots();
|
|
@@ -2312,39 +2426,39 @@ function apply(ctx, _config) {
|
|
|
2312
2426
|
configStore.setBots(filtered);
|
|
2313
2427
|
return nextSelfId;
|
|
2314
2428
|
}, "upsertBot");
|
|
2315
|
-
ctx.command("onebot.balance", "
|
|
2429
|
+
ctx.command("onebot.balance", "?? OneBot ??????").alias("ob.balance").alias("????").action(async () => {
|
|
2316
2430
|
if (!configStore.getLoadBalance().enabled) {
|
|
2317
|
-
return "
|
|
2431
|
+
return "????????";
|
|
2318
2432
|
}
|
|
2319
2433
|
const status = loadBalancer.getDetailedStatus();
|
|
2320
2434
|
const elements = [];
|
|
2321
|
-
elements.push(import_koishi9.h.text("
|
|
2322
|
-
elements.push(import_koishi9.h.text(
|
|
2435
|
+
elements.push(import_koishi9.h.text("??? OneBot ?????? ???\n\n"));
|
|
2436
|
+
elements.push(import_koishi9.h.text(`?? ??
|
|
2323
2437
|
`));
|
|
2324
|
-
elements.push(import_koishi9.h.text(`
|
|
2438
|
+
elements.push(import_koishi9.h.text(` ?? Bot: ${status.onlineBots} ?
|
|
2325
2439
|
`));
|
|
2326
|
-
elements.push(import_koishi9.h.text(`
|
|
2440
|
+
elements.push(import_koishi9.h.text(` ????: ${status.totalChannels} ?
|
|
2327
2441
|
`));
|
|
2328
|
-
elements.push(import_koishi9.h.text(`
|
|
2442
|
+
elements.push(import_koishi9.h.text(` ????: ${status.assignedChannels} ?
|
|
2329
2443
|
`));
|
|
2330
|
-
elements.push(import_koishi9.h.text(`
|
|
2444
|
+
elements.push(import_koishi9.h.text(` ????: ${status.unassignedChannels} ?
|
|
2331
2445
|
|
|
2332
2446
|
`));
|
|
2333
|
-
elements.push(import_koishi9.h.text(
|
|
2447
|
+
elements.push(import_koishi9.h.text(`?? Bot ????
|
|
2334
2448
|
`));
|
|
2335
2449
|
for (const bot of status.bots) {
|
|
2336
|
-
const maxLoadStr = bot.maxLoad === 0 ? "
|
|
2337
|
-
const remainingStr = bot.maxLoad === 0 ? "
|
|
2450
|
+
const maxLoadStr = bot.maxLoad === 0 ? "???" : `${bot.maxLoad}`;
|
|
2451
|
+
const remainingStr = bot.maxLoad === 0 ? "?" : `${bot.remaining}`;
|
|
2338
2452
|
const barLength = 20;
|
|
2339
2453
|
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);
|
|
2340
|
-
const bar = "
|
|
2454
|
+
const bar = "?".repeat(usedLength) + "?".repeat(barLength - usedLength);
|
|
2341
2455
|
elements.push(import_koishi9.h.image(bot.avatarUrl));
|
|
2342
2456
|
elements.push(import_koishi9.h.text("\n"));
|
|
2343
2457
|
elements.push(import_koishi9.h.text(` ${bot.nickname} (${bot.selfId})
|
|
2344
2458
|
`));
|
|
2345
|
-
elements.push(import_koishi9.h.text(`
|
|
2459
|
+
elements.push(import_koishi9.h.text(` ???: ${bot.channelCount} | ???: ${bot.assignedCount} | ??: ${maxLoadStr}
|
|
2346
2460
|
`));
|
|
2347
|
-
elements.push(import_koishi9.h.text(`
|
|
2461
|
+
elements.push(import_koishi9.h.text(` ????: ${remainingStr}
|
|
2348
2462
|
`));
|
|
2349
2463
|
elements.push(import_koishi9.h.text(` [${bar}]
|
|
2350
2464
|
|
|
@@ -2407,7 +2521,7 @@ function apply(ctx, _config) {
|
|
|
2407
2521
|
}, { authority: 4 });
|
|
2408
2522
|
ctx2.console.addListener("onebot-multi/bots/profile", async ({ selfId, nickname }) => {
|
|
2409
2523
|
const bot = ctx2.bots.find((b) => b.selfId === selfId && b.platform === "onebot");
|
|
2410
|
-
if (!bot) return { success: false, error: `Bot ${selfId}
|
|
2524
|
+
if (!bot) return { success: false, error: `Bot ${selfId} ???` };
|
|
2411
2525
|
await bot.internal.setQqProfile(nickname || "", "", "", "", "");
|
|
2412
2526
|
const loginInfo = await bot.internal.getLoginInfo();
|
|
2413
2527
|
if (loginInfo?.nickname) {
|
|
@@ -2417,7 +2531,7 @@ function apply(ctx, _config) {
|
|
|
2417
2531
|
}, { authority: 4 });
|
|
2418
2532
|
ctx2.console.addListener("onebot-multi/bots/avatar", async ({ selfId, file }) => {
|
|
2419
2533
|
const bot = ctx2.bots.find((b) => b.selfId === selfId && b.platform === "onebot");
|
|
2420
|
-
if (!bot) return { success: false, error: `Bot ${selfId}
|
|
2534
|
+
if (!bot) return { success: false, error: `Bot ${selfId} ???` };
|
|
2421
2535
|
await bot.internal.setQqAvatar(file);
|
|
2422
2536
|
return { success: true };
|
|
2423
2537
|
}, { authority: 4 });
|
|
@@ -2443,7 +2557,7 @@ function apply(ctx, _config) {
|
|
|
2443
2557
|
}, { authority: 4 });
|
|
2444
2558
|
});
|
|
2445
2559
|
ctx.on("ready", () => {
|
|
2446
|
-
syncBots().catch((error) => logger.error("
|
|
2560
|
+
syncBots().catch((error) => logger.error("?? Bot ??????: %s", error));
|
|
2447
2561
|
});
|
|
2448
2562
|
ctx.on("onebot-multi/bot-online", (bot) => {
|
|
2449
2563
|
const configKey = String(bot.config?.managedKey || bot.selfId);
|
|
@@ -2458,7 +2572,7 @@ function apply(ctx, _config) {
|
|
|
2458
2572
|
bot.config.managedKey = runtimeSelfId;
|
|
2459
2573
|
managedBotIds.delete(configKey);
|
|
2460
2574
|
managedBotIds.add(runtimeSelfId);
|
|
2461
|
-
logger.info(`Bot
|
|
2575
|
+
logger.info(`Bot ?? selfId ????: ${configKey} -> ${runtimeSelfId}`);
|
|
2462
2576
|
});
|
|
2463
2577
|
ctx.on("dispose", async () => {
|
|
2464
2578
|
stopConfigWatcher();
|
package/lib/load-balancer.d.ts
CHANGED
package/lib/ws.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Adapter, Context, HTTP, Logger, Schema, Universal } from 'koishi';
|
|
2
|
-
import { WebSocketLayer } from '@koishijs/plugin-server';
|
|
3
2
|
import type { OneBotBot } from './bot';
|
|
4
3
|
export interface SharedConfig<T = 'ws' | 'ws-reverse'> {
|
|
5
4
|
protocol: T;
|
|
@@ -19,7 +18,7 @@ export declare namespace WsClient {
|
|
|
19
18
|
export declare class WsServer<C extends Context> extends Adapter<C, OneBotBot<C, any>> {
|
|
20
19
|
static inject: string[];
|
|
21
20
|
logger: Logger;
|
|
22
|
-
|
|
21
|
+
private hub;
|
|
23
22
|
constructor(ctx: C, bot: OneBotBot<C>);
|
|
24
23
|
disconnect(bot: OneBotBot<C>): Promise<void>;
|
|
25
24
|
}
|
package/package.json
CHANGED
package/lib/admin.d.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { Context } from 'koishi';
|
|
2
|
-
import { ConfigManager, BotConfigRecord } from './config-manager';
|
|
3
|
-
import { StatusManager, BotStatus } from './status';
|
|
4
|
-
export interface BotFullInfo extends BotStatus {
|
|
5
|
-
config: Omit<BotConfigRecord, 'token'>;
|
|
6
|
-
}
|
|
7
|
-
export interface AdminPanelData {
|
|
8
|
-
bots: BotFullInfo[];
|
|
9
|
-
updatedAt: number;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Console 管理面板服务
|
|
13
|
-
* 提供 Bot 配置的增删改查 API
|
|
14
|
-
*/
|
|
15
|
-
export declare function setupAdminPanel(ctx: Context, configManager: ConfigManager, statusManager: StatusManager): void;
|
|
16
|
-
declare module '@koishijs/console' {
|
|
17
|
-
interface Events {
|
|
18
|
-
'onebot-multi/admin/list'(): Promise<AdminPanelData>;
|
|
19
|
-
'onebot-multi/admin/create'(data: {
|
|
20
|
-
selfId: string;
|
|
21
|
-
token?: string;
|
|
22
|
-
protocol: 'ws' | 'ws-reverse';
|
|
23
|
-
endpoint?: string;
|
|
24
|
-
path?: string;
|
|
25
|
-
}): Promise<{
|
|
26
|
-
success: boolean;
|
|
27
|
-
record: BotConfigRecord;
|
|
28
|
-
}>;
|
|
29
|
-
'onebot-multi/admin/update'(data: {
|
|
30
|
-
selfId: string;
|
|
31
|
-
updates: {
|
|
32
|
-
token?: string;
|
|
33
|
-
protocol?: 'ws' | 'ws-reverse';
|
|
34
|
-
endpoint?: string;
|
|
35
|
-
path?: string;
|
|
36
|
-
};
|
|
37
|
-
}): Promise<{
|
|
38
|
-
success: boolean;
|
|
39
|
-
needRestart: boolean;
|
|
40
|
-
}>;
|
|
41
|
-
'onebot-multi/admin/delete'(selfId: string): Promise<{
|
|
42
|
-
success: boolean;
|
|
43
|
-
}>;
|
|
44
|
-
'onebot-multi/admin/toggle'(selfId: string): Promise<{
|
|
45
|
-
success: boolean;
|
|
46
|
-
enabled: boolean;
|
|
47
|
-
}>;
|
|
48
|
-
'onebot-multi/admin/restart'(selfId: string): Promise<{
|
|
49
|
-
success: boolean;
|
|
50
|
-
}>;
|
|
51
|
-
}
|
|
52
|
-
}
|
package/lib/config-manager.d.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { Context } from 'koishi';
|
|
2
|
-
import type { BotConfig } from './index';
|
|
3
|
-
declare module 'koishi' {
|
|
4
|
-
interface Tables {
|
|
5
|
-
'onebot_multi_bots': BotConfigRecord;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
export interface BotConfigRecord {
|
|
9
|
-
id: number;
|
|
10
|
-
name: string;
|
|
11
|
-
selfId?: string;
|
|
12
|
-
token?: string;
|
|
13
|
-
protocol: 'ws' | 'ws-reverse';
|
|
14
|
-
endpoint?: string;
|
|
15
|
-
path?: string;
|
|
16
|
-
enabled: boolean;
|
|
17
|
-
createdAt: Date;
|
|
18
|
-
updatedAt: Date;
|
|
19
|
-
}
|
|
20
|
-
export declare class ConfigManager {
|
|
21
|
-
private ctx;
|
|
22
|
-
constructor(ctx: Context);
|
|
23
|
-
/**
|
|
24
|
-
* 获取存储的密码哈希
|
|
25
|
-
*/
|
|
26
|
-
private getStoredPasswordHash;
|
|
27
|
-
/**
|
|
28
|
-
* 设置管理密码(哈希存储)
|
|
29
|
-
*/
|
|
30
|
-
setAdminPassword(password: string): Promise<void>;
|
|
31
|
-
/**
|
|
32
|
-
* 检查是否已设置密码
|
|
33
|
-
*/
|
|
34
|
-
hasAdminPassword(): Promise<boolean>;
|
|
35
|
-
/**
|
|
36
|
-
* 验证管理密码
|
|
37
|
-
*/
|
|
38
|
-
verifyAdminPassword(password: string): Promise<boolean>;
|
|
39
|
-
/**
|
|
40
|
-
* 获取所有 Bot 配置(排除系统配置)
|
|
41
|
-
*/
|
|
42
|
-
getAllConfigs(): Promise<BotConfigRecord[]>;
|
|
43
|
-
/**
|
|
44
|
-
* 获取启用的 Bot 配置(排除系统配置)
|
|
45
|
-
*/
|
|
46
|
-
getEnabledConfigs(): Promise<BotConfigRecord[]>;
|
|
47
|
-
/**
|
|
48
|
-
* 根据 ID 获取单个 Bot 配置
|
|
49
|
-
*/
|
|
50
|
-
getConfigById(id: number): Promise<BotConfigRecord | undefined>;
|
|
51
|
-
/**
|
|
52
|
-
* 根据 selfId 获取配置(用于关联运行时 Bot)
|
|
53
|
-
*/
|
|
54
|
-
getConfigBySelfId(selfId: string): Promise<BotConfigRecord | undefined>;
|
|
55
|
-
/**
|
|
56
|
-
* 创建 Bot 配置
|
|
57
|
-
*/
|
|
58
|
-
createConfig(config: Omit<BotConfigRecord, 'id' | 'selfId' | 'createdAt' | 'updatedAt'> & {
|
|
59
|
-
selfId?: string;
|
|
60
|
-
}): Promise<BotConfigRecord>;
|
|
61
|
-
/**
|
|
62
|
-
* 更新 Bot 配置(根据 ID)
|
|
63
|
-
*/
|
|
64
|
-
updateConfigById(id: number, updates: Partial<Omit<BotConfigRecord, 'id' | 'createdAt'>>): Promise<boolean>;
|
|
65
|
-
/**
|
|
66
|
-
* 删除 Bot 配置(根据 ID)
|
|
67
|
-
*/
|
|
68
|
-
deleteConfigById(id: number): Promise<boolean>;
|
|
69
|
-
/**
|
|
70
|
-
* 切换 Bot 启用状态
|
|
71
|
-
*/
|
|
72
|
-
toggleEnabled(id: number): Promise<boolean>;
|
|
73
|
-
/**
|
|
74
|
-
* 更新配置的 selfId(Bot 连接成功后调用)
|
|
75
|
-
*/
|
|
76
|
-
updateSelfId(id: number, selfId: string): Promise<boolean>;
|
|
77
|
-
/**
|
|
78
|
-
* 将数据库配置转换为 BotConfig 格式(用于启动 Bot)
|
|
79
|
-
*/
|
|
80
|
-
toBotConfig(record: BotConfigRecord): BotConfig & {
|
|
81
|
-
configId: number;
|
|
82
|
-
};
|
|
83
|
-
}
|
package/lib/csv-sync.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Context } from 'koishi';
|
|
2
|
-
import { ConfigStore } from './config-store';
|
|
3
|
-
import { LoadBalancer } from './load-balancer';
|
|
4
|
-
export declare class CsvSyncManager {
|
|
5
|
-
private ctx;
|
|
6
|
-
private configStore;
|
|
7
|
-
private loadBalancer;
|
|
8
|
-
private logger;
|
|
9
|
-
private csvPath;
|
|
10
|
-
constructor(ctx: Context, configStore: ConfigStore, loadBalancer: LoadBalancer);
|
|
11
|
-
/**
|
|
12
|
-
* 手动触发同步 CSV 到 PriorityChannels
|
|
13
|
-
*/
|
|
14
|
-
syncFromCsv(): {
|
|
15
|
-
success: boolean;
|
|
16
|
-
count: number;
|
|
17
|
-
error?: string;
|
|
18
|
-
};
|
|
19
|
-
}
|
package/lib/panel.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Context, Schema } from 'koishi';
|
|
2
|
-
import { StatusManager } from './status';
|
|
3
|
-
import { ConfigManager } from './config-manager';
|
|
4
|
-
export interface PanelConfig {
|
|
5
|
-
enabled?: boolean;
|
|
6
|
-
basePath?: string;
|
|
7
|
-
port?: number;
|
|
8
|
-
}
|
|
9
|
-
export declare const PanelConfig: Schema<PanelConfig>;
|
|
10
|
-
export declare class StatusPanel {
|
|
11
|
-
private ctx;
|
|
12
|
-
private config;
|
|
13
|
-
private statusManager;
|
|
14
|
-
private configManager;
|
|
15
|
-
private app?;
|
|
16
|
-
private server?;
|
|
17
|
-
constructor(ctx: Context, config: PanelConfig, statusManager: StatusManager, configManager: ConfigManager);
|
|
18
|
-
private parseJsonBody;
|
|
19
|
-
private setupRoutes;
|
|
20
|
-
private startBot;
|
|
21
|
-
private stopBot;
|
|
22
|
-
private renderAdminApp;
|
|
23
|
-
private renderStatusPage;
|
|
24
|
-
private formatUptime;
|
|
25
|
-
}
|