koishi-plugin-bind-bot 2.0.0 → 2.0.2

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.
@@ -56,10 +56,6 @@ export declare class WhitelistHandler extends BaseHandler {
56
56
  * 私有辅助方法:计算两个字符串的相似度
57
57
  */
58
58
  private similarityScore;
59
- /**
60
- * 私有辅助方法:计算Levenshtein距离
61
- */
62
- private levenshteinDistance;
63
59
  /**
64
60
  * 私有辅助方法:检查用户是否在特定服务器的白名单中
65
61
  */
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WhitelistHandler = void 0;
4
4
  const koishi_1 = require("koishi");
5
5
  const base_handler_1 = require("./base.handler");
6
+ const helpers_1 = require("../utils/helpers");
6
7
  /**
7
8
  * 白名单命令处理器
8
9
  * 处理所有 mcid.whitelist 子命令
@@ -756,38 +757,8 @@ class WhitelistHandler extends base_handler_1.BaseHandler {
756
757
  if (b.includes(a)) {
757
758
  return a.length / b.length;
758
759
  }
759
- // 否则计算Levenshtein距离的相似度
760
- const maxLength = Math.max(a.length, b.length);
761
- const editDistance = this.levenshteinDistance(a, b);
762
- return 1 - (editDistance / maxLength);
763
- }
764
- /**
765
- * 私有辅助方法:计算Levenshtein距离
766
- */
767
- levenshteinDistance(a, b) {
768
- const matrix = [];
769
- // 初始化矩阵
770
- for (let i = 0; i <= b.length; i++) {
771
- matrix[i] = [i];
772
- }
773
- for (let j = 0; j <= a.length; j++) {
774
- matrix[0][j] = j;
775
- }
776
- // 填充矩阵
777
- for (let i = 1; i <= b.length; i++) {
778
- for (let j = 1; j <= a.length; j++) {
779
- if (b.charAt(i - 1) === a.charAt(j - 1)) {
780
- matrix[i][j] = matrix[i - 1][j - 1];
781
- }
782
- else {
783
- matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // 替换
784
- matrix[i][j - 1] + 1, // 插入
785
- matrix[i - 1][j] + 1 // 删除
786
- );
787
- }
788
- }
789
- }
790
- return matrix[b.length][a.length];
760
+ // 否则使用工具函数计算Levenshtein距离的相似度
761
+ return (0, helpers_1.calculateSimilarity)(a, b);
791
762
  }
792
763
  /**
793
764
  * 私有辅助方法:检查用户是否在特定服务器的白名单中
package/lib/index.js CHANGED
@@ -12,6 +12,8 @@ const export_utils_1 = require("./export-utils");
12
12
  const logger_1 = require("./utils/logger");
13
13
  const rcon_manager_1 = require("./managers/rcon-manager");
14
14
  const rate_limiter_1 = require("./utils/rate-limiter");
15
+ const helpers_1 = require("./utils/helpers");
16
+ const error_utils_1 = require("./utils/error-utils");
15
17
  const mcidbind_repository_1 = require("./repositories/mcidbind.repository");
16
18
  const schedule_mute_repository_1 = require("./repositories/schedule-mute.repository");
17
19
  const handlers_1 = require("./handlers");
@@ -911,88 +913,6 @@ function apply(ctx, config) {
911
913
  return extractedId;
912
914
  };
913
915
  // 获取用户友好的错误信息
914
- const getFriendlyErrorMessage = (error) => {
915
- const errorMsg = error instanceof Error ? error.message : error;
916
- // 拆分错误信息
917
- const userError = getUserFacingErrorMessage(errorMsg);
918
- // 将警告级别错误标记出来
919
- if (isWarningError(userError)) {
920
- return `⚠️ ${userError}`;
921
- }
922
- // 将严重错误标记出来
923
- if (isCriticalError(userError)) {
924
- return `❌ ${userError}`;
925
- }
926
- return userError;
927
- };
928
- // 提取用户友好的错误信息
929
- const getUserFacingErrorMessage = (errorMsg) => {
930
- // Mojang API相关错误
931
- if (errorMsg.includes('ECONNABORTED') || errorMsg.includes('timeout')) {
932
- return '无法连接到Mojang服务器,请稍后再试';
933
- }
934
- if (errorMsg.includes('404')) {
935
- return '该Minecraft用户名不存在';
936
- }
937
- if (errorMsg.includes('network') || errorMsg.includes('connect')) {
938
- return '网络连接异常,请稍后再试';
939
- }
940
- // 数据库相关错误
941
- if (errorMsg.includes('unique') || errorMsg.includes('duplicate')) {
942
- return '该Minecraft用户名已被其他用户绑定';
943
- }
944
- // RCON相关错误
945
- if (errorMsg.includes('RCON') || errorMsg.includes('服务器')) {
946
- if (errorMsg.includes('authentication') || errorMsg.includes('auth') || errorMsg.includes('认证')) {
947
- return 'RCON认证失败,服务器拒绝访问,请联系管理员检查密码';
948
- }
949
- if (errorMsg.includes('ECONNREFUSED') || errorMsg.includes('ETIMEDOUT') || errorMsg.includes('无法连接')) {
950
- return '无法连接到游戏服务器,请确认服务器是否在线或联系管理员';
951
- }
952
- if (errorMsg.includes('command') || errorMsg.includes('执行命令')) {
953
- return '服务器命令执行失败,请稍后再试';
954
- }
955
- return '与游戏服务器通信失败,请稍后再试';
956
- }
957
- // 用户名相关错误
958
- if (errorMsg.includes('用户名') || errorMsg.includes('username')) {
959
- if (errorMsg.includes('不存在')) {
960
- return '该Minecraft用户名不存在,请检查拼写';
961
- }
962
- if (errorMsg.includes('已被')) {
963
- return '该Minecraft用户名已被其他用户绑定,请使用其他用户名';
964
- }
965
- if (errorMsg.includes('格式')) {
966
- return 'Minecraft用户名格式不正确,应为3-16位字母、数字和下划线';
967
- }
968
- return '用户名验证失败,请检查用户名并重试';
969
- }
970
- // 默认错误信息
971
- return '操作失败,请稍后再试';
972
- };
973
- // 判断是否为警告级别错误(用户可能输入有误)
974
- const isWarningError = (errorMsg) => {
975
- const warningPatterns = [
976
- '用户名不存在',
977
- '格式不正确',
978
- '已被其他用户绑定',
979
- '已在白名单中',
980
- '不在白名单中',
981
- '未绑定MC账号',
982
- '冷却期内'
983
- ];
984
- return warningPatterns.some(pattern => errorMsg.includes(pattern));
985
- };
986
- // 判断是否为严重错误(系统问题)
987
- const isCriticalError = (errorMsg) => {
988
- const criticalPatterns = [
989
- '无法连接',
990
- 'RCON认证失败',
991
- '服务器通信失败',
992
- '数据库操作出错'
993
- ];
994
- return criticalPatterns.some(pattern => errorMsg.includes(pattern));
995
- };
996
916
  // 封装发送消息的函数,处理私聊和群聊的不同格式
997
917
  const sendMessage = async (session, content, options) => {
998
918
  try {
@@ -1773,41 +1693,11 @@ function apply(ctx, config) {
1773
1693
  const lowerServerName = serverName.toLowerCase().trim();
1774
1694
  // 最小相似度阈值,低于此值的匹配结果将被忽略
1775
1695
  const MIN_SIMILARITY = 0.6; // 60%的相似度
1776
- // 计算Levenshtein距离的函数
1777
- const levenshteinDistance = (str1, str2) => {
1778
- const matrix = [];
1779
- for (let i = 0; i <= str2.length; i++) {
1780
- matrix[i] = [i];
1781
- }
1782
- for (let j = 0; j <= str1.length; j++) {
1783
- matrix[0][j] = j;
1784
- }
1785
- for (let i = 1; i <= str2.length; i++) {
1786
- for (let j = 1; j <= str1.length; j++) {
1787
- if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
1788
- matrix[i][j] = matrix[i - 1][j - 1];
1789
- }
1790
- else {
1791
- matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // 替换
1792
- matrix[i][j - 1] + 1, // 插入
1793
- matrix[i - 1][j] + 1 // 删除
1794
- );
1795
- }
1796
- }
1797
- }
1798
- return matrix[str2.length][str1.length];
1799
- };
1800
- // 计算相似度(0到1之间,1表示完全相同)
1801
- const calculateSimilarity = (str1, str2) => {
1802
- const distance = levenshteinDistance(str1, str2);
1803
- const maxLength = Math.max(str1.length, str2.length);
1804
- return 1 - distance / maxLength;
1805
- };
1806
1696
  // 查找最相似的服务器名称
1807
1697
  let bestMatch = null;
1808
1698
  let bestSimilarity = 0;
1809
1699
  for (const s of enabledServers) {
1810
- const similarity = calculateSimilarity(lowerServerName, s.name.toLowerCase().trim());
1700
+ const similarity = (0, helpers_1.calculateSimilarity)(lowerServerName, s.name.toLowerCase().trim());
1811
1701
  if (similarity > bestSimilarity && similarity >= MIN_SIMILARITY) {
1812
1702
  bestSimilarity = similarity;
1813
1703
  bestMatch = s;
@@ -1861,10 +1751,29 @@ function apply(ctx, config) {
1861
1751
  checkCooldown,
1862
1752
  getCrafatarUrl,
1863
1753
  getStarlightSkinUrl,
1754
+ // Database operations (for McidCommandHandler)
1755
+ getMcBindByQQId,
1756
+ getMcBindByUsername,
1757
+ createOrUpdateMcBind,
1758
+ deleteMcBind,
1759
+ checkUsernameExists,
1760
+ checkAndUpdateUsername,
1761
+ // API operations
1762
+ validateUsername,
1763
+ validateBUID,
1764
+ updateBuidInfoOnly,
1765
+ // Permission check functions
1766
+ isAdmin,
1767
+ isMaster,
1864
1768
  // Business functions
1865
1769
  sendMessage,
1866
1770
  autoSetGroupNickname,
1771
+ checkNicknameFormat,
1867
1772
  getBindInfo: getMcBindByQQId,
1773
+ // Config operations
1774
+ getServerConfigById,
1775
+ // Error handling
1776
+ getFriendlyErrorMessage: error_utils_1.getFriendlyErrorMessage,
1868
1777
  // Service instances
1869
1778
  rconManager,
1870
1779
  messageUtils,
@@ -1889,49 +1798,9 @@ function apply(ctx, config) {
1889
1798
  tagHandler.register();
1890
1799
  whitelistHandler.register();
1891
1800
  buidHandler.register();
1892
- // =========== MC命令组 ===========
1893
- const cmd = ctx.command('mcid', 'Minecraft 账号绑定管理');
1894
- // 创建McidHandler的依赖对象
1895
- const mcidHandlerDeps = {
1896
- config,
1897
- logger: loggerService,
1898
- mcidbindRepo,
1899
- groupExporter,
1900
- // 辅助函数依赖
1901
- normalizeQQId,
1902
- formatCommand,
1903
- formatUuid,
1904
- checkCooldown,
1905
- getCrafatarUrl,
1906
- getStarlightSkinUrl,
1907
- // 数据库操作
1908
- getMcBindByQQId,
1909
- getMcBindByUsername,
1910
- createOrUpdateMcBind,
1911
- deleteMcBind,
1912
- checkUsernameExists,
1913
- checkAndUpdateUsername,
1914
- // API操作
1915
- validateUsername,
1916
- validateBUID,
1917
- updateBuidInfoOnly,
1918
- // 权限检查
1919
- isAdmin,
1920
- isMaster,
1921
- // 消息操作
1922
- sendMessage,
1923
- autoSetGroupNickname,
1924
- checkNicknameFormat,
1925
- // 会话管理
1926
- removeBindingSession,
1927
- // 服务器配置
1928
- getServerConfigById,
1929
- // 工具函数
1930
- getFriendlyErrorMessage
1931
- };
1932
1801
  // 实例化McidCommandHandler并注册命令
1933
- const mcidHandler = new handlers_1.McidCommandHandler(ctx, mcidHandlerDeps);
1934
- mcidHandler.registerCommands(cmd);
1802
+ const mcidHandler = new handlers_1.McidCommandHandler(ctx, config, loggerService, repositories, handlerDependencies);
1803
+ mcidHandler.register();
1935
1804
  // 自定义文本前缀匹配
1936
1805
  if (config.allowTextPrefix && config.botNickname) {
1937
1806
  // 创建一个前缀匹配器
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 错误处理工具函数集合
3
+ * 提供统一的错误信息格式化和分类功能
4
+ */
5
+ /**
6
+ * 获取用户友好的错误信息
7
+ * 将技术性错误转换为用户可理解的提示
8
+ * @param error 错误对象或错误消息字符串
9
+ * @returns 格式化后的用户友好错误信息
10
+ */
11
+ export declare function getFriendlyErrorMessage(error: Error | string): string;
12
+ /**
13
+ * 提取用户友好的错误信息
14
+ * 根据错误消息内容返回对应的用户提示
15
+ * @param errorMsg 原始错误消息
16
+ * @returns 用户可理解的错误提示
17
+ */
18
+ export declare function getUserFacingErrorMessage(errorMsg: string): string;
19
+ /**
20
+ * 判断是否为警告级别错误(用户可能输入有误)
21
+ * @param errorMsg 错误消息
22
+ * @returns 是否为警告级别错误
23
+ */
24
+ export declare function isWarningError(errorMsg: string): boolean;
25
+ /**
26
+ * 判断是否为严重错误(系统问题)
27
+ * @param errorMsg 错误消息
28
+ * @returns 是否为严重错误
29
+ */
30
+ export declare function isCriticalError(errorMsg: string): boolean;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * 错误处理工具函数集合
4
+ * 提供统一的错误信息格式化和分类功能
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getFriendlyErrorMessage = getFriendlyErrorMessage;
8
+ exports.getUserFacingErrorMessage = getUserFacingErrorMessage;
9
+ exports.isWarningError = isWarningError;
10
+ exports.isCriticalError = isCriticalError;
11
+ /**
12
+ * 获取用户友好的错误信息
13
+ * 将技术性错误转换为用户可理解的提示
14
+ * @param error 错误对象或错误消息字符串
15
+ * @returns 格式化后的用户友好错误信息
16
+ */
17
+ function getFriendlyErrorMessage(error) {
18
+ const errorMsg = error instanceof Error ? error.message : error;
19
+ // 拆分错误信息
20
+ const userError = getUserFacingErrorMessage(errorMsg);
21
+ // 将警告级别错误标记出来
22
+ if (isWarningError(userError)) {
23
+ return `⚠️ ${userError}`;
24
+ }
25
+ // 将严重错误标记出来
26
+ if (isCriticalError(userError)) {
27
+ return `❌ ${userError}`;
28
+ }
29
+ return userError;
30
+ }
31
+ /**
32
+ * 提取用户友好的错误信息
33
+ * 根据错误消息内容返回对应的用户提示
34
+ * @param errorMsg 原始错误消息
35
+ * @returns 用户可理解的错误提示
36
+ */
37
+ function getUserFacingErrorMessage(errorMsg) {
38
+ // Mojang API相关错误
39
+ if (errorMsg.includes('ECONNABORTED') || errorMsg.includes('timeout')) {
40
+ return '无法连接到Mojang服务器,请稍后再试';
41
+ }
42
+ if (errorMsg.includes('404')) {
43
+ return '该Minecraft用户名不存在';
44
+ }
45
+ if (errorMsg.includes('network') || errorMsg.includes('connect')) {
46
+ return '网络连接异常,请稍后再试';
47
+ }
48
+ // 数据库相关错误
49
+ if (errorMsg.includes('unique') || errorMsg.includes('duplicate')) {
50
+ return '该Minecraft用户名已被其他用户绑定';
51
+ }
52
+ // RCON相关错误
53
+ if (errorMsg.includes('RCON') || errorMsg.includes('服务器')) {
54
+ if (errorMsg.includes('authentication') || errorMsg.includes('auth') || errorMsg.includes('认证')) {
55
+ return 'RCON认证失败,服务器拒绝访问,请联系管理员检查密码';
56
+ }
57
+ if (errorMsg.includes('ECONNREFUSED') || errorMsg.includes('ETIMEDOUT') || errorMsg.includes('无法连接')) {
58
+ return '无法连接到游戏服务器,请确认服务器是否在线或联系管理员';
59
+ }
60
+ if (errorMsg.includes('command') || errorMsg.includes('执行命令')) {
61
+ return '服务器命令执行失败,请稍后再试';
62
+ }
63
+ return '与游戏服务器通信失败,请稍后再试';
64
+ }
65
+ // 用户名相关错误
66
+ if (errorMsg.includes('用户名') || errorMsg.includes('username')) {
67
+ if (errorMsg.includes('不存在')) {
68
+ return '该Minecraft用户名不存在,请检查拼写';
69
+ }
70
+ if (errorMsg.includes('已被')) {
71
+ return '该Minecraft用户名已被其他用户绑定,请使用其他用户名';
72
+ }
73
+ if (errorMsg.includes('格式')) {
74
+ return 'Minecraft用户名格式不正确,应为3-16位字母、数字和下划线';
75
+ }
76
+ return '用户名验证失败,请检查用户名并重试';
77
+ }
78
+ // 默认错误信息
79
+ return '操作失败,请稍后再试';
80
+ }
81
+ /**
82
+ * 判断是否为警告级别错误(用户可能输入有误)
83
+ * @param errorMsg 错误消息
84
+ * @returns 是否为警告级别错误
85
+ */
86
+ function isWarningError(errorMsg) {
87
+ const warningPatterns = [
88
+ '用户名不存在',
89
+ '格式不正确',
90
+ '已被其他用户绑定',
91
+ '已在白名单中',
92
+ '不在白名单中',
93
+ '未绑定MC账号',
94
+ '冷却期内'
95
+ ];
96
+ return warningPatterns.some(pattern => errorMsg.includes(pattern));
97
+ }
98
+ /**
99
+ * 判断是否为严重错误(系统问题)
100
+ * @param errorMsg 错误消息
101
+ * @returns 是否为严重错误
102
+ */
103
+ function isCriticalError(errorMsg) {
104
+ const criticalPatterns = [
105
+ '无法连接',
106
+ 'RCON认证失败',
107
+ '服务器通信失败',
108
+ '数据库操作出错'
109
+ ];
110
+ return criticalPatterns.some(pattern => errorMsg.includes(pattern));
111
+ }
@@ -74,3 +74,18 @@ export declare function escapeRegExp(string: string): string;
74
74
  * @returns 清理后的输入内容
75
75
  */
76
76
  export declare function cleanUserInput(content: string, session: Session, botNickname: string, logger?: Logger): string;
77
+ /**
78
+ * 计算两个字符串之间的Levenshtein距离
79
+ * Levenshtein距离是指将一个字符串转换成另一个字符串所需的最少编辑操作次数
80
+ * @param str1 第一个字符串
81
+ * @param str2 第二个字符串
82
+ * @returns 两个字符串之间的编辑距离
83
+ */
84
+ export declare function levenshteinDistance(str1: string, str2: string): number;
85
+ /**
86
+ * 计算两个字符串的相似度(基于Levenshtein距离)
87
+ * @param str1 第一个字符串
88
+ * @param str2 第二个字符串
89
+ * @returns 相似度值(0到1之间,1表示完全相同)
90
+ */
91
+ export declare function calculateSimilarity(str1: string, str2: string): number;
@@ -9,6 +9,8 @@ exports.getStarlightSkinUrl = getStarlightSkinUrl;
9
9
  exports.checkIrrelevantInput = checkIrrelevantInput;
10
10
  exports.escapeRegExp = escapeRegExp;
11
11
  exports.cleanUserInput = cleanUserInput;
12
+ exports.levenshteinDistance = levenshteinDistance;
13
+ exports.calculateSimilarity = calculateSimilarity;
12
14
  /**
13
15
  * 通用工具函数集合
14
16
  */
@@ -273,3 +275,47 @@ function cleanUserInput(content, session, botNickname, logger) {
273
275
  }
274
276
  return cleanedContent;
275
277
  }
278
+ /**
279
+ * 计算两个字符串之间的Levenshtein距离
280
+ * Levenshtein距离是指将一个字符串转换成另一个字符串所需的最少编辑操作次数
281
+ * @param str1 第一个字符串
282
+ * @param str2 第二个字符串
283
+ * @returns 两个字符串之间的编辑距离
284
+ */
285
+ function levenshteinDistance(str1, str2) {
286
+ const matrix = [];
287
+ // 初始化第一列
288
+ for (let i = 0; i <= str2.length; i++) {
289
+ matrix[i] = [i];
290
+ }
291
+ // 初始化第一行
292
+ for (let j = 0; j <= str1.length; j++) {
293
+ matrix[0][j] = j;
294
+ }
295
+ // 填充矩阵
296
+ for (let i = 1; i <= str2.length; i++) {
297
+ for (let j = 1; j <= str1.length; j++) {
298
+ if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
299
+ matrix[i][j] = matrix[i - 1][j - 1];
300
+ }
301
+ else {
302
+ matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // 替换
303
+ matrix[i][j - 1] + 1, // 插入
304
+ matrix[i - 1][j] + 1 // 删除
305
+ );
306
+ }
307
+ }
308
+ }
309
+ return matrix[str2.length][str1.length];
310
+ }
311
+ /**
312
+ * 计算两个字符串的相似度(基于Levenshtein距离)
313
+ * @param str1 第一个字符串
314
+ * @param str2 第二个字符串
315
+ * @returns 相似度值(0到1之间,1表示完全相同)
316
+ */
317
+ function calculateSimilarity(str1, str2) {
318
+ const distance = levenshteinDistance(str1, str2);
319
+ const maxLength = Math.max(str1.length, str2.length);
320
+ return 1 - distance / maxLength;
321
+ }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "koishi-plugin-bind-bot",
3
3
  "description": "[WittF自用] BIND-BOT - 账号绑定管理机器人,支持Minecraft账号和B站账号绑定与管理。",
4
- "version": "2.0.0",
4
+ "version": "2.0.2",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
8
8
  "lib"
9
9
  ],
10
- "license": "MIT",
10
+ "license": "CC-BY-NC-SA-4.0",
11
11
  "scripts": {
12
12
  "build": "tsc"
13
13
  },