koishi-plugin-bind-bot 2.2.8 → 2.3.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/export-utils.js +2 -1
- package/lib/handlers/binding.handler.d.ts +28 -0
- package/lib/handlers/binding.handler.js +169 -107
- package/lib/handlers/buid.handler.js +7 -10
- package/lib/handlers/group-request-review.handler.d.ts +6 -1
- package/lib/handlers/group-request-review.handler.js +86 -84
- package/lib/handlers/mcid.handler.js +10 -9
- package/lib/index.js +14 -298
- package/lib/repositories/mcidbind.repository.d.ts +4 -86
- package/lib/repositories/mcidbind.repository.js +61 -127
- package/lib/services/database.service.js +0 -6
- package/lib/types/config.d.ts +6 -2
- package/lib/types/database.d.ts +0 -6
- package/lib/types/update-data.d.ts +0 -11
- package/lib/utils/bind-status.d.ts +14 -0
- package/lib/utils/bind-status.js +22 -0
- package/lib/utils/helpers.d.ts +1 -9
- package/lib/utils/helpers.js +5 -0
- package/lib/utils/supabase-client.d.ts +14 -0
- package/lib/utils/supabase-client.js +44 -0
- package/package.json +1 -1
|
@@ -2,171 +2,143 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MCIDBINDRepository = void 0;
|
|
4
4
|
const helpers_1 = require("../utils/helpers");
|
|
5
|
+
const TABLE = 'user';
|
|
6
|
+
const DATE_FIELDS = ['lastModified', 'usernameLastChecked', 'lastActiveTime'];
|
|
7
|
+
function deserialize(row) {
|
|
8
|
+
if (!row)
|
|
9
|
+
return row;
|
|
10
|
+
for (const field of DATE_FIELDS) {
|
|
11
|
+
if (row[field] && typeof row[field] === 'string') {
|
|
12
|
+
row[field] = new Date(row[field]);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return row;
|
|
16
|
+
}
|
|
17
|
+
function serializeDates(data) {
|
|
18
|
+
const out = { ...data };
|
|
19
|
+
for (const field of DATE_FIELDS) {
|
|
20
|
+
if (out[field] instanceof Date) {
|
|
21
|
+
out[field] = out[field].toISOString();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
5
26
|
/**
|
|
6
27
|
* MCIDBIND 数据仓储类
|
|
7
|
-
*
|
|
28
|
+
* 通过 Supabase REST API 操作 user 表
|
|
8
29
|
*/
|
|
9
30
|
class MCIDBINDRepository {
|
|
10
|
-
|
|
31
|
+
supabase;
|
|
11
32
|
logger;
|
|
12
|
-
constructor(
|
|
13
|
-
this.
|
|
33
|
+
constructor(supabase, logger) {
|
|
34
|
+
this.supabase = supabase;
|
|
14
35
|
this.logger = logger;
|
|
15
36
|
}
|
|
16
|
-
/**
|
|
17
|
-
* 根据 QQ 号查询绑定信息
|
|
18
|
-
* @param qqId QQ号(已规范化)
|
|
19
|
-
* @returns 绑定信息或 null
|
|
20
|
-
*/
|
|
21
37
|
async findByQQId(qqId) {
|
|
22
38
|
try {
|
|
23
39
|
this.logger.debug('数据库', `查询QQ(${qqId})的绑定信息`);
|
|
24
|
-
const
|
|
25
|
-
return
|
|
40
|
+
const rows = await this.supabase.get(TABLE, `qqId=eq.${qqId}&limit=1`);
|
|
41
|
+
return rows.length > 0 ? deserialize(rows[0]) : null;
|
|
26
42
|
}
|
|
27
43
|
catch (error) {
|
|
28
44
|
this.logger.error('数据库', `查询QQ(${qqId})绑定信息失败: ${error.message}`);
|
|
29
45
|
return null;
|
|
30
46
|
}
|
|
31
47
|
}
|
|
32
|
-
/**
|
|
33
|
-
* 根据 MC 用户名查询绑定信息(精确匹配)
|
|
34
|
-
* @param mcUsername MC用户名
|
|
35
|
-
* @returns 绑定信息或 null
|
|
36
|
-
*/
|
|
37
48
|
async findByMCUsername(mcUsername) {
|
|
38
49
|
try {
|
|
39
50
|
this.logger.debug('数据库', `查询MC用户名(${mcUsername})的绑定信息`);
|
|
40
|
-
const
|
|
41
|
-
return
|
|
51
|
+
const rows = await this.supabase.get(TABLE, `mcUsername=eq.${encodeURIComponent(mcUsername)}&limit=1`);
|
|
52
|
+
return rows.length > 0 ? deserialize(rows[0]) : null;
|
|
42
53
|
}
|
|
43
54
|
catch (error) {
|
|
44
55
|
this.logger.error('数据库', `查询MC用户名(${mcUsername})绑定信息失败: ${error.message}`);
|
|
45
56
|
return null;
|
|
46
57
|
}
|
|
47
58
|
}
|
|
48
|
-
/**
|
|
49
|
-
* 根据 MC 用户名查询绑定信息(不区分大小写)
|
|
50
|
-
* @param mcUsername MC用户名
|
|
51
|
-
* @returns 绑定信息或 null
|
|
52
|
-
*/
|
|
53
59
|
async findByUsernameIgnoreCase(mcUsername) {
|
|
54
60
|
try {
|
|
55
61
|
const normalizedInput = (0, helpers_1.normalizeUsername)(mcUsername, this.logger.getRawLogger());
|
|
56
62
|
this.logger.debug('数据库', `查询MC用户名(${mcUsername} -> ${normalizedInput})的绑定信息(不区分大小写)`);
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
return match || null;
|
|
63
|
+
const rows = await this.supabase.get(TABLE, `mcUsername=ilike.${encodeURIComponent(mcUsername)}`);
|
|
64
|
+
const match = rows.find(bind => bind.mcUsername && (0, helpers_1.normalizeUsername)(bind.mcUsername) === normalizedInput);
|
|
65
|
+
return match ? deserialize(match) : null;
|
|
61
66
|
}
|
|
62
67
|
catch (error) {
|
|
63
68
|
this.logger.error('数据库', `查询MC用户名(${mcUsername})绑定信息失败(不区分大小写): ${error.message}`);
|
|
64
69
|
return null;
|
|
65
70
|
}
|
|
66
71
|
}
|
|
67
|
-
/**
|
|
68
|
-
* 根据 MC UUID 查询绑定信息
|
|
69
|
-
* @param mcUuid MC UUID(可带或不带连字符)
|
|
70
|
-
* @returns 绑定信息或 null
|
|
71
|
-
*/
|
|
72
72
|
async findByUuid(mcUuid) {
|
|
73
73
|
try {
|
|
74
|
-
// 规范化 UUID(移除连字符)
|
|
75
74
|
const cleanUuid = mcUuid.replace(/-/g, '');
|
|
76
75
|
this.logger.debug('数据库', `查询MC UUID(${cleanUuid})的绑定信息`);
|
|
77
|
-
// 先尝试精确匹配
|
|
78
|
-
let binds = await this.ctx.database.get('mcidbind', { mcUuid: cleanUuid });
|
|
79
|
-
if (binds.length > 0)
|
|
80
|
-
return binds[0];
|
|
81
|
-
// 尝试带连字符的格式
|
|
82
76
|
const formattedUuid = `${cleanUuid.substring(0, 8)}-${cleanUuid.substring(8, 12)}-${cleanUuid.substring(12, 16)}-${cleanUuid.substring(16, 20)}-${cleanUuid.substring(20)}`;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
77
|
+
// 用 or 一次查两种格式
|
|
78
|
+
const rows = await this.supabase.get(TABLE, `or=(mcUuid.eq.${cleanUuid},mcUuid.eq.${formattedUuid})&limit=1`);
|
|
79
|
+
if (rows.length > 0)
|
|
80
|
+
return deserialize(rows[0]);
|
|
81
|
+
// 兜底:用 ilike 模糊匹配
|
|
82
|
+
const fallback = await this.supabase.get(TABLE, `mcUuid=ilike.*${cleanUuid}*&limit=1`);
|
|
83
|
+
if (fallback.length > 0)
|
|
84
|
+
return deserialize(fallback[0]);
|
|
85
|
+
return null;
|
|
90
86
|
}
|
|
91
87
|
catch (error) {
|
|
92
88
|
this.logger.error('数据库', `查询MC UUID(${mcUuid})绑定信息失败: ${error.message}`);
|
|
93
89
|
return null;
|
|
94
90
|
}
|
|
95
91
|
}
|
|
96
|
-
/**
|
|
97
|
-
* 根据 B站 UID 查询绑定信息
|
|
98
|
-
* @param buidUid B站UID
|
|
99
|
-
* @returns 绑定信息或 null
|
|
100
|
-
*/
|
|
101
92
|
async findByBuidUid(buidUid) {
|
|
102
93
|
try {
|
|
103
94
|
this.logger.debug('数据库', `查询B站UID(${buidUid})的绑定信息`);
|
|
104
|
-
const
|
|
105
|
-
return
|
|
95
|
+
const rows = await this.supabase.get(TABLE, `buidUid=eq.${buidUid}&limit=1`);
|
|
96
|
+
return rows.length > 0 ? deserialize(rows[0]) : null;
|
|
106
97
|
}
|
|
107
98
|
catch (error) {
|
|
108
99
|
this.logger.error('数据库', `查询B站UID(${buidUid})绑定信息失败: ${error.message}`);
|
|
109
100
|
return null;
|
|
110
101
|
}
|
|
111
102
|
}
|
|
112
|
-
/**
|
|
113
|
-
* 获取所有绑定记录
|
|
114
|
-
* @param options 查询选项
|
|
115
|
-
* @returns 绑定记录列表
|
|
116
|
-
*/
|
|
117
103
|
async findAll(options) {
|
|
118
104
|
try {
|
|
119
105
|
this.logger.debug('数据库', `获取所有绑定记录${options?.limit ? ` (limit: ${options.limit})` : ''}`);
|
|
120
|
-
const
|
|
121
|
-
|
|
106
|
+
const query = options?.limit ? `limit=${options.limit}` : '';
|
|
107
|
+
const rows = await this.supabase.get(TABLE, query);
|
|
108
|
+
return rows.map(deserialize);
|
|
122
109
|
}
|
|
123
110
|
catch (error) {
|
|
124
111
|
this.logger.error('数据库', `获取所有绑定记录失败: ${error.message}`);
|
|
125
112
|
return [];
|
|
126
113
|
}
|
|
127
114
|
}
|
|
128
|
-
/**
|
|
129
|
-
* 根据标签查询绑定记录
|
|
130
|
-
* @param tag 标签名称
|
|
131
|
-
* @returns 包含该标签的绑定记录列表
|
|
132
|
-
*/
|
|
133
115
|
async findByTag(tag) {
|
|
134
116
|
try {
|
|
135
117
|
this.logger.debug('数据库', `查询包含标签"${tag}"的绑定记录`);
|
|
136
|
-
const
|
|
137
|
-
return
|
|
118
|
+
const rows = await this.supabase.get(TABLE, `tags=cs.${encodeURIComponent(JSON.stringify([tag]))}`);
|
|
119
|
+
return rows.map(deserialize);
|
|
138
120
|
}
|
|
139
121
|
catch (error) {
|
|
140
122
|
this.logger.error('数据库', `查询标签"${tag}"的绑定记录失败: ${error.message}`);
|
|
141
123
|
return [];
|
|
142
124
|
}
|
|
143
125
|
}
|
|
144
|
-
/**
|
|
145
|
-
* 创建新的绑定记录
|
|
146
|
-
* @param data 绑定数据
|
|
147
|
-
* @returns 创建的记录
|
|
148
|
-
*/
|
|
149
126
|
async create(data) {
|
|
150
127
|
try {
|
|
151
128
|
this.logger.debug('数据库', `创建QQ(${data.qqId})的绑定记录`);
|
|
152
|
-
const created = await this.
|
|
129
|
+
const created = await this.supabase.post(TABLE, serializeDates(data));
|
|
153
130
|
this.logger.info('数据库', `成功创建QQ(${data.qqId})的绑定记录`, true);
|
|
154
|
-
return created;
|
|
131
|
+
return deserialize(created);
|
|
155
132
|
}
|
|
156
133
|
catch (error) {
|
|
157
134
|
this.logger.error('数据库', `创建QQ(${data.qqId})绑定记录失败: ${error.message}`);
|
|
158
135
|
throw error;
|
|
159
136
|
}
|
|
160
137
|
}
|
|
161
|
-
/**
|
|
162
|
-
* 更新绑定记录(部分字段)
|
|
163
|
-
* @param qqId QQ号
|
|
164
|
-
* @param data 要更新的字段
|
|
165
|
-
*/
|
|
166
138
|
async update(qqId, data) {
|
|
167
139
|
try {
|
|
168
140
|
this.logger.debug('数据库', `更新QQ(${qqId})的绑定记录`);
|
|
169
|
-
await this.
|
|
141
|
+
await this.supabase.patch(TABLE, `qqId=eq.${qqId}`, serializeDates(data));
|
|
170
142
|
this.logger.info('数据库', `成功更新QQ(${qqId})的绑定记录`, true);
|
|
171
143
|
}
|
|
172
144
|
catch (error) {
|
|
@@ -174,49 +146,35 @@ class MCIDBINDRepository {
|
|
|
174
146
|
throw error;
|
|
175
147
|
}
|
|
176
148
|
}
|
|
177
|
-
/**
|
|
178
|
-
* 删除绑定记录
|
|
179
|
-
* @param qqId QQ号
|
|
180
|
-
* @returns 删除的记录数
|
|
181
|
-
*/
|
|
182
149
|
async delete(qqId) {
|
|
183
150
|
try {
|
|
184
151
|
this.logger.debug('数据库', `删除QQ(${qqId})的绑定记录`);
|
|
185
|
-
const
|
|
186
|
-
this.logger.info('数据库', `成功删除QQ(${qqId})的绑定记录(删除${
|
|
187
|
-
return
|
|
152
|
+
const removed = await this.supabase.del(TABLE, `qqId=eq.${qqId}`);
|
|
153
|
+
this.logger.info('数据库', `成功删除QQ(${qqId})的绑定记录(删除${removed}条)`, true);
|
|
154
|
+
return removed;
|
|
188
155
|
}
|
|
189
156
|
catch (error) {
|
|
190
157
|
this.logger.error('数据库', `删除QQ(${qqId})绑定记录失败: ${error.message}`);
|
|
191
158
|
throw error;
|
|
192
159
|
}
|
|
193
160
|
}
|
|
194
|
-
/**
|
|
195
|
-
* 删除所有绑定记录
|
|
196
|
-
* @returns 删除的记录数
|
|
197
|
-
*/
|
|
198
161
|
async deleteAll() {
|
|
199
162
|
try {
|
|
200
163
|
this.logger.debug('数据库', '删除所有绑定记录');
|
|
201
|
-
const
|
|
202
|
-
this.logger.info('数据库', `成功删除所有绑定记录(删除${
|
|
203
|
-
return
|
|
164
|
+
const removed = await this.supabase.del(TABLE, 'qqId=not.is.null');
|
|
165
|
+
this.logger.info('数据库', `成功删除所有绑定记录(删除${removed}条)`, true);
|
|
166
|
+
return removed;
|
|
204
167
|
}
|
|
205
168
|
catch (error) {
|
|
206
169
|
this.logger.error('数据库', `删除所有绑定记录失败: ${error.message}`);
|
|
207
170
|
throw error;
|
|
208
171
|
}
|
|
209
172
|
}
|
|
210
|
-
/**
|
|
211
|
-
* 批量创建绑定记录
|
|
212
|
-
* @param records 绑定记录列表
|
|
213
|
-
*/
|
|
214
173
|
async batchCreate(records) {
|
|
215
174
|
try {
|
|
216
175
|
this.logger.debug('数据库', `批量创建${records.length}条绑定记录`);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
176
|
+
const serialized = records.map(r => serializeDates(r));
|
|
177
|
+
await this.supabase.postMany(TABLE, serialized);
|
|
220
178
|
this.logger.info('数据库', `成功批量创建${records.length}条绑定记录`, true);
|
|
221
179
|
}
|
|
222
180
|
catch (error) {
|
|
@@ -224,11 +182,6 @@ class MCIDBINDRepository {
|
|
|
224
182
|
throw error;
|
|
225
183
|
}
|
|
226
184
|
}
|
|
227
|
-
/**
|
|
228
|
-
* 为用户添加标签
|
|
229
|
-
* @param qqId QQ号
|
|
230
|
-
* @param tag 标签名称
|
|
231
|
-
*/
|
|
232
185
|
async addTag(qqId, tag) {
|
|
233
186
|
try {
|
|
234
187
|
const bind = await this.findByQQId(qqId);
|
|
@@ -247,11 +200,6 @@ class MCIDBINDRepository {
|
|
|
247
200
|
throw error;
|
|
248
201
|
}
|
|
249
202
|
}
|
|
250
|
-
/**
|
|
251
|
-
* 为用户移除标签
|
|
252
|
-
* @param qqId QQ号
|
|
253
|
-
* @param tag 标签名称
|
|
254
|
-
*/
|
|
255
203
|
async removeTag(qqId, tag) {
|
|
256
204
|
try {
|
|
257
205
|
const bind = await this.findByQQId(qqId);
|
|
@@ -271,11 +219,6 @@ class MCIDBINDRepository {
|
|
|
271
219
|
throw error;
|
|
272
220
|
}
|
|
273
221
|
}
|
|
274
|
-
/**
|
|
275
|
-
* 为用户添加白名单服务器
|
|
276
|
-
* @param qqId QQ号
|
|
277
|
-
* @param serverId 服务器ID
|
|
278
|
-
*/
|
|
279
222
|
async addWhitelist(qqId, serverId) {
|
|
280
223
|
try {
|
|
281
224
|
const bind = await this.findByQQId(qqId);
|
|
@@ -294,11 +237,6 @@ class MCIDBINDRepository {
|
|
|
294
237
|
throw error;
|
|
295
238
|
}
|
|
296
239
|
}
|
|
297
|
-
/**
|
|
298
|
-
* 为用户移除白名单服务器
|
|
299
|
-
* @param qqId QQ号
|
|
300
|
-
* @param serverId 服务器ID
|
|
301
|
-
*/
|
|
302
240
|
async removeWhitelist(qqId, serverId) {
|
|
303
241
|
try {
|
|
304
242
|
const bind = await this.findByQQId(qqId);
|
|
@@ -318,15 +256,11 @@ class MCIDBINDRepository {
|
|
|
318
256
|
throw error;
|
|
319
257
|
}
|
|
320
258
|
}
|
|
321
|
-
/**
|
|
322
|
-
* 获取所有管理员
|
|
323
|
-
* @returns 管理员列表
|
|
324
|
-
*/
|
|
325
259
|
async findAllAdmins() {
|
|
326
260
|
try {
|
|
327
261
|
this.logger.debug('数据库', '获取所有管理员');
|
|
328
|
-
const
|
|
329
|
-
return
|
|
262
|
+
const rows = await this.supabase.get(TABLE, 'isAdmin=eq.true');
|
|
263
|
+
return rows.map(deserialize);
|
|
330
264
|
}
|
|
331
265
|
catch (error) {
|
|
332
266
|
this.logger.error('数据库', `获取所有管理员失败: ${error.message}`);
|
|
@@ -274,9 +274,7 @@ class DatabaseService {
|
|
|
274
274
|
buidUid: buidUser.uid.toString(), // 转换为字符串存储
|
|
275
275
|
buidUsername: buidUser.username,
|
|
276
276
|
guardLevel: buidUser.guard_level || 0,
|
|
277
|
-
guardLevelText: buidUser.guard_level_text || '',
|
|
278
277
|
maxGuardLevel: buidUser.max_guard_level || 0,
|
|
279
|
-
maxGuardLevelText: buidUser.max_guard_level_text || '',
|
|
280
278
|
medalName: buidUser.medal?.name || '',
|
|
281
279
|
medalLevel: buidUser.medal?.level || 0,
|
|
282
280
|
wealthMedalLevel: buidUser.wealthMedalLevel || 0,
|
|
@@ -334,9 +332,7 @@ class DatabaseService {
|
|
|
334
332
|
const updateData = {
|
|
335
333
|
buidUsername: buidUser.username,
|
|
336
334
|
guardLevel: buidUser.guard_level || 0,
|
|
337
|
-
guardLevelText: buidUser.guard_level_text || '',
|
|
338
335
|
maxGuardLevel: buidUser.max_guard_level || 0,
|
|
339
|
-
maxGuardLevelText: buidUser.max_guard_level_text || '',
|
|
340
336
|
medalName: buidUser.medal?.name || '',
|
|
341
337
|
medalLevel: buidUser.medal?.level || 0,
|
|
342
338
|
wealthMedalLevel: buidUser.wealthMedalLevel || 0,
|
|
@@ -386,9 +382,7 @@ class DatabaseService {
|
|
|
386
382
|
buidUid: null,
|
|
387
383
|
buidUsername: null,
|
|
388
384
|
guardLevel: 0,
|
|
389
|
-
guardLevelText: '',
|
|
390
385
|
maxGuardLevel: 0,
|
|
391
|
-
maxGuardLevelText: '',
|
|
392
386
|
medalName: '',
|
|
393
387
|
medalLevel: 0,
|
|
394
388
|
wealthMedalLevel: 0,
|
package/lib/types/config.d.ts
CHANGED
|
@@ -53,6 +53,10 @@ export interface Config {
|
|
|
53
53
|
forceBindTargetRoomId: number;
|
|
54
54
|
/** 强制绑定目标粉丝牌名称 */
|
|
55
55
|
forceBindTargetMedalName: string;
|
|
56
|
+
/** Supabase项目URL */
|
|
57
|
+
supabaseUrl: string;
|
|
58
|
+
/** Supabase API Key */
|
|
59
|
+
supabaseKey: string;
|
|
56
60
|
/** 入群申请审批功能配置 */
|
|
57
61
|
groupRequestReview?: GroupRequestReviewConfig;
|
|
58
62
|
}
|
|
@@ -134,9 +138,9 @@ export interface GroupRequestReviewConfig {
|
|
|
134
138
|
targetGroupId: string;
|
|
135
139
|
/** 管理员审批操作所在的群ID(播报群) */
|
|
136
140
|
reviewGroupId: string;
|
|
137
|
-
/** 批准并自动绑定的表情ID(默认:
|
|
141
|
+
/** 批准并自动绑定的表情ID(默认:76 /赞) */
|
|
138
142
|
approveAutoBindEmoji: string;
|
|
139
|
-
/** 批准并交互式绑定的表情ID(默认:
|
|
143
|
+
/** 批准并交互式绑定的表情ID(默认:124 /OK) */
|
|
140
144
|
approveInteractiveBindEmoji: string;
|
|
141
145
|
/** 拒绝申请的表情ID(默认:123 /NO) */
|
|
142
146
|
rejectEmoji: string;
|
package/lib/types/database.d.ts
CHANGED
|
@@ -24,9 +24,7 @@
|
|
|
24
24
|
* buidUid: '87654321',
|
|
25
25
|
* buidUsername: 'B站用户名',
|
|
26
26
|
* guardLevel: 3,
|
|
27
|
-
* guardLevelText: '舰长',
|
|
28
27
|
* maxGuardLevel: 3,
|
|
29
|
-
* maxGuardLevelText: '舰长',
|
|
30
28
|
* medalName: '粉丝牌',
|
|
31
29
|
* medalLevel: 20,
|
|
32
30
|
* wealthMedalLevel: 15,
|
|
@@ -60,12 +58,8 @@ export interface MCIDBIND {
|
|
|
60
58
|
buidUsername: string | null;
|
|
61
59
|
/** 当前舰长等级 (0=无, 1=总督, 2=提督, 3=舰长) */
|
|
62
60
|
guardLevel: number;
|
|
63
|
-
/** 当前舰长等级文本 (例: '舰长', '提督', '总督') */
|
|
64
|
-
guardLevelText: string;
|
|
65
61
|
/** 历史最高舰长等级 */
|
|
66
62
|
maxGuardLevel: number;
|
|
67
|
-
/** 历史最高舰长等级文本 */
|
|
68
|
-
maxGuardLevelText: string;
|
|
69
63
|
/** 粉丝牌名称 */
|
|
70
64
|
medalName: string;
|
|
71
65
|
/** 粉丝牌等级 */
|
|
@@ -54,9 +54,7 @@ export interface UpdateMcBindData {
|
|
|
54
54
|
* buidUid: '87654321',
|
|
55
55
|
* buidUsername: 'B站用户名',
|
|
56
56
|
* guardLevel: 3,
|
|
57
|
-
* guardLevelText: '舰长',
|
|
58
57
|
* maxGuardLevel: 3,
|
|
59
|
-
* maxGuardLevelText: '舰长',
|
|
60
58
|
* medalName: '粉丝牌',
|
|
61
59
|
* medalLevel: 20,
|
|
62
60
|
* wealthMedalLevel: 15,
|
|
@@ -72,12 +70,8 @@ export interface UpdateBuidBindData {
|
|
|
72
70
|
buidUsername?: string | null;
|
|
73
71
|
/** 当前舰长等级 */
|
|
74
72
|
guardLevel?: number;
|
|
75
|
-
/** 当前舰长等级文本 */
|
|
76
|
-
guardLevelText?: string;
|
|
77
73
|
/** 历史最高舰长等级 */
|
|
78
74
|
maxGuardLevel?: number;
|
|
79
|
-
/** 历史最高舰长等级文本 */
|
|
80
|
-
maxGuardLevelText?: string;
|
|
81
75
|
/** 粉丝牌名称 */
|
|
82
76
|
medalName?: string;
|
|
83
77
|
/** 粉丝牌等级 */
|
|
@@ -104,7 +98,6 @@ export interface UpdateBuidBindData {
|
|
|
104
98
|
* {
|
|
105
99
|
* buidUsername: '新用户名',
|
|
106
100
|
* guardLevel: 3,
|
|
107
|
-
* guardLevelText: '舰长',
|
|
108
101
|
* medalLevel: 21
|
|
109
102
|
* }
|
|
110
103
|
* ```
|
|
@@ -114,12 +107,8 @@ export interface UpdateBuidInfoData {
|
|
|
114
107
|
buidUsername?: string;
|
|
115
108
|
/** 当前舰长等级 */
|
|
116
109
|
guardLevel?: number;
|
|
117
|
-
/** 当前舰长等级文本 */
|
|
118
|
-
guardLevelText?: string;
|
|
119
110
|
/** 历史最高舰长等级 */
|
|
120
111
|
maxGuardLevel?: number;
|
|
121
|
-
/** 历史最高舰长等级文本 */
|
|
122
|
-
maxGuardLevelText?: string;
|
|
123
112
|
/** 粉丝牌名称 */
|
|
124
113
|
medalName?: string;
|
|
125
114
|
/** 粉丝牌等级 */
|
|
@@ -79,6 +79,20 @@ export declare class BindStatus {
|
|
|
79
79
|
* ```
|
|
80
80
|
*/
|
|
81
81
|
static hasCompletedAllBinds(bind: MCIDBIND | null | undefined): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* 检查是否只绑定了MC(未绑定B站)
|
|
84
|
+
*
|
|
85
|
+
* @param bind - 绑定记录(可为 null 或 undefined)
|
|
86
|
+
* @returns true 表示已绑定MC但未绑定B站
|
|
87
|
+
*/
|
|
88
|
+
static hasOnlyMcBind(bind: MCIDBIND | null | undefined): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* 检查是否只绑定了B站(未绑定MC)
|
|
91
|
+
*
|
|
92
|
+
* @param bind - 绑定记录(可为 null 或 undefined)
|
|
93
|
+
* @returns true 表示已绑定B站但未绑定MC
|
|
94
|
+
*/
|
|
95
|
+
static hasOnlyBuidBind(bind: MCIDBIND | null | undefined): boolean;
|
|
82
96
|
/**
|
|
83
97
|
* 获取绑定状态摘要信息
|
|
84
98
|
*
|
package/lib/utils/bind-status.js
CHANGED
|
@@ -111,6 +111,28 @@ class BindStatus {
|
|
|
111
111
|
static hasCompletedAllBinds(bind) {
|
|
112
112
|
return this.hasValidMcBind(bind) && this.hasValidBuidBind(bind);
|
|
113
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* 检查是否只绑定了MC(未绑定B站)
|
|
116
|
+
*
|
|
117
|
+
* @param bind - 绑定记录(可为 null 或 undefined)
|
|
118
|
+
* @returns true 表示已绑定MC但未绑定B站
|
|
119
|
+
*/
|
|
120
|
+
static hasOnlyMcBind(bind) {
|
|
121
|
+
return this.hasValidMcBind(bind) && !this.hasValidBuidBind(bind);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 检查是否只绑定了B站(未绑定MC)
|
|
125
|
+
*
|
|
126
|
+
* @param bind - 绑定记录(可为 null 或 undefined)
|
|
127
|
+
* @returns true 表示已绑定B站但未绑定MC
|
|
128
|
+
*/
|
|
129
|
+
static hasOnlyBuidBind(bind) {
|
|
130
|
+
return (bind !== null &&
|
|
131
|
+
bind !== undefined &&
|
|
132
|
+
!!bind.buidUid &&
|
|
133
|
+
!!bind.buidUsername &&
|
|
134
|
+
!this.hasValidMcBind(bind));
|
|
135
|
+
}
|
|
114
136
|
/**
|
|
115
137
|
* 获取绑定状态摘要信息
|
|
116
138
|
*
|
package/lib/utils/helpers.d.ts
CHANGED
|
@@ -89,15 +89,7 @@ export declare function levenshteinDistance(str1: string, str2: string): number;
|
|
|
89
89
|
* @returns 相似度值(0到1之间,1表示完全相同)
|
|
90
90
|
*/
|
|
91
91
|
export declare function calculateSimilarity(str1: string, str2: string): number;
|
|
92
|
-
|
|
93
|
-
* 规范化 Minecraft 用户名(统一小写,用于存储和比较)
|
|
94
|
-
* Minecraft 用户名不区分大小写,但 Mojang 返回的是规范大小写
|
|
95
|
-
* 为避免 "Notch" 和 "notch" 被视为不同用户,统一转小写存储
|
|
96
|
-
*
|
|
97
|
-
* @param username MC 用户名
|
|
98
|
-
* @param logger Koishi Logger实例(用于日志)
|
|
99
|
-
* @returns 规范化后的用户名(小写)
|
|
100
|
-
*/
|
|
92
|
+
export declare function getGuardLevelText(level: number): string;
|
|
101
93
|
export declare function normalizeUsername(username: string, logger?: Logger): string;
|
|
102
94
|
/**
|
|
103
95
|
* 比较两个 Minecraft 用户名是否相同(不区分大小写)
|
package/lib/utils/helpers.js
CHANGED
|
@@ -11,6 +11,7 @@ exports.escapeRegExp = escapeRegExp;
|
|
|
11
11
|
exports.cleanUserInput = cleanUserInput;
|
|
12
12
|
exports.levenshteinDistance = levenshteinDistance;
|
|
13
13
|
exports.calculateSimilarity = calculateSimilarity;
|
|
14
|
+
exports.getGuardLevelText = getGuardLevelText;
|
|
14
15
|
exports.normalizeUsername = normalizeUsername;
|
|
15
16
|
exports.isSameUsername = isSameUsername;
|
|
16
17
|
exports.extractBuidUsernameFromNickname = extractBuidUsernameFromNickname;
|
|
@@ -369,6 +370,10 @@ function calculateSimilarity(str1, str2) {
|
|
|
369
370
|
* @param logger Koishi Logger实例(用于日志)
|
|
370
371
|
* @returns 规范化后的用户名(小写)
|
|
371
372
|
*/
|
|
373
|
+
const GUARD_LEVEL_TEXT = { 1: '总督', 2: '提督', 3: '舰长' };
|
|
374
|
+
function getGuardLevelText(level) {
|
|
375
|
+
return GUARD_LEVEL_TEXT[level] || '';
|
|
376
|
+
}
|
|
372
377
|
function normalizeUsername(username, logger) {
|
|
373
378
|
if (!username) {
|
|
374
379
|
logger?.warn('[用户名规范化] 收到空用户名');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SupabaseConfig {
|
|
2
|
+
url: string;
|
|
3
|
+
key: string;
|
|
4
|
+
}
|
|
5
|
+
export declare class SupabaseClient {
|
|
6
|
+
private config;
|
|
7
|
+
private client;
|
|
8
|
+
constructor(config: SupabaseConfig);
|
|
9
|
+
get<T>(table: string, query?: string): Promise<T[]>;
|
|
10
|
+
post<T>(table: string, body: object | object[]): Promise<T>;
|
|
11
|
+
postMany<T>(table: string, body: object[]): Promise<T[]>;
|
|
12
|
+
patch(table: string, query: string, body: object): Promise<void>;
|
|
13
|
+
del(table: string, query: string): Promise<number>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SupabaseClient = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
class SupabaseClient {
|
|
9
|
+
config;
|
|
10
|
+
client;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.client = axios_1.default.create({
|
|
14
|
+
baseURL: `${config.url}/rest/v1`,
|
|
15
|
+
headers: {
|
|
16
|
+
apikey: config.key,
|
|
17
|
+
Authorization: `Bearer ${config.key}`,
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
Prefer: 'return=representation',
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async get(table, query = '') {
|
|
24
|
+
const url = query ? `/${table}?${query}` : `/${table}`;
|
|
25
|
+
const { data } = await this.client.get(url);
|
|
26
|
+
return data;
|
|
27
|
+
}
|
|
28
|
+
async post(table, body) {
|
|
29
|
+
const { data } = await this.client.post(`/${table}`, body);
|
|
30
|
+
return Array.isArray(data) ? data[0] : data;
|
|
31
|
+
}
|
|
32
|
+
async postMany(table, body) {
|
|
33
|
+
const { data } = await this.client.post(`/${table}`, body);
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
async patch(table, query, body) {
|
|
37
|
+
await this.client.patch(`/${table}?${query}`, body);
|
|
38
|
+
}
|
|
39
|
+
async del(table, query) {
|
|
40
|
+
const { data } = await this.client.delete(`/${table}?${query}`);
|
|
41
|
+
return Array.isArray(data) ? data.length : 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.SupabaseClient = SupabaseClient;
|