koishi-plugin-maibot 1.7.22 → 1.7.23
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 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +657 -529
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -58,6 +58,15 @@ exports.Config = koishi_1.Schema.object({
|
|
|
58
58
|
message: '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。',
|
|
59
59
|
}),
|
|
60
60
|
autoRecall: koishi_1.Schema.boolean().default(true).description('自动撤回用户发送的SGID消息(尝试撤回,如不支持则忽略)'),
|
|
61
|
+
queue: koishi_1.Schema.object({
|
|
62
|
+
enabled: koishi_1.Schema.boolean().default(false).description('队列系统开关,开启后限制并发请求'),
|
|
63
|
+
interval: koishi_1.Schema.number().default(10000).description('处理间隔(毫秒),默认10秒(10000毫秒),每间隔时间只处理一个请求'),
|
|
64
|
+
message: koishi_1.Schema.string().default('你正在排队,前面还有 {queuePosition} 人。预计等待 {queueEST} 秒。').description('队列提示消息模板(支持占位符:{queuePosition} 队列位置,{queueEST} 预计等待秒数)'),
|
|
65
|
+
}).description('请求队列配置').default({
|
|
66
|
+
enabled: false,
|
|
67
|
+
interval: 10000,
|
|
68
|
+
message: '你正在排队,前面还有 {queuePosition} 人。预计等待 {queueEST} 秒。',
|
|
69
|
+
}),
|
|
61
70
|
});
|
|
62
71
|
// 我认识了很多朋友 以下是我认识的好朋友们!
|
|
63
72
|
// Fracture_Hikaritsu
|
|
@@ -346,6 +355,84 @@ async function extractQRCodeFromSession(session, ctx) {
|
|
|
346
355
|
}
|
|
347
356
|
return null;
|
|
348
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* 队列管理器
|
|
360
|
+
*/
|
|
361
|
+
class RequestQueue {
|
|
362
|
+
constructor(interval) {
|
|
363
|
+
this.queue = [];
|
|
364
|
+
this.processing = false;
|
|
365
|
+
this.lastProcessTime = 0;
|
|
366
|
+
this.interval = interval;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* 加入队列并等待处理
|
|
370
|
+
* @returns Promise<number>,当轮到处理时resolve,返回加入队列时的位置(0表示直接执行,没有排队)
|
|
371
|
+
*/
|
|
372
|
+
async enqueue() {
|
|
373
|
+
// 如果队列为空且距离上次处理已过间隔时间,直接执行
|
|
374
|
+
if (this.queue.length === 0 && !this.processing) {
|
|
375
|
+
const now = Date.now();
|
|
376
|
+
const timeSinceLastProcess = now - this.lastProcessTime;
|
|
377
|
+
if (timeSinceLastProcess >= this.interval) {
|
|
378
|
+
this.lastProcessTime = now;
|
|
379
|
+
return Promise.resolve(0); // 0表示直接执行,没有排队
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// 需要加入队列
|
|
383
|
+
return new Promise((resolve, reject) => {
|
|
384
|
+
// 记录加入队列时的位置(这是用户前面的人数)
|
|
385
|
+
const queuePosition = this.queue.length;
|
|
386
|
+
this.queue.push({
|
|
387
|
+
resolve: () => resolve(queuePosition),
|
|
388
|
+
reject,
|
|
389
|
+
timestamp: Date.now(),
|
|
390
|
+
});
|
|
391
|
+
// 启动处理循环(如果还没启动)
|
|
392
|
+
if (!this.processing) {
|
|
393
|
+
// 使用setTimeout避免阻塞
|
|
394
|
+
setTimeout(() => {
|
|
395
|
+
this.processQueue();
|
|
396
|
+
}, 0);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* 处理队列
|
|
402
|
+
*/
|
|
403
|
+
async processQueue() {
|
|
404
|
+
while (this.queue.length > 0) {
|
|
405
|
+
this.processing = true;
|
|
406
|
+
// 等待间隔时间
|
|
407
|
+
const now = Date.now();
|
|
408
|
+
const timeSinceLastProcess = now - this.lastProcessTime;
|
|
409
|
+
if (timeSinceLastProcess < this.interval) {
|
|
410
|
+
await new Promise(resolve => setTimeout(resolve, this.interval - timeSinceLastProcess));
|
|
411
|
+
}
|
|
412
|
+
// 处理队列中的第一个任务
|
|
413
|
+
if (this.queue.length > 0) {
|
|
414
|
+
const task = this.queue.shift();
|
|
415
|
+
this.lastProcessTime = Date.now();
|
|
416
|
+
task.resolve();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
this.processing = false;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* 获取队列位置
|
|
423
|
+
*/
|
|
424
|
+
getQueuePosition() {
|
|
425
|
+
return this.queue.length;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* 获取预计等待时间(秒)
|
|
429
|
+
*/
|
|
430
|
+
getEstimatedWaitTime() {
|
|
431
|
+
const position = this.getQueuePosition();
|
|
432
|
+
const waitTime = position * (this.interval / 1000);
|
|
433
|
+
return Math.ceil(waitTime);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
349
436
|
/**
|
|
350
437
|
* 检查群是否在白名单中(如果白名单功能启用)
|
|
351
438
|
*/
|
|
@@ -660,6 +747,32 @@ function apply(ctx, config) {
|
|
|
660
747
|
timeout: config.apiTimeout,
|
|
661
748
|
});
|
|
662
749
|
const logger = ctx.logger('maibot');
|
|
750
|
+
// 初始化队列系统
|
|
751
|
+
const queueConfig = config.queue || { enabled: false, interval: 10000, message: '你正在排队,前面还有 {queuePosition} 人。预计等待 {queueEST} 秒。' };
|
|
752
|
+
const requestQueue = queueConfig.enabled ? new RequestQueue(queueConfig.interval) : null;
|
|
753
|
+
/**
|
|
754
|
+
* 队列包装函数:将命令action包装在队列中
|
|
755
|
+
*/
|
|
756
|
+
async function withQueue(session, action) {
|
|
757
|
+
if (!requestQueue) {
|
|
758
|
+
// 队列未启用,直接执行
|
|
759
|
+
return action();
|
|
760
|
+
}
|
|
761
|
+
// 加入队列并等待处理
|
|
762
|
+
const queuePosition = await requestQueue.enqueue();
|
|
763
|
+
// 如果前面有人(queuePosition > 0),说明用户排了队,发送队列提示
|
|
764
|
+
// 注意:这里queuePosition是加入队列时的位置,如果为0表示直接执行
|
|
765
|
+
if (queuePosition > 0) {
|
|
766
|
+
// 计算预计等待时间(基于加入时的位置)
|
|
767
|
+
const estimatedWait = Math.ceil(queuePosition * (queueConfig.interval / 1000));
|
|
768
|
+
const queueMessage = queueConfig.message
|
|
769
|
+
.replace(/{queuePosition}/g, String(queuePosition))
|
|
770
|
+
.replace(/{queueEST}/g, String(estimatedWait));
|
|
771
|
+
await session.send(queueMessage);
|
|
772
|
+
}
|
|
773
|
+
// 执行实际的操作
|
|
774
|
+
return action();
|
|
775
|
+
}
|
|
663
776
|
// 监听用户消息,尝试自动撤回包含SGID、水鱼token或落雪代码的消息
|
|
664
777
|
if (config.autoRecall !== false) {
|
|
665
778
|
ctx.on('message', async (session) => {
|
|
@@ -1167,172 +1280,175 @@ function apply(ctx, config) {
|
|
|
1167
1280
|
if (!whitelistCheck.allowed) {
|
|
1168
1281
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
1169
1282
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
if (!qrCode) {
|
|
1179
|
-
const actualTimeout = rebindTimeout;
|
|
1180
|
-
let promptMessageId;
|
|
1181
|
-
try {
|
|
1182
|
-
const sentMessage = await session.send(`请在${actualTimeout / 1000}秒内发送SGID(长按玩家二维码识别后发送)或公众号提供的网页地址`);
|
|
1183
|
-
if (typeof sentMessage === 'string') {
|
|
1184
|
-
promptMessageId = sentMessage;
|
|
1185
|
-
}
|
|
1186
|
-
else if (sentMessage && sentMessage.messageId) {
|
|
1187
|
-
promptMessageId = sentMessage.messageId;
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
catch (error) {
|
|
1191
|
-
ctx.logger('maibot').warn('发送提示消息失败:', error);
|
|
1283
|
+
// 使用队列系统
|
|
1284
|
+
return withQueue(session, async () => {
|
|
1285
|
+
const userId = session.userId;
|
|
1286
|
+
try {
|
|
1287
|
+
// 检查是否已绑定
|
|
1288
|
+
const existing = await ctx.database.get('maibot_bindings', { userId });
|
|
1289
|
+
if (existing.length > 0) {
|
|
1290
|
+
return `❌ 您已经绑定了账号\n绑定时间: ${new Date(existing[0].bindTime).toLocaleString('zh-CN')}\n\n如需重新绑定,请先使用 /mai解绑`;
|
|
1192
1291
|
}
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1292
|
+
// 如果没有提供SGID,提示用户输入
|
|
1293
|
+
if (!qrCode) {
|
|
1294
|
+
const actualTimeout = rebindTimeout;
|
|
1295
|
+
let promptMessageId;
|
|
1296
|
+
try {
|
|
1297
|
+
const sentMessage = await session.send(`请在${actualTimeout / 1000}秒内发送SGID(长按玩家二维码识别后发送)或公众号提供的网页地址`);
|
|
1298
|
+
if (typeof sentMessage === 'string') {
|
|
1299
|
+
promptMessageId = sentMessage;
|
|
1300
|
+
}
|
|
1301
|
+
else if (sentMessage && sentMessage.messageId) {
|
|
1302
|
+
promptMessageId = sentMessage.messageId;
|
|
1303
|
+
}
|
|
1199
1304
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1305
|
+
catch (error) {
|
|
1306
|
+
ctx.logger('maibot').warn('发送提示消息失败:', error);
|
|
1307
|
+
}
|
|
1308
|
+
try {
|
|
1309
|
+
logger.info(`开始等待用户 ${session.userId} 输入SGID,超时时间: ${actualTimeout}ms`);
|
|
1310
|
+
// 使用session.prompt等待用户输入SGID文本
|
|
1311
|
+
const promptText = await session.prompt(actualTimeout);
|
|
1312
|
+
if (!promptText || !promptText.trim()) {
|
|
1313
|
+
throw new Error('超时未收到响应');
|
|
1314
|
+
}
|
|
1315
|
+
const trimmed = promptText.trim();
|
|
1316
|
+
logger.debug(`收到用户输入: ${trimmed.substring(0, 50)}`);
|
|
1317
|
+
qrCode = trimmed;
|
|
1318
|
+
// 检查是否为公众号网页地址格式(https://wq.wahlap.net/qrcode/req/)
|
|
1319
|
+
const isLink = trimmed.includes('https://wq.wahlap.net/qrcode/req/');
|
|
1320
|
+
const isSGID = trimmed.startsWith('SGWCMAID');
|
|
1321
|
+
// 如果是网页地址,提取MAID并转换为SGWCMAID格式
|
|
1322
|
+
if (isLink) {
|
|
1323
|
+
try {
|
|
1324
|
+
// 从URL中提取MAID部分:https://wq.wahlap.net/qrcode/req/MAID2601...55.html?...
|
|
1325
|
+
// 匹配 /qrcode/req/ 后面的 MAID 开头的内容(到 .html 或 ? 之前)
|
|
1326
|
+
const match = trimmed.match(/qrcode\/req\/(MAID[^?\.]+)/i);
|
|
1327
|
+
if (match && match[1]) {
|
|
1328
|
+
const maid = match[1];
|
|
1329
|
+
// 在前面加上 SGWC 变成 SGWCMAID...
|
|
1330
|
+
qrCode = 'SGWC' + maid;
|
|
1331
|
+
logger.info(`从网页地址提取MAID并转换: ${maid.substring(0, 20)}... -> ${qrCode.substring(0, 24)}...`);
|
|
1332
|
+
}
|
|
1333
|
+
else {
|
|
1334
|
+
await session.send('⚠️ 无法从网页地址中提取MAID,请发送SGID文本(SGWCMAID开头)或公众号提供的网页地址');
|
|
1335
|
+
throw new Error('无法从网页地址中提取MAID');
|
|
1336
|
+
}
|
|
1217
1337
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1338
|
+
catch (error) {
|
|
1339
|
+
logger.warn('解析网页地址失败:', error);
|
|
1340
|
+
await session.send('⚠️ 网页地址格式错误,请发送SGID文本(SGWCMAID开头)或公众号提供的网页地址');
|
|
1341
|
+
throw new Error('网页地址格式错误');
|
|
1221
1342
|
}
|
|
1222
1343
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
throw new Error('网页地址格式错误');
|
|
1344
|
+
else if (!isSGID) {
|
|
1345
|
+
await session.send('⚠️ 未识别到有效的SGID格式或网页地址,请发送SGID文本(SGWCMAID开头)或公众号提供的网页地址(https://wq.wahlap.net/qrcode/req/...)');
|
|
1346
|
+
throw new Error('无效的二维码格式,必须是SGID文本或网页地址');
|
|
1227
1347
|
}
|
|
1348
|
+
// 验证SGID格式和长度
|
|
1349
|
+
if (!qrCode.startsWith('SGWCMAID')) {
|
|
1350
|
+
await session.send('⚠️ 未识别到有效的SGID格式,请发送SGID文本(SGWCMAID开头)或公众号提供的网页地址');
|
|
1351
|
+
throw new Error('无效的二维码格式,必须以 SGWCMAID 开头');
|
|
1352
|
+
}
|
|
1353
|
+
if (qrCode.length < 48 || qrCode.length > 128) {
|
|
1354
|
+
await session.send('❌ SGID长度错误,应在48-128字符之间');
|
|
1355
|
+
throw new Error('二维码长度错误,应在48-128字符之间');
|
|
1356
|
+
}
|
|
1357
|
+
logger.info(`✅ 接收到${isLink ? '网页地址(已转换)' : 'SGID'}: ${qrCode.substring(0, 50)}...`);
|
|
1358
|
+
// 发送识别中反馈
|
|
1359
|
+
await session.send('⏳ 正在处理,请稍候...');
|
|
1228
1360
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
throw new Error('二维码长度错误,应在48-128字符之间');
|
|
1361
|
+
catch (error) {
|
|
1362
|
+
logger.error(`等待用户输入二维码失败: ${error?.message}`, error);
|
|
1363
|
+
if (error.message?.includes('超时') || error.message?.includes('timeout') || error.message?.includes('未收到响应')) {
|
|
1364
|
+
await session.send(`❌ 绑定超时(${actualTimeout / 1000}秒),请稍后使用 /mai绑定 重新绑定`);
|
|
1365
|
+
return '❌ 超时未收到响应,绑定已取消';
|
|
1366
|
+
}
|
|
1367
|
+
if (error.message?.includes('无效的二维码')) {
|
|
1368
|
+
return `❌ 绑定失败:${error.message}`;
|
|
1369
|
+
}
|
|
1370
|
+
await session.send(`❌ 绑定过程中发生错误:${error?.message || '未知错误'}`);
|
|
1371
|
+
return `❌ 绑定失败:${error?.message || '未知错误'}`;
|
|
1241
1372
|
}
|
|
1242
|
-
logger.info(`✅ 接收到${isLink ? '网页地址(已转换)' : 'SGID'}: ${qrCode.substring(0, 50)}...`);
|
|
1243
|
-
// 发送识别中反馈
|
|
1244
|
-
await session.send('⏳ 正在处理,请稍候...');
|
|
1245
1373
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1374
|
+
// 检查是否为公众号网页地址格式(https://wq.wahlap.net/qrcode/req/)
|
|
1375
|
+
const isLink = qrCode.includes('https://wq.wahlap.net/qrcode/req/');
|
|
1376
|
+
const isSGID = qrCode.startsWith('SGWCMAID');
|
|
1377
|
+
// 如果是网页地址,提取MAID并转换为SGWCMAID格式
|
|
1378
|
+
if (isLink) {
|
|
1379
|
+
try {
|
|
1380
|
+
// 从URL中提取MAID部分:https://wq.wahlap.net/qrcode/req/MAID2601...55.html?...
|
|
1381
|
+
// 匹配 /qrcode/req/ 后面的 MAID 开头的内容(到 .html 或 ? 之前)
|
|
1382
|
+
const match = qrCode.match(/qrcode\/req\/(MAID[^?\.]+)/i);
|
|
1383
|
+
if (match && match[1]) {
|
|
1384
|
+
const maid = match[1];
|
|
1385
|
+
// 在前面加上 SGWC 变成 SGWCMAID...
|
|
1386
|
+
qrCode = 'SGWC' + maid;
|
|
1387
|
+
logger.info(`从网页地址提取MAID并转换: ${maid.substring(0, 20)}... -> ${qrCode.substring(0, 24)}...`);
|
|
1388
|
+
}
|
|
1389
|
+
else {
|
|
1390
|
+
return '❌ 无法从网页地址中提取MAID,请发送SGID文本(SGWCMAID开头)或公众号提供的网页地址';
|
|
1391
|
+
}
|
|
1251
1392
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1393
|
+
catch (error) {
|
|
1394
|
+
logger.warn('解析网页地址失败:', error);
|
|
1395
|
+
return '❌ 网页地址格式错误,请发送SGID文本(SGWCMAID开头)或公众号提供的网页地址';
|
|
1254
1396
|
}
|
|
1255
|
-
await session.send(`❌ 绑定过程中发生错误:${error?.message || '未知错误'}`);
|
|
1256
|
-
return `❌ 绑定失败:${error?.message || '未知错误'}`;
|
|
1257
1397
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1398
|
+
else if (!isSGID) {
|
|
1399
|
+
return '❌ 二维码格式错误,必须是SGID文本(SGWCMAID开头)或公众号提供的网页地址(https://wq.wahlap.net/qrcode/req/...)';
|
|
1400
|
+
}
|
|
1401
|
+
// 验证SGID格式和长度
|
|
1402
|
+
if (!qrCode.startsWith('SGWCMAID')) {
|
|
1403
|
+
return '❌ 二维码格式错误,必须以 SGWCMAID 开头';
|
|
1404
|
+
}
|
|
1405
|
+
if (qrCode.length < 48 || qrCode.length > 128) {
|
|
1406
|
+
return '❌ 二维码长度错误,应在48-128字符之间';
|
|
1407
|
+
}
|
|
1408
|
+
// 使用新API获取用户信息(需要client_id)
|
|
1409
|
+
const machineInfo = config.machineInfo;
|
|
1410
|
+
let previewResult;
|
|
1264
1411
|
try {
|
|
1265
|
-
|
|
1266
|
-
// 匹配 /qrcode/req/ 后面的 MAID 开头的内容(到 .html 或 ? 之前)
|
|
1267
|
-
const match = qrCode.match(/qrcode\/req\/(MAID[^?\.]+)/i);
|
|
1268
|
-
if (match && match[1]) {
|
|
1269
|
-
const maid = match[1];
|
|
1270
|
-
// 在前面加上 SGWC 变成 SGWCMAID...
|
|
1271
|
-
qrCode = 'SGWC' + maid;
|
|
1272
|
-
logger.info(`从网页地址提取MAID并转换: ${maid.substring(0, 20)}... -> ${qrCode.substring(0, 24)}...`);
|
|
1273
|
-
}
|
|
1274
|
-
else {
|
|
1275
|
-
return '❌ 无法从网页地址中提取MAID,请发送SGID文本(SGWCMAID开头)或公众号提供的网页地址';
|
|
1276
|
-
}
|
|
1412
|
+
previewResult = await api.getPreview(machineInfo.clientId, qrCode);
|
|
1277
1413
|
}
|
|
1278
1414
|
catch (error) {
|
|
1279
|
-
logger.
|
|
1280
|
-
return
|
|
1415
|
+
ctx.logger('maibot').error('获取用户预览信息失败:', error);
|
|
1416
|
+
return `❌ 绑定失败:无法从二维码获取用户信息\n错误信息: ${error?.message || '未知错误'}`;
|
|
1281
1417
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1418
|
+
// 检查是否获取成功
|
|
1419
|
+
if (previewResult.UserID === -1 || (typeof previewResult.UserID === 'string' && previewResult.UserID === '-1')) {
|
|
1420
|
+
return `❌ 绑定失败:无效或过期的二维码`;
|
|
1421
|
+
}
|
|
1422
|
+
// UserID在新API中是加密的字符串
|
|
1423
|
+
const maiUid = String(previewResult.UserID);
|
|
1424
|
+
const userName = previewResult.UserName;
|
|
1425
|
+
const rating = previewResult.Rating ? String(previewResult.Rating) : undefined;
|
|
1426
|
+
// 存储到数据库
|
|
1427
|
+
await ctx.database.create('maibot_bindings', {
|
|
1428
|
+
userId,
|
|
1429
|
+
maiUid,
|
|
1430
|
+
qrCode,
|
|
1431
|
+
bindTime: new Date(),
|
|
1432
|
+
userName,
|
|
1433
|
+
rating,
|
|
1434
|
+
});
|
|
1435
|
+
return `✅ 绑定成功!\n` +
|
|
1436
|
+
(userName ? `用户名: ${userName}\n` : '') +
|
|
1437
|
+
(rating ? `Rating: ${rating}\n` : '') +
|
|
1438
|
+
`绑定时间: ${new Date().toLocaleString('zh-CN')}\n\n` +
|
|
1439
|
+
`⚠️ 为了确保账户安全,请手动撤回群内包含SGID的消息`;
|
|
1298
1440
|
}
|
|
1299
1441
|
catch (error) {
|
|
1300
|
-
ctx.logger('maibot').error('
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
const maiUid = String(previewResult.UserID);
|
|
1309
|
-
const userName = previewResult.UserName;
|
|
1310
|
-
const rating = previewResult.Rating ? String(previewResult.Rating) : undefined;
|
|
1311
|
-
// 存储到数据库
|
|
1312
|
-
await ctx.database.create('maibot_bindings', {
|
|
1313
|
-
userId,
|
|
1314
|
-
maiUid,
|
|
1315
|
-
qrCode,
|
|
1316
|
-
bindTime: new Date(),
|
|
1317
|
-
userName,
|
|
1318
|
-
rating,
|
|
1319
|
-
});
|
|
1320
|
-
return `✅ 绑定成功!\n` +
|
|
1321
|
-
(userName ? `用户名: ${userName}\n` : '') +
|
|
1322
|
-
(rating ? `Rating: ${rating}\n` : '') +
|
|
1323
|
-
`绑定时间: ${new Date().toLocaleString('zh-CN')}\n\n` +
|
|
1324
|
-
`⚠️ 为了确保账户安全,请手动撤回群内包含SGID的消息`;
|
|
1325
|
-
}
|
|
1326
|
-
catch (error) {
|
|
1327
|
-
ctx.logger('maibot').error('绑定失败:', error);
|
|
1328
|
-
if (maintenanceMode) {
|
|
1329
|
-
return maintenanceMessage;
|
|
1330
|
-
}
|
|
1331
|
-
if (error?.response) {
|
|
1332
|
-
return `❌ API请求失败: ${error.response.status} ${error.response.statusText}\n\n${maintenanceMessage}`;
|
|
1442
|
+
ctx.logger('maibot').error('绑定失败:', error);
|
|
1443
|
+
if (maintenanceMode) {
|
|
1444
|
+
return maintenanceMessage;
|
|
1445
|
+
}
|
|
1446
|
+
if (error?.response) {
|
|
1447
|
+
return `❌ API请求失败: ${error.response.status} ${error.response.statusText}\n\n${maintenanceMessage}`;
|
|
1448
|
+
}
|
|
1449
|
+
return `❌ 绑定失败: ${error?.message || '未知错误'}\n\n${maintenanceMessage}`;
|
|
1333
1450
|
}
|
|
1334
|
-
|
|
1335
|
-
}
|
|
1451
|
+
});
|
|
1336
1452
|
});
|
|
1337
1453
|
/**
|
|
1338
1454
|
* 解绑用户
|
|
@@ -1383,131 +1499,134 @@ function apply(ctx, config) {
|
|
|
1383
1499
|
if (!whitelistCheck.allowed) {
|
|
1384
1500
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
1385
1501
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
const { binding, isProxy, error } = await getTargetBinding(session, targetUserId);
|
|
1389
|
-
if (error || !binding) {
|
|
1390
|
-
return error || '❌ 获取用户绑定失败';
|
|
1391
|
-
}
|
|
1392
|
-
const userId = binding.userId;
|
|
1393
|
-
let statusInfo = `✅ 已绑定账号\n\n` +
|
|
1394
|
-
`绑定时间: ${new Date(binding.bindTime).toLocaleString('zh-CN')}\n` +
|
|
1395
|
-
`🚨 /maialert查看账号提醒状态\n`;
|
|
1396
|
-
// 尝试获取最新状态并更新数据库(需要新二维码)
|
|
1502
|
+
// 使用队列系统
|
|
1503
|
+
return withQueue(session, async () => {
|
|
1397
1504
|
try {
|
|
1398
|
-
//
|
|
1399
|
-
const
|
|
1400
|
-
if (
|
|
1401
|
-
|
|
1505
|
+
// 获取目标用户绑定
|
|
1506
|
+
const { binding, isProxy, error } = await getTargetBinding(session, targetUserId);
|
|
1507
|
+
if (error || !binding) {
|
|
1508
|
+
return error || '❌ 获取用户绑定失败';
|
|
1402
1509
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
const
|
|
1417
|
-
//
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
const
|
|
1428
|
-
//
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1510
|
+
const userId = binding.userId;
|
|
1511
|
+
let statusInfo = `✅ 已绑定账号\n\n` +
|
|
1512
|
+
`绑定时间: ${new Date(binding.bindTime).toLocaleString('zh-CN')}\n` +
|
|
1513
|
+
`🚨 /maialert查看账号提醒状态\n`;
|
|
1514
|
+
// 尝试获取最新状态并更新数据库(需要新二维码)
|
|
1515
|
+
try {
|
|
1516
|
+
// 废弃旧的uid策略,每次都需要新的二维码
|
|
1517
|
+
const qrTextResult = await getQrText(session, ctx, api, binding, config, rebindTimeout, '请在60秒内发送SGID(长按玩家二维码识别后发送)或公众号提供的网页地址以查询账号状态');
|
|
1518
|
+
if (qrTextResult.error) {
|
|
1519
|
+
statusInfo += `\n⚠️ 无法获取最新状态:${qrTextResult.error}`;
|
|
1520
|
+
}
|
|
1521
|
+
else {
|
|
1522
|
+
try {
|
|
1523
|
+
const preview = await api.getPreview(machineInfo.clientId, qrTextResult.qrText);
|
|
1524
|
+
// 更新数据库中的用户名和Rating
|
|
1525
|
+
await ctx.database.set('maibot_bindings', { userId }, {
|
|
1526
|
+
userName: preview.UserName,
|
|
1527
|
+
rating: preview.Rating ? String(preview.Rating) : undefined,
|
|
1528
|
+
});
|
|
1529
|
+
// 格式化版本信息
|
|
1530
|
+
let versionInfo = '';
|
|
1531
|
+
if (preview.RomVersion && preview.DataVersion) {
|
|
1532
|
+
// 机台版本:取前两个数字,如 1.52.00 -> 1.52
|
|
1533
|
+
const romVersionMatch = preview.RomVersion.match(/^(\d+\.\d+)/);
|
|
1534
|
+
const romVersion = romVersionMatch ? romVersionMatch[1] : preview.RomVersion;
|
|
1535
|
+
// 数据版本:取前两个数字 + 最后两个数字转换为字母,如 1.50.09 -> 1.50 - I
|
|
1536
|
+
const dataVersionPrefixMatch = preview.DataVersion.match(/^(\d+\.\d+)/);
|
|
1537
|
+
const dataVersionPrefix = dataVersionPrefixMatch ? dataVersionPrefixMatch[1] : preview.DataVersion;
|
|
1538
|
+
// 从版本号末尾提取最后两位数字,如 "1.50.01" -> "01", "1.50.09" -> "09"
|
|
1539
|
+
// 匹配最后一个点后的数字(确保只匹配版本号末尾)
|
|
1540
|
+
let dataVersionLetter = '';
|
|
1541
|
+
// 匹配最后一个点后的1-2位数字
|
|
1542
|
+
const dataVersionMatch = preview.DataVersion.match(/\.(\d{1,2})$/);
|
|
1543
|
+
if (dataVersionMatch) {
|
|
1544
|
+
// 提取数字字符串,如 "09" 或 "9"
|
|
1545
|
+
const digitsStr = dataVersionMatch[1];
|
|
1546
|
+
// 转换为数字,如 "09" -> 9, "9" -> 9
|
|
1547
|
+
const versionNumber = parseInt(digitsStr, 10);
|
|
1548
|
+
// 验证转换是否正确
|
|
1549
|
+
if (!isNaN(versionNumber) && versionNumber >= 1) {
|
|
1550
|
+
// 01 -> A, 02 -> B, ..., 09 -> I, 10 -> J, ..., 26 -> Z
|
|
1551
|
+
// 使用模运算确保在 A-Z 范围内循环(27 -> A, 28 -> B, ...)
|
|
1552
|
+
const letterIndex = ((versionNumber - 1) % 26) + 1;
|
|
1553
|
+
// 转换为大写字母:A=65, B=66, ..., Z=90
|
|
1554
|
+
dataVersionLetter = String.fromCharCode(64 + letterIndex).toUpperCase();
|
|
1555
|
+
}
|
|
1437
1556
|
}
|
|
1557
|
+
versionInfo = `机台版本: ${romVersion}\n` +
|
|
1558
|
+
`数据版本: ${dataVersionPrefix} - ${dataVersionLetter}\n`;
|
|
1438
1559
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1560
|
+
statusInfo += `\n📊 账号信息:\n` +
|
|
1561
|
+
`用户名: ${preview.UserName || '未知'}\n` +
|
|
1562
|
+
`Rating: ${preview.Rating || '未知'}\n` +
|
|
1563
|
+
(versionInfo ? versionInfo : '') +
|
|
1564
|
+
`登录状态: ${preview.IsLogin === true ? '已登录' : '未登录'}\n` +
|
|
1565
|
+
`封禁状态: ${preview.BanState === 0 ? '正常' : '已封禁'}\n`;
|
|
1566
|
+
}
|
|
1567
|
+
catch (error) {
|
|
1568
|
+
logger.warn('获取用户预览信息失败:', error);
|
|
1569
|
+
statusInfo += `\n⚠️ 无法获取最新状态,请检查API服务`;
|
|
1441
1570
|
}
|
|
1442
|
-
statusInfo += `\n📊 账号信息:\n` +
|
|
1443
|
-
`用户名: ${preview.UserName || '未知'}\n` +
|
|
1444
|
-
`Rating: ${preview.Rating || '未知'}\n` +
|
|
1445
|
-
(versionInfo ? versionInfo : '') +
|
|
1446
|
-
`登录状态: ${preview.IsLogin === true ? '已登录' : '未登录'}\n` +
|
|
1447
|
-
`封禁状态: ${preview.BanState === 0 ? '正常' : '已封禁'}\n`;
|
|
1448
|
-
}
|
|
1449
|
-
catch (error) {
|
|
1450
|
-
logger.warn('获取用户预览信息失败:', error);
|
|
1451
|
-
statusInfo += `\n⚠️ 无法获取最新状态,请检查API服务`;
|
|
1452
1571
|
}
|
|
1453
1572
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1573
|
+
catch (error) {
|
|
1574
|
+
// 如果获取失败,使用缓存的信息
|
|
1575
|
+
if (binding.userName) {
|
|
1576
|
+
statusInfo += `\n📊 账号信息(缓存):\n` +
|
|
1577
|
+
`用户名: ${binding.userName}\n` +
|
|
1578
|
+
(binding.rating ? `Rating: ${binding.rating}\n` : '');
|
|
1579
|
+
}
|
|
1580
|
+
statusInfo += `\n⚠️ 无法获取最新状态,请检查API服务`;
|
|
1461
1581
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
if (binding.fishToken) {
|
|
1466
|
-
statusInfo += `\n\n🐟 水鱼Token: 已绑定`;
|
|
1467
|
-
}
|
|
1468
|
-
else {
|
|
1469
|
-
statusInfo += `\n\n🐟 水鱼Token: 未绑定\n使用 /mai绑定水鱼 <token> 进行绑定`;
|
|
1470
|
-
}
|
|
1471
|
-
// 显示落雪代码绑定状态
|
|
1472
|
-
if (binding.lxnsCode) {
|
|
1473
|
-
statusInfo += `\n\n❄️ 落雪代码: 已绑定`;
|
|
1474
|
-
}
|
|
1475
|
-
else {
|
|
1476
|
-
statusInfo += `\n\n❄️ 落雪代码: 未绑定\n使用 /mai绑定落雪 <lxns_code> 进行绑定`;
|
|
1477
|
-
}
|
|
1478
|
-
// 显示保护模式状态(如果未隐藏)
|
|
1479
|
-
if (!hideLockAndProtection) {
|
|
1480
|
-
if (binding.protectionMode) {
|
|
1481
|
-
statusInfo += `\n\n🛡️ 保护模式: 已开启\n使用 /mai保护模式 off 关闭`;
|
|
1582
|
+
// 显示水鱼Token绑定状态
|
|
1583
|
+
if (binding.fishToken) {
|
|
1584
|
+
statusInfo += `\n\n🐟 水鱼Token: 已绑定`;
|
|
1482
1585
|
}
|
|
1483
1586
|
else {
|
|
1484
|
-
statusInfo += `\n\n
|
|
1587
|
+
statusInfo += `\n\n🐟 水鱼Token: 未绑定\n使用 /mai绑定水鱼 <token> 进行绑定`;
|
|
1485
1588
|
}
|
|
1486
|
-
//
|
|
1487
|
-
if (binding.
|
|
1488
|
-
|
|
1489
|
-
? new Date(binding.lockTime).toLocaleString('zh-CN')
|
|
1490
|
-
: '未知';
|
|
1491
|
-
statusInfo += `\n\n🔒 锁定状态: 已锁定`;
|
|
1492
|
-
statusInfo += `\n锁定时间: ${lockTime}`;
|
|
1493
|
-
statusInfo += `\n使用 /mai解锁 可以解锁账号`;
|
|
1589
|
+
// 显示落雪代码绑定状态
|
|
1590
|
+
if (binding.lxnsCode) {
|
|
1591
|
+
statusInfo += `\n\n❄️ 落雪代码: 已绑定`;
|
|
1494
1592
|
}
|
|
1495
1593
|
else {
|
|
1496
|
-
statusInfo += `\n\n
|
|
1594
|
+
statusInfo += `\n\n❄️ 落雪代码: 未绑定\n使用 /mai绑定落雪 <lxns_code> 进行绑定`;
|
|
1497
1595
|
}
|
|
1596
|
+
// 显示保护模式状态(如果未隐藏)
|
|
1597
|
+
if (!hideLockAndProtection) {
|
|
1598
|
+
if (binding.protectionMode) {
|
|
1599
|
+
statusInfo += `\n\n🛡️ 保护模式: 已开启\n使用 /mai保护模式 off 关闭`;
|
|
1600
|
+
}
|
|
1601
|
+
else {
|
|
1602
|
+
statusInfo += `\n\n🛡️ 保护模式: 未开启\n使用 /mai保护模式 on 开启(自动锁定已下线的账号)`;
|
|
1603
|
+
}
|
|
1604
|
+
// 显示锁定状态(不显示LoginId)
|
|
1605
|
+
if (binding.isLocked) {
|
|
1606
|
+
const lockTime = binding.lockTime
|
|
1607
|
+
? new Date(binding.lockTime).toLocaleString('zh-CN')
|
|
1608
|
+
: '未知';
|
|
1609
|
+
statusInfo += `\n\n🔒 锁定状态: 已锁定`;
|
|
1610
|
+
statusInfo += `\n锁定时间: ${lockTime}`;
|
|
1611
|
+
statusInfo += `\n使用 /mai解锁 可以解锁账号`;
|
|
1612
|
+
}
|
|
1613
|
+
else {
|
|
1614
|
+
statusInfo += `\n\n🔒 锁定状态: 未锁定\n使用 /mai锁定 可以锁定账号(防止他人登录)`;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
// 显示票券信息
|
|
1618
|
+
// @deprecated getCharge功能已在新API中移除,已注释
|
|
1619
|
+
statusInfo += `\n\n🎫 票券情况: 此功能已在新API中移除`;
|
|
1620
|
+
return statusInfo;
|
|
1498
1621
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
ctx.logger('maibot').error('查询状态失败:', error);
|
|
1506
|
-
if (maintenanceMode) {
|
|
1507
|
-
return maintenanceMessage;
|
|
1622
|
+
catch (error) {
|
|
1623
|
+
ctx.logger('maibot').error('查询状态失败:', error);
|
|
1624
|
+
if (maintenanceMode) {
|
|
1625
|
+
return maintenanceMessage;
|
|
1626
|
+
}
|
|
1627
|
+
return `❌ 查询失败: ${error?.message || '未知错误'}\n\n${maintenanceMessage}`;
|
|
1508
1628
|
}
|
|
1509
|
-
|
|
1510
|
-
}
|
|
1629
|
+
});
|
|
1511
1630
|
});
|
|
1512
1631
|
/**
|
|
1513
1632
|
* 锁定账号(登录保持)
|
|
@@ -1905,100 +2024,103 @@ function apply(ctx, config) {
|
|
|
1905
2024
|
if (!Number.isInteger(multiple) || multiple < 2 || multiple > 6) {
|
|
1906
2025
|
return '❌ 倍数必须是2-6之间的整数\n例如:/mai发票 3\n例如:/mai发票 6 @userid';
|
|
1907
2026
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
const proxyTip = isProxy ? `(代操作用户 ${userId})` : '';
|
|
1916
|
-
// 确认操作(如果未使用 -bypass)
|
|
1917
|
-
if (!options?.bypass) {
|
|
1918
|
-
const baseTip = `⚠️ 即将发放 ${multiple} 倍票${proxyTip}`;
|
|
1919
|
-
const confirmFirst = await promptYesLocal(session, `${baseTip}\n操作具有风险,请谨慎`);
|
|
1920
|
-
if (!confirmFirst) {
|
|
1921
|
-
return '操作已取消(第一次确认未通过)';
|
|
1922
|
-
}
|
|
1923
|
-
const confirmSecond = await promptYesLocal(session, '二次确认:若理解风险,请再次输入 Y 执行');
|
|
1924
|
-
if (!confirmSecond) {
|
|
1925
|
-
return '操作已取消(第二次确认未通过)';
|
|
1926
|
-
}
|
|
1927
|
-
if (multiple >= 3) {
|
|
1928
|
-
const confirmThird = await promptYesLocal(session, '第三次确认:3倍及以上票券风险更高,确定继续?');
|
|
1929
|
-
if (!confirmThird) {
|
|
1930
|
-
return '操作已取消(第三次确认未通过)';
|
|
1931
|
-
}
|
|
2027
|
+
// 使用队列系统
|
|
2028
|
+
return withQueue(session, async () => {
|
|
2029
|
+
try {
|
|
2030
|
+
// 获取目标用户绑定
|
|
2031
|
+
const { binding, isProxy, error } = await getTargetBinding(session, targetUserId);
|
|
2032
|
+
if (error || !binding) {
|
|
2033
|
+
return error || '❌ 获取用户绑定失败';
|
|
1932
2034
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
const
|
|
1939
|
-
if (!
|
|
1940
|
-
return
|
|
2035
|
+
const userId = binding.userId;
|
|
2036
|
+
const proxyTip = isProxy ? `(代操作用户 ${userId})` : '';
|
|
2037
|
+
// 确认操作(如果未使用 -bypass)
|
|
2038
|
+
if (!options?.bypass) {
|
|
2039
|
+
const baseTip = `⚠️ 即将发放 ${multiple} 倍票${proxyTip}`;
|
|
2040
|
+
const confirmFirst = await promptYesLocal(session, `${baseTip}\n操作具有风险,请谨慎`);
|
|
2041
|
+
if (!confirmFirst) {
|
|
2042
|
+
return '操作已取消(第一次确认未通过)';
|
|
1941
2043
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
if (retryQrText.error) {
|
|
1946
|
-
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
2044
|
+
const confirmSecond = await promptYesLocal(session, '二次确认:若理解风险,请再次输入 Y 执行');
|
|
2045
|
+
if (!confirmSecond) {
|
|
2046
|
+
return '操作已取消(第二次确认未通过)';
|
|
1947
2047
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
2048
|
+
if (multiple >= 3) {
|
|
2049
|
+
const confirmThird = await promptYesLocal(session, '第三次确认:3倍及以上票券风险更高,确定继续?');
|
|
2050
|
+
if (!confirmThird) {
|
|
2051
|
+
return '操作已取消(第三次确认未通过)';
|
|
2052
|
+
}
|
|
1953
2053
|
}
|
|
1954
|
-
return `✅ 已发放 ${multiple} 倍票\n请稍等几分钟在游戏内确认`;
|
|
1955
2054
|
}
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
2055
|
+
// 获取qr_text(交互式或从绑定中获取)
|
|
2056
|
+
const qrTextResult = await getQrText(session, ctx, api, binding, config, rebindTimeout);
|
|
2057
|
+
if (qrTextResult.error) {
|
|
2058
|
+
if (qrTextResult.needRebind) {
|
|
2059
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2060
|
+
if (!rebindResult.success) {
|
|
2061
|
+
return `❌ 重新绑定失败:${rebindResult.error || '未知错误'}\n请使用 /mai绑定 重新绑定二维码`;
|
|
2062
|
+
}
|
|
2063
|
+
// 重新绑定成功后,使用新的binding
|
|
2064
|
+
const updatedBinding = rebindResult.newBinding || binding;
|
|
2065
|
+
const retryQrText = await getQrText(session, ctx, api, updatedBinding, config, rebindTimeout);
|
|
2066
|
+
if (retryQrText.error) {
|
|
2067
|
+
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
2068
|
+
}
|
|
2069
|
+
// 使用新的qrText继续
|
|
2070
|
+
await session.send('请求成功提交,请等待服务器响应。(通常需要2-3分钟)');
|
|
2071
|
+
const ticketResult = await api.getTicket(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, multiple, retryQrText.qrText);
|
|
2072
|
+
if (!ticketResult.TicketStatus || !ticketResult.LoginStatus || !ticketResult.LogoutStatus) {
|
|
2073
|
+
return '❌ 发放功能票失败:服务器返回未成功,请稍后再试';
|
|
2074
|
+
}
|
|
2075
|
+
return `✅ 已发放 ${multiple} 倍票\n请稍等几分钟在游戏内确认`;
|
|
1972
2076
|
}
|
|
1973
|
-
|
|
2077
|
+
return `❌ 获取二维码失败:${qrTextResult.error}`;
|
|
1974
2078
|
}
|
|
1975
|
-
|
|
1976
|
-
|
|
2079
|
+
await session.send('请求成功提交,请等待服务器响应。(通常需要2-3分钟)');
|
|
2080
|
+
// 使用新API获取功能票(需要qr_text)
|
|
2081
|
+
let ticketResult;
|
|
2082
|
+
try {
|
|
2083
|
+
ticketResult = await api.getTicket(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, multiple, qrTextResult.qrText);
|
|
1977
2084
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2085
|
+
catch (error) {
|
|
2086
|
+
// 如果API返回失败,可能需要重新绑定
|
|
2087
|
+
const failureResult = await handleApiFailure(session, ctx, api, binding, config, error, rebindTimeout);
|
|
2088
|
+
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
2089
|
+
// 重新绑定成功,重试获取功能票
|
|
2090
|
+
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
2091
|
+
if (retryQrText.error) {
|
|
2092
|
+
return `❌ 重新绑定后获取二维码失败:${retryQrText.error}`;
|
|
2093
|
+
}
|
|
2094
|
+
ticketResult = await api.getTicket(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, multiple, retryQrText.qrText);
|
|
2095
|
+
}
|
|
2096
|
+
else {
|
|
2097
|
+
throw error;
|
|
1985
2098
|
}
|
|
1986
|
-
return `❌ 发放功能票失败:服务器返回未成功\n重新绑定失败:${rebindResult.error || '未知错误'}`;
|
|
1987
2099
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2100
|
+
if (!ticketResult.TicketStatus || !ticketResult.LoginStatus || !ticketResult.LogoutStatus) {
|
|
2101
|
+
// 如果返回失败,可能需要重新绑定
|
|
2102
|
+
if (!ticketResult.QrStatus || ticketResult.LoginStatus === false) {
|
|
2103
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2104
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
2105
|
+
return `✅ 重新绑定成功!请重新执行发票操作。`;
|
|
2106
|
+
}
|
|
2107
|
+
return `❌ 发放功能票失败:服务器返回未成功\n重新绑定失败:${rebindResult.error || '未知错误'}`;
|
|
2108
|
+
}
|
|
2109
|
+
return '❌ 发票失败:服务器返回未成功,请确认是否已在短时间内多次执行发票指令或稍后再试或点击获取二维码刷新账号后再试。';
|
|
2110
|
+
}
|
|
2111
|
+
return `✅ 已发放 ${multiple} 倍票\n请稍等几分钟在游戏内确认`;
|
|
1996
2112
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
2113
|
+
catch (error) {
|
|
2114
|
+
logger.error('发票失败:', error);
|
|
2115
|
+
if (maintenanceMode) {
|
|
2116
|
+
return maintenanceMessage;
|
|
2117
|
+
}
|
|
2118
|
+
if (error?.response) {
|
|
2119
|
+
return `❌ API请求失败: ${error.response.status} ${error.response.statusText}\n\n${maintenanceMessage}`;
|
|
2120
|
+
}
|
|
2121
|
+
return `❌ 发票失败: ${error?.message || '未知错误'}\n\n${maintenanceMessage}`;
|
|
1999
2122
|
}
|
|
2000
|
-
|
|
2001
|
-
}
|
|
2123
|
+
});
|
|
2002
2124
|
});
|
|
2003
2125
|
/**
|
|
2004
2126
|
* 舞里程发放 / 签到
|
|
@@ -2103,109 +2225,112 @@ function apply(ctx, config) {
|
|
|
2103
2225
|
if (!whitelistCheck.allowed) {
|
|
2104
2226
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
2105
2227
|
}
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
if (
|
|
2128
|
-
|
|
2228
|
+
// 使用队列系统
|
|
2229
|
+
return withQueue(session, async () => {
|
|
2230
|
+
try {
|
|
2231
|
+
// 获取目标用户绑定
|
|
2232
|
+
const { binding, isProxy, error } = await getTargetBinding(session, targetUserId);
|
|
2233
|
+
if (error || !binding) {
|
|
2234
|
+
return error || '❌ 获取用户绑定失败';
|
|
2235
|
+
}
|
|
2236
|
+
const userId = binding.userId;
|
|
2237
|
+
// 检查是否已绑定水鱼Token
|
|
2238
|
+
if (!binding.fishToken) {
|
|
2239
|
+
return '❌ 请先绑定水鱼Token\n使用 /mai绑定水鱼 <token> 进行绑定';
|
|
2240
|
+
}
|
|
2241
|
+
// 维护时间内直接提示,不发起上传请求
|
|
2242
|
+
const maintenanceMsg = getMaintenanceMessage(maintenanceNotice);
|
|
2243
|
+
if (maintenanceMsg) {
|
|
2244
|
+
return maintenanceMsg;
|
|
2245
|
+
}
|
|
2246
|
+
// 获取qr_text(交互式或从绑定中获取)
|
|
2247
|
+
const qrTextResult = await getQrText(session, ctx, api, binding, config, rebindTimeout);
|
|
2248
|
+
if (qrTextResult.error) {
|
|
2249
|
+
if (qrTextResult.needRebind) {
|
|
2250
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2251
|
+
if (!rebindResult.success) {
|
|
2252
|
+
return `❌ 重新绑定失败:${rebindResult.error || '未知错误'}\n请使用 /mai绑定 重新绑定二维码`;
|
|
2253
|
+
}
|
|
2254
|
+
// 重新绑定成功后,使用新的binding
|
|
2255
|
+
const updatedBinding = rebindResult.newBinding || binding;
|
|
2256
|
+
const retryQrText = await getQrText(session, ctx, api, updatedBinding, config, rebindTimeout);
|
|
2257
|
+
if (retryQrText.error) {
|
|
2258
|
+
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
2259
|
+
}
|
|
2260
|
+
// 使用新的qrText继续
|
|
2261
|
+
const result = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, binding.fishToken);
|
|
2262
|
+
if (!result.UploadStatus) {
|
|
2263
|
+
if (result.msg === '该账号下存在未完成的任务') {
|
|
2264
|
+
return '⚠️ 当前账号已有未完成的水鱼B50任务,请稍后再试,无需重复上传。';
|
|
2265
|
+
}
|
|
2266
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2267
|
+
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
2268
|
+
}
|
|
2269
|
+
scheduleB50Notification(session, result.task_id);
|
|
2270
|
+
return `✅ B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
2129
2271
|
}
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2272
|
+
return `❌ 获取二维码失败:${qrTextResult.error}`;
|
|
2273
|
+
}
|
|
2274
|
+
// 上传B50(使用新API,需要qr_text)
|
|
2275
|
+
let result;
|
|
2276
|
+
try {
|
|
2277
|
+
result = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, qrTextResult.qrText, binding.fishToken);
|
|
2278
|
+
}
|
|
2279
|
+
catch (error) {
|
|
2280
|
+
// 如果API返回失败,可能需要重新绑定
|
|
2281
|
+
const failureResult = await handleApiFailure(session, ctx, api, binding, config, error, rebindTimeout);
|
|
2282
|
+
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
2283
|
+
// 重新绑定成功,重试上传
|
|
2284
|
+
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
2285
|
+
if (retryQrText.error) {
|
|
2286
|
+
return `❌ 重新绑定后获取二维码失败:${retryQrText.error}`;
|
|
2287
|
+
}
|
|
2288
|
+
result = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, binding.fishToken);
|
|
2289
|
+
}
|
|
2290
|
+
else {
|
|
2291
|
+
throw error;
|
|
2135
2292
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
if (
|
|
2139
|
-
|
|
2140
|
-
|
|
2293
|
+
}
|
|
2294
|
+
if (!result.UploadStatus) {
|
|
2295
|
+
if (result.msg === '该账号下存在未完成的任务') {
|
|
2296
|
+
return '⚠️ 当前账号已有未完成的水鱼B50任务,请耐心等待任务完成,预计1-10分钟,无需重复上传。';
|
|
2297
|
+
}
|
|
2298
|
+
// 如果返回失败,可能需要重新绑定
|
|
2299
|
+
if (result.msg?.includes('二维码') || result.msg?.includes('qr_text') || result.msg?.includes('无效')) {
|
|
2300
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2301
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
2302
|
+
return `✅ 重新绑定成功!请重新执行上传操作。`;
|
|
2141
2303
|
}
|
|
2142
2304
|
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2143
|
-
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
2305
|
+
return `❌ 上传失败:${result.msg || '未知错误'}\n重新绑定失败:${rebindResult.error || '未知错误'}${taskIdInfo}`;
|
|
2144
2306
|
}
|
|
2145
|
-
|
|
2146
|
-
return
|
|
2307
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2308
|
+
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
2147
2309
|
}
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
// 上传B50(使用新API,需要qr_text)
|
|
2151
|
-
let result;
|
|
2152
|
-
try {
|
|
2153
|
-
result = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, qrTextResult.qrText, binding.fishToken);
|
|
2310
|
+
scheduleB50Notification(session, result.task_id);
|
|
2311
|
+
return `✅ B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
2154
2312
|
}
|
|
2155
2313
|
catch (error) {
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
// 重新绑定成功,重试上传
|
|
2160
|
-
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
2161
|
-
if (retryQrText.error) {
|
|
2162
|
-
return `❌ 重新绑定后获取二维码失败:${retryQrText.error}`;
|
|
2163
|
-
}
|
|
2164
|
-
result = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, binding.fishToken);
|
|
2165
|
-
}
|
|
2166
|
-
else {
|
|
2167
|
-
throw error;
|
|
2314
|
+
ctx.logger('maibot').error('上传B50失败:', error);
|
|
2315
|
+
if (maintenanceMode) {
|
|
2316
|
+
return maintenanceMessage;
|
|
2168
2317
|
}
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
if (result.msg?.includes('二维码') || result.msg?.includes('qr_text') || result.msg?.includes('无效')) {
|
|
2176
|
-
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2177
|
-
if (rebindResult.success && rebindResult.newBinding) {
|
|
2178
|
-
return `✅ 重新绑定成功!请重新执行上传操作。`;
|
|
2318
|
+
// 处理请求超时类错误,统一提示
|
|
2319
|
+
if (error?.code === 'ECONNABORTED' || String(error?.message || '').includes('timeout')) {
|
|
2320
|
+
let msg = '水鱼B50任务 上传失败,请稍后再试一次。';
|
|
2321
|
+
const maintenanceMsg = getMaintenanceMessage(maintenanceNotice);
|
|
2322
|
+
if (maintenanceMsg) {
|
|
2323
|
+
msg += `\n${maintenanceMsg}`;
|
|
2179
2324
|
}
|
|
2180
|
-
|
|
2181
|
-
return
|
|
2325
|
+
msg += `\n\n${maintenanceMessage}`;
|
|
2326
|
+
return msg;
|
|
2182
2327
|
}
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
}
|
|
2186
|
-
scheduleB50Notification(session, result.task_id);
|
|
2187
|
-
return `✅ B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
2188
|
-
}
|
|
2189
|
-
catch (error) {
|
|
2190
|
-
ctx.logger('maibot').error('上传B50失败:', error);
|
|
2191
|
-
if (maintenanceMode) {
|
|
2192
|
-
return maintenanceMessage;
|
|
2193
|
-
}
|
|
2194
|
-
// 处理请求超时类错误,统一提示
|
|
2195
|
-
if (error?.code === 'ECONNABORTED' || String(error?.message || '').includes('timeout')) {
|
|
2196
|
-
let msg = '水鱼B50任务 上传失败,请稍后再试一次。';
|
|
2197
|
-
const maintenanceMsg = getMaintenanceMessage(maintenanceNotice);
|
|
2198
|
-
if (maintenanceMsg) {
|
|
2199
|
-
msg += `\n${maintenanceMsg}`;
|
|
2328
|
+
if (error?.response) {
|
|
2329
|
+
return `❌ API请求失败: ${error.response.status} ${error.response.statusText}\n\n${maintenanceMessage}`;
|
|
2200
2330
|
}
|
|
2201
|
-
|
|
2202
|
-
return msg;
|
|
2331
|
+
return `❌ 上传失败: ${error?.message || '未知错误'}\n\n${maintenanceMessage}`;
|
|
2203
2332
|
}
|
|
2204
|
-
|
|
2205
|
-
return `❌ API请求失败: ${error.response.status} ${error.response.statusText}\n\n${maintenanceMessage}`;
|
|
2206
|
-
}
|
|
2207
|
-
return `❌ 上传失败: ${error?.message || '未知错误'}\n\n${maintenanceMessage}`;
|
|
2208
|
-
}
|
|
2333
|
+
});
|
|
2209
2334
|
});
|
|
2210
2335
|
/**
|
|
2211
2336
|
* 清空功能票
|
|
@@ -2665,122 +2790,125 @@ function apply(ctx, config) {
|
|
|
2665
2790
|
if (!whitelistCheck.allowed) {
|
|
2666
2791
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
2667
2792
|
}
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
// 确定使用的落雪代码
|
|
2676
|
-
let finalLxnsCode;
|
|
2677
|
-
if (lxnsCode) {
|
|
2678
|
-
// 如果提供了参数,使用参数
|
|
2679
|
-
// 验证落雪代码长度
|
|
2680
|
-
if (lxnsCode.length !== 15) {
|
|
2681
|
-
return '❌ 落雪代码长度错误,必须为15个字符';
|
|
2793
|
+
// 使用队列系统
|
|
2794
|
+
return withQueue(session, async () => {
|
|
2795
|
+
try {
|
|
2796
|
+
// 获取目标用户绑定
|
|
2797
|
+
const { binding, isProxy, error } = await getTargetBinding(session, targetUserId);
|
|
2798
|
+
if (error || !binding) {
|
|
2799
|
+
return error || '❌ 获取用户绑定失败';
|
|
2682
2800
|
}
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
}
|
|
2692
|
-
// 维护时间内直接提示,不发起上传请求
|
|
2693
|
-
const maintenanceMsg = getMaintenanceMessage(maintenanceNotice);
|
|
2694
|
-
if (maintenanceMsg) {
|
|
2695
|
-
return maintenanceMsg;
|
|
2696
|
-
}
|
|
2697
|
-
// 获取qr_text(交互式或从绑定中获取)
|
|
2698
|
-
const qrTextResult = await getQrText(session, ctx, api, binding, config, rebindTimeout);
|
|
2699
|
-
if (qrTextResult.error) {
|
|
2700
|
-
if (qrTextResult.needRebind) {
|
|
2701
|
-
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2702
|
-
if (!rebindResult.success) {
|
|
2703
|
-
return `❌ 重新绑定失败:${rebindResult.error || '未知错误'}\n请使用 /mai绑定 重新绑定二维码`;
|
|
2801
|
+
const userId = binding.userId;
|
|
2802
|
+
// 确定使用的落雪代码
|
|
2803
|
+
let finalLxnsCode;
|
|
2804
|
+
if (lxnsCode) {
|
|
2805
|
+
// 如果提供了参数,使用参数
|
|
2806
|
+
// 验证落雪代码长度
|
|
2807
|
+
if (lxnsCode.length !== 15) {
|
|
2808
|
+
return '❌ 落雪代码长度错误,必须为15个字符';
|
|
2704
2809
|
}
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2810
|
+
finalLxnsCode = lxnsCode;
|
|
2811
|
+
}
|
|
2812
|
+
else {
|
|
2813
|
+
// 如果没有提供参数,使用绑定的代码
|
|
2814
|
+
if (!binding.lxnsCode) {
|
|
2815
|
+
return '❌ 请先绑定落雪代码或提供落雪代码参数\n使用 /mai绑定落雪 <lxns_code> 进行绑定\n或使用 /mai上传落雪b50 <lxns_code> 直接提供代码';
|
|
2710
2816
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2817
|
+
finalLxnsCode = binding.lxnsCode;
|
|
2818
|
+
}
|
|
2819
|
+
// 维护时间内直接提示,不发起上传请求
|
|
2820
|
+
const maintenanceMsg = getMaintenanceMessage(maintenanceNotice);
|
|
2821
|
+
if (maintenanceMsg) {
|
|
2822
|
+
return maintenanceMsg;
|
|
2823
|
+
}
|
|
2824
|
+
// 获取qr_text(交互式或从绑定中获取)
|
|
2825
|
+
const qrTextResult = await getQrText(session, ctx, api, binding, config, rebindTimeout);
|
|
2826
|
+
if (qrTextResult.error) {
|
|
2827
|
+
if (qrTextResult.needRebind) {
|
|
2828
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2829
|
+
if (!rebindResult.success) {
|
|
2830
|
+
return `❌ 重新绑定失败:${rebindResult.error || '未知错误'}\n请使用 /mai绑定 重新绑定二维码`;
|
|
2716
2831
|
}
|
|
2717
|
-
|
|
2718
|
-
|
|
2832
|
+
// 重新绑定成功后,使用新的binding
|
|
2833
|
+
const updatedBinding = rebindResult.newBinding || binding;
|
|
2834
|
+
const retryQrText = await getQrText(session, ctx, api, updatedBinding, config, rebindTimeout);
|
|
2835
|
+
if (retryQrText.error) {
|
|
2836
|
+
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
2837
|
+
}
|
|
2838
|
+
// 使用新的qrText继续
|
|
2839
|
+
const result = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, finalLxnsCode);
|
|
2840
|
+
if (!result.UploadStatus) {
|
|
2841
|
+
if (result.msg === '该账号下存在未完成的任务') {
|
|
2842
|
+
return '⚠️ 当前账号已有未完成的落雪B50任务,请稍后再试,无需重复上传。';
|
|
2843
|
+
}
|
|
2844
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2845
|
+
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
2846
|
+
}
|
|
2847
|
+
scheduleLxB50Notification(session, result.task_id);
|
|
2848
|
+
return `✅ 落雪B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
2719
2849
|
}
|
|
2720
|
-
|
|
2721
|
-
return `✅ 落雪B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
2850
|
+
return `❌ 获取二维码失败:${qrTextResult.error}`;
|
|
2722
2851
|
}
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2852
|
+
// 上传落雪B50(使用新API,需要qr_text)
|
|
2853
|
+
let result;
|
|
2854
|
+
try {
|
|
2855
|
+
result = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, qrTextResult.qrText, finalLxnsCode);
|
|
2856
|
+
}
|
|
2857
|
+
catch (error) {
|
|
2858
|
+
// 如果API返回失败,可能需要重新绑定
|
|
2859
|
+
const failureResult = await handleApiFailure(session, ctx, api, binding, config, error, rebindTimeout);
|
|
2860
|
+
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
2861
|
+
// 重新绑定成功,重试上传
|
|
2862
|
+
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
2863
|
+
if (retryQrText.error) {
|
|
2864
|
+
return `❌ 重新绑定后获取二维码失败:${retryQrText.error}`;
|
|
2865
|
+
}
|
|
2866
|
+
result = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, finalLxnsCode);
|
|
2867
|
+
}
|
|
2868
|
+
else {
|
|
2869
|
+
throw error;
|
|
2738
2870
|
}
|
|
2739
|
-
result = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, finalLxnsCode);
|
|
2740
2871
|
}
|
|
2741
|
-
|
|
2742
|
-
|
|
2872
|
+
if (!result.UploadStatus) {
|
|
2873
|
+
if (result.msg === '该账号下存在未完成的任务') {
|
|
2874
|
+
return '⚠️ 当前账号已有未完成的落雪B50任务,请耐心等待任务完成,预计1-10分钟,无需重复上传。';
|
|
2875
|
+
}
|
|
2876
|
+
// 如果返回失败,可能需要重新绑定
|
|
2877
|
+
if (result.msg?.includes('二维码') || result.msg?.includes('qr_text') || result.msg?.includes('无效')) {
|
|
2878
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2879
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
2880
|
+
return `✅ 重新绑定成功!请重新执行上传操作。`;
|
|
2881
|
+
}
|
|
2882
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2883
|
+
return `❌ 上传失败:${result.msg || '未知错误'}\n重新绑定失败:${rebindResult.error || '未知错误'}${taskIdInfo}`;
|
|
2884
|
+
}
|
|
2885
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2886
|
+
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
2743
2887
|
}
|
|
2888
|
+
scheduleLxB50Notification(session, result.task_id);
|
|
2889
|
+
return `✅ 落雪B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
2744
2890
|
}
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2891
|
+
catch (error) {
|
|
2892
|
+
ctx.logger('maibot').error('上传落雪B50失败:', error);
|
|
2893
|
+
if (maintenanceMode) {
|
|
2894
|
+
return maintenanceMessage;
|
|
2748
2895
|
}
|
|
2749
|
-
//
|
|
2750
|
-
if (
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2896
|
+
// 处理请求超时类错误,统一提示
|
|
2897
|
+
if (error?.code === 'ECONNABORTED' || String(error?.message || '').includes('timeout')) {
|
|
2898
|
+
let msg = '落雪B50任务 上传失败,请稍后再试一次。';
|
|
2899
|
+
const maintenanceMsg = getMaintenanceMessage(maintenanceNotice);
|
|
2900
|
+
if (maintenanceMsg) {
|
|
2901
|
+
msg += `\n${maintenanceMsg}`;
|
|
2754
2902
|
}
|
|
2755
|
-
|
|
2756
|
-
return
|
|
2903
|
+
msg += `\n\n${maintenanceMessage}`;
|
|
2904
|
+
return msg;
|
|
2757
2905
|
}
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
}
|
|
2761
|
-
scheduleLxB50Notification(session, result.task_id);
|
|
2762
|
-
return `✅ 落雪B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
2763
|
-
}
|
|
2764
|
-
catch (error) {
|
|
2765
|
-
ctx.logger('maibot').error('上传落雪B50失败:', error);
|
|
2766
|
-
if (maintenanceMode) {
|
|
2767
|
-
return maintenanceMessage;
|
|
2768
|
-
}
|
|
2769
|
-
// 处理请求超时类错误,统一提示
|
|
2770
|
-
if (error?.code === 'ECONNABORTED' || String(error?.message || '').includes('timeout')) {
|
|
2771
|
-
let msg = '落雪B50任务 上传失败,请稍后再试一次。';
|
|
2772
|
-
const maintenanceMsg = getMaintenanceMessage(maintenanceNotice);
|
|
2773
|
-
if (maintenanceMsg) {
|
|
2774
|
-
msg += `\n${maintenanceMsg}`;
|
|
2906
|
+
if (error?.response) {
|
|
2907
|
+
return `❌ API请求失败: ${error.response.status} ${error.response.statusText}\n\n${maintenanceMessage}`;
|
|
2775
2908
|
}
|
|
2776
|
-
|
|
2777
|
-
return msg;
|
|
2778
|
-
}
|
|
2779
|
-
if (error?.response) {
|
|
2780
|
-
return `❌ API请求失败: ${error.response.status} ${error.response.statusText}\n\n${maintenanceMessage}`;
|
|
2909
|
+
return `❌ 上传失败: ${error?.message || '未知错误'}\n\n${maintenanceMessage}`;
|
|
2781
2910
|
}
|
|
2782
|
-
|
|
2783
|
-
}
|
|
2911
|
+
});
|
|
2784
2912
|
});
|
|
2785
2913
|
// 查询落雪B50任务状态功能已暂时取消
|
|
2786
2914
|
/**
|