mioki 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-C6wwvPpM.mjs +18 -0
- package/dist/index.cjs +1910 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +655 -3
- package/dist/index.d.mts +653 -3
- package/dist/index.mjs +1715 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -2
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,1915 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (all, symbols) => {
|
|
9
|
+
let target = {};
|
|
10
|
+
for (var name in all) {
|
|
11
|
+
__defProp(target, name, {
|
|
12
|
+
get: all[name],
|
|
13
|
+
enumerable: true
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
if (symbols) {
|
|
17
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
18
|
+
}
|
|
19
|
+
return target;
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
24
|
+
key = keys[i];
|
|
25
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
26
|
+
__defProp(to, key, {
|
|
27
|
+
get: ((k) => from[k]).bind(null, key),
|
|
28
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return to;
|
|
34
|
+
};
|
|
35
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
36
|
+
value: mod,
|
|
37
|
+
enumerable: true
|
|
38
|
+
}) : target, mod));
|
|
1
39
|
|
|
2
|
-
//#
|
|
3
|
-
|
|
40
|
+
//#endregion
|
|
41
|
+
let pino = require("pino");
|
|
42
|
+
pino = __toESM(pino);
|
|
43
|
+
let node_fs = require("node:fs");
|
|
44
|
+
node_fs = __toESM(node_fs);
|
|
45
|
+
let node_path = require("node:path");
|
|
46
|
+
node_path = __toESM(node_path);
|
|
47
|
+
let node_process = require("node:process");
|
|
48
|
+
let mri = require("mri");
|
|
49
|
+
mri = __toESM(mri);
|
|
50
|
+
let node_crypto = require("node:crypto");
|
|
51
|
+
node_crypto = __toESM(node_crypto);
|
|
52
|
+
let lowdb = require("lowdb");
|
|
53
|
+
let lowdb_node = require("lowdb/node");
|
|
54
|
+
let jiti = require("jiti");
|
|
55
|
+
let string2argv = require("string2argv");
|
|
56
|
+
let node_url = require("node:url");
|
|
57
|
+
let napcat_sdk = require("napcat-sdk");
|
|
58
|
+
let pretty_ms = require("pretty-ms");
|
|
59
|
+
pretty_ms = __toESM(pretty_ms);
|
|
60
|
+
let filesize = require("filesize");
|
|
61
|
+
let dayjs = require("dayjs");
|
|
62
|
+
dayjs = __toESM(dayjs);
|
|
63
|
+
let dedent = require("dedent");
|
|
64
|
+
dedent = __toESM(dedent);
|
|
65
|
+
let systeminformation = require("systeminformation");
|
|
66
|
+
systeminformation = __toESM(systeminformation);
|
|
67
|
+
let node_os = require("node:os");
|
|
68
|
+
node_os = __toESM(node_os);
|
|
69
|
+
let node_child_process = require("node:child_process");
|
|
70
|
+
node_child_process = __toESM(node_child_process);
|
|
71
|
+
let node_cron = require("node-cron");
|
|
72
|
+
node_cron = __toESM(node_cron);
|
|
73
|
+
|
|
74
|
+
//#region src/logger.ts
|
|
75
|
+
const getMiokiLogger = (level) => {
|
|
76
|
+
return (0, pino.default)({
|
|
77
|
+
level,
|
|
78
|
+
name: "mioki",
|
|
79
|
+
transport: {
|
|
80
|
+
target: "pino-pretty",
|
|
81
|
+
options: { colorize: true }
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/utils.ts
|
|
88
|
+
var utils_exports = /* @__PURE__ */ __export({
|
|
89
|
+
ChromeUA: () => ChromeUA,
|
|
90
|
+
START_TIME: () => START_TIME,
|
|
91
|
+
base64Decode: () => base64Decode,
|
|
92
|
+
base64Encode: () => base64Encode,
|
|
93
|
+
bindBot: () => bindBot,
|
|
94
|
+
clamp: () => clamp,
|
|
95
|
+
createCmd: () => createCmd,
|
|
96
|
+
createDB: () => createDB,
|
|
97
|
+
createStore: () => createStore,
|
|
98
|
+
dayjs: () => dayjs.default,
|
|
99
|
+
dedent: () => dedent.default,
|
|
100
|
+
ensureBuffer: () => ensureBuffer,
|
|
101
|
+
filesize: () => filesize.filesize,
|
|
102
|
+
filter: () => filter,
|
|
103
|
+
find: () => find,
|
|
104
|
+
formatDuration: () => formatDuration,
|
|
105
|
+
formatQQLevel: () => formatQQLevel,
|
|
106
|
+
fs: () => node_fs.default,
|
|
107
|
+
getAuthCodeViaTicket: () => getAuthCodeViaTicket,
|
|
108
|
+
getBfaceUrl: () => getBfaceUrl,
|
|
109
|
+
getGroupAvatarLink: () => getGroupAvatarLink,
|
|
110
|
+
getImage: () => getImage,
|
|
111
|
+
getImageUrl: () => getImageUrl,
|
|
112
|
+
getMentionedImage: () => getMentionedImage,
|
|
113
|
+
getMentionedImageUrl: () => getMentionedImageUrl,
|
|
114
|
+
getMentionedUserId: () => getMentionedUserId,
|
|
115
|
+
getMinicoTokenViaAuthCode: () => getMinicoTokenViaAuthCode,
|
|
116
|
+
getQQAvatarLink: () => getQQAvatarLink,
|
|
117
|
+
getQuoteImage: () => getQuoteImage,
|
|
118
|
+
getQuoteImageUrl: () => getQuoteImageUrl,
|
|
119
|
+
getQuoteMessage: () => getQuoteMessage,
|
|
120
|
+
getQuoteText: () => getQuoteText,
|
|
121
|
+
getTerminalInput: () => getTerminalInput,
|
|
122
|
+
isBoolean: () => isBoolean,
|
|
123
|
+
isDefined: () => isDefined,
|
|
124
|
+
isFunction: () => isFunction,
|
|
125
|
+
isGroupMsg: () => isGroupMsg,
|
|
126
|
+
isNumber: () => isNumber,
|
|
127
|
+
isObject: () => isObject,
|
|
128
|
+
isPrivateMsg: () => isPrivateMsg,
|
|
129
|
+
isString: () => isString,
|
|
130
|
+
jiti: () => jiti$1,
|
|
131
|
+
localNum: () => localNum,
|
|
132
|
+
localeDate: () => localeDate,
|
|
133
|
+
localeTime: () => localeTime,
|
|
134
|
+
match: () => match,
|
|
135
|
+
md5: () => md5,
|
|
136
|
+
mri: () => mri.default,
|
|
137
|
+
noNullish: () => noNullish,
|
|
138
|
+
path: () => node_path.default,
|
|
139
|
+
prettyMs: () => pretty_ms.default,
|
|
140
|
+
qs: () => qs,
|
|
141
|
+
queryDevToolsLoginStatus: () => queryDevToolsLoginStatus,
|
|
142
|
+
randomId: () => randomId,
|
|
143
|
+
randomInt: () => randomInt,
|
|
144
|
+
randomItem: () => randomItem,
|
|
145
|
+
randomItems: () => randomItems,
|
|
146
|
+
requestLoginViaDevTools: () => requestLoginViaDevTools,
|
|
147
|
+
runWithReaction: () => runWithReaction,
|
|
148
|
+
string2argv: () => string2argv.string2argv,
|
|
149
|
+
stringifyError: () => stringifyError,
|
|
150
|
+
systemInfo: () => systeminformation.default,
|
|
151
|
+
text: () => text,
|
|
152
|
+
toArray: () => toArray,
|
|
153
|
+
toMsgId: () => toMsgId,
|
|
154
|
+
unique: () => unique,
|
|
155
|
+
uuid: () => uuid,
|
|
156
|
+
wait: () => wait
|
|
157
|
+
});
|
|
158
|
+
const ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/131.0.0.0";
|
|
159
|
+
/**
|
|
160
|
+
* Jiti 实例
|
|
161
|
+
*/
|
|
162
|
+
const jiti$1 = (0, jiti.createJiti)(__dirname, {
|
|
163
|
+
extensions: [
|
|
164
|
+
".ts",
|
|
165
|
+
".js",
|
|
166
|
+
".cts",
|
|
167
|
+
".cjs",
|
|
168
|
+
".mts",
|
|
169
|
+
".mjs",
|
|
170
|
+
".tsx",
|
|
171
|
+
".jsx",
|
|
172
|
+
".json"
|
|
173
|
+
],
|
|
174
|
+
cache: false,
|
|
175
|
+
fsCache: false,
|
|
176
|
+
moduleCache: false,
|
|
177
|
+
requireCache: false,
|
|
178
|
+
sourceMaps: false,
|
|
179
|
+
interopDefault: true,
|
|
180
|
+
jsx: {
|
|
181
|
+
importSource: "react",
|
|
182
|
+
runtime: "automatic"
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
/**
|
|
186
|
+
* 解析命令字符串,返回命令和参数
|
|
187
|
+
*/
|
|
188
|
+
function createCmd(cmdStr, options = {}) {
|
|
189
|
+
const { prefix = "", onPrefix = () => {} } = options;
|
|
190
|
+
const { _, ...cmdOptions } = (0, mri.default)((0, string2argv.string2argv)(cmdStr));
|
|
191
|
+
const [cmd$1, ...params] = _;
|
|
192
|
+
if (prefix) {
|
|
193
|
+
if (cmd$1 !== prefix) return {
|
|
194
|
+
cmd: void 0,
|
|
195
|
+
params: [],
|
|
196
|
+
options: cmdOptions
|
|
197
|
+
};
|
|
198
|
+
if (params.length === 0) onPrefix();
|
|
199
|
+
return {
|
|
200
|
+
cmd: params.shift(),
|
|
201
|
+
params,
|
|
202
|
+
options: cmdOptions
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
cmd: cmd$1,
|
|
207
|
+
params,
|
|
208
|
+
options: cmdOptions
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 带有表情反应的函数执行包装器
|
|
213
|
+
*/
|
|
214
|
+
async function runWithReaction(e, fn, id = "60") {
|
|
215
|
+
await e.addReaction(id);
|
|
216
|
+
const result = await fn();
|
|
217
|
+
await e.delReaction(id);
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* 创建一个 LowDB 数据库实例
|
|
222
|
+
*/
|
|
223
|
+
async function createDB(filename, options = {}) {
|
|
224
|
+
const { defaultData = {}, compress = false } = options;
|
|
225
|
+
const database = new lowdb.Low(new lowdb_node.DataFile(filename, {
|
|
226
|
+
parse: JSON.parse,
|
|
227
|
+
stringify: (data) => JSON.stringify(data, null, compress ? 0 : 2)
|
|
228
|
+
}), defaultData);
|
|
229
|
+
await database.read();
|
|
230
|
+
return database;
|
|
231
|
+
}
|
|
232
|
+
function ensureBuffer(buffer, text$1 = "图片渲染失败") {
|
|
233
|
+
return buffer ? napcat_sdk.segment.image(`data:image/png;base64,${buffer.toString("base64")}`) : text$1;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 格式化时间间隔为可读字符串
|
|
237
|
+
*
|
|
238
|
+
* @param ms 时间间隔(毫秒)
|
|
239
|
+
* @returns 可读字符串
|
|
240
|
+
*/
|
|
241
|
+
function formatDuration(ms) {
|
|
242
|
+
const seconds = Math.floor(ms / 1e3);
|
|
243
|
+
const minutes = Math.floor(seconds / 60);
|
|
244
|
+
const hours = Math.floor(minutes / 60);
|
|
245
|
+
const days = Math.floor(hours / 24);
|
|
246
|
+
if (days > 0) return `${days}天${hours % 24}小时`;
|
|
247
|
+
if (hours > 0) return `${hours}小时${minutes % 60}分钟`;
|
|
248
|
+
if (minutes > 0) return `${minutes}分钟${seconds % 60}秒`;
|
|
249
|
+
return `${seconds}秒`;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 匹配输入文本与匹配模式,如果匹配成功,则回复匹配结果
|
|
253
|
+
*
|
|
254
|
+
* @param event 消息事件
|
|
255
|
+
* @param pattern 匹配模式
|
|
256
|
+
* @param quote 是否引用回复
|
|
257
|
+
* @returns 匹配结果
|
|
258
|
+
*/
|
|
259
|
+
async function match(event, pattern, quote = true) {
|
|
260
|
+
const inputText = text(event);
|
|
261
|
+
for (const [key, value] of Object.entries(pattern)) if (key === inputText) {
|
|
262
|
+
const res = await (typeof value === "function" ? value() : value);
|
|
263
|
+
if (res) return event.reply(res, quote);
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* 创建一个持久化数据库,基于 createDB 封装
|
|
269
|
+
*/
|
|
270
|
+
async function createStore(defaultData, options) {
|
|
271
|
+
const { compress = false, __dirname: __dirname$1, importMeta: meta, filename = "data.json" } = options || {};
|
|
272
|
+
const dirname = __dirname$1 || meta?.dirname || (meta?.url ? node_path.default.dirname((0, node_url.fileURLToPath)(meta.url)) : "");
|
|
273
|
+
if (!dirname) throw new Error("createStore: options.__dirname or options.meta must be provided");
|
|
274
|
+
const database = new lowdb.Low(new lowdb_node.DataFile(node_path.default.join(dirname, filename), {
|
|
275
|
+
parse: JSON.parse,
|
|
276
|
+
stringify: (data) => JSON.stringify(data, null, compress ? 0 : 2)
|
|
277
|
+
}), defaultData);
|
|
278
|
+
await database.read();
|
|
279
|
+
await database.write();
|
|
280
|
+
return database;
|
|
281
|
+
}
|
|
282
|
+
function md5(text$1, encoding = "hex") {
|
|
283
|
+
const hash = node_crypto.default.createHash("md5").update(text$1);
|
|
284
|
+
if (encoding === "buffer") return hash.digest();
|
|
285
|
+
return hash.digest(encoding);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* 数组去重
|
|
289
|
+
*/
|
|
290
|
+
function unique(array) {
|
|
291
|
+
return Array.from(new Set(array));
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* 确保值为数组
|
|
295
|
+
*
|
|
296
|
+
*/
|
|
297
|
+
function toArray(value) {
|
|
298
|
+
return Array.isArray(value) ? value : [value];
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* 是否是群消息
|
|
302
|
+
*/
|
|
303
|
+
const isGroupMsg = (event) => {
|
|
304
|
+
return "group" in event;
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* 是否是私聊消息
|
|
308
|
+
*/
|
|
309
|
+
const isPrivateMsg = (event) => {
|
|
310
|
+
return !isGroupMsg(event);
|
|
311
|
+
};
|
|
312
|
+
/**
|
|
313
|
+
* 异步延时函数
|
|
314
|
+
*
|
|
315
|
+
* @param {number} ms 等待毫秒数
|
|
316
|
+
* @return {Promise<void>}
|
|
317
|
+
*/
|
|
318
|
+
async function wait(ms) {
|
|
319
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* 获取今天的固定日期字符串,可用来作为「稳定随机」的参数,用于签到、每日任务等场景
|
|
323
|
+
*
|
|
324
|
+
* 格式: 2024/12/12,可选控制时区,默认为 'Asia/Shanghai' (亚洲/上海 时区)
|
|
325
|
+
*
|
|
326
|
+
* @param timeZone 指定的时区,默认为 'Asia/Shanghai'
|
|
327
|
+
* @returns 返回当前日期的字符串格式
|
|
328
|
+
*/
|
|
329
|
+
function localeDate(ts = Date.now(), options = {}) {
|
|
330
|
+
const { locale = "zh-CN", timeZone = "Asia/Shanghai" } = options;
|
|
331
|
+
const today = ts instanceof Date ? ts : new Date(ts);
|
|
332
|
+
return new Intl.DateTimeFormat(locale, {
|
|
333
|
+
year: "numeric",
|
|
334
|
+
month: "2-digit",
|
|
335
|
+
day: "2-digit",
|
|
336
|
+
timeZone
|
|
337
|
+
}).format(today);
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* 获取当前时间的固定时间字符串
|
|
341
|
+
*/
|
|
342
|
+
function localeTime(ts = Date.now(), options = {}) {
|
|
343
|
+
const { locale = "zh-CN", timeZone = "Asia/Shanghai", seconds = true } = options;
|
|
344
|
+
const now = ts instanceof Date ? ts : new Date(ts);
|
|
345
|
+
return new Intl.DateTimeFormat(locale, {
|
|
346
|
+
year: "numeric",
|
|
347
|
+
month: "2-digit",
|
|
348
|
+
day: "2-digit",
|
|
349
|
+
hour: "2-digit",
|
|
350
|
+
hourCycle: "h23",
|
|
351
|
+
minute: "2-digit",
|
|
352
|
+
second: seconds ? "2-digit" : void 0,
|
|
353
|
+
timeZone
|
|
354
|
+
}).format(now);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 生成指定范围(min ~ max)内的随机整数
|
|
358
|
+
*
|
|
359
|
+
* 额外支持「稳定随机」,继续传入额外参数即可,如果额外参数相同(忽略顺序),则生成的随机数相同
|
|
360
|
+
*/
|
|
361
|
+
function randomInt(min, max, ...hashArgs) {
|
|
362
|
+
if (min > max) throw new Error("min must be less than or equal to max");
|
|
363
|
+
if (hashArgs.length === 0) return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
364
|
+
const sortedArgs = hashArgs.slice().sort((a, b) => {
|
|
365
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
366
|
+
return JSON.stringify(a).localeCompare(JSON.stringify(b));
|
|
367
|
+
});
|
|
368
|
+
const hash = md5(JSON.stringify(sortedArgs));
|
|
369
|
+
const hashValue = Number.parseInt(hash.slice(0, 8), 16);
|
|
370
|
+
const range = max - min + 1;
|
|
371
|
+
return (hashValue % range + range) % range + min;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* 取数组内随机一项
|
|
375
|
+
*
|
|
376
|
+
* 额外支持「稳定随机」,继续传入额外参数即可,如果额外参数相同(忽略顺序),则生成的随机项
|
|
377
|
+
*/
|
|
378
|
+
function randomItem(array, ...hashArgs) {
|
|
379
|
+
if (!Array.isArray(array) || !array.length) throw new Error("randomItem: 参数必须是数组,且不能为空");
|
|
380
|
+
return array[randomInt(0, array.length - 1, ...hashArgs)];
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* 从数组中随机选出指定数量的项(不重复)
|
|
384
|
+
*
|
|
385
|
+
* 额外支持「稳定随机」,继续传入额外参数即可,如果额外参数相同(忽略顺序),则生成的随机项相同
|
|
386
|
+
*
|
|
387
|
+
* @param array 源数组
|
|
388
|
+
* @param count 要选择的项数量
|
|
389
|
+
* @param hashArgs 稳定随机的额外参数
|
|
390
|
+
* @returns 随机选出的项组成的数组
|
|
391
|
+
*/
|
|
392
|
+
function randomItems(array, count, ...hashArgs) {
|
|
393
|
+
if (!Array.isArray(array) || !array.length) throw new Error("randomItems: 参数必须是数组,且不能为空");
|
|
394
|
+
if (count < 0) throw new Error("randomItems: count 必须为非负整数");
|
|
395
|
+
if (count === 0) return [];
|
|
396
|
+
if (count > array.length) throw new Error(`randomItems: 要选择的数量 (${count}) 超过了数组长度 (${array.length})`);
|
|
397
|
+
if (count === array.length) return [...array];
|
|
398
|
+
const indices = Array.from({ length: array.length }, (_, i) => i);
|
|
399
|
+
const selected = [];
|
|
400
|
+
for (let i = 0; i < count; i++) {
|
|
401
|
+
const randomIdx = randomInt(0, indices.length - i - 1, ...hashArgs, `select_${i}`);
|
|
402
|
+
selected.push(indices[randomIdx]);
|
|
403
|
+
const lastIdx = indices.length - 1 - i;
|
|
404
|
+
[indices[randomIdx], indices[lastIdx]] = [indices[lastIdx], indices[randomIdx]];
|
|
405
|
+
}
|
|
406
|
+
const hasString = array.some(isString);
|
|
407
|
+
const items = selected.map((idx) => array[idx]);
|
|
408
|
+
return hasString ? items.sort((p, n) => isString(p) && isString(n) ? p.localeCompare(n) : 0) : items;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* 包含大写字母与数字的 6 位随机 ID 生成器
|
|
412
|
+
*/
|
|
413
|
+
function randomId() {
|
|
414
|
+
return Math.random().toString(16).slice(2, 8).toUpperCase();
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* 简单生成符合 UUID 规范的字符串,但不保证唯一性
|
|
418
|
+
*/
|
|
419
|
+
function uuid() {
|
|
420
|
+
return `${randStr(8)}-${randStr(4)}-${randStr(4)}-${randStr(4)}-${randStr(12)}`;
|
|
421
|
+
function randStr(length = 4) {
|
|
422
|
+
return Math.random().toString(16).substring(2, length + 2);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* clamp 操作,限制数值在指定范围内
|
|
427
|
+
*/
|
|
428
|
+
function clamp(n, min, max) {
|
|
429
|
+
return Math.min(max, Math.max(min, n));
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* 排除 null 和 undefined
|
|
433
|
+
*/
|
|
434
|
+
function noNullish(val) {
|
|
435
|
+
return val !== null && val !== void 0;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* 判断是否定义
|
|
439
|
+
*/
|
|
440
|
+
function isDefined(val) {
|
|
441
|
+
return typeof val !== "undefined";
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* 通过消息事件生成唯一 id
|
|
445
|
+
*/
|
|
446
|
+
function toMsgId(event) {
|
|
447
|
+
return `${event.seq}_${event.rand}`;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* 判断是否为函数
|
|
451
|
+
*/
|
|
452
|
+
function isFunction(val) {
|
|
453
|
+
return typeof val === "function";
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* 判断是否为数字
|
|
457
|
+
*/
|
|
458
|
+
function isNumber(val) {
|
|
459
|
+
return typeof val === "number";
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* 判断是否为布尔值
|
|
463
|
+
*/
|
|
464
|
+
function isBoolean(val) {
|
|
465
|
+
return typeof val === "boolean";
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* 判断是否为字符串
|
|
469
|
+
*/
|
|
470
|
+
function isString(val) {
|
|
471
|
+
return typeof val === "string";
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* 判断是否为对象
|
|
475
|
+
*/
|
|
476
|
+
function isObject(val) {
|
|
477
|
+
return Object.prototype.toString.call(val) === "[object Object]";
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* 将数字转换为本地化数字字符串
|
|
481
|
+
*/
|
|
482
|
+
function localNum(num, locale = "zh-CN") {
|
|
483
|
+
return num.toLocaleString(locale);
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* 通过 QQ 号获取任意头像链接
|
|
487
|
+
*
|
|
488
|
+
* size 可选: 0 | 40 | 100 | 160 | 640,0 为原图
|
|
489
|
+
*/
|
|
490
|
+
function getQQAvatarLink(qq, size = 640) {
|
|
491
|
+
return `https://q.qlogo.cn/headimg_dl?dst_uin=${qq}&spec=${size}`;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* 通过群号获取任意群头像链接
|
|
495
|
+
*
|
|
496
|
+
* size 可选: 40 | 100 | 640,0 为原图
|
|
497
|
+
*/
|
|
498
|
+
function getGroupAvatarLink(group, size = 640) {
|
|
499
|
+
return `https://p.qlogo.cn/gh/111111/${group}/${size}`;
|
|
500
|
+
}
|
|
501
|
+
const messageCacheMap = /* @__PURE__ */ new Map();
|
|
502
|
+
/** 获取引用回复的消息 */
|
|
503
|
+
async function getQuoteMessage(event, timeout = 3e3) {
|
|
504
|
+
if (!event.quote_id) return null;
|
|
505
|
+
const quote_id = event.quote_id;
|
|
506
|
+
const key = isGroupMsg(event) ? `${event.group_id}_${quote_id}` : `${event.sender.user_id}_${quote_id}`;
|
|
507
|
+
const cacheMsg = messageCacheMap.get(key);
|
|
508
|
+
const isFetching = cacheMsg === "loading";
|
|
509
|
+
if (cacheMsg !== void 0 && !isFetching) return cacheMsg;
|
|
510
|
+
if (isFetching) {
|
|
511
|
+
const start$1 = Date.now();
|
|
512
|
+
return new Promise((resolve) => {
|
|
513
|
+
const timer = setInterval(() => {
|
|
514
|
+
const cacheMsg$1 = messageCacheMap.get(key);
|
|
515
|
+
if (cacheMsg$1 !== void 0 && !(cacheMsg$1 === "loading")) {
|
|
516
|
+
clearInterval(timer);
|
|
517
|
+
resolve(cacheMsg$1);
|
|
518
|
+
} else if (Date.now() - start$1 > timeout) {
|
|
519
|
+
clearInterval(timer);
|
|
520
|
+
throw new Error(`>>> 获取引用消息超时 ${timeout}, Key: ${key}`);
|
|
521
|
+
}
|
|
522
|
+
}, 100);
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
messageCacheMap.set(key, "loading");
|
|
526
|
+
const msg = await event.getQuoteMessage();
|
|
527
|
+
if (messageCacheMap.size > 100) messageCacheMap.clear();
|
|
528
|
+
messageCacheMap.set(key, msg);
|
|
529
|
+
return msg;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* 获取原创表情包的图片链接
|
|
533
|
+
*/
|
|
534
|
+
async function getBfaceUrl(file) {
|
|
535
|
+
const id = file.slice(0, 2);
|
|
536
|
+
const hash = file.slice(0, 32);
|
|
537
|
+
for (const f of [
|
|
538
|
+
"raw300.gif",
|
|
539
|
+
"raw200.gif",
|
|
540
|
+
"raw100.gif",
|
|
541
|
+
"300x300.png",
|
|
542
|
+
"200x200.png",
|
|
543
|
+
"100x100.png"
|
|
544
|
+
]) {
|
|
545
|
+
const url = `https://gxh.vip.qq.com/club/item/parcel/item/${id}/${hash}/${f}`;
|
|
546
|
+
if ((await fetch(url, { method: "HEAD" })).status === 200) return url;
|
|
547
|
+
}
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* 获取消息中的图片链接
|
|
552
|
+
*/
|
|
553
|
+
async function getImageUrl(event) {
|
|
554
|
+
return find(event, "image")?.url || "";
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* 获取引用回复的消息中的图片链接
|
|
558
|
+
*/
|
|
559
|
+
async function getQuoteImageUrl(event) {
|
|
560
|
+
const quoteMsg = await getQuoteMessage(event);
|
|
561
|
+
if (!quoteMsg) return "";
|
|
562
|
+
return await getImageUrl(quoteMsg);
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* 获取消息提及的图片链接(消息或者引用消息)
|
|
566
|
+
*/
|
|
567
|
+
async function getMentionedImageUrl(event) {
|
|
568
|
+
return await getImageUrl(event) || await getQuoteImageUrl(event);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* 获取消息中的图片元素
|
|
572
|
+
*/
|
|
573
|
+
function getImage(event) {
|
|
574
|
+
return find(Array.isArray(event) ? event : event.message, "image") || null;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* 获取引用回复的图片消息
|
|
578
|
+
*/
|
|
579
|
+
async function getQuoteImage(event) {
|
|
580
|
+
const quoteMsg = await getQuoteMessage(event);
|
|
581
|
+
if (quoteMsg) return find(quoteMsg.message, "image") || null;
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* 获取消息提及的图片(消息或者引用消息)
|
|
586
|
+
*/
|
|
587
|
+
async function getMentionedImage(event) {
|
|
588
|
+
return getImage(event) || await getQuoteImage(event);
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* 获取消息中的文本内容,默认采取 'whole' 模式,去除整体的首尾空格,可选 'each' 模式,去除每个文本的首尾空格
|
|
592
|
+
*
|
|
593
|
+
* 如: whole 模式下 => ' 123 [表情] 456 ' => '123 456'
|
|
594
|
+
* 如: each 模式下 => ' 123 [表情] 456 ' => '123456'
|
|
595
|
+
*/
|
|
596
|
+
function text(event, options = {}) {
|
|
597
|
+
const { trim = true } = options;
|
|
598
|
+
const texts = (Array.isArray(event) ? event : event.message).filter((msg) => msg.type === "text").map((msg) => msg.text);
|
|
599
|
+
let result;
|
|
600
|
+
if (trim === "whole") result = texts.join("").trim();
|
|
601
|
+
else if (trim === "each") result = texts.map((t) => t.trim()).join("");
|
|
602
|
+
else if (trim === true) result = texts.map((t) => t.trim()).join("");
|
|
603
|
+
else result = texts.join("");
|
|
604
|
+
return result || "";
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* 获取回复的消息中的文本内容
|
|
608
|
+
*/
|
|
609
|
+
async function getQuoteText(event) {
|
|
610
|
+
const msg = await getQuoteMessage(event);
|
|
611
|
+
if (!msg) return "";
|
|
612
|
+
return text(msg);
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* 获取提到的用户 QQ 号,可以通过 if(!qq) 判断是否提到了用户,返回 0 代表没有提到用户
|
|
616
|
+
*/
|
|
617
|
+
async function getMentionedUserId(event) {
|
|
618
|
+
const quoteId = (await getQuoteMessage(event))?.sender.user_id || 0;
|
|
619
|
+
const msgAtId = +(find(event.message, "at")?.qq || 0);
|
|
620
|
+
return Number.isNaN(msgAtId) || !msgAtId ? quoteId : msgAtId;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* 获取 **一个** 指定类型的消息元素,如获取图片、表情等,如果没有则返回 undefined
|
|
624
|
+
*/
|
|
625
|
+
function find(event, type) {
|
|
626
|
+
return (Array.isArray(event) ? event : event.message).find((msg) => msg.type === type);
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* 获取 **所有** 指定类型的消息元素,如获取图片、表情等,如果没有则返回 []
|
|
630
|
+
*/
|
|
631
|
+
function filter(event, type) {
|
|
632
|
+
return (Array.isArray(event) ? event : event.message).filter((msg) => msg.type === type);
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* 错误信息字符串格式化
|
|
636
|
+
*
|
|
637
|
+
* @param {any} error 待处理错误
|
|
638
|
+
* @return {string} stringify 结果
|
|
639
|
+
*/
|
|
640
|
+
function stringifyError(error) {
|
|
641
|
+
if (typeof error === "object") return `${error.constructor?.name ?? "未知错误"}: ${error.message ?? "[无报错信息]"}`;
|
|
642
|
+
return String(error);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Encodes string | number | buffer using base64.
|
|
646
|
+
*/
|
|
647
|
+
function base64Encode(str) {
|
|
648
|
+
return Buffer.from(str.toString()).toString("base64");
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Decodes the string from base64 to UTF-8.
|
|
652
|
+
*
|
|
653
|
+
* @param {string} str - The base64-encoded string.
|
|
654
|
+
*/
|
|
655
|
+
function base64Decode(str, type = "utf8") {
|
|
656
|
+
if (type === "buffer") return Buffer.from(str, "base64");
|
|
657
|
+
return Buffer.from(str, "base64").toString(type);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* JS 对象转换成 `urlencoded` 格式字符串 { name: 'Bob', age: 18 } => name=Bob&age=18
|
|
661
|
+
*
|
|
662
|
+
* @param {Record<number | string, any>} obj JS 对象
|
|
663
|
+
* @return {string} 转换后的字符串
|
|
664
|
+
*/
|
|
665
|
+
function qs(obj) {
|
|
666
|
+
return new URLSearchParams(obj).toString();
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* 格式化展示 QQ 等级
|
|
670
|
+
*/
|
|
671
|
+
function formatQQLevel(level) {
|
|
672
|
+
return "👑".repeat(Math.floor(level / 64)) + "☀️".repeat(Math.floor(level % 64 / 16)) + "🌙".repeat(Math.floor(level % 16 / 4)) + "⭐️".repeat(level % 4);
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* 申请通过开发者工具登录,以获取 Cookie
|
|
676
|
+
*/
|
|
677
|
+
async function requestLoginViaDevTools() {
|
|
678
|
+
const code = await getDevToolsLoginCode();
|
|
679
|
+
return {
|
|
680
|
+
code,
|
|
681
|
+
url: `https://h5.qzone.qq.com/qqq/code/${code}?_proxy=1&from=ide`
|
|
682
|
+
};
|
|
683
|
+
/**
|
|
684
|
+
* 获取开发者工具登录码
|
|
685
|
+
*/
|
|
686
|
+
async function getDevToolsLoginCode() {
|
|
687
|
+
const response = await fetch("https://q.qq.com/ide/devtoolAuth/GetLoginCode", {
|
|
688
|
+
method: "GET",
|
|
689
|
+
headers: {
|
|
690
|
+
qua: "V1_HT5_QDT_0.70.2209190_x64_0_DEV_D",
|
|
691
|
+
host: "q.qq.com",
|
|
692
|
+
accept: "application/json",
|
|
693
|
+
"content-type": "application/json"
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
if (!response.ok) return "";
|
|
697
|
+
const { code: code$1, data } = await response.json();
|
|
698
|
+
if (+code$1 !== 0) return "";
|
|
699
|
+
return data.code ?? "";
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* 获取开发者工具登录结果
|
|
704
|
+
*/
|
|
705
|
+
async function queryDevToolsLoginStatus(code) {
|
|
706
|
+
const response = await fetch(`https://q.qq.com/ide/devtoolAuth/syncScanSateGetTicket?code=${code}`, {
|
|
707
|
+
method: "GET",
|
|
708
|
+
headers: {
|
|
709
|
+
qua: "V1_HT5_QDT_0.70.2209190_x64_0_DEV_D",
|
|
710
|
+
host: "q.qq.com",
|
|
711
|
+
accept: "application/json",
|
|
712
|
+
"content-type": "application/json"
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
if (!response.ok) return { status: "Error" };
|
|
716
|
+
const { code: resCode, data } = await response.json();
|
|
717
|
+
if (+resCode === 0) {
|
|
718
|
+
if (+data.ok !== 1) return { status: "Wait" };
|
|
719
|
+
return {
|
|
720
|
+
status: "OK",
|
|
721
|
+
ticket: data.ticket
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
if (+resCode === -10003) return { status: "Used" };
|
|
725
|
+
return { status: "Error" };
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* 通过开发者工具登录获取 AuthCode
|
|
729
|
+
*/
|
|
730
|
+
async function getAuthCodeViaTicket(ticket, appid) {
|
|
731
|
+
const response = await fetch("https://q.qq.com/ide/login", {
|
|
732
|
+
method: "POST",
|
|
733
|
+
headers: {
|
|
734
|
+
qua: "V1_HT5_QDT_0.70.2209190_x64_0_DEV_D",
|
|
735
|
+
host: "q.qq.com",
|
|
736
|
+
accept: "application/json",
|
|
737
|
+
"content-type": "application/json"
|
|
738
|
+
},
|
|
739
|
+
body: JSON.stringify({
|
|
740
|
+
appid,
|
|
741
|
+
ticket
|
|
742
|
+
})
|
|
743
|
+
});
|
|
744
|
+
if (!response.ok) return "";
|
|
745
|
+
const { code } = await response.json();
|
|
746
|
+
return code || "";
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* 通过 Auth Code 获取 minico Token
|
|
750
|
+
*/
|
|
751
|
+
async function getMinicoTokenViaAuthCode(authCode, appid) {
|
|
752
|
+
const response = await fetch("https://minico.qq.com/minico/oauth20?uin=QQ%E5%AE%89%E5%85%A8%E4%B8%AD%E5%BF%83", {
|
|
753
|
+
method: "POST",
|
|
754
|
+
headers: { "Content-Type": "application/json" },
|
|
755
|
+
body: JSON.stringify({
|
|
756
|
+
appid,
|
|
757
|
+
code: authCode,
|
|
758
|
+
platform: "qq"
|
|
759
|
+
})
|
|
760
|
+
});
|
|
761
|
+
if (!response.ok) return {};
|
|
762
|
+
const { retcode, data } = await response.json();
|
|
763
|
+
if (+retcode !== 0 || !data) return {};
|
|
764
|
+
return data || {};
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* 获取终端输入,返回 Promise,支持提示信息
|
|
768
|
+
*/
|
|
769
|
+
async function getTerminalInput(inputTip = "请输入") {
|
|
770
|
+
return new Promise((resolve) => {
|
|
771
|
+
if (inputTip) console.log(inputTip);
|
|
772
|
+
function getInput() {
|
|
773
|
+
process.stdin.once("data", async (e) => {
|
|
774
|
+
const input = e.toString().trim();
|
|
775
|
+
if (input) {
|
|
776
|
+
resolve(input);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
getInput();
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
getInput();
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
function bindBot(bot, func) {
|
|
786
|
+
return (...args) => func(bot, ...args);
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* 当前 Node.js 进程的启动时间,常量,Date 类型
|
|
790
|
+
*/
|
|
791
|
+
const START_TIME = /* @__PURE__ */ new Date();
|
|
792
|
+
|
|
793
|
+
//#endregion
|
|
794
|
+
//#region src/config.ts
|
|
795
|
+
var config_exports = /* @__PURE__ */ __export({
|
|
796
|
+
BOT_CWD: () => BOT_CWD,
|
|
797
|
+
botConfig: () => botConfig,
|
|
798
|
+
getLogFilePath: () => getLogFilePath,
|
|
799
|
+
hasRight: () => hasRight,
|
|
800
|
+
isAdmin: () => isAdmin,
|
|
801
|
+
isInPm2: () => isInPm2,
|
|
802
|
+
isOwner: () => isOwner,
|
|
803
|
+
isOwnerOrAdmin: () => isOwnerOrAdmin,
|
|
804
|
+
readMiokiConfig: () => readMiokiConfig,
|
|
805
|
+
readPackageJson: () => readPackageJson,
|
|
806
|
+
updateBotCWD: () => updateBotCWD,
|
|
807
|
+
updateBotConfig: () => updateBotConfig,
|
|
808
|
+
writePackageJson: () => writePackageJson
|
|
809
|
+
});
|
|
810
|
+
/**
|
|
811
|
+
* 机器人根目录
|
|
812
|
+
*/
|
|
813
|
+
const BOT_CWD = { value: process.cwd() };
|
|
814
|
+
function readPackageJson() {
|
|
815
|
+
if (!node_fs.default.existsSync(node_path.default.join(BOT_CWD.value, "package.json"))) throw new Error(`无法在 ${BOT_CWD.value} 下找到 package.json 文件,请确认当前目录是否为机器人根目录`);
|
|
816
|
+
return JSON.parse(node_fs.default.readFileSync(node_path.default.join(BOT_CWD.value, "package.json"), "utf-8")) || {};
|
|
817
|
+
}
|
|
818
|
+
function writePackageJson(pkg) {
|
|
819
|
+
node_fs.default.writeFileSync(node_path.default.join(BOT_CWD.value, "package.json"), JSON.stringify(pkg, null, 2), "utf-8");
|
|
820
|
+
}
|
|
821
|
+
function readMiokiConfig() {
|
|
822
|
+
const config = readPackageJson().mioki;
|
|
823
|
+
if (!config) throw new Error(`无法在 package.json 中找到 mioki 配置,请确认 package.json 文件中是否包含 mioki 字段`);
|
|
824
|
+
if (!config.napcat) throw new Error(`mioki 配置中缺少 napcat 字段,请补全后重试`);
|
|
825
|
+
return readPackageJson().mioki;
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* `mioki` 框架相关配置
|
|
829
|
+
*/
|
|
830
|
+
const botConfig = readMiokiConfig();
|
|
831
|
+
/**
|
|
832
|
+
* 更新 `mioki` 配置,同时同步更新本地配置文件
|
|
833
|
+
*/
|
|
834
|
+
const updateBotConfig = async (draftFn) => {
|
|
835
|
+
await draftFn(botConfig);
|
|
836
|
+
botConfig.plugins = unique(botConfig.plugins).toSorted((prev, next) => prev.localeCompare(next));
|
|
837
|
+
botConfig.admins = unique(botConfig.admins).toSorted((prev, next) => prev - next);
|
|
838
|
+
const pkg = readPackageJson();
|
|
839
|
+
pkg.mioki = structuredClone(botConfig);
|
|
840
|
+
writePackageJson(pkg);
|
|
841
|
+
console.log(`>>> 检测到配置变动,已同步至 package.json 文件`);
|
|
842
|
+
};
|
|
843
|
+
/**
|
|
844
|
+
* 更新机器人根目录
|
|
845
|
+
*/
|
|
846
|
+
const updateBotCWD = (root) => {
|
|
847
|
+
BOT_CWD.value = root;
|
|
848
|
+
console.log(`>>> 机器人根目录已设置为 ${root}`);
|
|
849
|
+
};
|
|
850
|
+
/**
|
|
851
|
+
* 是否是主人
|
|
852
|
+
*/
|
|
853
|
+
const isOwner = (id) => {
|
|
854
|
+
const owners = botConfig.owners;
|
|
855
|
+
return isNumber(id) ? owners.includes(id) : "sender" in id ? owners.includes(id.sender.user_id) : owners.includes(id.user_id);
|
|
856
|
+
};
|
|
857
|
+
/**
|
|
858
|
+
* 是否是管理员,注意: 主人不是管理员
|
|
859
|
+
*/
|
|
860
|
+
const isAdmin = (id) => {
|
|
861
|
+
const admins = botConfig.admins;
|
|
862
|
+
return isNumber(id) ? admins.includes(id) : "sender" in id ? admins.includes(id.sender.user_id) : admins.includes(id.user_id);
|
|
863
|
+
};
|
|
864
|
+
/**
|
|
865
|
+
* 是否是主人或管理员
|
|
866
|
+
*/
|
|
867
|
+
const isOwnerOrAdmin = (id) => {
|
|
868
|
+
return isOwner(id) || isAdmin(id);
|
|
869
|
+
};
|
|
870
|
+
/**
|
|
871
|
+
* 是否有权限,即:主人或管理员
|
|
872
|
+
*/
|
|
873
|
+
const hasRight = (id) => {
|
|
874
|
+
return isOwnerOrAdmin(id);
|
|
875
|
+
};
|
|
876
|
+
/**
|
|
877
|
+
* 是否在 PM2 中运行
|
|
878
|
+
*/
|
|
879
|
+
const isInPm2 = Boolean("pm_id" in process.env || "PM2_USAGE" in process.env);
|
|
880
|
+
/**
|
|
881
|
+
* 获取日志文件名
|
|
882
|
+
*/
|
|
883
|
+
function getLogFilePath(uin, platformName) {
|
|
884
|
+
const startTime = (0, dayjs.default)().format("YYYY-MM-DD_HH-mm-ss");
|
|
885
|
+
return node_path.default.join(BOT_CWD.value, `logs/${uin}_${platformName}_${startTime}.log`);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
//#endregion
|
|
889
|
+
//#region package.json
|
|
890
|
+
var version = "0.2.1";
|
|
891
|
+
|
|
892
|
+
//#endregion
|
|
893
|
+
//#region src/actions.ts
|
|
894
|
+
var actions_exports = /* @__PURE__ */ __export({
|
|
895
|
+
createForwardMsg: () => createForwardMsg,
|
|
896
|
+
getViolationRecords: () => getViolationRecords,
|
|
897
|
+
noticeAdmins: () => noticeAdmins,
|
|
898
|
+
noticeFriends: () => noticeFriends,
|
|
899
|
+
noticeGroups: () => noticeGroups,
|
|
900
|
+
noticeMainOwner: () => noticeMainOwner,
|
|
901
|
+
noticeOwners: () => noticeOwners,
|
|
902
|
+
runWithErrorHandler: () => runWithErrorHandler,
|
|
903
|
+
signArk: () => signArk,
|
|
904
|
+
uploadImageToCollection: () => uploadImageToCollection,
|
|
905
|
+
uploadImageToGroupHomework: () => uploadImageToGroupHomework,
|
|
906
|
+
uploadImageToGroupNotice: () => uploadImageToGroupNotice
|
|
907
|
+
});
|
|
908
|
+
/**
|
|
909
|
+
* 群发群消息
|
|
910
|
+
*/
|
|
911
|
+
async function noticeGroups(bot, groupIdList, message, delay = 1e3) {
|
|
912
|
+
if (!bot.isOnline()) {
|
|
913
|
+
bot.logger.error("发送失败,Bot 不在线");
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
if (!message) {
|
|
917
|
+
bot.logger.warn("消息内容为空");
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
for (const groupId of groupIdList) {
|
|
921
|
+
const group = await bot.pickGroup(groupId);
|
|
922
|
+
if (!group) continue;
|
|
923
|
+
await group.sendMsg(message);
|
|
924
|
+
await wait(delay);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* 群发好友消息
|
|
929
|
+
*/
|
|
930
|
+
async function noticeFriends(bot, friendIdList, message, delay = 1e3) {
|
|
931
|
+
if (!bot.isOnline) {
|
|
932
|
+
bot.logger.error("发送失败,Bot 不在线");
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
if (!message) {
|
|
936
|
+
bot.logger.warn("消息内容为空");
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
for (const friendId of friendIdList) {
|
|
940
|
+
const friend = await bot.pickFriend(friendId);
|
|
941
|
+
if (!friend) continue;
|
|
942
|
+
await friend.sendMsg(message);
|
|
943
|
+
await wait(delay);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* 群发通知给管理员
|
|
948
|
+
*/
|
|
949
|
+
async function noticeAdmins(bot, message, delay = 1e3) {
|
|
950
|
+
await noticeFriends(bot, botConfig.admins, message, delay);
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* 群发通知给主人
|
|
954
|
+
*/
|
|
955
|
+
async function noticeOwners(bot, message, delay = 1e3) {
|
|
956
|
+
await noticeFriends(bot, botConfig.owners, message, delay);
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* 群发通知给第一个主人
|
|
960
|
+
*/
|
|
961
|
+
async function noticeMainOwner(bot, message) {
|
|
962
|
+
if (!bot.isOnline()) {
|
|
963
|
+
bot.logger.error("发送失败,Bot 不在线");
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (!message) {
|
|
967
|
+
bot.logger.warn("消息内容为空");
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
const mainOwner = botConfig.owners[0];
|
|
971
|
+
if (mainOwner) {
|
|
972
|
+
await (await bot.pickFriend(mainOwner))?.sendMsg(message);
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
throw new Error("请至少设置一个主人");
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* 签名卡片 json
|
|
979
|
+
*/
|
|
980
|
+
async function signArk(bot, json) {
|
|
981
|
+
const { cookie, gtk } = await bot.getCookie("qzone.qq.com");
|
|
982
|
+
const fetchArk = (url) => {
|
|
983
|
+
return fetch(url, {
|
|
984
|
+
method: "POST",
|
|
985
|
+
headers: {
|
|
986
|
+
"Content-Type": "application/json",
|
|
987
|
+
Cookie: cookie
|
|
988
|
+
},
|
|
989
|
+
body: JSON.stringify({ ark: json })
|
|
990
|
+
});
|
|
991
|
+
};
|
|
992
|
+
const SignUrl = {
|
|
993
|
+
normal: `https://h5.qzone.qq.com/v2/vip/tx/trpc/ark-share/GenSignedArk?g_tk=${gtk}`,
|
|
994
|
+
new: `https://h5.qzone.qq.com/v2/vip/tx/trpc/ark-share/GenNewSignedArk?g_tk=${gtk}`
|
|
995
|
+
};
|
|
996
|
+
try {
|
|
997
|
+
const { code, data = {} } = await (await fetchArk(SignUrl.normal)).json();
|
|
998
|
+
if (+code === 0 && data?.signed_ark) return data.signed_ark;
|
|
999
|
+
throw new Error("签不了一点");
|
|
1000
|
+
} catch {
|
|
1001
|
+
const { code, data = {} } = await (await fetchArk(SignUrl.new)).json();
|
|
1002
|
+
if (+code === 0 && data?.signed_ark) return data.signed_ark;
|
|
1003
|
+
throw new Error("签不了一点");
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* 运行函数并捕获错误, 并通过 event.reply 发送错误信息
|
|
1008
|
+
*/
|
|
1009
|
+
async function runWithErrorHandler(bot, fn, event, message = (err) => `报...报错了啦! 杂鱼~ 杂鱼~ \n\n${err}`) {
|
|
1010
|
+
try {
|
|
1011
|
+
return await fn();
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
const errorMsg = typeof message === "function" ? message(stringifyError(error)) : message;
|
|
1014
|
+
if (event) await event.reply(errorMsg);
|
|
1015
|
+
else try {
|
|
1016
|
+
await noticeMainOwner(bot, "发送失败,可能被风控,请检查签名状态。");
|
|
1017
|
+
} catch {
|
|
1018
|
+
bot.logger.error("发送失败,可能被风控,请检查签名状态。");
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
/** 创建和并转发消息 */
|
|
1023
|
+
function createForwardMsg(bot, message = [], options = {}) {
|
|
1024
|
+
const { user_id = bot.uin, nickname } = options;
|
|
1025
|
+
const content = message.map((msg) => typeof msg === "string" ? napcat_sdk.segment.text(msg) : msg);
|
|
1026
|
+
return napcat_sdk.segment.node({
|
|
1027
|
+
user_id: String(user_id),
|
|
1028
|
+
nickname,
|
|
1029
|
+
content
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* 上传图片到收藏
|
|
1034
|
+
*/
|
|
1035
|
+
async function uploadImageToCollection(bot, buffer) {
|
|
1036
|
+
const pskey = await bot.getPskey("weiyun.com") || "";
|
|
1037
|
+
const uuid$1 = node_crypto.default.randomUUID();
|
|
1038
|
+
let randomID = node_crypto.default.randomInt(1, 99);
|
|
1039
|
+
if (randomID < 10) randomID = `0${randomID}`;
|
|
1040
|
+
const options = {
|
|
1041
|
+
method: "POST",
|
|
1042
|
+
headers: {
|
|
1043
|
+
"Content-Type": "application/octet-stream",
|
|
1044
|
+
Cookie: `uin=${bot.uin}; vt=27; vi=${pskey}; pid=00${randomID}/${uuid$1}; appid=30243`,
|
|
1045
|
+
"User-Agent": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
|
|
1046
|
+
},
|
|
1047
|
+
body: buffer
|
|
1048
|
+
};
|
|
1049
|
+
let code = void 0;
|
|
1050
|
+
for (let i = 3; i > 0; i--) {
|
|
1051
|
+
const response = await fetch("https://uploader.collector.weiyun.com/pic_uploader.fcg", options);
|
|
1052
|
+
const headers = response.headers;
|
|
1053
|
+
code = +(headers.get("user-returncode") || "");
|
|
1054
|
+
const msg = headers.get("user-errmsg");
|
|
1055
|
+
console.log(3 - i, "uploadImageToCollector", response.status, code, msg);
|
|
1056
|
+
if (code === 0) break;
|
|
1057
|
+
}
|
|
1058
|
+
return code === 0 ? `https://shp.qpic.cn/collector//${uuid$1}/0` : "";
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* 上传图片到群作业
|
|
1062
|
+
*/
|
|
1063
|
+
async function uploadImageToGroupHomework(bot, imgBase64) {
|
|
1064
|
+
const { cookie, bkn } = await bot.getCookie("qun.qq.com");
|
|
1065
|
+
const data = await (await fetch("https://qun.qq.com/cgi-bin/hw/util/image", {
|
|
1066
|
+
method: "POST",
|
|
1067
|
+
headers: {
|
|
1068
|
+
cookie,
|
|
1069
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1070
|
+
origin: "https://qun.qq.com",
|
|
1071
|
+
referer: "https://qun.qq.com/homework/p/features/index.html"
|
|
1072
|
+
},
|
|
1073
|
+
body: `pic=${encodeURIComponent(imgBase64)}&client_type=1&bkn=${bkn}`
|
|
1074
|
+
})).json();
|
|
1075
|
+
if (+data.retcode !== 0) return "";
|
|
1076
|
+
return data?.data?.url?.origin || "";
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* 上传图片到群公告
|
|
1080
|
+
*/
|
|
1081
|
+
async function uploadImageToGroupNotice(bot, urlOrBlob) {
|
|
1082
|
+
const { bkn, legacyCookie } = await bot.getCookie("qun.qq.com");
|
|
1083
|
+
const blob = urlOrBlob instanceof Blob ? urlOrBlob : await (await fetch(urlOrBlob)).blob();
|
|
1084
|
+
const form = new FormData();
|
|
1085
|
+
form.append("m", "0");
|
|
1086
|
+
form.append("source", "troopNotice");
|
|
1087
|
+
form.append("bkn", String(bkn));
|
|
1088
|
+
form.append("qid", "0");
|
|
1089
|
+
form.append("pic_up", blob, `image.${blob.type.split("/")[1]}`);
|
|
1090
|
+
const { id, ec } = await (await fetch("https://web.qun.qq.com/cgi-bin/announce/upload_img", {
|
|
1091
|
+
method: "POST",
|
|
1092
|
+
body: form,
|
|
1093
|
+
headers: {
|
|
1094
|
+
"content-type": "multipart/form-data",
|
|
1095
|
+
cookie: legacyCookie
|
|
1096
|
+
}
|
|
1097
|
+
})).json() || {};
|
|
1098
|
+
if (!id) throw new Error(`图片上传失败,ec: ${ec}`);
|
|
1099
|
+
const imgData = JSON.parse(id.replace(/"/g, "\"")) || {};
|
|
1100
|
+
if (!imgData.id) throw new Error("图片上传失败,未获取到图片 id");
|
|
1101
|
+
return {
|
|
1102
|
+
...imgData,
|
|
1103
|
+
url: `https://p.qpic.cn/gdynamic/${imgData.id}/0`,
|
|
1104
|
+
url2: `https://p.qlogo.cn/gdynamic/${imgData.id}/0`,
|
|
1105
|
+
url3: `https://p2.qpic.cn/gdynamic/${imgData.id}/0`,
|
|
1106
|
+
url4: `https://gdynamic.qpic.cn/gdynamic/${imgData.id}/0`,
|
|
1107
|
+
url5: `https://img.wecar.qq.com/gdynamic/${imgData.id}/0`,
|
|
1108
|
+
url6: `https://cross.store.qq.com/gdynamic/${imgData.id}/0`
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* 获取 QQ 安全中心违规记录
|
|
1113
|
+
*/
|
|
1114
|
+
async function getViolationRecords(bot, authCode, appid, size = 100) {
|
|
1115
|
+
const minicoData = await getMinicoTokenViaAuthCode(authCode, appid);
|
|
1116
|
+
if (!minicoData) return [];
|
|
1117
|
+
const params = new URLSearchParams({
|
|
1118
|
+
...minicoData,
|
|
1119
|
+
appid,
|
|
1120
|
+
token: minicoData.minico_token
|
|
1121
|
+
});
|
|
1122
|
+
params.delete("expire");
|
|
1123
|
+
params.delete("minico_token");
|
|
1124
|
+
const { retcode, records } = await (await fetch(`https://minico.qq.com/minico/cgiproxy/v3_release/v3/getillegalityhistory?${params.toString()}`, {
|
|
1125
|
+
method: "POST",
|
|
1126
|
+
headers: { "Content-Type": "application/json" },
|
|
1127
|
+
body: JSON.stringify({
|
|
1128
|
+
com: {
|
|
1129
|
+
src: 0,
|
|
1130
|
+
scene: 1001,
|
|
1131
|
+
platform: 2,
|
|
1132
|
+
version: "8.9.85.12820"
|
|
1133
|
+
},
|
|
1134
|
+
pageNum: 0,
|
|
1135
|
+
pageSize: size
|
|
1136
|
+
})
|
|
1137
|
+
})).json();
|
|
1138
|
+
if (+retcode !== 0 || !records) return [];
|
|
1139
|
+
return records || [];
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
//#endregion
|
|
1143
|
+
//#region src/services.ts
|
|
1144
|
+
const USER_SERVICE = {};
|
|
1145
|
+
/**
|
|
1146
|
+
* 服务,由其他插件贡献的方法、数据等
|
|
1147
|
+
*/
|
|
1148
|
+
const services = USER_SERVICE;
|
|
1149
|
+
/**
|
|
1150
|
+
* 给 `Mioki` 添加公共服务,可用于插件间通信和共享数据
|
|
1151
|
+
*
|
|
1152
|
+
* 请注意合理设置插件的 `priority` 属性,以确保服务的正确加载顺序,`priority` 默认为 100,越小越先加载
|
|
1153
|
+
*
|
|
1154
|
+
* 建议需要调用 `addService` 的插件设置 `priority` 为 `10`
|
|
1155
|
+
*/
|
|
1156
|
+
function addService(name, service, cover = true) {
|
|
1157
|
+
if (cover || !USER_SERVICE[name]) USER_SERVICE[name] = service;
|
|
1158
|
+
return () => USER_SERVICE[name] = void 0;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
//#endregion
|
|
1162
|
+
//#region src/plugin.ts
|
|
1163
|
+
const runtimePlugins = /* @__PURE__ */ new Map();
|
|
1164
|
+
const buildRemovedActions = (bot) => Object.fromEntries(Object.entries(actions_exports).map(([k, v]) => [k, bindBot(bot, v)]));
|
|
1165
|
+
/**
|
|
1166
|
+
* 定义一个 Mioki 插件
|
|
1167
|
+
* @param plugin Mioki 插件对象
|
|
1168
|
+
* @returns Mioki 插件对象
|
|
1169
|
+
*/
|
|
1170
|
+
function definePlugin(plugin) {
|
|
1171
|
+
return plugin;
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* 确保插件目录存在
|
|
1175
|
+
*/
|
|
1176
|
+
function ensurePluginDir() {
|
|
1177
|
+
const dir = getAbsPluginDir();
|
|
1178
|
+
if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* 获取插件目录的绝对路径
|
|
1182
|
+
*/
|
|
1183
|
+
function getAbsPluginDir(defaultDir = "plugin") {
|
|
1184
|
+
const cwd = BOT_CWD.value;
|
|
1185
|
+
return node_path.default.resolve(cwd, botConfig.plugin_dir || defaultDir);
|
|
1186
|
+
}
|
|
1187
|
+
async function enablePlugin(bot, plugin, type = "external") {
|
|
1188
|
+
const typeDesc = type === "builtin" ? "内置" : "用户";
|
|
1189
|
+
const pluginName = plugin.name || "null";
|
|
1190
|
+
const { name = pluginName, version: version$1 = "null", description = "-", setup = () => {} } = plugin;
|
|
1191
|
+
try {
|
|
1192
|
+
const start$1 = node_process.hrtime.bigint();
|
|
1193
|
+
const clears = /* @__PURE__ */ new Set();
|
|
1194
|
+
const userClears = /* @__PURE__ */ new Set();
|
|
1195
|
+
const context = {
|
|
1196
|
+
bot,
|
|
1197
|
+
segment: bot.segment,
|
|
1198
|
+
...utils_exports,
|
|
1199
|
+
...config_exports,
|
|
1200
|
+
...buildRemovedActions(bot),
|
|
1201
|
+
services,
|
|
1202
|
+
clears: userClears,
|
|
1203
|
+
addService: (name$1, service, cover) => {
|
|
1204
|
+
const removeService = addService(name$1, service, cover);
|
|
1205
|
+
clears.add(removeService);
|
|
1206
|
+
return removeService;
|
|
1207
|
+
},
|
|
1208
|
+
handle: (eventName, handler) => {
|
|
1209
|
+
bot.on(eventName, handler);
|
|
1210
|
+
const unsubscribe = () => bot.off(eventName, handler);
|
|
1211
|
+
clears.add(unsubscribe);
|
|
1212
|
+
return unsubscribe;
|
|
1213
|
+
},
|
|
1214
|
+
cron: (cronExpression, handler) => {
|
|
1215
|
+
const job = node_cron.default.schedule(cronExpression, (now) => handler(context, now));
|
|
1216
|
+
const clear = () => job.stop();
|
|
1217
|
+
clears.add(clear);
|
|
1218
|
+
return job;
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
clears.add(await setup(context) || (() => {}));
|
|
1222
|
+
runtimePlugins.set(name, {
|
|
1223
|
+
name,
|
|
1224
|
+
type,
|
|
1225
|
+
version: version$1,
|
|
1226
|
+
description,
|
|
1227
|
+
plugin,
|
|
1228
|
+
disable: async () => {
|
|
1229
|
+
try {
|
|
1230
|
+
await Promise.all([...clears, ...userClears].map((fn) => fn?.()));
|
|
1231
|
+
runtimePlugins.delete(name);
|
|
1232
|
+
} catch (err) {
|
|
1233
|
+
throw new Error(`禁用插件 [${typeDesc}]${name}@${version$1} 失败: ${err?.message}`);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
const end = node_process.hrtime.bigint();
|
|
1238
|
+
const time = Math.round(Number(end - start$1)) / 1e6;
|
|
1239
|
+
bot.logger.info(`启用插件 [${typeDesc}]${name}@${version$1} => 耗时 ${time} ms`);
|
|
1240
|
+
} catch (e) {
|
|
1241
|
+
throw new Error(`启用插件 [${typeDesc}]${name}@${version$1} 失败: ${e?.message}`);
|
|
1242
|
+
}
|
|
1243
|
+
return plugin;
|
|
1244
|
+
}
|
|
1245
|
+
async function findLocalPlugins() {
|
|
1246
|
+
return (await node_fs.default.promises.readdir(getAbsPluginDir(), { withFileTypes: true })).filter((e) => e.isDirectory() && !!e.name && !e.name.startsWith("_")).map((e) => ({
|
|
1247
|
+
name: e.name,
|
|
1248
|
+
absPath: node_path.default.join(getAbsPluginDir(), e.name)
|
|
1249
|
+
}));
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
//#endregion
|
|
1253
|
+
//#region src/builtins/cmd/status.ts
|
|
1254
|
+
const SystemMap = {
|
|
1255
|
+
Linux: "Linux",
|
|
1256
|
+
Darwin: "macOS",
|
|
1257
|
+
Windows_NT: "Win"
|
|
1258
|
+
};
|
|
1259
|
+
const ArchMap = {
|
|
1260
|
+
ia32: "x86",
|
|
1261
|
+
arm: "arm",
|
|
1262
|
+
arm64: "arm64",
|
|
1263
|
+
x64: "x64"
|
|
1264
|
+
};
|
|
1265
|
+
async function getMiokiStatus(bot) {
|
|
1266
|
+
const osType = node_os.default.type();
|
|
1267
|
+
const osArch = node_os.default.arch();
|
|
1268
|
+
const isInUnix = ["Linux", "Darwin"].includes(osType);
|
|
1269
|
+
const arch = ArchMap[osArch] || osArch;
|
|
1270
|
+
const [osInfo, localPlugins, versionInfo] = await Promise.all([
|
|
1271
|
+
systeminformation.default.osInfo(),
|
|
1272
|
+
findLocalPlugins(),
|
|
1273
|
+
bot.getVersionInfo()
|
|
1274
|
+
]);
|
|
1275
|
+
const pluginCount = localPlugins.length + BUILTIN_PLUGINS.length;
|
|
1276
|
+
const system = isInUnix ? {
|
|
1277
|
+
name: osInfo.distro,
|
|
1278
|
+
version: osInfo.release
|
|
1279
|
+
} : {
|
|
1280
|
+
name: SystemMap[osType] || osType,
|
|
1281
|
+
version: "-"
|
|
1282
|
+
};
|
|
1283
|
+
const totalMem = node_os.default.totalmem();
|
|
1284
|
+
const usedMem = totalMem - node_os.default.freemem();
|
|
1285
|
+
const rssMem = process.memoryUsage.rss();
|
|
1286
|
+
const nodeVersion = process.versions.node;
|
|
1287
|
+
const cpu = getCpuInfo();
|
|
1288
|
+
return {
|
|
1289
|
+
bot: {
|
|
1290
|
+
uin: bot.uin,
|
|
1291
|
+
nickname: bot.nickname
|
|
1292
|
+
},
|
|
1293
|
+
plugins: {
|
|
1294
|
+
enabled: runtimePlugins.size,
|
|
1295
|
+
total: pluginCount
|
|
1296
|
+
},
|
|
1297
|
+
stats: { uptime: process.uptime() * 1e3 },
|
|
1298
|
+
versions: {
|
|
1299
|
+
node: nodeVersion,
|
|
1300
|
+
mioki: version,
|
|
1301
|
+
napcat: versionInfo.app_version,
|
|
1302
|
+
protocol: versionInfo.protocol_version
|
|
1303
|
+
},
|
|
1304
|
+
system: {
|
|
1305
|
+
name: system.name,
|
|
1306
|
+
version: system.version,
|
|
1307
|
+
arch
|
|
1308
|
+
},
|
|
1309
|
+
memory: {
|
|
1310
|
+
used: usedMem,
|
|
1311
|
+
total: totalMem,
|
|
1312
|
+
percent: Number((usedMem / totalMem * 100).toFixed(1)),
|
|
1313
|
+
rss: {
|
|
1314
|
+
used: rssMem,
|
|
1315
|
+
percent: Number((rssMem / totalMem * 100).toFixed(1))
|
|
1316
|
+
}
|
|
1317
|
+
},
|
|
1318
|
+
disk: isInUnix ? await getDiskUsageInUnix() : {
|
|
1319
|
+
total: 0,
|
|
1320
|
+
used: 0,
|
|
1321
|
+
free: 0,
|
|
1322
|
+
percent: 0
|
|
1323
|
+
},
|
|
1324
|
+
cpu: {
|
|
1325
|
+
name: cpu.name,
|
|
1326
|
+
count: cpu.count,
|
|
1327
|
+
percent: Number((await measureCpuUsage()).toFixed(1))
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
async function getMiokiStatusStr(client) {
|
|
1332
|
+
const { bot, plugins, stats, system, disk, cpu, memory, versions } = await getMiokiStatus(client);
|
|
1333
|
+
const diskValid = disk.total > 0 && disk.free >= 0;
|
|
1334
|
+
const diskDesc = `${disk.percent}%-${(0, filesize.filesize)(disk.used, { round: 1 })}/${(0, filesize.filesize)(disk.total, { round: 1 })}`;
|
|
1335
|
+
return `
|
|
1336
|
+
👤 ${bot.nickname}
|
|
1337
|
+
🆔 ${bot.uin}
|
|
1338
|
+
🧩 启用了 ${localNum(plugins.enabled)} 个插件,共 ${localNum(plugins.total)} 个
|
|
1339
|
+
🚀 ${(0, filesize.filesize)(memory.rss.used, { round: 1 })}/${memory.percent}%
|
|
1340
|
+
⏳ 已运行 ${(0, pretty_ms.default)(stats.uptime, {
|
|
1341
|
+
hideYear: true,
|
|
1342
|
+
secondsDecimalDigits: 0
|
|
1343
|
+
})}
|
|
1344
|
+
🤖 NapCat/${versions.napcat}-mioki/${versions.mioki}
|
|
1345
|
+
🖥️ ${system.name.split(" ")[0]}/${system.version.split(".")[0]}-${system.name}-node/${versions.node.split(".")[0]}
|
|
1346
|
+
📊 ${memory.percent}%-${(0, filesize.filesize)(memory.used, {
|
|
1347
|
+
base: 2,
|
|
1348
|
+
round: 1
|
|
1349
|
+
})}/${(0, filesize.filesize)(memory.total, {
|
|
1350
|
+
base: 2,
|
|
1351
|
+
round: 1
|
|
1352
|
+
})}
|
|
1353
|
+
🧮 ${cpu.percent}%-${cpu.name}-${cpu.count}核
|
|
1354
|
+
💾 ${diskValid ? diskDesc : "N/A (不适用)"}
|
|
1355
|
+
`.trim();
|
|
1356
|
+
}
|
|
1357
|
+
async function getDiskUsageInUnix(path$6 = "/") {
|
|
1358
|
+
return new Promise((resolve) => {
|
|
1359
|
+
node_child_process.default.exec(`df -k ${path$6} | tail -1 | awk '{print $2,$4}'`, (err, stdout) => {
|
|
1360
|
+
if (err) {
|
|
1361
|
+
console.error(err);
|
|
1362
|
+
return resolve({
|
|
1363
|
+
total: 0,
|
|
1364
|
+
used: 0,
|
|
1365
|
+
free: 0,
|
|
1366
|
+
percent: 0
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
const [_total, _free] = stdout.trim().split(" ");
|
|
1370
|
+
const total = Number(_total) * 1024;
|
|
1371
|
+
const free = Number(_free) * 1024;
|
|
1372
|
+
const used = total - free;
|
|
1373
|
+
resolve({
|
|
1374
|
+
total,
|
|
1375
|
+
free,
|
|
1376
|
+
used,
|
|
1377
|
+
percent: Number((used / total * 100).toFixed(1))
|
|
1378
|
+
});
|
|
1379
|
+
});
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
async function measureCpuUsage(interval = 600) {
|
|
1383
|
+
const start$1 = getCpuTimes();
|
|
1384
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
1385
|
+
const end = getCpuTimes();
|
|
1386
|
+
return (1 - (end.idle - start$1.idle) / (end.total - start$1.total)) * 100;
|
|
1387
|
+
}
|
|
1388
|
+
function getCpuTimes() {
|
|
1389
|
+
const cpus = node_os.default.cpus();
|
|
1390
|
+
let idle = 0;
|
|
1391
|
+
let total = 0;
|
|
1392
|
+
for (const cpu of cpus) {
|
|
1393
|
+
for (const type in cpu.times) total += cpu.times[type];
|
|
1394
|
+
idle += cpu.times.idle;
|
|
1395
|
+
}
|
|
1396
|
+
return {
|
|
1397
|
+
idle,
|
|
1398
|
+
total
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
function getCpuInfo() {
|
|
1402
|
+
const cpus = node_os.default.cpus();
|
|
1403
|
+
return {
|
|
1404
|
+
name: cpus[0]?.model || "[未知CPU]",
|
|
1405
|
+
count: cpus.length
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
//#endregion
|
|
1410
|
+
//#region src/builtins/cmd/index.ts
|
|
1411
|
+
const corePlugins = ["mioki-cmd"];
|
|
1412
|
+
const cmd = definePlugin({
|
|
1413
|
+
name: "kivi-cmd",
|
|
1414
|
+
version,
|
|
1415
|
+
priority: 1,
|
|
1416
|
+
setup(ctx) {
|
|
1417
|
+
const prefix = (ctx.botConfig.prefix ?? "#").replace(/[-_.^$?[\]{}]/g, "\\$&");
|
|
1418
|
+
const cmdPrefix = /* @__PURE__ */ new RegExp(`^${prefix}`);
|
|
1419
|
+
const displayPrefix = prefix.replace(/\\\\/g, "\\");
|
|
1420
|
+
ctx.addService("miokiStatus", () => getMiokiStatus(ctx.bot));
|
|
1421
|
+
ctx.handle("message", (e) => ctx.runWithErrorHandler(async () => {
|
|
1422
|
+
const text$1 = ctx.text(e);
|
|
1423
|
+
if (!cmdPrefix.test(text$1)) return;
|
|
1424
|
+
if (text$1.replace(cmdPrefix, "") === "状态") {
|
|
1425
|
+
const status = await getMiokiStatusStr(ctx.bot);
|
|
1426
|
+
await e.reply(`〓 🟢 mioki 状态 〓\n${status}`.trim());
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
if (!ctx.isOwner(e)) return;
|
|
1430
|
+
const { _: params, ..._options } = (0, mri.default)((0, string2argv.string2argv)(text$1));
|
|
1431
|
+
const cmd$1 = params.shift()?.replace(cmdPrefix, "") ?? "";
|
|
1432
|
+
const [subCmd, target, ..._subParams] = params;
|
|
1433
|
+
switch (cmd$1) {
|
|
1434
|
+
case "帮助":
|
|
1435
|
+
await e.reply((0, dedent.default)(`
|
|
1436
|
+
〓 💡 mioki 帮助 〓
|
|
1437
|
+
${displayPrefix}插件 👉 框架插件管理
|
|
1438
|
+
${displayPrefix}状态 👉 显示框架状态
|
|
1439
|
+
${displayPrefix}设置 👉 框架设置管理
|
|
1440
|
+
${displayPrefix}帮助 👉 显示帮助信息
|
|
1441
|
+
${displayPrefix}退出 👉 退出框架进程
|
|
1442
|
+
`).trim());
|
|
1443
|
+
break;
|
|
1444
|
+
case "插件":
|
|
1445
|
+
if (corePlugins.includes(target)) {
|
|
1446
|
+
await e.reply("内置插件无法操作", true);
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
switch (subCmd) {
|
|
1450
|
+
case "列表": {
|
|
1451
|
+
const plugins = unique([...(await findLocalPlugins()).map((e$1) => e$1.name), ...runtimePlugins.keys()]).map((name) => {
|
|
1452
|
+
const isEnable = runtimePlugins.get(name);
|
|
1453
|
+
return `${isEnable ? "🟢" : "🔴"} ${isEnable && isEnable?.type === "builtin" ? "[内置]" : "[用户]"} ${name}`;
|
|
1454
|
+
}).toSorted((pre, next) => {
|
|
1455
|
+
function weight(str) {
|
|
1456
|
+
let w = 0;
|
|
1457
|
+
if (str.includes("🟢")) w += 10;
|
|
1458
|
+
if (str.includes("[内置]")) w += 1;
|
|
1459
|
+
return w;
|
|
1460
|
+
}
|
|
1461
|
+
const preWeight = weight(pre);
|
|
1462
|
+
return weight(next) - preWeight || pre.localeCompare(next);
|
|
1463
|
+
});
|
|
1464
|
+
await e.reply((0, dedent.default)(`
|
|
1465
|
+
〓 插件列表 〓
|
|
1466
|
+
${plugins.join("\n")}
|
|
1467
|
+
共 ${plugins.length} 个,启用 ${runtimePlugins.size} 个
|
|
1468
|
+
`).trim());
|
|
1469
|
+
break;
|
|
1470
|
+
}
|
|
1471
|
+
case "启用": {
|
|
1472
|
+
if (!target) {
|
|
1473
|
+
await e.reply("请指定插件 ID", true);
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
if (runtimePlugins.has(target)) {
|
|
1477
|
+
await e.reply(`插件 ${target} 已经是启用状态`, true);
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
const pluginPath = node_path.default.join(BOT_CWD.value, "plugins", target);
|
|
1481
|
+
if (!node_fs.default.existsSync(pluginPath)) {
|
|
1482
|
+
await e.reply(`插件 ${target} 不存在`, true);
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
try {
|
|
1486
|
+
const plugin = await jiti$1.import(pluginPath, { default: true });
|
|
1487
|
+
if (plugin.name !== target) {
|
|
1488
|
+
const tip = `[插件目录名称: ${target}] 和插件代码中设置的 [name: ${plugin.name}] 不一致,可能导致重载异常,请修改后重启。`;
|
|
1489
|
+
ctx.bot.logger.warn(tip);
|
|
1490
|
+
ctx.noticeMainOwner(tip);
|
|
1491
|
+
}
|
|
1492
|
+
await enablePlugin(ctx.bot, plugin);
|
|
1493
|
+
} catch (err) {
|
|
1494
|
+
await e.reply(`插件 ${target} 启用失败:${err?.message || "未知错误"}`, true);
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
await updateBotConfig((c) => c.plugins = [...botConfig.plugins, target]);
|
|
1498
|
+
await e.reply(`插件 ${target} 启用成功`, true);
|
|
1499
|
+
break;
|
|
1500
|
+
}
|
|
1501
|
+
case "禁用": {
|
|
1502
|
+
if (!target) {
|
|
1503
|
+
await e.reply("请指定插件 ID", true);
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
const plugin = runtimePlugins.get(target);
|
|
1507
|
+
if (!plugin) {
|
|
1508
|
+
await e.reply(`插件 ${target} 不存在`, true);
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
try {
|
|
1512
|
+
await plugin.disable();
|
|
1513
|
+
} catch (err) {
|
|
1514
|
+
await e.reply(err?.message, true);
|
|
1515
|
+
break;
|
|
1516
|
+
}
|
|
1517
|
+
await updateBotConfig((c) => c.plugins = botConfig.plugins.filter((name) => name !== target));
|
|
1518
|
+
ctx.bot.logger.info(`禁用插件 => ${target}`);
|
|
1519
|
+
await e.reply(`插件 ${target} 已禁用`, true);
|
|
1520
|
+
break;
|
|
1521
|
+
}
|
|
1522
|
+
case "重载": {
|
|
1523
|
+
if (!target) {
|
|
1524
|
+
await e.reply("请指定插件 ID", true);
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
let isOff = false;
|
|
1528
|
+
const plugin = runtimePlugins.get(target);
|
|
1529
|
+
try {
|
|
1530
|
+
if (plugin) await plugin.disable();
|
|
1531
|
+
const pluginPath = node_path.default.join(BOT_CWD.value, "plugins", target);
|
|
1532
|
+
if (!node_fs.default.existsSync(pluginPath)) {
|
|
1533
|
+
await e.reply(`插件 ${target} 不存在`, true);
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
if (!plugin) isOff = true;
|
|
1537
|
+
const importedPlugin = await jiti$1.import(pluginPath, { default: true });
|
|
1538
|
+
if (importedPlugin.name !== target) {
|
|
1539
|
+
const tip = `插件目录名称: ${target} 和插件代码中设置的 name: ${importedPlugin.name} 不一致,可能导致重载异常,请修改后重启。`;
|
|
1540
|
+
ctx.bot.logger.warn(tip);
|
|
1541
|
+
ctx.noticeMainOwner(tip);
|
|
1542
|
+
}
|
|
1543
|
+
await enablePlugin(ctx.bot, importedPlugin);
|
|
1544
|
+
} catch (err) {
|
|
1545
|
+
await e.reply(err?.message, true);
|
|
1546
|
+
await updateBotConfig((c) => c.plugins = c.plugins.filter((name) => name !== target));
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
1549
|
+
await updateBotConfig((c) => c.plugins = [...c.plugins, target]);
|
|
1550
|
+
await e.reply(`插件 ${target} 已${isOff ? "直接启用" : "重载"}`, true);
|
|
1551
|
+
break;
|
|
1552
|
+
}
|
|
1553
|
+
default:
|
|
1554
|
+
await e.reply((0, dedent.default)(`
|
|
1555
|
+
〓 🧩 mioki 插件 〓
|
|
1556
|
+
${displayPrefix}插件 列表
|
|
1557
|
+
${displayPrefix}插件 启用 <插件 ID>
|
|
1558
|
+
${displayPrefix}插件 禁用 <插件 ID>
|
|
1559
|
+
${displayPrefix}插件 重载 <插件 ID>
|
|
1560
|
+
`).trim());
|
|
1561
|
+
break;
|
|
1562
|
+
}
|
|
1563
|
+
break;
|
|
1564
|
+
case "设置":
|
|
1565
|
+
switch (subCmd) {
|
|
1566
|
+
case "详情":
|
|
1567
|
+
await e.reply((0, dedent.default)(`
|
|
1568
|
+
〓 设置详情 〓
|
|
1569
|
+
主人: ${botConfig.owners.join(", ")}
|
|
1570
|
+
管理: ${botConfig.admins.join(", ").trim()}
|
|
1571
|
+
启用插件: ${botConfig.plugins.join(", ").trim()}
|
|
1572
|
+
`).trim());
|
|
1573
|
+
break;
|
|
1574
|
+
case "加主人":
|
|
1575
|
+
case "添加主人": {
|
|
1576
|
+
const inputUid = Number.parseInt(target);
|
|
1577
|
+
const uid = Number.isNaN(inputUid) ? +(e.message.find((e$1) => e$1.type === "at")?.qq || 0) : inputUid || 0;
|
|
1578
|
+
if (!uid || Number.isNaN(uid)) {
|
|
1579
|
+
await e.reply("请指定主人 QQ/AT", true);
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
if (botConfig.owners.includes(uid)) {
|
|
1583
|
+
await e.reply(`主人 ${uid} 已存在`, true);
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
await updateBotConfig((c) => c.owners = [...c.owners, uid]);
|
|
1587
|
+
await e.reply(`已添加主人 ${uid}`, true);
|
|
1588
|
+
break;
|
|
1589
|
+
}
|
|
1590
|
+
case "删主人":
|
|
1591
|
+
case "删除主人": {
|
|
1592
|
+
const inputUid = Number.parseInt(target);
|
|
1593
|
+
const uid = Number.isNaN(inputUid) ? +(e.message.find((e$1) => e$1.type === "at")?.qq || 0) : inputUid || 0;
|
|
1594
|
+
if (!uid || Number.isNaN(uid)) {
|
|
1595
|
+
await e.reply("请指定主人 QQ/AT", true);
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
if (uid === ctx.botConfig.admins[0]) {
|
|
1599
|
+
await e.reply("不能删除第一主人", true);
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
const idx = botConfig.owners.indexOf(uid);
|
|
1603
|
+
if (idx === -1) {
|
|
1604
|
+
await e.reply(`主人 ${uid} 不存在`, true);
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
await updateBotConfig((c) => c.owners.splice(idx, 1));
|
|
1608
|
+
await e.reply(`已删除主人 ${uid}`, true);
|
|
1609
|
+
break;
|
|
1610
|
+
}
|
|
1611
|
+
case "加管理":
|
|
1612
|
+
case "添加管理": {
|
|
1613
|
+
const inputUid = Number.parseInt(target);
|
|
1614
|
+
const uid = Number.isNaN(inputUid) ? +(e.message.find((e$1) => e$1.type === "at")?.qq || 0) : inputUid || 0;
|
|
1615
|
+
if (!uid || Number.isNaN(uid)) {
|
|
1616
|
+
await e.reply("请指定管理 QQ/AT", true);
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
if (botConfig.admins.includes(uid)) {
|
|
1620
|
+
await e.reply(`管理 ${uid} 已存在`, true);
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
await updateBotConfig((c) => c.admins = [...c.admins, uid]);
|
|
1624
|
+
await e.reply(`已添加管理 ${uid}`, true);
|
|
1625
|
+
break;
|
|
1626
|
+
}
|
|
1627
|
+
case "删管理":
|
|
1628
|
+
case "删除管理": {
|
|
1629
|
+
const inputUid = Number.parseInt(target);
|
|
1630
|
+
const uid = Number.isNaN(inputUid) ? +(e.message.find((e$1) => e$1.type === "at")?.qq || 0) : inputUid || 0;
|
|
1631
|
+
if (!uid || Number.isNaN(uid)) {
|
|
1632
|
+
await e.reply("请指定管理 QQ/AT", true);
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
const idx = botConfig.admins.indexOf(uid);
|
|
1636
|
+
if (idx === -1) {
|
|
1637
|
+
await e.reply(`管理 ${uid} 不存在`, true);
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
await updateBotConfig((c) => c.admins.splice(idx, 1));
|
|
1641
|
+
await e.reply(`已删除管理 ${uid}`, true);
|
|
1642
|
+
break;
|
|
1643
|
+
}
|
|
1644
|
+
default:
|
|
1645
|
+
await e.reply((0, dedent.default)(`
|
|
1646
|
+
〓 ⚙️ mioki 设置 〓
|
|
1647
|
+
${displayPrefix}设置 详情
|
|
1648
|
+
${displayPrefix}设置 [加/删]主人 <QQ/AT>
|
|
1649
|
+
${displayPrefix}设置 [加/删]管理 <QQ/AT>
|
|
1650
|
+
`).trim());
|
|
1651
|
+
break;
|
|
1652
|
+
}
|
|
1653
|
+
break;
|
|
1654
|
+
case "退出":
|
|
1655
|
+
await e.reply("またね~", true);
|
|
1656
|
+
ctx.bot.logger.info("接收到退出指令,即将退出... 如需自动重启,请使用 pm2 部署。");
|
|
1657
|
+
process.exit(0);
|
|
1658
|
+
}
|
|
1659
|
+
}, e));
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
var cmd_default = cmd;
|
|
1663
|
+
|
|
1664
|
+
//#endregion
|
|
1665
|
+
//#region src/builtins/index.ts
|
|
1666
|
+
const BUILTIN_PLUGINS = [cmd_default];
|
|
1667
|
+
|
|
1668
|
+
//#endregion
|
|
1669
|
+
//#region src/start.ts
|
|
1670
|
+
async function start(options = {}) {
|
|
1671
|
+
const { cwd = process.cwd() } = options;
|
|
1672
|
+
if (cwd !== BOT_CWD.value) updateBotCWD(cwd);
|
|
1673
|
+
process.title = `mioki v${version}`;
|
|
1674
|
+
const logger = getMiokiLogger(botConfig.log_level || "info");
|
|
1675
|
+
const plugin_dir = getAbsPluginDir();
|
|
1676
|
+
logger.info(`>>> mioki v${version} 启动中,工作目录: ${BOT_CWD.value},插件目录: ${plugin_dir}`);
|
|
1677
|
+
const napcat = new napcat_sdk.NapCat({
|
|
1678
|
+
...botConfig.napcat,
|
|
1679
|
+
logger
|
|
1680
|
+
});
|
|
1681
|
+
napcat.on("napcat.connected", async ({ uin }) => {
|
|
1682
|
+
logger.info(`>>> 已连接到 NapCat 服务器,当前登录 QQ 账号: ${uin}`);
|
|
1683
|
+
let lastNoticeTime = 0;
|
|
1684
|
+
process.on("uncaughtException", async (err) => {
|
|
1685
|
+
napcat.logger.error(">>> uncaughtException, 出错了", err);
|
|
1686
|
+
if (Date.now() - lastNoticeTime < 1e3) return;
|
|
1687
|
+
lastNoticeTime = Date.now();
|
|
1688
|
+
await noticeMainOwner(napcat, `mioki 发生未捕获异常:\n\n${err?.message || "未知错误"}`);
|
|
1689
|
+
});
|
|
1690
|
+
process.on("unhandledRejection", async (err) => {
|
|
1691
|
+
napcat.logger.error(">>> unhandledRejection, 出错了", err);
|
|
1692
|
+
if (Date.now() - lastNoticeTime < 1e3) return;
|
|
1693
|
+
lastNoticeTime = Date.now();
|
|
1694
|
+
const date = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
1695
|
+
await noticeMainOwner(napcat, `【${date}】\n\nmioki 发生未处理异常:\n\n${err?.message || "未知错误"}`);
|
|
1696
|
+
});
|
|
1697
|
+
ensurePluginDir();
|
|
1698
|
+
const plugins = botConfig.plugins.map((p) => ({
|
|
1699
|
+
dirName: p,
|
|
1700
|
+
absPath: node_path.default.resolve(plugin_dir, p)
|
|
1701
|
+
})).filter((p) => {
|
|
1702
|
+
if (!node_fs.default.existsSync(p.absPath)) {
|
|
1703
|
+
napcat.logger.warn(`>>> 插件 ${p.dirName} 不存在,已忽略`);
|
|
1704
|
+
return false;
|
|
1705
|
+
}
|
|
1706
|
+
return true;
|
|
1707
|
+
});
|
|
1708
|
+
const failedImportPlugins = [];
|
|
1709
|
+
const promises = plugins.map(async ({ absPath, dirName }) => {
|
|
1710
|
+
try {
|
|
1711
|
+
const plugin = await jiti$1.import(absPath, { default: true });
|
|
1712
|
+
if (plugin.name !== dirName) {
|
|
1713
|
+
const tip = `>>> 插件目录名 [${dirName}] 和插件声明的 name [${plugin.name}] 不一致,可能导致重载异常,请修改一致后重启。`;
|
|
1714
|
+
napcat.logger.warn(tip);
|
|
1715
|
+
noticeMainOwner(napcat, tip);
|
|
1716
|
+
}
|
|
1717
|
+
return plugin;
|
|
1718
|
+
} catch (e) {
|
|
1719
|
+
const err = stringifyError(e);
|
|
1720
|
+
failedImportPlugins.push([dirName, err]);
|
|
1721
|
+
return null;
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
const start$1 = node_process.hrtime.bigint();
|
|
1725
|
+
const sortedUserPlugins = (await Promise.all(promises)).filter(Boolean).toSorted((prev, next) => (prev.priority ?? 100) - (next.priority ?? 100));
|
|
1726
|
+
if (failedImportPlugins.length) {
|
|
1727
|
+
const tip = `>>> ${failedImportPlugins.length} 个插件加载失败: \n\n${failedImportPlugins.map(([dirName, err]) => `${dirName}: ${err}`).join("\n\n")}`;
|
|
1728
|
+
napcat.logger.warn(tip);
|
|
1729
|
+
noticeMainOwner(napcat, tip);
|
|
1730
|
+
}
|
|
1731
|
+
const pluginGroups = /* @__PURE__ */ new Map();
|
|
1732
|
+
for (const plugin of sortedUserPlugins) {
|
|
1733
|
+
const priority = plugin.priority ?? 100;
|
|
1734
|
+
if (!pluginGroups.has(priority)) pluginGroups.set(priority, []);
|
|
1735
|
+
pluginGroups.get(priority).push(plugin);
|
|
1736
|
+
}
|
|
1737
|
+
const sortedGroups = Array.from(pluginGroups.entries()).toSorted(([a], [b]) => a - b);
|
|
1738
|
+
const failedEnablePlugins = [];
|
|
1739
|
+
try {
|
|
1740
|
+
napcat.logger.info(`>>> 加载内置插件: ${BUILTIN_PLUGINS.map((p) => p.name).join(", ")}`);
|
|
1741
|
+
await Promise.all(BUILTIN_PLUGINS.map((p) => enablePlugin(napcat, p, "builtin")));
|
|
1742
|
+
for (const [priority, plugins$1] of sortedGroups) {
|
|
1743
|
+
napcat.logger.info(`>>> 加载优先级 ${priority} 的插件: ${plugins$1.map((p) => p.name).join(", ")}`);
|
|
1744
|
+
await Promise.all(plugins$1.map(async (p) => {
|
|
1745
|
+
try {
|
|
1746
|
+
await enablePlugin(napcat, p, "external");
|
|
1747
|
+
} catch (e) {
|
|
1748
|
+
const err = stringifyError(e);
|
|
1749
|
+
failedEnablePlugins.push([p.name, err]);
|
|
1750
|
+
}
|
|
1751
|
+
}));
|
|
1752
|
+
}
|
|
1753
|
+
} catch (e) {
|
|
1754
|
+
napcat.logger.error(e?.message);
|
|
1755
|
+
noticeMainOwner(napcat, e?.message);
|
|
1756
|
+
}
|
|
1757
|
+
const end = node_process.hrtime.bigint();
|
|
1758
|
+
const costTime = Math.round(Number(end - start$1)) / 1e6;
|
|
1759
|
+
const failedCount = failedImportPlugins.length + failedEnablePlugins.length;
|
|
1760
|
+
const failedInfo = failedCount > 0 ? `${failedCount} 个失败 (导入 ${failedImportPlugins.length},启用 ${failedImportPlugins.length})。` : "";
|
|
1761
|
+
napcat.logger.info(`>>> 成功加载了 ${runtimePlugins.size} 个插件。${failedInfo ? failedInfo : ""}总耗时 ${costTime} ms`);
|
|
1762
|
+
if (botConfig.online_push) await noticeMainOwner(napcat, `✅ mioki v${version} 已就绪`);
|
|
1763
|
+
});
|
|
1764
|
+
await napcat.run();
|
|
1765
|
+
}
|
|
4
1766
|
|
|
5
1767
|
//#endregion
|
|
1768
|
+
exports.BOT_CWD = BOT_CWD;
|
|
1769
|
+
exports.ChromeUA = ChromeUA;
|
|
1770
|
+
exports.START_TIME = START_TIME;
|
|
1771
|
+
exports.addService = addService;
|
|
1772
|
+
exports.base64Decode = base64Decode;
|
|
1773
|
+
exports.base64Encode = base64Encode;
|
|
1774
|
+
exports.botConfig = botConfig;
|
|
1775
|
+
exports.clamp = clamp;
|
|
1776
|
+
exports.createCmd = createCmd;
|
|
1777
|
+
exports.createDB = createDB;
|
|
1778
|
+
exports.createForwardMsg = createForwardMsg;
|
|
1779
|
+
exports.createStore = createStore;
|
|
1780
|
+
Object.defineProperty(exports, 'dayjs', {
|
|
1781
|
+
enumerable: true,
|
|
1782
|
+
get: function () {
|
|
1783
|
+
return dayjs.default;
|
|
1784
|
+
}
|
|
1785
|
+
});
|
|
1786
|
+
Object.defineProperty(exports, 'dedent', {
|
|
1787
|
+
enumerable: true,
|
|
1788
|
+
get: function () {
|
|
1789
|
+
return dedent.default;
|
|
1790
|
+
}
|
|
1791
|
+
});
|
|
1792
|
+
exports.definePlugin = definePlugin;
|
|
1793
|
+
exports.enablePlugin = enablePlugin;
|
|
1794
|
+
exports.ensureBuffer = ensureBuffer;
|
|
1795
|
+
exports.ensurePluginDir = ensurePluginDir;
|
|
1796
|
+
Object.defineProperty(exports, 'filesize', {
|
|
1797
|
+
enumerable: true,
|
|
1798
|
+
get: function () {
|
|
1799
|
+
return filesize.filesize;
|
|
1800
|
+
}
|
|
1801
|
+
});
|
|
1802
|
+
exports.filter = filter;
|
|
1803
|
+
exports.find = find;
|
|
1804
|
+
exports.findLocalPlugins = findLocalPlugins;
|
|
1805
|
+
exports.formatDuration = formatDuration;
|
|
1806
|
+
exports.formatQQLevel = formatQQLevel;
|
|
1807
|
+
Object.defineProperty(exports, 'fs', {
|
|
1808
|
+
enumerable: true,
|
|
1809
|
+
get: function () {
|
|
1810
|
+
return node_fs.default;
|
|
1811
|
+
}
|
|
1812
|
+
});
|
|
1813
|
+
exports.getAbsPluginDir = getAbsPluginDir;
|
|
1814
|
+
exports.getAuthCodeViaTicket = getAuthCodeViaTicket;
|
|
1815
|
+
exports.getBfaceUrl = getBfaceUrl;
|
|
1816
|
+
exports.getGroupAvatarLink = getGroupAvatarLink;
|
|
1817
|
+
exports.getImage = getImage;
|
|
1818
|
+
exports.getImageUrl = getImageUrl;
|
|
1819
|
+
exports.getLogFilePath = getLogFilePath;
|
|
1820
|
+
exports.getMentionedImage = getMentionedImage;
|
|
1821
|
+
exports.getMentionedImageUrl = getMentionedImageUrl;
|
|
1822
|
+
exports.getMentionedUserId = getMentionedUserId;
|
|
1823
|
+
exports.getMinicoTokenViaAuthCode = getMinicoTokenViaAuthCode;
|
|
1824
|
+
exports.getMiokiLogger = getMiokiLogger;
|
|
1825
|
+
exports.getQQAvatarLink = getQQAvatarLink;
|
|
1826
|
+
exports.getQuoteImage = getQuoteImage;
|
|
1827
|
+
exports.getQuoteImageUrl = getQuoteImageUrl;
|
|
1828
|
+
exports.getQuoteMessage = getQuoteMessage;
|
|
1829
|
+
exports.getQuoteText = getQuoteText;
|
|
1830
|
+
exports.getTerminalInput = getTerminalInput;
|
|
1831
|
+
exports.getViolationRecords = getViolationRecords;
|
|
1832
|
+
exports.hasRight = hasRight;
|
|
1833
|
+
exports.isAdmin = isAdmin;
|
|
1834
|
+
exports.isBoolean = isBoolean;
|
|
1835
|
+
exports.isDefined = isDefined;
|
|
1836
|
+
exports.isFunction = isFunction;
|
|
1837
|
+
exports.isGroupMsg = isGroupMsg;
|
|
1838
|
+
exports.isInPm2 = isInPm2;
|
|
1839
|
+
exports.isNumber = isNumber;
|
|
1840
|
+
exports.isObject = isObject;
|
|
1841
|
+
exports.isOwner = isOwner;
|
|
1842
|
+
exports.isOwnerOrAdmin = isOwnerOrAdmin;
|
|
1843
|
+
exports.isPrivateMsg = isPrivateMsg;
|
|
1844
|
+
exports.isString = isString;
|
|
1845
|
+
exports.jiti = jiti$1;
|
|
1846
|
+
exports.localNum = localNum;
|
|
1847
|
+
exports.localeDate = localeDate;
|
|
1848
|
+
exports.localeTime = localeTime;
|
|
1849
|
+
exports.match = match;
|
|
1850
|
+
exports.md5 = md5;
|
|
1851
|
+
Object.defineProperty(exports, 'mri', {
|
|
1852
|
+
enumerable: true,
|
|
1853
|
+
get: function () {
|
|
1854
|
+
return mri.default;
|
|
1855
|
+
}
|
|
1856
|
+
});
|
|
1857
|
+
exports.noNullish = noNullish;
|
|
1858
|
+
exports.noticeAdmins = noticeAdmins;
|
|
1859
|
+
exports.noticeFriends = noticeFriends;
|
|
1860
|
+
exports.noticeGroups = noticeGroups;
|
|
1861
|
+
exports.noticeMainOwner = noticeMainOwner;
|
|
1862
|
+
exports.noticeOwners = noticeOwners;
|
|
1863
|
+
Object.defineProperty(exports, 'path', {
|
|
1864
|
+
enumerable: true,
|
|
1865
|
+
get: function () {
|
|
1866
|
+
return node_path.default;
|
|
1867
|
+
}
|
|
1868
|
+
});
|
|
1869
|
+
Object.defineProperty(exports, 'prettyMs', {
|
|
1870
|
+
enumerable: true,
|
|
1871
|
+
get: function () {
|
|
1872
|
+
return pretty_ms.default;
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
exports.qs = qs;
|
|
1876
|
+
exports.queryDevToolsLoginStatus = queryDevToolsLoginStatus;
|
|
1877
|
+
exports.randomId = randomId;
|
|
1878
|
+
exports.randomInt = randomInt;
|
|
1879
|
+
exports.randomItem = randomItem;
|
|
1880
|
+
exports.randomItems = randomItems;
|
|
1881
|
+
exports.readMiokiConfig = readMiokiConfig;
|
|
1882
|
+
exports.readPackageJson = readPackageJson;
|
|
1883
|
+
exports.requestLoginViaDevTools = requestLoginViaDevTools;
|
|
1884
|
+
exports.runWithErrorHandler = runWithErrorHandler;
|
|
1885
|
+
exports.runWithReaction = runWithReaction;
|
|
1886
|
+
exports.runtimePlugins = runtimePlugins;
|
|
1887
|
+
exports.services = services;
|
|
1888
|
+
exports.signArk = signArk;
|
|
6
1889
|
exports.start = start;
|
|
1890
|
+
Object.defineProperty(exports, 'string2argv', {
|
|
1891
|
+
enumerable: true,
|
|
1892
|
+
get: function () {
|
|
1893
|
+
return string2argv.string2argv;
|
|
1894
|
+
}
|
|
1895
|
+
});
|
|
1896
|
+
exports.stringifyError = stringifyError;
|
|
1897
|
+
Object.defineProperty(exports, 'systemInfo', {
|
|
1898
|
+
enumerable: true,
|
|
1899
|
+
get: function () {
|
|
1900
|
+
return systeminformation.default;
|
|
1901
|
+
}
|
|
1902
|
+
});
|
|
1903
|
+
exports.text = text;
|
|
1904
|
+
exports.toArray = toArray;
|
|
1905
|
+
exports.toMsgId = toMsgId;
|
|
1906
|
+
exports.unique = unique;
|
|
1907
|
+
exports.updateBotCWD = updateBotCWD;
|
|
1908
|
+
exports.updateBotConfig = updateBotConfig;
|
|
1909
|
+
exports.uploadImageToCollection = uploadImageToCollection;
|
|
1910
|
+
exports.uploadImageToGroupHomework = uploadImageToGroupHomework;
|
|
1911
|
+
exports.uploadImageToGroupNotice = uploadImageToGroupNotice;
|
|
1912
|
+
exports.uuid = uuid;
|
|
1913
|
+
exports.wait = wait;
|
|
1914
|
+
exports.writePackageJson = writePackageJson;
|
|
7
1915
|
//# sourceMappingURL=index.cjs.map
|