mioki 0.12.2 → 0.14.0
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/dist/cli.cjs +10 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +10 -7
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +344 -167
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -17
- package/dist/index.d.mts +59 -17
- package/dist/index.mjs +341 -168
- package/dist/index.mjs.map +1 -1
- package/dist/package-DFYh9fsp.mjs +6 -0
- package/dist/package-DFYh9fsp.mjs.map +1 -0
- package/dist/{package-BQBq2Zmr.cjs → package-xEK88jTG.cjs} +2 -2
- package/dist/package-xEK88jTG.cjs.map +1 -0
- package/package.json +3 -3
- package/dist/package-BQBq2Zmr.cjs.map +0 -1
- package/dist/package-DhsQExzV.mjs +0 -6
- package/dist/package-DhsQExzV.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_package = require('./package-
|
|
1
|
+
const require_package = require('./package-xEK88jTG.cjs');
|
|
2
2
|
let node_fs = require("node:fs");
|
|
3
3
|
node_fs = require_package.__toESM(node_fs);
|
|
4
4
|
let node_util = require("node:util");
|
|
@@ -790,12 +790,20 @@ var config_exports = /* @__PURE__ */ require_package.__exportAll({
|
|
|
790
790
|
isInPm2: () => isInPm2,
|
|
791
791
|
isOwner: () => isOwner,
|
|
792
792
|
isOwnerOrAdmin: () => isOwnerOrAdmin,
|
|
793
|
+
normalizeNapCatConfig: () => normalizeNapCatConfig,
|
|
793
794
|
readMiokiConfig: () => readMiokiConfig,
|
|
794
795
|
readPackageJson: () => readPackageJson,
|
|
795
796
|
updateBotCWD: () => updateBotCWD,
|
|
796
797
|
updateBotConfig: () => updateBotConfig,
|
|
797
798
|
writePackageJson: () => writePackageJson
|
|
798
799
|
});
|
|
800
|
+
function isSingleNapCatConfig(config) {
|
|
801
|
+
return !Array.isArray(config);
|
|
802
|
+
}
|
|
803
|
+
function normalizeNapCatConfig(config) {
|
|
804
|
+
if (isSingleNapCatConfig(config)) return [config];
|
|
805
|
+
return config;
|
|
806
|
+
}
|
|
799
807
|
/**
|
|
800
808
|
* 机器人根目录
|
|
801
809
|
*/
|
|
@@ -811,7 +819,10 @@ function readMiokiConfig() {
|
|
|
811
819
|
const config = readPackageJson().mioki;
|
|
812
820
|
if (!config) throw new Error(`无法在 package.json 中找到 mioki 配置,请确认 package.json 文件中是否包含 mioki 字段`);
|
|
813
821
|
if (!config.napcat) throw new Error(`mioki 配置中缺少 napcat 字段,请补全后重试`);
|
|
814
|
-
return
|
|
822
|
+
return {
|
|
823
|
+
...config,
|
|
824
|
+
napcat: normalizeNapCatConfig(config.napcat)
|
|
825
|
+
};
|
|
815
826
|
}
|
|
816
827
|
/**
|
|
817
828
|
* `mioki` 框架相关配置
|
|
@@ -1212,9 +1223,58 @@ function addService(name, service, cover = true) {
|
|
|
1212
1223
|
function bindBot(bot, func) {
|
|
1213
1224
|
return (...args) => func(bot, ...args);
|
|
1214
1225
|
}
|
|
1226
|
+
/**
|
|
1227
|
+
* 消息去重器
|
|
1228
|
+
* 处理多个 bot 在同一个群时,同一消息只处理一次
|
|
1229
|
+
*/
|
|
1230
|
+
var MessageDeduplicator = class {
|
|
1231
|
+
processedMessages = /* @__PURE__ */ new Set();
|
|
1232
|
+
maxSize = 1e3;
|
|
1233
|
+
/**
|
|
1234
|
+
* 生成消息唯一键
|
|
1235
|
+
* 对于群消息:使用 group_id:user_id:time
|
|
1236
|
+
* 对于私聊消息:使用 private:user_id:time
|
|
1237
|
+
*/
|
|
1238
|
+
getKey(event) {
|
|
1239
|
+
if (event.message_type === "group") return `group:${event.group_id}:${event.user_id}:${event.time}`;
|
|
1240
|
+
else return `private:${event.user_id}:${event.time}`;
|
|
1241
|
+
}
|
|
1242
|
+
isProcessed(event) {
|
|
1243
|
+
return this.processedMessages.has(this.getKey(event));
|
|
1244
|
+
}
|
|
1245
|
+
markProcessed(event) {
|
|
1246
|
+
if (this.processedMessages.size >= this.maxSize) {
|
|
1247
|
+
const first = this.processedMessages.values().next();
|
|
1248
|
+
if (!first.done) this.processedMessages.delete(first.value);
|
|
1249
|
+
}
|
|
1250
|
+
this.processedMessages.add(this.getKey(event));
|
|
1251
|
+
}
|
|
1252
|
+
clear() {
|
|
1253
|
+
this.processedMessages.clear();
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
const deduplicator = new MessageDeduplicator();
|
|
1215
1257
|
const runtimePlugins = /* @__PURE__ */ new Map();
|
|
1216
1258
|
const buildRemovedActions = (bot) => Object.fromEntries(Object.entries(actions_exports).map(([k, v]) => [k, bindBot(bot, v)]));
|
|
1217
1259
|
/**
|
|
1260
|
+
* 检查事件是否是群消息事件
|
|
1261
|
+
*/
|
|
1262
|
+
function isGroupMessageEvent(event) {
|
|
1263
|
+
return event?.post_type === "message" && event?.message_type === "group";
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* 检查事件是否是私聊消息事件
|
|
1267
|
+
*/
|
|
1268
|
+
function isPrivateMessageEvent(event) {
|
|
1269
|
+
return event?.post_type === "message" && event?.message_type === "private";
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* 检查事件是否是消息事件
|
|
1273
|
+
*/
|
|
1274
|
+
function isMessageEvent(event) {
|
|
1275
|
+
return isGroupMessageEvent(event) || isPrivateMessageEvent(event);
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1218
1278
|
* 定义一个 Mioki 插件
|
|
1219
1279
|
* @param plugin Mioki 插件对象
|
|
1220
1280
|
* @returns Mioki 插件对象
|
|
@@ -1236,55 +1296,79 @@ function getAbsPluginDir(defaultDir = "plugins") {
|
|
|
1236
1296
|
const cwd = BOT_CWD.value;
|
|
1237
1297
|
return node_path.default.join(cwd, botConfig.plugins_dir || defaultDir);
|
|
1238
1298
|
}
|
|
1239
|
-
async function enablePlugin(
|
|
1299
|
+
async function enablePlugin(bots, plugin, type = "external") {
|
|
1240
1300
|
const typeDesc = type === "builtin" ? "内置" : "用户";
|
|
1241
1301
|
const pluginName = plugin.name || "null";
|
|
1242
1302
|
const { name = pluginName, version: version$1 = "null", description = "-", setup = () => {} } = plugin;
|
|
1303
|
+
if (!bots[0]) throw new Error("没有可用的 bot 实例");
|
|
1243
1304
|
try {
|
|
1244
1305
|
const start$1 = node_process.hrtime.bigint();
|
|
1245
1306
|
const clears = /* @__PURE__ */ new Set();
|
|
1246
1307
|
const userClears = /* @__PURE__ */ new Set();
|
|
1247
|
-
const logger$1 =
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1308
|
+
const logger$1 = logger.withDefaults({ tag: `plugin:${name}` });
|
|
1309
|
+
const createContext = (bot) => {
|
|
1310
|
+
return {
|
|
1311
|
+
bot,
|
|
1312
|
+
bots,
|
|
1313
|
+
self_id: bot.bot_id,
|
|
1314
|
+
segment: bot.segment,
|
|
1315
|
+
getCookie: bot.getCookie.bind(bot),
|
|
1316
|
+
...utils_exports,
|
|
1317
|
+
...config_exports,
|
|
1318
|
+
...buildRemovedActions(bot),
|
|
1319
|
+
logger: logger$1,
|
|
1320
|
+
services,
|
|
1321
|
+
clears: userClears,
|
|
1322
|
+
deduplicator,
|
|
1323
|
+
addService: (name$1, service, cover) => {
|
|
1324
|
+
const remove = addService(name$1, service, cover);
|
|
1325
|
+
clears.add(remove);
|
|
1326
|
+
return remove;
|
|
1327
|
+
},
|
|
1328
|
+
handle: (eventName, handler) => {
|
|
1329
|
+
logger$1.debug(`Registering event handler for event: ${String(eventName)}`);
|
|
1330
|
+
const unsubscribes = [];
|
|
1331
|
+
for (const bot$1 of bots) {
|
|
1332
|
+
const wrappedHandler = (event) => {
|
|
1333
|
+
if (isMessageEvent(event)) {
|
|
1334
|
+
const messageEvent = event;
|
|
1335
|
+
if (isPrivateMessageEvent(messageEvent)) {
|
|
1336
|
+
if (messageEvent.self_id !== bot$1.bot_id) return;
|
|
1337
|
+
}
|
|
1338
|
+
if (isGroupMessageEvent(messageEvent)) {
|
|
1339
|
+
if (deduplicator.isProcessed(messageEvent)) return;
|
|
1340
|
+
deduplicator.markProcessed(messageEvent);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
handler(event);
|
|
1344
|
+
};
|
|
1345
|
+
bot$1.on(eventName, wrappedHandler);
|
|
1346
|
+
const unsubscribe = () => {
|
|
1347
|
+
logger$1.debug(`Unregistering event handler for event: ${String(eventName)}`);
|
|
1348
|
+
bot$1.off(eventName, wrappedHandler);
|
|
1349
|
+
};
|
|
1350
|
+
unsubscribes.push(unsubscribe);
|
|
1351
|
+
}
|
|
1352
|
+
const clearAll = () => {
|
|
1353
|
+
unsubscribes.forEach((fn) => fn());
|
|
1354
|
+
};
|
|
1355
|
+
clears.add(clearAll);
|
|
1356
|
+
return clearAll;
|
|
1357
|
+
},
|
|
1358
|
+
cron: (cronExpression, handler) => {
|
|
1359
|
+
logger$1.debug(`Scheduling cron job: ${cronExpression}`);
|
|
1360
|
+
const job = node_cron.default.schedule(cronExpression, (now) => handler(createContext(bot), now));
|
|
1361
|
+
const clear = () => {
|
|
1362
|
+
logger$1.debug(`Stopping cron job: ${cronExpression}`);
|
|
1363
|
+
job.stop();
|
|
1364
|
+
};
|
|
1365
|
+
clears.add(clear);
|
|
1366
|
+
return job;
|
|
1367
|
+
}
|
|
1368
|
+
};
|
|
1286
1369
|
};
|
|
1287
|
-
|
|
1370
|
+
const mainContext = createContext(bots[0]);
|
|
1371
|
+
clears.add(await setup(mainContext) || (() => {}));
|
|
1288
1372
|
runtimePlugins.set(name, {
|
|
1289
1373
|
name,
|
|
1290
1374
|
type,
|
|
@@ -1303,7 +1387,7 @@ async function enablePlugin(bot, plugin, type = "external") {
|
|
|
1303
1387
|
});
|
|
1304
1388
|
const end = node_process.hrtime.bigint();
|
|
1305
1389
|
const time = Math.round(Number(end - start$1)) / 1e6;
|
|
1306
|
-
|
|
1390
|
+
logger$1.info(`- 启用插件 ${consola_utils.colors.yellow(`[${typeDesc}]`)} ${consola_utils.colors.yellow(`${name}@${version$1}`)} => 耗时 ${consola_utils.colors.green(time.toFixed(2))} 毫秒`);
|
|
1307
1391
|
} catch (e) {
|
|
1308
1392
|
throw new Error(`启用插件 ${consola_utils.colors.yellow(`[${typeDesc}]`)} ${consola_utils.colors.yellow(`${name}@${version$1}`)} 失败: ${e?.message}`);
|
|
1309
1393
|
}
|
|
@@ -1329,18 +1413,12 @@ const ArchMap = {
|
|
|
1329
1413
|
arm64: "arm64",
|
|
1330
1414
|
x64: "x64"
|
|
1331
1415
|
};
|
|
1332
|
-
async function getMiokiStatus(
|
|
1416
|
+
async function getMiokiStatus(bots) {
|
|
1333
1417
|
const osType = node_os.default.type();
|
|
1334
1418
|
const osArch = node_os.default.arch();
|
|
1335
1419
|
const isInUnix = ["Linux", "Darwin"].includes(osType);
|
|
1336
1420
|
const arch = ArchMap[osArch] || osArch;
|
|
1337
|
-
const [osInfo, localPlugins
|
|
1338
|
-
systeminformation.default.osInfo(),
|
|
1339
|
-
findLocalPlugins(),
|
|
1340
|
-
bot.getVersionInfo(),
|
|
1341
|
-
bot.getFriendList(),
|
|
1342
|
-
bot.getGroupList()
|
|
1343
|
-
]);
|
|
1421
|
+
const [osInfo, localPlugins] = await Promise.all([systeminformation.default.osInfo(), findLocalPlugins()]);
|
|
1344
1422
|
const pluginCount = localPlugins.length + BUILTIN_PLUGINS.length;
|
|
1345
1423
|
const system = isInUnix ? {
|
|
1346
1424
|
name: osInfo.distro,
|
|
@@ -1354,27 +1432,58 @@ async function getMiokiStatus(bot) {
|
|
|
1354
1432
|
const rssMem = process.memoryUsage.rss();
|
|
1355
1433
|
const nodeVersion = process.versions.node;
|
|
1356
1434
|
const cpu = getCpuInfo();
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1435
|
+
const botStatuses = [];
|
|
1436
|
+
let totalSend = 0;
|
|
1437
|
+
let totalReceive = 0;
|
|
1438
|
+
let mainVersionInfo = {
|
|
1439
|
+
app_version: "unknown",
|
|
1440
|
+
protocol_version: "unknown"
|
|
1441
|
+
};
|
|
1442
|
+
for (const bot of bots) try {
|
|
1443
|
+
const [versionInfo, friendList, groupList] = await Promise.all([
|
|
1444
|
+
bot.getVersionInfo(),
|
|
1445
|
+
bot.getFriendList(),
|
|
1446
|
+
bot.getGroupList()
|
|
1447
|
+
]);
|
|
1448
|
+
mainVersionInfo = versionInfo;
|
|
1449
|
+
botStatuses.push({
|
|
1450
|
+
uin: bot.bot_id,
|
|
1360
1451
|
nickname: bot.nickname,
|
|
1452
|
+
name: bot.name,
|
|
1361
1453
|
friends: friendList.length,
|
|
1362
|
-
groups: groupList.length
|
|
1363
|
-
|
|
1454
|
+
groups: groupList.length,
|
|
1455
|
+
send: bot.stat.send.group + bot.stat.send.private,
|
|
1456
|
+
receive: bot.stat.recv.group + bot.stat.recv.private
|
|
1457
|
+
});
|
|
1458
|
+
totalSend += bot.stat.send.group + bot.stat.send.private;
|
|
1459
|
+
totalReceive += bot.stat.recv.group + bot.stat.recv.private;
|
|
1460
|
+
} catch (err) {
|
|
1461
|
+
botStatuses.push({
|
|
1462
|
+
uin: bot.bot_id,
|
|
1463
|
+
nickname: bot.nickname,
|
|
1464
|
+
name: bot.name,
|
|
1465
|
+
friends: 0,
|
|
1466
|
+
groups: 0,
|
|
1467
|
+
send: 0,
|
|
1468
|
+
receive: 0
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
return {
|
|
1472
|
+
bots: botStatuses,
|
|
1364
1473
|
plugins: {
|
|
1365
1474
|
enabled: runtimePlugins.size,
|
|
1366
1475
|
total: pluginCount
|
|
1367
1476
|
},
|
|
1368
1477
|
stats: {
|
|
1369
1478
|
uptime: process.uptime() * 1e3,
|
|
1370
|
-
send:
|
|
1371
|
-
receive:
|
|
1479
|
+
send: totalSend,
|
|
1480
|
+
receive: totalReceive
|
|
1372
1481
|
},
|
|
1373
1482
|
versions: {
|
|
1374
1483
|
node: nodeVersion,
|
|
1375
1484
|
mioki: require_package.version,
|
|
1376
|
-
napcat:
|
|
1377
|
-
protocol:
|
|
1485
|
+
napcat: mainVersionInfo.app_version,
|
|
1486
|
+
protocol: mainVersionInfo.protocol_version
|
|
1378
1487
|
},
|
|
1379
1488
|
system: {
|
|
1380
1489
|
name: system.name || "N/A",
|
|
@@ -1404,16 +1513,16 @@ async function getMiokiStatus(bot) {
|
|
|
1404
1513
|
};
|
|
1405
1514
|
}
|
|
1406
1515
|
async function formatMiokiStatus(status) {
|
|
1407
|
-
const {
|
|
1516
|
+
const { bots, plugins, stats, system, disk, cpu, memory, versions } = status;
|
|
1408
1517
|
const diskValid = disk.total > 0 && disk.free >= 0;
|
|
1409
1518
|
const diskDesc = `${disk.percent}%-${(0, filesize.filesize)(disk.used, { round: 1 })}/${(0, filesize.filesize)(disk.total, { round: 1 })}`;
|
|
1410
1519
|
return `
|
|
1411
1520
|
〓 🟢 mioki 状态 〓
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1521
|
+
${bots.map((bot, index) => {
|
|
1522
|
+
return `👤 ${bot.name ? `[${bot.name}] ` : ""}${bot.nickname} (${bot.uin})\n 📋 ${localNum(bot.friends)} 好友 / ${localNum(bot.groups)} 群 / 📮 收 ${localNum(bot.receive)} 发 ${localNum(bot.send)}`;
|
|
1523
|
+
}).join("\n")}
|
|
1415
1524
|
🧩 启用了 ${localNum(plugins.enabled)} 个插件,共 ${localNum(plugins.total)} 个
|
|
1416
|
-
📮 收 ${localNum(stats.receive)} 条,发 ${localNum(stats.send)} 条
|
|
1525
|
+
📮 总计: 收 ${localNum(stats.receive)} 条,发 ${localNum(stats.send)} 条
|
|
1417
1526
|
🚀 ${(0, filesize.filesize)(memory.rss.used, { round: 1 })}/${memory.percent}%
|
|
1418
1527
|
⏳ 已运行 ${(0, pretty_ms.default)(stats.uptime, {
|
|
1419
1528
|
hideYear: true,
|
|
@@ -1497,7 +1606,7 @@ const core = definePlugin({
|
|
|
1497
1606
|
const displayPrefix = prefix.replace(/\\\\/g, "\\");
|
|
1498
1607
|
const statusAdminOnly = ctx.botConfig.status_permission === "admin-only";
|
|
1499
1608
|
let statusFormatter = (status) => formatMiokiStatus(status);
|
|
1500
|
-
ctx.addService("getMiokiStatus", () => getMiokiStatus(ctx.
|
|
1609
|
+
ctx.addService("getMiokiStatus", () => getMiokiStatus(ctx.bots));
|
|
1501
1610
|
ctx.addService("formatMiokiStatus", (status) => formatMiokiStatus(status));
|
|
1502
1611
|
ctx.addService("customFormatMiokiStatus", (formatter) => statusFormatter = formatter);
|
|
1503
1612
|
ctx.handle("message", (e) => ctx.runWithErrorHandler(async () => {
|
|
@@ -1505,7 +1614,7 @@ const core = definePlugin({
|
|
|
1505
1614
|
if (!cmdPrefix.test(text$1)) return;
|
|
1506
1615
|
if (statusAdminOnly && !ctx.hasRight(e)) return;
|
|
1507
1616
|
if (text$1.replace(cmdPrefix, "") === "状态") {
|
|
1508
|
-
const status = await statusFormatter(await getMiokiStatus(ctx.
|
|
1617
|
+
const status = await statusFormatter(await getMiokiStatus(ctx.bots));
|
|
1509
1618
|
await e.reply(status);
|
|
1510
1619
|
return;
|
|
1511
1620
|
}
|
|
@@ -1570,10 +1679,10 @@ const core = definePlugin({
|
|
|
1570
1679
|
const plugin = await ctx.jiti.import(pluginPath, { default: true });
|
|
1571
1680
|
if (plugin.name !== target) {
|
|
1572
1681
|
const tip = `[插件目录名称: ${target}] 和插件代码中设置的 [name: ${plugin.name}] 不一致,可能导致重载异常,请修改后重启。`;
|
|
1573
|
-
ctx.
|
|
1682
|
+
ctx.logger.warn(tip);
|
|
1574
1683
|
ctx.noticeMainOwner(tip);
|
|
1575
1684
|
}
|
|
1576
|
-
await enablePlugin(ctx.
|
|
1685
|
+
await enablePlugin(ctx.bots, plugin);
|
|
1577
1686
|
} catch (err) {
|
|
1578
1687
|
await e.reply(`插件 ${target} 启用失败:${err?.message || "未知错误"}`, true);
|
|
1579
1688
|
return;
|
|
@@ -1599,7 +1708,7 @@ const core = definePlugin({
|
|
|
1599
1708
|
break;
|
|
1600
1709
|
}
|
|
1601
1710
|
await ctx.updateBotConfig((c) => c.plugins = ctx.botConfig.plugins.filter((name) => name !== target));
|
|
1602
|
-
ctx.
|
|
1711
|
+
ctx.logger.info(`禁用插件 => ${target}`);
|
|
1603
1712
|
await e.reply(`插件 ${target} 已禁用`, true);
|
|
1604
1713
|
break;
|
|
1605
1714
|
}
|
|
@@ -1621,10 +1730,10 @@ const core = definePlugin({
|
|
|
1621
1730
|
const importedPlugin = await ctx.jiti.import(pluginPath, { default: true });
|
|
1622
1731
|
if (importedPlugin.name !== target) {
|
|
1623
1732
|
const tip = `插件目录名称: ${target} 和插件代码中设置的 name: ${importedPlugin.name} 不一致,可能导致重载异常,请修改后重启。`;
|
|
1624
|
-
ctx.
|
|
1733
|
+
ctx.logger.warn(tip);
|
|
1625
1734
|
ctx.noticeMainOwner(tip);
|
|
1626
1735
|
}
|
|
1627
|
-
await enablePlugin(ctx.
|
|
1736
|
+
await enablePlugin(ctx.bots, importedPlugin);
|
|
1628
1737
|
} catch (err) {
|
|
1629
1738
|
await e.reply(err?.message, true);
|
|
1630
1739
|
await ctx.updateBotConfig((c) => c.plugins = c.plugins.filter((name) => name !== target));
|
|
@@ -1737,7 +1846,7 @@ const core = definePlugin({
|
|
|
1737
1846
|
break;
|
|
1738
1847
|
case "退出":
|
|
1739
1848
|
await e.reply("またね~", true);
|
|
1740
|
-
ctx.
|
|
1849
|
+
ctx.logger.info("接收到退出指令,即将退出... 如需自动重启,请使用 pm2 部署。");
|
|
1741
1850
|
process.exit(0);
|
|
1742
1851
|
}
|
|
1743
1852
|
}, e));
|
|
@@ -1751,6 +1860,120 @@ const BUILTIN_PLUGINS = [core_default];
|
|
|
1751
1860
|
|
|
1752
1861
|
//#endregion
|
|
1753
1862
|
//#region src/start.ts
|
|
1863
|
+
const connectedBots = /* @__PURE__ */ new Map();
|
|
1864
|
+
async function connectBot(config, index) {
|
|
1865
|
+
const { protocol = "ws", port = 3001, host = "localhost", token = "", name } = config;
|
|
1866
|
+
const botName = name || `Bot${index + 1}`;
|
|
1867
|
+
const wsUrl = consola_utils.colors.green(`${protocol}://${host}:${port}${token ? "?access_token=***" : ""}`);
|
|
1868
|
+
logger.info(`>>> 正在连接 ${consola_utils.colors.cyan(botName)}: ${wsUrl}`);
|
|
1869
|
+
const napcat = new napcat_sdk.NapCat({
|
|
1870
|
+
token,
|
|
1871
|
+
protocol,
|
|
1872
|
+
host,
|
|
1873
|
+
port,
|
|
1874
|
+
logger
|
|
1875
|
+
});
|
|
1876
|
+
return new Promise((resolve) => {
|
|
1877
|
+
napcat.on("ws.close", () => {
|
|
1878
|
+
logger.warn(`${consola_utils.colors.yellow(botName)} WS 连接已关闭`);
|
|
1879
|
+
});
|
|
1880
|
+
napcat.on("ws.error", (err) => {
|
|
1881
|
+
logger.error(`${consola_utils.colors.red(botName)} WS 连接错误: ${err}`);
|
|
1882
|
+
});
|
|
1883
|
+
napcat.once("napcat.connected", ({ user_id, nickname, app_name, app_version }) => {
|
|
1884
|
+
logger.info(`已连接到 ${consola_utils.colors.cyan(botName)}: ${consola_utils.colors.green(`${app_name}-v${app_version} ${nickname}(${user_id})`)}`);
|
|
1885
|
+
if (connectedBots.has(user_id)) {
|
|
1886
|
+
const existingBot = connectedBots.get(user_id);
|
|
1887
|
+
if (existingBot.name) logger.warn(`${consola_utils.colors.yellow(botName)} (${user_id}) 与 ${consola_utils.colors.yellow(existingBot.name)} (${user_id}) QQ 号重复,将跳过`);
|
|
1888
|
+
napcat.close();
|
|
1889
|
+
resolve(null);
|
|
1890
|
+
return;
|
|
1891
|
+
}
|
|
1892
|
+
const extendedNapCat = napcat;
|
|
1893
|
+
extendedNapCat.bot_id = user_id;
|
|
1894
|
+
extendedNapCat.app_name = app_name;
|
|
1895
|
+
extendedNapCat.app_version = app_version;
|
|
1896
|
+
extendedNapCat.name = botName;
|
|
1897
|
+
resolve(extendedNapCat);
|
|
1898
|
+
});
|
|
1899
|
+
napcat.run().catch((err) => {
|
|
1900
|
+
logger.error(`${consola_utils.colors.red(botName)} 连接失败: ${err.message}`);
|
|
1901
|
+
resolve(null);
|
|
1902
|
+
});
|
|
1903
|
+
});
|
|
1904
|
+
}
|
|
1905
|
+
async function setupPlugins(napcat, bots) {
|
|
1906
|
+
const plugin_dir = getAbsPluginDir();
|
|
1907
|
+
const mainBot = napcat;
|
|
1908
|
+
ensurePluginDir();
|
|
1909
|
+
const plugins = botConfig.plugins.map((p) => ({
|
|
1910
|
+
dirName: p,
|
|
1911
|
+
absPath: node_path.default.resolve(plugin_dir, p)
|
|
1912
|
+
})).filter((p) => {
|
|
1913
|
+
if (!node_fs.default.existsSync(p.absPath)) {
|
|
1914
|
+
mainBot.logger.warn(`插件 ${consola_utils.colors.red(p.dirName)} 不存在,已忽略`);
|
|
1915
|
+
return false;
|
|
1916
|
+
}
|
|
1917
|
+
return true;
|
|
1918
|
+
});
|
|
1919
|
+
const failedImportPlugins = [];
|
|
1920
|
+
const promises = plugins.map(async ({ absPath, dirName }) => {
|
|
1921
|
+
try {
|
|
1922
|
+
const plugin = await jiti$1.import(absPath, { default: true });
|
|
1923
|
+
if (plugin.name !== dirName) {
|
|
1924
|
+
const tip = `插件目录名 [${consola_utils.colors.yellow(dirName)}] 和插件声明的 name [${consola_utils.colors.yellow(plugin.name)}] 不一致,可能导致重载异常,请修改一致后重启。`;
|
|
1925
|
+
mainBot.logger.warn(tip);
|
|
1926
|
+
noticeMainOwner(mainBot, tip);
|
|
1927
|
+
}
|
|
1928
|
+
return plugin;
|
|
1929
|
+
} catch (e) {
|
|
1930
|
+
const err = stringifyError(e);
|
|
1931
|
+
failedImportPlugins.push([dirName, err]);
|
|
1932
|
+
return null;
|
|
1933
|
+
}
|
|
1934
|
+
});
|
|
1935
|
+
const start$1 = node_process.hrtime.bigint();
|
|
1936
|
+
const sortedUserPlugins = (await Promise.all(promises)).filter(Boolean).toSorted((prev, next) => (prev.priority ?? 100) - (next.priority ?? 100));
|
|
1937
|
+
if (failedImportPlugins.length) {
|
|
1938
|
+
const tip = `${consola_utils.colors.red(failedImportPlugins.length)} 个插件加载失败: \n\n${failedImportPlugins.map(([dirName, err]) => `${dirName}: ${err}`).join("\n\n")}`;
|
|
1939
|
+
mainBot.logger.warn(tip);
|
|
1940
|
+
noticeMainOwner(mainBot, tip);
|
|
1941
|
+
}
|
|
1942
|
+
const pluginGroups = /* @__PURE__ */ new Map();
|
|
1943
|
+
for (const plugin of sortedUserPlugins) {
|
|
1944
|
+
const priority = plugin.priority ?? 100;
|
|
1945
|
+
if (!pluginGroups.has(priority)) pluginGroups.set(priority, []);
|
|
1946
|
+
pluginGroups.get(priority).push(plugin);
|
|
1947
|
+
}
|
|
1948
|
+
const sortedGroups = Array.from(pluginGroups.entries()).toSorted(([a], [b]) => a - b);
|
|
1949
|
+
const failedEnablePlugins = [];
|
|
1950
|
+
try {
|
|
1951
|
+
mainBot.logger.info(`>>> 加载内置插件: ${BUILTIN_PLUGINS.map((p) => consola_utils.colors.cyan(p.name)).join(", ")}`);
|
|
1952
|
+
await Promise.all(BUILTIN_PLUGINS.map((p) => enablePlugin(bots, p, "builtin")));
|
|
1953
|
+
mainBot.logger.info(`>>> 加载用户插件: ${sortedGroups.map(([priority, plugins$1]) => `优先级 ${consola_utils.colors.yellow(priority)} (${plugins$1.map((p) => consola_utils.colors.cyan(p.name)).join(", ")})`).join(",")}`);
|
|
1954
|
+
for (const [_, plugins$1] of sortedGroups) await Promise.all(plugins$1.map(async (p) => {
|
|
1955
|
+
try {
|
|
1956
|
+
await enablePlugin(bots, p, "external");
|
|
1957
|
+
} catch (e) {
|
|
1958
|
+
failedEnablePlugins.push([p.name, stringifyError(e)]);
|
|
1959
|
+
}
|
|
1960
|
+
}));
|
|
1961
|
+
} catch (e) {
|
|
1962
|
+
mainBot.logger.error(e?.message);
|
|
1963
|
+
await noticeMainOwner(mainBot, e?.message).catch(() => {
|
|
1964
|
+
mainBot.logger.error("发送插件启用失败通知失败");
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
const end = node_process.hrtime.bigint();
|
|
1968
|
+
const costTime = Math.round(Number(end - start$1)) / 1e6;
|
|
1969
|
+
const failedCount = failedImportPlugins.length + failedEnablePlugins.length;
|
|
1970
|
+
const failedInfo = failedCount > 0 ? `${consola_utils.colors.red(failedCount)} 个失败 (导入 ${consola_utils.colors.red(failedImportPlugins.length)},启用 ${consola_utils.colors.red(failedEnablePlugins.length)})` : "";
|
|
1971
|
+
mainBot.logger.info(`成功加载了 ${consola_utils.colors.green(runtimePlugins.size)} 个插件,${failedInfo ? failedInfo : ""}总耗时 ${consola_utils.colors.green(costTime.toFixed(2))} 毫秒`);
|
|
1972
|
+
mainBot.logger.info(consola_utils.colors.green(`mioki v${require_package.version} 启动完成,向机器人发送「${consola_utils.colors.magentaBright(`${botConfig.prefix}帮助`)}」查看消息指令`));
|
|
1973
|
+
if (botConfig.online_push) await noticeMainOwner(mainBot, `✅ mioki v${require_package.version} 已就绪`).catch((err) => {
|
|
1974
|
+
mainBot.logger.error(`发送就绪通知失败: ${stringifyError(err)}`);
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1754
1977
|
async function start(options = {}) {
|
|
1755
1978
|
const { cwd = process.cwd() } = options;
|
|
1756
1979
|
if (cwd !== BOT_CWD.value) updateBotCWD(node_path.default.resolve(cwd));
|
|
@@ -1769,117 +1992,67 @@ async function start(options = {}) {
|
|
|
1769
1992
|
logger.info(`${consola_utils.colors.dim("插件目录: ")}${consola_utils.colors.blue(plugin_dir)}`);
|
|
1770
1993
|
logger.info(`${consola_utils.colors.dim("配置文件: ")}${consola_utils.colors.blue(`${BOT_CWD.value}/package.json`)}`);
|
|
1771
1994
|
logger.info(consola_utils.colors.dim("=".repeat(40)));
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1995
|
+
const napcatConfigs = botConfig.napcat;
|
|
1996
|
+
if (napcatConfigs.length === 0) {
|
|
1997
|
+
logger.warn("未配置任何 NapCat 实例,框架将以无实例模式启动");
|
|
1998
|
+
logger.info(consola_utils.colors.green(`mioki v${require_package.version} 启动完成,向机器人发送「${consola_utils.colors.magentaBright(`${botConfig.prefix}帮助`)}」查看消息指令`));
|
|
1999
|
+
return;
|
|
2000
|
+
}
|
|
2001
|
+
const seenEndpoints = /* @__PURE__ */ new Set();
|
|
2002
|
+
const duplicateConfigs = [];
|
|
2003
|
+
for (const config of napcatConfigs) {
|
|
2004
|
+
const { protocol = "ws", host = "localhost", port = 3001 } = config;
|
|
2005
|
+
const endpoint = `${protocol}://${host}:${port}`;
|
|
2006
|
+
if (seenEndpoints.has(endpoint)) duplicateConfigs.push(`${config.name || "未命名"} (${endpoint})`);
|
|
2007
|
+
else seenEndpoints.add(endpoint);
|
|
2008
|
+
}
|
|
2009
|
+
if (duplicateConfigs.length > 0) {
|
|
2010
|
+
logger.error(`检测到重复的 NapCat 实例配置:`);
|
|
2011
|
+
duplicateConfigs.forEach((dup) => logger.error(` - ${dup}`));
|
|
2012
|
+
logger.error("请检查配置文件,确保每个实例的 host:port 组合唯一");
|
|
1784
2013
|
process.exit(1);
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
2014
|
+
}
|
|
2015
|
+
logger.info(consola_utils.colors.dim("=".repeat(40)));
|
|
2016
|
+
logger.info(`>>> 正在连接 ${napcatConfigs.length} 个 NapCat 实例...`);
|
|
2017
|
+
const bots = (await Promise.all(napcatConfigs.map((config, index) => connectBot(config, index)))).filter((b) => b !== null);
|
|
2018
|
+
if (bots.length === 0) {
|
|
2019
|
+
logger.error("所有 NapCat 实例连接失败,框架无法启动");
|
|
2020
|
+
process.exit(1);
|
|
2021
|
+
}
|
|
2022
|
+
for (const bot of bots) connectedBots.set(bot.bot_id, bot);
|
|
2023
|
+
if (bots.length < napcatConfigs.length) logger.warn(`${consola_utils.colors.yellow(napcatConfigs.length - bots.length)} 个 NapCat 实例连接失败`);
|
|
2024
|
+
const botNames = bots.map((b) => `${b.name}(${b.bot_id})`).join(", ");
|
|
2025
|
+
logger.info(consola_utils.colors.green(`成功连接 ${bots.length} 个实例: ${botNames}`));
|
|
2026
|
+
logger.info(consola_utils.colors.dim("=".repeat(40)));
|
|
2027
|
+
const mainBot = bots[0];
|
|
2028
|
+
process.title = `mioki v${require_package.version} ${bots.map((b) => `${b.bot_id}`).join(", ")}`;
|
|
2029
|
+
let lastNoticeTime = 0;
|
|
2030
|
+
for (const bot of bots) {
|
|
1790
2031
|
process.on("uncaughtException", async (err) => {
|
|
1791
2032
|
const msg = stringifyError(err);
|
|
1792
|
-
|
|
2033
|
+
bot.logger.error(`uncaughtException, 出错了: ${msg}`);
|
|
1793
2034
|
if (botConfig.error_push) {
|
|
1794
2035
|
if (Date.now() - lastNoticeTime < 1e3) return;
|
|
1795
2036
|
lastNoticeTime = Date.now();
|
|
1796
|
-
await noticeMainOwner(
|
|
1797
|
-
|
|
2037
|
+
await noticeMainOwner(mainBot, `mioki 发生未捕获异常:\n\n${msg}`).catch(() => {
|
|
2038
|
+
mainBot.logger.error("发送未捕获异常通知失败");
|
|
1798
2039
|
});
|
|
1799
2040
|
}
|
|
1800
2041
|
});
|
|
1801
2042
|
process.on("unhandledRejection", async (err) => {
|
|
1802
2043
|
const msg = stringifyError(err);
|
|
1803
|
-
|
|
2044
|
+
bot.logger.error(`unhandledRejection, 出错了: ${msg}`);
|
|
1804
2045
|
if (botConfig.error_push) {
|
|
1805
2046
|
if (Date.now() - lastNoticeTime < 1e3) return;
|
|
1806
2047
|
lastNoticeTime = Date.now();
|
|
1807
2048
|
const date = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
1808
|
-
await noticeMainOwner(
|
|
1809
|
-
|
|
2049
|
+
await noticeMainOwner(mainBot, `【${date}】\n\nmioki 发生未处理异常:\n\n${msg}`).catch(() => {
|
|
2050
|
+
mainBot.logger.error("发送未处理异常通知失败");
|
|
1810
2051
|
});
|
|
1811
2052
|
}
|
|
1812
2053
|
});
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
dirName: p,
|
|
1816
|
-
absPath: node_path.default.resolve(plugin_dir, p)
|
|
1817
|
-
})).filter((p) => {
|
|
1818
|
-
if (!node_fs.default.existsSync(p.absPath)) {
|
|
1819
|
-
napcat.logger.warn(`插件 ${consola_utils.colors.red(p.dirName)} 不存在,已忽略`);
|
|
1820
|
-
return false;
|
|
1821
|
-
}
|
|
1822
|
-
return true;
|
|
1823
|
-
});
|
|
1824
|
-
const failedImportPlugins = [];
|
|
1825
|
-
const promises = plugins.map(async ({ absPath, dirName }) => {
|
|
1826
|
-
try {
|
|
1827
|
-
const plugin = await jiti$1.import(absPath, { default: true });
|
|
1828
|
-
if (plugin.name !== dirName) {
|
|
1829
|
-
const tip = `插件目录名 [${consola_utils.colors.yellow(dirName)}] 和插件声明的 name [${consola_utils.colors.yellow(plugin.name)}] 不一致,可能导致重载异常,请修改一致后重启。`;
|
|
1830
|
-
napcat.logger.warn(tip);
|
|
1831
|
-
noticeMainOwner(napcat, tip);
|
|
1832
|
-
}
|
|
1833
|
-
return plugin;
|
|
1834
|
-
} catch (e) {
|
|
1835
|
-
const err = stringifyError(e);
|
|
1836
|
-
failedImportPlugins.push([dirName, err]);
|
|
1837
|
-
return null;
|
|
1838
|
-
}
|
|
1839
|
-
});
|
|
1840
|
-
const start$1 = node_process.hrtime.bigint();
|
|
1841
|
-
const sortedUserPlugins = (await Promise.all(promises)).filter(Boolean).toSorted((prev, next) => (prev.priority ?? 100) - (next.priority ?? 100));
|
|
1842
|
-
if (failedImportPlugins.length) {
|
|
1843
|
-
const tip = `${consola_utils.colors.red(failedImportPlugins.length)} 个插件加载失败: \n\n${failedImportPlugins.map(([dirName, err]) => `${dirName}: ${err}`).join("\n\n")}`;
|
|
1844
|
-
napcat.logger.warn(tip);
|
|
1845
|
-
noticeMainOwner(napcat, tip);
|
|
1846
|
-
}
|
|
1847
|
-
const pluginGroups = /* @__PURE__ */ new Map();
|
|
1848
|
-
for (const plugin of sortedUserPlugins) {
|
|
1849
|
-
const priority = plugin.priority ?? 100;
|
|
1850
|
-
if (!pluginGroups.has(priority)) pluginGroups.set(priority, []);
|
|
1851
|
-
pluginGroups.get(priority).push(plugin);
|
|
1852
|
-
}
|
|
1853
|
-
const sortedGroups = Array.from(pluginGroups.entries()).toSorted(([a], [b]) => a - b);
|
|
1854
|
-
const failedEnablePlugins = [];
|
|
1855
|
-
try {
|
|
1856
|
-
napcat.logger.info(`>>> 加载内置插件: ${BUILTIN_PLUGINS.map((p) => consola_utils.colors.cyan(p.name)).join(", ")}`);
|
|
1857
|
-
await Promise.all(BUILTIN_PLUGINS.map((p) => enablePlugin(napcat, p, "builtin")));
|
|
1858
|
-
napcat.logger.info(`>>> 加载用户插件: ${sortedGroups.map(([priority, plugins$1]) => `优先级 ${consola_utils.colors.yellow(priority)} (${plugins$1.map((p) => consola_utils.colors.cyan(p.name)).join(", ")})`).join(",")}`);
|
|
1859
|
-
for (const [_, plugins$1] of sortedGroups) await Promise.all(plugins$1.map(async (p) => {
|
|
1860
|
-
try {
|
|
1861
|
-
await enablePlugin(napcat, p, "external");
|
|
1862
|
-
} catch (e) {
|
|
1863
|
-
failedEnablePlugins.push([p.name, stringifyError(e)]);
|
|
1864
|
-
}
|
|
1865
|
-
}));
|
|
1866
|
-
} catch (e) {
|
|
1867
|
-
napcat.logger.error(e?.message);
|
|
1868
|
-
await noticeMainOwner(napcat, e?.message).catch(() => {
|
|
1869
|
-
napcat.logger.error("发送插件启用失败通知失败");
|
|
1870
|
-
});
|
|
1871
|
-
}
|
|
1872
|
-
const end = node_process.hrtime.bigint();
|
|
1873
|
-
const costTime = Math.round(Number(end - start$1)) / 1e6;
|
|
1874
|
-
const failedCount = failedImportPlugins.length + failedEnablePlugins.length;
|
|
1875
|
-
const failedInfo = failedCount > 0 ? `${consola_utils.colors.red(failedCount)} 个失败 (导入 ${consola_utils.colors.red(failedImportPlugins.length)},启用 ${consola_utils.colors.red(failedEnablePlugins.length)})` : "";
|
|
1876
|
-
napcat.logger.info(`成功加载了 ${consola_utils.colors.green(runtimePlugins.size)} 个插件,${failedInfo ? failedInfo : ""}总耗时 ${consola_utils.colors.green(costTime.toFixed(2))} 毫秒`);
|
|
1877
|
-
napcat.logger.info(consola_utils.colors.green(`mioki v${require_package.version} 启动完成,向机器人发送「${consola_utils.colors.magentaBright(`${botConfig.prefix}帮助`)}」查看消息指令`));
|
|
1878
|
-
if (botConfig.online_push) await noticeMainOwner(napcat, `✅ mioki v${require_package.version} 已就绪`).catch((err) => {
|
|
1879
|
-
napcat.logger.error(`发送就绪通知失败: ${stringifyError(err)}`);
|
|
1880
|
-
});
|
|
1881
|
-
});
|
|
1882
|
-
await napcat.run();
|
|
2054
|
+
}
|
|
2055
|
+
await setupPlugins(mainBot, bots);
|
|
1883
2056
|
}
|
|
1884
2057
|
|
|
1885
2058
|
//#endregion
|
|
@@ -1888,6 +2061,7 @@ exports.BOT_CWD = BOT_CWD;
|
|
|
1888
2061
|
exports.BUILTIN_PLUGINS = BUILTIN_PLUGINS;
|
|
1889
2062
|
exports.CORE_PLUGINS = CORE_PLUGINS;
|
|
1890
2063
|
exports.ChromeUA = ChromeUA;
|
|
2064
|
+
exports.MessageDeduplicator = MessageDeduplicator;
|
|
1891
2065
|
exports.START_TIME = START_TIME;
|
|
1892
2066
|
exports.SystemMap = SystemMap;
|
|
1893
2067
|
exports.addService = addService;
|
|
@@ -1914,6 +2088,7 @@ Object.defineProperty(exports, 'colors', {
|
|
|
1914
2088
|
return consola_utils.colors;
|
|
1915
2089
|
}
|
|
1916
2090
|
});
|
|
2091
|
+
exports.connectedBots = connectedBots;
|
|
1917
2092
|
exports.createCmd = createCmd;
|
|
1918
2093
|
exports.createDB = createDB;
|
|
1919
2094
|
exports.createForwardMsg = createForwardMsg;
|
|
@@ -1930,6 +2105,7 @@ Object.defineProperty(exports, 'dedent', {
|
|
|
1930
2105
|
return dedent.default;
|
|
1931
2106
|
}
|
|
1932
2107
|
});
|
|
2108
|
+
exports.deduplicator = deduplicator;
|
|
1933
2109
|
exports.definePlugin = definePlugin;
|
|
1934
2110
|
exports.enablePlugin = enablePlugin;
|
|
1935
2111
|
exports.ensureBuffer = ensureBuffer;
|
|
@@ -2000,6 +2176,7 @@ Object.defineProperty(exports, 'mri', {
|
|
|
2000
2176
|
}
|
|
2001
2177
|
});
|
|
2002
2178
|
exports.noNullish = noNullish;
|
|
2179
|
+
exports.normalizeNapCatConfig = normalizeNapCatConfig;
|
|
2003
2180
|
exports.noticeAdmins = noticeAdmins;
|
|
2004
2181
|
exports.noticeFriends = noticeFriends;
|
|
2005
2182
|
exports.noticeGroups = noticeGroups;
|