koishi-plugin-class-score-system 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +305 -269
- package/package.json +1 -1
- package/lib/index.js.map +0 -7
package/lib/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
1
4
|
// src/index.ts
|
|
2
5
|
import { Schema } from "koishi";
|
|
3
6
|
|
|
@@ -43,9 +46,13 @@ function extendDatabase(ctx) {
|
|
|
43
46
|
unique: ["openId", "guildId"]
|
|
44
47
|
});
|
|
45
48
|
}
|
|
49
|
+
__name(extendDatabase, "extendDatabase");
|
|
46
50
|
|
|
47
51
|
// src/services/api.ts
|
|
48
52
|
var CsmsApiService = class {
|
|
53
|
+
static {
|
|
54
|
+
__name(this, "CsmsApiService");
|
|
55
|
+
}
|
|
49
56
|
baseUrl;
|
|
50
57
|
token;
|
|
51
58
|
ctx;
|
|
@@ -67,18 +74,18 @@ var CsmsApiService = class {
|
|
|
67
74
|
cleanParams[key] = String(value);
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
|
-
this.ctx.logger.debug(`API
|
|
77
|
+
this.ctx.logger.debug(`API请求: ${url}`, cleanParams);
|
|
71
78
|
const response = await this.ctx.http.get(url, {
|
|
72
79
|
params: cleanParams,
|
|
73
80
|
headers: {
|
|
74
81
|
"Authorization": this.token
|
|
75
82
|
}
|
|
76
83
|
});
|
|
77
|
-
this.ctx.logger.debug(`API
|
|
84
|
+
this.ctx.logger.debug(`API响应:`, response);
|
|
78
85
|
return response;
|
|
79
86
|
} catch (error) {
|
|
80
|
-
this.ctx.logger.error("CSMS API
|
|
81
|
-
return { error: error.message || "API
|
|
87
|
+
this.ctx.logger.error("CSMS API 请求失败:", error);
|
|
88
|
+
return { error: error.message || "API请求失败" };
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
91
|
/**
|
|
@@ -97,20 +104,20 @@ var CsmsApiService = class {
|
|
|
97
104
|
}),
|
|
98
105
|
token: this.token
|
|
99
106
|
};
|
|
100
|
-
this.ctx.logger.info(
|
|
107
|
+
this.ctx.logger.info(`验证Token中...`);
|
|
101
108
|
const response = await this.ctx.http.get(url, { params });
|
|
102
109
|
if (response.error) {
|
|
103
110
|
const errorLower = response.error.toLowerCase();
|
|
104
|
-
if (errorLower.includes("
|
|
105
|
-
this.ctx.logger.warn(`Token
|
|
111
|
+
if (errorLower.includes("未授权") || errorLower.includes("token") || errorLower.includes("无效")) {
|
|
112
|
+
this.ctx.logger.warn(`Token验证失败: ${response.error}`);
|
|
106
113
|
return { valid: false };
|
|
107
114
|
}
|
|
108
|
-
this.ctx.logger.info(`Token
|
|
115
|
+
this.ctx.logger.info(`Token验证成功`);
|
|
109
116
|
return { valid: true };
|
|
110
117
|
}
|
|
111
118
|
return { valid: true };
|
|
112
119
|
} catch (error) {
|
|
113
|
-
this.ctx.logger.error(`Token
|
|
120
|
+
this.ctx.logger.error(`Token验证异常:`, error);
|
|
114
121
|
return { valid: false };
|
|
115
122
|
}
|
|
116
123
|
}
|
|
@@ -227,12 +234,12 @@ var CsmsApiService = class {
|
|
|
227
234
|
* GET /api/global_api.php?action=add_score&data={"users":[...],"description":"xxx"}&token=xxx
|
|
228
235
|
*/
|
|
229
236
|
async batchAddScore(data) {
|
|
230
|
-
this.ctx.logger.info(`batchAddScore
|
|
237
|
+
this.ctx.logger.info(`batchAddScore 请求数据:`, JSON.stringify(data));
|
|
231
238
|
const response = await this.request({
|
|
232
239
|
action: "add_score",
|
|
233
240
|
data: JSON.stringify(data)
|
|
234
241
|
});
|
|
235
|
-
this.ctx.logger.info(`batchAddScore API
|
|
242
|
+
this.ctx.logger.info(`batchAddScore API 响应:`, JSON.stringify(response));
|
|
236
243
|
if (response.error) {
|
|
237
244
|
return {
|
|
238
245
|
success: false,
|
|
@@ -250,15 +257,15 @@ var CsmsApiService = class {
|
|
|
250
257
|
if (Array.isArray(response.data)) {
|
|
251
258
|
return {
|
|
252
259
|
success: true,
|
|
253
|
-
message: "
|
|
260
|
+
message: "操作完成",
|
|
254
261
|
summary: { success_count: response.data.length, failed_count: 0, total_count: response.data.length },
|
|
255
262
|
details: response.data
|
|
256
263
|
};
|
|
257
264
|
}
|
|
258
|
-
this.ctx.logger.error(`batchAddScore
|
|
265
|
+
this.ctx.logger.error(`batchAddScore 返回数据格式异常,原始响应:`, response);
|
|
259
266
|
return {
|
|
260
267
|
success: false,
|
|
261
|
-
message: "API
|
|
268
|
+
message: "API 返回数据格式异常",
|
|
262
269
|
summary: { success_count: 0, failed_count: 0, total_count: 0 },
|
|
263
270
|
details: []
|
|
264
271
|
};
|
|
@@ -276,6 +283,9 @@ var CsmsApiService = class {
|
|
|
276
283
|
|
|
277
284
|
// src/services/server.ts
|
|
278
285
|
var ServerConfigService = class {
|
|
286
|
+
static {
|
|
287
|
+
__name(this, "ServerConfigService");
|
|
288
|
+
}
|
|
279
289
|
ctx;
|
|
280
290
|
constructor(ctx) {
|
|
281
291
|
this.ctx = ctx;
|
|
@@ -331,14 +341,14 @@ var ServerConfigService = class {
|
|
|
331
341
|
return {
|
|
332
342
|
valid: true,
|
|
333
343
|
actualUsername: result.username,
|
|
334
|
-
error:
|
|
344
|
+
error: `警告:提供的用户名 "${username}" 与实际登录的管理员 "${result.username}" 不一致`
|
|
335
345
|
};
|
|
336
346
|
}
|
|
337
347
|
return { valid: true, actualUsername: result.username };
|
|
338
348
|
}
|
|
339
|
-
return { valid: false, error: "Token
|
|
349
|
+
return { valid: false, error: "Token 验证失败,请检查 Token 是否正确" };
|
|
340
350
|
} catch (error) {
|
|
341
|
-
this.ctx.logger.error("
|
|
351
|
+
this.ctx.logger.error("验证服务器配置失败:", error);
|
|
342
352
|
return { valid: false, error: error.message };
|
|
343
353
|
}
|
|
344
354
|
}
|
|
@@ -346,6 +356,9 @@ var ServerConfigService = class {
|
|
|
346
356
|
|
|
347
357
|
// src/services/binding.ts
|
|
348
358
|
var QqBindingService = class {
|
|
359
|
+
static {
|
|
360
|
+
__name(this, "QqBindingService");
|
|
361
|
+
}
|
|
349
362
|
ctx;
|
|
350
363
|
constructor(ctx) {
|
|
351
364
|
this.ctx = ctx;
|
|
@@ -405,6 +418,9 @@ var QqBindingService = class {
|
|
|
405
418
|
|
|
406
419
|
// src/services/group-admin.ts
|
|
407
420
|
var GroupAdminService = class {
|
|
421
|
+
static {
|
|
422
|
+
__name(this, "GroupAdminService");
|
|
423
|
+
}
|
|
408
424
|
ctx;
|
|
409
425
|
constructor(ctx) {
|
|
410
426
|
this.ctx = ctx;
|
|
@@ -493,36 +509,39 @@ var CONSTANTS = {
|
|
|
493
509
|
DESCRIPTION_MAX_LENGTH: 255
|
|
494
510
|
};
|
|
495
511
|
var ERROR_MESSAGES = {
|
|
496
|
-
NO_SERVER_CONFIG: "
|
|
497
|
-
INVALID_TOKEN: "
|
|
498
|
-
INVALID_QQ: "
|
|
499
|
-
INVALID_SCORE: "
|
|
500
|
-
INVALID_USERNAME: "
|
|
501
|
-
INVALID_DESCRIPTION: "
|
|
502
|
-
INVALID_LIMIT: "
|
|
503
|
-
SERVER_NOT_FOUND: "
|
|
504
|
-
USER_NOT_FOUND: "
|
|
505
|
-
USER_ALREADY_BOUND: "
|
|
506
|
-
USER_NOT_BOUND: "
|
|
507
|
-
API_REQUEST_FAILED: "API
|
|
508
|
-
INSUFFICIENT_PERMISSION: "
|
|
509
|
-
BINDING_FAILED: "
|
|
510
|
-
UNBINDING_FAILED: "
|
|
511
|
-
CONFIGURATION_FAILED: "
|
|
512
|
+
NO_SERVER_CONFIG: "未配置CSMS服务器,请先使用 /绑定服务器 命令配置",
|
|
513
|
+
INVALID_TOKEN: "无效的Token格式,Token必须由数字和英文大写字母组成",
|
|
514
|
+
INVALID_QQ: "无效的QQ号码格式",
|
|
515
|
+
INVALID_SCORE: "分数必须在 -1000 到 1000 之间",
|
|
516
|
+
INVALID_USERNAME: "用户名不能为空且长度不能超过50个字符",
|
|
517
|
+
INVALID_DESCRIPTION: "描述信息长度不能超过255个字符",
|
|
518
|
+
INVALID_LIMIT: "数量必须在 1 到 50 之间",
|
|
519
|
+
SERVER_NOT_FOUND: "服务器未找到",
|
|
520
|
+
USER_NOT_FOUND: "用户未找到",
|
|
521
|
+
USER_ALREADY_BOUND: "该QQ号已绑定其他用户",
|
|
522
|
+
USER_NOT_BOUND: "该QQ号未绑定任何用户",
|
|
523
|
+
API_REQUEST_FAILED: "API请求失败",
|
|
524
|
+
INSUFFICIENT_PERMISSION: "权限不足,仅管理员可执行此操作",
|
|
525
|
+
BINDING_FAILED: "绑定失败",
|
|
526
|
+
UNBINDING_FAILED: "解除绑定失败",
|
|
527
|
+
CONFIGURATION_FAILED: "配置保存失败"
|
|
512
528
|
};
|
|
513
529
|
var HELP_MESSAGES = {
|
|
514
|
-
BIND_SERVER: "
|
|
515
|
-
QUERY_SCORE: "
|
|
516
|
-
ADJUST_SCORE: "
|
|
517
|
-
BIND_QQ: "\
|
|
518
|
-
VIEW_BINDING: "
|
|
519
|
-
UNBIND_QQ: "
|
|
520
|
-
RANKING: "
|
|
521
|
-
STATISTICS: "
|
|
530
|
+
BIND_SERVER: "绑定CSMS服务器\n用法:/绑定服务器 <服务器地址> <管理员用户名> <管理员Token>\n示例:/绑定服务器 https://example.com admin ABC123",
|
|
531
|
+
QUERY_SCORE: "查询积分\n用法:/查询积分 [用户名]\n示例:/查询积分 张三",
|
|
532
|
+
ADJUST_SCORE: "调整积分\n用法:/调整积分 <用户名> <分数> <原因>\n示例:/调整积分 张三 +5 表现优秀",
|
|
533
|
+
BIND_QQ: "绑定QQ\n用法:/绑定QQ <用户名>\n示例:/绑定QQ 张三",
|
|
534
|
+
VIEW_BINDING: "查看绑定\n用法:/查看绑定 [用户名]\n示例:/查看绑定 张三",
|
|
535
|
+
UNBIND_QQ: "解除绑定\n用法:/解除绑定 <QQ号>\n示例:/解除绑定 123456789",
|
|
536
|
+
RANKING: "排行榜\n用法:/排行榜 [数量]\n示例:/排行榜 10",
|
|
537
|
+
STATISTICS: "统计\n用法:/统计 [用户名]\n示例:/统计 张三"
|
|
522
538
|
};
|
|
523
539
|
|
|
524
540
|
// src/utils/validator.ts
|
|
525
541
|
var Validator = class {
|
|
542
|
+
static {
|
|
543
|
+
__name(this, "Validator");
|
|
544
|
+
}
|
|
526
545
|
static validateToken(token) {
|
|
527
546
|
return CONSTANTS.TOKEN_PATTERN.test(token);
|
|
528
547
|
}
|
|
@@ -573,7 +592,7 @@ var Validator = class {
|
|
|
573
592
|
}
|
|
574
593
|
static validateLimitWithMessage(limit, max = CONSTANTS.RANKING_MAX_LIMIT) {
|
|
575
594
|
if (!this.validateLimit(limit, max)) {
|
|
576
|
-
return
|
|
595
|
+
return `数量必须在 1 到 ${max} 之间`;
|
|
577
596
|
}
|
|
578
597
|
return null;
|
|
579
598
|
}
|
|
@@ -581,30 +600,33 @@ var Validator = class {
|
|
|
581
600
|
|
|
582
601
|
// src/utils/formatter.ts
|
|
583
602
|
var Formatter = class {
|
|
603
|
+
static {
|
|
604
|
+
__name(this, "Formatter");
|
|
605
|
+
}
|
|
584
606
|
// QQ 消息单条最大长度限制(留有余量)
|
|
585
607
|
static MAX_MESSAGE_LENGTH = 1800;
|
|
586
608
|
static formatScoreInfo(user, ranking) {
|
|
587
609
|
return [
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
610
|
+
`用户: ${user.username}`,
|
|
611
|
+
`排名: ${ranking || "未知"}`,
|
|
612
|
+
`总积分: ${user.total_score}`,
|
|
613
|
+
`累计加分: ${user.add_score}`,
|
|
614
|
+
`累计扣分: ${user.deduct_score}`,
|
|
615
|
+
`记录数: ${user.score_count}`
|
|
594
616
|
].join("\n");
|
|
595
617
|
}
|
|
596
618
|
static formatRanking(users) {
|
|
597
619
|
if (users.length === 0) {
|
|
598
|
-
return "
|
|
620
|
+
return "暂无排名数据";
|
|
599
621
|
}
|
|
600
|
-
const lines = ["
|
|
622
|
+
const lines = ["【积分排行榜】"];
|
|
601
623
|
const displayUsers = users.slice(0, 20);
|
|
602
624
|
displayUsers.forEach((user, index) => {
|
|
603
|
-
const medal = index < 3 ? ["
|
|
604
|
-
lines.push(`${medal} ${user.username}: ${user.total_score}
|
|
625
|
+
const medal = index < 3 ? ["🥇", "🥈", "🥉"][index] : `${index + 1}.`;
|
|
626
|
+
lines.push(`${medal} ${user.username}: ${user.total_score}分`);
|
|
605
627
|
});
|
|
606
628
|
if (users.length > 20) {
|
|
607
|
-
lines.push(`...
|
|
629
|
+
lines.push(`... 共 ${users.length} 人,显示前20名`);
|
|
608
630
|
}
|
|
609
631
|
return lines.join("\n");
|
|
610
632
|
}
|
|
@@ -613,70 +635,70 @@ var Formatter = class {
|
|
|
613
635
|
*/
|
|
614
636
|
static formatRankingWithRank(users, startRank) {
|
|
615
637
|
if (users.length === 0) {
|
|
616
|
-
return "
|
|
638
|
+
return "暂无排名数据";
|
|
617
639
|
}
|
|
618
|
-
const lines = ["
|
|
640
|
+
const lines = ["【积分排行榜】"];
|
|
619
641
|
users.forEach((user, index) => {
|
|
620
642
|
const rank = startRank + index;
|
|
621
|
-
const medal = rank <= 3 ? ["
|
|
622
|
-
lines.push(`${medal} ${user.username}: ${user.total_score}
|
|
643
|
+
const medal = rank <= 3 ? ["🥇", "🥈", "🥉"][rank - 1] : `${rank}.`;
|
|
644
|
+
lines.push(`${medal} ${user.username}: ${user.total_score}分`);
|
|
623
645
|
});
|
|
624
646
|
return lines.join("\n");
|
|
625
647
|
}
|
|
626
648
|
static getMedal(index) {
|
|
627
|
-
const medals = ["
|
|
649
|
+
const medals = ["🥇", "🥈", "🥉"];
|
|
628
650
|
return medals[index] || `${index + 1}.`;
|
|
629
651
|
}
|
|
630
652
|
static formatScoreLogs(logs) {
|
|
631
653
|
if (logs.length === 0) {
|
|
632
|
-
return "
|
|
654
|
+
return "暂无积分记录";
|
|
633
655
|
}
|
|
634
|
-
const lines = ["
|
|
656
|
+
const lines = ["【积分记录】"];
|
|
635
657
|
const displayLogs = logs.slice(0, 10);
|
|
636
658
|
displayLogs.forEach((log) => {
|
|
637
659
|
const change = log.score_change > 0 ? `+${log.score_change}` : log.score_change;
|
|
638
|
-
lines.push(`${change}
|
|
660
|
+
lines.push(`${change}分 - ${log.description}`);
|
|
639
661
|
});
|
|
640
662
|
if (logs.length > 10) {
|
|
641
|
-
lines.push(`...
|
|
663
|
+
lines.push(`... 共 ${logs.length} 条记录`);
|
|
642
664
|
}
|
|
643
665
|
return lines.join("\n");
|
|
644
666
|
}
|
|
645
667
|
static formatStatistics(user, logs) {
|
|
646
668
|
const lines = [
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
"
|
|
669
|
+
`【${user.username} 的统计信息】`,
|
|
670
|
+
`总积分: ${user.total_score}`,
|
|
671
|
+
`累计加分: ${user.add_score}`,
|
|
672
|
+
`累计扣分: ${user.deduct_score}`,
|
|
673
|
+
`记录数: ${user.score_count}`,
|
|
674
|
+
"最近积分记录:"
|
|
653
675
|
];
|
|
654
676
|
const recentLogs = logs.slice(0, 5);
|
|
655
677
|
if (recentLogs.length > 0) {
|
|
656
678
|
recentLogs.forEach((log) => {
|
|
657
679
|
const change = log.score_change > 0 ? `+${log.score_change}` : log.score_change;
|
|
658
|
-
lines.push(`${change}
|
|
680
|
+
lines.push(`${change}分 - ${log.description}`);
|
|
659
681
|
});
|
|
660
682
|
} else {
|
|
661
|
-
lines.push("
|
|
683
|
+
lines.push("暂无记录");
|
|
662
684
|
}
|
|
663
685
|
return lines.join("\n");
|
|
664
686
|
}
|
|
665
687
|
static formatAddScoreResult(result) {
|
|
666
|
-
const status = result.success ? "
|
|
667
|
-
const summary =
|
|
688
|
+
const status = result.success ? "积分调整成功" : "积分调整失败";
|
|
689
|
+
const summary = `成功: ${result.summary.success_count} 条,失败: ${result.summary.failed_count} 条`;
|
|
668
690
|
const lines = [status, summary];
|
|
669
691
|
if (!result.success && result.message) {
|
|
670
|
-
lines.push(
|
|
692
|
+
lines.push(`原因: ${result.message}`);
|
|
671
693
|
}
|
|
672
694
|
if (result.details && result.details.length > 0) {
|
|
673
|
-
lines.push("
|
|
695
|
+
lines.push("详情:");
|
|
674
696
|
result.details.forEach((detail) => {
|
|
675
697
|
if (detail.success) {
|
|
676
698
|
const change = detail.score_change > 0 ? "+" : "";
|
|
677
|
-
lines.push(`[OK] ${detail.username}: ${change}${detail.score_change}
|
|
699
|
+
lines.push(`[OK] ${detail.username}: ${change}${detail.score_change}分`);
|
|
678
700
|
} else {
|
|
679
|
-
lines.push(`[FAIL] ${detail.username}: ${detail.error || "
|
|
701
|
+
lines.push(`[FAIL] ${detail.username}: ${detail.error || "失败"}`);
|
|
680
702
|
}
|
|
681
703
|
});
|
|
682
704
|
}
|
|
@@ -722,68 +744,69 @@ var Formatter = class {
|
|
|
722
744
|
|
|
723
745
|
// src/commands/bind-server.ts
|
|
724
746
|
function registerBindServerCommand(ctx, serverService, adminService) {
|
|
725
|
-
ctx.command("
|
|
747
|
+
ctx.command("绑定服务器 <地址:string> <用户名:string> <token:string>").alias("bind-server").action(async ({ session }, address, username, token) => {
|
|
726
748
|
if (!address || !username || !token) {
|
|
727
749
|
return HELP_MESSAGES.BIND_SERVER;
|
|
728
750
|
}
|
|
729
751
|
if (!session.guildId) {
|
|
730
|
-
return "
|
|
752
|
+
return "此命令只能在群聊中使用";
|
|
731
753
|
}
|
|
732
754
|
const openId = session.userId?.toString();
|
|
733
755
|
if (!openId) {
|
|
734
|
-
return "
|
|
756
|
+
return "无法获取您的用户ID";
|
|
735
757
|
}
|
|
736
758
|
const existingConfig = await serverService.getConfigByGuild(session.guildId);
|
|
737
759
|
if (existingConfig) {
|
|
738
|
-
return "
|
|
760
|
+
return "❌ 该群聊已绑定服务器,如需更换请先使用 /服务器解绑";
|
|
739
761
|
}
|
|
740
762
|
const tokenError = Validator.validateTokenWithMessage(token);
|
|
741
763
|
if (tokenError) {
|
|
742
|
-
ctx.logger.info(`Token
|
|
743
|
-
return `Token
|
|
764
|
+
ctx.logger.info(`Token 格式验证失败: ${tokenError}`);
|
|
765
|
+
return `Token 格式验证失败`;
|
|
744
766
|
}
|
|
745
767
|
try {
|
|
746
|
-
ctx.logger.info(
|
|
768
|
+
ctx.logger.info(`正在验证服务器配置...`);
|
|
747
769
|
const result = await serverService.validateConfig(address, username, token);
|
|
748
770
|
if (!result.valid) {
|
|
749
|
-
const errorMsg = result.error || "
|
|
750
|
-
ctx.logger.info(
|
|
751
|
-
return
|
|
771
|
+
const errorMsg = result.error || "服务器配置验证失败";
|
|
772
|
+
ctx.logger.info(`服务器配置验证失败: ${errorMsg}`);
|
|
773
|
+
return `验证失败: ${errorMsg}`;
|
|
752
774
|
}
|
|
753
775
|
const actualUsername = result.actualUsername || username;
|
|
754
|
-
const config = await serverService.saveConfig(session.guildId, "CSMS
|
|
755
|
-
await adminService.addAdmin(session.guildId, openId, "
|
|
756
|
-
ctx.logger.info(
|
|
776
|
+
const config = await serverService.saveConfig(session.guildId, "CSMS服务器", address, actualUsername, token);
|
|
777
|
+
await adminService.addAdmin(session.guildId, openId, "管理员");
|
|
778
|
+
ctx.logger.info(`群聊 ${session.guildId} 的首个绑定者 ${openId} 被自动添加为管理员`);
|
|
757
779
|
const domain = config.address.replace(/^https?:\/\//, "").split("/")[0];
|
|
758
|
-
ctx.logger.info(
|
|
759
|
-
return
|
|
780
|
+
ctx.logger.info(`服务器配置成功,domain=${domain},username=${actualUsername}`);
|
|
781
|
+
return `✅ 服务器配置成功`;
|
|
760
782
|
} catch (error) {
|
|
761
|
-
ctx.logger.error("
|
|
762
|
-
return
|
|
783
|
+
ctx.logger.error("保存服务器配置失败:", error);
|
|
784
|
+
return `配置保存失败: ${error.message}`;
|
|
763
785
|
}
|
|
764
786
|
});
|
|
765
|
-
ctx.command("
|
|
787
|
+
ctx.command("服务器解绑").alias("unbind-server").action(async ({ session }) => {
|
|
766
788
|
if (!session.guildId) {
|
|
767
|
-
return "
|
|
789
|
+
return "此命令只能在群聊中使用";
|
|
768
790
|
}
|
|
769
791
|
const hasPermission = await adminService.hasAdminPermission(session);
|
|
770
792
|
if (!hasPermission) {
|
|
771
|
-
return "
|
|
793
|
+
return "❌ 此命令仅限管理员使用";
|
|
772
794
|
}
|
|
773
795
|
const config = await serverService.getConfigByGuild(session.guildId);
|
|
774
796
|
if (!config) {
|
|
775
|
-
return "
|
|
797
|
+
return "❌ 该群聊尚未绑定任何服务器";
|
|
776
798
|
}
|
|
777
|
-
await session.send("
|
|
799
|
+
await session.send("⚠️ 确定要解除服务器绑定吗?解绑后其他人可重新绑定。请在 30 秒内输入「确认」来完成操作");
|
|
778
800
|
const reply = await session.prompt(3e4);
|
|
779
|
-
if (reply?.trim() !== "
|
|
780
|
-
return "
|
|
801
|
+
if (reply?.trim() !== "确认") {
|
|
802
|
+
return "✅ 已取消解绑操作";
|
|
781
803
|
}
|
|
782
804
|
await serverService.deleteConfig(session.guildId);
|
|
783
|
-
ctx.logger.info(
|
|
784
|
-
return
|
|
805
|
+
ctx.logger.info(`群聊 ${session.guildId} 解除了服务器绑定`);
|
|
806
|
+
return `✅ 已解除服务器绑定`;
|
|
785
807
|
});
|
|
786
808
|
}
|
|
809
|
+
__name(registerBindServerCommand, "registerBindServerCommand");
|
|
787
810
|
|
|
788
811
|
// src/utils/image.ts
|
|
789
812
|
import { h } from "koishi";
|
|
@@ -818,10 +841,11 @@ async function generateImage(ctx, html, options = {}) {
|
|
|
818
841
|
});
|
|
819
842
|
return screenshot;
|
|
820
843
|
} catch (error) {
|
|
821
|
-
ctx.logger.error("
|
|
844
|
+
ctx.logger.error("生成图片失败:", error);
|
|
822
845
|
return null;
|
|
823
846
|
}
|
|
824
847
|
}
|
|
848
|
+
__name(generateImage, "generateImage");
|
|
825
849
|
async function sendImageOrText(ctx, session, html, fallbackText, options = {}) {
|
|
826
850
|
const image = await generateImage(ctx, html, options);
|
|
827
851
|
if (image) {
|
|
@@ -830,6 +854,7 @@ async function sendImageOrText(ctx, session, html, fallbackText, options = {}) {
|
|
|
830
854
|
await session.send(fallbackText);
|
|
831
855
|
}
|
|
832
856
|
}
|
|
857
|
+
__name(sendImageOrText, "sendImageOrText");
|
|
833
858
|
function getBaseStyles() {
|
|
834
859
|
return `
|
|
835
860
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
@@ -877,12 +902,13 @@ function getBaseStyles() {
|
|
|
877
902
|
}
|
|
878
903
|
`;
|
|
879
904
|
}
|
|
905
|
+
__name(getBaseStyles, "getBaseStyles");
|
|
880
906
|
|
|
881
907
|
// src/commands/query-score.ts
|
|
882
908
|
function registerQueryScoreCommand(ctx, serverService, bindingService) {
|
|
883
|
-
ctx.command("
|
|
909
|
+
ctx.command("查询积分 [用户名:string]").alias("query-score").action(async ({ session }, username) => {
|
|
884
910
|
if (!session.guildId) {
|
|
885
|
-
return "
|
|
911
|
+
return "此命令只能在群聊中使用";
|
|
886
912
|
}
|
|
887
913
|
const api = await serverService.createApiService(session.guildId);
|
|
888
914
|
if (!api) {
|
|
@@ -893,11 +919,11 @@ function registerQueryScoreCommand(ctx, serverService, bindingService) {
|
|
|
893
919
|
if (!username) {
|
|
894
920
|
const openId = session.userId?.toString();
|
|
895
921
|
if (!openId) {
|
|
896
|
-
return "
|
|
922
|
+
return "无法获取您的用户ID";
|
|
897
923
|
}
|
|
898
924
|
const binding = await bindingService.getUserByOpenId(session.guildId, openId);
|
|
899
925
|
if (!binding) {
|
|
900
|
-
return `${ERROR_MESSAGES.USER_NOT_BOUND}
|
|
926
|
+
return `${ERROR_MESSAGES.USER_NOT_BOUND}。请先使用 /绑定QQ <用户名> 命令绑定您的账号`;
|
|
901
927
|
}
|
|
902
928
|
targetUsername = binding.username;
|
|
903
929
|
}
|
|
@@ -965,40 +991,41 @@ function registerQueryScoreCommand(ctx, serverService, bindingService) {
|
|
|
965
991
|
</head>
|
|
966
992
|
<body>
|
|
967
993
|
<div class="container">
|
|
968
|
-
<div class="title"
|
|
994
|
+
<div class="title">📊 积分查询结果</div>
|
|
969
995
|
<div class="username">${user.username}</div>
|
|
970
|
-
<div class="subtitle"
|
|
996
|
+
<div class="subtitle">用户ID: ${user.id}</div>
|
|
971
997
|
<div class="info-grid">
|
|
972
998
|
<div class="info-item">
|
|
973
|
-
<div class="info-label"
|
|
999
|
+
<div class="info-label">当前积分</div>
|
|
974
1000
|
<div class="info-value score">${user.total_score}</div>
|
|
975
1001
|
</div>
|
|
976
1002
|
<div class="info-item">
|
|
977
|
-
<div class="info-label"
|
|
1003
|
+
<div class="info-label">排行榜</div>
|
|
978
1004
|
<div class="info-value rank">#${ranking}</div>
|
|
979
1005
|
</div>
|
|
980
1006
|
</div>
|
|
981
|
-
<div class="footer"
|
|
1007
|
+
<div class="footer">班级操行分管理系统 v1.0</div>
|
|
982
1008
|
</div>
|
|
983
1009
|
</body>
|
|
984
1010
|
</html>`;
|
|
985
1011
|
await sendImageOrText(ctx, session, html, textResult, { height: 600 });
|
|
986
1012
|
return "";
|
|
987
1013
|
} catch (error) {
|
|
988
|
-
ctx.logger.error("
|
|
1014
|
+
ctx.logger.error("查询积分失败:", error);
|
|
989
1015
|
return ERROR_MESSAGES.API_REQUEST_FAILED;
|
|
990
1016
|
}
|
|
991
1017
|
});
|
|
992
1018
|
}
|
|
1019
|
+
__name(registerQueryScoreCommand, "registerQueryScoreCommand");
|
|
993
1020
|
|
|
994
1021
|
// src/commands/bind-qq.ts
|
|
995
1022
|
function registerBindQqCommand(ctx, serverService, bindingService) {
|
|
996
|
-
ctx.command("
|
|
1023
|
+
ctx.command("绑定QQ <用户名:string>").alias("bind-qq").action(async ({ session }, username) => {
|
|
997
1024
|
if (!username) {
|
|
998
1025
|
return HELP_MESSAGES.BIND_QQ;
|
|
999
1026
|
}
|
|
1000
1027
|
if (!session.guildId) {
|
|
1001
|
-
return "
|
|
1028
|
+
return "此命令只能在群聊中使用";
|
|
1002
1029
|
}
|
|
1003
1030
|
const usernameError = Validator.validateUsernameWithMessage(username);
|
|
1004
1031
|
if (usernameError) {
|
|
@@ -1006,9 +1033,9 @@ function registerBindQqCommand(ctx, serverService, bindingService) {
|
|
|
1006
1033
|
}
|
|
1007
1034
|
const openId = session.userId?.toString();
|
|
1008
1035
|
if (!openId) {
|
|
1009
|
-
return "
|
|
1036
|
+
return "无法获取您的用户ID";
|
|
1010
1037
|
}
|
|
1011
|
-
ctx.logger.info(
|
|
1038
|
+
ctx.logger.info(`获取到的用户 OpenID: ${openId}`);
|
|
1012
1039
|
const config = await serverService.getConfigByGuild(session.guildId);
|
|
1013
1040
|
if (!config) {
|
|
1014
1041
|
return ERROR_MESSAGES.NO_SERVER_CONFIG;
|
|
@@ -1016,11 +1043,11 @@ function registerBindQqCommand(ctx, serverService, bindingService) {
|
|
|
1016
1043
|
try {
|
|
1017
1044
|
const existingBinding = await bindingService.getUserByOpenId(session.guildId, openId);
|
|
1018
1045
|
if (existingBinding) {
|
|
1019
|
-
return
|
|
1046
|
+
return `❌ 您已绑定「${existingBinding.username}」,如需更换账号请先使用 /解除绑定 解绑后重新绑定`;
|
|
1020
1047
|
}
|
|
1021
1048
|
const existingBindings = await bindingService.getBindingsByUsername(session.guildId, username);
|
|
1022
1049
|
if (existingBindings.length > 0) {
|
|
1023
|
-
return
|
|
1050
|
+
return `❌ 用户名「${username}」已被其他QQ账号绑定,每个用户名只能绑定一个QQ`;
|
|
1024
1051
|
}
|
|
1025
1052
|
const api = await serverService.createApiService(session.guildId);
|
|
1026
1053
|
if (!api) {
|
|
@@ -1032,20 +1059,20 @@ function registerBindQqCommand(ctx, serverService, bindingService) {
|
|
|
1032
1059
|
}
|
|
1033
1060
|
const user = response.data[0];
|
|
1034
1061
|
await bindingService.bindQq(session.guildId, user.id, username, openId, config.id);
|
|
1035
|
-
return
|
|
1036
|
-
|
|
1062
|
+
return `✅ 绑定成功
|
|
1063
|
+
用户名: ${username}`;
|
|
1037
1064
|
} catch (error) {
|
|
1038
|
-
ctx.logger.error("
|
|
1065
|
+
ctx.logger.error("绑定QQ失败:", error);
|
|
1039
1066
|
return ERROR_MESSAGES.BINDING_FAILED;
|
|
1040
1067
|
}
|
|
1041
1068
|
});
|
|
1042
|
-
ctx.command("
|
|
1069
|
+
ctx.command("解除绑定").alias("unbind").action(async ({ session }, username) => {
|
|
1043
1070
|
if (!session.guildId) {
|
|
1044
|
-
return "
|
|
1071
|
+
return "此命令只能在群聊中使用";
|
|
1045
1072
|
}
|
|
1046
1073
|
const openId = session.userId?.toString();
|
|
1047
1074
|
if (!openId) {
|
|
1048
|
-
return "
|
|
1075
|
+
return "无法获取您的用户ID";
|
|
1049
1076
|
}
|
|
1050
1077
|
let binding = null;
|
|
1051
1078
|
if (username) {
|
|
@@ -1055,27 +1082,28 @@ function registerBindQqCommand(ctx, serverService, bindingService) {
|
|
|
1055
1082
|
binding = await bindingService.getUserByOpenId(session.guildId, openId);
|
|
1056
1083
|
}
|
|
1057
1084
|
if (!binding) {
|
|
1058
|
-
return
|
|
1085
|
+
return `❌ 您尚未绑定任何账号${username ? `(用户:${username})` : ""}`;
|
|
1059
1086
|
}
|
|
1060
|
-
await session.send(
|
|
1087
|
+
await session.send(`⚠️ 确定要解除「${binding.username}」的绑定吗?请在 30 秒内输入「确认」来完成操作`);
|
|
1061
1088
|
const reply = await session.prompt(3e4);
|
|
1062
|
-
if (reply?.trim() !== "
|
|
1063
|
-
return "
|
|
1089
|
+
if (reply?.trim() !== "确认") {
|
|
1090
|
+
return "✅ 已取消解绑操作";
|
|
1064
1091
|
}
|
|
1065
1092
|
await bindingService.unbindQq(session.guildId, openId);
|
|
1066
|
-
ctx.logger.info(
|
|
1067
|
-
return
|
|
1093
|
+
ctx.logger.info(`用户 ${binding.username}(OpenID: ${openId})解除了绑定`);
|
|
1094
|
+
return `✅ 已解除「${binding.username}」的绑定`;
|
|
1068
1095
|
});
|
|
1069
1096
|
}
|
|
1097
|
+
__name(registerBindQqCommand, "registerBindQqCommand");
|
|
1070
1098
|
|
|
1071
1099
|
// src/commands/adjust-score.ts
|
|
1072
1100
|
function registerAdjustScoreCommand(ctx, serverService, adminService) {
|
|
1073
|
-
ctx.command("
|
|
1101
|
+
ctx.command("调整积分 <用户名:string> <分数:number> <原因:text>").alias("adjust-score").action(async ({ session }, username, score, reason) => {
|
|
1074
1102
|
if (!username || score === void 0 || !reason) {
|
|
1075
1103
|
return HELP_MESSAGES.ADJUST_SCORE;
|
|
1076
1104
|
}
|
|
1077
1105
|
if (!session.guildId) {
|
|
1078
|
-
return "
|
|
1106
|
+
return "此命令只能在群聊中使用";
|
|
1079
1107
|
}
|
|
1080
1108
|
const hasPermission = await adminService.hasAdminPermission(session);
|
|
1081
1109
|
if (!hasPermission) {
|
|
@@ -1107,24 +1135,25 @@ function registerAdjustScoreCommand(ctx, serverService, adminService) {
|
|
|
1107
1135
|
],
|
|
1108
1136
|
description: reason
|
|
1109
1137
|
};
|
|
1110
|
-
ctx.logger.info(
|
|
1138
|
+
ctx.logger.info(`调整积分请求: 用户=${username}, 分数=${score}, 原因=${reason}`);
|
|
1111
1139
|
const result = await api.batchAddScore(data);
|
|
1112
|
-
ctx.logger.info(
|
|
1140
|
+
ctx.logger.info(`调整积分结果:`, JSON.stringify(result));
|
|
1113
1141
|
const message = Formatter.formatAddScoreResult(result);
|
|
1114
1142
|
return Formatter.splitMessage(message);
|
|
1115
1143
|
} catch (error) {
|
|
1116
|
-
ctx.logger.error("
|
|
1144
|
+
ctx.logger.error("调整积分失败:", error);
|
|
1117
1145
|
return ERROR_MESSAGES.API_REQUEST_FAILED;
|
|
1118
1146
|
}
|
|
1119
1147
|
});
|
|
1120
1148
|
}
|
|
1149
|
+
__name(registerAdjustScoreCommand, "registerAdjustScoreCommand");
|
|
1121
1150
|
|
|
1122
1151
|
// src/commands/ranking.ts
|
|
1123
1152
|
var PAGE_SIZE = 10;
|
|
1124
1153
|
function registerRankingCommand(ctx, serverService) {
|
|
1125
|
-
ctx.command("
|
|
1154
|
+
ctx.command("排行榜 [页码:number]").alias("ranking").action(async ({ session }, page = 1) => {
|
|
1126
1155
|
if (!session.guildId) {
|
|
1127
|
-
return "
|
|
1156
|
+
return "此命令只能在群聊中使用";
|
|
1128
1157
|
}
|
|
1129
1158
|
const api = await serverService.createApiService(session.guildId);
|
|
1130
1159
|
if (!api) {
|
|
@@ -1145,14 +1174,14 @@ function registerRankingCommand(ctx, serverService) {
|
|
|
1145
1174
|
return ERROR_MESSAGES.API_REQUEST_FAILED;
|
|
1146
1175
|
}
|
|
1147
1176
|
if (response.data.length === 0) {
|
|
1148
|
-
return
|
|
1177
|
+
return `暂无排行数据`;
|
|
1149
1178
|
}
|
|
1150
1179
|
const startRank = offset + 1;
|
|
1151
1180
|
const message = Formatter.formatRankingWithRank(response.data, startRank);
|
|
1152
|
-
const pageInfo = totalPages > 1 ?
|
|
1181
|
+
const pageInfo = totalPages > 1 ? `第 ${page}/${totalPages} 页,共 ${totalUsers} 人` : `共 ${totalUsers} 人`;
|
|
1153
1182
|
const rankItems = response.data.map((user, index) => {
|
|
1154
1183
|
const rank = startRank + index;
|
|
1155
|
-
const medal = rank === 1 ? "
|
|
1184
|
+
const medal = rank === 1 ? "🥇" : rank === 2 ? "🥈" : rank === 3 ? "🥉" : `${rank}.`;
|
|
1156
1185
|
const scoreColor = user.total_score >= 0 ? "#27ae60" : "#e74c3c";
|
|
1157
1186
|
return `
|
|
1158
1187
|
<div class="rank-item">
|
|
@@ -1216,32 +1245,33 @@ function registerRankingCommand(ctx, serverService) {
|
|
|
1216
1245
|
</head>
|
|
1217
1246
|
<body>
|
|
1218
1247
|
<div class="container">
|
|
1219
|
-
<div class="title"><span class="title-icon"
|
|
1248
|
+
<div class="title"><span class="title-icon">🏆</span>积分排行榜</div>
|
|
1220
1249
|
<div class="rank-list">
|
|
1221
1250
|
${rankItems}
|
|
1222
1251
|
</div>
|
|
1223
1252
|
<div class="page-info">${pageInfo}</div>
|
|
1224
|
-
<div class="footer"
|
|
1253
|
+
<div class="footer">班级操行分管理系统 v1.0</div>
|
|
1225
1254
|
</div>
|
|
1226
1255
|
</body>
|
|
1227
1256
|
</html>`;
|
|
1228
1257
|
const textResult = message + (totalPages > 1 ? `
|
|
1229
1258
|
|
|
1230
|
-
|
|
1259
|
+
📄 ${pageInfo}` : "");
|
|
1231
1260
|
await sendImageOrText(ctx, session, html, textResult, { height: 820 });
|
|
1232
1261
|
return "";
|
|
1233
1262
|
} catch (error) {
|
|
1234
|
-
ctx.logger.error("
|
|
1263
|
+
ctx.logger.error("获取排行榜失败:", error);
|
|
1235
1264
|
return ERROR_MESSAGES.API_REQUEST_FAILED;
|
|
1236
1265
|
}
|
|
1237
1266
|
});
|
|
1238
1267
|
}
|
|
1268
|
+
__name(registerRankingCommand, "registerRankingCommand");
|
|
1239
1269
|
|
|
1240
1270
|
// src/commands/statistics.ts
|
|
1241
1271
|
function registerStatisticsCommand(ctx, serverService, bindingService) {
|
|
1242
|
-
ctx.command("
|
|
1272
|
+
ctx.command("统计 [用户名:string]").alias("statistics").action(async ({ session }, username) => {
|
|
1243
1273
|
if (!session.guildId) {
|
|
1244
|
-
return "
|
|
1274
|
+
return "此命令只能在群聊中使用";
|
|
1245
1275
|
}
|
|
1246
1276
|
const api = await serverService.createApiService(session.guildId);
|
|
1247
1277
|
if (!api) {
|
|
@@ -1252,13 +1282,13 @@ function registerStatisticsCommand(ctx, serverService, bindingService) {
|
|
|
1252
1282
|
if (!username) {
|
|
1253
1283
|
const openId = session.userId?.toString();
|
|
1254
1284
|
if (!openId) {
|
|
1255
|
-
return "
|
|
1285
|
+
return "无法获取您的用户ID";
|
|
1256
1286
|
}
|
|
1257
1287
|
const binding = await bindingService.getUserByOpenId(session.guildId, openId);
|
|
1258
1288
|
if (!binding) {
|
|
1259
1289
|
return `${ERROR_MESSAGES.USER_NOT_BOUND}
|
|
1260
1290
|
|
|
1261
|
-
|
|
1291
|
+
请先使用 /绑定QQ <用户名> 命令绑定您的账号`;
|
|
1262
1292
|
}
|
|
1263
1293
|
targetUsername = binding.username;
|
|
1264
1294
|
}
|
|
@@ -1330,48 +1360,49 @@ function registerStatisticsCommand(ctx, serverService, bindingService) {
|
|
|
1330
1360
|
</head>
|
|
1331
1361
|
<body>
|
|
1332
1362
|
<div class="container">
|
|
1333
|
-
<div class="title"
|
|
1363
|
+
<div class="title">📊 积分统计</div>
|
|
1334
1364
|
<div class="user-info">
|
|
1335
1365
|
<div class="username">${user.username}</div>
|
|
1336
|
-
<div class="user-id"
|
|
1366
|
+
<div class="user-id">用户ID: ${user.id} | 当前积分: ${user.total_score}</div>
|
|
1337
1367
|
</div>
|
|
1338
1368
|
<div class="stats-grid">
|
|
1339
1369
|
<div class="stat-card positive">
|
|
1340
|
-
<div class="stat-label"
|
|
1370
|
+
<div class="stat-label">累计加分</div>
|
|
1341
1371
|
<div class="stat-value positive">+${addScore}</div>
|
|
1342
1372
|
</div>
|
|
1343
1373
|
<div class="stat-card negative">
|
|
1344
|
-
<div class="stat-label"
|
|
1374
|
+
<div class="stat-label">累计扣分</div>
|
|
1345
1375
|
<div class="stat-value negative">-${deductScore}</div>
|
|
1346
1376
|
</div>
|
|
1347
1377
|
<div class="stat-card total">
|
|
1348
|
-
<div class="stat-label"
|
|
1378
|
+
<div class="stat-label">总积分</div>
|
|
1349
1379
|
<div class="stat-value total">${totalScore}</div>
|
|
1350
1380
|
</div>
|
|
1351
1381
|
<div class="stat-card">
|
|
1352
|
-
<div class="stat-label"
|
|
1382
|
+
<div class="stat-label">操作次数</div>
|
|
1353
1383
|
<div class="stat-value">${scoreCount}</div>
|
|
1354
1384
|
</div>
|
|
1355
1385
|
</div>
|
|
1356
|
-
<div class="footer"
|
|
1386
|
+
<div class="footer">班级操行分管理系统 v1.0</div>
|
|
1357
1387
|
</div>
|
|
1358
1388
|
</body>
|
|
1359
1389
|
</html>`;
|
|
1360
1390
|
const textResult = [
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1391
|
+
`【${user.username} 的统计信息】`,
|
|
1392
|
+
`总积分: ${user.total_score}`,
|
|
1393
|
+
`累计加分: +${addScore}`,
|
|
1394
|
+
`累计扣分: -${deductScore}`,
|
|
1395
|
+
`操作次数: ${scoreCount}`
|
|
1366
1396
|
].join("\n");
|
|
1367
1397
|
await sendImageOrText(ctx, session, html, textResult, { height: 550 });
|
|
1368
1398
|
return "";
|
|
1369
1399
|
} catch (error) {
|
|
1370
|
-
ctx.logger.error("
|
|
1400
|
+
ctx.logger.error("获取统计信息失败:", error);
|
|
1371
1401
|
return ERROR_MESSAGES.API_REQUEST_FAILED;
|
|
1372
1402
|
}
|
|
1373
1403
|
});
|
|
1374
1404
|
}
|
|
1405
|
+
__name(registerStatisticsCommand, "registerStatisticsCommand");
|
|
1375
1406
|
|
|
1376
1407
|
// src/commands/admin-manager.ts
|
|
1377
1408
|
import { h as h2 } from "koishi";
|
|
@@ -1384,6 +1415,7 @@ function generateCode() {
|
|
|
1384
1415
|
}
|
|
1385
1416
|
return code;
|
|
1386
1417
|
}
|
|
1418
|
+
__name(generateCode, "generateCode");
|
|
1387
1419
|
function cleanupExpiredRequests() {
|
|
1388
1420
|
const now = Date.now();
|
|
1389
1421
|
for (const [code, req] of pendingRequests) {
|
|
@@ -1392,6 +1424,7 @@ function cleanupExpiredRequests() {
|
|
|
1392
1424
|
}
|
|
1393
1425
|
}
|
|
1394
1426
|
}
|
|
1427
|
+
__name(cleanupExpiredRequests, "cleanupExpiredRequests");
|
|
1395
1428
|
setInterval(cleanupExpiredRequests, 3e4);
|
|
1396
1429
|
function registerGroupAdminCommand(ctx, adminService) {
|
|
1397
1430
|
function getAtId(session) {
|
|
@@ -1403,17 +1436,18 @@ function registerGroupAdminCommand(ctx, adminService) {
|
|
|
1403
1436
|
}
|
|
1404
1437
|
return "";
|
|
1405
1438
|
}
|
|
1406
|
-
|
|
1439
|
+
__name(getAtId, "getAtId");
|
|
1440
|
+
ctx.command("加管 [备注:string]").alias("add-admin").action(async ({ session }, remark) => {
|
|
1407
1441
|
if (!session.guildId) {
|
|
1408
|
-
return "
|
|
1442
|
+
return "此命令只能在群聊中使用";
|
|
1409
1443
|
}
|
|
1410
1444
|
const hasPermission = await adminService.hasAdminPermission(session);
|
|
1411
1445
|
if (!hasPermission) {
|
|
1412
|
-
return "
|
|
1446
|
+
return "❌ 此命令仅限管理员使用";
|
|
1413
1447
|
}
|
|
1414
1448
|
const targetId = getAtId(session);
|
|
1415
1449
|
if (!targetId) {
|
|
1416
|
-
return "
|
|
1450
|
+
return "请用 @ 指定要添加为群管的用户:/加管 @某人";
|
|
1417
1451
|
}
|
|
1418
1452
|
cleanupExpiredRequests();
|
|
1419
1453
|
for (const [, req] of pendingRequests) {
|
|
@@ -1421,11 +1455,11 @@ function registerGroupAdminCommand(ctx, adminService) {
|
|
|
1421
1455
|
const admins = await adminService.getAllAdmins(session.guildId);
|
|
1422
1456
|
const existing = admins.find((a) => a.verify === 0);
|
|
1423
1457
|
if (existing) {
|
|
1424
|
-
return
|
|
1458
|
+
return `❌ 该用户已有正在等待确认的加管请求`;
|
|
1425
1459
|
}
|
|
1426
1460
|
}
|
|
1427
1461
|
}
|
|
1428
|
-
const adminRemark = remark || "
|
|
1462
|
+
const adminRemark = remark || "未命名";
|
|
1429
1463
|
const code = generateCode();
|
|
1430
1464
|
pendingRequests.set(code, {
|
|
1431
1465
|
guildId: session.guildId,
|
|
@@ -1433,96 +1467,96 @@ function registerGroupAdminCommand(ctx, adminService) {
|
|
|
1433
1467
|
createdAt: Date.now()
|
|
1434
1468
|
});
|
|
1435
1469
|
await session.send(
|
|
1436
|
-
|
|
1437
|
-
` + h2.at(targetId) + `
|
|
1438
|
-
|
|
1439
|
-
|
|
1470
|
+
`✅ 已发起加管请求
|
|
1471
|
+
` + h2.at(targetId) + ` 请发送 /确认 ${code} 同意加管
|
|
1472
|
+
备注:${adminRemark}
|
|
1473
|
+
(120秒内有效)`
|
|
1440
1474
|
);
|
|
1441
1475
|
return "";
|
|
1442
1476
|
});
|
|
1443
|
-
ctx.command("
|
|
1477
|
+
ctx.command("确认 [确认码:string]").alias("confirm").action(async ({ session }, code) => {
|
|
1444
1478
|
if (!session.guildId) {
|
|
1445
|
-
return "
|
|
1479
|
+
return "此命令只能在群聊中使用";
|
|
1446
1480
|
}
|
|
1447
1481
|
const openId = session.userId?.toString();
|
|
1448
1482
|
if (!openId) {
|
|
1449
|
-
return "
|
|
1483
|
+
return "无法获取您的用户ID";
|
|
1450
1484
|
}
|
|
1451
1485
|
if (!code) {
|
|
1452
|
-
return "
|
|
1486
|
+
return "请提供确认码:/确认 [确认码]";
|
|
1453
1487
|
}
|
|
1454
1488
|
cleanupExpiredRequests();
|
|
1455
1489
|
const req = pendingRequests.get(code);
|
|
1456
1490
|
if (!req) {
|
|
1457
|
-
return "
|
|
1491
|
+
return "❌ 无效的确认码或已过期";
|
|
1458
1492
|
}
|
|
1459
1493
|
if (req.guildId !== session.guildId) {
|
|
1460
|
-
return "
|
|
1494
|
+
return "❌ 请在发起加管请求的群聊中确认";
|
|
1461
1495
|
}
|
|
1462
1496
|
const remark = req.remark;
|
|
1463
1497
|
pendingRequests.delete(code);
|
|
1464
1498
|
await adminService.createPendingAdmin(session.guildId, openId, remark);
|
|
1465
|
-
ctx.logger.info(
|
|
1499
|
+
ctx.logger.info(`群聊 ${session.guildId} 用户同意加管: OpenID=${openId}, 备注=${remark}, Verify=0`);
|
|
1466
1500
|
await session.send(
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1501
|
+
`✅ 您已同意加管
|
|
1502
|
+
您的 OpenID:${openId}
|
|
1503
|
+
备注:${remark}
|
|
1504
|
+
等待管理员执行 /加管确认 完成加管
|
|
1505
|
+
(120秒内有效)`
|
|
1472
1506
|
);
|
|
1473
1507
|
return "";
|
|
1474
1508
|
});
|
|
1475
|
-
ctx.command("
|
|
1509
|
+
ctx.command("加管确认 [确认码:string]").alias("confirm-add-admin").action(async ({ session }, code) => {
|
|
1476
1510
|
if (!session.guildId) {
|
|
1477
|
-
return "
|
|
1511
|
+
return "此命令只能在群聊中使用";
|
|
1478
1512
|
}
|
|
1479
1513
|
const hasPermission = await adminService.hasAdminPermission(session);
|
|
1480
1514
|
if (!hasPermission) {
|
|
1481
|
-
return "
|
|
1515
|
+
return "❌ 此命令仅限管理员使用";
|
|
1482
1516
|
}
|
|
1483
1517
|
if (!code) {
|
|
1484
|
-
return "
|
|
1518
|
+
return "请提供确认码:/加管确认 [确认码]";
|
|
1485
1519
|
}
|
|
1486
1520
|
const admins = await adminService.getAllAdmins(session.guildId);
|
|
1487
1521
|
const pendingAdmin = admins.find((a) => a.verify === 0);
|
|
1488
1522
|
if (!pendingAdmin) {
|
|
1489
|
-
return "
|
|
1523
|
+
return "❌ 没有待确认的加管请求";
|
|
1490
1524
|
}
|
|
1491
1525
|
await adminService.confirmAdmin(session.guildId, pendingAdmin.openId);
|
|
1492
|
-
ctx.logger.info(
|
|
1493
|
-
return
|
|
1494
|
-
|
|
1495
|
-
OpenID
|
|
1526
|
+
ctx.logger.info(`群聊 ${session.guildId} 添加群管完成: OpenID=${pendingAdmin.openId}, 备注=${pendingAdmin.remark}, Verify=1`);
|
|
1527
|
+
return `✅ 加管成功
|
|
1528
|
+
备注:${pendingAdmin.remark}
|
|
1529
|
+
OpenID:${pendingAdmin.openId}`;
|
|
1496
1530
|
});
|
|
1497
|
-
ctx.command("
|
|
1531
|
+
ctx.command("减管 [备注:string]").alias("remove-admin").action(async ({ session }, remark) => {
|
|
1498
1532
|
if (!session.guildId) {
|
|
1499
|
-
return "
|
|
1533
|
+
return "此命令只能在群聊中使用";
|
|
1500
1534
|
}
|
|
1501
1535
|
const hasPermission = await adminService.hasAdminPermission(session);
|
|
1502
1536
|
if (!hasPermission) {
|
|
1503
|
-
return "
|
|
1537
|
+
return "❌ 此命令仅限管理员使用";
|
|
1504
1538
|
}
|
|
1505
1539
|
if (!remark) {
|
|
1506
|
-
return "
|
|
1540
|
+
return "请提供要移除的群管备注:/减管 [备注]";
|
|
1507
1541
|
}
|
|
1508
1542
|
await adminService.removeAdminByRemark(session.guildId, remark);
|
|
1509
|
-
ctx.logger.info(
|
|
1510
|
-
return
|
|
1511
|
-
|
|
1543
|
+
ctx.logger.info(`群聊 ${session.guildId} 移除群管: 备注=${remark}`);
|
|
1544
|
+
return `✅ 已移除群管
|
|
1545
|
+
备注:${remark}`;
|
|
1512
1546
|
});
|
|
1513
|
-
ctx.command("
|
|
1547
|
+
ctx.command("群管列表").alias("admin-list").action(async ({ session }) => {
|
|
1514
1548
|
if (!session.guildId) {
|
|
1515
|
-
return "
|
|
1549
|
+
return "此命令只能在群聊中使用";
|
|
1516
1550
|
}
|
|
1517
1551
|
const admins = await adminService.getAllAdmins(session.guildId);
|
|
1518
1552
|
if (admins.length === 0) {
|
|
1519
|
-
return "
|
|
1553
|
+
return "当前群聊没有管理员";
|
|
1520
1554
|
}
|
|
1521
1555
|
const list = admins.map(
|
|
1522
|
-
(admin, index) => `${index + 1}. [${admin.remark}] ${admin.openId} ${admin.verify === 1 ? "
|
|
1556
|
+
(admin, index) => `${index + 1}. [${admin.remark}] ${admin.openId} ${admin.verify === 1 ? "✅" : "⏳"}`
|
|
1523
1557
|
).join("\n");
|
|
1524
1558
|
const adminItems = admins.map((admin, index) => {
|
|
1525
|
-
const status = admin.verify === 1 ? "
|
|
1559
|
+
const status = admin.verify === 1 ? "✅ 已验证" : "⏳ 待确认";
|
|
1526
1560
|
const statusColor = admin.verify === 1 ? "#27ae60" : "#f39c12";
|
|
1527
1561
|
return `
|
|
1528
1562
|
<div class="admin-item">
|
|
@@ -1603,31 +1637,32 @@ OpenID\uFF1A${pendingAdmin.openId}`;
|
|
|
1603
1637
|
</head>
|
|
1604
1638
|
<body>
|
|
1605
1639
|
<div class="container">
|
|
1606
|
-
<div class="title"
|
|
1607
|
-
<div class="count-badge"
|
|
1640
|
+
<div class="title">👥 群管列表</div>
|
|
1641
|
+
<div class="count-badge">共 ${admins.length} 位管理员</div>
|
|
1608
1642
|
<div class="admin-list">
|
|
1609
1643
|
${adminItems}
|
|
1610
1644
|
</div>
|
|
1611
|
-
<div class="footer"
|
|
1645
|
+
<div class="footer">班级操行分管理系统 v1.0</div>
|
|
1612
1646
|
</div>
|
|
1613
1647
|
</body>
|
|
1614
1648
|
</html>`;
|
|
1615
|
-
await sendImageOrText(ctx, session, html,
|
|
1649
|
+
await sendImageOrText(ctx, session, html, `当前群聊的管理员:
|
|
1616
1650
|
${list}`, { height: 600 });
|
|
1617
1651
|
return "";
|
|
1618
1652
|
});
|
|
1619
|
-
ctx.command("
|
|
1653
|
+
ctx.command("我的ID").alias("my-id").action(async ({ session }) => {
|
|
1620
1654
|
if (!session.guildId) {
|
|
1621
|
-
return "
|
|
1655
|
+
return "此命令只能在群聊中使用";
|
|
1622
1656
|
}
|
|
1623
1657
|
const openId = session.userId?.toString();
|
|
1624
1658
|
if (!openId) {
|
|
1625
|
-
return "
|
|
1659
|
+
return "无法获取您的用户ID";
|
|
1626
1660
|
}
|
|
1627
|
-
return
|
|
1661
|
+
return `您的 OpenID:
|
|
1628
1662
|
${openId}`;
|
|
1629
1663
|
});
|
|
1630
1664
|
}
|
|
1665
|
+
__name(registerGroupAdminCommand, "registerGroupAdminCommand");
|
|
1631
1666
|
|
|
1632
1667
|
// src/commands/menu.ts
|
|
1633
1668
|
import { h as h3 } from "koishi";
|
|
@@ -1701,7 +1736,7 @@ var MENU_HTML = `
|
|
|
1701
1736
|
border-bottom: none;
|
|
1702
1737
|
}
|
|
1703
1738
|
.command-item::before {
|
|
1704
|
-
content: '
|
|
1739
|
+
content: '•';
|
|
1705
1740
|
color: #9b59b6;
|
|
1706
1741
|
font-weight: bold;
|
|
1707
1742
|
margin-right: 6px;
|
|
@@ -1734,80 +1769,80 @@ var MENU_HTML = `
|
|
|
1734
1769
|
</head>
|
|
1735
1770
|
<body>
|
|
1736
1771
|
<div class="container">
|
|
1737
|
-
<div class="title"
|
|
1772
|
+
<div class="title">📋 班级操行分管理系统菜单</div>
|
|
1738
1773
|
|
|
1739
1774
|
<div class="section">
|
|
1740
|
-
<div class="section-title"
|
|
1775
|
+
<div class="section-title">🛠️ 基础指令</div>
|
|
1741
1776
|
<ul class="command-list">
|
|
1742
1777
|
<li class="command-item">
|
|
1743
|
-
<span class="command-name"
|
|
1744
|
-
<span class="command-desc">[
|
|
1778
|
+
<span class="command-name">/查询积分</span>
|
|
1779
|
+
<span class="command-desc">[用户名]</span>
|
|
1745
1780
|
</li>
|
|
1746
1781
|
<li class="command-item">
|
|
1747
|
-
<span class="command-name"
|
|
1748
|
-
<span class="command-desc"><
|
|
1782
|
+
<span class="command-name">/绑定QQ</span>
|
|
1783
|
+
<span class="command-desc"><用户名></span>
|
|
1749
1784
|
</li>
|
|
1750
1785
|
<li class="command-item">
|
|
1751
|
-
<span class="command-name"
|
|
1752
|
-
<span class="command-desc">[
|
|
1786
|
+
<span class="command-name">/排行榜</span>
|
|
1787
|
+
<span class="command-desc">[数量]</span>
|
|
1753
1788
|
</li>
|
|
1754
1789
|
<li class="command-item">
|
|
1755
|
-
<span class="command-name"
|
|
1756
|
-
<span class="command-desc">[
|
|
1790
|
+
<span class="command-name">/统计</span>
|
|
1791
|
+
<span class="command-desc">[用户名]</span>
|
|
1757
1792
|
</li>
|
|
1758
1793
|
</ul>
|
|
1759
1794
|
</div>
|
|
1760
1795
|
|
|
1761
1796
|
<div class="section">
|
|
1762
|
-
<div class="section-title"
|
|
1797
|
+
<div class="section-title">⚙️ 管理员指令</div>
|
|
1763
1798
|
<ul class="command-list">
|
|
1764
1799
|
<li class="command-item">
|
|
1765
|
-
<span class="command-name"
|
|
1766
|
-
<span class="command-desc"><
|
|
1800
|
+
<span class="command-name">/绑定服务器</span>
|
|
1801
|
+
<span class="command-desc"><地址> <用户> <token></span>
|
|
1767
1802
|
</li>
|
|
1768
1803
|
<li class="command-item">
|
|
1769
|
-
<span class="command-name"
|
|
1804
|
+
<span class="command-name">/服务器解绑</span>
|
|
1770
1805
|
</li>
|
|
1771
1806
|
<li class="command-item">
|
|
1772
|
-
<span class="command-name"
|
|
1773
|
-
<span class="command-desc"><
|
|
1807
|
+
<span class="command-name">/调整积分</span>
|
|
1808
|
+
<span class="command-desc"><用户> <分数> <原因></span>
|
|
1774
1809
|
</li>
|
|
1775
1810
|
</ul>
|
|
1776
1811
|
</div>
|
|
1777
1812
|
|
|
1778
1813
|
<div class="section">
|
|
1779
|
-
<div class="section-title"
|
|
1814
|
+
<div class="section-title">👤 群管指令</div>
|
|
1780
1815
|
<ul class="command-list">
|
|
1781
1816
|
<li class="command-item">
|
|
1782
|
-
<span class="command-name"
|
|
1783
|
-
<span class="command-desc"
|
|
1817
|
+
<span class="command-name">/加管</span>
|
|
1818
|
+
<span class="command-desc">@某人 <备注></span>
|
|
1784
1819
|
</li>
|
|
1785
1820
|
<li class="command-item">
|
|
1786
|
-
<span class="command-name"
|
|
1787
|
-
<span class="command-desc"><
|
|
1821
|
+
<span class="command-name">/确认</span>
|
|
1822
|
+
<span class="command-desc"><验证码></span>
|
|
1788
1823
|
</li>
|
|
1789
1824
|
<li class="command-item">
|
|
1790
|
-
<span class="command-name"
|
|
1825
|
+
<span class="command-name">/加管确认</span>
|
|
1791
1826
|
</li>
|
|
1792
1827
|
<li class="command-item">
|
|
1793
|
-
<span class="command-name"
|
|
1794
|
-
<span class="command-desc"><
|
|
1828
|
+
<span class="command-name">/减管</span>
|
|
1829
|
+
<span class="command-desc"><备注></span>
|
|
1795
1830
|
</li>
|
|
1796
1831
|
<li class="command-item">
|
|
1797
|
-
<span class="command-name"
|
|
1832
|
+
<span class="command-name">/群管列表</span>
|
|
1798
1833
|
</li>
|
|
1799
1834
|
</ul>
|
|
1800
1835
|
</div>
|
|
1801
1836
|
|
|
1802
|
-
<div class="footer"
|
|
1837
|
+
<div class="footer">班级操行分管理系统 v1.0</div>
|
|
1803
1838
|
</div>
|
|
1804
1839
|
</body>
|
|
1805
1840
|
</html>
|
|
1806
1841
|
`;
|
|
1807
1842
|
function registerMenuCommand(ctx) {
|
|
1808
|
-
ctx.command("
|
|
1843
|
+
ctx.command("菜单").alias("menu").action(async ({ session }) => {
|
|
1809
1844
|
if (!session.guildId) {
|
|
1810
|
-
return "
|
|
1845
|
+
return "此命令只能在群聊中使用";
|
|
1811
1846
|
}
|
|
1812
1847
|
try {
|
|
1813
1848
|
const page = await ctx.puppeteer.page();
|
|
@@ -1825,44 +1860,45 @@ function registerMenuCommand(ctx) {
|
|
|
1825
1860
|
await session.send(h3.image(screenshot, "image/png"));
|
|
1826
1861
|
return "";
|
|
1827
1862
|
} catch (error) {
|
|
1828
|
-
ctx.logger.error("
|
|
1829
|
-
return
|
|
1863
|
+
ctx.logger.error("生成菜单图片失败:", error);
|
|
1864
|
+
return `📋 **班级操行分管理系统菜单**
|
|
1830
1865
|
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1866
|
+
━━━━━━━━━━━━━━━━━━━━━━
|
|
1867
|
+
🛠️ **基础指令**
|
|
1868
|
+
━━━━━━━━━━━━━━━━━━━━━━
|
|
1869
|
+
• /查询积分 [用户名] - 查询积分和排名
|
|
1870
|
+
• /绑定QQ <用户名> - 绑定QQ号与系统账号
|
|
1871
|
+
• /排行榜 [数量] - 显示积分排行榜
|
|
1872
|
+
• /统计 [用户名] - 显示积分统计信息
|
|
1838
1873
|
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1874
|
+
━━━━━━━━━━━━━━━━━━━━━━
|
|
1875
|
+
⚙️ **管理员指令**
|
|
1876
|
+
━━━━━━━━━━━━━━━━━━━━━━
|
|
1877
|
+
• /绑定服务器 <地址> <用户名> <token> - 绑定CSMS服务器
|
|
1878
|
+
• /服务器解绑 - 解除服务器绑定
|
|
1879
|
+
• /调整积分 <用户名> <分数> <原因> - 调整积分
|
|
1845
1880
|
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1881
|
+
━━━━━━━━━━━━━━━━━━━━━━
|
|
1882
|
+
👤 **群管指令**
|
|
1883
|
+
━━━━━━━━━━━━━━━━━━━━━━
|
|
1884
|
+
• /加管 @某人 <备注> - 添加管理员
|
|
1885
|
+
• /确认 <验证码> - 确认加管
|
|
1886
|
+
• /加管确认 - 完成加管流程
|
|
1887
|
+
• /减管 <备注> - 移除管理员
|
|
1888
|
+
• /群管列表 - 显示管理员列表
|
|
1854
1889
|
|
|
1855
|
-
|
|
1890
|
+
━━━━━━━━━━━━━━━━━━━━━━`;
|
|
1856
1891
|
}
|
|
1857
1892
|
});
|
|
1858
1893
|
}
|
|
1894
|
+
__name(registerMenuCommand, "registerMenuCommand");
|
|
1859
1895
|
|
|
1860
1896
|
// src/index.ts
|
|
1861
1897
|
var name = "class-score-system";
|
|
1862
1898
|
var inject = ["database", "puppeteer"];
|
|
1863
1899
|
var Config = Schema.object({});
|
|
1864
1900
|
function apply(ctx) {
|
|
1865
|
-
ctx.logger.info("
|
|
1901
|
+
ctx.logger.info("班级操行分管理系统插件正在加载...");
|
|
1866
1902
|
extendDatabase(ctx);
|
|
1867
1903
|
const serverService = new ServerConfigService(ctx);
|
|
1868
1904
|
const bindingService = new QqBindingService(ctx);
|
|
@@ -1875,12 +1911,12 @@ function apply(ctx) {
|
|
|
1875
1911
|
registerRankingCommand(ctx, serverService);
|
|
1876
1912
|
registerStatisticsCommand(ctx, serverService, bindingService);
|
|
1877
1913
|
registerMenuCommand(ctx);
|
|
1878
|
-
ctx.logger.info("
|
|
1914
|
+
ctx.logger.info("班级操行分管理系统插件加载完成");
|
|
1879
1915
|
}
|
|
1916
|
+
__name(apply, "apply");
|
|
1880
1917
|
export {
|
|
1881
1918
|
Config,
|
|
1882
1919
|
apply,
|
|
1883
1920
|
inject,
|
|
1884
1921
|
name
|
|
1885
1922
|
};
|
|
1886
|
-
//# sourceMappingURL=index.js.map
|