koishi-plugin-maibot 1.7.31 → 1.7.33
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/LICENSE +201 -0
- package/lib/database.d.ts +17 -0
- package/lib/database.d.ts.map +1 -1
- package/lib/database.js +23 -1
- package/lib/database.js.map +1 -1
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +703 -158
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -67,6 +67,13 @@ exports.Config = koishi_1.Schema.object({
|
|
|
67
67
|
interval: 10000,
|
|
68
68
|
message: '你正在排队,前面还有 {queuePosition} 人。预计等待 {queueEST} 秒。',
|
|
69
69
|
}),
|
|
70
|
+
operationLog: koishi_1.Schema.object({
|
|
71
|
+
enabled: koishi_1.Schema.boolean().default(true).description('操作记录开关,开启后记录所有操作'),
|
|
72
|
+
refIdLabel: koishi_1.Schema.string().default('Ref_ID').description('Ref_ID 显示标签(可自定义),默认 "Ref_ID"'),
|
|
73
|
+
}).description('操作记录配置').default({
|
|
74
|
+
enabled: true,
|
|
75
|
+
refIdLabel: 'Ref_ID',
|
|
76
|
+
}),
|
|
70
77
|
});
|
|
71
78
|
// 我认识了很多朋友 以下是我认识的好朋友们!
|
|
72
79
|
// Fracture_Hikaritsu
|
|
@@ -674,13 +681,26 @@ async function waitForUserReply(session, ctx, timeout) {
|
|
|
674
681
|
}
|
|
675
682
|
/**
|
|
676
683
|
* 交互式获取二维码文本(qr_text)
|
|
677
|
-
*
|
|
678
|
-
*
|
|
684
|
+
* 支持10分钟内使用上次输入的SGID缓存
|
|
685
|
+
* 如果缓存存在且有效,直接使用;否则提示用户输入
|
|
679
686
|
*/
|
|
680
|
-
async function getQrText(session, ctx, api, binding, config, timeout = 60000, promptMessage
|
|
687
|
+
async function getQrText(session, ctx, api, binding, config, timeout = 60000, promptMessage, useCache = true // 是否使用缓存(默认启用)
|
|
688
|
+
) {
|
|
681
689
|
const logger = ctx.logger('maibot');
|
|
682
|
-
//
|
|
683
|
-
|
|
690
|
+
// 如果启用缓存且binding存在,检查是否有10分钟内的SGID缓存
|
|
691
|
+
if (useCache && binding && binding.lastQrCode && binding.lastQrCodeTime) {
|
|
692
|
+
const cacheAge = Date.now() - new Date(binding.lastQrCodeTime).getTime();
|
|
693
|
+
const cacheValidDuration = 10 * 60 * 1000; // 10分钟
|
|
694
|
+
if (cacheAge < cacheValidDuration && binding.lastQrCode.startsWith('SGWCMAID')) {
|
|
695
|
+
logger.info(`使用缓存的SGID(${Math.floor(cacheAge / 1000)}秒前输入)`);
|
|
696
|
+
// 直接返回缓存的SGID,不验证(让调用方验证,如果失败再提示输入)
|
|
697
|
+
return { qrText: binding.lastQrCode, fromCache: true };
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
logger.debug(`缓存已过期(${Math.floor(cacheAge / 1000)}秒前输入,超过10分钟)`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
// 没有有效缓存,提示用户输入
|
|
684
704
|
const actualTimeout = timeout;
|
|
685
705
|
const message = promptMessage || `请在${actualTimeout / 1000}秒内发送SGID(长按玩家二维码识别后发送)或公众号提供的网页地址`;
|
|
686
706
|
try {
|
|
@@ -749,12 +769,14 @@ async function getQrText(session, ctx, api, binding, config, timeout = 60000, pr
|
|
|
749
769
|
await session.send('❌ 无效或过期的二维码,请重新发送');
|
|
750
770
|
return { qrText: '', error: '无效或过期的二维码' };
|
|
751
771
|
}
|
|
752
|
-
// 如果binding存在,更新数据库中的qrCode
|
|
772
|
+
// 如果binding存在,更新数据库中的qrCode和缓存
|
|
753
773
|
if (binding) {
|
|
754
774
|
await ctx.database.set('maibot_bindings', { userId: binding.userId }, {
|
|
755
775
|
qrCode: qrText,
|
|
776
|
+
lastQrCode: qrText, // 更新缓存
|
|
777
|
+
lastQrCodeTime: new Date(), // 更新时间戳
|
|
756
778
|
});
|
|
757
|
-
logger.info(`已更新用户 ${binding.userId} 的qrCode
|
|
779
|
+
logger.info(`已更新用户 ${binding.userId} 的qrCode和缓存`);
|
|
758
780
|
}
|
|
759
781
|
return { qrText: qrText };
|
|
760
782
|
}
|
|
@@ -907,6 +929,8 @@ async function promptForRebind(session, ctx, api, binding, config, timeout = 600
|
|
|
907
929
|
bindTime: new Date(),
|
|
908
930
|
userName,
|
|
909
931
|
rating,
|
|
932
|
+
lastQrCode: qrCode, // 保存为缓存
|
|
933
|
+
lastQrCodeTime: new Date(), // 保存时间戳
|
|
910
934
|
});
|
|
911
935
|
// 发送成功反馈
|
|
912
936
|
await session.send(`✅ 重新绑定成功!${userName ? `\n用户名: ${userName}` : ''}${rating ? `\nRating: ${rating}` : ''}\n\n⚠️ 为了确保账户安全,请手动撤回群内包含SGID的消息`);
|
|
@@ -942,6 +966,54 @@ function apply(ctx, config) {
|
|
|
942
966
|
// 初始化队列系统
|
|
943
967
|
const queueConfig = config.queue || { enabled: false, interval: 10000, message: '你正在排队,前面还有 {queuePosition} 人。预计等待 {queueEST} 秒。' };
|
|
944
968
|
const requestQueue = queueConfig.enabled ? new RequestQueue(queueConfig.interval) : null;
|
|
969
|
+
// 操作记录配置
|
|
970
|
+
const operationLogConfig = config.operationLog || { enabled: true, refIdLabel: 'Ref_ID' };
|
|
971
|
+
/**
|
|
972
|
+
* 生成唯一的 ref_id
|
|
973
|
+
*/
|
|
974
|
+
function generateRefId() {
|
|
975
|
+
const timestamp = Date.now().toString(36);
|
|
976
|
+
const random = Math.random().toString(36).substring(2, 9);
|
|
977
|
+
return `${timestamp}-${random}`.toUpperCase();
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* 记录操作日志
|
|
981
|
+
*/
|
|
982
|
+
async function logOperation(params) {
|
|
983
|
+
if (!operationLogConfig.enabled) {
|
|
984
|
+
return '';
|
|
985
|
+
}
|
|
986
|
+
const refId = generateRefId();
|
|
987
|
+
try {
|
|
988
|
+
await ctx.database.create('maibot_operation_logs', {
|
|
989
|
+
refId,
|
|
990
|
+
command: params.command,
|
|
991
|
+
userId: params.session.userId || '',
|
|
992
|
+
targetUserId: params.targetUserId,
|
|
993
|
+
guildId: params.session.guildId || undefined,
|
|
994
|
+
channelId: params.session.channelId || undefined,
|
|
995
|
+
status: params.status,
|
|
996
|
+
result: params.result,
|
|
997
|
+
errorMessage: params.errorMessage,
|
|
998
|
+
apiResponse: params.apiResponse ? JSON.stringify(params.apiResponse) : undefined,
|
|
999
|
+
createdAt: new Date(),
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
catch (error) {
|
|
1003
|
+
logger.warn(`记录操作日志失败: ${error?.message || '未知错误'}`);
|
|
1004
|
+
}
|
|
1005
|
+
return refId;
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* 在结果消息中添加 Ref_ID
|
|
1009
|
+
*/
|
|
1010
|
+
function appendRefId(message, refId) {
|
|
1011
|
+
if (!refId || !operationLogConfig.enabled) {
|
|
1012
|
+
return message;
|
|
1013
|
+
}
|
|
1014
|
+
const label = operationLogConfig.refIdLabel || 'Ref_ID';
|
|
1015
|
+
return `${message}\n${label}: ${refId}`;
|
|
1016
|
+
}
|
|
945
1017
|
/**
|
|
946
1018
|
* 在API调用前加入队列并等待
|
|
947
1019
|
* 这个函数应该在获取到SGID后、调用API前使用
|
|
@@ -1652,11 +1724,27 @@ function apply(ctx, config) {
|
|
|
1652
1724
|
}
|
|
1653
1725
|
catch (error) {
|
|
1654
1726
|
ctx.logger('maibot').error('获取用户预览信息失败:', error);
|
|
1655
|
-
|
|
1727
|
+
const errorMessage = `❌ 绑定失败:无法从二维码获取用户信息\n错误信息: ${error?.message || '未知错误'}`;
|
|
1728
|
+
const refId = await logOperation({
|
|
1729
|
+
command: 'mai绑定',
|
|
1730
|
+
session,
|
|
1731
|
+
status: 'error',
|
|
1732
|
+
errorMessage: error?.message || '未知错误',
|
|
1733
|
+
apiResponse: error?.response?.data,
|
|
1734
|
+
});
|
|
1735
|
+
return appendRefId(errorMessage, refId);
|
|
1656
1736
|
}
|
|
1657
1737
|
// 检查是否获取成功
|
|
1658
1738
|
if (previewResult.UserID === -1 || (typeof previewResult.UserID === 'string' && previewResult.UserID === '-1')) {
|
|
1659
|
-
|
|
1739
|
+
const errorMessage = `❌ 绑定失败:无效或过期的二维码`;
|
|
1740
|
+
const refId = await logOperation({
|
|
1741
|
+
command: 'mai绑定',
|
|
1742
|
+
session,
|
|
1743
|
+
status: 'failure',
|
|
1744
|
+
errorMessage: '无效或过期的二维码',
|
|
1745
|
+
apiResponse: previewResult,
|
|
1746
|
+
});
|
|
1747
|
+
return appendRefId(errorMessage, refId);
|
|
1660
1748
|
}
|
|
1661
1749
|
// UserID在新API中是加密的字符串
|
|
1662
1750
|
const maiUid = String(previewResult.UserID);
|
|
@@ -1670,22 +1758,37 @@ function apply(ctx, config) {
|
|
|
1670
1758
|
bindTime: new Date(),
|
|
1671
1759
|
userName,
|
|
1672
1760
|
rating,
|
|
1761
|
+
lastQrCode: qrCode, // 保存为缓存
|
|
1762
|
+
lastQrCodeTime: new Date(), // 保存时间戳
|
|
1673
1763
|
});
|
|
1674
|
-
|
|
1764
|
+
const successMessage = `✅ 绑定成功!\n` +
|
|
1675
1765
|
(userName ? `用户名: ${userName}\n` : '') +
|
|
1676
1766
|
(rating ? `Rating: ${rating}\n` : '') +
|
|
1677
1767
|
`绑定时间: ${new Date().toLocaleString('zh-CN')}\n\n` +
|
|
1678
1768
|
`⚠️ 为了确保账户安全,请手动撤回群内包含SGID的消息`;
|
|
1769
|
+
const refId = await logOperation({
|
|
1770
|
+
command: 'mai绑定',
|
|
1771
|
+
session,
|
|
1772
|
+
status: 'success',
|
|
1773
|
+
result: successMessage,
|
|
1774
|
+
});
|
|
1775
|
+
return appendRefId(successMessage, refId);
|
|
1679
1776
|
}
|
|
1680
1777
|
catch (error) {
|
|
1681
1778
|
ctx.logger('maibot').error('绑定失败:', error);
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1779
|
+
const errorMessage = maintenanceMode
|
|
1780
|
+
? maintenanceMessage
|
|
1781
|
+
: (error?.response
|
|
1782
|
+
? `❌ API请求失败: ${error.response.status} ${error.response.statusText}\n\n${maintenanceMessage}`
|
|
1783
|
+
: `❌ 绑定失败: ${error?.message || '未知错误'}\n\n${maintenanceMessage}`);
|
|
1784
|
+
const refId = await logOperation({
|
|
1785
|
+
command: 'mai绑定',
|
|
1786
|
+
session,
|
|
1787
|
+
status: 'error',
|
|
1788
|
+
errorMessage: error?.message || '未知错误',
|
|
1789
|
+
apiResponse: error?.response?.data,
|
|
1790
|
+
});
|
|
1791
|
+
return appendRefId(errorMessage, refId);
|
|
1689
1792
|
}
|
|
1690
1793
|
});
|
|
1691
1794
|
/**
|
|
@@ -1757,10 +1860,14 @@ function apply(ctx, config) {
|
|
|
1757
1860
|
statusInfo += `\n⚠️ 无法获取最新状态:${qrTextResult.error}`;
|
|
1758
1861
|
}
|
|
1759
1862
|
else {
|
|
1760
|
-
// 在调用API
|
|
1863
|
+
// 在调用API前加入队列(只调用一次)
|
|
1761
1864
|
await waitForQueue(session);
|
|
1762
1865
|
try {
|
|
1763
|
-
|
|
1866
|
+
// 同时获取 preview 和 getCharge(并行执行,避免重复排队)
|
|
1867
|
+
const [preview, chargeResult] = await Promise.all([
|
|
1868
|
+
api.getPreview(machineInfo.clientId, qrTextResult.qrText),
|
|
1869
|
+
api.getCharge(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, qrTextResult.qrText)
|
|
1870
|
+
]);
|
|
1764
1871
|
// 更新数据库中的用户名和Rating
|
|
1765
1872
|
await ctx.database.set('maibot_bindings', { userId }, {
|
|
1766
1873
|
userName: preview.UserName,
|
|
@@ -1803,6 +1910,9 @@ function apply(ctx, config) {
|
|
|
1803
1910
|
(versionInfo ? versionInfo : '') +
|
|
1804
1911
|
`登录状态: ${preview.IsLogin === true ? '已登录' : '未登录'}\n` +
|
|
1805
1912
|
`封禁状态: ${preview.BanState === 0 ? '正常' : '已封禁'}\n`;
|
|
1913
|
+
// 保存 chargeResult 供后续使用
|
|
1914
|
+
qrTextResultForCharge = { ...qrTextResult };
|
|
1915
|
+
qrTextResultForCharge.chargeResult = chargeResult;
|
|
1806
1916
|
}
|
|
1807
1917
|
catch (error) {
|
|
1808
1918
|
logger.warn('获取用户预览信息失败:', error);
|
|
@@ -1857,9 +1967,17 @@ function apply(ctx, config) {
|
|
|
1857
1967
|
// 显示票券信息(使用新的getCharge API)
|
|
1858
1968
|
try {
|
|
1859
1969
|
if (qrTextResultForCharge && !qrTextResultForCharge.error) {
|
|
1860
|
-
//
|
|
1861
|
-
|
|
1862
|
-
|
|
1970
|
+
// 如果已经在上面获取了 chargeResult,直接使用;否则重新获取
|
|
1971
|
+
let chargeResult;
|
|
1972
|
+
if (qrTextResultForCharge.chargeResult) {
|
|
1973
|
+
// 已经在上面并行获取了,直接使用
|
|
1974
|
+
chargeResult = qrTextResultForCharge.chargeResult;
|
|
1975
|
+
}
|
|
1976
|
+
else {
|
|
1977
|
+
// 如果上面获取失败,这里重新获取(需要排队)
|
|
1978
|
+
await waitForQueue(session);
|
|
1979
|
+
chargeResult = await api.getCharge(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, qrTextResultForCharge.qrText);
|
|
1980
|
+
}
|
|
1863
1981
|
if (chargeResult.ChargeStatus && chargeResult.userChargeList) {
|
|
1864
1982
|
const now = new Date();
|
|
1865
1983
|
const validTickets = [];
|
|
@@ -1918,10 +2036,26 @@ function apply(ctx, config) {
|
|
|
1918
2036
|
logger.warn('获取票券信息失败:', error);
|
|
1919
2037
|
statusInfo += `\n\n🎫 票券情况: 获取失败(${error?.message || '未知错误'})`;
|
|
1920
2038
|
}
|
|
1921
|
-
|
|
2039
|
+
const refId = await logOperation({
|
|
2040
|
+
command: 'mai状态',
|
|
2041
|
+
session,
|
|
2042
|
+
targetUserId,
|
|
2043
|
+
status: 'success',
|
|
2044
|
+
result: statusInfo,
|
|
2045
|
+
});
|
|
2046
|
+
return appendRefId(statusInfo, refId);
|
|
1922
2047
|
}
|
|
1923
2048
|
catch (error) {
|
|
1924
2049
|
ctx.logger('maibot').error('查询状态失败:', error);
|
|
2050
|
+
const errorMessage = `❌ 查询状态失败: ${error?.message || '未知错误'}`;
|
|
2051
|
+
const refId = await logOperation({
|
|
2052
|
+
command: 'mai状态',
|
|
2053
|
+
session,
|
|
2054
|
+
targetUserId,
|
|
2055
|
+
status: 'error',
|
|
2056
|
+
errorMessage: error?.message || '未知错误',
|
|
2057
|
+
});
|
|
2058
|
+
return appendRefId(errorMessage, refId);
|
|
1925
2059
|
if (maintenanceMode) {
|
|
1926
2060
|
return maintenanceMessage;
|
|
1927
2061
|
}
|
|
@@ -2400,46 +2534,100 @@ function apply(ctx, config) {
|
|
|
2400
2534
|
await session.send('请求成功提交,请等待服务器响应。(通常需要2-3分钟)');
|
|
2401
2535
|
// 使用新API获取功能票(需要qr_text)
|
|
2402
2536
|
let ticketResult;
|
|
2537
|
+
let usedCache = qrTextResult.fromCache === true;
|
|
2403
2538
|
try {
|
|
2404
2539
|
ticketResult = await api.getTicket(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, multiple, qrTextResult.qrText);
|
|
2405
2540
|
}
|
|
2406
2541
|
catch (error) {
|
|
2407
|
-
//
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
//
|
|
2411
|
-
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
2542
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
2543
|
+
if (usedCache) {
|
|
2544
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
2545
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
2412
2546
|
if (retryQrText.error) {
|
|
2413
|
-
return `❌
|
|
2547
|
+
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
2414
2548
|
}
|
|
2549
|
+
// 在调用API前加入队列
|
|
2550
|
+
await waitForQueue(session);
|
|
2415
2551
|
ticketResult = await api.getTicket(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, multiple, retryQrText.qrText);
|
|
2416
2552
|
}
|
|
2417
2553
|
else {
|
|
2418
|
-
|
|
2554
|
+
// 如果API返回失败,可能需要重新绑定
|
|
2555
|
+
const failureResult = await handleApiFailure(session, ctx, api, binding, config, error, rebindTimeout);
|
|
2556
|
+
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
2557
|
+
// 重新绑定成功,重试获取功能票
|
|
2558
|
+
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
2559
|
+
if (retryQrText.error) {
|
|
2560
|
+
return `❌ 重新绑定后获取二维码失败:${retryQrText.error}`;
|
|
2561
|
+
}
|
|
2562
|
+
// 在调用API前加入队列
|
|
2563
|
+
await waitForQueue(session);
|
|
2564
|
+
ticketResult = await api.getTicket(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, multiple, retryQrText.qrText);
|
|
2565
|
+
}
|
|
2566
|
+
else {
|
|
2567
|
+
throw error;
|
|
2568
|
+
}
|
|
2419
2569
|
}
|
|
2420
2570
|
}
|
|
2421
2571
|
if (!ticketResult.TicketStatus || !ticketResult.LoginStatus || !ticketResult.LogoutStatus) {
|
|
2422
|
-
//
|
|
2423
|
-
if (!ticketResult.QrStatus || ticketResult.LoginStatus === false) {
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2572
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
2573
|
+
if (usedCache && (!ticketResult.QrStatus || ticketResult.LoginStatus === false)) {
|
|
2574
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
2575
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
2576
|
+
if (retryQrText.error) {
|
|
2577
|
+
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
2578
|
+
}
|
|
2579
|
+
// 在调用API前加入队列
|
|
2580
|
+
await waitForQueue(session);
|
|
2581
|
+
ticketResult = await api.getTicket(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, multiple, retryQrText.qrText);
|
|
2582
|
+
if (!ticketResult.TicketStatus || !ticketResult.LoginStatus || !ticketResult.LogoutStatus) {
|
|
2583
|
+
if (!ticketResult.QrStatus || ticketResult.LoginStatus === false) {
|
|
2584
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2585
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
2586
|
+
return `✅ 重新绑定成功!请重新执行发票操作。`;
|
|
2587
|
+
}
|
|
2588
|
+
return `❌ 发放功能票失败:服务器返回未成功\n重新绑定失败:${rebindResult.error || '未知错误'}`;
|
|
2589
|
+
}
|
|
2590
|
+
return '❌ 发票失败:服务器返回未成功,请确认是否已在短时间内多次执行发票指令或稍后再试或点击获取二维码刷新账号后再试。';
|
|
2427
2591
|
}
|
|
2428
|
-
return `❌ 发放功能票失败:服务器返回未成功\n重新绑定失败:${rebindResult.error || '未知错误'}`;
|
|
2429
2592
|
}
|
|
2430
|
-
|
|
2593
|
+
else {
|
|
2594
|
+
// 如果返回失败,可能需要重新绑定
|
|
2595
|
+
if (!ticketResult.QrStatus || ticketResult.LoginStatus === false) {
|
|
2596
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2597
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
2598
|
+
return `✅ 重新绑定成功!请重新执行发票操作。`;
|
|
2599
|
+
}
|
|
2600
|
+
return `❌ 发放功能票失败:服务器返回未成功\n重新绑定失败:${rebindResult.error || '未知错误'}`;
|
|
2601
|
+
}
|
|
2602
|
+
return '❌ 发票失败:服务器返回未成功,请确认是否已在短时间内多次执行发票指令或稍后再试或点击获取二维码刷新账号后再试。';
|
|
2603
|
+
}
|
|
2431
2604
|
}
|
|
2432
|
-
|
|
2605
|
+
const successMessage = `✅ 已发放 ${multiple} 倍票\n请稍等几分钟在游戏内确认`;
|
|
2606
|
+
const refId = await logOperation({
|
|
2607
|
+
command: 'mai发票',
|
|
2608
|
+
session,
|
|
2609
|
+
targetUserId,
|
|
2610
|
+
status: 'success',
|
|
2611
|
+
result: successMessage,
|
|
2612
|
+
});
|
|
2613
|
+
return appendRefId(successMessage, refId);
|
|
2433
2614
|
}
|
|
2434
2615
|
catch (error) {
|
|
2435
2616
|
logger.error('发票失败:', error);
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2617
|
+
const errorMessage = maintenanceMode
|
|
2618
|
+
? maintenanceMessage
|
|
2619
|
+
: (error?.response
|
|
2620
|
+
? `❌ API请求失败: ${error.response.status} ${error.response.statusText}\n\n${maintenanceMessage}`
|
|
2621
|
+
: `❌ 发票失败: ${error?.message || '未知错误'}\n\n${maintenanceMessage}`);
|
|
2622
|
+
const refId = await logOperation({
|
|
2623
|
+
command: 'mai发票',
|
|
2624
|
+
session,
|
|
2625
|
+
targetUserId,
|
|
2626
|
+
status: 'error',
|
|
2627
|
+
errorMessage: error?.message || '未知错误',
|
|
2628
|
+
apiResponse: error?.response?.data,
|
|
2629
|
+
});
|
|
2630
|
+
return appendRefId(errorMessage, refId);
|
|
2443
2631
|
}
|
|
2444
2632
|
});
|
|
2445
2633
|
/**
|
|
@@ -2621,7 +2809,16 @@ function apply(ctx, config) {
|
|
|
2621
2809
|
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
2622
2810
|
}
|
|
2623
2811
|
scheduleB50Notification(session, result.task_id);
|
|
2624
|
-
|
|
2812
|
+
const successMessage = `✅ B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
2813
|
+
const refId = await logOperation({
|
|
2814
|
+
command: 'mai上传B50',
|
|
2815
|
+
session,
|
|
2816
|
+
targetUserId,
|
|
2817
|
+
status: 'success',
|
|
2818
|
+
result: successMessage,
|
|
2819
|
+
apiResponse: result,
|
|
2820
|
+
});
|
|
2821
|
+
return appendRefId(successMessage, refId);
|
|
2625
2822
|
}
|
|
2626
2823
|
return `❌ 获取二维码失败:${qrTextResult.error}`;
|
|
2627
2824
|
}
|
|
@@ -2629,41 +2826,76 @@ function apply(ctx, config) {
|
|
|
2629
2826
|
await waitForQueue(session);
|
|
2630
2827
|
// 上传B50(使用新API,需要qr_text)
|
|
2631
2828
|
let result;
|
|
2829
|
+
let usedCache = qrTextResult.fromCache === true;
|
|
2632
2830
|
try {
|
|
2633
2831
|
result = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, qrTextResult.qrText, binding.fishToken);
|
|
2634
2832
|
}
|
|
2635
2833
|
catch (error) {
|
|
2636
|
-
//
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
//
|
|
2640
|
-
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
2834
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
2835
|
+
if (usedCache) {
|
|
2836
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
2837
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
2641
2838
|
if (retryQrText.error) {
|
|
2642
|
-
return `❌
|
|
2839
|
+
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
2643
2840
|
}
|
|
2644
2841
|
// 在调用API前加入队列
|
|
2645
2842
|
await waitForQueue(session);
|
|
2646
2843
|
result = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, binding.fishToken);
|
|
2647
2844
|
}
|
|
2648
2845
|
else {
|
|
2649
|
-
|
|
2846
|
+
// 如果API返回失败,可能需要重新绑定
|
|
2847
|
+
const failureResult = await handleApiFailure(session, ctx, api, binding, config, error, rebindTimeout);
|
|
2848
|
+
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
2849
|
+
// 重新绑定成功,重试上传
|
|
2850
|
+
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
2851
|
+
if (retryQrText.error) {
|
|
2852
|
+
return `❌ 重新绑定后获取二维码失败:${retryQrText.error}`;
|
|
2853
|
+
}
|
|
2854
|
+
// 在调用API前加入队列
|
|
2855
|
+
await waitForQueue(session);
|
|
2856
|
+
result = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, binding.fishToken);
|
|
2857
|
+
}
|
|
2858
|
+
else {
|
|
2859
|
+
throw error;
|
|
2860
|
+
}
|
|
2650
2861
|
}
|
|
2651
2862
|
}
|
|
2652
2863
|
if (!result.UploadStatus) {
|
|
2653
|
-
|
|
2654
|
-
|
|
2864
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
2865
|
+
if (usedCache && (result.msg?.includes('二维码') || result.msg?.includes('qr_text') || result.msg?.includes('无效'))) {
|
|
2866
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
2867
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
2868
|
+
if (retryQrText.error) {
|
|
2869
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2870
|
+
return `❌ 上传失败:${result.msg || '未知错误'}\n获取新二维码失败:${retryQrText.error}${taskIdInfo}`;
|
|
2871
|
+
}
|
|
2872
|
+
// 在调用API前加入队列
|
|
2873
|
+
await waitForQueue(session);
|
|
2874
|
+
result = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, binding.fishToken);
|
|
2875
|
+
if (!result.UploadStatus) {
|
|
2876
|
+
if (result.msg === '该账号下存在未完成的任务') {
|
|
2877
|
+
return '⚠️ 当前账号已有未完成的水鱼B50任务,请耐心等待任务完成,预计1-10分钟,无需重复上传。';
|
|
2878
|
+
}
|
|
2879
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2880
|
+
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
2881
|
+
}
|
|
2655
2882
|
}
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2883
|
+
else {
|
|
2884
|
+
if (result.msg === '该账号下存在未完成的任务') {
|
|
2885
|
+
return '⚠️ 当前账号已有未完成的水鱼B50任务,请耐心等待任务完成,预计1-10分钟,无需重复上传。';
|
|
2886
|
+
}
|
|
2887
|
+
// 如果返回失败,可能需要重新绑定
|
|
2888
|
+
if (result.msg?.includes('二维码') || result.msg?.includes('qr_text') || result.msg?.includes('无效')) {
|
|
2889
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
2890
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
2891
|
+
return `✅ 重新绑定成功!请重新执行上传操作。`;
|
|
2892
|
+
}
|
|
2893
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2894
|
+
return `❌ 上传失败:${result.msg || '未知错误'}\n重新绑定失败:${rebindResult.error || '未知错误'}${taskIdInfo}`;
|
|
2661
2895
|
}
|
|
2662
2896
|
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2663
|
-
return `❌ 上传失败:${result.msg || '未知错误'}
|
|
2897
|
+
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
2664
2898
|
}
|
|
2665
|
-
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
2666
|
-
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
2667
2899
|
}
|
|
2668
2900
|
scheduleB50Notification(session, result.task_id);
|
|
2669
2901
|
return `✅ B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
@@ -2771,97 +3003,198 @@ function apply(ctx, config) {
|
|
|
2771
3003
|
return `❌ 获取二维码失败:${qrTextResult.error}`;
|
|
2772
3004
|
}
|
|
2773
3005
|
const results = [];
|
|
2774
|
-
//
|
|
2775
|
-
|
|
2776
|
-
|
|
3006
|
+
// 先上传水鱼B50,等待完成后再上传落雪(串行执行,避免同时登录)
|
|
3007
|
+
try {
|
|
3008
|
+
await waitForQueue(session);
|
|
3009
|
+
let fishResult = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, qrTextResult.qrText, fishToken);
|
|
3010
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
3011
|
+
if (qrTextResult.fromCache && !fishResult.UploadStatus && (fishResult.msg?.includes('二维码') || fishResult.msg?.includes('qr_text') || fishResult.msg?.includes('无效'))) {
|
|
3012
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
3013
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
3014
|
+
if (retryQrText.error) {
|
|
3015
|
+
const taskIdInfo = fishResult.task_id ? `\n任务ID: ${fishResult.task_id}` : '';
|
|
3016
|
+
return `🐟 水鱼: ❌ 上传失败:${fishResult.msg || '未知错误'}\n获取新二维码失败:${retryQrText.error}${taskIdInfo}`;
|
|
3017
|
+
}
|
|
3018
|
+
// 在调用API前加入队列
|
|
2777
3019
|
await waitForQueue(session);
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
}
|
|
2789
|
-
const taskIdInfo = fishResult.task_id ? `\n任务ID: ${fishResult.task_id}` : '';
|
|
2790
|
-
return `❌ 水鱼上传失败:${fishResult.msg || '未知错误'}\n重新绑定失败:${rebindResult.error || '未知错误'}${taskIdInfo}`;
|
|
3020
|
+
fishResult = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, fishToken);
|
|
3021
|
+
}
|
|
3022
|
+
if (!fishResult.UploadStatus) {
|
|
3023
|
+
if (fishResult.msg === '该账号下存在未完成的任务') {
|
|
3024
|
+
results.push('🐟 水鱼: ⚠️ 当前账号已有未完成的B50任务,请稍后再试,无需重复上传。');
|
|
3025
|
+
}
|
|
3026
|
+
else if (fishResult.msg?.includes('二维码') || fishResult.msg?.includes('qr_text') || fishResult.msg?.includes('无效')) {
|
|
3027
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
3028
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
3029
|
+
return '✅ 重新绑定成功!请重新执行 /maiua 上传操作。';
|
|
2791
3030
|
}
|
|
3031
|
+
const taskIdInfo = fishResult.task_id ? `\n任务ID: ${fishResult.task_id}` : '';
|
|
3032
|
+
return `❌ 水鱼上传失败:${fishResult.msg || '未知错误'}\n重新绑定失败:${rebindResult.error || '未知错误'}${taskIdInfo}`;
|
|
3033
|
+
}
|
|
3034
|
+
else {
|
|
2792
3035
|
const taskIdInfo = fishResult.task_id ? `\n任务ID: ${fishResult.task_id}` : '';
|
|
2793
3036
|
results.push(`🐟 水鱼: ❌ 上传失败:${fishResult.msg || '未知错误'}${taskIdInfo}`);
|
|
2794
|
-
return null;
|
|
2795
3037
|
}
|
|
3038
|
+
}
|
|
3039
|
+
else {
|
|
2796
3040
|
scheduleB50Notification(session, fishResult.task_id);
|
|
2797
3041
|
results.push(`🐟 水鱼: ✅ B50任务已提交!\n任务ID: ${fishResult.task_id}\n请耐心等待任务完成,预计1-10分钟`);
|
|
2798
|
-
return null;
|
|
2799
3042
|
}
|
|
2800
|
-
|
|
3043
|
+
}
|
|
3044
|
+
catch (error) {
|
|
3045
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
3046
|
+
if (qrTextResult.fromCache) {
|
|
3047
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
3048
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
3049
|
+
if (retryQrText.error) {
|
|
3050
|
+
return `🐟 水鱼: ❌ 获取二维码失败:${retryQrText.error}`;
|
|
3051
|
+
}
|
|
3052
|
+
// 在调用API前加入队列
|
|
3053
|
+
await waitForQueue(session);
|
|
3054
|
+
try {
|
|
3055
|
+
const fishResult = await api.uploadB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, fishToken);
|
|
3056
|
+
if (!fishResult.UploadStatus) {
|
|
3057
|
+
if (fishResult.msg === '该账号下存在未完成的任务') {
|
|
3058
|
+
results.push('🐟 水鱼: ⚠️ 当前账号已有未完成的B50任务,请稍后再试,无需重复上传。');
|
|
3059
|
+
}
|
|
3060
|
+
else {
|
|
3061
|
+
const taskIdInfo = fishResult.task_id ? `\n任务ID: ${fishResult.task_id}` : '';
|
|
3062
|
+
return `🐟 水鱼: ❌ 上传失败:${fishResult.msg || '未知错误'}${taskIdInfo}`;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
else {
|
|
3066
|
+
scheduleB50Notification(session, fishResult.task_id);
|
|
3067
|
+
results.push(`🐟 水鱼: ✅ B50任务已提交!\n任务ID: ${fishResult.task_id}\n请耐心等待任务完成,预计1-10分钟`);
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
catch (retryError) {
|
|
3071
|
+
const failureResult = await handleApiFailure(session, ctx, api, binding, config, retryError, rebindTimeout);
|
|
3072
|
+
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
3073
|
+
return '✅ 重新绑定成功!请重新执行 /maiua 上传操作。';
|
|
3074
|
+
}
|
|
3075
|
+
if (retryError?.code === 'ECONNABORTED' || String(retryError?.message || '').includes('timeout')) {
|
|
3076
|
+
return '🐟 水鱼: ❌ 上传超时,请稍后再试一次。';
|
|
3077
|
+
}
|
|
3078
|
+
if (retryError?.response) {
|
|
3079
|
+
return `🐟 水鱼: ❌ API请求失败: ${retryError.response.status} ${retryError.response.statusText}`;
|
|
3080
|
+
}
|
|
3081
|
+
return `🐟 水鱼: ❌ 上传失败: ${retryError?.message || '未知错误'}`;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
else {
|
|
2801
3085
|
const failureResult = await handleApiFailure(session, ctx, api, binding, config, error, rebindTimeout);
|
|
2802
3086
|
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
2803
3087
|
return '✅ 重新绑定成功!请重新执行 /maiua 上传操作。';
|
|
2804
3088
|
}
|
|
2805
3089
|
if (error?.code === 'ECONNABORTED' || String(error?.message || '').includes('timeout')) {
|
|
2806
|
-
|
|
2807
|
-
return null;
|
|
3090
|
+
return '🐟 水鱼: ❌ 上传超时,请稍后再试一次。';
|
|
2808
3091
|
}
|
|
2809
3092
|
if (error?.response) {
|
|
2810
|
-
|
|
2811
|
-
return null;
|
|
3093
|
+
return `🐟 水鱼: ❌ API请求失败: ${error.response.status} ${error.response.statusText}`;
|
|
2812
3094
|
}
|
|
2813
|
-
|
|
2814
|
-
return null;
|
|
3095
|
+
return `🐟 水鱼: ❌ 上传失败: ${error?.message || '未知错误'}`;
|
|
2815
3096
|
}
|
|
2816
|
-
})();
|
|
2817
|
-
if (fishAbort) {
|
|
2818
|
-
return fishAbort;
|
|
2819
3097
|
}
|
|
3098
|
+
// 等待水鱼上传完成后再上传落雪(避免同时登录导致失败)
|
|
2820
3099
|
// 上传落雪B50
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
}
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
3100
|
+
try {
|
|
3101
|
+
await waitForQueue(session);
|
|
3102
|
+
let lxResult = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, qrTextResult.qrText, finalLxnsCode);
|
|
3103
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
3104
|
+
if (qrTextResult.fromCache && !lxResult.UploadStatus && (lxResult.msg?.includes('二维码') || lxResult.msg?.includes('qr_text') || lxResult.msg?.includes('无效'))) {
|
|
3105
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
3106
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
3107
|
+
if (retryQrText.error) {
|
|
3108
|
+
const taskIdInfo = lxResult.task_id ? `\n任务ID: ${lxResult.task_id}` : '';
|
|
3109
|
+
results.push(`❄️ 落雪: ❌ 上传失败:${lxResult.msg || '未知错误'}\n获取新二维码失败:${retryQrText.error}${taskIdInfo}`);
|
|
3110
|
+
}
|
|
3111
|
+
else {
|
|
3112
|
+
// 在调用API前加入队列
|
|
3113
|
+
await waitForQueue(session);
|
|
3114
|
+
lxResult = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, finalLxnsCode);
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
if (!lxResult.UploadStatus) {
|
|
3118
|
+
if (lxResult.msg === '该账号下存在未完成的任务') {
|
|
3119
|
+
results.push('❄️ 落雪: ⚠️ 当前账号已有未完成的B50任务,请稍后再试,无需重复上传。');
|
|
3120
|
+
}
|
|
3121
|
+
else if (lxResult.msg?.includes('二维码') || lxResult.msg?.includes('qr_text') || lxResult.msg?.includes('无效')) {
|
|
3122
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
3123
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
3124
|
+
return '✅ 重新绑定成功!请重新执行 /maiua 上传操作。';
|
|
2837
3125
|
}
|
|
3126
|
+
const taskIdInfo = lxResult.task_id ? `\n任务ID: ${lxResult.task_id}` : '';
|
|
3127
|
+
return `❌ 落雪上传失败:${lxResult.msg || '未知错误'}\n重新绑定失败:${rebindResult.error || '未知错误'}${taskIdInfo}`;
|
|
3128
|
+
}
|
|
3129
|
+
else {
|
|
2838
3130
|
const taskIdInfo = lxResult.task_id ? `\n任务ID: ${lxResult.task_id}` : '';
|
|
2839
3131
|
results.push(`❄️ 落雪: ❌ 上传失败:${lxResult.msg || '未知错误'}${taskIdInfo}`);
|
|
2840
|
-
return null;
|
|
2841
3132
|
}
|
|
3133
|
+
}
|
|
3134
|
+
else {
|
|
2842
3135
|
scheduleLxB50Notification(session, lxResult.task_id);
|
|
2843
3136
|
results.push(`❄️ 落雪: ✅ B50任务已提交!\n任务ID: ${lxResult.task_id}\n请耐心等待任务完成,预计1-10分钟`);
|
|
2844
|
-
return null;
|
|
2845
3137
|
}
|
|
2846
|
-
|
|
3138
|
+
}
|
|
3139
|
+
catch (error) {
|
|
3140
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
3141
|
+
if (qrTextResult.fromCache) {
|
|
3142
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
3143
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
3144
|
+
if (retryQrText.error) {
|
|
3145
|
+
results.push(`❄️ 落雪: ❌ 获取二维码失败:${retryQrText.error}`);
|
|
3146
|
+
}
|
|
3147
|
+
else {
|
|
3148
|
+
// 在调用API前加入队列
|
|
3149
|
+
await waitForQueue(session);
|
|
3150
|
+
try {
|
|
3151
|
+
const lxResult = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, finalLxnsCode);
|
|
3152
|
+
if (!lxResult.UploadStatus) {
|
|
3153
|
+
if (lxResult.msg === '该账号下存在未完成的任务') {
|
|
3154
|
+
results.push('❄️ 落雪: ⚠️ 当前账号已有未完成的B50任务,请稍后再试,无需重复上传。');
|
|
3155
|
+
}
|
|
3156
|
+
else {
|
|
3157
|
+
const taskIdInfo = lxResult.task_id ? `\n任务ID: ${lxResult.task_id}` : '';
|
|
3158
|
+
results.push(`❄️ 落雪: ❌ 上传失败:${lxResult.msg || '未知错误'}${taskIdInfo}`);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
else {
|
|
3162
|
+
scheduleLxB50Notification(session, lxResult.task_id);
|
|
3163
|
+
results.push(`❄️ 落雪: ✅ B50任务已提交!\n任务ID: ${lxResult.task_id}\n请耐心等待任务完成,预计1-10分钟`);
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
catch (retryError) {
|
|
3167
|
+
const failureResult = await handleApiFailure(session, ctx, api, binding, config, retryError, rebindTimeout);
|
|
3168
|
+
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
3169
|
+
return '✅ 重新绑定成功!请重新执行 /maiua 上传操作。';
|
|
3170
|
+
}
|
|
3171
|
+
if (retryError?.code === 'ECONNABORTED' || String(retryError?.message || '').includes('timeout')) {
|
|
3172
|
+
results.push('❄️ 落雪: ❌ 上传超时,请稍后再试一次。');
|
|
3173
|
+
}
|
|
3174
|
+
else if (retryError?.response) {
|
|
3175
|
+
results.push(`❄️ 落雪: ❌ API请求失败: ${retryError.response.status} ${retryError.response.statusText}`);
|
|
3176
|
+
}
|
|
3177
|
+
else {
|
|
3178
|
+
results.push(`❄️ 落雪: ❌ 上传失败: ${retryError?.message || '未知错误'}`);
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
else {
|
|
2847
3184
|
const failureResult = await handleApiFailure(session, ctx, api, binding, config, error, rebindTimeout);
|
|
2848
3185
|
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
2849
3186
|
return '✅ 重新绑定成功!请重新执行 /maiua 上传操作。';
|
|
2850
3187
|
}
|
|
2851
3188
|
if (error?.code === 'ECONNABORTED' || String(error?.message || '').includes('timeout')) {
|
|
2852
3189
|
results.push('❄️ 落雪: ❌ 上传超时,请稍后再试一次。');
|
|
2853
|
-
return null;
|
|
2854
3190
|
}
|
|
2855
|
-
if (error?.response) {
|
|
3191
|
+
else if (error?.response) {
|
|
2856
3192
|
results.push(`❄️ 落雪: ❌ API请求失败: ${error.response.status} ${error.response.statusText}`);
|
|
2857
|
-
return null;
|
|
2858
3193
|
}
|
|
2859
|
-
|
|
2860
|
-
|
|
3194
|
+
else {
|
|
3195
|
+
results.push(`❄️ 落雪: ❌ 上传失败: ${error?.message || '未知错误'}`);
|
|
3196
|
+
}
|
|
2861
3197
|
}
|
|
2862
|
-
})();
|
|
2863
|
-
if (lxnsAbort) {
|
|
2864
|
-
return lxnsAbort;
|
|
2865
3198
|
}
|
|
2866
3199
|
if (results.length === 0) {
|
|
2867
3200
|
return `⚠️ 未能发起上传请求${proxyTip}`;
|
|
@@ -3060,18 +3393,18 @@ function apply(ctx, config) {
|
|
|
3060
3393
|
// 使用新API获取收藏品(需要qr_text)
|
|
3061
3394
|
const machineInfo = config.machineInfo;
|
|
3062
3395
|
let result;
|
|
3396
|
+
let usedCache = qrTextResult.fromCache === true;
|
|
3063
3397
|
try {
|
|
3064
3398
|
result = await api.getItem(machineInfo.regionId, machineInfo.regionName, machineInfo.clientId, machineInfo.placeId, machineInfo.placeName, parseInt(itemId, 10), itemKind, 1, // item_stock: 1
|
|
3065
3399
|
qrTextResult.qrText);
|
|
3066
3400
|
}
|
|
3067
3401
|
catch (error) {
|
|
3068
|
-
//
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
//
|
|
3072
|
-
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
3402
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
3403
|
+
if (usedCache) {
|
|
3404
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
3405
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
3073
3406
|
if (retryQrText.error) {
|
|
3074
|
-
return `❌
|
|
3407
|
+
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
3075
3408
|
}
|
|
3076
3409
|
// 在调用API前加入队列
|
|
3077
3410
|
await waitForQueue(session);
|
|
@@ -3079,19 +3412,58 @@ function apply(ctx, config) {
|
|
|
3079
3412
|
retryQrText.qrText);
|
|
3080
3413
|
}
|
|
3081
3414
|
else {
|
|
3082
|
-
|
|
3415
|
+
// 如果API返回失败,可能需要重新绑定
|
|
3416
|
+
const failureResult = await handleApiFailure(session, ctx, api, binding, config, error, rebindTimeout);
|
|
3417
|
+
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
3418
|
+
// 重新绑定成功,重试获取收藏品
|
|
3419
|
+
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
3420
|
+
if (retryQrText.error) {
|
|
3421
|
+
return `❌ 重新绑定后获取二维码失败:${retryQrText.error}`;
|
|
3422
|
+
}
|
|
3423
|
+
// 在调用API前加入队列
|
|
3424
|
+
await waitForQueue(session);
|
|
3425
|
+
result = await api.getItem(machineInfo.regionId, machineInfo.regionName, machineInfo.clientId, machineInfo.placeId, machineInfo.placeName, parseInt(itemId, 10), itemKind, 1, // item_stock: 1
|
|
3426
|
+
retryQrText.qrText);
|
|
3427
|
+
}
|
|
3428
|
+
else {
|
|
3429
|
+
throw error;
|
|
3430
|
+
}
|
|
3083
3431
|
}
|
|
3084
3432
|
}
|
|
3085
3433
|
if (!result.UserAllStatus || !result.LoginStatus || !result.LogoutStatus) {
|
|
3086
|
-
//
|
|
3087
|
-
if (!result.QrStatus || result.LoginStatus === false) {
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3434
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
3435
|
+
if (usedCache && (!result.QrStatus || result.LoginStatus === false)) {
|
|
3436
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
3437
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
3438
|
+
if (retryQrText.error) {
|
|
3439
|
+
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
3440
|
+
}
|
|
3441
|
+
// 在调用API前加入队列
|
|
3442
|
+
await waitForQueue(session);
|
|
3443
|
+
result = await api.getItem(machineInfo.regionId, machineInfo.regionName, machineInfo.clientId, machineInfo.placeId, machineInfo.placeName, parseInt(itemId, 10), itemKind, 1, // item_stock: 1
|
|
3444
|
+
retryQrText.qrText);
|
|
3445
|
+
if (!result.UserAllStatus || !result.LoginStatus || !result.LogoutStatus) {
|
|
3446
|
+
if (!result.QrStatus || result.LoginStatus === false) {
|
|
3447
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
3448
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
3449
|
+
return `✅ 重新绑定成功!请重新执行发收藏品操作。`;
|
|
3450
|
+
}
|
|
3451
|
+
return `❌ 发放收藏品失败:服务器返回未成功\n重新绑定失败:${rebindResult.error || '未知错误'}`;
|
|
3452
|
+
}
|
|
3453
|
+
return '❌ 发放收藏品失败:服务器返回未成功,请确认是否已在短时间内多次执行发收藏品指令或稍后再试或点击获取二维码刷新账号后再试。';
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
else {
|
|
3457
|
+
// 如果返回失败,可能需要重新绑定
|
|
3458
|
+
if (!result.QrStatus || result.LoginStatus === false) {
|
|
3459
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
3460
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
3461
|
+
return `✅ 重新绑定成功!请重新执行发收藏品操作。`;
|
|
3462
|
+
}
|
|
3463
|
+
return `❌ 发放收藏品失败:服务器返回未成功\n重新绑定失败:${rebindResult.error || '未知错误'}`;
|
|
3091
3464
|
}
|
|
3092
|
-
return
|
|
3465
|
+
return '❌ 发放收藏品失败:服务器返回未成功,请确认是否已在短时间内多次执行发收藏品指令或稍后再试或点击获取二维码刷新账号后再试。';
|
|
3093
3466
|
}
|
|
3094
|
-
return '❌ 发放收藏品失败:服务器返回未成功,请确认是否已在短时间内多次执行发收藏品指令或稍后再试或点击获取二维码刷新账号后再试。';
|
|
3095
3467
|
}
|
|
3096
3468
|
return `✅ 已为 ${maskUserId(binding.maiUid)} 发放收藏品${proxyTip}\n类型: ${selectedType?.label}\nID: ${itemId}`;
|
|
3097
3469
|
}
|
|
@@ -3464,41 +3836,76 @@ function apply(ctx, config) {
|
|
|
3464
3836
|
await waitForQueue(session);
|
|
3465
3837
|
// 上传落雪B50(使用新API,需要qr_text)
|
|
3466
3838
|
let result;
|
|
3839
|
+
let usedCache = qrTextResult.fromCache === true;
|
|
3467
3840
|
try {
|
|
3468
3841
|
result = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, qrTextResult.qrText, finalLxnsCode);
|
|
3469
3842
|
}
|
|
3470
3843
|
catch (error) {
|
|
3471
|
-
//
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
//
|
|
3475
|
-
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
3844
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
3845
|
+
if (usedCache) {
|
|
3846
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
3847
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
3476
3848
|
if (retryQrText.error) {
|
|
3477
|
-
return `❌
|
|
3849
|
+
return `❌ 获取二维码失败:${retryQrText.error}`;
|
|
3478
3850
|
}
|
|
3479
3851
|
// 在调用API前加入队列
|
|
3480
3852
|
await waitForQueue(session);
|
|
3481
3853
|
result = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, finalLxnsCode);
|
|
3482
3854
|
}
|
|
3483
3855
|
else {
|
|
3484
|
-
|
|
3856
|
+
// 如果API返回失败,可能需要重新绑定
|
|
3857
|
+
const failureResult = await handleApiFailure(session, ctx, api, binding, config, error, rebindTimeout);
|
|
3858
|
+
if (failureResult.rebindResult && failureResult.rebindResult.success && failureResult.rebindResult.newBinding) {
|
|
3859
|
+
// 重新绑定成功,重试上传
|
|
3860
|
+
const retryQrText = await getQrText(session, ctx, api, failureResult.rebindResult.newBinding, config, rebindTimeout);
|
|
3861
|
+
if (retryQrText.error) {
|
|
3862
|
+
return `❌ 重新绑定后获取二维码失败:${retryQrText.error}`;
|
|
3863
|
+
}
|
|
3864
|
+
// 在调用API前加入队列
|
|
3865
|
+
await waitForQueue(session);
|
|
3866
|
+
result = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, finalLxnsCode);
|
|
3867
|
+
}
|
|
3868
|
+
else {
|
|
3869
|
+
throw error;
|
|
3870
|
+
}
|
|
3485
3871
|
}
|
|
3486
3872
|
}
|
|
3487
3873
|
if (!result.UploadStatus) {
|
|
3488
|
-
|
|
3489
|
-
|
|
3874
|
+
// 如果使用了缓存且失败,尝试重新获取SGID
|
|
3875
|
+
if (usedCache && (result.msg?.includes('二维码') || result.msg?.includes('qr_text') || result.msg?.includes('无效'))) {
|
|
3876
|
+
logger.info('使用缓存的SGID失败,尝试重新获取SGID');
|
|
3877
|
+
const retryQrText = await getQrText(session, ctx, api, binding, config, rebindTimeout, undefined, false); // 禁用缓存,强制重新输入
|
|
3878
|
+
if (retryQrText.error) {
|
|
3879
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
3880
|
+
return `❌ 上传失败:${result.msg || '未知错误'}\n获取新二维码失败:${retryQrText.error}${taskIdInfo}`;
|
|
3881
|
+
}
|
|
3882
|
+
// 在调用API前加入队列
|
|
3883
|
+
await waitForQueue(session);
|
|
3884
|
+
result = await api.uploadLxB50(machineInfo.regionId, machineInfo.clientId, machineInfo.placeId, retryQrText.qrText, finalLxnsCode);
|
|
3885
|
+
if (!result.UploadStatus) {
|
|
3886
|
+
if (result.msg === '该账号下存在未完成的任务') {
|
|
3887
|
+
return '⚠️ 当前账号已有未完成的落雪B50任务,请耐心等待任务完成,预计1-10分钟,无需重复上传。';
|
|
3888
|
+
}
|
|
3889
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
3890
|
+
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
3891
|
+
}
|
|
3490
3892
|
}
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3893
|
+
else {
|
|
3894
|
+
if (result.msg === '该账号下存在未完成的任务') {
|
|
3895
|
+
return '⚠️ 当前账号已有未完成的落雪B50任务,请耐心等待任务完成,预计1-10分钟,无需重复上传。';
|
|
3896
|
+
}
|
|
3897
|
+
// 如果返回失败,可能需要重新绑定
|
|
3898
|
+
if (result.msg?.includes('二维码') || result.msg?.includes('qr_text') || result.msg?.includes('无效')) {
|
|
3899
|
+
const rebindResult = await promptForRebind(session, ctx, api, binding, config, rebindTimeout);
|
|
3900
|
+
if (rebindResult.success && rebindResult.newBinding) {
|
|
3901
|
+
return `✅ 重新绑定成功!请重新执行上传操作。`;
|
|
3902
|
+
}
|
|
3903
|
+
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
3904
|
+
return `❌ 上传失败:${result.msg || '未知错误'}\n重新绑定失败:${rebindResult.error || '未知错误'}${taskIdInfo}`;
|
|
3496
3905
|
}
|
|
3497
3906
|
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
3498
|
-
return `❌ 上传失败:${result.msg || '未知错误'}
|
|
3907
|
+
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
3499
3908
|
}
|
|
3500
|
-
const taskIdInfo = result.task_id ? `\n任务ID: ${result.task_id}` : '';
|
|
3501
|
-
return `❌ 上传失败:${result.msg || '未知错误'}${taskIdInfo}`;
|
|
3502
3909
|
}
|
|
3503
3910
|
scheduleLxB50Notification(session, result.task_id);
|
|
3504
3911
|
return `✅ 落雪B50上传任务已提交!\n任务ID: ${result.task_id}\n\n请耐心等待任务完成,预计1-10分钟`;
|
|
@@ -4347,16 +4754,154 @@ function apply(ctx, config) {
|
|
|
4347
4754
|
resultMessage = `ℹ️ 没有需要更新的用户\n所有用户都未开启锁定模式和保护模式`
|
|
4348
4755
|
}
|
|
4349
4756
|
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
})
|
|
4757
|
+
const refId = await logOperation({
|
|
4758
|
+
command: 'mai管理员一键关闭',
|
|
4759
|
+
session,
|
|
4760
|
+
status: 'success',
|
|
4761
|
+
result: resultMessage,
|
|
4762
|
+
})
|
|
4763
|
+
|
|
4764
|
+
return appendRefId(resultMessage, refId)
|
|
4765
|
+
} catch (error: any) {
|
|
4766
|
+
logger.error('管理员一键关闭操作失败:', error)
|
|
4767
|
+
const errorMessage = maintenanceMode
|
|
4768
|
+
? maintenanceMessage
|
|
4769
|
+
: `❌ 操作失败: ${error?.message || '未知错误'}\n\n${maintenanceMessage}`
|
|
4770
|
+
|
|
4771
|
+
const refId = await logOperation({
|
|
4772
|
+
command: 'mai管理员一键关闭',
|
|
4773
|
+
session,
|
|
4774
|
+
status: 'error',
|
|
4775
|
+
errorMessage: error?.message || '未知错误',
|
|
4776
|
+
})
|
|
4777
|
+
|
|
4778
|
+
return appendRefId(errorMessage, refId)
|
|
4779
|
+
}
|
|
4780
|
+
})
|
|
4359
4781
|
|
|
4782
|
+
/**
|
|
4783
|
+
* 管理员查询操作记录(通过 ref_id)
|
|
4784
|
+
* 用法: /mai管理员查询操作 <ref_id>
|
|
4785
|
+
*/
|
|
4786
|
+
ctx.command('mai管理员查询操作 <refId:text>', '通过 Ref_ID 查询操作详细信息(需要auth等级3以上)')
|
|
4787
|
+
.userFields(['authority'])
|
|
4788
|
+
.action(async ({ session }, refId) => {
|
|
4789
|
+
if (!session) {
|
|
4790
|
+
return '❌ 无法获取会话信息';
|
|
4791
|
+
}
|
|
4792
|
+
if ((session.user?.authority ?? 0) < 3) {
|
|
4793
|
+
return '❌ 权限不足,需要auth等级3以上才能执行此操作';
|
|
4794
|
+
}
|
|
4795
|
+
try {
|
|
4796
|
+
const logs = await ctx.database.get('maibot_operation_logs', { refId: refId.trim() });
|
|
4797
|
+
if (logs.length === 0) {
|
|
4798
|
+
return `❌ 未找到 Ref_ID 为 "${refId}" 的操作记录`;
|
|
4799
|
+
}
|
|
4800
|
+
const log = logs[0];
|
|
4801
|
+
const statusText = {
|
|
4802
|
+
success: '✅ 成功',
|
|
4803
|
+
failure: '❌ 失败',
|
|
4804
|
+
error: '⚠️ 错误',
|
|
4805
|
+
}[log.status] || log.status;
|
|
4806
|
+
let result = `📋 操作记录详情\n\n`;
|
|
4807
|
+
result += `Ref_ID: ${log.refId}\n`;
|
|
4808
|
+
result += `命令: ${log.command}\n`;
|
|
4809
|
+
result += `操作人: ${log.userId}\n`;
|
|
4810
|
+
if (log.targetUserId) {
|
|
4811
|
+
result += `目标用户: ${log.targetUserId}\n`;
|
|
4812
|
+
}
|
|
4813
|
+
result += `状态: ${statusText}\n`;
|
|
4814
|
+
result += `操作时间: ${new Date(log.createdAt).toLocaleString('zh-CN')}\n`;
|
|
4815
|
+
if (log.guildId) {
|
|
4816
|
+
result += `群组ID: ${log.guildId}\n`;
|
|
4817
|
+
}
|
|
4818
|
+
if (log.channelId) {
|
|
4819
|
+
result += `频道ID: ${log.channelId}\n`;
|
|
4820
|
+
}
|
|
4821
|
+
if (log.result) {
|
|
4822
|
+
result += `\n操作结果:\n${log.result}\n`;
|
|
4823
|
+
}
|
|
4824
|
+
if (log.errorMessage) {
|
|
4825
|
+
result += `\n错误信息:\n${log.errorMessage}\n`;
|
|
4826
|
+
}
|
|
4827
|
+
if (log.apiResponse) {
|
|
4828
|
+
try {
|
|
4829
|
+
const apiResp = JSON.parse(log.apiResponse);
|
|
4830
|
+
result += `\nAPI响应:\n${JSON.stringify(apiResp, null, 2)}\n`;
|
|
4831
|
+
}
|
|
4832
|
+
catch {
|
|
4833
|
+
result += `\nAPI响应:\n${log.apiResponse}\n`;
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4836
|
+
return result;
|
|
4837
|
+
}
|
|
4838
|
+
catch (error) {
|
|
4839
|
+
logger.error('查询操作记录失败:', error);
|
|
4840
|
+
return `❌ 查询失败: ${error?.message || '未知错误'}`;
|
|
4841
|
+
}
|
|
4842
|
+
});
|
|
4843
|
+
/**
|
|
4844
|
+
* 管理员查看今日命令统计
|
|
4845
|
+
* 用法: /mai管理员统计
|
|
4846
|
+
*/
|
|
4847
|
+
ctx.command('mai管理员统计', '查看今日各指令执行次数统计(需要auth等级3以上)')
|
|
4848
|
+
.userFields(['authority'])
|
|
4849
|
+
.action(async ({ session }) => {
|
|
4850
|
+
if (!session) {
|
|
4851
|
+
return '❌ 无法获取会话信息';
|
|
4852
|
+
}
|
|
4853
|
+
if ((session.user?.authority ?? 0) < 3) {
|
|
4854
|
+
return '❌ 权限不足,需要auth等级3以上才能执行此操作';
|
|
4855
|
+
}
|
|
4856
|
+
try {
|
|
4857
|
+
const today = new Date();
|
|
4858
|
+
today.setHours(0, 0, 0, 0);
|
|
4859
|
+
const todayStart = today.getTime();
|
|
4860
|
+
// 获取今日所有操作记录
|
|
4861
|
+
const allLogs = await ctx.database.get('maibot_operation_logs', {});
|
|
4862
|
+
const todayLogs = allLogs.filter(log => new Date(log.createdAt).getTime() >= todayStart);
|
|
4863
|
+
// 统计各命令执行次数
|
|
4864
|
+
const commandStats = {};
|
|
4865
|
+
for (const log of todayLogs) {
|
|
4866
|
+
if (!commandStats[log.command]) {
|
|
4867
|
+
commandStats[log.command] = { total: 0, success: 0, failure: 0, error: 0 };
|
|
4868
|
+
}
|
|
4869
|
+
commandStats[log.command].total++;
|
|
4870
|
+
if (log.status === 'success') {
|
|
4871
|
+
commandStats[log.command].success++;
|
|
4872
|
+
}
|
|
4873
|
+
else if (log.status === 'failure') {
|
|
4874
|
+
commandStats[log.command].failure++;
|
|
4875
|
+
}
|
|
4876
|
+
else if (log.status === 'error') {
|
|
4877
|
+
commandStats[log.command].error++;
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4880
|
+
// 按执行次数排序
|
|
4881
|
+
const sortedCommands = Object.entries(commandStats).sort((a, b) => b[1].total - a[1].total);
|
|
4882
|
+
let result = `📊 今日命令执行统计\n\n`;
|
|
4883
|
+
result += `统计时间: ${new Date().toLocaleString('zh-CN')}\n`;
|
|
4884
|
+
result += `总操作数: ${todayLogs.length}\n\n`;
|
|
4885
|
+
if (sortedCommands.length === 0) {
|
|
4886
|
+
result += `ℹ️ 今日暂无操作记录`;
|
|
4887
|
+
}
|
|
4888
|
+
else {
|
|
4889
|
+
result += `各命令执行情况:\n`;
|
|
4890
|
+
for (const [command, stats] of sortedCommands) {
|
|
4891
|
+
result += `\n${command}:\n`;
|
|
4892
|
+
result += ` 总次数: ${stats.total}\n`;
|
|
4893
|
+
result += ` 成功: ${stats.success}\n`;
|
|
4894
|
+
result += ` 失败: ${stats.failure}\n`;
|
|
4895
|
+
result += ` 错误: ${stats.error}\n`;
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
return result;
|
|
4899
|
+
}
|
|
4900
|
+
catch (error) {
|
|
4901
|
+
logger.error('查询统计失败:', error);
|
|
4902
|
+
return `❌ 查询失败: ${error?.message || '未知错误'}`;
|
|
4903
|
+
}
|
|
4904
|
+
});
|
|
4360
4905
|
/**
|
|
4361
4906
|
* 管理员关闭/开启登录播报功能(全局开关)
|
|
4362
4907
|
* 用法: /mai管理员关闭登录播报 [on|off]
|