koishi-plugin-starfx-bot 0.27.2 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +5 -2
- package/lib/index.js +1034 -335
- package/lib/plugins/logService.d.ts +6 -0
- package/lib/plugins/recordApi.d.ts +4 -0
- package/lib/plugins/verifyService.d.ts +86 -0
- package/package.json +1 -1
- package/readme.md +44 -2
- package/assets/songRoom.ejs +0 -862
package/lib/index.js
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
5
5
|
var __getProtoOf = Object.getPrototypeOf;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
7
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __commonJS = (cb, mod) => function __require() {
|
|
9
12
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
13
|
};
|
|
@@ -30,106 +33,76 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
33
|
));
|
|
31
34
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
35
|
|
|
33
|
-
// src/locales/zh-CN.yml
|
|
34
|
-
var require_zh_CN = __commonJS({
|
|
35
|
-
"src/locales/zh-CN.yml"(exports2, module2) {
|
|
36
|
-
module2.exports = { commands: { roll: { messages: { noParam: "你问啥?", invalid: "你要不看看你说的是啥", "too-many": "太、太多了啦!", noodles: "扔出了{num}个{noodles}面的骰子,点数如下:{points}", possibility: "{param}是{possibility}%", choose: "当然是{option}啦!" } }, "room-number": { messages: { roomNumber: "当前的房间号是: {room}", changeRoom: "已将当前房间号 {oldRoom} 修改为 {newRoom}", newRoom: "已添加房间号: {room}", noRoom: "当前没有保存的房间", delRoom: "已删除房间: {room}", invalid: "无效输入" } }, forward: { messages: { delete: "已删除转发群聊: {gid}", noTarget: "未配置转发群组", success: "已转发消息到: {target}", noMessage: "请引用要转发的消息", setOK: "已配置转发: {target}", forwardContent: "[转发消息]<br/> {content}" } }, "waiting-play": { messages: { addReady: "已添加,当前待机{num}人: <br/>{list}", delReady: "已删除,当前待机{num}人: <br/>{list}", delFailed: "未删除,当前待机{num}人: <br/>{list}", clearReady: "待机列表已清空", listReady: "当前待机{num}人: <br/>{list}", invalid: "输入无效" } }, 开启车牌检测: { messages: { success: "车牌检测:已打开\n当前车牌位数: {length}", noPermission: "你没有权限开启车牌检测", noGuild: "请在群聊中使用该指令", invalidLength: "位数设定不合理,请输入 1-20 之间的数字" } }, 关闭车牌检测: { messages: { success: "已关闭车牌检测", noPermission: "你没有权限关闭车牌检测", noGuild: "请在群聊中使用该指令" } }, 车牌检测状态: { messages: { enabled: "车牌检测: 已打开\n当前车牌位数: {length}", disabled: "车牌检测: 已关闭", managerEnabled: "管理员权限正常", managerDisabled: "无管理员权限,无法修改群名", noPermission: "你没有权限查看车牌检测状态", noGuild: "请在群聊中使用该指令" } }, 车牌检测权限: { messages: { status: { enabled: "普通群员开关车牌检测: 允许", disabled: "普通群员开关车牌检测: 禁止" }, enabled: "已允许普通群员开关车牌检测", disabled: "已禁止普通群员开关车牌检测", invalid: "无效输入,请输入 开 / 关", noPermission: "你没有权限设置车牌检测权限", noGuild: "请在群聊中使用该指令" } } }, middleware: { messages: { atNotReply: "艾特我又不说话", atNotReplyOther: "艾特他又不说话", notBot: "我才不是机器人!", loveMessage: "我喜欢你", iLoveU: "我也喜欢你", notManager: "当前bot没有管理员权限,无法修改群名", editFuncNotFound: "无法找到设置群名的底层方法", editGroupNameSuccess: "检测到车牌 {content},群名修改成功!", editGroupNameRetrySuccess: '检测到车牌 {content},由于限制已修改为 "{spacedContent}"' } } };
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// src/index.ts
|
|
41
|
-
var src_exports = {};
|
|
42
|
-
__export(src_exports, {
|
|
43
|
-
Config: () => Config,
|
|
44
|
-
apply: () => apply,
|
|
45
|
-
assetsDir: () => assetsDir,
|
|
46
|
-
baseDir: () => baseDir,
|
|
47
|
-
inject: () => inject,
|
|
48
|
-
name: () => name,
|
|
49
|
-
repeatContextMap: () => repeatContextMap,
|
|
50
|
-
starfxLogger: () => starfxLogger,
|
|
51
|
-
usage: () => usage
|
|
52
|
-
});
|
|
53
|
-
module.exports = __toCommonJS(src_exports);
|
|
54
|
-
var fs2 = __toESM(require("node:fs"));
|
|
55
|
-
var import_node_path4 = __toESM(require("node:path"));
|
|
56
|
-
var import_koishi5 = require("koishi");
|
|
57
|
-
var import_mime_types = __toESM(require("mime-types"));
|
|
58
|
-
|
|
59
36
|
// package.json
|
|
60
|
-
var package_default
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
37
|
+
var package_default;
|
|
38
|
+
var init_package = __esm({
|
|
39
|
+
"package.json"() {
|
|
40
|
+
package_default = {
|
|
41
|
+
name: "koishi-plugin-starfx-bot",
|
|
42
|
+
description: "StarFreedomX机器人的小功能,自用",
|
|
43
|
+
contributors: [
|
|
44
|
+
"StarFreedomX <starfreedomx@outlook.com>"
|
|
45
|
+
],
|
|
46
|
+
version: "0.28.0",
|
|
47
|
+
main: "lib/index.js",
|
|
48
|
+
typings: "lib/index.d.ts",
|
|
49
|
+
files: [
|
|
50
|
+
"lib",
|
|
51
|
+
"dist",
|
|
52
|
+
"assets"
|
|
53
|
+
],
|
|
54
|
+
license: "MIT",
|
|
55
|
+
homepage: "https://github.com/StarFreedomX/starfx-bot",
|
|
56
|
+
repository: {
|
|
57
|
+
type: "git",
|
|
58
|
+
url: "git+https://github.com/StarFreedomX/starfx-bot.git"
|
|
59
|
+
},
|
|
60
|
+
keywords: [
|
|
61
|
+
"chatbot",
|
|
62
|
+
"koishi",
|
|
63
|
+
"plugin"
|
|
64
|
+
],
|
|
65
|
+
koishi: {
|
|
66
|
+
description: {
|
|
67
|
+
zh: "starfx的机器人小插件,目前支持: 明日方舟封印功能 闲鱼“卖掉了”功能 艾特我/他又不说话 我才不是机器人! 我也喜欢你! BanG Dream!边框绘制 群语录 roll 待机人数记录 房间号记录 推特查看原图 复读 echo 撤回 查汇率 黑白名单配置"
|
|
68
|
+
},
|
|
69
|
+
service: {
|
|
70
|
+
optional: [
|
|
71
|
+
"skia",
|
|
72
|
+
"QhzySharp"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
preview: true,
|
|
76
|
+
hidden: false
|
|
77
|
+
},
|
|
78
|
+
peerDependencies: {
|
|
79
|
+
koishi: "^4.18.7"
|
|
80
|
+
},
|
|
81
|
+
dependencies: {
|
|
82
|
+
axios: "^1.15.0",
|
|
83
|
+
"chart.js": "^4.5.1",
|
|
84
|
+
"chartjs-adapter-dayjs-3": "^1.2.3",
|
|
85
|
+
cheerio: "^1.0.0",
|
|
86
|
+
dayjs: "^1.11.19",
|
|
87
|
+
"http-proxy-agent": "^7.0.2",
|
|
88
|
+
"https-proxy-agent": "^7.0.6",
|
|
89
|
+
"mime-types": "^3.0.1",
|
|
90
|
+
"rss-parser": "^3.13.0"
|
|
91
|
+
},
|
|
92
|
+
devDependencies: {
|
|
93
|
+
"@biomejs/biome": "2.3.7",
|
|
94
|
+
"@ltxhhz/koishi-plugin-skia-canvas": "^0.0.10",
|
|
95
|
+
"@quanhuzeyu/koishi-plugin-qhzy-sharp": "^1.2.1",
|
|
96
|
+
"@quanhuzeyu/sharp-for-koishi": "^0.0.7",
|
|
97
|
+
"@types/mime-types": "^3.0.1"
|
|
98
|
+
}
|
|
99
|
+
};
|
|
118
100
|
}
|
|
119
|
-
};
|
|
101
|
+
});
|
|
120
102
|
|
|
121
103
|
// src/plugins/currencySearch.ts
|
|
122
|
-
var import_node_path = __toESM(require("node:path"));
|
|
123
|
-
var import_chart = require("chart.js");
|
|
124
|
-
var cheerio = __toESM(require("cheerio"));
|
|
125
|
-
var import_koishi = require("koishi");
|
|
126
|
-
import_chart.Chart.register(...import_chart.registerables);
|
|
127
|
-
var mvpAPIKey;
|
|
128
|
-
var currencyCodes = [];
|
|
129
|
-
var currencyCodesMap = {};
|
|
130
104
|
async function intervalGetExchangeRate(_ctx, _cfg, _session, _searchString, _exchangeRatePath) {
|
|
131
105
|
}
|
|
132
|
-
__name(intervalGetExchangeRate, "intervalGetExchangeRate");
|
|
133
106
|
async function parseNaturalCurrency(searchString) {
|
|
134
107
|
const index = await getCurrencyCodesMap();
|
|
135
108
|
const s = searchString.replace(/\s+/g, "");
|
|
@@ -146,7 +119,6 @@ async function parseNaturalCurrency(searchString) {
|
|
|
146
119
|
}
|
|
147
120
|
return null;
|
|
148
121
|
}
|
|
149
|
-
__name(parseNaturalCurrency, "parseNaturalCurrency");
|
|
150
122
|
async function getExchangeRate(ctx, cfg, session, searchString, raw, retryTimes = 1) {
|
|
151
123
|
const apiKey = retryTimes ? await getMvpAPIKey() : await updateMvpAPIKey();
|
|
152
124
|
try {
|
|
@@ -250,7 +222,6 @@ ${item.name}
|
|
|
250
222
|
}
|
|
251
223
|
}
|
|
252
224
|
}
|
|
253
|
-
__name(getExchangeRate, "getExchangeRate");
|
|
254
225
|
async function mergeBuffersVertical(ctx2, buf1, buf2) {
|
|
255
226
|
const { Canvas, loadImage } = ctx2.skia;
|
|
256
227
|
const img1 = await loadImage(buf1);
|
|
@@ -263,7 +234,6 @@ async function mergeBuffersVertical(ctx2, buf1, buf2) {
|
|
|
263
234
|
ctx.drawImage(img2, 0, img1.height);
|
|
264
235
|
return await canvas.toBuffer("png");
|
|
265
236
|
}
|
|
266
|
-
__name(mergeBuffersVertical, "mergeBuffersVertical");
|
|
267
237
|
async function updateMvpAPIKey() {
|
|
268
238
|
try {
|
|
269
239
|
const htmlResp = await fetch(
|
|
@@ -315,11 +285,9 @@ async function updateMvpAPIKey() {
|
|
|
315
285
|
return null;
|
|
316
286
|
}
|
|
317
287
|
}
|
|
318
|
-
__name(updateMvpAPIKey, "updateMvpAPIKey");
|
|
319
288
|
async function getMvpAPIKey() {
|
|
320
289
|
return mvpAPIKey ? mvpAPIKey : updateMvpAPIKey();
|
|
321
290
|
}
|
|
322
|
-
__name(getMvpAPIKey, "getMvpAPIKey");
|
|
323
291
|
async function getExchangeGuids(symbols, apikey) {
|
|
324
292
|
try {
|
|
325
293
|
const url = `https://assets.msn.cn/service/Finance/IdMap?apikey=${apikey}&MStarIds=${encodeURIComponent(symbols)}`;
|
|
@@ -335,7 +303,6 @@ async function getExchangeGuids(symbols, apikey) {
|
|
|
335
303
|
return [];
|
|
336
304
|
}
|
|
337
305
|
}
|
|
338
|
-
__name(getExchangeGuids, "getExchangeGuids");
|
|
339
306
|
async function getQuotes(ids, apiKey) {
|
|
340
307
|
try {
|
|
341
308
|
const url = `https://assets.msn.com/service/Finance/Quotes?apikey=${encodeURIComponent(apiKey)}&cm=zh-cn&it=edgeid&ids=${encodeURIComponent(ids)}&wrapodata=false`;
|
|
@@ -376,7 +343,6 @@ async function getQuotes(ids, apiKey) {
|
|
|
376
343
|
return [];
|
|
377
344
|
}
|
|
378
345
|
}
|
|
379
|
-
__name(getQuotes, "getQuotes");
|
|
380
346
|
async function getMSNPrices(ids, apiKey, type = "1M1H") {
|
|
381
347
|
const url = `https://assets.msn.com/service/Finance/QuoteSummary?apikey=${apiKey}&ids=${ids}&intents=Charts,Exchanges&type=${type}&wrapodata=false`;
|
|
382
348
|
const res = await fetch(url);
|
|
@@ -398,7 +364,6 @@ async function getMSNPrices(ids, apiKey, type = "1M1H") {
|
|
|
398
364
|
}
|
|
399
365
|
);
|
|
400
366
|
}
|
|
401
|
-
__name(getMSNPrices, "getMSNPrices");
|
|
402
367
|
async function drawCurrencyChartSkia(ctx, prices, timeStamps, title = "兑换汇率", width = 1200, height = 600) {
|
|
403
368
|
if (!prices.length || !timeStamps.length || prices.length !== timeStamps.length)
|
|
404
369
|
return;
|
|
@@ -474,19 +439,16 @@ async function drawCurrencyChartSkia(ctx, prices, timeStamps, title = "兑换汇
|
|
|
474
439
|
});
|
|
475
440
|
return await canvas.toBuffer("png");
|
|
476
441
|
}
|
|
477
|
-
__name(drawCurrencyChartSkia, "drawCurrencyChartSkia");
|
|
478
442
|
async function getCurrencyCodes() {
|
|
479
443
|
if (currencyCodes?.length) return currencyCodes;
|
|
480
444
|
currencyCodes = require(import_node_path.default.resolve(assetsDir, "./currency.json"));
|
|
481
445
|
return currencyCodes;
|
|
482
446
|
}
|
|
483
|
-
__name(getCurrencyCodes, "getCurrencyCodes");
|
|
484
447
|
async function getCurrencyCodesMap() {
|
|
485
448
|
if (Object.keys(currencyCodesMap)?.length) return currencyCodesMap;
|
|
486
449
|
currencyCodesMap = await buildCurrencyIndex();
|
|
487
450
|
return currencyCodesMap;
|
|
488
451
|
}
|
|
489
|
-
__name(getCurrencyCodesMap, "getCurrencyCodesMap");
|
|
490
452
|
async function buildCurrencyIndex() {
|
|
491
453
|
const map = {};
|
|
492
454
|
(await getCurrencyCodes()).forEach((c) => {
|
|
@@ -498,18 +460,34 @@ async function buildCurrencyIndex() {
|
|
|
498
460
|
map.港币 = "HKD";
|
|
499
461
|
return map;
|
|
500
462
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
463
|
+
var import_node_path, import_chart, cheerio, import_koishi, mvpAPIKey, currencyCodes, currencyCodesMap;
|
|
464
|
+
var init_currencySearch = __esm({
|
|
465
|
+
"src/plugins/currencySearch.ts"() {
|
|
466
|
+
import_node_path = __toESM(require("node:path"));
|
|
467
|
+
import_chart = require("chart.js");
|
|
468
|
+
cheerio = __toESM(require("cheerio"));
|
|
469
|
+
import_koishi = require("koishi");
|
|
470
|
+
init_src();
|
|
471
|
+
import_chart.Chart.register(...import_chart.registerables);
|
|
472
|
+
currencyCodes = [];
|
|
473
|
+
currencyCodesMap = {};
|
|
474
|
+
__name(intervalGetExchangeRate, "intervalGetExchangeRate");
|
|
475
|
+
__name(parseNaturalCurrency, "parseNaturalCurrency");
|
|
476
|
+
__name(getExchangeRate, "getExchangeRate");
|
|
477
|
+
__name(mergeBuffersVertical, "mergeBuffersVertical");
|
|
478
|
+
__name(updateMvpAPIKey, "updateMvpAPIKey");
|
|
479
|
+
__name(getMvpAPIKey, "getMvpAPIKey");
|
|
480
|
+
__name(getExchangeGuids, "getExchangeGuids");
|
|
481
|
+
__name(getQuotes, "getQuotes");
|
|
482
|
+
__name(getMSNPrices, "getMSNPrices");
|
|
483
|
+
__name(drawCurrencyChartSkia, "drawCurrencyChartSkia");
|
|
484
|
+
__name(getCurrencyCodes, "getCurrencyCodes");
|
|
485
|
+
__name(getCurrencyCodesMap, "getCurrencyCodesMap");
|
|
486
|
+
__name(buildCurrencyIndex, "buildCurrencyIndex");
|
|
487
|
+
}
|
|
488
|
+
});
|
|
507
489
|
|
|
508
490
|
// src/utils.ts
|
|
509
|
-
var import_node_fs = __toESM(require("node:fs"));
|
|
510
|
-
var import_node_path2 = __toESM(require("node:path"));
|
|
511
|
-
var import_koishi2 = require("koishi");
|
|
512
|
-
var import_chartjs_adapter_dayjs_3 = require("chartjs-adapter-dayjs-3");
|
|
513
491
|
async function addRecord(ctx, gid, avatarUrl) {
|
|
514
492
|
const recordDir = `${assetsDir}/record/${gid}`;
|
|
515
493
|
const avatarBuffer = await ctx.http.get(avatarUrl, {
|
|
@@ -518,7 +496,6 @@ async function addRecord(ctx, gid, avatarUrl) {
|
|
|
518
496
|
saveImage(avatarBuffer, recordDir);
|
|
519
497
|
return "投稿收到啦";
|
|
520
498
|
}
|
|
521
|
-
__name(addRecord, "addRecord");
|
|
522
499
|
async function getRecord(cfg, gid, tag) {
|
|
523
500
|
const links = structuredClone(cfg.recordLink);
|
|
524
501
|
links[gid] = { linkGroup: gid, linkWeight: 100 };
|
|
@@ -545,7 +522,6 @@ async function getRecord(cfg, gid, tag) {
|
|
|
545
522
|
}
|
|
546
523
|
return null;
|
|
547
524
|
}
|
|
548
|
-
__name(getRecord, "getRecord");
|
|
549
525
|
function getRandomLinkGroup(record) {
|
|
550
526
|
const entries = Object.values(record);
|
|
551
527
|
const totalWeight = entries.reduce((sum, item) => sum + item.linkWeight, 0);
|
|
@@ -558,7 +534,6 @@ function getRandomLinkGroup(record) {
|
|
|
558
534
|
}
|
|
559
535
|
return entries[entries.length - 1].linkGroup;
|
|
560
536
|
}
|
|
561
|
-
__name(getRandomLinkGroup, "getRandomLinkGroup");
|
|
562
537
|
function getTodayPrefix() {
|
|
563
538
|
const now = /* @__PURE__ */ new Date();
|
|
564
539
|
const yyyy = now.getFullYear();
|
|
@@ -566,7 +541,6 @@ function getTodayPrefix() {
|
|
|
566
541
|
const dd = String(now.getDate()).padStart(2, "0");
|
|
567
542
|
return `${yyyy}${mm}${dd}`;
|
|
568
543
|
}
|
|
569
|
-
__name(getTodayPrefix, "getTodayPrefix");
|
|
570
544
|
function getNextSequenceNumber(directory, prefix, suffix = "jpg") {
|
|
571
545
|
const files = import_node_fs.default.existsSync(directory) ? import_node_fs.default.readdirSync(directory) : [];
|
|
572
546
|
const regex = new RegExp(`^${prefix}-(\\d+)\\.${suffix}$`);
|
|
@@ -582,7 +556,6 @@ function getNextSequenceNumber(directory, prefix, suffix = "jpg") {
|
|
|
582
556
|
});
|
|
583
557
|
return maxNum + 1;
|
|
584
558
|
}
|
|
585
|
-
__name(getNextSequenceNumber, "getNextSequenceNumber");
|
|
586
559
|
function saveImage(arrayBuffer, directory, filename) {
|
|
587
560
|
if (!filename) {
|
|
588
561
|
const prefix = getTodayPrefix();
|
|
@@ -597,37 +570,58 @@ function saveImage(arrayBuffer, directory, filename) {
|
|
|
597
570
|
import_node_fs.default.writeFileSync(filepath, buffer);
|
|
598
571
|
console.log(`已保存图片:${filepath}`);
|
|
599
572
|
}
|
|
600
|
-
__name(saveImage, "saveImage");
|
|
601
573
|
async function getImageSrc(session, param, option) {
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
574
|
+
const {
|
|
575
|
+
number = true,
|
|
576
|
+
img = true,
|
|
577
|
+
at = true,
|
|
578
|
+
noParam = true,
|
|
579
|
+
quote = true
|
|
580
|
+
} = option ?? {};
|
|
581
|
+
if (number && /^\d+$/.test(param)) {
|
|
582
|
+
const avatar = await getHeadUrlById(session, param);
|
|
583
|
+
if (avatar) return avatar;
|
|
584
|
+
}
|
|
585
|
+
if (noParam && !param?.length) {
|
|
586
|
+
const avatar = await getHeadUrlById(session, session.userId);
|
|
587
|
+
if (avatar) return avatar;
|
|
588
|
+
}
|
|
589
|
+
const findSrcInElements = /* @__PURE__ */ __name(async (elements, skipSelfAt = false) => {
|
|
590
|
+
if (!elements?.length) return null;
|
|
591
|
+
let list = elements;
|
|
592
|
+
if (skipSelfAt) {
|
|
593
|
+
const startIdx = list.findIndex(
|
|
594
|
+
(el) => !(el?.type === "at" && el?.attrs?.id === session.selfId)
|
|
595
|
+
);
|
|
596
|
+
list = startIdx === -1 ? [] : list.slice(startIdx);
|
|
597
|
+
}
|
|
598
|
+
for (const el of list) {
|
|
599
|
+
if (img && el?.type === "img" && el?.attrs?.src) {
|
|
600
|
+
return el.attrs.src;
|
|
601
|
+
}
|
|
602
|
+
if (at && el?.type === "at" && el?.attrs?.id && el.attrs.id !== session.selfId) {
|
|
603
|
+
const avatar = await getHeadUrlById(session, el.attrs.id);
|
|
604
|
+
if (avatar) return avatar;
|
|
617
605
|
}
|
|
618
606
|
}
|
|
607
|
+
return null;
|
|
608
|
+
}, "findSrcInElements");
|
|
609
|
+
if (quote) {
|
|
610
|
+
const avatar = await findSrcInElements(session?.quote?.elements);
|
|
611
|
+
if (avatar) return avatar;
|
|
619
612
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
return `https://q1.qlogo.cn/g?b=qq&nk=${
|
|
626
|
-
|
|
613
|
+
return await findSrcInElements(session?.elements, true) ?? "";
|
|
614
|
+
}
|
|
615
|
+
async function getHeadUrlById(session, id) {
|
|
616
|
+
switch (session.platform) {
|
|
617
|
+
case "onebot":
|
|
618
|
+
return `https://q1.qlogo.cn/g?b=qq&nk=${id}&s=640`;
|
|
619
|
+
case "qq":
|
|
620
|
+
return `https://thirdqq.qlogo.cn/qqapp/${session.selfId}/${id}/640`;
|
|
621
|
+
default:
|
|
622
|
+
return (await session.bot.getUser(id)).avatar || "";
|
|
627
623
|
}
|
|
628
|
-
return "";
|
|
629
624
|
}
|
|
630
|
-
__name(getImageSrc, "getImageSrc");
|
|
631
625
|
async function getImageFromUrl(ctx, url) {
|
|
632
626
|
if (!url) throw new Error("URL must be provided");
|
|
633
627
|
const sharp = getSharpConstructor(ctx);
|
|
@@ -647,7 +641,6 @@ async function getImageFromUrl(ctx, url) {
|
|
|
647
641
|
throw new Error("Get image failed");
|
|
648
642
|
}
|
|
649
643
|
}
|
|
650
|
-
__name(getImageFromUrl, "getImageFromUrl");
|
|
651
644
|
async function atNotSayReply(cfg, session, elements) {
|
|
652
645
|
const trimElements = elements.filter(
|
|
653
646
|
(e) => !(e.type === "text" && /^\s*$/.test(e.attrs.content))
|
|
@@ -661,7 +654,6 @@ async function atNotSayReply(cfg, session, elements) {
|
|
|
661
654
|
}
|
|
662
655
|
}
|
|
663
656
|
}
|
|
664
|
-
__name(atNotSayReply, "atNotSayReply");
|
|
665
657
|
async function replyBot(cfg, session, elements) {
|
|
666
658
|
if (cfg.replyBot !== "关闭") {
|
|
667
659
|
const bots = ["bot", "机器人", "Bot", "BOT", "机器人!", "机器人!", "人机"];
|
|
@@ -674,7 +666,6 @@ async function replyBot(cfg, session, elements) {
|
|
|
674
666
|
}
|
|
675
667
|
}
|
|
676
668
|
}
|
|
677
|
-
__name(replyBot, "replyBot");
|
|
678
669
|
async function iLoveYou(cfg, session, elements) {
|
|
679
670
|
if (cfg.iLoveYou) {
|
|
680
671
|
const hasAtMe = elements?.some(
|
|
@@ -693,7 +684,6 @@ async function iLoveYou(cfg, session, elements) {
|
|
|
693
684
|
}
|
|
694
685
|
}
|
|
695
686
|
}
|
|
696
|
-
__name(iLoveYou, "iLoveYou");
|
|
697
687
|
function parseFeatureControl(array) {
|
|
698
688
|
try {
|
|
699
689
|
return Object.fromEntries(
|
|
@@ -712,7 +702,6 @@ function parseFeatureControl(array) {
|
|
|
712
702
|
return {};
|
|
713
703
|
}
|
|
714
704
|
}
|
|
715
|
-
__name(parseFeatureControl, "parseFeatureControl");
|
|
716
705
|
function detectControl(controlJson, guildId, funName) {
|
|
717
706
|
const rule = controlJson?.[funName];
|
|
718
707
|
if (!rule || rule.whitelist === void 0 || !Array.isArray(rule.groups)) {
|
|
@@ -721,7 +710,6 @@ function detectControl(controlJson, guildId, funName) {
|
|
|
721
710
|
const inList = rule.groups.includes(Number(guildId));
|
|
722
711
|
return rule.whitelist ? inList : !inList;
|
|
723
712
|
}
|
|
724
|
-
__name(detectControl, "detectControl");
|
|
725
713
|
function handleRoll(session) {
|
|
726
714
|
const elements = session.elements;
|
|
727
715
|
const parts = [];
|
|
@@ -767,7 +755,6 @@ function handleRoll(session) {
|
|
|
767
755
|
}
|
|
768
756
|
return session.text(".noParam");
|
|
769
757
|
}
|
|
770
|
-
__name(handleRoll, "handleRoll");
|
|
771
758
|
function getPoints(session, num, noodles) {
|
|
772
759
|
if (!Number.isInteger(num) || !Number.isInteger(noodles) || num <= 0 || noodles <= 0)
|
|
773
760
|
return session.text(".invalid");
|
|
@@ -779,7 +766,6 @@ function getPoints(session, num, noodles) {
|
|
|
779
766
|
points: points.join(", ")
|
|
780
767
|
});
|
|
781
768
|
}
|
|
782
|
-
__name(getPoints, "getPoints");
|
|
783
769
|
async function undo(_cfg, session) {
|
|
784
770
|
if (session?.quote?.id && session.quote.user.id === session.selfId && Date.now() - session.quote.timestamp < 2 * 60 * 1e3 - 5 * 1e3) {
|
|
785
771
|
await session.bot.deleteMessage(
|
|
@@ -788,7 +774,6 @@ async function undo(_cfg, session) {
|
|
|
788
774
|
);
|
|
789
775
|
}
|
|
790
776
|
}
|
|
791
|
-
__name(undo, "undo");
|
|
792
777
|
function safeQuote(str, useQuote) {
|
|
793
778
|
let unquoted = str.trim();
|
|
794
779
|
if (unquoted.startsWith('"')) {
|
|
@@ -803,7 +788,6 @@ function safeQuote(str, useQuote) {
|
|
|
803
788
|
return unquoted;
|
|
804
789
|
}
|
|
805
790
|
}
|
|
806
|
-
__name(safeQuote, "safeQuote");
|
|
807
791
|
function writeMap(map, dest) {
|
|
808
792
|
const dir = import_node_path2.default.dirname(dest);
|
|
809
793
|
if (!import_node_fs.default.existsSync(dir)) {
|
|
@@ -811,7 +795,6 @@ function writeMap(map, dest) {
|
|
|
811
795
|
}
|
|
812
796
|
import_node_fs.default.writeFileSync(dest, JSON.stringify([...map], null, 2), "utf-8");
|
|
813
797
|
}
|
|
814
|
-
__name(writeMap, "writeMap");
|
|
815
798
|
function readMap(url) {
|
|
816
799
|
if (!import_node_fs.default.existsSync(url)) {
|
|
817
800
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -821,7 +804,6 @@ function readMap(url) {
|
|
|
821
804
|
const mapArray = JSON.parse(raw);
|
|
822
805
|
return new Map(mapArray);
|
|
823
806
|
}
|
|
824
|
-
__name(readMap, "readMap");
|
|
825
807
|
function ready(session, cfg, param, readyMap) {
|
|
826
808
|
const nowReadyMap = cfg.saveReadyAsFile ? readMap(cfg.saveReadyAsFile) : readyMap;
|
|
827
809
|
let strArr = nowReadyMap.get(session.gid) ?? [];
|
|
@@ -855,17 +837,17 @@ function ready(session, cfg, param, readyMap) {
|
|
|
855
837
|
writeMap(nowReadyMap, cfg.saveReadyAsFile);
|
|
856
838
|
return returnMessage;
|
|
857
839
|
}
|
|
858
|
-
__name(ready, "ready");
|
|
859
840
|
function getSharpConstructor(ctx) {
|
|
860
841
|
const raw = ctx.QhzySharp?.Sharp;
|
|
861
842
|
if (!raw) throw new Error("Sharp Service 尚未就绪");
|
|
862
843
|
const sharpConstructor = typeof raw === "function" ? raw : raw.default;
|
|
863
844
|
if (typeof sharpConstructor !== "function") {
|
|
864
|
-
throw new Error(
|
|
845
|
+
throw new Error(
|
|
846
|
+
"无法获取 Sharp 构造函数,请尝试重启 Koishi 或检查 w-node 状态"
|
|
847
|
+
);
|
|
865
848
|
}
|
|
866
849
|
return sharpConstructor;
|
|
867
850
|
}
|
|
868
|
-
__name(getSharpConstructor, "getSharpConstructor");
|
|
869
851
|
async function getAuthority(session) {
|
|
870
852
|
const user = await session.observeUser(["authority"]);
|
|
871
853
|
const authority = user?.authority || 0;
|
|
@@ -874,22 +856,59 @@ async function getAuthority(session) {
|
|
|
874
856
|
);
|
|
875
857
|
return { authority, roles };
|
|
876
858
|
}
|
|
877
|
-
__name(getAuthority, "getAuthority");
|
|
878
859
|
async function canBotManage(session) {
|
|
879
860
|
try {
|
|
880
|
-
const selfMember = await session.bot.getGuildMember(
|
|
881
|
-
|
|
861
|
+
const selfMember = await session.bot.getGuildMember(
|
|
862
|
+
session.guildId,
|
|
863
|
+
session.selfId
|
|
864
|
+
);
|
|
865
|
+
const roles = (selfMember.roles || []).map(
|
|
866
|
+
(r) => typeof r === "string" ? r : r.id
|
|
867
|
+
);
|
|
882
868
|
return roles.includes("admin") || roles.includes("owner");
|
|
883
869
|
} catch (e) {
|
|
884
870
|
return false;
|
|
885
871
|
}
|
|
886
872
|
}
|
|
887
|
-
__name(canBotManage, "canBotManage");
|
|
888
873
|
async function canGrant(session) {
|
|
889
874
|
const { authority, roles } = await getAuthority(session);
|
|
890
875
|
return authority > 1 || roles.includes("admin") || roles.includes("owner");
|
|
891
876
|
}
|
|
892
|
-
|
|
877
|
+
var import_node_fs, import_node_path2, import_koishi2, import_chartjs_adapter_dayjs_3;
|
|
878
|
+
var init_utils = __esm({
|
|
879
|
+
"src/utils.ts"() {
|
|
880
|
+
import_node_fs = __toESM(require("node:fs"));
|
|
881
|
+
import_node_path2 = __toESM(require("node:path"));
|
|
882
|
+
import_koishi2 = require("koishi");
|
|
883
|
+
init_src();
|
|
884
|
+
import_chartjs_adapter_dayjs_3 = require("chartjs-adapter-dayjs-3");
|
|
885
|
+
__name(addRecord, "addRecord");
|
|
886
|
+
__name(getRecord, "getRecord");
|
|
887
|
+
__name(getRandomLinkGroup, "getRandomLinkGroup");
|
|
888
|
+
__name(getTodayPrefix, "getTodayPrefix");
|
|
889
|
+
__name(getNextSequenceNumber, "getNextSequenceNumber");
|
|
890
|
+
__name(saveImage, "saveImage");
|
|
891
|
+
__name(getImageSrc, "getImageSrc");
|
|
892
|
+
__name(getHeadUrlById, "getHeadUrlById");
|
|
893
|
+
__name(getImageFromUrl, "getImageFromUrl");
|
|
894
|
+
__name(atNotSayReply, "atNotSayReply");
|
|
895
|
+
__name(replyBot, "replyBot");
|
|
896
|
+
__name(iLoveYou, "iLoveYou");
|
|
897
|
+
__name(parseFeatureControl, "parseFeatureControl");
|
|
898
|
+
__name(detectControl, "detectControl");
|
|
899
|
+
__name(handleRoll, "handleRoll");
|
|
900
|
+
__name(getPoints, "getPoints");
|
|
901
|
+
__name(undo, "undo");
|
|
902
|
+
__name(safeQuote, "safeQuote");
|
|
903
|
+
__name(writeMap, "writeMap");
|
|
904
|
+
__name(readMap, "readMap");
|
|
905
|
+
__name(ready, "ready");
|
|
906
|
+
__name(getSharpConstructor, "getSharpConstructor");
|
|
907
|
+
__name(getAuthority, "getAuthority");
|
|
908
|
+
__name(canBotManage, "canBotManage");
|
|
909
|
+
__name(canGrant, "canGrant");
|
|
910
|
+
}
|
|
911
|
+
});
|
|
893
912
|
|
|
894
913
|
// src/plugins/drawHead.ts
|
|
895
914
|
async function handleBanGDreamConfig(options) {
|
|
@@ -1017,18 +1036,17 @@ async function handleBanGDreamConfig(options) {
|
|
|
1017
1036
|
}
|
|
1018
1037
|
return drawConfig;
|
|
1019
1038
|
}
|
|
1020
|
-
__name(handleBanGDreamConfig, "handleBanGDreamConfig");
|
|
1021
1039
|
async function drawLock(ctx, baseImage) {
|
|
1022
1040
|
let image;
|
|
1023
1041
|
try {
|
|
1024
1042
|
image = await getImageFromUrl(ctx, baseImage);
|
|
1025
|
-
} catch (
|
|
1026
|
-
if (
|
|
1043
|
+
} catch (error2) {
|
|
1044
|
+
if (error2.message === "Get image failed") {
|
|
1027
1045
|
return "发生错误";
|
|
1028
|
-
} else if (
|
|
1046
|
+
} else if (error2.message === "URL must be provided") {
|
|
1029
1047
|
return "输入无效";
|
|
1030
1048
|
} else {
|
|
1031
|
-
console.error(
|
|
1049
|
+
console.error(error2.message);
|
|
1032
1050
|
}
|
|
1033
1051
|
}
|
|
1034
1052
|
const sharp = getSharpConstructor(ctx);
|
|
@@ -1041,18 +1059,17 @@ async function drawLock(ctx, baseImage) {
|
|
|
1041
1059
|
image.composite([{ input: await overlay.toBuffer() }]);
|
|
1042
1060
|
return import_koishi3.h.image(await image.png().toBuffer(), "image/png");
|
|
1043
1061
|
}
|
|
1044
|
-
__name(drawLock, "drawLock");
|
|
1045
1062
|
async function drawSold(ctx, baseImage) {
|
|
1046
1063
|
let image;
|
|
1047
1064
|
try {
|
|
1048
1065
|
image = await getImageFromUrl(ctx, baseImage);
|
|
1049
|
-
} catch (
|
|
1050
|
-
if (
|
|
1066
|
+
} catch (error2) {
|
|
1067
|
+
if (error2.message === "Get image failed") {
|
|
1051
1068
|
return "发生错误";
|
|
1052
|
-
} else if (
|
|
1069
|
+
} else if (error2.message === "URL must be provided") {
|
|
1053
1070
|
return "输入无效";
|
|
1054
1071
|
} else {
|
|
1055
|
-
console.error(
|
|
1072
|
+
console.error(error2.message);
|
|
1056
1073
|
}
|
|
1057
1074
|
}
|
|
1058
1075
|
const sharp = getSharpConstructor(ctx);
|
|
@@ -1085,7 +1102,6 @@ async function drawSold(ctx, baseImage) {
|
|
|
1085
1102
|
]);
|
|
1086
1103
|
return import_koishi3.h.image(await image.png().toBuffer(), "image/png");
|
|
1087
1104
|
}
|
|
1088
|
-
__name(drawSold, "drawSold");
|
|
1089
1105
|
async function drawBanGDream(ctx, avatar, inputOptions) {
|
|
1090
1106
|
if (!avatar) return "";
|
|
1091
1107
|
const colors = ["cool", "pure", "happy", "powerful"];
|
|
@@ -1166,12 +1182,22 @@ async function drawBanGDream(ctx, avatar, inputOptions) {
|
|
|
1166
1182
|
return "";
|
|
1167
1183
|
}
|
|
1168
1184
|
}
|
|
1169
|
-
|
|
1185
|
+
var fsp, import_node_path3, import_koishi3;
|
|
1186
|
+
var init_drawHead = __esm({
|
|
1187
|
+
"src/plugins/drawHead.ts"() {
|
|
1188
|
+
fsp = __toESM(require("node:fs/promises"));
|
|
1189
|
+
import_node_path3 = __toESM(require("node:path"));
|
|
1190
|
+
import_koishi3 = require("koishi");
|
|
1191
|
+
init_src();
|
|
1192
|
+
init_utils();
|
|
1193
|
+
__name(handleBanGDreamConfig, "handleBanGDreamConfig");
|
|
1194
|
+
__name(drawLock, "drawLock");
|
|
1195
|
+
__name(drawSold, "drawSold");
|
|
1196
|
+
__name(drawBanGDream, "drawBanGDream");
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1170
1199
|
|
|
1171
1200
|
// src/plugins/getOriginImg.ts
|
|
1172
|
-
var cheerio2 = __toESM(require("cheerio"));
|
|
1173
|
-
var import_koishi4 = require("koishi");
|
|
1174
|
-
var import_rss_parser = __toESM(require("rss-parser"));
|
|
1175
1201
|
async function getXUrl(urls) {
|
|
1176
1202
|
const regex = /https:\/\/x\.com\/([^/]+)\/status\/(\d+)/g;
|
|
1177
1203
|
let match;
|
|
@@ -1187,16 +1213,9 @@ async function getXUrl(urls) {
|
|
|
1187
1213
|
} while (match !== null);
|
|
1188
1214
|
return results;
|
|
1189
1215
|
}
|
|
1190
|
-
__name(getXUrl, "getXUrl");
|
|
1191
|
-
var parser = new import_rss_parser.default({
|
|
1192
|
-
customFields: {
|
|
1193
|
-
item: ["description", "link"]
|
|
1194
|
-
}
|
|
1195
|
-
});
|
|
1196
1216
|
async function getXNum(session) {
|
|
1197
1217
|
return session.content.trim().split(" ").slice(1).filter((item) => !Number.isNaN(+item) && item).map((str) => Number(str) - 1);
|
|
1198
1218
|
}
|
|
1199
|
-
__name(getXNum, "getXNum");
|
|
1200
1219
|
async function getXImage(rssUrl, xUrls) {
|
|
1201
1220
|
const xUrlsArray = Array.isArray(xUrls) ? xUrls : [xUrls];
|
|
1202
1221
|
const feed = await parser.parseURL(rssUrl);
|
|
@@ -1213,7 +1232,6 @@ async function getXImage(rssUrl, xUrls) {
|
|
|
1213
1232
|
}
|
|
1214
1233
|
return allImageUrls;
|
|
1215
1234
|
}
|
|
1216
|
-
__name(getXImage, "getXImage");
|
|
1217
1235
|
function chunk(arr, size) {
|
|
1218
1236
|
const res = [];
|
|
1219
1237
|
for (let i = 0; i < arr.length; i += size) {
|
|
@@ -1221,7 +1239,6 @@ function chunk(arr, size) {
|
|
|
1221
1239
|
}
|
|
1222
1240
|
return res;
|
|
1223
1241
|
}
|
|
1224
|
-
__name(chunk, "chunk");
|
|
1225
1242
|
async function sendImages(ctx, session, imageUrls) {
|
|
1226
1243
|
const chunks = chunk(imageUrls, 10);
|
|
1227
1244
|
for (const group of chunks) {
|
|
@@ -1238,121 +1255,640 @@ async function sendImages(ctx, session, imageUrls) {
|
|
|
1238
1255
|
}
|
|
1239
1256
|
}
|
|
1240
1257
|
}
|
|
1241
|
-
__name(sendImages, "sendImages");
|
|
1242
1258
|
async function getXImageBase64(ctx, url) {
|
|
1243
1259
|
const res = await ctx.http.get(url, { responseType: "arraybuffer" });
|
|
1244
1260
|
const base64 = Buffer.from(res).toString("base64");
|
|
1245
1261
|
return `data:image/png;base64,${base64}`;
|
|
1246
1262
|
}
|
|
1247
|
-
|
|
1263
|
+
var cheerio2, import_koishi4, import_rss_parser, parser;
|
|
1264
|
+
var init_getOriginImg = __esm({
|
|
1265
|
+
"src/plugins/getOriginImg.ts"() {
|
|
1266
|
+
cheerio2 = __toESM(require("cheerio"));
|
|
1267
|
+
import_koishi4 = require("koishi");
|
|
1268
|
+
import_rss_parser = __toESM(require("rss-parser"));
|
|
1269
|
+
__name(getXUrl, "getXUrl");
|
|
1270
|
+
parser = new import_rss_parser.default({
|
|
1271
|
+
customFields: {
|
|
1272
|
+
item: ["description", "link"]
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
__name(getXNum, "getXNum");
|
|
1276
|
+
__name(getXImage, "getXImage");
|
|
1277
|
+
__name(chunk, "chunk");
|
|
1278
|
+
__name(sendImages, "sendImages");
|
|
1279
|
+
__name(getXImageBase64, "getXImageBase64");
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
// src/locales/zh-CN.yml
|
|
1284
|
+
var require_zh_CN = __commonJS({
|
|
1285
|
+
"src/locales/zh-CN.yml"(exports2, module2) {
|
|
1286
|
+
module2.exports = { commands: { roll: { messages: { noParam: "你问啥?", invalid: "你要不看看你说的是啥", "too-many": "太、太多了啦!", noodles: "扔出了{num}个{noodles}面的骰子,点数如下:{points}", possibility: "{param}是{possibility}%", choose: "当然是{option}啦!" } }, "room-number": { messages: { roomNumber: "当前的房间号是: {room}", changeRoom: "已将当前房间号 {oldRoom} 修改为 {newRoom}", newRoom: "已添加房间号: {room}", noRoom: "当前没有保存的房间", delRoom: "已删除房间: {room}", invalid: "无效输入" } }, forward: { messages: { delete: "已删除转发群聊: {gid}", noTarget: "未配置转发群组", success: "已转发消息到: {target}", noMessage: "请引用要转发的消息", setOK: "已配置转发: {target}", forwardContent: "[转发消息]<br/> {content}" } }, "waiting-play": { messages: { addReady: "已添加,当前待机{num}人: <br/>{list}", delReady: "已删除,当前待机{num}人: <br/>{list}", delFailed: "未删除,当前待机{num}人: <br/>{list}", clearReady: "待机列表已清空", listReady: "当前待机{num}人: <br/>{list}", invalid: "输入无效" } }, 开启车牌检测: { messages: { success: "车牌检测:已打开\n当前车牌位数: {length}", noPermission: "你没有权限开启车牌检测", noGuild: "请在群聊中使用该指令", invalidLength: "位数设定不合理,请输入 1-20 之间的数字" } }, 关闭车牌检测: { messages: { success: "已关闭车牌检测", noPermission: "你没有权限关闭车牌检测", noGuild: "请在群聊中使用该指令" } }, 车牌检测状态: { messages: { enabled: "车牌检测: 已打开\n当前车牌位数: {length}", disabled: "车牌检测: 已关闭", managerEnabled: "管理员权限正常", managerDisabled: "无管理员权限,无法修改群名", noPermission: "你没有权限查看车牌检测状态", noGuild: "请在群聊中使用该指令" } }, 车牌检测权限: { messages: { status: { enabled: "普通群员开关车牌检测: 允许", disabled: "普通群员开关车牌检测: 禁止" }, enabled: "已允许普通群员开关车牌检测", disabled: "已禁止普通群员开关车牌检测", invalid: "无效输入,请输入 开 / 关", noPermission: "你没有权限设置车牌检测权限", noGuild: "请在群聊中使用该指令" } } }, middleware: { messages: { atNotReply: "艾特我又不说话", atNotReplyOther: "艾特他又不说话", notBot: "我才不是机器人!", loveMessage: "我喜欢你", iLoveU: "我也喜欢你", notManager: "当前bot没有管理员权限,无法修改群名", editFuncNotFound: "无法找到设置群名的底层方法", editGroupNameSuccess: "已修改群名: {content}", editGroupNameRetrySuccess: '修改群名: "{content}"失败,由于限制已修改为: "{spacedContent}"' } } };
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
// src/plugins/logService.ts
|
|
1291
|
+
var logService_exports = {};
|
|
1292
|
+
__export(logService_exports, {
|
|
1293
|
+
error: () => error,
|
|
1294
|
+
info: () => info,
|
|
1295
|
+
initLog: () => initLog,
|
|
1296
|
+
readRecent: () => readRecent,
|
|
1297
|
+
warn: () => warn
|
|
1298
|
+
});
|
|
1299
|
+
function ensureDir() {
|
|
1300
|
+
if (!import_node_fs2.default.existsSync(logDir)) {
|
|
1301
|
+
import_node_fs2.default.mkdirSync(logDir, { recursive: true });
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
function today() {
|
|
1305
|
+
const d = /* @__PURE__ */ new Date();
|
|
1306
|
+
const y = d.getFullYear();
|
|
1307
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
1308
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
1309
|
+
return `${y}-${m}-${day}`;
|
|
1310
|
+
}
|
|
1311
|
+
function timestamp() {
|
|
1312
|
+
const d = /* @__PURE__ */ new Date();
|
|
1313
|
+
const h6 = String(d.getHours()).padStart(2, "0");
|
|
1314
|
+
const m = String(d.getMinutes()).padStart(2, "0");
|
|
1315
|
+
const s = String(d.getSeconds()).padStart(2, "0");
|
|
1316
|
+
return `${today()} ${h6}:${m}:${s}`;
|
|
1317
|
+
}
|
|
1318
|
+
function initLog(baseDir2) {
|
|
1319
|
+
logDir = import_node_path4.default.join(baseDir2, "data/starfx-bot/logs");
|
|
1320
|
+
ensureDir();
|
|
1321
|
+
todayFile = import_node_path4.default.join(logDir, `record-${today()}.log`);
|
|
1322
|
+
ensureFile();
|
|
1323
|
+
}
|
|
1324
|
+
function ensureFile() {
|
|
1325
|
+
const f = import_node_path4.default.join(logDir, `record-${today()}.log`);
|
|
1326
|
+
if (f !== todayFile) {
|
|
1327
|
+
todayFile = f;
|
|
1328
|
+
}
|
|
1329
|
+
if (!import_node_fs2.default.existsSync(todayFile)) {
|
|
1330
|
+
import_node_fs2.default.writeFileSync(todayFile, "", "utf8");
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
function write(level, msg) {
|
|
1334
|
+
ensureFile();
|
|
1335
|
+
const line = `[${timestamp()}] [${level}] ${msg}
|
|
1336
|
+
`;
|
|
1337
|
+
import_node_fs2.default.appendFileSync(todayFile, line, "utf8");
|
|
1338
|
+
console.log(`[log] ${line.trim()}`);
|
|
1339
|
+
}
|
|
1340
|
+
function info(msg) {
|
|
1341
|
+
write("INFO", msg);
|
|
1342
|
+
}
|
|
1343
|
+
function warn(msg) {
|
|
1344
|
+
write("WARN", msg);
|
|
1345
|
+
}
|
|
1346
|
+
function error(msg) {
|
|
1347
|
+
write("ERROR", msg);
|
|
1348
|
+
}
|
|
1349
|
+
function readRecent(days = 7) {
|
|
1350
|
+
ensureDir();
|
|
1351
|
+
const files = import_node_fs2.default.readdirSync(logDir).filter((f) => f.startsWith("record-") && f.endsWith(".log")).sort().reverse().slice(0, days);
|
|
1352
|
+
const lines = [];
|
|
1353
|
+
for (const f of files.reverse()) {
|
|
1354
|
+
const content = import_node_fs2.default.readFileSync(import_node_path4.default.join(logDir, f), "utf8");
|
|
1355
|
+
lines.push(...content.trim().split("\n").filter(Boolean));
|
|
1356
|
+
}
|
|
1357
|
+
return lines.join("\n");
|
|
1358
|
+
}
|
|
1359
|
+
var import_node_fs2, import_node_path4, logDir, todayFile;
|
|
1360
|
+
var init_logService = __esm({
|
|
1361
|
+
"src/plugins/logService.ts"() {
|
|
1362
|
+
import_node_fs2 = __toESM(require("node:fs"));
|
|
1363
|
+
import_node_path4 = __toESM(require("node:path"));
|
|
1364
|
+
logDir = "";
|
|
1365
|
+
todayFile = "";
|
|
1366
|
+
__name(ensureDir, "ensureDir");
|
|
1367
|
+
__name(today, "today");
|
|
1368
|
+
__name(timestamp, "timestamp");
|
|
1369
|
+
__name(initLog, "initLog");
|
|
1370
|
+
__name(ensureFile, "ensureFile");
|
|
1371
|
+
__name(write, "write");
|
|
1372
|
+
__name(info, "info");
|
|
1373
|
+
__name(warn, "warn");
|
|
1374
|
+
__name(error, "error");
|
|
1375
|
+
__name(readRecent, "readRecent");
|
|
1376
|
+
}
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
// src/plugins/verifyService.ts
|
|
1380
|
+
var verifyService_exports = {};
|
|
1381
|
+
__export(verifyService_exports, {
|
|
1382
|
+
VerifyService: () => VerifyService
|
|
1383
|
+
});
|
|
1384
|
+
function randomCode(length) {
|
|
1385
|
+
let code = "";
|
|
1386
|
+
for (let i = 0; i < length; i++) {
|
|
1387
|
+
code += CODE_CHARS[Math.floor(Math.random() * CODE_CHARS.length)];
|
|
1388
|
+
}
|
|
1389
|
+
return code;
|
|
1390
|
+
}
|
|
1391
|
+
function randomToken() {
|
|
1392
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
1393
|
+
let t = TOKEN_PREFIX;
|
|
1394
|
+
for (let i = 0; i < TOKEN_RANDOM_LEN; i++) {
|
|
1395
|
+
t += chars[Math.floor(Math.random() * chars.length)];
|
|
1396
|
+
}
|
|
1397
|
+
return t;
|
|
1398
|
+
}
|
|
1399
|
+
var import_node_fs3, import_node_path5, CODE_CHARS, CODE_LENGTH, PREFIX_LENGTH, TTL_MS, CLEANUP_INTERVAL_MS, MAX_PENDING, TOKEN_PREFIX, TOKEN_RANDOM_LEN, VerifyService;
|
|
1400
|
+
var init_verifyService = __esm({
|
|
1401
|
+
"src/plugins/verifyService.ts"() {
|
|
1402
|
+
import_node_fs3 = __toESM(require("node:fs"));
|
|
1403
|
+
import_node_path5 = __toESM(require("node:path"));
|
|
1404
|
+
init_utils();
|
|
1405
|
+
CODE_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
1406
|
+
CODE_LENGTH = 10;
|
|
1407
|
+
PREFIX_LENGTH = 6;
|
|
1408
|
+
TTL_MS = 5 * 60 * 1e3;
|
|
1409
|
+
CLEANUP_INTERVAL_MS = 3e4;
|
|
1410
|
+
MAX_PENDING = 1e3;
|
|
1411
|
+
TOKEN_PREFIX = "sfx_";
|
|
1412
|
+
TOKEN_RANDOM_LEN = 16;
|
|
1413
|
+
__name(randomCode, "randomCode");
|
|
1414
|
+
__name(randomToken, "randomToken");
|
|
1415
|
+
VerifyService = class {
|
|
1416
|
+
constructor(ctx, cfg, ownerId) {
|
|
1417
|
+
this.ctx = ctx;
|
|
1418
|
+
this.cfg = cfg;
|
|
1419
|
+
this.ownerId = ownerId;
|
|
1420
|
+
this.log = this.ctx.logger("starfx-bot");
|
|
1421
|
+
}
|
|
1422
|
+
static {
|
|
1423
|
+
__name(this, "VerifyService");
|
|
1424
|
+
}
|
|
1425
|
+
pending = /* @__PURE__ */ new Map();
|
|
1426
|
+
timer = null;
|
|
1427
|
+
log;
|
|
1428
|
+
get autoTokensPath() {
|
|
1429
|
+
return import_node_path5.default.join(
|
|
1430
|
+
this.ctx.baseDir,
|
|
1431
|
+
"data/starfx-bot/assets",
|
|
1432
|
+
"auto-tokens.json"
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
// ========== 生命周期 ==========
|
|
1436
|
+
start() {
|
|
1437
|
+
this.timer = this.ctx.setInterval(
|
|
1438
|
+
() => this.cleanup(),
|
|
1439
|
+
CLEANUP_INTERVAL_MS
|
|
1440
|
+
);
|
|
1441
|
+
this.ctx.on("dispose", () => this.stop());
|
|
1442
|
+
}
|
|
1443
|
+
stop() {
|
|
1444
|
+
if (this.timer) {
|
|
1445
|
+
this.timer();
|
|
1446
|
+
this.timer = null;
|
|
1447
|
+
}
|
|
1448
|
+
this.pending.clear();
|
|
1449
|
+
}
|
|
1450
|
+
// ========== HTTP API ==========
|
|
1451
|
+
requestVerify(qqId, mode = "login") {
|
|
1452
|
+
if (this.pending.size >= MAX_PENDING && !this.pending.has(qqId))
|
|
1453
|
+
return null;
|
|
1454
|
+
const fullCode = randomCode(CODE_LENGTH);
|
|
1455
|
+
this.pending.set(qqId, {
|
|
1456
|
+
fullCode,
|
|
1457
|
+
createdAt: Date.now(),
|
|
1458
|
+
verified: false,
|
|
1459
|
+
mode
|
|
1460
|
+
});
|
|
1461
|
+
return fullCode;
|
|
1462
|
+
}
|
|
1463
|
+
checkVerify(fullCode) {
|
|
1464
|
+
for (const [, entry] of this.pending) {
|
|
1465
|
+
if (entry.fullCode === fullCode) {
|
|
1466
|
+
if (Date.now() - entry.createdAt > TTL_MS)
|
|
1467
|
+
return { verified: false, token: null };
|
|
1468
|
+
return { verified: entry.verified, token: entry.token ?? null };
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
return { verified: false, token: null };
|
|
1472
|
+
}
|
|
1473
|
+
async addGroup(token, rawGroupId) {
|
|
1474
|
+
const data = this.load();
|
|
1475
|
+
const qqId = this.findQqIdByToken(data, token);
|
|
1476
|
+
if (!qqId) return { success: false, message: "Token 无效" };
|
|
1477
|
+
const entry = data.users[qqId];
|
|
1478
|
+
const fullGid = `${entry.platform}:${rawGroupId}`;
|
|
1479
|
+
if (entry.groups[fullGid])
|
|
1480
|
+
return { success: false, message: "该群已在列表中" };
|
|
1481
|
+
const bot = this.ctx.bots.find((b) => b.platform === entry.platform);
|
|
1482
|
+
if (!bot)
|
|
1483
|
+
return {
|
|
1484
|
+
success: false,
|
|
1485
|
+
message: `未找到 ${entry.platform} 平台的机器人`
|
|
1486
|
+
};
|
|
1487
|
+
const featureControl = parseFeatureControl(this.cfg.featureControl);
|
|
1488
|
+
if (!detectControl(featureControl, rawGroupId, "record")) {
|
|
1489
|
+
return { success: false, message: "该群未启用语录功能" };
|
|
1490
|
+
}
|
|
1491
|
+
try {
|
|
1492
|
+
const member = await bot.getGuildMember(rawGroupId, qqId);
|
|
1493
|
+
if (!member) return { success: false, message: "你不在该群中" };
|
|
1494
|
+
} catch {
|
|
1495
|
+
return { success: false, message: "查询群成员失败,请确认群号正确" };
|
|
1496
|
+
}
|
|
1497
|
+
let groupName = fullGid;
|
|
1498
|
+
try {
|
|
1499
|
+
const guild = await bot.getGuild(rawGroupId);
|
|
1500
|
+
if (guild?.name) groupName = guild.name;
|
|
1501
|
+
} catch {
|
|
1502
|
+
}
|
|
1503
|
+
entry.groups[fullGid] = groupName;
|
|
1504
|
+
this.save(data);
|
|
1505
|
+
return { success: true, message: "添加成功", groupId: fullGid, groupName };
|
|
1506
|
+
}
|
|
1507
|
+
revokeToken(token) {
|
|
1508
|
+
const data = this.load();
|
|
1509
|
+
const qqId = this.findQqIdByToken(data, token);
|
|
1510
|
+
if (!qqId) return { success: false, message: "Token 无效" };
|
|
1511
|
+
delete data.users[qqId];
|
|
1512
|
+
this.pending.delete(qqId);
|
|
1513
|
+
this.save(data);
|
|
1514
|
+
return { success: true, message: "已撤销" };
|
|
1515
|
+
}
|
|
1516
|
+
// ========== QQ 命令 ==========
|
|
1517
|
+
async verifyCommand(qqId, platform, prefix) {
|
|
1518
|
+
if (prefix.length !== PREFIX_LENGTH) return "验证码格式错误";
|
|
1519
|
+
const entry = this.pending.get(qqId);
|
|
1520
|
+
if (!entry) return "没有待验证的请求,请先在网页上获取验证码";
|
|
1521
|
+
if (Date.now() - entry.createdAt > TTL_MS) {
|
|
1522
|
+
this.pending.delete(qqId);
|
|
1523
|
+
return "验证码已过期,请重新在网页上获取";
|
|
1524
|
+
}
|
|
1525
|
+
if (!entry.fullCode.startsWith(prefix)) return "验证码错误";
|
|
1526
|
+
const data = this.load();
|
|
1527
|
+
const existingUser = data.users[qqId];
|
|
1528
|
+
let token;
|
|
1529
|
+
if (entry.mode === "login" && existingUser) {
|
|
1530
|
+
token = existingUser.token;
|
|
1531
|
+
} else {
|
|
1532
|
+
token = randomToken();
|
|
1533
|
+
}
|
|
1534
|
+
entry.verified = true;
|
|
1535
|
+
entry.token = token;
|
|
1536
|
+
let username = qqId;
|
|
1537
|
+
for (const bot of this.ctx.bots) {
|
|
1538
|
+
try {
|
|
1539
|
+
const user = await bot.getUser(qqId);
|
|
1540
|
+
if (user?.nickname || user?.name) {
|
|
1541
|
+
username = (user.nickname || user.name) ?? qqId;
|
|
1542
|
+
break;
|
|
1543
|
+
}
|
|
1544
|
+
} catch {
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
data.users[qqId] = {
|
|
1548
|
+
token,
|
|
1549
|
+
username,
|
|
1550
|
+
platform,
|
|
1551
|
+
groups: data.users[qqId]?.groups ?? {}
|
|
1552
|
+
};
|
|
1553
|
+
this.save(data);
|
|
1554
|
+
return "";
|
|
1555
|
+
}
|
|
1556
|
+
resetByQqId(qqId) {
|
|
1557
|
+
this.pending.delete(qqId);
|
|
1558
|
+
const data = this.load();
|
|
1559
|
+
if (!data.users[qqId])
|
|
1560
|
+
return { success: false, message: "你还没有绑定 Token" };
|
|
1561
|
+
delete data.users[qqId];
|
|
1562
|
+
this.save(data);
|
|
1563
|
+
return { success: true, message: "已重置,请重新验证" };
|
|
1564
|
+
}
|
|
1565
|
+
async removeGroup(qqId, platform, rawGroupId) {
|
|
1566
|
+
const data = this.load();
|
|
1567
|
+
const entry = data.users[qqId];
|
|
1568
|
+
if (!entry) return { success: false, message: "你还没有绑定 Token" };
|
|
1569
|
+
const fullGid = `${platform}:${rawGroupId}`;
|
|
1570
|
+
if (!entry.groups[fullGid])
|
|
1571
|
+
return { success: false, message: "该群不在你的列表中" };
|
|
1572
|
+
delete entry.groups[fullGid];
|
|
1573
|
+
this.save(data);
|
|
1574
|
+
return { success: true, message: "已移除" };
|
|
1575
|
+
}
|
|
1576
|
+
// ========== 角色查询 ==========
|
|
1577
|
+
getRoleByQqId(qqId) {
|
|
1578
|
+
const data = this.load();
|
|
1579
|
+
if (data.roles.owner === qqId) return "owner";
|
|
1580
|
+
if (data.roles.admin.includes(qqId)) return "admin";
|
|
1581
|
+
return "user";
|
|
1582
|
+
}
|
|
1583
|
+
getRoleByToken(token) {
|
|
1584
|
+
const data = this.load();
|
|
1585
|
+
const qqId = this.findQqIdByToken(data, token);
|
|
1586
|
+
return qqId ? this.getRoleByQqId(qqId) : "user";
|
|
1587
|
+
}
|
|
1588
|
+
getLabelByToken(token) {
|
|
1589
|
+
const data = this.load();
|
|
1590
|
+
const qqId = this.findQqIdByToken(data, token);
|
|
1591
|
+
if (!qqId) return `${String(token).substring(0, 8)}…`;
|
|
1592
|
+
const entry = data.users[qqId];
|
|
1593
|
+
return entry ? `${entry.username}(${qqId})` : qqId;
|
|
1594
|
+
}
|
|
1595
|
+
// ========== 权限管理(owner) ==========
|
|
1596
|
+
setRole(operatorQqId, targetQqId, role) {
|
|
1597
|
+
if (this.getRoleByQqId(operatorQqId) !== "owner") {
|
|
1598
|
+
return { success: false, message: "权限不足" };
|
|
1599
|
+
}
|
|
1600
|
+
if (operatorQqId === targetQqId) {
|
|
1601
|
+
return { success: false, message: "不能修改自己的权限" };
|
|
1602
|
+
}
|
|
1603
|
+
if (role === "owner" && operatorQqId !== this.ownerId) {
|
|
1604
|
+
return {
|
|
1605
|
+
success: false,
|
|
1606
|
+
message: "只有配置文件中指定的 owner 才能授予 owner 权限"
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
if (role === "owner" && targetQqId !== this.ownerId) {
|
|
1610
|
+
return { success: false, message: "owner 已由配置文件指定,无法更改" };
|
|
1611
|
+
}
|
|
1612
|
+
const data = this.load();
|
|
1613
|
+
const targetEntry = data.users[targetQqId];
|
|
1614
|
+
if (!targetEntry) return { success: false, message: "目标用户未验证" };
|
|
1615
|
+
if (data.roles.owner === targetQqId) data.roles.owner = this.ownerId;
|
|
1616
|
+
data.roles.admin = data.roles.admin.filter((id) => id !== targetQqId);
|
|
1617
|
+
if (role === "admin") {
|
|
1618
|
+
data.roles.admin.push(targetQqId);
|
|
1619
|
+
} else if (role === "owner") {
|
|
1620
|
+
data.roles.owner = targetQqId;
|
|
1621
|
+
}
|
|
1622
|
+
this.save(data);
|
|
1623
|
+
return {
|
|
1624
|
+
success: true,
|
|
1625
|
+
message: `已将 ${targetEntry.username} 设为 ${role}`
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
listUsers(operatorQqId) {
|
|
1629
|
+
if (this.getRoleByQqId(operatorQqId) !== "owner") {
|
|
1630
|
+
return { success: false, message: "权限不足" };
|
|
1631
|
+
}
|
|
1632
|
+
const data = this.load();
|
|
1633
|
+
const users = Object.entries(data.users).map(([qqId, e]) => ({
|
|
1634
|
+
qqId,
|
|
1635
|
+
username: e.username,
|
|
1636
|
+
role: this.getRoleByQqId(qqId)
|
|
1637
|
+
}));
|
|
1638
|
+
return { success: true, users };
|
|
1639
|
+
}
|
|
1640
|
+
deleteUser(operatorQqId, targetQqId) {
|
|
1641
|
+
if (this.getRoleByQqId(operatorQqId) !== "owner") {
|
|
1642
|
+
return { success: false, message: "权限不足" };
|
|
1643
|
+
}
|
|
1644
|
+
if (targetQqId === operatorQqId)
|
|
1645
|
+
return { success: false, message: "不能删除自己" };
|
|
1646
|
+
if (targetQqId === this.ownerId)
|
|
1647
|
+
return { success: false, message: "无法删除配置文件中指定的 owner" };
|
|
1648
|
+
const data = this.load();
|
|
1649
|
+
if (!data.users[targetQqId])
|
|
1650
|
+
return { success: false, message: "目标用户不存在" };
|
|
1651
|
+
delete data.users[targetQqId];
|
|
1652
|
+
if (data.roles.owner === targetQqId) data.roles.owner = this.ownerId;
|
|
1653
|
+
data.roles.admin = data.roles.admin.filter((id) => id !== targetQqId);
|
|
1654
|
+
this.pending.delete(targetQqId);
|
|
1655
|
+
this.save(data);
|
|
1656
|
+
return { success: true, message: "已删除" };
|
|
1657
|
+
}
|
|
1658
|
+
// ========== HTTP API 代理(token → qqId 转换) ==========
|
|
1659
|
+
findQqIdByToken(data, token) {
|
|
1660
|
+
for (const [qqId, e] of Object.entries(data.users)) {
|
|
1661
|
+
if (e.token === token) return qqId;
|
|
1662
|
+
}
|
|
1663
|
+
return null;
|
|
1664
|
+
}
|
|
1665
|
+
listUsersByToken(token) {
|
|
1666
|
+
const qqId = this.findQqIdByToken(this.load(), token);
|
|
1667
|
+
if (!qqId) return { success: false, message: "Token 无效" };
|
|
1668
|
+
const result = this.listUsers(qqId);
|
|
1669
|
+
return { ...result, selfQqId: result.success ? qqId : void 0 };
|
|
1670
|
+
}
|
|
1671
|
+
setRoleByToken(token, targetQqId, role) {
|
|
1672
|
+
const qqId = this.findQqIdByToken(this.load(), token);
|
|
1673
|
+
if (!qqId) return { success: false, message: "Token 无效" };
|
|
1674
|
+
return this.setRole(qqId, targetQqId, role);
|
|
1675
|
+
}
|
|
1676
|
+
deleteUserByToken(token, targetQqId) {
|
|
1677
|
+
const qqId = this.findQqIdByToken(this.load(), token);
|
|
1678
|
+
if (!qqId) return { success: false, message: "Token 无效" };
|
|
1679
|
+
return this.deleteUser(qqId, targetQqId);
|
|
1680
|
+
}
|
|
1681
|
+
// ========== 内部实现 ==========
|
|
1682
|
+
cleanup() {
|
|
1683
|
+
const now = Date.now();
|
|
1684
|
+
for (const [qqId, entry] of this.pending) {
|
|
1685
|
+
if (now - entry.createdAt > TTL_MS) this.pending.delete(qqId);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
defaultData() {
|
|
1689
|
+
return { roles: { owner: this.ownerId, admin: [] }, users: {} };
|
|
1690
|
+
}
|
|
1691
|
+
load() {
|
|
1692
|
+
try {
|
|
1693
|
+
if (import_node_fs3.default.existsSync(this.autoTokensPath)) {
|
|
1694
|
+
return JSON.parse(import_node_fs3.default.readFileSync(this.autoTokensPath, "utf8"));
|
|
1695
|
+
}
|
|
1696
|
+
} catch (e) {
|
|
1697
|
+
this.log.warn("读取 auto-tokens.json 失败", e);
|
|
1698
|
+
}
|
|
1699
|
+
return this.defaultData();
|
|
1700
|
+
}
|
|
1701
|
+
save(data) {
|
|
1702
|
+
try {
|
|
1703
|
+
const dir = import_node_path5.default.dirname(this.autoTokensPath);
|
|
1704
|
+
if (!import_node_fs3.default.existsSync(dir)) import_node_fs3.default.mkdirSync(dir, { recursive: true });
|
|
1705
|
+
import_node_fs3.default.writeFileSync(
|
|
1706
|
+
this.autoTokensPath,
|
|
1707
|
+
JSON.stringify(data, null, 2),
|
|
1708
|
+
"utf8"
|
|
1709
|
+
);
|
|
1710
|
+
} catch (e) {
|
|
1711
|
+
this.log.error("写入 auto-tokens.json 失败", e);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1716
|
+
});
|
|
1717
|
+
|
|
1718
|
+
// src/plugins/recordApi.ts
|
|
1719
|
+
var recordApi_exports = {};
|
|
1720
|
+
__export(recordApi_exports, {
|
|
1721
|
+
runRecordApi: () => runRecordApi
|
|
1722
|
+
});
|
|
1723
|
+
function runRecordApi(ctx, cfg, verifyService) {
|
|
1724
|
+
const { initLog: initLog2 } = (init_logService(), __toCommonJS(logService_exports));
|
|
1725
|
+
initLog2(ctx.baseDir);
|
|
1726
|
+
if (ctx.server) {
|
|
1727
|
+
const secretGuard = /* @__PURE__ */ __name((kctx) => {
|
|
1728
|
+
if (kctx.get("x-secret") !== cfg.apiSecret) {
|
|
1729
|
+
kctx.status = 401;
|
|
1730
|
+
kctx.body = { error: "unauthorized" };
|
|
1731
|
+
return false;
|
|
1732
|
+
}
|
|
1733
|
+
return true;
|
|
1734
|
+
}, "secretGuard");
|
|
1735
|
+
ctx.server.post(
|
|
1736
|
+
"/starfx/api/record/verify-request",
|
|
1737
|
+
async (kctx) => {
|
|
1738
|
+
if (!secretGuard(kctx)) return;
|
|
1739
|
+
const body = kctx.request.body ?? {};
|
|
1740
|
+
const qqId = body.qqId;
|
|
1741
|
+
if (!qqId) {
|
|
1742
|
+
kctx.status = 400;
|
|
1743
|
+
kctx.body = { error: "missing qqId" };
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
const fullCode = verifyService.requestVerify(
|
|
1747
|
+
String(qqId),
|
|
1748
|
+
body.mode === "reset" ? "reset" : "login"
|
|
1749
|
+
);
|
|
1750
|
+
if (fullCode === null) {
|
|
1751
|
+
kctx.status = 429;
|
|
1752
|
+
kctx.body = { error: "too many requests" };
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
kctx.body = { fullCode };
|
|
1756
|
+
}
|
|
1757
|
+
);
|
|
1758
|
+
ctx.server.post("/starfx/api/record/check-verify", async (kctx) => {
|
|
1759
|
+
if (!secretGuard(kctx)) return;
|
|
1760
|
+
const { fullCode } = kctx.request.body ?? {};
|
|
1761
|
+
if (!fullCode) {
|
|
1762
|
+
kctx.status = 400;
|
|
1763
|
+
kctx.body = { error: "missing fullCode" };
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
kctx.body = verifyService.checkVerify(String(fullCode));
|
|
1767
|
+
});
|
|
1768
|
+
ctx.server.post("/starfx/api/record/add-group", async (kctx) => {
|
|
1769
|
+
if (!secretGuard(kctx)) return;
|
|
1770
|
+
const { token, groupId } = kctx.request.body ?? {};
|
|
1771
|
+
if (!token || !groupId) {
|
|
1772
|
+
kctx.status = 400;
|
|
1773
|
+
kctx.body = { error: "missing token or groupId" };
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
kctx.body = await verifyService.addGroup(String(token), String(groupId));
|
|
1777
|
+
});
|
|
1778
|
+
ctx.server.post("/starfx/api/record/revoke-token", async (kctx) => {
|
|
1779
|
+
if (!secretGuard(kctx)) return;
|
|
1780
|
+
const { token } = kctx.request.body ?? {};
|
|
1781
|
+
if (!token) {
|
|
1782
|
+
kctx.status = 400;
|
|
1783
|
+
kctx.body = { error: "missing token" };
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
kctx.body = verifyService.revokeToken(String(token));
|
|
1787
|
+
});
|
|
1788
|
+
ctx.server.post("/starfx/api/record/list-users", async (kctx) => {
|
|
1789
|
+
if (!secretGuard(kctx)) return;
|
|
1790
|
+
const { token } = kctx.request.body ?? {};
|
|
1791
|
+
const role = verifyService.getRoleByToken(String(token ?? ""));
|
|
1792
|
+
if (role !== "owner") {
|
|
1793
|
+
kctx.status = 403;
|
|
1794
|
+
kctx.body = { error: "权限不足" };
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
kctx.body = verifyService.listUsersByToken(String(token));
|
|
1798
|
+
});
|
|
1799
|
+
ctx.server.post("/starfx/api/record/set-role", async (kctx) => {
|
|
1800
|
+
if (!secretGuard(kctx)) return;
|
|
1801
|
+
const {
|
|
1802
|
+
token,
|
|
1803
|
+
targetQqId,
|
|
1804
|
+
role: newRole
|
|
1805
|
+
} = kctx.request.body ?? {};
|
|
1806
|
+
const opRole = verifyService.getRoleByToken(String(token ?? ""));
|
|
1807
|
+
if (opRole !== "owner") {
|
|
1808
|
+
kctx.status = 403;
|
|
1809
|
+
kctx.body = { error: "权限不足" };
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
kctx.body = verifyService.setRoleByToken(
|
|
1813
|
+
String(token),
|
|
1814
|
+
String(targetQqId),
|
|
1815
|
+
newRole ?? ""
|
|
1816
|
+
);
|
|
1817
|
+
});
|
|
1818
|
+
ctx.server.post("/starfx/api/record/delete-user", async (kctx) => {
|
|
1819
|
+
if (!secretGuard(kctx)) return;
|
|
1820
|
+
const { token, targetQqId } = kctx.request.body ?? {};
|
|
1821
|
+
const opRole = verifyService.getRoleByToken(String(token ?? ""));
|
|
1822
|
+
if (opRole !== "owner") {
|
|
1823
|
+
kctx.status = 403;
|
|
1824
|
+
kctx.body = { error: "权限不足" };
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
kctx.body = verifyService.deleteUserByToken(
|
|
1828
|
+
String(token),
|
|
1829
|
+
String(targetQqId)
|
|
1830
|
+
);
|
|
1831
|
+
});
|
|
1832
|
+
}
|
|
1833
|
+
ctx.command("语录.验证 <code>").action(async ({ session }, code) => {
|
|
1834
|
+
const error2 = await verifyService.verifyCommand(
|
|
1835
|
+
session.userId,
|
|
1836
|
+
session.platform,
|
|
1837
|
+
code
|
|
1838
|
+
);
|
|
1839
|
+
if (error2) return error2;
|
|
1840
|
+
return "验证成功!回到网页等待自动跳转即可";
|
|
1841
|
+
});
|
|
1842
|
+
ctx.command("语录.重置").action(async ({ session }) => {
|
|
1843
|
+
return verifyService.resetByQqId(session.userId).message;
|
|
1844
|
+
});
|
|
1845
|
+
ctx.command("语录.删除群 <group>").action(async ({ session }, groupId) => {
|
|
1846
|
+
const r = await verifyService.removeGroup(
|
|
1847
|
+
session.userId,
|
|
1848
|
+
session.platform,
|
|
1849
|
+
groupId
|
|
1850
|
+
);
|
|
1851
|
+
return r.message;
|
|
1852
|
+
});
|
|
1853
|
+
ctx.command("语录.设权限 <qqId> <role>").action(async ({ session }, qqId, role) => {
|
|
1854
|
+
if (role !== "user" && role !== "admin" && role !== "owner") {
|
|
1855
|
+
return "角色只能是 user、admin 或 owner";
|
|
1856
|
+
}
|
|
1857
|
+
return verifyService.setRole(
|
|
1858
|
+
session.userId,
|
|
1859
|
+
qqId,
|
|
1860
|
+
role
|
|
1861
|
+
).message;
|
|
1862
|
+
});
|
|
1863
|
+
ctx.command("语录.用户列表").action(async ({ session }) => {
|
|
1864
|
+
const r = verifyService.listUsers(session.userId);
|
|
1865
|
+
if (!r.success) return r.message;
|
|
1866
|
+
return r.users?.map((u) => `${u.username}(${u.qqId}) - ${u.role}`).join("\n") || "暂无用户";
|
|
1867
|
+
});
|
|
1868
|
+
ctx.command("语录.删除用户 <qqId>").action(async ({ session }, qqId) => {
|
|
1869
|
+
return verifyService.deleteUser(session.userId, qqId).message;
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
var init_recordApi = __esm({
|
|
1873
|
+
"src/plugins/recordApi.ts"() {
|
|
1874
|
+
__name(runRecordApi, "runRecordApi");
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1248
1877
|
|
|
1249
1878
|
// src/index.ts
|
|
1250
|
-
var
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
<li>卖掉了</li>
|
|
1264
|
-
<li>封印</li>
|
|
1265
|
-
<li>bdbd</li>
|
|
1266
|
-
`;
|
|
1267
|
-
var repeatContextMap = /* @__PURE__ */ new Map();
|
|
1268
|
-
var Config = import_koishi5.Schema.intersect([
|
|
1269
|
-
import_koishi5.Schema.object({
|
|
1270
|
-
openLock: import_koishi5.Schema.boolean().default(true).description("开启明日方舟封印功能"),
|
|
1271
|
-
openSold: import_koishi5.Schema.boolean().default(true).description('开启闲鱼"卖掉了"功能'),
|
|
1272
|
-
bangdreamBorder: import_koishi5.Schema.boolean().default(true).description("开启BanG Dream!边框功能")
|
|
1273
|
-
}).description("绘图功能"),
|
|
1274
|
-
import_koishi5.Schema.object({
|
|
1275
|
-
record: import_koishi5.Schema.boolean().default(true).description("开启群语录功能"),
|
|
1276
|
-
tagWeight: import_koishi5.Schema.number().default(5).min(1).description("tag匹配时的权重,越高权重越大"),
|
|
1277
|
-
recordLink: import_koishi5.Schema.dict(
|
|
1278
|
-
import_koishi5.Schema.object({
|
|
1279
|
-
linkGroup: import_koishi5.Schema.string(),
|
|
1280
|
-
linkWeight: import_koishi5.Schema.number()
|
|
1281
|
-
})
|
|
1282
|
-
).role("table").description(
|
|
1283
|
-
"群组链接,使得群可以调用被链接群的语录,<br>可以配置权重,作为键的群自身的权重为100(注意格式为平台名:群组名)"
|
|
1284
|
-
),
|
|
1285
|
-
saveArchive: import_koishi5.Schema.boolean().default(false).description("开启入典功能").hidden()
|
|
1286
|
-
}).description("语录记录功能"),
|
|
1287
|
-
import_koishi5.Schema.object({
|
|
1288
|
-
roll: import_koishi5.Schema.boolean().default(true).description("开启roll随机数功能"),
|
|
1289
|
-
undo: import_koishi5.Schema.boolean().default(true).description("机器人撤回消息功能(只测试了qq的onebot适配器)"),
|
|
1290
|
-
echo: import_koishi5.Schema.boolean().default(true).description("echo回声洞功能"),
|
|
1291
|
-
echoBanner: import_koishi5.Schema.array(String).role("table").description("echo屏蔽词,对文本生效"),
|
|
1292
|
-
ready: import_koishi5.Schema.boolean().default(false).description("待机人数记录功能"),
|
|
1293
|
-
saveReadyAsFile: import_koishi5.Schema.string().description(
|
|
1294
|
-
"写入待机人数的本地地址,留空则不写入"
|
|
1295
|
-
),
|
|
1296
|
-
roomNumber: import_koishi5.Schema.boolean().default(false).description("主跑房间号记录功能"),
|
|
1297
|
-
saveRoomAsFile: import_koishi5.Schema.string().description(
|
|
1298
|
-
"写入房间号的本地地址,留空则不写入"
|
|
1299
|
-
),
|
|
1300
|
-
forward: import_koishi5.Schema.boolean().default(true).description("消息转发功能"),
|
|
1301
|
-
searchExchangeRate: import_koishi5.Schema.boolean().default(false).description("查汇率功能"),
|
|
1302
|
-
intervalGetExchangeRate: import_koishi5.Schema.boolean().default(false).description("汇率定时推送功能").hidden(),
|
|
1303
|
-
roomNumToName: import_koishi5.Schema.boolean().default(false).description("监听车牌修改群名功能"),
|
|
1304
|
-
roomNumLength: import_koishi5.Schema.number().default(5).description("默认车牌位数")
|
|
1305
|
-
}).description("指令小功能"),
|
|
1306
|
-
import_koishi5.Schema.object({
|
|
1307
|
-
atNotSay: import_koishi5.Schema.boolean().default(true).description("开启‘艾特我又不说话’功能"),
|
|
1308
|
-
atNotSayProperty: import_koishi5.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.5).description("'艾特我又不说话'回复概率"),
|
|
1309
|
-
atNotSayOther: import_koishi5.Schema.boolean().default(true).description("开启‘艾特他又不说话’功能"),
|
|
1310
|
-
atNotSayOtherProperty: import_koishi5.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.5).description("'艾特他又不说话'回复概率"),
|
|
1311
|
-
iLoveYou: import_koishi5.Schema.boolean().default(true).description("开启‘我喜欢你’功能"),
|
|
1312
|
-
replyBot: import_koishi5.Schema.union(["关闭", "无需at", "必须at"]).default("无需at").description("回复‘我才不是机器人!’功能")
|
|
1313
|
-
}).description("特定回应功能"),
|
|
1314
|
-
import_koishi5.Schema.object({
|
|
1315
|
-
sendLocalImage: import_koishi5.Schema.dict(
|
|
1316
|
-
import_koishi5.Schema.object({
|
|
1317
|
-
hiddenInHelp: import_koishi5.Schema.boolean(),
|
|
1318
|
-
imgPath: import_koishi5.Schema.string()
|
|
1319
|
-
})
|
|
1320
|
-
).role("table").description(
|
|
1321
|
-
"特定指令发送本地图片功能,其中键是指令名称,imgPath是图片文件的绝对路径"
|
|
1322
|
-
)
|
|
1323
|
-
}),
|
|
1324
|
-
import_koishi5.Schema.object({
|
|
1325
|
-
openRepeat: import_koishi5.Schema.boolean().default(true).description("开启复读功能"),
|
|
1326
|
-
minRepeatTimes: import_koishi5.Schema.number().default(2).description("最少重复次数"),
|
|
1327
|
-
repeatPossibility: import_koishi5.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.3).description("复读发生概率")
|
|
1328
|
-
}).description("复读功能"),
|
|
1329
|
-
import_koishi5.Schema.object({
|
|
1330
|
-
myId: import_koishi5.Schema.boolean().default(false).description("查询gid uid cid")
|
|
1331
|
-
}).description("我的信息查询"),
|
|
1332
|
-
import_koishi5.Schema.object({
|
|
1333
|
-
filePathToBase64: import_koishi5.Schema.boolean().default(false).description(
|
|
1334
|
-
"在消息发送前检查是否有file://,如果有那么转换为base64再发送"
|
|
1335
|
-
),
|
|
1336
|
-
originImg: import_koishi5.Schema.boolean().default(false).description("根据链接获取原图开关")
|
|
1337
|
-
}).description("自用功能"),
|
|
1338
|
-
import_koishi5.Schema.union([
|
|
1339
|
-
import_koishi5.Schema.object({
|
|
1340
|
-
originImg: import_koishi5.Schema.const(true).required(),
|
|
1341
|
-
originImgRSSUrl: import_koishi5.Schema.string().required().description("推特列表rss地址")
|
|
1342
|
-
}),
|
|
1343
|
-
import_koishi5.Schema.object({})
|
|
1344
|
-
]),
|
|
1345
|
-
import_koishi5.Schema.object({
|
|
1346
|
-
featureControl: import_koishi5.Schema.array(
|
|
1347
|
-
import_koishi5.Schema.object({
|
|
1348
|
-
functionName: import_koishi5.Schema.string(),
|
|
1349
|
-
whitelist: import_koishi5.Schema.boolean(),
|
|
1350
|
-
groups: import_koishi5.Schema.string()
|
|
1351
|
-
})
|
|
1352
|
-
).role("table").description(`黑/白名单配置,群组间用英文半角逗号分隔,<br>
|
|
1353
|
-
可配置功能键及用法详见 [项目地址](https://github.com/StarFreedomX/starfx-bot)或[npm发布页](https://www.npmjs.com/package/koishi-plugin-bangdream-ccg)`)
|
|
1354
|
-
}).description("高级配置")
|
|
1355
|
-
]);
|
|
1879
|
+
var src_exports = {};
|
|
1880
|
+
__export(src_exports, {
|
|
1881
|
+
Config: () => Config,
|
|
1882
|
+
apply: () => apply,
|
|
1883
|
+
assetsDir: () => assetsDir,
|
|
1884
|
+
baseDir: () => baseDir,
|
|
1885
|
+
inject: () => inject,
|
|
1886
|
+
name: () => name,
|
|
1887
|
+
repeatContextMap: () => repeatContextMap,
|
|
1888
|
+
starfxLogger: () => starfxLogger,
|
|
1889
|
+
usage: () => usage
|
|
1890
|
+
});
|
|
1891
|
+
module.exports = __toCommonJS(src_exports);
|
|
1356
1892
|
function apply(ctx, cfg) {
|
|
1357
1893
|
ctx.model.extend("channel", {
|
|
1358
1894
|
pickupLength: "unsigned",
|
|
@@ -1363,7 +1899,7 @@ function apply(ctx, cfg) {
|
|
|
1363
1899
|
baseDir = ctx.baseDir;
|
|
1364
1900
|
assetsDir = `${ctx.baseDir}/data/starfx-bot/assets`;
|
|
1365
1901
|
initAssets();
|
|
1366
|
-
|
|
1902
|
+
const featureControl = parseFeatureControl(cfg.featureControl);
|
|
1367
1903
|
if (cfg.openLock) {
|
|
1368
1904
|
ctx.command("封印 [param]").action(async ({ session }, param) => {
|
|
1369
1905
|
if (ctx.QhzySharp && detectControl(featureControl, session.guildId, "lock"))
|
|
@@ -1432,7 +1968,7 @@ function apply(ctx, cfg) {
|
|
|
1432
1968
|
});
|
|
1433
1969
|
}
|
|
1434
1970
|
if (cfg.bangdreamBorder) {
|
|
1435
|
-
ctx.command("bdbd [param]").option("starNum", "-n <starNum: number>").option("color", "-c <color: string>").option("train", "-t <train: string>").option("band", "-b <band: string>").action(async ({ session, options }, param) => {
|
|
1971
|
+
ctx.command("bdbd [param:text]").option("starNum", "-n <starNum: number>").option("color", "-c <color: string>").option("train", "-t <train: string>").option("band", "-b <band: string>").action(async ({ session, options }, param) => {
|
|
1436
1972
|
if (ctx.QhzySharp && detectControl(featureControl, session.guildId, "bdbd")) {
|
|
1437
1973
|
const drawConfig = await handleBanGDreamConfig(options);
|
|
1438
1974
|
const imgSrc = await getImageSrc(session, param);
|
|
@@ -1448,6 +1984,8 @@ function apply(ctx, cfg) {
|
|
|
1448
1984
|
});
|
|
1449
1985
|
}
|
|
1450
1986
|
if (cfg.record) {
|
|
1987
|
+
const { info: logRecord } = cfg.autoToken ? (init_logService(), __toCommonJS(logService_exports)) : { info: /* @__PURE__ */ __name(() => {
|
|
1988
|
+
}, "info") };
|
|
1451
1989
|
ctx.command("投稿 [param]").action(async ({ session }, param) => {
|
|
1452
1990
|
if (detectControl(featureControl, session.guildId, "record") && detectControl(featureControl, session.guildId, "record-push")) {
|
|
1453
1991
|
const imageSrc = await getImageSrc(session, param, {
|
|
@@ -1460,11 +1998,12 @@ function apply(ctx, cfg) {
|
|
|
1460
1998
|
if (!imageSrc) {
|
|
1461
1999
|
return "请发送带图片的指令消息或引用图片消息进行投稿";
|
|
1462
2000
|
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
2001
|
+
const groupFolder = session.gid.replaceAll(":", "_");
|
|
2002
|
+
const result = await addRecord(ctx, groupFolder, imageSrc);
|
|
2003
|
+
logRecord(
|
|
2004
|
+
`[投稿] ${session.username || session.userId}(${session.userId}) group=${session.gid}`
|
|
1467
2005
|
);
|
|
2006
|
+
return result;
|
|
1468
2007
|
}
|
|
1469
2008
|
});
|
|
1470
2009
|
ctx.command("语录 [tag:string]").action(async ({ session }, tag) => {
|
|
@@ -1480,6 +2019,13 @@ function apply(ctx, cfg) {
|
|
|
1480
2019
|
}
|
|
1481
2020
|
});
|
|
1482
2021
|
}
|
|
2022
|
+
if (cfg.autoToken && cfg.apiSecret) {
|
|
2023
|
+
const { VerifyService: VerifyService2 } = (init_verifyService(), __toCommonJS(verifyService_exports));
|
|
2024
|
+
const { runRecordApi: runRecordApi2 } = (init_recordApi(), __toCommonJS(recordApi_exports));
|
|
2025
|
+
const verifyService = new VerifyService2(ctx, cfg, cfg.ownerId);
|
|
2026
|
+
verifyService.start();
|
|
2027
|
+
runRecordApi2(ctx, cfg, verifyService);
|
|
2028
|
+
}
|
|
1483
2029
|
for (const key in cfg.sendLocalImage) {
|
|
1484
2030
|
ctx.command(key, { hidden: cfg.sendLocalImage[key].hiddenInHelp }).action(async ({ session }) => {
|
|
1485
2031
|
if (detectControl(
|
|
@@ -1536,7 +2082,7 @@ function apply(ctx, cfg) {
|
|
|
1536
2082
|
if (cfg.forward) {
|
|
1537
2083
|
ctx.command("forward").option("group", "-g <group:string>").option("platform", "-p <platform:string>").usage("转发消息").action(async ({ session, options }) => {
|
|
1538
2084
|
if (detectControl(featureControl, session.guildId, "forward")) {
|
|
1539
|
-
const mapPath =
|
|
2085
|
+
const mapPath = import_node_path6.default.join(assetsDir, "forward.json");
|
|
1540
2086
|
const groupMap = readMap(mapPath);
|
|
1541
2087
|
if (options.group) {
|
|
1542
2088
|
if (["0", "clear", "del"].includes(options.group)) {
|
|
@@ -1609,7 +2155,7 @@ function apply(ctx, cfg) {
|
|
|
1609
2155
|
if (cfg.intervalGetExchangeRate) {
|
|
1610
2156
|
ctx.command("开启汇率推送 [exchangeParam:string]").action(async ({ session }, exchangeParam) => {
|
|
1611
2157
|
if (ctx.skia && detectControl(featureControl, session.guildId, "exchangeRate")) {
|
|
1612
|
-
const exchangeRatePath =
|
|
2158
|
+
const exchangeRatePath = import_node_path6.default.join(assetsDir, "exchangeRate.json");
|
|
1613
2159
|
return await intervalGetExchangeRate(
|
|
1614
2160
|
ctx,
|
|
1615
2161
|
cfg,
|
|
@@ -1635,19 +2181,19 @@ function apply(ctx, cfg) {
|
|
|
1635
2181
|
} else {
|
|
1636
2182
|
}
|
|
1637
2183
|
}
|
|
1638
|
-
return
|
|
2184
|
+
return import_node_path6.default.normalize(filePath);
|
|
1639
2185
|
} catch (e) {
|
|
1640
2186
|
starfxLogger.error("URL解析失败:", e);
|
|
1641
2187
|
return uri;
|
|
1642
2188
|
}
|
|
1643
2189
|
}
|
|
1644
|
-
if (
|
|
1645
|
-
return
|
|
2190
|
+
if (import_node_path6.default.isAbsolute(uri) || uri.startsWith(".") || uri.startsWith("..")) {
|
|
2191
|
+
return import_node_path6.default.normalize(uri);
|
|
1646
2192
|
}
|
|
1647
2193
|
return uri;
|
|
1648
2194
|
}, toBase64String = function(src) {
|
|
1649
2195
|
try {
|
|
1650
|
-
const data =
|
|
2196
|
+
const data = fs4.readFileSync(src);
|
|
1651
2197
|
return data.toString("base64");
|
|
1652
2198
|
} catch (err) {
|
|
1653
2199
|
starfxLogger.error(
|
|
@@ -1691,51 +2237,67 @@ function apply(ctx, cfg) {
|
|
|
1691
2237
|
}, "canTogglePickup");
|
|
1692
2238
|
if (cfg.roomNumToName) {
|
|
1693
2239
|
ctx.command("车牌检测权限 [allow:text]").channelFields(["pickupGrant"]).action(async ({ session }, allow) => {
|
|
1694
|
-
if (!detectControl(featureControl, session.guildId, "pickup"))
|
|
2240
|
+
if (!detectControl(featureControl, session.guildId, "pickup"))
|
|
2241
|
+
return;
|
|
1695
2242
|
if (!session.guildId) return session.text(".noGuild");
|
|
1696
|
-
if (!await canGrant(session))
|
|
2243
|
+
if (!await canGrant(session))
|
|
2244
|
+
return session.text(".noPermission");
|
|
1697
2245
|
if (!allow) {
|
|
1698
2246
|
return session.channel.pickupGrant ? session.text(".status.enabled") : session.text(".status.disabled");
|
|
1699
2247
|
}
|
|
1700
2248
|
const normalized = String(allow).trim().toLowerCase();
|
|
1701
|
-
if (["1", "true", "yes", "y", "on", "开", "开启", "允许"].includes(
|
|
2249
|
+
if (["1", "true", "yes", "y", "on", "开", "开启", "允许"].includes(
|
|
2250
|
+
normalized
|
|
2251
|
+
)) {
|
|
1702
2252
|
session.channel.pickupGrant = true;
|
|
1703
2253
|
return session.text(".enabled");
|
|
1704
2254
|
}
|
|
1705
|
-
if (["0", "false", "no", "n", "off", "关", "关闭", "禁止"].includes(
|
|
2255
|
+
if (["0", "false", "no", "n", "off", "关", "关闭", "禁止"].includes(
|
|
2256
|
+
normalized
|
|
2257
|
+
)) {
|
|
1706
2258
|
session.channel.pickupGrant = false;
|
|
1707
2259
|
return session.text(".disabled");
|
|
1708
2260
|
}
|
|
1709
2261
|
return session.text(".invalid");
|
|
1710
2262
|
});
|
|
1711
2263
|
ctx.command("开启车牌检测 [length:number]").alias("打开车牌检测", "打开拾取").channelFields(["pickupLength", "pickupGrant"]).action(async ({ session }, length) => {
|
|
1712
|
-
if (!detectControl(featureControl, session.guildId, "pickup"))
|
|
2264
|
+
if (!detectControl(featureControl, session.guildId, "pickup"))
|
|
2265
|
+
return;
|
|
1713
2266
|
if (!session.guildId) return session.text(".noGuild");
|
|
1714
|
-
if (!await canTogglePickup(session))
|
|
1715
|
-
|
|
2267
|
+
if (!await canTogglePickup(session))
|
|
2268
|
+
return session.text(".noPermission");
|
|
2269
|
+
if (!await canBotManage(session))
|
|
2270
|
+
return session.text("middleware.messages.notManager");
|
|
1716
2271
|
const finalLength = length || cfg.roomNumLength || 5;
|
|
1717
|
-
if (finalLength < 1 || finalLength > 20)
|
|
2272
|
+
if (finalLength < 1 || finalLength > 20)
|
|
2273
|
+
return session.text(".invalidLength");
|
|
1718
2274
|
session.channel.pickupLength = finalLength;
|
|
1719
2275
|
return session.text(".success", { length: finalLength });
|
|
1720
2276
|
});
|
|
1721
2277
|
ctx.command("关闭车牌检测").alias("关闭拾取").channelFields(["pickupLength", "pickupGrant"]).action(async ({ session }) => {
|
|
1722
|
-
if (!detectControl(featureControl, session.guildId, "pickup"))
|
|
2278
|
+
if (!detectControl(featureControl, session.guildId, "pickup"))
|
|
2279
|
+
return;
|
|
1723
2280
|
if (!session.guildId) return session.text(".noGuild");
|
|
1724
|
-
if (!await canTogglePickup(session))
|
|
2281
|
+
if (!await canTogglePickup(session))
|
|
2282
|
+
return session.text(".noPermission");
|
|
1725
2283
|
session.channel.pickupLength = 0;
|
|
1726
2284
|
return session.text(".success");
|
|
1727
2285
|
});
|
|
1728
2286
|
ctx.command("车牌检测状态").channelFields(["pickupLength", "pickupGrant"]).action(async ({ session }) => {
|
|
1729
|
-
if (!detectControl(featureControl, session.guildId, "pickup"))
|
|
2287
|
+
if (!detectControl(featureControl, session.guildId, "pickup"))
|
|
2288
|
+
return;
|
|
1730
2289
|
if (!session.guildId) return session.text(".noGuild");
|
|
1731
2290
|
return `${session.channel.pickupLength > 0 ? session.text(".enabled", { length: session.channel.pickupLength }) : session.text(".disabled")}
|
|
1732
|
-
${
|
|
2291
|
+
${await canBotManage(session) ? session.text(".managerEnabled") : session.text(".managerDisabled")}`;
|
|
1733
2292
|
});
|
|
1734
2293
|
}
|
|
1735
2294
|
ctx.middleware(async (session, next) => {
|
|
1736
2295
|
const elements = session.elements;
|
|
1737
2296
|
if (cfg.roomNumToName && session.guildId && detectControl(featureControl, session.guildId, "pickup")) {
|
|
1738
|
-
const channel = await session.observeChannel([
|
|
2297
|
+
const channel = await session.observeChannel([
|
|
2298
|
+
"pickupLength",
|
|
2299
|
+
"pickupGrant"
|
|
2300
|
+
]);
|
|
1739
2301
|
const targetLength = channel.pickupLength;
|
|
1740
2302
|
const content = session.content?.trim();
|
|
1741
2303
|
if (targetLength > 0 && new RegExp(`^\\d{${targetLength}}$`).test(content)) {
|
|
@@ -1747,7 +2309,9 @@ ${!!await canBotManage(session) ? session.text(".managerEnabled") : session.text
|
|
|
1747
2309
|
group_name: name2
|
|
1748
2310
|
});
|
|
1749
2311
|
if (response && response.retcode !== 0 && response.status !== "ok") {
|
|
1750
|
-
throw new Error(
|
|
2312
|
+
throw new Error(
|
|
2313
|
+
`OneBot 报错 (Code: ${response.retcode}): ${JSON.stringify(response)}`
|
|
2314
|
+
);
|
|
1751
2315
|
}
|
|
1752
2316
|
return response;
|
|
1753
2317
|
}
|
|
@@ -1788,7 +2352,11 @@ ${!!await canBotManage(session) ? session.text(".managerEnabled") : session.text
|
|
|
1788
2352
|
newName = contentDigits;
|
|
1789
2353
|
}
|
|
1790
2354
|
await setGroupName(newName);
|
|
1791
|
-
await session.send(
|
|
2355
|
+
await session.send(
|
|
2356
|
+
session.text("middleware.messages.editGroupNameSuccess", {
|
|
2357
|
+
content: newName
|
|
2358
|
+
})
|
|
2359
|
+
);
|
|
1792
2360
|
return;
|
|
1793
2361
|
} catch (e) {
|
|
1794
2362
|
starfxLogger.warn(`纯数字修改失败,尝试添加空格重试: ${content}`);
|
|
@@ -1810,7 +2378,12 @@ ${!!await canBotManage(session) ? session.text(".managerEnabled") : session.text
|
|
|
1810
2378
|
return spacedContent + " " + currentName;
|
|
1811
2379
|
})() : spacedContent;
|
|
1812
2380
|
await setGroupName(finalName);
|
|
1813
|
-
await session.send(
|
|
2381
|
+
await session.send(
|
|
2382
|
+
session.text("middleware.messages.editGroupNameRetrySuccess", {
|
|
2383
|
+
content,
|
|
2384
|
+
spacedContent: finalName
|
|
2385
|
+
})
|
|
2386
|
+
);
|
|
1814
2387
|
return;
|
|
1815
2388
|
} catch (retryError) {
|
|
1816
2389
|
starfxLogger.error(`尝试添加空格修改群名仍失败: ${retryError}`);
|
|
@@ -1847,27 +2420,27 @@ ${!!await canBotManage(session) ? session.text(".managerEnabled") : session.text
|
|
|
1847
2420
|
});
|
|
1848
2421
|
}
|
|
1849
2422
|
function initAssets() {
|
|
1850
|
-
const defaultAssetsDir =
|
|
1851
|
-
assetsDir =
|
|
1852
|
-
if (!
|
|
1853
|
-
const versionFile =
|
|
2423
|
+
const defaultAssetsDir = import_node_path6.default.join(__dirname, "../assets");
|
|
2424
|
+
assetsDir = import_node_path6.default.join(ctx.baseDir, "data/starfx-bot/assets");
|
|
2425
|
+
if (!fs4.existsSync(assetsDir)) fs4.mkdirSync(assetsDir, { recursive: true });
|
|
2426
|
+
const versionFile = import_node_path6.default.join(assetsDir, "plugin_version.json");
|
|
1854
2427
|
let localVersion = "0";
|
|
1855
|
-
if (
|
|
2428
|
+
if (fs4.existsSync(versionFile)) {
|
|
1856
2429
|
try {
|
|
1857
|
-
localVersion = JSON.parse(
|
|
2430
|
+
localVersion = JSON.parse(fs4.readFileSync(versionFile, "utf-8")).version || "0";
|
|
1858
2431
|
} catch {
|
|
1859
2432
|
}
|
|
1860
2433
|
}
|
|
1861
2434
|
const pluginVersion = package_default.version;
|
|
1862
2435
|
if (pluginVersion > localVersion) {
|
|
1863
2436
|
try {
|
|
1864
|
-
if (
|
|
1865
|
-
|
|
2437
|
+
if (fs4.existsSync(defaultAssetsDir)) {
|
|
2438
|
+
fs4.cpSync(defaultAssetsDir, assetsDir, {
|
|
1866
2439
|
recursive: true,
|
|
1867
2440
|
force: true
|
|
1868
2441
|
});
|
|
1869
2442
|
}
|
|
1870
|
-
|
|
2443
|
+
fs4.writeFileSync(
|
|
1871
2444
|
versionFile,
|
|
1872
2445
|
JSON.stringify({ version: pluginVersion })
|
|
1873
2446
|
);
|
|
@@ -1878,7 +2451,133 @@ ${!!await canBotManage(session) ? session.text(".managerEnabled") : session.text
|
|
|
1878
2451
|
}
|
|
1879
2452
|
__name(initAssets, "initAssets");
|
|
1880
2453
|
}
|
|
1881
|
-
|
|
2454
|
+
var fs4, import_node_path6, import_koishi5, import_mime_types, name, inject, baseDir, assetsDir, starfxLogger, usage, repeatContextMap, Config;
|
|
2455
|
+
var init_src = __esm({
|
|
2456
|
+
"src/index.ts"() {
|
|
2457
|
+
fs4 = __toESM(require("node:fs"));
|
|
2458
|
+
import_node_path6 = __toESM(require("node:path"));
|
|
2459
|
+
import_koishi5 = require("koishi");
|
|
2460
|
+
import_mime_types = __toESM(require("mime-types"));
|
|
2461
|
+
init_package();
|
|
2462
|
+
init_currencySearch();
|
|
2463
|
+
init_drawHead();
|
|
2464
|
+
init_getOriginImg();
|
|
2465
|
+
init_utils();
|
|
2466
|
+
name = "starfx-bot";
|
|
2467
|
+
inject = {
|
|
2468
|
+
optional: ["skia", "QhzySharp", "server"]
|
|
2469
|
+
};
|
|
2470
|
+
starfxLogger = new import_koishi5.Logger("starfx-bot");
|
|
2471
|
+
usage = `
|
|
2472
|
+
<h1>StarFreedomX的自用插件</h1>
|
|
2473
|
+
<h2>可选功能依赖:</h2>
|
|
2474
|
+
<h3><a href="/market?keyword=skia-canvas">skia</a></h3>
|
|
2475
|
+
<li>查汇率</li>
|
|
2476
|
+
<h3><a href="/market?keyword=@quanhuzeyu+sharp">QhzySharp</a></h3>
|
|
2477
|
+
<li>卖掉了</li>
|
|
2478
|
+
<li>封印</li>
|
|
2479
|
+
<li>bdbd</li>
|
|
2480
|
+
`;
|
|
2481
|
+
repeatContextMap = /* @__PURE__ */ new Map();
|
|
2482
|
+
Config = import_koishi5.Schema.intersect([
|
|
2483
|
+
import_koishi5.Schema.object({
|
|
2484
|
+
openLock: import_koishi5.Schema.boolean().default(true).description("开启明日方舟封印功能"),
|
|
2485
|
+
openSold: import_koishi5.Schema.boolean().default(true).description('开启闲鱼"卖掉了"功能'),
|
|
2486
|
+
bangdreamBorder: import_koishi5.Schema.boolean().default(true).description("开启BanG Dream!边框功能")
|
|
2487
|
+
}).description("绘图功能"),
|
|
2488
|
+
import_koishi5.Schema.object({
|
|
2489
|
+
record: import_koishi5.Schema.boolean().default(true).description("开启群语录功能"),
|
|
2490
|
+
tagWeight: import_koishi5.Schema.number().default(5).min(1).description("tag匹配时的权重,越高权重越大"),
|
|
2491
|
+
recordLink: import_koishi5.Schema.dict(
|
|
2492
|
+
import_koishi5.Schema.object({
|
|
2493
|
+
linkGroup: import_koishi5.Schema.string(),
|
|
2494
|
+
linkWeight: import_koishi5.Schema.number()
|
|
2495
|
+
})
|
|
2496
|
+
).role("table").description(
|
|
2497
|
+
"群组链接,使得群可以调用被链接群的语录,<br>可以配置权重,作为键的群自身的权重为100(注意格式为平台名:群组名)"
|
|
2498
|
+
),
|
|
2499
|
+
saveArchive: import_koishi5.Schema.boolean().default(false).description("开启入典功能").hidden()
|
|
2500
|
+
}).description("语录记录功能"),
|
|
2501
|
+
import_koishi5.Schema.object({
|
|
2502
|
+
autoToken: import_koishi5.Schema.boolean().default(false).description("开启自动获取 Token 功能(配合 image-tag-editor-web 使用)"),
|
|
2503
|
+
apiSecret: import_koishi5.Schema.string().description("Web 端调用 Bot API 的共享密钥"),
|
|
2504
|
+
ownerId: import_koishi5.Schema.string().description(
|
|
2505
|
+
"owner 的用户 ID(自动获得 owner 权限)"
|
|
2506
|
+
)
|
|
2507
|
+
}).description("Token 自动获取"),
|
|
2508
|
+
import_koishi5.Schema.object({
|
|
2509
|
+
roll: import_koishi5.Schema.boolean().default(true).description("开启roll随机数功能"),
|
|
2510
|
+
undo: import_koishi5.Schema.boolean().default(true).description("机器人撤回消息功能(只测试了qq的onebot适配器)"),
|
|
2511
|
+
echo: import_koishi5.Schema.boolean().default(true).description("echo回声洞功能"),
|
|
2512
|
+
echoBanner: import_koishi5.Schema.array(String).role("table").description("echo屏蔽词,对文本生效"),
|
|
2513
|
+
ready: import_koishi5.Schema.boolean().default(false).description("待机人数记录功能"),
|
|
2514
|
+
saveReadyAsFile: import_koishi5.Schema.string().description(
|
|
2515
|
+
"写入待机人数的本地地址,留空则不写入"
|
|
2516
|
+
),
|
|
2517
|
+
roomNumber: import_koishi5.Schema.boolean().default(false).description("主跑房间号记录功能"),
|
|
2518
|
+
saveRoomAsFile: import_koishi5.Schema.string().description(
|
|
2519
|
+
"写入房间号的本地地址,留空则不写入"
|
|
2520
|
+
),
|
|
2521
|
+
forward: import_koishi5.Schema.boolean().default(true).description("消息转发功能"),
|
|
2522
|
+
searchExchangeRate: import_koishi5.Schema.boolean().default(false).description("查汇率功能"),
|
|
2523
|
+
intervalGetExchangeRate: import_koishi5.Schema.boolean().default(false).description("汇率定时推送功能").hidden(),
|
|
2524
|
+
roomNumToName: import_koishi5.Schema.boolean().default(false).description("监听车牌修改群名功能"),
|
|
2525
|
+
roomNumLength: import_koishi5.Schema.number().default(5).description("默认车牌位数")
|
|
2526
|
+
}).description("指令小功能"),
|
|
2527
|
+
import_koishi5.Schema.object({
|
|
2528
|
+
atNotSay: import_koishi5.Schema.boolean().default(true).description("开启‘艾特我又不说话’功能"),
|
|
2529
|
+
atNotSayProperty: import_koishi5.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.5).description("'艾特我又不说话'回复概率"),
|
|
2530
|
+
atNotSayOther: import_koishi5.Schema.boolean().default(true).description("开启‘艾特他又不说话’功能"),
|
|
2531
|
+
atNotSayOtherProperty: import_koishi5.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.5).description("'艾特他又不说话'回复概率"),
|
|
2532
|
+
iLoveYou: import_koishi5.Schema.boolean().default(true).description("开启‘我喜欢你’功能"),
|
|
2533
|
+
replyBot: import_koishi5.Schema.union(["关闭", "无需at", "必须at"]).default("无需at").description("回复‘我才不是机器人!’功能")
|
|
2534
|
+
}).description("特定回应功能"),
|
|
2535
|
+
import_koishi5.Schema.object({
|
|
2536
|
+
sendLocalImage: import_koishi5.Schema.dict(
|
|
2537
|
+
import_koishi5.Schema.object({
|
|
2538
|
+
hiddenInHelp: import_koishi5.Schema.boolean(),
|
|
2539
|
+
imgPath: import_koishi5.Schema.string()
|
|
2540
|
+
})
|
|
2541
|
+
).role("table").description(
|
|
2542
|
+
"特定指令发送本地图片功能,其中键是指令名称,imgPath是图片文件的绝对路径"
|
|
2543
|
+
)
|
|
2544
|
+
}),
|
|
2545
|
+
import_koishi5.Schema.object({
|
|
2546
|
+
openRepeat: import_koishi5.Schema.boolean().default(true).description("开启复读功能"),
|
|
2547
|
+
minRepeatTimes: import_koishi5.Schema.number().default(2).description("最少重复次数"),
|
|
2548
|
+
repeatPossibility: import_koishi5.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.3).description("复读发生概率")
|
|
2549
|
+
}).description("复读功能"),
|
|
2550
|
+
import_koishi5.Schema.object({
|
|
2551
|
+
myId: import_koishi5.Schema.boolean().default(false).description("查询gid uid cid")
|
|
2552
|
+
}).description("我的信息查询"),
|
|
2553
|
+
import_koishi5.Schema.object({
|
|
2554
|
+
filePathToBase64: import_koishi5.Schema.boolean().default(false).description(
|
|
2555
|
+
"在消息发送前检查是否有file://,如果有那么转换为base64再发送"
|
|
2556
|
+
),
|
|
2557
|
+
originImg: import_koishi5.Schema.boolean().default(false).description("根据链接获取原图开关")
|
|
2558
|
+
}).description("自用功能"),
|
|
2559
|
+
import_koishi5.Schema.union([
|
|
2560
|
+
import_koishi5.Schema.object({
|
|
2561
|
+
originImg: import_koishi5.Schema.const(true).required(),
|
|
2562
|
+
originImgRSSUrl: import_koishi5.Schema.string().required().description("推特列表rss地址")
|
|
2563
|
+
}),
|
|
2564
|
+
import_koishi5.Schema.object({})
|
|
2565
|
+
]),
|
|
2566
|
+
import_koishi5.Schema.object({
|
|
2567
|
+
featureControl: import_koishi5.Schema.array(
|
|
2568
|
+
import_koishi5.Schema.object({
|
|
2569
|
+
functionName: import_koishi5.Schema.string(),
|
|
2570
|
+
whitelist: import_koishi5.Schema.boolean(),
|
|
2571
|
+
groups: import_koishi5.Schema.string()
|
|
2572
|
+
})
|
|
2573
|
+
).role("table").description(`黑/白名单配置,群组间用英文半角逗号分隔,<br>
|
|
2574
|
+
可配置功能键及用法详见 [项目地址](https://github.com/StarFreedomX/starfx-bot)或[npm发布页](https://www.npmjs.com/package/koishi-plugin-bangdream-ccg)`)
|
|
2575
|
+
}).description("高级配置")
|
|
2576
|
+
]);
|
|
2577
|
+
__name(apply, "apply");
|
|
2578
|
+
}
|
|
2579
|
+
});
|
|
2580
|
+
init_src();
|
|
1882
2581
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1883
2582
|
0 && (module.exports = {
|
|
1884
2583
|
Config,
|