mioku 1.0.8 → 1.0.9
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/index.cjs +3 -4
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +3 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/{exec-BzdYBQLQ.cjs → exec-BUUBQy_V.cjs} +2 -2
- package/dist/{exec-BzdYBQLQ.cjs.map → exec-BUUBQy_V.cjs.map} +1 -1
- package/dist/{exec-3C9aJ9OX.js → exec-C3QTbHLt.js} +2 -2
- package/dist/{exec-3C9aJ9OX.js.map → exec-C3QTbHLt.js.map} +1 -1
- package/dist/index.cjs +860 -307
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +131 -13
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +131 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +854 -308
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildSpawnPlan, commandExists, resolveCommand, runCommand, runCommandInherit, version } from "./exec-
|
|
1
|
+
import { buildSpawnPlan, commandExists, resolveCommand, runCommand, runCommandInherit, version } from "./exec-C3QTbHLt.js";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as fs$5 from "node:fs";
|
|
4
4
|
import * as fs$4 from "node:fs";
|
|
@@ -871,6 +871,7 @@ var AdapterContextImpl = class {
|
|
|
871
871
|
#logger;
|
|
872
872
|
#emit;
|
|
873
873
|
#correlator;
|
|
874
|
+
#commands;
|
|
874
875
|
#pendingStarts = new Set();
|
|
875
876
|
constructor(options) {
|
|
876
877
|
this.#state = options.state;
|
|
@@ -881,6 +882,7 @@ var AdapterContextImpl = class {
|
|
|
881
882
|
this.#logger = options.logger;
|
|
882
883
|
this.#emit = options.emit;
|
|
883
884
|
this.#correlator = options.correlator;
|
|
885
|
+
this.#commands = options.commands;
|
|
884
886
|
}
|
|
885
887
|
registerBot(bot) {
|
|
886
888
|
return this.#bots.register(bot);
|
|
@@ -955,6 +957,12 @@ var AdapterContextImpl = class {
|
|
|
955
957
|
}
|
|
956
958
|
}
|
|
957
959
|
}
|
|
960
|
+
if (event.kind === "message") {
|
|
961
|
+
const duplicate = this.#correlator?.observationOf(event)?.duplicate ?? false;
|
|
962
|
+
if (!duplicate) {
|
|
963
|
+
if (await this.#commands.dispatch(event)) return;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
958
966
|
await this.#bus.dispatch(event);
|
|
959
967
|
}
|
|
960
968
|
listen(route, handler) {
|
|
@@ -1070,9 +1078,13 @@ var EventBus = class {
|
|
|
1070
1078
|
#nextOrder = 0;
|
|
1071
1079
|
#dispatched = 0;
|
|
1072
1080
|
#logger = null;
|
|
1081
|
+
#filter = null;
|
|
1073
1082
|
setLogger(logger) {
|
|
1074
1083
|
this.#logger = logger;
|
|
1075
1084
|
}
|
|
1085
|
+
setFilter(filter$1) {
|
|
1086
|
+
this.#filter = filter$1;
|
|
1087
|
+
}
|
|
1076
1088
|
/** 注册事件监听,返回取消注册函数 */
|
|
1077
1089
|
register(route, handler, options = {}) {
|
|
1078
1090
|
if (!route) throw new Error("EventBus.register: route must be a non-empty string");
|
|
@@ -1106,6 +1118,11 @@ var EventBus = class {
|
|
|
1106
1118
|
const matched = [];
|
|
1107
1119
|
for (const reg of this.#registrations) {
|
|
1108
1120
|
if (reg.disposed) continue;
|
|
1121
|
+
if (this.#filter) try {
|
|
1122
|
+
if (!this.#filter(reg, event)) continue;
|
|
1123
|
+
} catch (err) {
|
|
1124
|
+
this.#logger?.("error", `Event registration filter failed for "${reg.source}"`, err);
|
|
1125
|
+
}
|
|
1109
1126
|
if (isWildcardMatch(reg.route, event.type)) {
|
|
1110
1127
|
matched.push(reg);
|
|
1111
1128
|
continue;
|
|
@@ -1144,6 +1161,57 @@ var EventBus = class {
|
|
|
1144
1161
|
}
|
|
1145
1162
|
};
|
|
1146
1163
|
|
|
1164
|
+
//#endregion
|
|
1165
|
+
//#region src/runtime/permissions.ts
|
|
1166
|
+
const isObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1167
|
+
/** 从事件对象中提取发送者 id(支持裸 id / user_id / sender.user_id) */
|
|
1168
|
+
const toUserId = (value) => {
|
|
1169
|
+
if (typeof value === "number" || typeof value === "string" || typeof value === "bigint") return String(value);
|
|
1170
|
+
if (isObject$1(value)) {
|
|
1171
|
+
if ("user_id" in value) return toUserId(value.user_id);
|
|
1172
|
+
if ("sender" in value) {
|
|
1173
|
+
const sender = value.sender;
|
|
1174
|
+
if (isObject$1(sender) && "user_id" in sender) return toUserId(sender.user_id);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
return void 0;
|
|
1178
|
+
};
|
|
1179
|
+
/** 是否群消息 */
|
|
1180
|
+
const isGroupMessage = (event) => isObject$1(event) && event.kind === "message" && event.message_type === "group";
|
|
1181
|
+
/** 当前群群主(仅限所在群) */
|
|
1182
|
+
const isEventGroupOwner = (event) => {
|
|
1183
|
+
if (!isGroupMessage(event)) return false;
|
|
1184
|
+
return event.sender?.role === "owner";
|
|
1185
|
+
};
|
|
1186
|
+
/** 当前群群主或群管理员(仅限所在群) */
|
|
1187
|
+
const isEventGroupAdmin = (event) => {
|
|
1188
|
+
if (!isGroupMessage(event)) return false;
|
|
1189
|
+
const role = event.sender?.role;
|
|
1190
|
+
return role === "owner" || role === "admin";
|
|
1191
|
+
};
|
|
1192
|
+
/** 仅看 owners 名单:bot 主人(无群身份加成) */
|
|
1193
|
+
const isEventMaster$1 = (event) => {
|
|
1194
|
+
const id = toUserId(event);
|
|
1195
|
+
if (!id) return false;
|
|
1196
|
+
return isOwner(id);
|
|
1197
|
+
};
|
|
1198
|
+
/** 仅看 admins 名单:配置管理员 */
|
|
1199
|
+
const isEventAdminConfigOnly = (event) => {
|
|
1200
|
+
const id = toUserId(event);
|
|
1201
|
+
if (!id) return false;
|
|
1202
|
+
return isAdmin(id);
|
|
1203
|
+
};
|
|
1204
|
+
/**
|
|
1205
|
+
* master 或当前群群主
|
|
1206
|
+
*/
|
|
1207
|
+
const isEventOwner$1 = (event) => isEventMaster$1(event) || isEventGroupOwner(event);
|
|
1208
|
+
/**
|
|
1209
|
+
* master、配置管理员、当前群群主或群管理员
|
|
1210
|
+
*/
|
|
1211
|
+
const isEventAdmin$1 = (event) => isEventOwner$1(event) || isEventAdminConfigOnly(event) || isEventGroupAdmin(event);
|
|
1212
|
+
const isEventOwnerOrAdmin$1 = (event) => isEventAdmin$1(event);
|
|
1213
|
+
const hasEventRight$1 = (event) => isEventOwnerOrAdmin$1(event);
|
|
1214
|
+
|
|
1147
1215
|
//#endregion
|
|
1148
1216
|
//#region src/utils.ts
|
|
1149
1217
|
const ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/131.0.0.0";
|
|
@@ -1911,31 +1979,18 @@ const Services = {
|
|
|
1911
1979
|
|
|
1912
1980
|
//#endregion
|
|
1913
1981
|
//#region src/runtime/mioku-context.ts
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
};
|
|
1927
|
-
const isEventOwner = (event) => {
|
|
1928
|
-
const id = toUserId(event);
|
|
1929
|
-
if (!id) return false;
|
|
1930
|
-
return isOwner(id);
|
|
1931
|
-
};
|
|
1932
|
-
const isEventAdmin = (event) => {
|
|
1933
|
-
const id = toUserId(event);
|
|
1934
|
-
if (!id) return false;
|
|
1935
|
-
return isAdmin(id);
|
|
1936
|
-
};
|
|
1937
|
-
const isEventOwnerOrAdmin = (event) => isEventOwner(event) || isEventAdmin(event);
|
|
1938
|
-
const hasEventRight = (event) => isEventOwnerOrAdmin(event);
|
|
1982
|
+
/** 仅看 owners 名单:bot 主人(无群身份加成) */
|
|
1983
|
+
const isEventMaster = (event) => isEventMaster$1(event);
|
|
1984
|
+
/**
|
|
1985
|
+
* master 或当前群群主。等价于 `ctx.command({ permission: "owner" })` 的判断。
|
|
1986
|
+
*/
|
|
1987
|
+
const isEventOwner = (event) => isEventOwner$1(event);
|
|
1988
|
+
/**
|
|
1989
|
+
* master、配置管理员、当前群群主或群管理员。等价于 `ctx.command({ permission: "admin" })` 的判断。
|
|
1990
|
+
*/
|
|
1991
|
+
const isEventAdmin = (event) => isEventAdmin$1(event);
|
|
1992
|
+
const isEventOwnerOrAdmin = (event) => isEventOwnerOrAdmin$1(event);
|
|
1993
|
+
const hasEventRight = (event) => hasEventRight$1(event);
|
|
1939
1994
|
const CTX_UTILS = {
|
|
1940
1995
|
localeDate,
|
|
1941
1996
|
localeTime,
|
|
@@ -1990,6 +2045,9 @@ var MiokuContext = class {
|
|
|
1990
2045
|
get pluginName() {
|
|
1991
2046
|
return this.#options.pluginName;
|
|
1992
2047
|
}
|
|
2048
|
+
get pluginId() {
|
|
2049
|
+
return this.#options.pluginId;
|
|
2050
|
+
}
|
|
1993
2051
|
/** 第一个已连接的 bot(多数单 bot 场景直接用这个) */
|
|
1994
2052
|
get bot() {
|
|
1995
2053
|
return this.#options.bots.all()[0];
|
|
@@ -2089,7 +2147,7 @@ var MiokuContext = class {
|
|
|
2089
2147
|
const inputRoutes = Array.isArray(route) ? route : [route];
|
|
2090
2148
|
const bypassDedup = inputRoutes.some((item) => item.startsWith("!"));
|
|
2091
2149
|
const routes = inputRoutes.map((item) => item.startsWith("!") ? item.slice(1) : item);
|
|
2092
|
-
const source = `plugin:${this.#options.
|
|
2150
|
+
const source = `plugin:${this.#options.pluginId}`;
|
|
2093
2151
|
const handledEvents = new WeakSet();
|
|
2094
2152
|
const correlator = bypassDedup ? void 0 : this.#options.correlator;
|
|
2095
2153
|
const wrappedHandler = async (event) => {
|
|
@@ -2111,6 +2169,17 @@ var MiokuContext = class {
|
|
|
2111
2169
|
this.#addCleanup(dispose);
|
|
2112
2170
|
return dispose;
|
|
2113
2171
|
}
|
|
2172
|
+
command(definitionOrMatcher, handler, options = {}) {
|
|
2173
|
+
const dispose = typeof definitionOrMatcher === "object" && definitionOrMatcher !== null && "name" in definitionOrMatcher ? this.#options.commands.register(this.#options.pluginId, {
|
|
2174
|
+
...definitionOrMatcher,
|
|
2175
|
+
priority: definitionOrMatcher.priority ?? this.#options.priority
|
|
2176
|
+
}, this) : this.#options.commands.register(this.#options.pluginId, definitionOrMatcher, handler, {
|
|
2177
|
+
...options,
|
|
2178
|
+
priority: options.priority ?? this.#options.priority
|
|
2179
|
+
}, this);
|
|
2180
|
+
this.#addCleanup(dispose);
|
|
2181
|
+
return dispose;
|
|
2182
|
+
}
|
|
2114
2183
|
/** 注册 cron 定时任务,插件卸载时自动停止 */
|
|
2115
2184
|
cron(expression, handler) {
|
|
2116
2185
|
const task = nodeCron.schedule(expression, async (taskContext) => {
|
|
@@ -2167,6 +2236,10 @@ var MiokuContext = class {
|
|
|
2167
2236
|
get buses() {
|
|
2168
2237
|
return this.#options.bus;
|
|
2169
2238
|
}
|
|
2239
|
+
/** 当前运行时的命令管理器 */
|
|
2240
|
+
get commands() {
|
|
2241
|
+
return this.#options.commands;
|
|
2242
|
+
}
|
|
2170
2243
|
get segment() {
|
|
2171
2244
|
return segment;
|
|
2172
2245
|
}
|
|
@@ -2207,24 +2280,30 @@ var MiokuContext = class {
|
|
|
2207
2280
|
return noticeAdmins(this.#options.bots.all(), this.#options.config.admins, message, options);
|
|
2208
2281
|
}
|
|
2209
2282
|
isGroupMsg(event) {
|
|
2210
|
-
return isObject
|
|
2283
|
+
return isObject(event) && event.kind === "message" && event.message_type === "group";
|
|
2211
2284
|
}
|
|
2212
2285
|
isPrivateMsg(event) {
|
|
2213
|
-
return isObject
|
|
2286
|
+
return isObject(event) && event.kind === "message" && event.message_type === "private";
|
|
2214
2287
|
}
|
|
2215
|
-
/**
|
|
2288
|
+
/** 事件发送者是否为 bot 主人(仅看 owners 名单,不识别群身份) */
|
|
2289
|
+
isMaster(event) {
|
|
2290
|
+
return isEventMaster(event);
|
|
2291
|
+
}
|
|
2292
|
+
/** 事件发送者是否为 bot 主人或当前群群主;等价于 `permission: "owner"` */
|
|
2216
2293
|
isOwner(event) {
|
|
2217
2294
|
return isEventOwner(event);
|
|
2218
2295
|
}
|
|
2219
|
-
/**
|
|
2296
|
+
/** 事件发送者是否为 bot 主人、配置管理员、当前群群主或群管理员;等价于 `permission: "admin"` */
|
|
2220
2297
|
isAdmin(event) {
|
|
2221
2298
|
return isEventAdmin(event);
|
|
2222
2299
|
}
|
|
2300
|
+
/** 同 `isAdmin`,保留旧名称 */
|
|
2223
2301
|
isOwnerOrAdmin(event) {
|
|
2224
|
-
return
|
|
2302
|
+
return isEventAdmin(event);
|
|
2225
2303
|
}
|
|
2304
|
+
/** 同 `isAdmin`,别名 */
|
|
2226
2305
|
hasRight(event) {
|
|
2227
|
-
return
|
|
2306
|
+
return isEventAdmin(event);
|
|
2228
2307
|
}
|
|
2229
2308
|
async dispose() {
|
|
2230
2309
|
const all = Array.from(this.#cleanup);
|
|
@@ -2515,19 +2594,387 @@ var EventCorrelator = class {
|
|
|
2515
2594
|
}
|
|
2516
2595
|
};
|
|
2517
2596
|
|
|
2597
|
+
//#endregion
|
|
2598
|
+
//#region src/runtime/commands.ts
|
|
2599
|
+
const normalizeRole = (role) => {
|
|
2600
|
+
const value = String(role ?? "member").trim().toLowerCase();
|
|
2601
|
+
if (value === "master" || value === "owner" || value === "admin") return value;
|
|
2602
|
+
return "member";
|
|
2603
|
+
};
|
|
2604
|
+
const userIdOf = (event) => toUserId(event);
|
|
2605
|
+
const textHookMatches = (text$1, raw) => {
|
|
2606
|
+
const source = String(raw ?? "").trim();
|
|
2607
|
+
if (!source) return false;
|
|
2608
|
+
if (source.startsWith("/") && source.length > 1) {
|
|
2609
|
+
const end = source.lastIndexOf("/");
|
|
2610
|
+
if (end > 0) try {
|
|
2611
|
+
const flags = source.slice(end + 1);
|
|
2612
|
+
const regex = new RegExp(source.slice(1, end), flags);
|
|
2613
|
+
return regex.test(text$1);
|
|
2614
|
+
} catch {
|
|
2615
|
+
return false;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
return text$1 === source || text$1.startsWith(source);
|
|
2619
|
+
};
|
|
2620
|
+
const routeMatches = (event, route) => {
|
|
2621
|
+
if (!route) return false;
|
|
2622
|
+
return event.type === route || event.routes.some((item) => item === route);
|
|
2623
|
+
};
|
|
2624
|
+
const parsePrefixes = (prefixes, defaultPrefix) => {
|
|
2625
|
+
if (prefixes === false) return ["", defaultPrefix];
|
|
2626
|
+
const values = Array.isArray(prefixes) ? prefixes : [prefixes];
|
|
2627
|
+
const normalized = values.map((value) => String(value)).filter((value, index, all) => all.indexOf(value) === index);
|
|
2628
|
+
return normalized.length > 0 ? normalized : [defaultPrefix];
|
|
2629
|
+
};
|
|
2630
|
+
const commandBody = (text$1, prefixes) => {
|
|
2631
|
+
const prefix = [...prefixes].sort((a, b) => b.length - a.length).find((item) => item === "" || text$1.startsWith(item));
|
|
2632
|
+
if (prefix === void 0) return void 0;
|
|
2633
|
+
return text$1.slice(prefix.length).trimStart();
|
|
2634
|
+
};
|
|
2635
|
+
const stringMatch = (body, matcher) => {
|
|
2636
|
+
const value = matcher.trim();
|
|
2637
|
+
if (!value) return void 0;
|
|
2638
|
+
if (body !== value && !(body.startsWith(value) && /\s/.test(body[value.length] ?? ""))) return void 0;
|
|
2639
|
+
const rest = body.slice(value.length).trim();
|
|
2640
|
+
return { args: rest ? rest.split(/\s+/) : [] };
|
|
2641
|
+
};
|
|
2642
|
+
const commandKey$1 = (value) => String(value).trim().replace(/^[#/.]+\s*/, "").split(/\s+/)[0].toLowerCase();
|
|
2643
|
+
const displayCommand = (command, defaultPrefix) => {
|
|
2644
|
+
if (command.prefixes === false) return command.name;
|
|
2645
|
+
const prefixes = command.prefixes === void 0 ? [defaultPrefix] : Array.isArray(command.prefixes) ? command.prefixes : [command.prefixes];
|
|
2646
|
+
return `${prefixes.find((prefix) => prefix.length > 0) ?? ""}${command.name}`;
|
|
2647
|
+
};
|
|
2648
|
+
var CommandManager = class {
|
|
2649
|
+
#getDefaultPrefix;
|
|
2650
|
+
#getOwners;
|
|
2651
|
+
#getAdmins;
|
|
2652
|
+
#logger;
|
|
2653
|
+
#commands = [];
|
|
2654
|
+
#legacy = new Map();
|
|
2655
|
+
#access;
|
|
2656
|
+
#nextOrder = 0;
|
|
2657
|
+
constructor(options) {
|
|
2658
|
+
this.#getDefaultPrefix = options.getDefaultPrefix;
|
|
2659
|
+
this.#getOwners = options.getOwners;
|
|
2660
|
+
this.#getAdmins = options.getAdmins;
|
|
2661
|
+
this.#logger = options.logger;
|
|
2662
|
+
}
|
|
2663
|
+
register(plugin, definitionOrMatcher, handlerOrContext, optionsOrContext = {}, context) {
|
|
2664
|
+
const isDefinition = typeof definitionOrMatcher === "object" && definitionOrMatcher !== null && "name" in definitionOrMatcher;
|
|
2665
|
+
const shortcutOptions = isDefinition ? {} : optionsOrContext;
|
|
2666
|
+
const resolvedContext = isDefinition ? handlerOrContext : context;
|
|
2667
|
+
const handler = isDefinition ? void 0 : handlerOrContext;
|
|
2668
|
+
const definition = isDefinition ? definitionOrMatcher : {
|
|
2669
|
+
...shortcutOptions,
|
|
2670
|
+
name: shortcutOptions.name ?? String(definitionOrMatcher),
|
|
2671
|
+
match: definitionOrMatcher,
|
|
2672
|
+
handler
|
|
2673
|
+
};
|
|
2674
|
+
if (typeof definition.handler !== "function") throw new Error("CommandManager.register: handler must be a function");
|
|
2675
|
+
if (!resolvedContext) throw new Error("CommandManager.register: context is required");
|
|
2676
|
+
const name = String(definition.name ?? "").trim();
|
|
2677
|
+
if (!name) throw new Error("CommandManager.register: name is required");
|
|
2678
|
+
const matcher = definition.match ?? name;
|
|
2679
|
+
const command = {
|
|
2680
|
+
plugin,
|
|
2681
|
+
id: String(definition.id ?? name).trim() || name,
|
|
2682
|
+
name,
|
|
2683
|
+
aliases: (definition.aliases ?? []).map((item) => String(item).trim()).filter(Boolean),
|
|
2684
|
+
matcher,
|
|
2685
|
+
description: String(definition.description ?? "").trim(),
|
|
2686
|
+
usage: definition.usage,
|
|
2687
|
+
permission: normalizeRole(definition.permission),
|
|
2688
|
+
priority: typeof definition.priority === "number" && !Number.isNaN(definition.priority) ? definition.priority : 0,
|
|
2689
|
+
prefixes: definition.prefixes,
|
|
2690
|
+
handler: definition.handler,
|
|
2691
|
+
order: this.#nextOrder++,
|
|
2692
|
+
context: resolvedContext
|
|
2693
|
+
};
|
|
2694
|
+
this.#commands.push(command);
|
|
2695
|
+
return () => {
|
|
2696
|
+
const index = this.#commands.indexOf(command);
|
|
2697
|
+
if (index >= 0) this.#commands.splice(index, 1);
|
|
2698
|
+
};
|
|
2699
|
+
}
|
|
2700
|
+
setAccessControl(config) {
|
|
2701
|
+
this.#access = config;
|
|
2702
|
+
}
|
|
2703
|
+
syncMetadata(metadata) {
|
|
2704
|
+
this.#legacy.set(metadata.name, { metadata });
|
|
2705
|
+
}
|
|
2706
|
+
removePlugin(plugin) {
|
|
2707
|
+
this.#legacy.delete(plugin);
|
|
2708
|
+
for (let i = this.#commands.length - 1; i >= 0; i--) if (this.#commands[i].plugin === plugin) this.#commands.splice(i, 1);
|
|
2709
|
+
}
|
|
2710
|
+
clear() {
|
|
2711
|
+
this.#commands.length = 0;
|
|
2712
|
+
this.#legacy.clear();
|
|
2713
|
+
this.#access = void 0;
|
|
2714
|
+
}
|
|
2715
|
+
list() {
|
|
2716
|
+
return this.#orderedCommands();
|
|
2717
|
+
}
|
|
2718
|
+
#orderedCommands() {
|
|
2719
|
+
return [...this.#commands].sort((a, b) => a.priority - b.priority || a.order - b.order);
|
|
2720
|
+
}
|
|
2721
|
+
getPluginHelp(plugin) {
|
|
2722
|
+
const metadata = this.#legacy.get(plugin)?.metadata;
|
|
2723
|
+
const runtime = this.#commands.filter((item) => item.plugin === plugin);
|
|
2724
|
+
if (!metadata?.config.help && runtime.length === 0) return void 0;
|
|
2725
|
+
const commands = [];
|
|
2726
|
+
const seen = new Set();
|
|
2727
|
+
const runtimeByKey = new Map(runtime.map((command) => [commandKey$1(command.name), command]));
|
|
2728
|
+
for (const command of metadata?.config.help?.commands ?? []) {
|
|
2729
|
+
const key = commandKey$1(command.cmd);
|
|
2730
|
+
const runtimeCommand = runtimeByKey.get(key);
|
|
2731
|
+
if (runtimeCommand) {
|
|
2732
|
+
const cmd = displayCommand(runtimeCommand, this.#getDefaultPrefix());
|
|
2733
|
+
seen.add(key);
|
|
2734
|
+
commands.push({
|
|
2735
|
+
cmd,
|
|
2736
|
+
desc: runtimeCommand.description || command.desc || cmd,
|
|
2737
|
+
usage: runtimeCommand.usage ?? command.usage,
|
|
2738
|
+
role: runtimeCommand.permission
|
|
2739
|
+
});
|
|
2740
|
+
} else if (!seen.has(key)) {
|
|
2741
|
+
seen.add(key);
|
|
2742
|
+
commands.push(command);
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
for (const command of runtime) {
|
|
2746
|
+
const cmd = displayCommand(command, this.#getDefaultPrefix());
|
|
2747
|
+
if (seen.has(commandKey$1(cmd))) continue;
|
|
2748
|
+
seen.add(commandKey$1(cmd));
|
|
2749
|
+
commands.push({
|
|
2750
|
+
cmd,
|
|
2751
|
+
desc: command.description || cmd,
|
|
2752
|
+
usage: command.usage,
|
|
2753
|
+
role: command.permission
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
const help = metadata?.config.help;
|
|
2757
|
+
return {
|
|
2758
|
+
title: help?.title || plugin,
|
|
2759
|
+
description: help?.description || metadata?.description || "",
|
|
2760
|
+
commands
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
catalog() {
|
|
2764
|
+
const items = [];
|
|
2765
|
+
const plugins = new Set([...this.#legacy.keys(), ...this.#commands.map((item) => item.plugin)]);
|
|
2766
|
+
for (const plugin of plugins) {
|
|
2767
|
+
const metadata = this.#legacy.get(plugin)?.metadata;
|
|
2768
|
+
const help = metadata?.config.help;
|
|
2769
|
+
items.push({
|
|
2770
|
+
kind: "plugin",
|
|
2771
|
+
plugin,
|
|
2772
|
+
id: plugin,
|
|
2773
|
+
label: help?.title || plugin,
|
|
2774
|
+
desc: help?.description || metadata?.description,
|
|
2775
|
+
source: metadata ? "manifest" : "runtime"
|
|
2776
|
+
});
|
|
2777
|
+
const seen = new Set();
|
|
2778
|
+
for (const command of this.#commands.filter((item) => item.plugin === plugin)) {
|
|
2779
|
+
seen.add(commandKey$1(command.id));
|
|
2780
|
+
items.push({
|
|
2781
|
+
kind: "command",
|
|
2782
|
+
plugin,
|
|
2783
|
+
id: command.id,
|
|
2784
|
+
label: command.name,
|
|
2785
|
+
desc: command.description,
|
|
2786
|
+
usage: command.usage,
|
|
2787
|
+
permission: command.permission,
|
|
2788
|
+
priority: command.priority,
|
|
2789
|
+
match: command.matcher instanceof RegExp ? command.matcher.toString() : command.matcher,
|
|
2790
|
+
source: "runtime"
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
for (const hook of metadata?.config.accessHooks ?? []) {
|
|
2794
|
+
if (seen.has(commandKey$1(hook.id))) continue;
|
|
2795
|
+
seen.add(commandKey$1(hook.id));
|
|
2796
|
+
items.push({
|
|
2797
|
+
kind: "command",
|
|
2798
|
+
plugin,
|
|
2799
|
+
id: hook.id,
|
|
2800
|
+
label: hook.id,
|
|
2801
|
+
desc: hook.description,
|
|
2802
|
+
match: hook.match,
|
|
2803
|
+
event: hook.event,
|
|
2804
|
+
source: "manifest"
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
for (const command of help?.commands ?? []) {
|
|
2808
|
+
if (seen.has(commandKey$1(command.cmd))) continue;
|
|
2809
|
+
seen.add(commandKey$1(command.cmd));
|
|
2810
|
+
items.push({
|
|
2811
|
+
kind: "command",
|
|
2812
|
+
plugin,
|
|
2813
|
+
id: command.cmd,
|
|
2814
|
+
label: command.cmd,
|
|
2815
|
+
desc: command.desc,
|
|
2816
|
+
usage: command.usage,
|
|
2817
|
+
permission: command.role,
|
|
2818
|
+
source: "manifest"
|
|
2819
|
+
});
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
return items.sort((a, b) => a.kind === b.kind ? a.plugin.localeCompare(b.plugin) || a.id.localeCompare(b.id) : a.kind === "plugin" ? -1 : 1);
|
|
2823
|
+
}
|
|
2824
|
+
async dispatch(event) {
|
|
2825
|
+
const text$1 = String(event.message?.text?.() ?? "").trim();
|
|
2826
|
+
if (!text$1) return false;
|
|
2827
|
+
const match$1 = this.#find(text$1);
|
|
2828
|
+
if (!match$1) return false;
|
|
2829
|
+
if (!this.#canUse(match$1.command, event)) return true;
|
|
2830
|
+
try {
|
|
2831
|
+
await match$1.command.handler({
|
|
2832
|
+
ctx: match$1.command.context,
|
|
2833
|
+
event,
|
|
2834
|
+
command: match$1.command,
|
|
2835
|
+
text: text$1,
|
|
2836
|
+
body: match$1.body,
|
|
2837
|
+
args: match$1.args,
|
|
2838
|
+
match: match$1.match
|
|
2839
|
+
});
|
|
2840
|
+
} catch (error) {
|
|
2841
|
+
this.#logger?.error(`Command "${match$1.command.plugin}:${match$1.command.id}" failed`, error);
|
|
2842
|
+
}
|
|
2843
|
+
return true;
|
|
2844
|
+
}
|
|
2845
|
+
shouldDispatch(source, event) {
|
|
2846
|
+
if (!source.startsWith("plugin:")) return true;
|
|
2847
|
+
const plugin = source.slice(7);
|
|
2848
|
+
const metadata = this.#legacy.get(plugin)?.metadata;
|
|
2849
|
+
if (!metadata) return this.#pluginAllowed(plugin, event);
|
|
2850
|
+
if (!this.#pluginAllowed(plugin, event)) return false;
|
|
2851
|
+
const text$1 = event.kind === "message" ? String(event.message.text()).trim() : "";
|
|
2852
|
+
const matches = (metadata.config.accessHooks ?? []).filter((hook) => event.kind === "message" && textHookMatches(text$1, hook.match) || routeMatches(event, hook.event));
|
|
2853
|
+
return matches.every((hook) => this.#commandAllowed(plugin, hook.id, event));
|
|
2854
|
+
}
|
|
2855
|
+
#find(text$1) {
|
|
2856
|
+
for (const command of this.#orderedCommands()) {
|
|
2857
|
+
const prefixes = parsePrefixes(command.prefixes ?? this.#getDefaultPrefix(), this.#getDefaultPrefix());
|
|
2858
|
+
const body = commandBody(text$1, prefixes);
|
|
2859
|
+
if (body === void 0) continue;
|
|
2860
|
+
if (typeof command.matcher === "string") {
|
|
2861
|
+
const candidates = [
|
|
2862
|
+
command.matcher,
|
|
2863
|
+
command.name,
|
|
2864
|
+
...command.aliases
|
|
2865
|
+
];
|
|
2866
|
+
for (const candidate of candidates) {
|
|
2867
|
+
const result = stringMatch(body, candidate);
|
|
2868
|
+
if (result) return {
|
|
2869
|
+
command,
|
|
2870
|
+
body,
|
|
2871
|
+
args: result.args
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
continue;
|
|
2875
|
+
}
|
|
2876
|
+
command.matcher.lastIndex = 0;
|
|
2877
|
+
const matched = body.match(command.matcher);
|
|
2878
|
+
if (!matched) continue;
|
|
2879
|
+
const args = matched.length > 1 ? matched.slice(1).filter((item) => item != null) : body.slice(matched[0].length).trim().split(/\s+/).filter(Boolean);
|
|
2880
|
+
return {
|
|
2881
|
+
command,
|
|
2882
|
+
body,
|
|
2883
|
+
args,
|
|
2884
|
+
match: matched
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
return void 0;
|
|
2888
|
+
}
|
|
2889
|
+
#canUse(command, event) {
|
|
2890
|
+
const userId = userIdOf(event);
|
|
2891
|
+
const master = Boolean(userId && this.#isMaster(userId));
|
|
2892
|
+
const owner = master || this.#isGroupOwner(event);
|
|
2893
|
+
const admin = owner || Boolean(userId && this.#isAdmin(userId)) || this.#isGroupAdmin(event);
|
|
2894
|
+
if (command.permission === "master") {
|
|
2895
|
+
if (!master) return false;
|
|
2896
|
+
} else if (command.permission === "owner") {
|
|
2897
|
+
if (!owner) return false;
|
|
2898
|
+
} else if (command.permission === "admin" && !admin) return false;
|
|
2899
|
+
return this.#commandAllowed(command.plugin, command.id, event);
|
|
2900
|
+
}
|
|
2901
|
+
#pluginAllowed(plugin, event) {
|
|
2902
|
+
const userId = event.kind === "message" ? userIdOf(event) : "user_id" in event ? event.user_id : void 0;
|
|
2903
|
+
if (userId && (this.#isMaster(userId) || this.#isAdmin(userId)) || this.#isGroupOwner(event) || this.#isGroupAdmin(event)) return true;
|
|
2904
|
+
return this.#resolveAction(plugin, void 0, event) !== "block";
|
|
2905
|
+
}
|
|
2906
|
+
#commandAllowed(plugin, command, event) {
|
|
2907
|
+
const userId = event.kind === "message" ? userIdOf(event) : "user_id" in event ? event.user_id : void 0;
|
|
2908
|
+
if (userId && (this.#isMaster(userId) || this.#isAdmin(userId)) || this.#isGroupOwner(event) || this.#isGroupAdmin(event)) return true;
|
|
2909
|
+
return this.#resolveAction(plugin, command, event) !== "block";
|
|
2910
|
+
}
|
|
2911
|
+
#resolveAction(plugin, command, event) {
|
|
2912
|
+
const access = this.#access;
|
|
2913
|
+
if (!access) return void 0;
|
|
2914
|
+
const scopes = [
|
|
2915
|
+
(() => {
|
|
2916
|
+
const userId = event.kind === "message" ? userIdOf(event) : "user_id" in event && event.user_id ? String(event.user_id) : void 0;
|
|
2917
|
+
return userId ? access.users[userId] : void 0;
|
|
2918
|
+
})(),
|
|
2919
|
+
(() => {
|
|
2920
|
+
const groupId = "group_id" in event && event.group_id ? String(event.group_id) : void 0;
|
|
2921
|
+
return groupId ? access.groups[groupId] : void 0;
|
|
2922
|
+
})(),
|
|
2923
|
+
access.global
|
|
2924
|
+
];
|
|
2925
|
+
for (const scope of scopes) {
|
|
2926
|
+
if (!scope) continue;
|
|
2927
|
+
const commandRule = command ? scope.commands?.[plugin]?.[command]?.action : void 0;
|
|
2928
|
+
if (commandRule) return commandRule;
|
|
2929
|
+
const pluginRule = scope.plugins?.[plugin]?.action;
|
|
2930
|
+
if (pluginRule) return pluginRule;
|
|
2931
|
+
}
|
|
2932
|
+
return void 0;
|
|
2933
|
+
}
|
|
2934
|
+
#isMaster(userId) {
|
|
2935
|
+
return this.#owners().includes(userId);
|
|
2936
|
+
}
|
|
2937
|
+
#isAdmin(userId) {
|
|
2938
|
+
return this.#isMaster(userId) || this.#admins().includes(userId);
|
|
2939
|
+
}
|
|
2940
|
+
#isGroupOwner(event) {
|
|
2941
|
+
return isEventGroupOwner(event);
|
|
2942
|
+
}
|
|
2943
|
+
#isGroupAdmin(event) {
|
|
2944
|
+
return isEventGroupAdmin(event);
|
|
2945
|
+
}
|
|
2946
|
+
#owners() {
|
|
2947
|
+
return this.#configIds("owners");
|
|
2948
|
+
}
|
|
2949
|
+
#admins() {
|
|
2950
|
+
return this.#configIds("admins");
|
|
2951
|
+
}
|
|
2952
|
+
#configIds(key) {
|
|
2953
|
+
return [...key === "owners" ? this.#getOwners() : this.#getAdmins()].map(String);
|
|
2954
|
+
}
|
|
2955
|
+
};
|
|
2956
|
+
let activeCommandManager;
|
|
2957
|
+
const setActiveCommandManager = (manager) => {
|
|
2958
|
+
activeCommandManager = manager;
|
|
2959
|
+
};
|
|
2960
|
+
const getActiveCommandManager = () => activeCommandManager;
|
|
2961
|
+
|
|
2518
2962
|
//#endregion
|
|
2519
2963
|
//#region src/runtime/plugin-metadata.ts
|
|
2520
2964
|
const store$1 = getOrCreate("plugin-metadata", () => new Map());
|
|
2521
2965
|
const setPluginMetadata = (meta) => {
|
|
2522
2966
|
store$1.set(meta.name, meta);
|
|
2967
|
+
getActiveCommandManager()?.syncMetadata(meta);
|
|
2523
2968
|
};
|
|
2524
2969
|
const removePluginMetadata = (name) => {
|
|
2525
2970
|
store$1.delete(name);
|
|
2971
|
+
getActiveCommandManager()?.removePlugin(name);
|
|
2526
2972
|
};
|
|
2527
2973
|
const getPluginMetadata = (name) => store$1.get(name);
|
|
2528
2974
|
const getPluginMetadataList = () => [...store$1.values()];
|
|
2529
2975
|
const resetPluginMetadata = () => {
|
|
2530
2976
|
store$1.clear();
|
|
2977
|
+
getActiveCommandManager()?.clear();
|
|
2531
2978
|
};
|
|
2532
2979
|
|
|
2533
2980
|
//#endregion
|
|
@@ -3187,12 +3634,6 @@ async function sendTextOrForward(options) {
|
|
|
3187
3634
|
}
|
|
3188
3635
|
}
|
|
3189
3636
|
|
|
3190
|
-
//#endregion
|
|
3191
|
-
//#region src/builtin/core/commands/prefix.ts
|
|
3192
|
-
function getCommandPrefix() {
|
|
3193
|
-
return botConfig.prefix ?? ".";
|
|
3194
|
-
}
|
|
3195
|
-
|
|
3196
3637
|
//#endregion
|
|
3197
3638
|
//#region src/builtin/core/system/package-manager.ts
|
|
3198
3639
|
const NPM_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -3784,102 +4225,102 @@ async function performUpdateAndReport(ctx, event, names) {
|
|
|
3784
4225
|
triggerRestart(marker);
|
|
3785
4226
|
}
|
|
3786
4227
|
function registerUpdateCommands(ctx) {
|
|
3787
|
-
const dispose = ctx.
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
await performUpdateAndReport(ctx, event, managed.names);
|
|
3807
|
-
return;
|
|
3808
|
-
}
|
|
3809
|
-
if (arg === "mioku" || arg === "self") {
|
|
3810
|
-
await performUpdateAndReport(ctx, event, ["mioku"]);
|
|
3811
|
-
return;
|
|
3812
|
-
}
|
|
3813
|
-
if (arg.startsWith("plugin ") || arg.startsWith("service ")) {
|
|
3814
|
-
const [typeRaw, ...rest] = arg.split(/\s+/);
|
|
3815
|
-
const name = rest.join(" ").trim();
|
|
3816
|
-
const type = typeRaw === "plugin" ? "plugin" : "service";
|
|
3817
|
-
if (!name) {
|
|
3818
|
-
await replyText(event, `用法:${prefix}update ${type} <名称>`);
|
|
4228
|
+
const dispose = ctx.command({
|
|
4229
|
+
name: "update",
|
|
4230
|
+
aliases: ["更新"],
|
|
4231
|
+
permission: "master",
|
|
4232
|
+
priority: -1e3,
|
|
4233
|
+
description: "检查并选择插件/服务更新",
|
|
4234
|
+
handler: async ({ event, args }) => {
|
|
4235
|
+
const prefix = String(ctx.config.prefix ?? ".");
|
|
4236
|
+
const arg = args.join(" ").trim();
|
|
4237
|
+
const selfId = String(event?.self_id || "");
|
|
4238
|
+
const bot = event.bot;
|
|
4239
|
+
if (!bot) return;
|
|
4240
|
+
if (arg === "all") {
|
|
4241
|
+
const managed = await updateAllManaged();
|
|
4242
|
+
if (managed.names.length === 0) {
|
|
4243
|
+
await replyText(event, "未找到可更新的 mioku 包");
|
|
4244
|
+
return;
|
|
4245
|
+
}
|
|
4246
|
+
await performUpdateAndReport(ctx, event, managed.names);
|
|
3819
4247
|
return;
|
|
3820
4248
|
}
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
await performUpdateAndReport(ctx, event, [fullName]);
|
|
3824
|
-
return;
|
|
3825
|
-
}
|
|
3826
|
-
if (arg === "check" || arg === "检查") {
|
|
3827
|
-
const pending = getPendingMap();
|
|
3828
|
-
const key = conversationKey(event);
|
|
3829
|
-
const existing = pending.get(key);
|
|
3830
|
-
if (existing) {
|
|
3831
|
-
existing.disposer();
|
|
3832
|
-
clearTimeout(existing.timer);
|
|
3833
|
-
pending.delete(key);
|
|
3834
|
-
}
|
|
3835
|
-
let items;
|
|
3836
|
-
try {
|
|
3837
|
-
items = await checkUpdates();
|
|
3838
|
-
} catch (error) {
|
|
3839
|
-
ctx.logger.error(`[core] 检查更新失败: ${error}`);
|
|
3840
|
-
await replyText(event, `检查更新失败:${String(error)}`);
|
|
4249
|
+
if (arg === "mioku" || arg === "self") {
|
|
4250
|
+
await performUpdateAndReport(ctx, event, ["mioku"]);
|
|
3841
4251
|
return;
|
|
3842
4252
|
}
|
|
3843
|
-
if (
|
|
3844
|
-
|
|
4253
|
+
if (arg.startsWith("plugin ") || arg.startsWith("service ")) {
|
|
4254
|
+
const [typeRaw, ...rest] = arg.split(/\s+/);
|
|
4255
|
+
const name = rest.join(" ").trim();
|
|
4256
|
+
const type = typeRaw === "plugin" ? "plugin" : "service";
|
|
4257
|
+
if (!name) {
|
|
4258
|
+
await replyText(event, `用法:${prefix}update ${type} <名称>`);
|
|
4259
|
+
return;
|
|
4260
|
+
}
|
|
4261
|
+
const pkgPrefix = type === "plugin" ? "mioku-plugin-" : "mioku-service-";
|
|
4262
|
+
const fullName = name.startsWith(pkgPrefix) ? name : `${pkgPrefix}${name}`;
|
|
4263
|
+
await performUpdateAndReport(ctx, event, [fullName]);
|
|
3845
4264
|
return;
|
|
3846
4265
|
}
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
if (
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
if (chosen.length === 0) {
|
|
3863
|
-
await replyText(event, "未选择任何有效项,已取消");
|
|
4266
|
+
if (arg === "check" || arg === "检查") {
|
|
4267
|
+
const pending = getPendingMap();
|
|
4268
|
+
const key = conversationKey(event);
|
|
4269
|
+
const existing = pending.get(key);
|
|
4270
|
+
if (existing) {
|
|
4271
|
+
existing.disposer();
|
|
4272
|
+
clearTimeout(existing.timer);
|
|
4273
|
+
pending.delete(key);
|
|
4274
|
+
}
|
|
4275
|
+
let items;
|
|
4276
|
+
try {
|
|
4277
|
+
items = await checkUpdates();
|
|
4278
|
+
} catch (error) {
|
|
4279
|
+
ctx.logger.error(`[core] 检查更新失败: ${error}`);
|
|
4280
|
+
await replyText(event, `检查更新失败:${String(error)}`);
|
|
3864
4281
|
return;
|
|
3865
4282
|
}
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
4283
|
+
if (items.length === 0) {
|
|
4284
|
+
await replyText(event, "所有插件与服务均已是最新版本");
|
|
4285
|
+
return;
|
|
4286
|
+
}
|
|
4287
|
+
await replyText(event, renderUpdateList(items));
|
|
4288
|
+
const timeoutMs = SELECT_TIMEOUT_MS;
|
|
4289
|
+
const listenerDispose = ctx.handle("message", async (ev) => {
|
|
4290
|
+
if (String(ev?.self_id || "") !== selfId) return;
|
|
4291
|
+
if (conversationKey(ev) !== key) return;
|
|
4292
|
+
if (!isEventMaster(ev)) return;
|
|
4293
|
+
const evText = ctx.text(ev)?.trim() || "";
|
|
4294
|
+
if (evText.startsWith(`${prefix}update`)) return;
|
|
4295
|
+
const sel = pending.get(key);
|
|
4296
|
+
if (!sel) return;
|
|
4297
|
+
clearTimeout(sel.timer);
|
|
4298
|
+
sel.disposer();
|
|
4299
|
+
pending.delete(key);
|
|
4300
|
+
const parsed = parseSelection(evText, sel.items.length);
|
|
4301
|
+
const chosen = parsed.all ? sel.items : parsed.indices.map((i) => sel.items[i]).filter(Boolean);
|
|
4302
|
+
if (chosen.length === 0) {
|
|
4303
|
+
await replyText(event, "未选择任何有效项,已取消");
|
|
4304
|
+
return;
|
|
4305
|
+
}
|
|
4306
|
+
await performUpdateAndReport(ctx, event, chosen.map((i) => i.name));
|
|
4307
|
+
});
|
|
4308
|
+
const timer = setTimeout(async () => {
|
|
4309
|
+
const sel = pending.get(key);
|
|
4310
|
+
if (!sel) return;
|
|
4311
|
+
sel.disposer();
|
|
4312
|
+
pending.delete(key);
|
|
4313
|
+
await replyText(event, "操作超时,已取消更新");
|
|
4314
|
+
}, timeoutMs);
|
|
4315
|
+
pending.set(key, {
|
|
4316
|
+
disposer: listenerDispose,
|
|
4317
|
+
timer,
|
|
4318
|
+
items
|
|
4319
|
+
});
|
|
4320
|
+
return;
|
|
4321
|
+
}
|
|
4322
|
+
await replyText(event, `用法:\n${prefix}update check 检查并选择更新\n${prefix}update all 更新全部\n${prefix}update mioku 更新框架\n${prefix}update plugin <名称>\n${prefix}update service <名称>`);
|
|
3881
4323
|
}
|
|
3882
|
-
await replyText(event, `用法:\n${prefix}update check 检查并选择更新\n${prefix}update all 更新全部\n${prefix}update mioku 更新框架\n${prefix}update plugin <名称>\n${prefix}update service <名称>`);
|
|
3883
4324
|
});
|
|
3884
4325
|
return () => {
|
|
3885
4326
|
dispose();
|
|
@@ -3894,15 +4335,13 @@ function registerUpdateCommands(ctx) {
|
|
|
3894
4335
|
|
|
3895
4336
|
//#endregion
|
|
3896
4337
|
//#region src/builtin/core/commands/install.ts
|
|
3897
|
-
function parseTargetArgs(
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
const
|
|
3901
|
-
if (!matched) return null;
|
|
3902
|
-
const name = matched[2].trim();
|
|
4338
|
+
function parseTargetArgs(args) {
|
|
4339
|
+
const type = args[0];
|
|
4340
|
+
if (type !== "plugin" && type !== "service") return null;
|
|
4341
|
+
const name = args.slice(1).join(" ").trim();
|
|
3903
4342
|
if (!name) return null;
|
|
3904
4343
|
return {
|
|
3905
|
-
type
|
|
4344
|
+
type,
|
|
3906
4345
|
name
|
|
3907
4346
|
};
|
|
3908
4347
|
}
|
|
@@ -3910,68 +4349,70 @@ function typeLabel(type) {
|
|
|
3910
4349
|
return type === "plugin" ? "插件" : "服务";
|
|
3911
4350
|
}
|
|
3912
4351
|
function registerInstallCommands(ctx) {
|
|
3913
|
-
const
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
4352
|
+
const register = (name) => ctx.command({
|
|
4353
|
+
name,
|
|
4354
|
+
aliases: name === "install" ? ["安装"] : ["卸载"],
|
|
4355
|
+
permission: "master",
|
|
4356
|
+
priority: -1e3,
|
|
4357
|
+
description: name === "install" ? "从 npm 安装插件/服务" : "卸载插件/服务",
|
|
4358
|
+
handler: async ({ event, args }) => {
|
|
4359
|
+
if (name === "install") {
|
|
4360
|
+
const parsed$1 = parseTargetArgs(args);
|
|
4361
|
+
if (!parsed$1) {
|
|
4362
|
+
await replyText(event, `用法:${ctx.config.prefix ?? "."}install plugin <名称> 或 ${ctx.config.prefix ?? "."}install service <名称>`);
|
|
4363
|
+
return;
|
|
4364
|
+
}
|
|
4365
|
+
await replyText(event, `正在从 npm 安装${typeLabel(parsed$1.type)} ${parsed$1.name}...`);
|
|
4366
|
+
try {
|
|
4367
|
+
const result = await installPackage(parsed$1.type, parsed$1.name);
|
|
4368
|
+
if (!result.ok) {
|
|
4369
|
+
ctx.logger.error(`[core] 安装失败: ${result.error || result.output}`);
|
|
4370
|
+
await replyText(event, `安装失败:${result.error || result.output}`);
|
|
4371
|
+
return;
|
|
4372
|
+
}
|
|
4373
|
+
const lines = [
|
|
4374
|
+
`已安装 ${result.packageName}`,
|
|
4375
|
+
result.enabled ? "已在 plugins 中启用" : "",
|
|
4376
|
+
"重启后生效"
|
|
4377
|
+
].filter(Boolean);
|
|
4378
|
+
await replyText(event, lines.join("\n"));
|
|
4379
|
+
} catch (error) {
|
|
4380
|
+
ctx.logger.error(`[core] 安装失败: ${error}`);
|
|
4381
|
+
await replyText(event, `安装失败:${String(error)}`);
|
|
4382
|
+
}
|
|
4383
|
+
return;
|
|
4384
|
+
}
|
|
4385
|
+
const parsed = parseTargetArgs(args);
|
|
4386
|
+
if (!parsed) {
|
|
4387
|
+
await replyText(event, `用法:${ctx.config.prefix ?? "."}uninstall plugin <名称> 或 ${ctx.config.prefix ?? "."}uninstall service <名称>`);
|
|
3928
4388
|
return;
|
|
3929
4389
|
}
|
|
3930
|
-
await replyText(event,
|
|
4390
|
+
await replyText(event, `正在卸载${typeLabel(parsed.type)} ${parsed.name}...`);
|
|
3931
4391
|
try {
|
|
3932
|
-
const result = await
|
|
4392
|
+
const result = await uninstallPackage(parsed.type, parsed.name);
|
|
3933
4393
|
if (!result.ok) {
|
|
3934
|
-
ctx.logger.error(`[core]
|
|
3935
|
-
await replyText(event,
|
|
4394
|
+
ctx.logger.error(`[core] 卸载失败: ${result.error || result.output}`);
|
|
4395
|
+
await replyText(event, `卸载失败:${result.error || result.output}`);
|
|
3936
4396
|
return;
|
|
3937
4397
|
}
|
|
3938
4398
|
const lines = [
|
|
3939
|
-
|
|
3940
|
-
result.
|
|
4399
|
+
`已卸载 ${result.packageName}`,
|
|
4400
|
+
result.removedFromConfig ? "已从 plugins 中移除" : "",
|
|
3941
4401
|
"重启后生效"
|
|
3942
4402
|
].filter(Boolean);
|
|
3943
4403
|
await replyText(event, lines.join("\n"));
|
|
3944
4404
|
} catch (error) {
|
|
3945
|
-
ctx.logger.error(`[core]
|
|
3946
|
-
await replyText(event,
|
|
4405
|
+
ctx.logger.error(`[core] 卸载失败: ${error}`);
|
|
4406
|
+
await replyText(event, `卸载失败:${String(error)}`);
|
|
3947
4407
|
}
|
|
3948
|
-
return;
|
|
3949
|
-
}
|
|
3950
|
-
const parsed = parseTargetArgs(text$1, uninstallHead);
|
|
3951
|
-
if (!parsed) {
|
|
3952
|
-
await replyText(event, `用法:${prefix}uninstall plugin <名称> 或 ${prefix}uninstall service <名称>`);
|
|
3953
|
-
return;
|
|
3954
|
-
}
|
|
3955
|
-
await replyText(event, `正在卸载${typeLabel(parsed.type)} ${parsed.name}...`);
|
|
3956
|
-
try {
|
|
3957
|
-
const result = await uninstallPackage(parsed.type, parsed.name);
|
|
3958
|
-
if (!result.ok) {
|
|
3959
|
-
ctx.logger.error(`[core] 卸载失败: ${result.error || result.output}`);
|
|
3960
|
-
await replyText(event, `卸载失败:${result.error || result.output}`);
|
|
3961
|
-
return;
|
|
3962
|
-
}
|
|
3963
|
-
const lines = [
|
|
3964
|
-
`已卸载 ${result.packageName}`,
|
|
3965
|
-
result.removedFromConfig ? "已从 plugins 中移除" : "",
|
|
3966
|
-
"重启后生效"
|
|
3967
|
-
].filter(Boolean);
|
|
3968
|
-
await replyText(event, lines.join("\n"));
|
|
3969
|
-
} catch (error) {
|
|
3970
|
-
ctx.logger.error(`[core] 卸载失败: ${error}`);
|
|
3971
|
-
await replyText(event, `卸载失败:${String(error)}`);
|
|
3972
4408
|
}
|
|
3973
4409
|
});
|
|
3974
|
-
|
|
4410
|
+
const install = register("install");
|
|
4411
|
+
const uninstall = register("uninstall");
|
|
4412
|
+
return () => {
|
|
4413
|
+
install();
|
|
4414
|
+
uninstall();
|
|
4415
|
+
};
|
|
3975
4416
|
}
|
|
3976
4417
|
|
|
3977
4418
|
//#endregion
|
|
@@ -3985,64 +4426,76 @@ function renderMarketText(kind, items) {
|
|
|
3985
4426
|
return [`Mioku ${label}市场(共 ${items.length} 个)`, ...lines].join("\n");
|
|
3986
4427
|
}
|
|
3987
4428
|
function registerMarketCommands(ctx) {
|
|
3988
|
-
const
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4429
|
+
const pluginMarket = ctx.command({
|
|
4430
|
+
id: "market",
|
|
4431
|
+
name: "plugin-market",
|
|
4432
|
+
aliases: ["插件市场"],
|
|
4433
|
+
permission: "master",
|
|
4434
|
+
priority: -1e3,
|
|
4435
|
+
description: "查看插件市场",
|
|
4436
|
+
handler: (input) => handleMarket(ctx, input.event, "plugin")
|
|
4437
|
+
});
|
|
4438
|
+
const serviceMarket = ctx.command({
|
|
4439
|
+
id: "market",
|
|
4440
|
+
name: "service-market",
|
|
4441
|
+
aliases: ["服务市场"],
|
|
4442
|
+
permission: "master",
|
|
4443
|
+
priority: -1e3,
|
|
4444
|
+
description: "查看服务市场",
|
|
4445
|
+
handler: (input) => handleMarket(ctx, input.event, "service")
|
|
4446
|
+
});
|
|
4447
|
+
return () => {
|
|
4448
|
+
pluginMarket();
|
|
4449
|
+
serviceMarket();
|
|
4450
|
+
};
|
|
4451
|
+
}
|
|
4452
|
+
async function handleMarket(ctx, event, kind) {
|
|
4453
|
+
let items;
|
|
4454
|
+
try {
|
|
4455
|
+
items = await getMarketItems(kind);
|
|
4456
|
+
} catch (error) {
|
|
4457
|
+
ctx.logger.error(`[core] 获取市场信息失败: ${error}`);
|
|
4458
|
+
await replyText(event, `获取市场失败:${String(error)}`);
|
|
4459
|
+
return;
|
|
4460
|
+
}
|
|
4461
|
+
await sendTextOrForward({
|
|
4462
|
+
ctx,
|
|
4463
|
+
event,
|
|
4464
|
+
text: renderMarketText(kind, items),
|
|
4465
|
+
source: `Mioku ${kind === "plugin" ? "插件" : "服务"}市场`,
|
|
4466
|
+
summary: `共 ${items.length} 个`
|
|
4013
4467
|
});
|
|
4014
|
-
return dispose;
|
|
4015
4468
|
}
|
|
4016
4469
|
|
|
4017
4470
|
//#endregion
|
|
4018
4471
|
//#region src/builtin/core/commands/restart.ts
|
|
4019
4472
|
function registerRestartCommand(ctx) {
|
|
4020
|
-
const dispose = ctx.
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
return;
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4473
|
+
const dispose = ctx.command({
|
|
4474
|
+
name: "restart",
|
|
4475
|
+
aliases: ["重启"],
|
|
4476
|
+
permission: "master",
|
|
4477
|
+
priority: -1e3,
|
|
4478
|
+
description: "重启机器人进程",
|
|
4479
|
+
handler: async ({ event }) => {
|
|
4480
|
+
if (event?.user_id === event?.self_id) return;
|
|
4481
|
+
const uptimeMs = process.uptime() * 1e3;
|
|
4482
|
+
const selfId = Number(event?.self_id || 0);
|
|
4483
|
+
const groupId = event?.message_type === "group" && event?.group_id ? Number(event.group_id) : null;
|
|
4484
|
+
const userId = Number(event?.user_id || 0);
|
|
4485
|
+
const marker = {
|
|
4486
|
+
initiatedAt: Date.now(),
|
|
4487
|
+
selfId,
|
|
4488
|
+
groupId,
|
|
4489
|
+
userId
|
|
4490
|
+
};
|
|
4491
|
+
try {
|
|
4492
|
+
await replyText(event, `Bot已运行${formatUptime(uptimeMs)},正在重启...`);
|
|
4493
|
+
} catch (error) {
|
|
4494
|
+
ctx.logger.warn(`[core] 发送重启提示失败: ${error}`);
|
|
4495
|
+
}
|
|
4496
|
+
ctx.logger.info(`[core] 正在执行重启命令 ${formatUptime(uptimeMs)}`);
|
|
4497
|
+
triggerRestart(marker);
|
|
4043
4498
|
}
|
|
4044
|
-
ctx.logger.info(`[core] 正在执行重启命令 ${formatUptime(uptimeMs)}`);
|
|
4045
|
-
triggerRestart(marker);
|
|
4046
4499
|
});
|
|
4047
4500
|
return dispose;
|
|
4048
4501
|
}
|
|
@@ -4080,63 +4533,63 @@ function chunk(arr, size) {
|
|
|
4080
4533
|
return result;
|
|
4081
4534
|
}
|
|
4082
4535
|
function registerLogCommand(ctx) {
|
|
4083
|
-
const dispose = ctx.
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
return;
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
})
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4536
|
+
const dispose = ctx.command({
|
|
4537
|
+
name: "log",
|
|
4538
|
+
aliases: ["日志"],
|
|
4539
|
+
permission: "master",
|
|
4540
|
+
priority: -1e3,
|
|
4541
|
+
description: "查看最近100条日志",
|
|
4542
|
+
handler: async ({ event }) => {
|
|
4543
|
+
if (event?.user_id === event?.self_id) return;
|
|
4544
|
+
const selfId = String(event?.self_id || "");
|
|
4545
|
+
const bot = event.bot;
|
|
4546
|
+
if (!bot) return;
|
|
4547
|
+
const logFile = getActiveLogFile();
|
|
4548
|
+
if (!logFile) {
|
|
4549
|
+
await replyText(event, "未找到日志文件。");
|
|
4550
|
+
return;
|
|
4551
|
+
}
|
|
4552
|
+
let lines;
|
|
4553
|
+
try {
|
|
4554
|
+
lines = readLastLines(logFile, LOG_LINE_COUNT);
|
|
4555
|
+
} catch (error) {
|
|
4556
|
+
ctx.logger.error(`[core] 读取日志失败: ${error}`);
|
|
4557
|
+
await replyText(event, `读取日志失败:${String(error)}`);
|
|
4558
|
+
return;
|
|
4559
|
+
}
|
|
4560
|
+
if (lines.length === 0) {
|
|
4561
|
+
await replyText(event, "日志为空。");
|
|
4562
|
+
return;
|
|
4563
|
+
}
|
|
4564
|
+
let infoCount = 0;
|
|
4565
|
+
let warnCount = 0;
|
|
4566
|
+
let errorCount = 0;
|
|
4567
|
+
for (const line of lines) if (/\bERROR\b|\berror\b/i.test(line)) errorCount++;
|
|
4568
|
+
else if (/\bWARN\b|\bwarn\b/i.test(line)) warnCount++;
|
|
4569
|
+
else if (/\bINFO\b|\binfo\b/i.test(line)) infoCount++;
|
|
4570
|
+
const summary = `info ${infoCount} warn ${warnCount} error ${errorCount}`;
|
|
4571
|
+
const chunks = chunk(lines, LINES_PER_NODE);
|
|
4572
|
+
const nodes = chunks.map((chunkLines, idx) => ({
|
|
4573
|
+
user_id: selfId,
|
|
4574
|
+
nickname: `第${idx + 1}条日志`,
|
|
4575
|
+
content: [ctx.segment.text(chunkLines.join("\n"))]
|
|
4576
|
+
}));
|
|
4577
|
+
try {
|
|
4578
|
+
const target = event?.message_type === "group" && event?.group_id ? {
|
|
4579
|
+
type: "group",
|
|
4580
|
+
group_id: event.group_id
|
|
4581
|
+
} : event?.user_id ? {
|
|
4582
|
+
type: "private",
|
|
4583
|
+
user_id: event.user_id
|
|
4584
|
+
} : void 0;
|
|
4585
|
+
if (target) await bot.sendForward(target, nodes, {
|
|
4586
|
+
source: "最近100条运行日志",
|
|
4587
|
+
summary
|
|
4588
|
+
});
|
|
4589
|
+
} catch (error) {
|
|
4590
|
+
ctx.logger.error(`[core] 发送日志转发失败: ${error}`);
|
|
4591
|
+
await replyText(event, `发送日志失败:${String(error)}`);
|
|
4592
|
+
}
|
|
4140
4593
|
}
|
|
4141
4594
|
});
|
|
4142
4595
|
return dispose;
|
|
@@ -4363,7 +4816,6 @@ const core = definePlugin({
|
|
|
4363
4816
|
description: "mioku 内置核心插件",
|
|
4364
4817
|
async setup(ctx) {
|
|
4365
4818
|
const rawPrefix = ctx.config.prefix ?? ".";
|
|
4366
|
-
const cmdPrefix = new RegExp(`^${escapeRegExp(rawPrefix)}`);
|
|
4367
4819
|
const displayPrefix = rawPrefix;
|
|
4368
4820
|
const statusAdminOnly = ctx.config.status_permission === "admin-only";
|
|
4369
4821
|
rootLogger.info("========================================");
|
|
@@ -4381,12 +4833,17 @@ const core = definePlugin({
|
|
|
4381
4833
|
}));
|
|
4382
4834
|
} else ctx.logger.warn("config-service 未加载,core 插件将使用默认配置");
|
|
4383
4835
|
let accessRules = ensureAccessControlConfig();
|
|
4836
|
+
ctx.commands.setAccessControl(accessRules);
|
|
4384
4837
|
if (configService) {
|
|
4385
4838
|
await configService.registerConfig("core", "access-control", accessRules);
|
|
4386
4839
|
const persisted = await configService.getConfig("core", "access-control");
|
|
4387
|
-
if (persisted)
|
|
4840
|
+
if (persisted) {
|
|
4841
|
+
accessRules = normalizeAccessConfig(persisted);
|
|
4842
|
+
ctx.commands.setAccessControl(accessRules);
|
|
4843
|
+
}
|
|
4388
4844
|
disposers.push(configService.onConfigChange("core", "access-control", (next) => {
|
|
4389
4845
|
accessRules = normalizeAccessConfig(next);
|
|
4846
|
+
ctx.commands.setAccessControl(accessRules);
|
|
4390
4847
|
}));
|
|
4391
4848
|
}
|
|
4392
4849
|
rootLogger.info(`访问控制已挂载: ${ctx.bots.length} 个 bot`);
|
|
@@ -4448,17 +4905,49 @@ const core = definePlugin({
|
|
|
4448
4905
|
bots: collectBots(),
|
|
4449
4906
|
adapters: collectAdapters()
|
|
4450
4907
|
}));
|
|
4908
|
+
ctx.command({
|
|
4909
|
+
name: "help",
|
|
4910
|
+
aliases: ["帮助"],
|
|
4911
|
+
permission: "master",
|
|
4912
|
+
priority: -1e3,
|
|
4913
|
+
description: "显示帮助信息",
|
|
4914
|
+
handler: async ({ event }) => {
|
|
4915
|
+
await event.reply(buildCoreHelpText(displayPrefix));
|
|
4916
|
+
}
|
|
4917
|
+
});
|
|
4918
|
+
ctx.command({
|
|
4919
|
+
name: "status",
|
|
4920
|
+
aliases: ["状态"],
|
|
4921
|
+
priority: -1e3,
|
|
4922
|
+
description: "显示框架状态",
|
|
4923
|
+
handler: async ({ event }) => {
|
|
4924
|
+
if (statusAdminOnly && !isEventOwnerOrAdmin(event)) return;
|
|
4925
|
+
await event.reply(await formatMiokuStatus(await getStatus()));
|
|
4926
|
+
}
|
|
4927
|
+
});
|
|
4928
|
+
ctx.command({
|
|
4929
|
+
name: "adapter",
|
|
4930
|
+
aliases: ["适配器"],
|
|
4931
|
+
priority: -1e3,
|
|
4932
|
+
description: "显示适配器与连接实例",
|
|
4933
|
+
handler: async ({ event }) => {
|
|
4934
|
+
if (statusAdminOnly && !isEventOwnerOrAdmin(event)) return;
|
|
4935
|
+
await event.reply(await getAdapterText());
|
|
4936
|
+
}
|
|
4937
|
+
});
|
|
4451
4938
|
const atTarget = (event) => {
|
|
4452
4939
|
const at = event.message.find((seg) => seg.type === "at");
|
|
4453
4940
|
const qq = at?.data?.qq ?? at?.data?.target;
|
|
4454
4941
|
return qq != null ? String(qq) : void 0;
|
|
4455
4942
|
};
|
|
4456
|
-
|
|
4943
|
+
const handleCoreMessage = async (event) => {
|
|
4457
4944
|
const ev = event;
|
|
4458
4945
|
const text$1 = ev.message.text();
|
|
4459
|
-
|
|
4946
|
+
const currentPrefix = String(ctx.config.prefix ?? ".");
|
|
4947
|
+
const currentCmdPrefix = new RegExp(`^${escapeRegExp(currentPrefix)}`);
|
|
4948
|
+
if (!currentCmdPrefix.test(text$1)) return;
|
|
4460
4949
|
if (statusAdminOnly && !isEventOwnerOrAdmin(ev)) return;
|
|
4461
|
-
const bare = text$1.replace(
|
|
4950
|
+
const bare = text$1.replace(currentCmdPrefix, "").trim();
|
|
4462
4951
|
if (bare === "状态" || bare === "status") {
|
|
4463
4952
|
await ev.reply(await formatMiokuStatus(await getStatus()));
|
|
4464
4953
|
return;
|
|
@@ -4467,10 +4956,10 @@ const core = definePlugin({
|
|
|
4467
4956
|
await ev.reply(await getAdapterText());
|
|
4468
4957
|
return;
|
|
4469
4958
|
}
|
|
4470
|
-
if (!
|
|
4959
|
+
if (!isEventMaster(ev)) return;
|
|
4471
4960
|
const { cmd, params } = createCmd(text$1);
|
|
4472
4961
|
if (!cmd) return;
|
|
4473
|
-
const subCmd = cmd.replace(
|
|
4962
|
+
const subCmd = cmd.replace(currentCmdPrefix, "").replace(/\s+/g, "");
|
|
4474
4963
|
const target = params[0];
|
|
4475
4964
|
switch (subCmd) {
|
|
4476
4965
|
case "help":
|
|
@@ -4692,7 +5181,29 @@ const core = definePlugin({
|
|
|
4692
5181
|
process.exit(0);
|
|
4693
5182
|
}
|
|
4694
5183
|
}
|
|
4695
|
-
}
|
|
5184
|
+
};
|
|
5185
|
+
for (const command of [
|
|
5186
|
+
{
|
|
5187
|
+
name: "plugin",
|
|
5188
|
+
aliases: ["插件"],
|
|
5189
|
+
description: "插件管理"
|
|
5190
|
+
},
|
|
5191
|
+
{
|
|
5192
|
+
name: "settings",
|
|
5193
|
+
aliases: ["设置"],
|
|
5194
|
+
description: "框架设置管理"
|
|
5195
|
+
},
|
|
5196
|
+
{
|
|
5197
|
+
name: "exit",
|
|
5198
|
+
aliases: ["退出"],
|
|
5199
|
+
description: "退出框架进程"
|
|
5200
|
+
}
|
|
5201
|
+
]) ctx.command({
|
|
5202
|
+
...command,
|
|
5203
|
+
permission: "master",
|
|
5204
|
+
priority: -1e3,
|
|
5205
|
+
handler: ({ event }) => handleCoreMessage(event)
|
|
5206
|
+
});
|
|
4696
5207
|
rootLogger.info("========================================");
|
|
4697
5208
|
rootLogger.info(" Mioku 服务初始化完成");
|
|
4698
5209
|
rootLogger.info("========================================");
|
|
@@ -5100,6 +5611,7 @@ var MiokuRuntime = class {
|
|
|
5100
5611
|
#driverFactory;
|
|
5101
5612
|
#builtinPlugins;
|
|
5102
5613
|
#correlator;
|
|
5614
|
+
#commands;
|
|
5103
5615
|
#started = false;
|
|
5104
5616
|
#stopped = false;
|
|
5105
5617
|
constructor(options) {
|
|
@@ -5117,6 +5629,14 @@ var MiokuRuntime = class {
|
|
|
5117
5629
|
});
|
|
5118
5630
|
this.#bots = new BotRegistry();
|
|
5119
5631
|
this.#capabilities = new CapabilityRegistry();
|
|
5632
|
+
this.#commands = new CommandManager({
|
|
5633
|
+
getDefaultPrefix: () => String(botConfig.prefix ?? "."),
|
|
5634
|
+
getOwners: () => botConfig.owners,
|
|
5635
|
+
getAdmins: () => botConfig.admins,
|
|
5636
|
+
logger: this.#logger
|
|
5637
|
+
});
|
|
5638
|
+
this.#bus.setFilter((registration, event) => this.#commands.shouldDispatch(registration.source, event));
|
|
5639
|
+
setActiveCommandManager(this.#commands);
|
|
5120
5640
|
}
|
|
5121
5641
|
get cwd() {
|
|
5122
5642
|
return this.#cwd;
|
|
@@ -5130,6 +5650,9 @@ var MiokuRuntime = class {
|
|
|
5130
5650
|
get bus() {
|
|
5131
5651
|
return this.#bus;
|
|
5132
5652
|
}
|
|
5653
|
+
get commands() {
|
|
5654
|
+
return this.#commands;
|
|
5655
|
+
}
|
|
5133
5656
|
/** 事件关联器(跨适配器去重);为 undefined 时表示已禁用 */
|
|
5134
5657
|
get correlator() {
|
|
5135
5658
|
return this.#correlator;
|
|
@@ -5148,14 +5671,16 @@ var MiokuRuntime = class {
|
|
|
5148
5671
|
return this.#bots.pick(bot_id);
|
|
5149
5672
|
}
|
|
5150
5673
|
listPlugins() {
|
|
5151
|
-
return Array.from(this.#enabledPlugins.entries()).map(([key
|
|
5674
|
+
return Array.from(this.#enabledPlugins.entries()).map(([key]) => {
|
|
5152
5675
|
const colon = key.indexOf(":");
|
|
5153
5676
|
const type = key.slice(0, colon);
|
|
5154
5677
|
const name = key.slice(colon + 1);
|
|
5678
|
+
const isExternal = type !== "builtin";
|
|
5679
|
+
const version$2 = isExternal ? getPluginMetadata(name)?.version : void 0;
|
|
5155
5680
|
return {
|
|
5156
5681
|
name,
|
|
5157
5682
|
type: type === "builtin" ? "builtin" : "external",
|
|
5158
|
-
version:
|
|
5683
|
+
version: version$2
|
|
5159
5684
|
};
|
|
5160
5685
|
});
|
|
5161
5686
|
}
|
|
@@ -5242,13 +5767,16 @@ var MiokuRuntime = class {
|
|
|
5242
5767
|
capabilities: this.#capabilities,
|
|
5243
5768
|
logger: this.#logger.child({ adapter: state.definition.name }),
|
|
5244
5769
|
emit: (event) => this.#emitLifecycle(event),
|
|
5245
|
-
correlator: this.#correlator
|
|
5770
|
+
correlator: this.#correlator,
|
|
5771
|
+
commands: this.#commands
|
|
5246
5772
|
});
|
|
5247
5773
|
}
|
|
5248
5774
|
async #loadPlugin(plugin, type) {
|
|
5249
5775
|
const cleanupTasks = [];
|
|
5250
5776
|
const ctx = new MiokuContext({
|
|
5251
5777
|
pluginName: `${type}:${plugin.name}`,
|
|
5778
|
+
pluginId: plugin.name === "mioku-core" ? "core" : plugin.name,
|
|
5779
|
+
commands: this.#commands,
|
|
5252
5780
|
bus: this.#bus,
|
|
5253
5781
|
bots: this.#bots,
|
|
5254
5782
|
driver: this.#driver,
|
|
@@ -5546,6 +6074,8 @@ var MiokuRuntime = class {
|
|
|
5546
6074
|
this.#capabilities.clear();
|
|
5547
6075
|
this.#bots.clear();
|
|
5548
6076
|
this.#correlator?.clear();
|
|
6077
|
+
this.#commands.clear();
|
|
6078
|
+
setActiveCommandManager(void 0);
|
|
5549
6079
|
resetPluginMetadata();
|
|
5550
6080
|
try {
|
|
5551
6081
|
await this.#driver.shutdown();
|
|
@@ -5562,22 +6092,38 @@ const createRuntime = (options) => {
|
|
|
5562
6092
|
|
|
5563
6093
|
//#endregion
|
|
5564
6094
|
//#region src/runtime/plugin-artifacts.ts
|
|
6095
|
+
const commandKey = (value) => String(value).trim().replace(/^[#/.]+\s*/, "").split(/\s+/)[0].toLowerCase();
|
|
5565
6096
|
async function registerPluginArtifacts(ctx) {
|
|
5566
6097
|
const helpService = ctx ? getService(ctx, Services.Help) : servicesRegistry["help"];
|
|
5567
6098
|
const plugins = getPluginMetadataList();
|
|
6099
|
+
const commandManager = getActiveCommandManager();
|
|
6100
|
+
const metadataByName = new Map(plugins.map((meta) => [meta.name, meta]));
|
|
6101
|
+
const pluginNames = new Set([...metadataByName.keys(), ...(commandManager?.catalog() ?? []).filter((item) => item.kind === "plugin").map((item) => item.plugin)]);
|
|
5568
6102
|
let helpCount = 0;
|
|
5569
|
-
for (const
|
|
5570
|
-
|
|
5571
|
-
|
|
6103
|
+
for (const pluginName of pluginNames) {
|
|
6104
|
+
const meta = metadataByName.get(pluginName);
|
|
6105
|
+
const generated = commandManager?.getPluginHelp(pluginName) ?? meta?.config?.help;
|
|
6106
|
+
const existing = helpService?.getHelp(pluginName);
|
|
6107
|
+
const help = generated ? {
|
|
6108
|
+
...generated,
|
|
6109
|
+
title: existing?.title || generated.title,
|
|
6110
|
+
description: existing?.description || generated.description,
|
|
6111
|
+
commands: [...existing?.commands ?? [], ...generated.commands.filter((command) => {
|
|
6112
|
+
const key = commandKey(command.cmd);
|
|
6113
|
+
return !(existing?.commands ?? []).some((item) => commandKey(item.cmd) === key);
|
|
6114
|
+
})]
|
|
6115
|
+
} : existing;
|
|
6116
|
+
if (help) if (helpService) {
|
|
6117
|
+
helpService.registerHelp(pluginName, help);
|
|
5572
6118
|
helpCount++;
|
|
5573
|
-
} else rootLogger.warn(`[plugin-artifacts] 帮助服务未加载,跳过插件 "${
|
|
5574
|
-
const required = meta
|
|
6119
|
+
} else rootLogger.warn(`[plugin-artifacts] 帮助服务未加载,跳过插件 "${pluginName}" 的帮助注册`);
|
|
6120
|
+
const required = meta?.config?.services;
|
|
5575
6121
|
if (required && required.length > 0) {
|
|
5576
6122
|
const missing = required.filter((name) => !servicesRegistry[name]);
|
|
5577
|
-
if (missing.length > 0) rootLogger.warn(`插件 "${
|
|
6123
|
+
if (missing.length > 0) rootLogger.warn(`插件 "${pluginName}" 声明依赖服务 [${missing.join(", ")}],但它们未加载`);
|
|
5578
6124
|
}
|
|
5579
6125
|
}
|
|
5580
|
-
rootLogger.info(`[plugin-artifacts] 已处理 ${
|
|
6126
|
+
rootLogger.info(`[plugin-artifacts] 已处理 ${pluginNames.size} 个插件,注册帮助 ${helpCount} 个`);
|
|
5581
6127
|
}
|
|
5582
6128
|
|
|
5583
6129
|
//#endregion
|
|
@@ -5732,5 +6278,5 @@ function installProcessErrorGuards() {
|
|
|
5732
6278
|
}
|
|
5733
6279
|
|
|
5734
6280
|
//#endregion
|
|
5735
|
-
export { AI_GEMINI_THINKING_LEVELS, AI_THINKING_LEVELS, AdapterContextImpl, AdapterRegistrationConflictError, ArchMap, BOT_CWD, BUILTIN_PLUGINS, BotRegistry, CORE_PLUGINS, CapabilityRegistry, ChromeUA, DriverShutdownError, EventBus, EventCorrelator, HttpRequestError, MessageSegmentImpl, MiokuContext, MiokuRuntime, ServiceManager, Services, SystemMap, TOOL_RESULT_FOLLOWUP_KEY, UnsupportedCapabilityError, WebSocketConnectTimeoutError, addService, applyLogLevel, asMessage, atOf, avatarGet, avatarSet, base64Decode, base64Encode, bindCapabilities, botConfig, botStatus, box, buildAdapterReport, buildMiokuStatus, buildRoutes, buildSpawnPlan, clamp, collectBotStats, colorize, colors, commandExists, connectedBotKey, connectedBots, conversationGetHistory, createCmd, createDB, createDefaultDriver, createFriendRef, createGroupRef, createImportContext, createMessage, createMiokuLogger, createRuntime, createStore, dayjs, dedent, defineAdapter, defineCapability, definePlugin, defineService, defineState, deleteServiceConfig, discoverAdapterCandidates, discoverPluginCandidates, displayAdapterId, ensureDataDir, filesize, filter, find, findLocalPlugins, formatAdapterReport, formatDuration, formatMiokuStatus, formatMiokuStatusFn, forwardSend, friendDelete, friendGetInfo, friendGetList, fs, getAllPluginRuntimeStates, getBuiltinPlugins, getConfigDir, getDataDir, getGTk, getPluginConfigDir, getPluginDataDir, getPluginMetadata, getPluginMetadataList, getPluginRuntimeState, getService, getServiceConfig, getServiceConfigDir, getServiceConfigs, getServiceDataDir, getTerminalInput, groupGetInfo, groupGetList, groupGetMembers, groupLeave, groupSetName, groupSetPortrait, groupSetWholeBan, hasEventRight, hasPluginState, hasRight, hasService, isAdapterEvent, isAdmin, isBoolean, isBotEvent, isDefined, isEventAdmin, isEventOwner, isEventOwnerOrAdmin, isFunction, isMessage, isMessageEvent, isMetaEvent, isMiokuPlugin, isNoticeEvent, isNumber, isObject, isOwner, isOwnerOrAdmin, isRequestEvent, isSegment, isString, loadAdapterDefinition, loadLocalPlugin, loadNpmPlugin, localNum, localeDate, localeTime, rootLogger as logger, match, md5, memberBan, memberGetInfo, memberKick, memberPoke, memberSetAdmin, memberSetCard, memberSetTitle, messageGet, messageGetForward, messageRecall, messageSend, mriLib as mri, noNullish, normalizeAIThinkingLevel, normalizeAdmins, normalizeOwners, normalizePlugins, normalizeSkillPermissionRole, noticeAdmins, noticeFriends, noticeGroups, noticeOwners, path, prettyMs, profileSet, qs, randomId, randomInt, randomItem, randomItems, readMiokuConfig, readPackageJson, readPackageJsonSafe, registerPluginArtifacts, registerServiceConfig, registerStatusProvider, reloadMiokuConfig, removePluginMetadata, requireService, resetPluginMetadata, resetPluginRuntimeState, resolveCommand, resolveEntry, resolveLocalPluginEntry, resolveLocalPluginPath, rootLogger, runCommand, runCommandInherit, segment, sendMessage, manager_default as serviceManager, servicesRegistry, setBotCwd, setBuiltinPlugins, setPluginMetadata, setPluginRuntimeState, setService, setWritableConfig, start, startRuntime, string2argv, stringifyError, stripAnsi, text, toArray, toId, toUserId, unique, updateMiokuConfig, updateServiceConfig, uuid, version$1 as version, wait, writePackageJson };
|
|
6281
|
+
export { AI_GEMINI_THINKING_LEVELS, AI_THINKING_LEVELS, AdapterContextImpl, AdapterRegistrationConflictError, ArchMap, BOT_CWD, BUILTIN_PLUGINS, BotRegistry, CORE_PLUGINS, CapabilityRegistry, ChromeUA, CommandManager, DriverShutdownError, EventBus, EventCorrelator, HttpRequestError, MessageSegmentImpl, MiokuContext, MiokuRuntime, ServiceManager, Services, SystemMap, TOOL_RESULT_FOLLOWUP_KEY, UnsupportedCapabilityError, WebSocketConnectTimeoutError, addService, applyLogLevel, asMessage, atOf, avatarGet, avatarSet, base64Decode, base64Encode, bindCapabilities, botConfig, botStatus, box, buildAdapterReport, buildMiokuStatus, buildRoutes, buildSpawnPlan, clamp, collectBotStats, colorize, colors, commandExists, connectedBotKey, connectedBots, conversationGetHistory, createCmd, createDB, createDefaultDriver, createFriendRef, createGroupRef, createImportContext, createMessage, createMiokuLogger, createRuntime, createStore, dayjs, dedent, defineAdapter, defineCapability, definePlugin, defineService, defineState, deleteServiceConfig, discoverAdapterCandidates, discoverPluginCandidates, displayAdapterId, ensureDataDir, filesize, filter, find, findLocalPlugins, formatAdapterReport, formatDuration, formatMiokuStatus, formatMiokuStatusFn, forwardSend, friendDelete, friendGetInfo, friendGetList, fs, getActiveCommandManager, getAllPluginRuntimeStates, getBuiltinPlugins, getConfigDir, getDataDir, getGTk, getPluginConfigDir, getPluginDataDir, getPluginMetadata, getPluginMetadataList, getPluginRuntimeState, getService, getServiceConfig, getServiceConfigDir, getServiceConfigs, getServiceDataDir, getTerminalInput, groupGetInfo, groupGetList, groupGetMembers, groupLeave, groupSetName, groupSetPortrait, groupSetWholeBan, hasEventRight, hasPluginState, hasRight, hasService, isAdapterEvent, isAdmin, isBoolean, isBotEvent, isDefined, isEventAdmin, isEventAdminConfigOnly, isEventGroupAdmin, isEventGroupOwner, isEventMaster, isEventOwner, isEventOwnerOrAdmin, isFunction, isMessage, isMessageEvent, isMetaEvent, isMiokuPlugin, isNoticeEvent, isNumber, isObject, isOwner, isOwnerOrAdmin, isRequestEvent, isSegment, isString, loadAdapterDefinition, loadLocalPlugin, loadNpmPlugin, localNum, localeDate, localeTime, rootLogger as logger, match, md5, memberBan, memberGetInfo, memberKick, memberPoke, memberSetAdmin, memberSetCard, memberSetTitle, messageGet, messageGetForward, messageRecall, messageSend, mriLib as mri, noNullish, normalizeAIThinkingLevel, normalizeAdmins, normalizeOwners, normalizePlugins, normalizeSkillPermissionRole, noticeAdmins, noticeFriends, noticeGroups, noticeOwners, path, prettyMs, profileSet, qs, randomId, randomInt, randomItem, randomItems, readMiokuConfig, readPackageJson, readPackageJsonSafe, registerPluginArtifacts, registerServiceConfig, registerStatusProvider, reloadMiokuConfig, removePluginMetadata, requireService, resetPluginMetadata, resetPluginRuntimeState, resolveCommand, resolveEntry, resolveLocalPluginEntry, resolveLocalPluginPath, rootLogger, runCommand, runCommandInherit, segment, sendMessage, manager_default as serviceManager, servicesRegistry, setActiveCommandManager, setBotCwd, setBuiltinPlugins, setPluginMetadata, setPluginRuntimeState, setService, setWritableConfig, start, startRuntime, string2argv, stringifyError, stripAnsi, text, toArray, toId, toUserId, unique, updateMiokuConfig, updateServiceConfig, uuid, version$1 as version, wait, writePackageJson };
|
|
5736
6282
|
//# sourceMappingURL=index.js.map
|