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.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_exec = require('./exec-
|
|
1
|
+
const require_exec = require('./exec-BUUBQy_V.cjs');
|
|
2
2
|
const node_fs = require_exec.__toESM(require("node:fs"));
|
|
3
3
|
const node_path = require_exec.__toESM(require("node:path"));
|
|
4
4
|
const node_url = require_exec.__toESM(require("node:url"));
|
|
@@ -856,6 +856,7 @@ var AdapterContextImpl = class {
|
|
|
856
856
|
#logger;
|
|
857
857
|
#emit;
|
|
858
858
|
#correlator;
|
|
859
|
+
#commands;
|
|
859
860
|
#pendingStarts = new Set();
|
|
860
861
|
constructor(options) {
|
|
861
862
|
this.#state = options.state;
|
|
@@ -866,6 +867,7 @@ var AdapterContextImpl = class {
|
|
|
866
867
|
this.#logger = options.logger;
|
|
867
868
|
this.#emit = options.emit;
|
|
868
869
|
this.#correlator = options.correlator;
|
|
870
|
+
this.#commands = options.commands;
|
|
869
871
|
}
|
|
870
872
|
registerBot(bot) {
|
|
871
873
|
return this.#bots.register(bot);
|
|
@@ -940,6 +942,12 @@ var AdapterContextImpl = class {
|
|
|
940
942
|
}
|
|
941
943
|
}
|
|
942
944
|
}
|
|
945
|
+
if (event.kind === "message") {
|
|
946
|
+
const duplicate = this.#correlator?.observationOf(event)?.duplicate ?? false;
|
|
947
|
+
if (!duplicate) {
|
|
948
|
+
if (await this.#commands.dispatch(event)) return;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
943
951
|
await this.#bus.dispatch(event);
|
|
944
952
|
}
|
|
945
953
|
listen(route, handler) {
|
|
@@ -1055,9 +1063,13 @@ var EventBus = class {
|
|
|
1055
1063
|
#nextOrder = 0;
|
|
1056
1064
|
#dispatched = 0;
|
|
1057
1065
|
#logger = null;
|
|
1066
|
+
#filter = null;
|
|
1058
1067
|
setLogger(logger) {
|
|
1059
1068
|
this.#logger = logger;
|
|
1060
1069
|
}
|
|
1070
|
+
setFilter(filter$1) {
|
|
1071
|
+
this.#filter = filter$1;
|
|
1072
|
+
}
|
|
1061
1073
|
/** 注册事件监听,返回取消注册函数 */
|
|
1062
1074
|
register(route, handler, options = {}) {
|
|
1063
1075
|
if (!route) throw new Error("EventBus.register: route must be a non-empty string");
|
|
@@ -1091,6 +1103,11 @@ var EventBus = class {
|
|
|
1091
1103
|
const matched = [];
|
|
1092
1104
|
for (const reg of this.#registrations) {
|
|
1093
1105
|
if (reg.disposed) continue;
|
|
1106
|
+
if (this.#filter) try {
|
|
1107
|
+
if (!this.#filter(reg, event)) continue;
|
|
1108
|
+
} catch (err) {
|
|
1109
|
+
this.#logger?.("error", `Event registration filter failed for "${reg.source}"`, err);
|
|
1110
|
+
}
|
|
1094
1111
|
if (isWildcardMatch(reg.route, event.type)) {
|
|
1095
1112
|
matched.push(reg);
|
|
1096
1113
|
continue;
|
|
@@ -1129,6 +1146,57 @@ var EventBus = class {
|
|
|
1129
1146
|
}
|
|
1130
1147
|
};
|
|
1131
1148
|
|
|
1149
|
+
//#endregion
|
|
1150
|
+
//#region src/runtime/permissions.ts
|
|
1151
|
+
const isObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1152
|
+
/** 从事件对象中提取发送者 id(支持裸 id / user_id / sender.user_id) */
|
|
1153
|
+
const toUserId = (value) => {
|
|
1154
|
+
if (typeof value === "number" || typeof value === "string" || typeof value === "bigint") return String(value);
|
|
1155
|
+
if (isObject$1(value)) {
|
|
1156
|
+
if ("user_id" in value) return toUserId(value.user_id);
|
|
1157
|
+
if ("sender" in value) {
|
|
1158
|
+
const sender = value.sender;
|
|
1159
|
+
if (isObject$1(sender) && "user_id" in sender) return toUserId(sender.user_id);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return void 0;
|
|
1163
|
+
};
|
|
1164
|
+
/** 是否群消息 */
|
|
1165
|
+
const isGroupMessage = (event) => isObject$1(event) && event.kind === "message" && event.message_type === "group";
|
|
1166
|
+
/** 当前群群主(仅限所在群) */
|
|
1167
|
+
const isEventGroupOwner = (event) => {
|
|
1168
|
+
if (!isGroupMessage(event)) return false;
|
|
1169
|
+
return event.sender?.role === "owner";
|
|
1170
|
+
};
|
|
1171
|
+
/** 当前群群主或群管理员(仅限所在群) */
|
|
1172
|
+
const isEventGroupAdmin = (event) => {
|
|
1173
|
+
if (!isGroupMessage(event)) return false;
|
|
1174
|
+
const role = event.sender?.role;
|
|
1175
|
+
return role === "owner" || role === "admin";
|
|
1176
|
+
};
|
|
1177
|
+
/** 仅看 owners 名单:bot 主人(无群身份加成) */
|
|
1178
|
+
const isEventMaster$1 = (event) => {
|
|
1179
|
+
const id = toUserId(event);
|
|
1180
|
+
if (!id) return false;
|
|
1181
|
+
return isOwner(id);
|
|
1182
|
+
};
|
|
1183
|
+
/** 仅看 admins 名单:配置管理员 */
|
|
1184
|
+
const isEventAdminConfigOnly = (event) => {
|
|
1185
|
+
const id = toUserId(event);
|
|
1186
|
+
if (!id) return false;
|
|
1187
|
+
return isAdmin(id);
|
|
1188
|
+
};
|
|
1189
|
+
/**
|
|
1190
|
+
* master 或当前群群主
|
|
1191
|
+
*/
|
|
1192
|
+
const isEventOwner$1 = (event) => isEventMaster$1(event) || isEventGroupOwner(event);
|
|
1193
|
+
/**
|
|
1194
|
+
* master、配置管理员、当前群群主或群管理员
|
|
1195
|
+
*/
|
|
1196
|
+
const isEventAdmin$1 = (event) => isEventOwner$1(event) || isEventAdminConfigOnly(event) || isEventGroupAdmin(event);
|
|
1197
|
+
const isEventOwnerOrAdmin$1 = (event) => isEventAdmin$1(event);
|
|
1198
|
+
const hasEventRight$1 = (event) => isEventOwnerOrAdmin$1(event);
|
|
1199
|
+
|
|
1132
1200
|
//#endregion
|
|
1133
1201
|
//#region src/utils.ts
|
|
1134
1202
|
const ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/131.0.0.0";
|
|
@@ -1896,31 +1964,18 @@ const Services = {
|
|
|
1896
1964
|
|
|
1897
1965
|
//#endregion
|
|
1898
1966
|
//#region src/runtime/mioku-context.ts
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
};
|
|
1912
|
-
const isEventOwner = (event) => {
|
|
1913
|
-
const id = toUserId(event);
|
|
1914
|
-
if (!id) return false;
|
|
1915
|
-
return isOwner(id);
|
|
1916
|
-
};
|
|
1917
|
-
const isEventAdmin = (event) => {
|
|
1918
|
-
const id = toUserId(event);
|
|
1919
|
-
if (!id) return false;
|
|
1920
|
-
return isAdmin(id);
|
|
1921
|
-
};
|
|
1922
|
-
const isEventOwnerOrAdmin = (event) => isEventOwner(event) || isEventAdmin(event);
|
|
1923
|
-
const hasEventRight = (event) => isEventOwnerOrAdmin(event);
|
|
1967
|
+
/** 仅看 owners 名单:bot 主人(无群身份加成) */
|
|
1968
|
+
const isEventMaster = (event) => isEventMaster$1(event);
|
|
1969
|
+
/**
|
|
1970
|
+
* master 或当前群群主。等价于 `ctx.command({ permission: "owner" })` 的判断。
|
|
1971
|
+
*/
|
|
1972
|
+
const isEventOwner = (event) => isEventOwner$1(event);
|
|
1973
|
+
/**
|
|
1974
|
+
* master、配置管理员、当前群群主或群管理员。等价于 `ctx.command({ permission: "admin" })` 的判断。
|
|
1975
|
+
*/
|
|
1976
|
+
const isEventAdmin = (event) => isEventAdmin$1(event);
|
|
1977
|
+
const isEventOwnerOrAdmin = (event) => isEventOwnerOrAdmin$1(event);
|
|
1978
|
+
const hasEventRight = (event) => hasEventRight$1(event);
|
|
1924
1979
|
const CTX_UTILS = {
|
|
1925
1980
|
localeDate,
|
|
1926
1981
|
localeTime,
|
|
@@ -1975,6 +2030,9 @@ var MiokuContext = class {
|
|
|
1975
2030
|
get pluginName() {
|
|
1976
2031
|
return this.#options.pluginName;
|
|
1977
2032
|
}
|
|
2033
|
+
get pluginId() {
|
|
2034
|
+
return this.#options.pluginId;
|
|
2035
|
+
}
|
|
1978
2036
|
/** 第一个已连接的 bot(多数单 bot 场景直接用这个) */
|
|
1979
2037
|
get bot() {
|
|
1980
2038
|
return this.#options.bots.all()[0];
|
|
@@ -2074,7 +2132,7 @@ var MiokuContext = class {
|
|
|
2074
2132
|
const inputRoutes = Array.isArray(route) ? route : [route];
|
|
2075
2133
|
const bypassDedup = inputRoutes.some((item) => item.startsWith("!"));
|
|
2076
2134
|
const routes = inputRoutes.map((item) => item.startsWith("!") ? item.slice(1) : item);
|
|
2077
|
-
const source = `plugin:${this.#options.
|
|
2135
|
+
const source = `plugin:${this.#options.pluginId}`;
|
|
2078
2136
|
const handledEvents = new WeakSet();
|
|
2079
2137
|
const correlator = bypassDedup ? void 0 : this.#options.correlator;
|
|
2080
2138
|
const wrappedHandler = async (event) => {
|
|
@@ -2096,6 +2154,17 @@ var MiokuContext = class {
|
|
|
2096
2154
|
this.#addCleanup(dispose);
|
|
2097
2155
|
return dispose;
|
|
2098
2156
|
}
|
|
2157
|
+
command(definitionOrMatcher, handler, options = {}) {
|
|
2158
|
+
const dispose = typeof definitionOrMatcher === "object" && definitionOrMatcher !== null && "name" in definitionOrMatcher ? this.#options.commands.register(this.#options.pluginId, {
|
|
2159
|
+
...definitionOrMatcher,
|
|
2160
|
+
priority: definitionOrMatcher.priority ?? this.#options.priority
|
|
2161
|
+
}, this) : this.#options.commands.register(this.#options.pluginId, definitionOrMatcher, handler, {
|
|
2162
|
+
...options,
|
|
2163
|
+
priority: options.priority ?? this.#options.priority
|
|
2164
|
+
}, this);
|
|
2165
|
+
this.#addCleanup(dispose);
|
|
2166
|
+
return dispose;
|
|
2167
|
+
}
|
|
2099
2168
|
/** 注册 cron 定时任务,插件卸载时自动停止 */
|
|
2100
2169
|
cron(expression, handler) {
|
|
2101
2170
|
const task = node_cron.default.schedule(expression, async (taskContext) => {
|
|
@@ -2152,6 +2221,10 @@ var MiokuContext = class {
|
|
|
2152
2221
|
get buses() {
|
|
2153
2222
|
return this.#options.bus;
|
|
2154
2223
|
}
|
|
2224
|
+
/** 当前运行时的命令管理器 */
|
|
2225
|
+
get commands() {
|
|
2226
|
+
return this.#options.commands;
|
|
2227
|
+
}
|
|
2155
2228
|
get segment() {
|
|
2156
2229
|
return segment;
|
|
2157
2230
|
}
|
|
@@ -2192,24 +2265,30 @@ var MiokuContext = class {
|
|
|
2192
2265
|
return noticeAdmins(this.#options.bots.all(), this.#options.config.admins, message, options);
|
|
2193
2266
|
}
|
|
2194
2267
|
isGroupMsg(event) {
|
|
2195
|
-
return isObject
|
|
2268
|
+
return isObject(event) && event.kind === "message" && event.message_type === "group";
|
|
2196
2269
|
}
|
|
2197
2270
|
isPrivateMsg(event) {
|
|
2198
|
-
return isObject
|
|
2271
|
+
return isObject(event) && event.kind === "message" && event.message_type === "private";
|
|
2199
2272
|
}
|
|
2200
|
-
/**
|
|
2273
|
+
/** 事件发送者是否为 bot 主人(仅看 owners 名单,不识别群身份) */
|
|
2274
|
+
isMaster(event) {
|
|
2275
|
+
return isEventMaster(event);
|
|
2276
|
+
}
|
|
2277
|
+
/** 事件发送者是否为 bot 主人或当前群群主;等价于 `permission: "owner"` */
|
|
2201
2278
|
isOwner(event) {
|
|
2202
2279
|
return isEventOwner(event);
|
|
2203
2280
|
}
|
|
2204
|
-
/**
|
|
2281
|
+
/** 事件发送者是否为 bot 主人、配置管理员、当前群群主或群管理员;等价于 `permission: "admin"` */
|
|
2205
2282
|
isAdmin(event) {
|
|
2206
2283
|
return isEventAdmin(event);
|
|
2207
2284
|
}
|
|
2285
|
+
/** 同 `isAdmin`,保留旧名称 */
|
|
2208
2286
|
isOwnerOrAdmin(event) {
|
|
2209
|
-
return
|
|
2287
|
+
return isEventAdmin(event);
|
|
2210
2288
|
}
|
|
2289
|
+
/** 同 `isAdmin`,别名 */
|
|
2211
2290
|
hasRight(event) {
|
|
2212
|
-
return
|
|
2291
|
+
return isEventAdmin(event);
|
|
2213
2292
|
}
|
|
2214
2293
|
async dispose() {
|
|
2215
2294
|
const all = Array.from(this.#cleanup);
|
|
@@ -2500,19 +2579,387 @@ var EventCorrelator = class {
|
|
|
2500
2579
|
}
|
|
2501
2580
|
};
|
|
2502
2581
|
|
|
2582
|
+
//#endregion
|
|
2583
|
+
//#region src/runtime/commands.ts
|
|
2584
|
+
const normalizeRole = (role) => {
|
|
2585
|
+
const value = String(role ?? "member").trim().toLowerCase();
|
|
2586
|
+
if (value === "master" || value === "owner" || value === "admin") return value;
|
|
2587
|
+
return "member";
|
|
2588
|
+
};
|
|
2589
|
+
const userIdOf = (event) => toUserId(event);
|
|
2590
|
+
const textHookMatches = (text$1, raw) => {
|
|
2591
|
+
const source = String(raw ?? "").trim();
|
|
2592
|
+
if (!source) return false;
|
|
2593
|
+
if (source.startsWith("/") && source.length > 1) {
|
|
2594
|
+
const end = source.lastIndexOf("/");
|
|
2595
|
+
if (end > 0) try {
|
|
2596
|
+
const flags = source.slice(end + 1);
|
|
2597
|
+
const regex = new RegExp(source.slice(1, end), flags);
|
|
2598
|
+
return regex.test(text$1);
|
|
2599
|
+
} catch {
|
|
2600
|
+
return false;
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
return text$1 === source || text$1.startsWith(source);
|
|
2604
|
+
};
|
|
2605
|
+
const routeMatches = (event, route) => {
|
|
2606
|
+
if (!route) return false;
|
|
2607
|
+
return event.type === route || event.routes.some((item) => item === route);
|
|
2608
|
+
};
|
|
2609
|
+
const parsePrefixes = (prefixes, defaultPrefix) => {
|
|
2610
|
+
if (prefixes === false) return ["", defaultPrefix];
|
|
2611
|
+
const values = Array.isArray(prefixes) ? prefixes : [prefixes];
|
|
2612
|
+
const normalized = values.map((value) => String(value)).filter((value, index, all) => all.indexOf(value) === index);
|
|
2613
|
+
return normalized.length > 0 ? normalized : [defaultPrefix];
|
|
2614
|
+
};
|
|
2615
|
+
const commandBody = (text$1, prefixes) => {
|
|
2616
|
+
const prefix = [...prefixes].sort((a, b) => b.length - a.length).find((item) => item === "" || text$1.startsWith(item));
|
|
2617
|
+
if (prefix === void 0) return void 0;
|
|
2618
|
+
return text$1.slice(prefix.length).trimStart();
|
|
2619
|
+
};
|
|
2620
|
+
const stringMatch = (body, matcher) => {
|
|
2621
|
+
const value = matcher.trim();
|
|
2622
|
+
if (!value) return void 0;
|
|
2623
|
+
if (body !== value && !(body.startsWith(value) && /\s/.test(body[value.length] ?? ""))) return void 0;
|
|
2624
|
+
const rest = body.slice(value.length).trim();
|
|
2625
|
+
return { args: rest ? rest.split(/\s+/) : [] };
|
|
2626
|
+
};
|
|
2627
|
+
const commandKey$1 = (value) => String(value).trim().replace(/^[#/.]+\s*/, "").split(/\s+/)[0].toLowerCase();
|
|
2628
|
+
const displayCommand = (command, defaultPrefix) => {
|
|
2629
|
+
if (command.prefixes === false) return command.name;
|
|
2630
|
+
const prefixes = command.prefixes === void 0 ? [defaultPrefix] : Array.isArray(command.prefixes) ? command.prefixes : [command.prefixes];
|
|
2631
|
+
return `${prefixes.find((prefix) => prefix.length > 0) ?? ""}${command.name}`;
|
|
2632
|
+
};
|
|
2633
|
+
var CommandManager = class {
|
|
2634
|
+
#getDefaultPrefix;
|
|
2635
|
+
#getOwners;
|
|
2636
|
+
#getAdmins;
|
|
2637
|
+
#logger;
|
|
2638
|
+
#commands = [];
|
|
2639
|
+
#legacy = new Map();
|
|
2640
|
+
#access;
|
|
2641
|
+
#nextOrder = 0;
|
|
2642
|
+
constructor(options) {
|
|
2643
|
+
this.#getDefaultPrefix = options.getDefaultPrefix;
|
|
2644
|
+
this.#getOwners = options.getOwners;
|
|
2645
|
+
this.#getAdmins = options.getAdmins;
|
|
2646
|
+
this.#logger = options.logger;
|
|
2647
|
+
}
|
|
2648
|
+
register(plugin, definitionOrMatcher, handlerOrContext, optionsOrContext = {}, context) {
|
|
2649
|
+
const isDefinition = typeof definitionOrMatcher === "object" && definitionOrMatcher !== null && "name" in definitionOrMatcher;
|
|
2650
|
+
const shortcutOptions = isDefinition ? {} : optionsOrContext;
|
|
2651
|
+
const resolvedContext = isDefinition ? handlerOrContext : context;
|
|
2652
|
+
const handler = isDefinition ? void 0 : handlerOrContext;
|
|
2653
|
+
const definition = isDefinition ? definitionOrMatcher : {
|
|
2654
|
+
...shortcutOptions,
|
|
2655
|
+
name: shortcutOptions.name ?? String(definitionOrMatcher),
|
|
2656
|
+
match: definitionOrMatcher,
|
|
2657
|
+
handler
|
|
2658
|
+
};
|
|
2659
|
+
if (typeof definition.handler !== "function") throw new Error("CommandManager.register: handler must be a function");
|
|
2660
|
+
if (!resolvedContext) throw new Error("CommandManager.register: context is required");
|
|
2661
|
+
const name = String(definition.name ?? "").trim();
|
|
2662
|
+
if (!name) throw new Error("CommandManager.register: name is required");
|
|
2663
|
+
const matcher = definition.match ?? name;
|
|
2664
|
+
const command = {
|
|
2665
|
+
plugin,
|
|
2666
|
+
id: String(definition.id ?? name).trim() || name,
|
|
2667
|
+
name,
|
|
2668
|
+
aliases: (definition.aliases ?? []).map((item) => String(item).trim()).filter(Boolean),
|
|
2669
|
+
matcher,
|
|
2670
|
+
description: String(definition.description ?? "").trim(),
|
|
2671
|
+
usage: definition.usage,
|
|
2672
|
+
permission: normalizeRole(definition.permission),
|
|
2673
|
+
priority: typeof definition.priority === "number" && !Number.isNaN(definition.priority) ? definition.priority : 0,
|
|
2674
|
+
prefixes: definition.prefixes,
|
|
2675
|
+
handler: definition.handler,
|
|
2676
|
+
order: this.#nextOrder++,
|
|
2677
|
+
context: resolvedContext
|
|
2678
|
+
};
|
|
2679
|
+
this.#commands.push(command);
|
|
2680
|
+
return () => {
|
|
2681
|
+
const index = this.#commands.indexOf(command);
|
|
2682
|
+
if (index >= 0) this.#commands.splice(index, 1);
|
|
2683
|
+
};
|
|
2684
|
+
}
|
|
2685
|
+
setAccessControl(config) {
|
|
2686
|
+
this.#access = config;
|
|
2687
|
+
}
|
|
2688
|
+
syncMetadata(metadata) {
|
|
2689
|
+
this.#legacy.set(metadata.name, { metadata });
|
|
2690
|
+
}
|
|
2691
|
+
removePlugin(plugin) {
|
|
2692
|
+
this.#legacy.delete(plugin);
|
|
2693
|
+
for (let i = this.#commands.length - 1; i >= 0; i--) if (this.#commands[i].plugin === plugin) this.#commands.splice(i, 1);
|
|
2694
|
+
}
|
|
2695
|
+
clear() {
|
|
2696
|
+
this.#commands.length = 0;
|
|
2697
|
+
this.#legacy.clear();
|
|
2698
|
+
this.#access = void 0;
|
|
2699
|
+
}
|
|
2700
|
+
list() {
|
|
2701
|
+
return this.#orderedCommands();
|
|
2702
|
+
}
|
|
2703
|
+
#orderedCommands() {
|
|
2704
|
+
return [...this.#commands].sort((a, b) => a.priority - b.priority || a.order - b.order);
|
|
2705
|
+
}
|
|
2706
|
+
getPluginHelp(plugin) {
|
|
2707
|
+
const metadata = this.#legacy.get(plugin)?.metadata;
|
|
2708
|
+
const runtime = this.#commands.filter((item) => item.plugin === plugin);
|
|
2709
|
+
if (!metadata?.config.help && runtime.length === 0) return void 0;
|
|
2710
|
+
const commands = [];
|
|
2711
|
+
const seen = new Set();
|
|
2712
|
+
const runtimeByKey = new Map(runtime.map((command) => [commandKey$1(command.name), command]));
|
|
2713
|
+
for (const command of metadata?.config.help?.commands ?? []) {
|
|
2714
|
+
const key = commandKey$1(command.cmd);
|
|
2715
|
+
const runtimeCommand = runtimeByKey.get(key);
|
|
2716
|
+
if (runtimeCommand) {
|
|
2717
|
+
const cmd = displayCommand(runtimeCommand, this.#getDefaultPrefix());
|
|
2718
|
+
seen.add(key);
|
|
2719
|
+
commands.push({
|
|
2720
|
+
cmd,
|
|
2721
|
+
desc: runtimeCommand.description || command.desc || cmd,
|
|
2722
|
+
usage: runtimeCommand.usage ?? command.usage,
|
|
2723
|
+
role: runtimeCommand.permission
|
|
2724
|
+
});
|
|
2725
|
+
} else if (!seen.has(key)) {
|
|
2726
|
+
seen.add(key);
|
|
2727
|
+
commands.push(command);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
for (const command of runtime) {
|
|
2731
|
+
const cmd = displayCommand(command, this.#getDefaultPrefix());
|
|
2732
|
+
if (seen.has(commandKey$1(cmd))) continue;
|
|
2733
|
+
seen.add(commandKey$1(cmd));
|
|
2734
|
+
commands.push({
|
|
2735
|
+
cmd,
|
|
2736
|
+
desc: command.description || cmd,
|
|
2737
|
+
usage: command.usage,
|
|
2738
|
+
role: command.permission
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
2741
|
+
const help = metadata?.config.help;
|
|
2742
|
+
return {
|
|
2743
|
+
title: help?.title || plugin,
|
|
2744
|
+
description: help?.description || metadata?.description || "",
|
|
2745
|
+
commands
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
catalog() {
|
|
2749
|
+
const items = [];
|
|
2750
|
+
const plugins = new Set([...this.#legacy.keys(), ...this.#commands.map((item) => item.plugin)]);
|
|
2751
|
+
for (const plugin of plugins) {
|
|
2752
|
+
const metadata = this.#legacy.get(plugin)?.metadata;
|
|
2753
|
+
const help = metadata?.config.help;
|
|
2754
|
+
items.push({
|
|
2755
|
+
kind: "plugin",
|
|
2756
|
+
plugin,
|
|
2757
|
+
id: plugin,
|
|
2758
|
+
label: help?.title || plugin,
|
|
2759
|
+
desc: help?.description || metadata?.description,
|
|
2760
|
+
source: metadata ? "manifest" : "runtime"
|
|
2761
|
+
});
|
|
2762
|
+
const seen = new Set();
|
|
2763
|
+
for (const command of this.#commands.filter((item) => item.plugin === plugin)) {
|
|
2764
|
+
seen.add(commandKey$1(command.id));
|
|
2765
|
+
items.push({
|
|
2766
|
+
kind: "command",
|
|
2767
|
+
plugin,
|
|
2768
|
+
id: command.id,
|
|
2769
|
+
label: command.name,
|
|
2770
|
+
desc: command.description,
|
|
2771
|
+
usage: command.usage,
|
|
2772
|
+
permission: command.permission,
|
|
2773
|
+
priority: command.priority,
|
|
2774
|
+
match: command.matcher instanceof RegExp ? command.matcher.toString() : command.matcher,
|
|
2775
|
+
source: "runtime"
|
|
2776
|
+
});
|
|
2777
|
+
}
|
|
2778
|
+
for (const hook of metadata?.config.accessHooks ?? []) {
|
|
2779
|
+
if (seen.has(commandKey$1(hook.id))) continue;
|
|
2780
|
+
seen.add(commandKey$1(hook.id));
|
|
2781
|
+
items.push({
|
|
2782
|
+
kind: "command",
|
|
2783
|
+
plugin,
|
|
2784
|
+
id: hook.id,
|
|
2785
|
+
label: hook.id,
|
|
2786
|
+
desc: hook.description,
|
|
2787
|
+
match: hook.match,
|
|
2788
|
+
event: hook.event,
|
|
2789
|
+
source: "manifest"
|
|
2790
|
+
});
|
|
2791
|
+
}
|
|
2792
|
+
for (const command of help?.commands ?? []) {
|
|
2793
|
+
if (seen.has(commandKey$1(command.cmd))) continue;
|
|
2794
|
+
seen.add(commandKey$1(command.cmd));
|
|
2795
|
+
items.push({
|
|
2796
|
+
kind: "command",
|
|
2797
|
+
plugin,
|
|
2798
|
+
id: command.cmd,
|
|
2799
|
+
label: command.cmd,
|
|
2800
|
+
desc: command.desc,
|
|
2801
|
+
usage: command.usage,
|
|
2802
|
+
permission: command.role,
|
|
2803
|
+
source: "manifest"
|
|
2804
|
+
});
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
return items.sort((a, b) => a.kind === b.kind ? a.plugin.localeCompare(b.plugin) || a.id.localeCompare(b.id) : a.kind === "plugin" ? -1 : 1);
|
|
2808
|
+
}
|
|
2809
|
+
async dispatch(event) {
|
|
2810
|
+
const text$1 = String(event.message?.text?.() ?? "").trim();
|
|
2811
|
+
if (!text$1) return false;
|
|
2812
|
+
const match$1 = this.#find(text$1);
|
|
2813
|
+
if (!match$1) return false;
|
|
2814
|
+
if (!this.#canUse(match$1.command, event)) return true;
|
|
2815
|
+
try {
|
|
2816
|
+
await match$1.command.handler({
|
|
2817
|
+
ctx: match$1.command.context,
|
|
2818
|
+
event,
|
|
2819
|
+
command: match$1.command,
|
|
2820
|
+
text: text$1,
|
|
2821
|
+
body: match$1.body,
|
|
2822
|
+
args: match$1.args,
|
|
2823
|
+
match: match$1.match
|
|
2824
|
+
});
|
|
2825
|
+
} catch (error) {
|
|
2826
|
+
this.#logger?.error(`Command "${match$1.command.plugin}:${match$1.command.id}" failed`, error);
|
|
2827
|
+
}
|
|
2828
|
+
return true;
|
|
2829
|
+
}
|
|
2830
|
+
shouldDispatch(source, event) {
|
|
2831
|
+
if (!source.startsWith("plugin:")) return true;
|
|
2832
|
+
const plugin = source.slice(7);
|
|
2833
|
+
const metadata = this.#legacy.get(plugin)?.metadata;
|
|
2834
|
+
if (!metadata) return this.#pluginAllowed(plugin, event);
|
|
2835
|
+
if (!this.#pluginAllowed(plugin, event)) return false;
|
|
2836
|
+
const text$1 = event.kind === "message" ? String(event.message.text()).trim() : "";
|
|
2837
|
+
const matches = (metadata.config.accessHooks ?? []).filter((hook) => event.kind === "message" && textHookMatches(text$1, hook.match) || routeMatches(event, hook.event));
|
|
2838
|
+
return matches.every((hook) => this.#commandAllowed(plugin, hook.id, event));
|
|
2839
|
+
}
|
|
2840
|
+
#find(text$1) {
|
|
2841
|
+
for (const command of this.#orderedCommands()) {
|
|
2842
|
+
const prefixes = parsePrefixes(command.prefixes ?? this.#getDefaultPrefix(), this.#getDefaultPrefix());
|
|
2843
|
+
const body = commandBody(text$1, prefixes);
|
|
2844
|
+
if (body === void 0) continue;
|
|
2845
|
+
if (typeof command.matcher === "string") {
|
|
2846
|
+
const candidates = [
|
|
2847
|
+
command.matcher,
|
|
2848
|
+
command.name,
|
|
2849
|
+
...command.aliases
|
|
2850
|
+
];
|
|
2851
|
+
for (const candidate of candidates) {
|
|
2852
|
+
const result = stringMatch(body, candidate);
|
|
2853
|
+
if (result) return {
|
|
2854
|
+
command,
|
|
2855
|
+
body,
|
|
2856
|
+
args: result.args
|
|
2857
|
+
};
|
|
2858
|
+
}
|
|
2859
|
+
continue;
|
|
2860
|
+
}
|
|
2861
|
+
command.matcher.lastIndex = 0;
|
|
2862
|
+
const matched = body.match(command.matcher);
|
|
2863
|
+
if (!matched) continue;
|
|
2864
|
+
const args = matched.length > 1 ? matched.slice(1).filter((item) => item != null) : body.slice(matched[0].length).trim().split(/\s+/).filter(Boolean);
|
|
2865
|
+
return {
|
|
2866
|
+
command,
|
|
2867
|
+
body,
|
|
2868
|
+
args,
|
|
2869
|
+
match: matched
|
|
2870
|
+
};
|
|
2871
|
+
}
|
|
2872
|
+
return void 0;
|
|
2873
|
+
}
|
|
2874
|
+
#canUse(command, event) {
|
|
2875
|
+
const userId = userIdOf(event);
|
|
2876
|
+
const master = Boolean(userId && this.#isMaster(userId));
|
|
2877
|
+
const owner = master || this.#isGroupOwner(event);
|
|
2878
|
+
const admin = owner || Boolean(userId && this.#isAdmin(userId)) || this.#isGroupAdmin(event);
|
|
2879
|
+
if (command.permission === "master") {
|
|
2880
|
+
if (!master) return false;
|
|
2881
|
+
} else if (command.permission === "owner") {
|
|
2882
|
+
if (!owner) return false;
|
|
2883
|
+
} else if (command.permission === "admin" && !admin) return false;
|
|
2884
|
+
return this.#commandAllowed(command.plugin, command.id, event);
|
|
2885
|
+
}
|
|
2886
|
+
#pluginAllowed(plugin, event) {
|
|
2887
|
+
const userId = event.kind === "message" ? userIdOf(event) : "user_id" in event ? event.user_id : void 0;
|
|
2888
|
+
if (userId && (this.#isMaster(userId) || this.#isAdmin(userId)) || this.#isGroupOwner(event) || this.#isGroupAdmin(event)) return true;
|
|
2889
|
+
return this.#resolveAction(plugin, void 0, event) !== "block";
|
|
2890
|
+
}
|
|
2891
|
+
#commandAllowed(plugin, command, event) {
|
|
2892
|
+
const userId = event.kind === "message" ? userIdOf(event) : "user_id" in event ? event.user_id : void 0;
|
|
2893
|
+
if (userId && (this.#isMaster(userId) || this.#isAdmin(userId)) || this.#isGroupOwner(event) || this.#isGroupAdmin(event)) return true;
|
|
2894
|
+
return this.#resolveAction(plugin, command, event) !== "block";
|
|
2895
|
+
}
|
|
2896
|
+
#resolveAction(plugin, command, event) {
|
|
2897
|
+
const access = this.#access;
|
|
2898
|
+
if (!access) return void 0;
|
|
2899
|
+
const scopes = [
|
|
2900
|
+
(() => {
|
|
2901
|
+
const userId = event.kind === "message" ? userIdOf(event) : "user_id" in event && event.user_id ? String(event.user_id) : void 0;
|
|
2902
|
+
return userId ? access.users[userId] : void 0;
|
|
2903
|
+
})(),
|
|
2904
|
+
(() => {
|
|
2905
|
+
const groupId = "group_id" in event && event.group_id ? String(event.group_id) : void 0;
|
|
2906
|
+
return groupId ? access.groups[groupId] : void 0;
|
|
2907
|
+
})(),
|
|
2908
|
+
access.global
|
|
2909
|
+
];
|
|
2910
|
+
for (const scope of scopes) {
|
|
2911
|
+
if (!scope) continue;
|
|
2912
|
+
const commandRule = command ? scope.commands?.[plugin]?.[command]?.action : void 0;
|
|
2913
|
+
if (commandRule) return commandRule;
|
|
2914
|
+
const pluginRule = scope.plugins?.[plugin]?.action;
|
|
2915
|
+
if (pluginRule) return pluginRule;
|
|
2916
|
+
}
|
|
2917
|
+
return void 0;
|
|
2918
|
+
}
|
|
2919
|
+
#isMaster(userId) {
|
|
2920
|
+
return this.#owners().includes(userId);
|
|
2921
|
+
}
|
|
2922
|
+
#isAdmin(userId) {
|
|
2923
|
+
return this.#isMaster(userId) || this.#admins().includes(userId);
|
|
2924
|
+
}
|
|
2925
|
+
#isGroupOwner(event) {
|
|
2926
|
+
return isEventGroupOwner(event);
|
|
2927
|
+
}
|
|
2928
|
+
#isGroupAdmin(event) {
|
|
2929
|
+
return isEventGroupAdmin(event);
|
|
2930
|
+
}
|
|
2931
|
+
#owners() {
|
|
2932
|
+
return this.#configIds("owners");
|
|
2933
|
+
}
|
|
2934
|
+
#admins() {
|
|
2935
|
+
return this.#configIds("admins");
|
|
2936
|
+
}
|
|
2937
|
+
#configIds(key) {
|
|
2938
|
+
return [...key === "owners" ? this.#getOwners() : this.#getAdmins()].map(String);
|
|
2939
|
+
}
|
|
2940
|
+
};
|
|
2941
|
+
let activeCommandManager;
|
|
2942
|
+
const setActiveCommandManager = (manager) => {
|
|
2943
|
+
activeCommandManager = manager;
|
|
2944
|
+
};
|
|
2945
|
+
const getActiveCommandManager = () => activeCommandManager;
|
|
2946
|
+
|
|
2503
2947
|
//#endregion
|
|
2504
2948
|
//#region src/runtime/plugin-metadata.ts
|
|
2505
2949
|
const store$1 = getOrCreate("plugin-metadata", () => new Map());
|
|
2506
2950
|
const setPluginMetadata = (meta) => {
|
|
2507
2951
|
store$1.set(meta.name, meta);
|
|
2952
|
+
getActiveCommandManager()?.syncMetadata(meta);
|
|
2508
2953
|
};
|
|
2509
2954
|
const removePluginMetadata = (name) => {
|
|
2510
2955
|
store$1.delete(name);
|
|
2956
|
+
getActiveCommandManager()?.removePlugin(name);
|
|
2511
2957
|
};
|
|
2512
2958
|
const getPluginMetadata = (name) => store$1.get(name);
|
|
2513
2959
|
const getPluginMetadataList = () => [...store$1.values()];
|
|
2514
2960
|
const resetPluginMetadata = () => {
|
|
2515
2961
|
store$1.clear();
|
|
2962
|
+
getActiveCommandManager()?.clear();
|
|
2516
2963
|
};
|
|
2517
2964
|
|
|
2518
2965
|
//#endregion
|
|
@@ -3172,12 +3619,6 @@ async function sendTextOrForward(options) {
|
|
|
3172
3619
|
}
|
|
3173
3620
|
}
|
|
3174
3621
|
|
|
3175
|
-
//#endregion
|
|
3176
|
-
//#region src/builtin/core/commands/prefix.ts
|
|
3177
|
-
function getCommandPrefix() {
|
|
3178
|
-
return botConfig.prefix ?? ".";
|
|
3179
|
-
}
|
|
3180
|
-
|
|
3181
3622
|
//#endregion
|
|
3182
3623
|
//#region src/builtin/core/system/package-manager.ts
|
|
3183
3624
|
const NPM_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -3769,102 +4210,102 @@ async function performUpdateAndReport(ctx, event, names) {
|
|
|
3769
4210
|
triggerRestart(marker);
|
|
3770
4211
|
}
|
|
3771
4212
|
function registerUpdateCommands(ctx) {
|
|
3772
|
-
const dispose = ctx.
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
await performUpdateAndReport(ctx, event, managed.names);
|
|
3792
|
-
return;
|
|
3793
|
-
}
|
|
3794
|
-
if (arg === "mioku" || arg === "self") {
|
|
3795
|
-
await performUpdateAndReport(ctx, event, ["mioku"]);
|
|
3796
|
-
return;
|
|
3797
|
-
}
|
|
3798
|
-
if (arg.startsWith("plugin ") || arg.startsWith("service ")) {
|
|
3799
|
-
const [typeRaw, ...rest] = arg.split(/\s+/);
|
|
3800
|
-
const name = rest.join(" ").trim();
|
|
3801
|
-
const type = typeRaw === "plugin" ? "plugin" : "service";
|
|
3802
|
-
if (!name) {
|
|
3803
|
-
await replyText(event, `用法:${prefix}update ${type} <名称>`);
|
|
4213
|
+
const dispose = ctx.command({
|
|
4214
|
+
name: "update",
|
|
4215
|
+
aliases: ["更新"],
|
|
4216
|
+
permission: "master",
|
|
4217
|
+
priority: -1e3,
|
|
4218
|
+
description: "检查并选择插件/服务更新",
|
|
4219
|
+
handler: async ({ event, args }) => {
|
|
4220
|
+
const prefix = String(ctx.config.prefix ?? ".");
|
|
4221
|
+
const arg = args.join(" ").trim();
|
|
4222
|
+
const selfId = String(event?.self_id || "");
|
|
4223
|
+
const bot = event.bot;
|
|
4224
|
+
if (!bot) return;
|
|
4225
|
+
if (arg === "all") {
|
|
4226
|
+
const managed = await updateAllManaged();
|
|
4227
|
+
if (managed.names.length === 0) {
|
|
4228
|
+
await replyText(event, "未找到可更新的 mioku 包");
|
|
4229
|
+
return;
|
|
4230
|
+
}
|
|
4231
|
+
await performUpdateAndReport(ctx, event, managed.names);
|
|
3804
4232
|
return;
|
|
3805
4233
|
}
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
await performUpdateAndReport(ctx, event, [fullName]);
|
|
3809
|
-
return;
|
|
3810
|
-
}
|
|
3811
|
-
if (arg === "check" || arg === "检查") {
|
|
3812
|
-
const pending = getPendingMap();
|
|
3813
|
-
const key = conversationKey(event);
|
|
3814
|
-
const existing = pending.get(key);
|
|
3815
|
-
if (existing) {
|
|
3816
|
-
existing.disposer();
|
|
3817
|
-
clearTimeout(existing.timer);
|
|
3818
|
-
pending.delete(key);
|
|
3819
|
-
}
|
|
3820
|
-
let items;
|
|
3821
|
-
try {
|
|
3822
|
-
items = await checkUpdates();
|
|
3823
|
-
} catch (error) {
|
|
3824
|
-
ctx.logger.error(`[core] 检查更新失败: ${error}`);
|
|
3825
|
-
await replyText(event, `检查更新失败:${String(error)}`);
|
|
4234
|
+
if (arg === "mioku" || arg === "self") {
|
|
4235
|
+
await performUpdateAndReport(ctx, event, ["mioku"]);
|
|
3826
4236
|
return;
|
|
3827
4237
|
}
|
|
3828
|
-
if (
|
|
3829
|
-
|
|
4238
|
+
if (arg.startsWith("plugin ") || arg.startsWith("service ")) {
|
|
4239
|
+
const [typeRaw, ...rest] = arg.split(/\s+/);
|
|
4240
|
+
const name = rest.join(" ").trim();
|
|
4241
|
+
const type = typeRaw === "plugin" ? "plugin" : "service";
|
|
4242
|
+
if (!name) {
|
|
4243
|
+
await replyText(event, `用法:${prefix}update ${type} <名称>`);
|
|
4244
|
+
return;
|
|
4245
|
+
}
|
|
4246
|
+
const pkgPrefix = type === "plugin" ? "mioku-plugin-" : "mioku-service-";
|
|
4247
|
+
const fullName = name.startsWith(pkgPrefix) ? name : `${pkgPrefix}${name}`;
|
|
4248
|
+
await performUpdateAndReport(ctx, event, [fullName]);
|
|
3830
4249
|
return;
|
|
3831
4250
|
}
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
if (
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
if (chosen.length === 0) {
|
|
3848
|
-
await replyText(event, "未选择任何有效项,已取消");
|
|
4251
|
+
if (arg === "check" || arg === "检查") {
|
|
4252
|
+
const pending = getPendingMap();
|
|
4253
|
+
const key = conversationKey(event);
|
|
4254
|
+
const existing = pending.get(key);
|
|
4255
|
+
if (existing) {
|
|
4256
|
+
existing.disposer();
|
|
4257
|
+
clearTimeout(existing.timer);
|
|
4258
|
+
pending.delete(key);
|
|
4259
|
+
}
|
|
4260
|
+
let items;
|
|
4261
|
+
try {
|
|
4262
|
+
items = await checkUpdates();
|
|
4263
|
+
} catch (error) {
|
|
4264
|
+
ctx.logger.error(`[core] 检查更新失败: ${error}`);
|
|
4265
|
+
await replyText(event, `检查更新失败:${String(error)}`);
|
|
3849
4266
|
return;
|
|
3850
4267
|
}
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
4268
|
+
if (items.length === 0) {
|
|
4269
|
+
await replyText(event, "所有插件与服务均已是最新版本");
|
|
4270
|
+
return;
|
|
4271
|
+
}
|
|
4272
|
+
await replyText(event, renderUpdateList(items));
|
|
4273
|
+
const timeoutMs = SELECT_TIMEOUT_MS;
|
|
4274
|
+
const listenerDispose = ctx.handle("message", async (ev) => {
|
|
4275
|
+
if (String(ev?.self_id || "") !== selfId) return;
|
|
4276
|
+
if (conversationKey(ev) !== key) return;
|
|
4277
|
+
if (!isEventMaster(ev)) return;
|
|
4278
|
+
const evText = ctx.text(ev)?.trim() || "";
|
|
4279
|
+
if (evText.startsWith(`${prefix}update`)) return;
|
|
4280
|
+
const sel = pending.get(key);
|
|
4281
|
+
if (!sel) return;
|
|
4282
|
+
clearTimeout(sel.timer);
|
|
4283
|
+
sel.disposer();
|
|
4284
|
+
pending.delete(key);
|
|
4285
|
+
const parsed = parseSelection(evText, sel.items.length);
|
|
4286
|
+
const chosen = parsed.all ? sel.items : parsed.indices.map((i) => sel.items[i]).filter(Boolean);
|
|
4287
|
+
if (chosen.length === 0) {
|
|
4288
|
+
await replyText(event, "未选择任何有效项,已取消");
|
|
4289
|
+
return;
|
|
4290
|
+
}
|
|
4291
|
+
await performUpdateAndReport(ctx, event, chosen.map((i) => i.name));
|
|
4292
|
+
});
|
|
4293
|
+
const timer = setTimeout(async () => {
|
|
4294
|
+
const sel = pending.get(key);
|
|
4295
|
+
if (!sel) return;
|
|
4296
|
+
sel.disposer();
|
|
4297
|
+
pending.delete(key);
|
|
4298
|
+
await replyText(event, "操作超时,已取消更新");
|
|
4299
|
+
}, timeoutMs);
|
|
4300
|
+
pending.set(key, {
|
|
4301
|
+
disposer: listenerDispose,
|
|
4302
|
+
timer,
|
|
4303
|
+
items
|
|
4304
|
+
});
|
|
4305
|
+
return;
|
|
4306
|
+
}
|
|
4307
|
+
await replyText(event, `用法:\n${prefix}update check 检查并选择更新\n${prefix}update all 更新全部\n${prefix}update mioku 更新框架\n${prefix}update plugin <名称>\n${prefix}update service <名称>`);
|
|
3866
4308
|
}
|
|
3867
|
-
await replyText(event, `用法:\n${prefix}update check 检查并选择更新\n${prefix}update all 更新全部\n${prefix}update mioku 更新框架\n${prefix}update plugin <名称>\n${prefix}update service <名称>`);
|
|
3868
4309
|
});
|
|
3869
4310
|
return () => {
|
|
3870
4311
|
dispose();
|
|
@@ -3879,15 +4320,13 @@ function registerUpdateCommands(ctx) {
|
|
|
3879
4320
|
|
|
3880
4321
|
//#endregion
|
|
3881
4322
|
//#region src/builtin/core/commands/install.ts
|
|
3882
|
-
function parseTargetArgs(
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
const
|
|
3886
|
-
if (!matched) return null;
|
|
3887
|
-
const name = matched[2].trim();
|
|
4323
|
+
function parseTargetArgs(args) {
|
|
4324
|
+
const type = args[0];
|
|
4325
|
+
if (type !== "plugin" && type !== "service") return null;
|
|
4326
|
+
const name = args.slice(1).join(" ").trim();
|
|
3888
4327
|
if (!name) return null;
|
|
3889
4328
|
return {
|
|
3890
|
-
type
|
|
4329
|
+
type,
|
|
3891
4330
|
name
|
|
3892
4331
|
};
|
|
3893
4332
|
}
|
|
@@ -3895,68 +4334,70 @@ function typeLabel(type) {
|
|
|
3895
4334
|
return type === "plugin" ? "插件" : "服务";
|
|
3896
4335
|
}
|
|
3897
4336
|
function registerInstallCommands(ctx) {
|
|
3898
|
-
const
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
4337
|
+
const register = (name) => ctx.command({
|
|
4338
|
+
name,
|
|
4339
|
+
aliases: name === "install" ? ["安装"] : ["卸载"],
|
|
4340
|
+
permission: "master",
|
|
4341
|
+
priority: -1e3,
|
|
4342
|
+
description: name === "install" ? "从 npm 安装插件/服务" : "卸载插件/服务",
|
|
4343
|
+
handler: async ({ event, args }) => {
|
|
4344
|
+
if (name === "install") {
|
|
4345
|
+
const parsed$1 = parseTargetArgs(args);
|
|
4346
|
+
if (!parsed$1) {
|
|
4347
|
+
await replyText(event, `用法:${ctx.config.prefix ?? "."}install plugin <名称> 或 ${ctx.config.prefix ?? "."}install service <名称>`);
|
|
4348
|
+
return;
|
|
4349
|
+
}
|
|
4350
|
+
await replyText(event, `正在从 npm 安装${typeLabel(parsed$1.type)} ${parsed$1.name}...`);
|
|
4351
|
+
try {
|
|
4352
|
+
const result = await installPackage(parsed$1.type, parsed$1.name);
|
|
4353
|
+
if (!result.ok) {
|
|
4354
|
+
ctx.logger.error(`[core] 安装失败: ${result.error || result.output}`);
|
|
4355
|
+
await replyText(event, `安装失败:${result.error || result.output}`);
|
|
4356
|
+
return;
|
|
4357
|
+
}
|
|
4358
|
+
const lines = [
|
|
4359
|
+
`已安装 ${result.packageName}`,
|
|
4360
|
+
result.enabled ? "已在 plugins 中启用" : "",
|
|
4361
|
+
"重启后生效"
|
|
4362
|
+
].filter(Boolean);
|
|
4363
|
+
await replyText(event, lines.join("\n"));
|
|
4364
|
+
} catch (error) {
|
|
4365
|
+
ctx.logger.error(`[core] 安装失败: ${error}`);
|
|
4366
|
+
await replyText(event, `安装失败:${String(error)}`);
|
|
4367
|
+
}
|
|
4368
|
+
return;
|
|
4369
|
+
}
|
|
4370
|
+
const parsed = parseTargetArgs(args);
|
|
4371
|
+
if (!parsed) {
|
|
4372
|
+
await replyText(event, `用法:${ctx.config.prefix ?? "."}uninstall plugin <名称> 或 ${ctx.config.prefix ?? "."}uninstall service <名称>`);
|
|
3913
4373
|
return;
|
|
3914
4374
|
}
|
|
3915
|
-
await replyText(event,
|
|
4375
|
+
await replyText(event, `正在卸载${typeLabel(parsed.type)} ${parsed.name}...`);
|
|
3916
4376
|
try {
|
|
3917
|
-
const result = await
|
|
4377
|
+
const result = await uninstallPackage(parsed.type, parsed.name);
|
|
3918
4378
|
if (!result.ok) {
|
|
3919
|
-
ctx.logger.error(`[core]
|
|
3920
|
-
await replyText(event,
|
|
4379
|
+
ctx.logger.error(`[core] 卸载失败: ${result.error || result.output}`);
|
|
4380
|
+
await replyText(event, `卸载失败:${result.error || result.output}`);
|
|
3921
4381
|
return;
|
|
3922
4382
|
}
|
|
3923
4383
|
const lines = [
|
|
3924
|
-
|
|
3925
|
-
result.
|
|
4384
|
+
`已卸载 ${result.packageName}`,
|
|
4385
|
+
result.removedFromConfig ? "已从 plugins 中移除" : "",
|
|
3926
4386
|
"重启后生效"
|
|
3927
4387
|
].filter(Boolean);
|
|
3928
4388
|
await replyText(event, lines.join("\n"));
|
|
3929
4389
|
} catch (error) {
|
|
3930
|
-
ctx.logger.error(`[core]
|
|
3931
|
-
await replyText(event,
|
|
4390
|
+
ctx.logger.error(`[core] 卸载失败: ${error}`);
|
|
4391
|
+
await replyText(event, `卸载失败:${String(error)}`);
|
|
3932
4392
|
}
|
|
3933
|
-
return;
|
|
3934
|
-
}
|
|
3935
|
-
const parsed = parseTargetArgs(text$1, uninstallHead);
|
|
3936
|
-
if (!parsed) {
|
|
3937
|
-
await replyText(event, `用法:${prefix}uninstall plugin <名称> 或 ${prefix}uninstall service <名称>`);
|
|
3938
|
-
return;
|
|
3939
|
-
}
|
|
3940
|
-
await replyText(event, `正在卸载${typeLabel(parsed.type)} ${parsed.name}...`);
|
|
3941
|
-
try {
|
|
3942
|
-
const result = await uninstallPackage(parsed.type, parsed.name);
|
|
3943
|
-
if (!result.ok) {
|
|
3944
|
-
ctx.logger.error(`[core] 卸载失败: ${result.error || result.output}`);
|
|
3945
|
-
await replyText(event, `卸载失败:${result.error || result.output}`);
|
|
3946
|
-
return;
|
|
3947
|
-
}
|
|
3948
|
-
const lines = [
|
|
3949
|
-
`已卸载 ${result.packageName}`,
|
|
3950
|
-
result.removedFromConfig ? "已从 plugins 中移除" : "",
|
|
3951
|
-
"重启后生效"
|
|
3952
|
-
].filter(Boolean);
|
|
3953
|
-
await replyText(event, lines.join("\n"));
|
|
3954
|
-
} catch (error) {
|
|
3955
|
-
ctx.logger.error(`[core] 卸载失败: ${error}`);
|
|
3956
|
-
await replyText(event, `卸载失败:${String(error)}`);
|
|
3957
4393
|
}
|
|
3958
4394
|
});
|
|
3959
|
-
|
|
4395
|
+
const install = register("install");
|
|
4396
|
+
const uninstall = register("uninstall");
|
|
4397
|
+
return () => {
|
|
4398
|
+
install();
|
|
4399
|
+
uninstall();
|
|
4400
|
+
};
|
|
3960
4401
|
}
|
|
3961
4402
|
|
|
3962
4403
|
//#endregion
|
|
@@ -3970,64 +4411,76 @@ function renderMarketText(kind, items) {
|
|
|
3970
4411
|
return [`Mioku ${label}市场(共 ${items.length} 个)`, ...lines].join("\n");
|
|
3971
4412
|
}
|
|
3972
4413
|
function registerMarketCommands(ctx) {
|
|
3973
|
-
const
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
4414
|
+
const pluginMarket = ctx.command({
|
|
4415
|
+
id: "market",
|
|
4416
|
+
name: "plugin-market",
|
|
4417
|
+
aliases: ["插件市场"],
|
|
4418
|
+
permission: "master",
|
|
4419
|
+
priority: -1e3,
|
|
4420
|
+
description: "查看插件市场",
|
|
4421
|
+
handler: (input) => handleMarket(ctx, input.event, "plugin")
|
|
4422
|
+
});
|
|
4423
|
+
const serviceMarket = ctx.command({
|
|
4424
|
+
id: "market",
|
|
4425
|
+
name: "service-market",
|
|
4426
|
+
aliases: ["服务市场"],
|
|
4427
|
+
permission: "master",
|
|
4428
|
+
priority: -1e3,
|
|
4429
|
+
description: "查看服务市场",
|
|
4430
|
+
handler: (input) => handleMarket(ctx, input.event, "service")
|
|
4431
|
+
});
|
|
4432
|
+
return () => {
|
|
4433
|
+
pluginMarket();
|
|
4434
|
+
serviceMarket();
|
|
4435
|
+
};
|
|
4436
|
+
}
|
|
4437
|
+
async function handleMarket(ctx, event, kind) {
|
|
4438
|
+
let items;
|
|
4439
|
+
try {
|
|
4440
|
+
items = await getMarketItems(kind);
|
|
4441
|
+
} catch (error) {
|
|
4442
|
+
ctx.logger.error(`[core] 获取市场信息失败: ${error}`);
|
|
4443
|
+
await replyText(event, `获取市场失败:${String(error)}`);
|
|
4444
|
+
return;
|
|
4445
|
+
}
|
|
4446
|
+
await sendTextOrForward({
|
|
4447
|
+
ctx,
|
|
4448
|
+
event,
|
|
4449
|
+
text: renderMarketText(kind, items),
|
|
4450
|
+
source: `Mioku ${kind === "plugin" ? "插件" : "服务"}市场`,
|
|
4451
|
+
summary: `共 ${items.length} 个`
|
|
3998
4452
|
});
|
|
3999
|
-
return dispose;
|
|
4000
4453
|
}
|
|
4001
4454
|
|
|
4002
4455
|
//#endregion
|
|
4003
4456
|
//#region src/builtin/core/commands/restart.ts
|
|
4004
4457
|
function registerRestartCommand(ctx) {
|
|
4005
|
-
const dispose = ctx.
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
return;
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4458
|
+
const dispose = ctx.command({
|
|
4459
|
+
name: "restart",
|
|
4460
|
+
aliases: ["重启"],
|
|
4461
|
+
permission: "master",
|
|
4462
|
+
priority: -1e3,
|
|
4463
|
+
description: "重启机器人进程",
|
|
4464
|
+
handler: async ({ event }) => {
|
|
4465
|
+
if (event?.user_id === event?.self_id) return;
|
|
4466
|
+
const uptimeMs = process.uptime() * 1e3;
|
|
4467
|
+
const selfId = Number(event?.self_id || 0);
|
|
4468
|
+
const groupId = event?.message_type === "group" && event?.group_id ? Number(event.group_id) : null;
|
|
4469
|
+
const userId = Number(event?.user_id || 0);
|
|
4470
|
+
const marker = {
|
|
4471
|
+
initiatedAt: Date.now(),
|
|
4472
|
+
selfId,
|
|
4473
|
+
groupId,
|
|
4474
|
+
userId
|
|
4475
|
+
};
|
|
4476
|
+
try {
|
|
4477
|
+
await replyText(event, `Bot已运行${formatUptime(uptimeMs)},正在重启...`);
|
|
4478
|
+
} catch (error) {
|
|
4479
|
+
ctx.logger.warn(`[core] 发送重启提示失败: ${error}`);
|
|
4480
|
+
}
|
|
4481
|
+
ctx.logger.info(`[core] 正在执行重启命令 ${formatUptime(uptimeMs)}`);
|
|
4482
|
+
triggerRestart(marker);
|
|
4028
4483
|
}
|
|
4029
|
-
ctx.logger.info(`[core] 正在执行重启命令 ${formatUptime(uptimeMs)}`);
|
|
4030
|
-
triggerRestart(marker);
|
|
4031
4484
|
});
|
|
4032
4485
|
return dispose;
|
|
4033
4486
|
}
|
|
@@ -4065,63 +4518,63 @@ function chunk(arr, size) {
|
|
|
4065
4518
|
return result;
|
|
4066
4519
|
}
|
|
4067
4520
|
function registerLogCommand(ctx) {
|
|
4068
|
-
const dispose = ctx.
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
return;
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
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
|
-
|
|
4521
|
+
const dispose = ctx.command({
|
|
4522
|
+
name: "log",
|
|
4523
|
+
aliases: ["日志"],
|
|
4524
|
+
permission: "master",
|
|
4525
|
+
priority: -1e3,
|
|
4526
|
+
description: "查看最近100条日志",
|
|
4527
|
+
handler: async ({ event }) => {
|
|
4528
|
+
if (event?.user_id === event?.self_id) return;
|
|
4529
|
+
const selfId = String(event?.self_id || "");
|
|
4530
|
+
const bot = event.bot;
|
|
4531
|
+
if (!bot) return;
|
|
4532
|
+
const logFile = getActiveLogFile();
|
|
4533
|
+
if (!logFile) {
|
|
4534
|
+
await replyText(event, "未找到日志文件。");
|
|
4535
|
+
return;
|
|
4536
|
+
}
|
|
4537
|
+
let lines;
|
|
4538
|
+
try {
|
|
4539
|
+
lines = readLastLines(logFile, LOG_LINE_COUNT);
|
|
4540
|
+
} catch (error) {
|
|
4541
|
+
ctx.logger.error(`[core] 读取日志失败: ${error}`);
|
|
4542
|
+
await replyText(event, `读取日志失败:${String(error)}`);
|
|
4543
|
+
return;
|
|
4544
|
+
}
|
|
4545
|
+
if (lines.length === 0) {
|
|
4546
|
+
await replyText(event, "日志为空。");
|
|
4547
|
+
return;
|
|
4548
|
+
}
|
|
4549
|
+
let infoCount = 0;
|
|
4550
|
+
let warnCount = 0;
|
|
4551
|
+
let errorCount = 0;
|
|
4552
|
+
for (const line of lines) if (/\bERROR\b|\berror\b/i.test(line)) errorCount++;
|
|
4553
|
+
else if (/\bWARN\b|\bwarn\b/i.test(line)) warnCount++;
|
|
4554
|
+
else if (/\bINFO\b|\binfo\b/i.test(line)) infoCount++;
|
|
4555
|
+
const summary = `info ${infoCount} warn ${warnCount} error ${errorCount}`;
|
|
4556
|
+
const chunks = chunk(lines, LINES_PER_NODE);
|
|
4557
|
+
const nodes = chunks.map((chunkLines, idx) => ({
|
|
4558
|
+
user_id: selfId,
|
|
4559
|
+
nickname: `第${idx + 1}条日志`,
|
|
4560
|
+
content: [ctx.segment.text(chunkLines.join("\n"))]
|
|
4561
|
+
}));
|
|
4562
|
+
try {
|
|
4563
|
+
const target = event?.message_type === "group" && event?.group_id ? {
|
|
4564
|
+
type: "group",
|
|
4565
|
+
group_id: event.group_id
|
|
4566
|
+
} : event?.user_id ? {
|
|
4567
|
+
type: "private",
|
|
4568
|
+
user_id: event.user_id
|
|
4569
|
+
} : void 0;
|
|
4570
|
+
if (target) await bot.sendForward(target, nodes, {
|
|
4571
|
+
source: "最近100条运行日志",
|
|
4572
|
+
summary
|
|
4573
|
+
});
|
|
4574
|
+
} catch (error) {
|
|
4575
|
+
ctx.logger.error(`[core] 发送日志转发失败: ${error}`);
|
|
4576
|
+
await replyText(event, `发送日志失败:${String(error)}`);
|
|
4577
|
+
}
|
|
4125
4578
|
}
|
|
4126
4579
|
});
|
|
4127
4580
|
return dispose;
|
|
@@ -4348,7 +4801,6 @@ const core = definePlugin({
|
|
|
4348
4801
|
description: "mioku 内置核心插件",
|
|
4349
4802
|
async setup(ctx) {
|
|
4350
4803
|
const rawPrefix = ctx.config.prefix ?? ".";
|
|
4351
|
-
const cmdPrefix = new RegExp(`^${escapeRegExp(rawPrefix)}`);
|
|
4352
4804
|
const displayPrefix = rawPrefix;
|
|
4353
4805
|
const statusAdminOnly = ctx.config.status_permission === "admin-only";
|
|
4354
4806
|
rootLogger.info("========================================");
|
|
@@ -4366,12 +4818,17 @@ const core = definePlugin({
|
|
|
4366
4818
|
}));
|
|
4367
4819
|
} else ctx.logger.warn("config-service 未加载,core 插件将使用默认配置");
|
|
4368
4820
|
let accessRules = ensureAccessControlConfig();
|
|
4821
|
+
ctx.commands.setAccessControl(accessRules);
|
|
4369
4822
|
if (configService) {
|
|
4370
4823
|
await configService.registerConfig("core", "access-control", accessRules);
|
|
4371
4824
|
const persisted = await configService.getConfig("core", "access-control");
|
|
4372
|
-
if (persisted)
|
|
4825
|
+
if (persisted) {
|
|
4826
|
+
accessRules = normalizeAccessConfig(persisted);
|
|
4827
|
+
ctx.commands.setAccessControl(accessRules);
|
|
4828
|
+
}
|
|
4373
4829
|
disposers.push(configService.onConfigChange("core", "access-control", (next) => {
|
|
4374
4830
|
accessRules = normalizeAccessConfig(next);
|
|
4831
|
+
ctx.commands.setAccessControl(accessRules);
|
|
4375
4832
|
}));
|
|
4376
4833
|
}
|
|
4377
4834
|
rootLogger.info(`访问控制已挂载: ${ctx.bots.length} 个 bot`);
|
|
@@ -4433,17 +4890,49 @@ const core = definePlugin({
|
|
|
4433
4890
|
bots: collectBots(),
|
|
4434
4891
|
adapters: collectAdapters()
|
|
4435
4892
|
}));
|
|
4893
|
+
ctx.command({
|
|
4894
|
+
name: "help",
|
|
4895
|
+
aliases: ["帮助"],
|
|
4896
|
+
permission: "master",
|
|
4897
|
+
priority: -1e3,
|
|
4898
|
+
description: "显示帮助信息",
|
|
4899
|
+
handler: async ({ event }) => {
|
|
4900
|
+
await event.reply(buildCoreHelpText(displayPrefix));
|
|
4901
|
+
}
|
|
4902
|
+
});
|
|
4903
|
+
ctx.command({
|
|
4904
|
+
name: "status",
|
|
4905
|
+
aliases: ["状态"],
|
|
4906
|
+
priority: -1e3,
|
|
4907
|
+
description: "显示框架状态",
|
|
4908
|
+
handler: async ({ event }) => {
|
|
4909
|
+
if (statusAdminOnly && !isEventOwnerOrAdmin(event)) return;
|
|
4910
|
+
await event.reply(await formatMiokuStatus(await getStatus()));
|
|
4911
|
+
}
|
|
4912
|
+
});
|
|
4913
|
+
ctx.command({
|
|
4914
|
+
name: "adapter",
|
|
4915
|
+
aliases: ["适配器"],
|
|
4916
|
+
priority: -1e3,
|
|
4917
|
+
description: "显示适配器与连接实例",
|
|
4918
|
+
handler: async ({ event }) => {
|
|
4919
|
+
if (statusAdminOnly && !isEventOwnerOrAdmin(event)) return;
|
|
4920
|
+
await event.reply(await getAdapterText());
|
|
4921
|
+
}
|
|
4922
|
+
});
|
|
4436
4923
|
const atTarget = (event) => {
|
|
4437
4924
|
const at = event.message.find((seg) => seg.type === "at");
|
|
4438
4925
|
const qq = at?.data?.qq ?? at?.data?.target;
|
|
4439
4926
|
return qq != null ? String(qq) : void 0;
|
|
4440
4927
|
};
|
|
4441
|
-
|
|
4928
|
+
const handleCoreMessage = async (event) => {
|
|
4442
4929
|
const ev = event;
|
|
4443
4930
|
const text$1 = ev.message.text();
|
|
4444
|
-
|
|
4931
|
+
const currentPrefix = String(ctx.config.prefix ?? ".");
|
|
4932
|
+
const currentCmdPrefix = new RegExp(`^${escapeRegExp(currentPrefix)}`);
|
|
4933
|
+
if (!currentCmdPrefix.test(text$1)) return;
|
|
4445
4934
|
if (statusAdminOnly && !isEventOwnerOrAdmin(ev)) return;
|
|
4446
|
-
const bare = text$1.replace(
|
|
4935
|
+
const bare = text$1.replace(currentCmdPrefix, "").trim();
|
|
4447
4936
|
if (bare === "状态" || bare === "status") {
|
|
4448
4937
|
await ev.reply(await formatMiokuStatus(await getStatus()));
|
|
4449
4938
|
return;
|
|
@@ -4452,10 +4941,10 @@ const core = definePlugin({
|
|
|
4452
4941
|
await ev.reply(await getAdapterText());
|
|
4453
4942
|
return;
|
|
4454
4943
|
}
|
|
4455
|
-
if (!
|
|
4944
|
+
if (!isEventMaster(ev)) return;
|
|
4456
4945
|
const { cmd, params } = createCmd(text$1);
|
|
4457
4946
|
if (!cmd) return;
|
|
4458
|
-
const subCmd = cmd.replace(
|
|
4947
|
+
const subCmd = cmd.replace(currentCmdPrefix, "").replace(/\s+/g, "");
|
|
4459
4948
|
const target = params[0];
|
|
4460
4949
|
switch (subCmd) {
|
|
4461
4950
|
case "help":
|
|
@@ -4677,7 +5166,29 @@ const core = definePlugin({
|
|
|
4677
5166
|
process.exit(0);
|
|
4678
5167
|
}
|
|
4679
5168
|
}
|
|
4680
|
-
}
|
|
5169
|
+
};
|
|
5170
|
+
for (const command of [
|
|
5171
|
+
{
|
|
5172
|
+
name: "plugin",
|
|
5173
|
+
aliases: ["插件"],
|
|
5174
|
+
description: "插件管理"
|
|
5175
|
+
},
|
|
5176
|
+
{
|
|
5177
|
+
name: "settings",
|
|
5178
|
+
aliases: ["设置"],
|
|
5179
|
+
description: "框架设置管理"
|
|
5180
|
+
},
|
|
5181
|
+
{
|
|
5182
|
+
name: "exit",
|
|
5183
|
+
aliases: ["退出"],
|
|
5184
|
+
description: "退出框架进程"
|
|
5185
|
+
}
|
|
5186
|
+
]) ctx.command({
|
|
5187
|
+
...command,
|
|
5188
|
+
permission: "master",
|
|
5189
|
+
priority: -1e3,
|
|
5190
|
+
handler: ({ event }) => handleCoreMessage(event)
|
|
5191
|
+
});
|
|
4681
5192
|
rootLogger.info("========================================");
|
|
4682
5193
|
rootLogger.info(" Mioku 服务初始化完成");
|
|
4683
5194
|
rootLogger.info("========================================");
|
|
@@ -5085,6 +5596,7 @@ var MiokuRuntime = class {
|
|
|
5085
5596
|
#driverFactory;
|
|
5086
5597
|
#builtinPlugins;
|
|
5087
5598
|
#correlator;
|
|
5599
|
+
#commands;
|
|
5088
5600
|
#started = false;
|
|
5089
5601
|
#stopped = false;
|
|
5090
5602
|
constructor(options) {
|
|
@@ -5102,6 +5614,14 @@ var MiokuRuntime = class {
|
|
|
5102
5614
|
});
|
|
5103
5615
|
this.#bots = new BotRegistry();
|
|
5104
5616
|
this.#capabilities = new CapabilityRegistry();
|
|
5617
|
+
this.#commands = new CommandManager({
|
|
5618
|
+
getDefaultPrefix: () => String(botConfig.prefix ?? "."),
|
|
5619
|
+
getOwners: () => botConfig.owners,
|
|
5620
|
+
getAdmins: () => botConfig.admins,
|
|
5621
|
+
logger: this.#logger
|
|
5622
|
+
});
|
|
5623
|
+
this.#bus.setFilter((registration, event) => this.#commands.shouldDispatch(registration.source, event));
|
|
5624
|
+
setActiveCommandManager(this.#commands);
|
|
5105
5625
|
}
|
|
5106
5626
|
get cwd() {
|
|
5107
5627
|
return this.#cwd;
|
|
@@ -5115,6 +5635,9 @@ var MiokuRuntime = class {
|
|
|
5115
5635
|
get bus() {
|
|
5116
5636
|
return this.#bus;
|
|
5117
5637
|
}
|
|
5638
|
+
get commands() {
|
|
5639
|
+
return this.#commands;
|
|
5640
|
+
}
|
|
5118
5641
|
/** 事件关联器(跨适配器去重);为 undefined 时表示已禁用 */
|
|
5119
5642
|
get correlator() {
|
|
5120
5643
|
return this.#correlator;
|
|
@@ -5133,14 +5656,16 @@ var MiokuRuntime = class {
|
|
|
5133
5656
|
return this.#bots.pick(bot_id);
|
|
5134
5657
|
}
|
|
5135
5658
|
listPlugins() {
|
|
5136
|
-
return Array.from(this.#enabledPlugins.entries()).map(([key
|
|
5659
|
+
return Array.from(this.#enabledPlugins.entries()).map(([key]) => {
|
|
5137
5660
|
const colon = key.indexOf(":");
|
|
5138
5661
|
const type = key.slice(0, colon);
|
|
5139
5662
|
const name = key.slice(colon + 1);
|
|
5663
|
+
const isExternal = type !== "builtin";
|
|
5664
|
+
const version$2 = isExternal ? getPluginMetadata(name)?.version : void 0;
|
|
5140
5665
|
return {
|
|
5141
5666
|
name,
|
|
5142
5667
|
type: type === "builtin" ? "builtin" : "external",
|
|
5143
|
-
version:
|
|
5668
|
+
version: version$2
|
|
5144
5669
|
};
|
|
5145
5670
|
});
|
|
5146
5671
|
}
|
|
@@ -5227,13 +5752,16 @@ var MiokuRuntime = class {
|
|
|
5227
5752
|
capabilities: this.#capabilities,
|
|
5228
5753
|
logger: this.#logger.child({ adapter: state.definition.name }),
|
|
5229
5754
|
emit: (event) => this.#emitLifecycle(event),
|
|
5230
|
-
correlator: this.#correlator
|
|
5755
|
+
correlator: this.#correlator,
|
|
5756
|
+
commands: this.#commands
|
|
5231
5757
|
});
|
|
5232
5758
|
}
|
|
5233
5759
|
async #loadPlugin(plugin, type) {
|
|
5234
5760
|
const cleanupTasks = [];
|
|
5235
5761
|
const ctx = new MiokuContext({
|
|
5236
5762
|
pluginName: `${type}:${plugin.name}`,
|
|
5763
|
+
pluginId: plugin.name === "mioku-core" ? "core" : plugin.name,
|
|
5764
|
+
commands: this.#commands,
|
|
5237
5765
|
bus: this.#bus,
|
|
5238
5766
|
bots: this.#bots,
|
|
5239
5767
|
driver: this.#driver,
|
|
@@ -5531,6 +6059,8 @@ var MiokuRuntime = class {
|
|
|
5531
6059
|
this.#capabilities.clear();
|
|
5532
6060
|
this.#bots.clear();
|
|
5533
6061
|
this.#correlator?.clear();
|
|
6062
|
+
this.#commands.clear();
|
|
6063
|
+
setActiveCommandManager(void 0);
|
|
5534
6064
|
resetPluginMetadata();
|
|
5535
6065
|
try {
|
|
5536
6066
|
await this.#driver.shutdown();
|
|
@@ -5547,22 +6077,38 @@ const createRuntime = (options) => {
|
|
|
5547
6077
|
|
|
5548
6078
|
//#endregion
|
|
5549
6079
|
//#region src/runtime/plugin-artifacts.ts
|
|
6080
|
+
const commandKey = (value) => String(value).trim().replace(/^[#/.]+\s*/, "").split(/\s+/)[0].toLowerCase();
|
|
5550
6081
|
async function registerPluginArtifacts(ctx) {
|
|
5551
6082
|
const helpService = ctx ? getService(ctx, Services.Help) : servicesRegistry["help"];
|
|
5552
6083
|
const plugins = getPluginMetadataList();
|
|
6084
|
+
const commandManager = getActiveCommandManager();
|
|
6085
|
+
const metadataByName = new Map(plugins.map((meta) => [meta.name, meta]));
|
|
6086
|
+
const pluginNames = new Set([...metadataByName.keys(), ...(commandManager?.catalog() ?? []).filter((item) => item.kind === "plugin").map((item) => item.plugin)]);
|
|
5553
6087
|
let helpCount = 0;
|
|
5554
|
-
for (const
|
|
5555
|
-
|
|
5556
|
-
|
|
6088
|
+
for (const pluginName of pluginNames) {
|
|
6089
|
+
const meta = metadataByName.get(pluginName);
|
|
6090
|
+
const generated = commandManager?.getPluginHelp(pluginName) ?? meta?.config?.help;
|
|
6091
|
+
const existing = helpService?.getHelp(pluginName);
|
|
6092
|
+
const help = generated ? {
|
|
6093
|
+
...generated,
|
|
6094
|
+
title: existing?.title || generated.title,
|
|
6095
|
+
description: existing?.description || generated.description,
|
|
6096
|
+
commands: [...existing?.commands ?? [], ...generated.commands.filter((command) => {
|
|
6097
|
+
const key = commandKey(command.cmd);
|
|
6098
|
+
return !(existing?.commands ?? []).some((item) => commandKey(item.cmd) === key);
|
|
6099
|
+
})]
|
|
6100
|
+
} : existing;
|
|
6101
|
+
if (help) if (helpService) {
|
|
6102
|
+
helpService.registerHelp(pluginName, help);
|
|
5557
6103
|
helpCount++;
|
|
5558
|
-
} else rootLogger.warn(`[plugin-artifacts] 帮助服务未加载,跳过插件 "${
|
|
5559
|
-
const required = meta
|
|
6104
|
+
} else rootLogger.warn(`[plugin-artifacts] 帮助服务未加载,跳过插件 "${pluginName}" 的帮助注册`);
|
|
6105
|
+
const required = meta?.config?.services;
|
|
5560
6106
|
if (required && required.length > 0) {
|
|
5561
6107
|
const missing = required.filter((name) => !servicesRegistry[name]);
|
|
5562
|
-
if (missing.length > 0) rootLogger.warn(`插件 "${
|
|
6108
|
+
if (missing.length > 0) rootLogger.warn(`插件 "${pluginName}" 声明依赖服务 [${missing.join(", ")}],但它们未加载`);
|
|
5563
6109
|
}
|
|
5564
6110
|
}
|
|
5565
|
-
rootLogger.info(`[plugin-artifacts] 已处理 ${
|
|
6111
|
+
rootLogger.info(`[plugin-artifacts] 已处理 ${pluginNames.size} 个插件,注册帮助 ${helpCount} 个`);
|
|
5566
6112
|
}
|
|
5567
6113
|
|
|
5568
6114
|
//#endregion
|
|
@@ -5728,6 +6274,7 @@ exports.BotRegistry = BotRegistry;
|
|
|
5728
6274
|
exports.CORE_PLUGINS = CORE_PLUGINS;
|
|
5729
6275
|
exports.CapabilityRegistry = CapabilityRegistry;
|
|
5730
6276
|
exports.ChromeUA = ChromeUA;
|
|
6277
|
+
exports.CommandManager = CommandManager;
|
|
5731
6278
|
exports.DriverShutdownError = DriverShutdownError;
|
|
5732
6279
|
exports.EventBus = EventBus;
|
|
5733
6280
|
exports.EventCorrelator = EventCorrelator;
|
|
@@ -5840,6 +6387,7 @@ Object.defineProperty(exports, 'fs', {
|
|
|
5840
6387
|
return node_fs.default;
|
|
5841
6388
|
}
|
|
5842
6389
|
});
|
|
6390
|
+
exports.getActiveCommandManager = getActiveCommandManager;
|
|
5843
6391
|
exports.getAllPluginRuntimeStates = getAllPluginRuntimeStates;
|
|
5844
6392
|
exports.getBuiltinPlugins = getBuiltinPlugins;
|
|
5845
6393
|
exports.getConfigDir = getConfigDir;
|
|
@@ -5873,6 +6421,10 @@ exports.isBoolean = isBoolean;
|
|
|
5873
6421
|
exports.isBotEvent = isBotEvent;
|
|
5874
6422
|
exports.isDefined = isDefined;
|
|
5875
6423
|
exports.isEventAdmin = isEventAdmin;
|
|
6424
|
+
exports.isEventAdminConfigOnly = isEventAdminConfigOnly;
|
|
6425
|
+
exports.isEventGroupAdmin = isEventGroupAdmin;
|
|
6426
|
+
exports.isEventGroupOwner = isEventGroupOwner;
|
|
6427
|
+
exports.isEventMaster = isEventMaster;
|
|
5876
6428
|
exports.isEventOwner = isEventOwner;
|
|
5877
6429
|
exports.isEventOwnerOrAdmin = isEventOwnerOrAdmin;
|
|
5878
6430
|
exports.isFunction = isFunction;
|
|
@@ -5959,6 +6511,7 @@ exports.segment = segment;
|
|
|
5959
6511
|
exports.sendMessage = sendMessage;
|
|
5960
6512
|
exports.serviceManager = manager_default;
|
|
5961
6513
|
exports.servicesRegistry = servicesRegistry;
|
|
6514
|
+
exports.setActiveCommandManager = setActiveCommandManager;
|
|
5962
6515
|
exports.setBotCwd = setBotCwd;
|
|
5963
6516
|
exports.setBuiltinPlugins = setBuiltinPlugins;
|
|
5964
6517
|
exports.setPluginMetadata = setPluginMetadata;
|