koishi-plugin-ets2-tools-tmp 2.6.0 → 3.0.1
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/command/ets-app/addMember/addMemberV2.js +93 -0
- package/lib/command/ets-app/changePoint/changePoint.js +14 -0
- package/lib/command/ets-app/changePoint/changePointV1.js +110 -0
- package/lib/command/ets-app/changePoint/changePointV2.js +91 -0
- package/lib/command/ets-app/queryPoint/queryPoint.js +38 -0
- package/lib/command/ets-app/queryPoint/queryPointV1.js +51 -0
- package/lib/command/ets-app/queryPoint/queryPointV2.js +51 -0
- package/lib/command/ets-app/resetPassword/resetPassword.js +20 -0
- package/lib/command/ets-app/resetPassword/resetPasswordV1.js +115 -0
- package/lib/command/ets-app/resetPassword/resetPasswordV2.js +71 -0
- package/lib/command/tmpActivityService/tmpActivityServiceV1.js +71 -0
- package/lib/command/tmpActivityService/tmpActivityServiceV2.js +85 -0
- package/lib/command/tmpActivityService.js +13 -120
- package/lib/index.js +42 -14
- package/package.json +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const dayjs = require("dayjs");
|
|
2
|
+
|
|
3
|
+
module.exports = async (ctx, cfg, session, tmpId, qq, teamNumber) => {
|
|
4
|
+
const { url, token, logOutput } = cfg.mainSettings?.settings || {};
|
|
5
|
+
const { adminUsers, teamNumberGenerateEnable } = cfg.addMember?.settings || {};
|
|
6
|
+
const currentUserQQ = session.userId;
|
|
7
|
+
const isAdmin = adminUsers.includes(currentUserQQ);
|
|
8
|
+
|
|
9
|
+
const log = (msg) => logOutput && ctx.logger.info(msg);
|
|
10
|
+
|
|
11
|
+
const parseAtQQ = (raw) => {
|
|
12
|
+
if (!raw?.startsWith("<at ")) return raw;
|
|
13
|
+
const idStart = raw.indexOf('id="');
|
|
14
|
+
if (idStart === -1) return "";
|
|
15
|
+
const idEnd = raw.indexOf('"', idStart + 4);
|
|
16
|
+
if (idEnd === -1) return "";
|
|
17
|
+
return raw.substring(idStart + 4, idEnd);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
if (!isAdmin) {
|
|
22
|
+
return "您没有权限使用新增成员功能,请联系管理员";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!token) return "未配置车队平台V2.0 API认证令牌";
|
|
26
|
+
|
|
27
|
+
if (!tmpId) {
|
|
28
|
+
return "请输入TMP ID";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!qq) {
|
|
32
|
+
return "请输入QQ号或@群成员";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const parsedQQ = parseAtQQ(qq) || qq;
|
|
36
|
+
if (!/^\d+$/.test(parsedQQ)) {
|
|
37
|
+
return "QQ号格式不正确,请输入纯数字QQ号";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const autoGenerate = teamNumberGenerateEnable !== false;
|
|
41
|
+
if (!autoGenerate && !teamNumber) {
|
|
42
|
+
return "自动生成车队编号已关闭,请输入车队编号";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const baseUrl = url || "open.vtcm.link";
|
|
46
|
+
const email = `${parsedQQ}@qq.com`;
|
|
47
|
+
const joinDate = dayjs().format("YYYY-MM-DD");
|
|
48
|
+
|
|
49
|
+
const requestBody = {
|
|
50
|
+
tmpId: Number(tmpId),
|
|
51
|
+
teamNumberGenerateEnable: autoGenerate ? 1 : 0,
|
|
52
|
+
qq: parsedQQ,
|
|
53
|
+
email,
|
|
54
|
+
joinDate,
|
|
55
|
+
state: 1
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
if (!autoGenerate) {
|
|
59
|
+
requestBody.teamNumber = Number(teamNumber);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const saveUrl = `https://${baseUrl}/members/save`;
|
|
63
|
+
log(`[V2] 新增成员请求: ${saveUrl}, body: ${JSON.stringify(requestBody)}`);
|
|
64
|
+
const response = await ctx.http.post(saveUrl, requestBody, { headers: { token } });
|
|
65
|
+
log(`[V2] 新增成员响应: ${JSON.stringify(response)}`);
|
|
66
|
+
|
|
67
|
+
if (response.code !== 200) {
|
|
68
|
+
return response.msg || "新增成员失败";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let result = `新增成员成功!\n`;
|
|
72
|
+
result += `TMP ID: ${tmpId}\n`;
|
|
73
|
+
result += `QQ: ${parsedQQ}\n`;
|
|
74
|
+
result += `邮箱: ${email}\n`;
|
|
75
|
+
result += `加入日期: ${joinDate}\n`;
|
|
76
|
+
if (!autoGenerate) {
|
|
77
|
+
result += `车队编号: ${teamNumber}\n`;
|
|
78
|
+
} else {
|
|
79
|
+
result += `车队编号: 自动生成\n`;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
|
|
83
|
+
} catch (error) {
|
|
84
|
+
ctx.logger.error(`[V2] 新增成员错误: ${error.message}`);
|
|
85
|
+
if (error.response) {
|
|
86
|
+
return `请求失败: ${error.response.status} ${error.response.statusText}`;
|
|
87
|
+
} else if (error.code) {
|
|
88
|
+
return `网络错误: ${error.code}`;
|
|
89
|
+
} else {
|
|
90
|
+
return "系统错误,请稍后重试";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const changePointV1 = require("./changePointV1");
|
|
2
|
+
const changePointV2 = require("./changePointV2");
|
|
3
|
+
|
|
4
|
+
module.exports = async (ctx, cfg, session, target, changeType, quantity, reason) => {
|
|
5
|
+
const { platformVersion } = cfg.mainSettings?.settings || {};
|
|
6
|
+
const platform = (platformVersion || "v1").toLowerCase();
|
|
7
|
+
|
|
8
|
+
switch (platform) {
|
|
9
|
+
case "v2":
|
|
10
|
+
return await changePointV2(ctx, cfg, session, target, changeType, quantity);
|
|
11
|
+
default:
|
|
12
|
+
return await changePointV1(ctx, cfg, session, target, changeType, quantity, reason);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module.exports = async (ctx, cfg, session, target, changeType, quantity, reason) => {
|
|
2
|
+
const { url, token, logOutput } = cfg.mainSettings?.settings || {};
|
|
3
|
+
const { adminUsers } = cfg.changePoint?.settings || {};
|
|
4
|
+
const currentUserQQ = session.userId;
|
|
5
|
+
const isPrivateChat = session.channelId === `private:${currentUserQQ}`;
|
|
6
|
+
const isAdmin = adminUsers.includes(currentUserQQ);
|
|
7
|
+
|
|
8
|
+
const log = (msg) => logOutput && ctx.logger.info(msg);
|
|
9
|
+
|
|
10
|
+
const parseAtQQ = (raw) => {
|
|
11
|
+
if (!raw?.startsWith("<at ")) return raw;
|
|
12
|
+
const idStart = raw.indexOf('id="');
|
|
13
|
+
if (idStart === -1) return "";
|
|
14
|
+
const idEnd = raw.indexOf('"', idStart + 4);
|
|
15
|
+
if (idEnd === -1) return "";
|
|
16
|
+
return raw.substring(idStart + 4, idEnd);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
if (isPrivateChat) {
|
|
21
|
+
return "积分修改功能仅支持群聊使用";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!isAdmin) {
|
|
25
|
+
return "您没有权限使用积分修改功能,请联系管理员";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!token) return "未配置车队平台API认证令牌";
|
|
29
|
+
|
|
30
|
+
if (!changeType || !["增加", "减少"].includes(changeType)) {
|
|
31
|
+
return "请指定操作类型:增加 或 减少";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!quantity || isNaN(Number(quantity)) || Number(quantity) <= 0) {
|
|
35
|
+
return "请输入有效的积分数量(正整数)";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!reason) {
|
|
39
|
+
return "请输入备注原因(必填)";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const baseUrl = url || "open.vtcm.link";
|
|
43
|
+
let userId;
|
|
44
|
+
|
|
45
|
+
if (target) {
|
|
46
|
+
const parsedQQ = parseAtQQ(target);
|
|
47
|
+
const isQQ = parsedQQ && /^\d+$/.test(parsedQQ);
|
|
48
|
+
const isTeamId = !isQQ && /^\d+$/.test(target);
|
|
49
|
+
|
|
50
|
+
let queryUrl;
|
|
51
|
+
if (isQQ) {
|
|
52
|
+
queryUrl = `https://${baseUrl}/api/user/info/list?page=0&limit=7&tmpId=&tmpName=&teamId=&qq=${encodeURIComponent(parsedQQ)}&state=0&teamRole=&token=${token}`;
|
|
53
|
+
log(`[V1] 通过QQ号查询成员: ${parsedQQ}`);
|
|
54
|
+
} else if (isTeamId) {
|
|
55
|
+
queryUrl = `https://${baseUrl}/api/user/info/list?page=0&limit=7&tmpId=&tmpName=&teamId=${encodeURIComponent(target)}&qq=&state=0&teamRole=&token=${token}`;
|
|
56
|
+
log(`[V1] 通过车队编号查询成员: ${target}`);
|
|
57
|
+
} else {
|
|
58
|
+
return "目标用户格式不正确,请输入车队编号或@群成员";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
log(`[V1] 查询成员URL: ${queryUrl.replace(token, "***")}`);
|
|
62
|
+
const queryResponse = await ctx.http.get(queryUrl, { timeout: 10000 });
|
|
63
|
+
log(`[V1] 查询成员响应: code=${queryResponse.code}, listCount=${queryResponse.page?.list?.length}`);
|
|
64
|
+
|
|
65
|
+
if (queryResponse.code !== 0) {
|
|
66
|
+
return queryResponse.msg || "成员信息查询失败";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const list = queryResponse.page?.list;
|
|
70
|
+
if (!list || list.length === 0) {
|
|
71
|
+
return "没有找到该成员";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
userId = list[0].id;
|
|
75
|
+
log(`[V1] 获取到成员ID: ${userId}`);
|
|
76
|
+
} else {
|
|
77
|
+
return "请指定目标用户(车队编号或@群成员)";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const changeTypeNum = changeType === "增加" ? 1 : 2;
|
|
81
|
+
const quantityNum = Number(quantity);
|
|
82
|
+
|
|
83
|
+
const changeUrl = `https://${baseUrl}/api/point/changeRecord/add`;
|
|
84
|
+
const requestBody = {
|
|
85
|
+
userId: userId,
|
|
86
|
+
changeType: changeTypeNum,
|
|
87
|
+
quantity: String(quantityNum),
|
|
88
|
+
reason: reason
|
|
89
|
+
};
|
|
90
|
+
log(`[V1] 积分修改请求: ${changeUrl}, body: ${JSON.stringify(requestBody)}`);
|
|
91
|
+
const changeResponse = await ctx.http.post(changeUrl, requestBody, { timeout: 10000 });
|
|
92
|
+
log(`[V1] 积分修改响应: ${JSON.stringify(changeResponse)}`);
|
|
93
|
+
|
|
94
|
+
if (changeResponse.code !== 0) {
|
|
95
|
+
return changeResponse.msg || "积分修改失败";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return `积分修改成功!\n目标ID: ${userId}\n操作: ${changeType} ${quantityNum} 积分\n备注: ${reason}`;
|
|
99
|
+
|
|
100
|
+
} catch (error) {
|
|
101
|
+
ctx.logger.error(`[V1] 积分修改错误: ${error.message}`);
|
|
102
|
+
if (error.response) {
|
|
103
|
+
return `请求失败: ${error.response.status} ${error.response.statusText}`;
|
|
104
|
+
} else if (error.code) {
|
|
105
|
+
return `网络错误: ${error.code}`;
|
|
106
|
+
} else {
|
|
107
|
+
return "系统错误,请稍后重试";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module.exports = async (ctx, cfg, session, target, changeType, quantity) => {
|
|
2
|
+
const { url, token, logOutput } = cfg.mainSettings?.settings || {};
|
|
3
|
+
const { adminUsers } = cfg.changePoint?.settings || {};
|
|
4
|
+
const currentUserQQ = session.userId;
|
|
5
|
+
const isPrivateChat = session.channelId === `private:${currentUserQQ}`;
|
|
6
|
+
const isAdmin = adminUsers.includes(currentUserQQ);
|
|
7
|
+
|
|
8
|
+
const log = (msg) => logOutput && ctx.logger.info(msg);
|
|
9
|
+
|
|
10
|
+
const parseAtQQ = (raw) => {
|
|
11
|
+
if (!raw?.startsWith("<at ")) return raw;
|
|
12
|
+
const idStart = raw.indexOf('id="');
|
|
13
|
+
if (idStart === -1) return "";
|
|
14
|
+
const idEnd = raw.indexOf('"', idStart + 4);
|
|
15
|
+
if (idEnd === -1) return "";
|
|
16
|
+
return raw.substring(idStart + 4, idEnd);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
if (isPrivateChat) {
|
|
21
|
+
return "积分修改功能仅支持群聊使用";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!isAdmin) {
|
|
25
|
+
return "您没有权限使用积分修改功能,请联系管理员";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!token) return "未配置车队平台V2.0 API认证令牌";
|
|
29
|
+
|
|
30
|
+
if (!changeType || !["增加", "减少"].includes(changeType)) {
|
|
31
|
+
return "请指定操作类型:增加 或 减少";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!quantity || isNaN(Number(quantity)) || Number(quantity) <= 0) {
|
|
35
|
+
return "请输入有效的积分数量(正整数)";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const baseUrl = url || "open.vtcm.link";
|
|
39
|
+
let uid;
|
|
40
|
+
|
|
41
|
+
if (target) {
|
|
42
|
+
const parsedQQ = parseAtQQ(target);
|
|
43
|
+
if (parsedQQ && /^\d+$/.test(parsedQQ)) {
|
|
44
|
+
const memberUrl = `https://${baseUrl}/members/get?qq=${encodeURIComponent(parsedQQ)}`;
|
|
45
|
+
log(`[V2] 获取成员信息: ${memberUrl}`);
|
|
46
|
+
const memberResponse = await ctx.http.get(memberUrl, { headers: { token } });
|
|
47
|
+
log(`[V2] 成员信息响应: ${JSON.stringify(memberResponse)}`);
|
|
48
|
+
|
|
49
|
+
if (memberResponse.code !== 200 || !memberResponse.data?.uid) {
|
|
50
|
+
return memberResponse.msg || `未找到QQ号 ${parsedQQ} 关联的用户信息`;
|
|
51
|
+
}
|
|
52
|
+
uid = memberResponse.data.uid;
|
|
53
|
+
} else if (/^\d+$/.test(target)) {
|
|
54
|
+
uid = target;
|
|
55
|
+
} else {
|
|
56
|
+
return "目标用户格式不正确,请输入UID或@群成员";
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
return "请指定目标用户(UID或@群成员)";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const changeTypeNum = changeType === "增加" ? 1 : 2;
|
|
63
|
+
const quantityNum = Number(quantity);
|
|
64
|
+
|
|
65
|
+
const changeUrl = `https://${baseUrl}/members/point/change`;
|
|
66
|
+
const requestBody = {
|
|
67
|
+
uid: Number(uid),
|
|
68
|
+
changeType: changeTypeNum,
|
|
69
|
+
quantity: quantityNum
|
|
70
|
+
};
|
|
71
|
+
log(`[V2] 积分修改请求: ${changeUrl}, body: ${JSON.stringify(requestBody)}`);
|
|
72
|
+
const changeResponse = await ctx.http.post(changeUrl, requestBody, { headers: { token } });
|
|
73
|
+
log(`[V2] 积分修改响应: ${JSON.stringify(changeResponse)}`);
|
|
74
|
+
|
|
75
|
+
if (changeResponse.code !== 200) {
|
|
76
|
+
return changeResponse.msg || "积分修改失败";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return `积分修改成功!\n目标UID: ${uid}\n操作: ${changeType} ${quantityNum} 积分`;
|
|
80
|
+
|
|
81
|
+
} catch (error) {
|
|
82
|
+
ctx.logger.error(`[V2] 积分修改错误: ${error.message}`);
|
|
83
|
+
if (error.response) {
|
|
84
|
+
return `请求失败: ${error.response.status} ${error.response.statusText}`;
|
|
85
|
+
} else if (error.code) {
|
|
86
|
+
return `网络错误: ${error.code}`;
|
|
87
|
+
} else {
|
|
88
|
+
return "系统错误,请稍后重试";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const queryPointV1 = require("./queryPointV1");
|
|
2
|
+
const queryPointV2 = require("./queryPointV2");
|
|
3
|
+
|
|
4
|
+
module.exports = async (ctx, cfg, session, targetQQ) => {
|
|
5
|
+
const { platformVersion } = cfg.mainSettings?.settings || {};
|
|
6
|
+
const platform = (platformVersion || "v1").toLowerCase();
|
|
7
|
+
|
|
8
|
+
let queryQQ = targetQQ;
|
|
9
|
+
if (!queryQQ) {
|
|
10
|
+
queryQQ = session.userId;
|
|
11
|
+
} else {
|
|
12
|
+
if (queryQQ.startsWith("<at ")) {
|
|
13
|
+
if (queryQQ.startsWith('<at ')) {
|
|
14
|
+
queryQQ = queryQQ.replace('<at ', '');
|
|
15
|
+
}
|
|
16
|
+
let id = '';
|
|
17
|
+
const idStart = queryQQ.indexOf('id="');
|
|
18
|
+
if (idStart !== -1) {
|
|
19
|
+
const valueStart = idStart + 4;
|
|
20
|
+
const valueEnd = queryQQ.indexOf('"', valueStart);
|
|
21
|
+
if (valueEnd !== -1) {
|
|
22
|
+
id = queryQQ.substring(valueStart, valueEnd);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
queryQQ = id;
|
|
26
|
+
}
|
|
27
|
+
if (!/^\d+$/.test(queryQQ)) {
|
|
28
|
+
return "QQ号格式不正确,请输入纯数字QQ号";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
switch (platform) {
|
|
33
|
+
case "v2":
|
|
34
|
+
return await queryPointV2(ctx, cfg, queryQQ);
|
|
35
|
+
default:
|
|
36
|
+
return await queryPointV1(ctx, cfg, queryQQ);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module.exports = async (ctx, cfg, queryQQ) => {
|
|
2
|
+
const { Name, url, token, logOutput } = cfg.mainSettings?.settings || {};
|
|
3
|
+
|
|
4
|
+
try {
|
|
5
|
+
if (logOutput) {
|
|
6
|
+
ctx.logger.info(`开始查询用户 ${queryQQ} 的积分`);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const userInfoUrl = `https://${url}/api/user/info/list?token=${token}&page=0&limit=7&tmpId=&tmpName=&teamId=&qq=${queryQQ}&state=0&teamRole=`;
|
|
10
|
+
if (logOutput) {
|
|
11
|
+
ctx.logger.info(`请求V1.0用户信息: ${userInfoUrl}`);
|
|
12
|
+
}
|
|
13
|
+
const userInfoResponse = await ctx.http.post(userInfoUrl);
|
|
14
|
+
if (logOutput) {
|
|
15
|
+
ctx.logger.info(`V1.0用户信息响应: ${JSON.stringify(userInfoResponse)}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (userInfoResponse.code !== 0) {
|
|
19
|
+
return `获取用户信息失败: ${userInfoResponse.msg || "未知错误"}`;
|
|
20
|
+
}
|
|
21
|
+
const userList = userInfoResponse.page?.list || [];
|
|
22
|
+
if (userList.length === 0) {
|
|
23
|
+
return `未找到QQ号 ${queryQQ} 关联的用户信息`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const userInfo = userList[0];
|
|
27
|
+
const tmpName = userInfo.tmpName || "未知用户";
|
|
28
|
+
const teamRole = userInfo.teamRole || userInfo.tmpRole || "未知职位";
|
|
29
|
+
const teamId = userInfo.teamId || userInfo.uid || "未知编号";
|
|
30
|
+
const rewardPoints = userInfo.rewardPoints || userInfo.point || 0;
|
|
31
|
+
const joinDate = userInfo.joinDate || "未知";
|
|
32
|
+
|
|
33
|
+
let message = `🚛 ${Name}平台 - 积分查询🚚\n`;
|
|
34
|
+
message += `👤 用户: ${tmpName}\n`;
|
|
35
|
+
message += `🆔 车队编号: ${teamId}\n`;
|
|
36
|
+
message += `📧 QQ: ${queryQQ}\n`;
|
|
37
|
+
message += `🏆 职位: ${teamRole}\n`;
|
|
38
|
+
message += `⭐ 当前积分: ${rewardPoints}\n`;
|
|
39
|
+
message += `📅 加入日期: ${joinDate}`;
|
|
40
|
+
return message;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
ctx.logger.error(`积分查询过程出错: ${error}`);
|
|
43
|
+
if (error.response) {
|
|
44
|
+
return `请求失败: ${error.response.status} ${error.response.statusText}`;
|
|
45
|
+
} else if (error.code) {
|
|
46
|
+
return `网络错误: ${error.code}`;
|
|
47
|
+
} else {
|
|
48
|
+
return "系统错误,请稍后重试";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module.exports = async (ctx, cfg, queryQQ) => {
|
|
2
|
+
const { Name, url, token, logOutput } = cfg.mainSettings?.settings || {};
|
|
3
|
+
|
|
4
|
+
try {
|
|
5
|
+
if (logOutput) {
|
|
6
|
+
ctx.logger.info(`开始查询用户 ${queryQQ} 的积分`);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const baseUrl = url || "open.vtcm.link";
|
|
10
|
+
const userInfoUrl = `https://${baseUrl}/members/get?token=${token}&qq=${queryQQ}`;
|
|
11
|
+
if (logOutput) {
|
|
12
|
+
ctx.logger.info(`请求V2.0用户信息: ${userInfoUrl}`);
|
|
13
|
+
}
|
|
14
|
+
const userInfoResponse = await ctx.http.get(userInfoUrl);
|
|
15
|
+
if (logOutput) {
|
|
16
|
+
ctx.logger.info(`V2.0用户信息响应: ${JSON.stringify(userInfoResponse)}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (userInfoResponse.code !== 200) {
|
|
20
|
+
return `获取用户信息失败: ${userInfoResponse.msg || "未知错误"}`;
|
|
21
|
+
}
|
|
22
|
+
if (!userInfoResponse.data) {
|
|
23
|
+
return `未找到QQ号 ${queryQQ} 关联的用户信息`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const userInfo = userInfoResponse.data;
|
|
27
|
+
const tmpName = userInfo.tmpName || "未知用户";
|
|
28
|
+
const teamRole = userInfo.teamRole || userInfo.tmpRole || "未知职位";
|
|
29
|
+
const teamId = userInfo.teamId || userInfo.uid || "未知编号";
|
|
30
|
+
const rewardPoints = userInfo.rewardPoints || userInfo.point || 0;
|
|
31
|
+
const joinDate = userInfo.joinDate || "未知";
|
|
32
|
+
|
|
33
|
+
let message = `🚛 ${Name}平台 - 积分查询🚚\n`;
|
|
34
|
+
message += `👤 用户: ${tmpName}\n`;
|
|
35
|
+
message += `🆔 车队编号: ${teamId}\n`;
|
|
36
|
+
message += `📧 QQ: ${queryQQ}\n`;
|
|
37
|
+
message += `🏆 职位: ${teamRole}\n`;
|
|
38
|
+
message += `⭐ 当前积分: ${rewardPoints}\n`;
|
|
39
|
+
message += `📅 加入日期: ${joinDate}`;
|
|
40
|
+
return message;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
ctx.logger.error(`积分查询过程出错: ${error}`);
|
|
43
|
+
if (error.response) {
|
|
44
|
+
return `请求失败: ${error.response.status} ${error.response.statusText}`;
|
|
45
|
+
} else if (error.code) {
|
|
46
|
+
return `网络错误: ${error.code}`;
|
|
47
|
+
} else {
|
|
48
|
+
return "系统错误,请稍后重试";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const resetPasswordV1 = require("./resetPasswordV1");
|
|
2
|
+
const resetPasswordV2 = require("./resetPasswordV2");
|
|
3
|
+
|
|
4
|
+
module.exports = async (ctx, cfg, session, targetTeamId, password) => {
|
|
5
|
+
const { platformVersion } = cfg.mainSettings?.settings || {};
|
|
6
|
+
const platform = (platformVersion || "v1").toLowerCase();
|
|
7
|
+
|
|
8
|
+
switch (platform) {
|
|
9
|
+
case "v2": {
|
|
10
|
+
let actualPassword = password;
|
|
11
|
+
if (targetTeamId && !password && !/^\d+$/.test(targetTeamId)) {
|
|
12
|
+
actualPassword = targetTeamId;
|
|
13
|
+
targetTeamId = undefined;
|
|
14
|
+
}
|
|
15
|
+
return await resetPasswordV2(ctx, cfg, session, actualPassword);
|
|
16
|
+
}
|
|
17
|
+
default:
|
|
18
|
+
return await resetPasswordV1(ctx, cfg, session, targetTeamId, password);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
module.exports = async (ctx, cfg, session, targetTeamId, password) => {
|
|
2
|
+
const { url, token, logOutput } = cfg.mainSettings?.settings || {};
|
|
3
|
+
const { adminUsers } = cfg.resetPassword?.settings || {};
|
|
4
|
+
const currentUserQQ = session.userId;
|
|
5
|
+
const isAdmin = adminUsers.includes(currentUserQQ);
|
|
6
|
+
const isPrivateChat = session.channelId === `private:${currentUserQQ}`;
|
|
7
|
+
|
|
8
|
+
const log = (msg) => logOutput && ctx.logger.info(msg);
|
|
9
|
+
|
|
10
|
+
const parseAtQQ = (raw) => {
|
|
11
|
+
if (!raw?.startsWith("<at ")) return raw;
|
|
12
|
+
const idStart = raw.indexOf('id="');
|
|
13
|
+
if (idStart === -1) return "";
|
|
14
|
+
const idEnd = raw.indexOf('"', idStart + 4);
|
|
15
|
+
if (idEnd === -1) return "";
|
|
16
|
+
return raw.substring(idStart + 4, idEnd);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const fetchData = async (apiUrl) => {
|
|
20
|
+
try {
|
|
21
|
+
const response = await ctx.http.post(apiUrl);
|
|
22
|
+
log(`请求响应: ${JSON.stringify(response)}`);
|
|
23
|
+
return response;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
log(`请求错误: ${error}`);
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const getUserInfo = async (params) => {
|
|
31
|
+
const query = new URLSearchParams({ token, page: 0, limit: 1, ...params }).toString();
|
|
32
|
+
const userInfoUrl = `https://${url}/api/user/info/list?${query}`;
|
|
33
|
+
log(`获取用户信息: ${userInfoUrl}`);
|
|
34
|
+
const response = await fetchData(userInfoUrl);
|
|
35
|
+
|
|
36
|
+
if (response.code !== 0 || !response.page?.list?.length) {
|
|
37
|
+
throw new Error(`未找到用户信息`);
|
|
38
|
+
}
|
|
39
|
+
return response.page.list[0];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const resetPasswordApi = async (teamId) => {
|
|
43
|
+
const resetUrl = `https://${url}/api/user/info/resetPasswordWithTeamId?token=${token}&teamId=${teamId}`;
|
|
44
|
+
log(`重置密码请求: ${resetUrl}`);
|
|
45
|
+
const response = await fetchData(resetUrl);
|
|
46
|
+
|
|
47
|
+
if (response.code !== 0) {
|
|
48
|
+
throw new Error(response.msg || "未知错误");
|
|
49
|
+
}
|
|
50
|
+
return teamId;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
let teamId, targetQQ;
|
|
55
|
+
|
|
56
|
+
if (targetTeamId?.startsWith("<at ")) {
|
|
57
|
+
if (!isAdmin) return "您没有权限重置其他用户的密码,请联系管理员";
|
|
58
|
+
|
|
59
|
+
const idStart = targetTeamId.indexOf('id="');
|
|
60
|
+
if (idStart === -1) return "获取QQ号错误,请使用车队编号重置";
|
|
61
|
+
|
|
62
|
+
targetQQ = targetTeamId.substring(idStart + 4, targetTeamId.indexOf('"', idStart + 4));
|
|
63
|
+
if (!/^\d+$/.test(targetQQ)) return "获取QQ号错误,请使用车队编号重置";
|
|
64
|
+
|
|
65
|
+
log(`管理员 ${currentUserQQ} 重置QQ: ${targetQQ} 密码`);
|
|
66
|
+
const userInfo = await getUserInfo({ qq: targetQQ });
|
|
67
|
+
teamId = userInfo.teamId;
|
|
68
|
+
} else {
|
|
69
|
+
if (isPrivateChat) {
|
|
70
|
+
if (!targetTeamId) {
|
|
71
|
+
log(`用户 ${currentUserQQ} 发起密码重置`);
|
|
72
|
+
const userInfo = await getUserInfo({ qq: currentUserQQ });
|
|
73
|
+
teamId = userInfo.teamId;
|
|
74
|
+
if (!teamId) return "未找到您的信息,请联系管理员";
|
|
75
|
+
} else {
|
|
76
|
+
log(`用户 ${currentUserQQ} 尝试重置车队编号 ${targetTeamId} 的密码`);
|
|
77
|
+
const userInfo = await getUserInfo({ teamId: targetTeamId });
|
|
78
|
+
|
|
79
|
+
if (!isAdmin && String(userInfo.qq) !== String(currentUserQQ)) {
|
|
80
|
+
return "您没有权限重置其他成员的密码,请联系管理员";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
teamId = targetTeamId;
|
|
84
|
+
if (isAdmin) {
|
|
85
|
+
log(`管理员 ${currentUserQQ} 重置车队编号 ${targetTeamId} 的密码`);
|
|
86
|
+
} else {
|
|
87
|
+
log(`验证通过,车队编号 ${targetTeamId} 对应的QQ ${userInfo.qq} 与当前用户QQ ${currentUserQQ} 一致`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
if (!isAdmin) return "您没有权限重置其他用户的密码,请联系管理员重置,或私聊机器人重置";
|
|
92
|
+
|
|
93
|
+
teamId = targetTeamId;
|
|
94
|
+
log(`管理员 ${currentUserQQ} 重置车队: ${teamId} 密码`);
|
|
95
|
+
await getUserInfo({ teamId });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const resetTeamId = await resetPasswordApi(teamId);
|
|
100
|
+
const isAdminOp = isAdmin && (targetTeamId || targetQQ);
|
|
101
|
+
return isAdminOp
|
|
102
|
+
? `管理员操作:车队编号 ${resetTeamId} 的密码重置成功!新密码已发送到用户邮箱。`
|
|
103
|
+
: "密码重置成功!新密码已发送到您的邮箱,请查收。";
|
|
104
|
+
|
|
105
|
+
} catch (error) {
|
|
106
|
+
ctx.logger.error(`密码重置错误: ${error.message}`);
|
|
107
|
+
if (error.response) {
|
|
108
|
+
return `请求失败: ${error.response.status} ${error.response.statusText}`;
|
|
109
|
+
} else if (error.code) {
|
|
110
|
+
return `网络错误: ${error.code}`;
|
|
111
|
+
} else {
|
|
112
|
+
return error.message.includes("未找到") ? error.message : "系统错误,请稍后重试";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module.exports = async (ctx, cfg, session, password) => {
|
|
2
|
+
const { url, token, logOutput } = cfg.mainSettings?.settings || {};
|
|
3
|
+
const currentUserQQ = session.userId;
|
|
4
|
+
const isPrivateChat = session.channelId === `private:${currentUserQQ}`;
|
|
5
|
+
|
|
6
|
+
const log = (msg) => logOutput && ctx.logger.info(msg);
|
|
7
|
+
|
|
8
|
+
const randomChar = (chars) => chars[Math.floor(Math.random() * chars.length)];
|
|
9
|
+
const generatePassword = () => {
|
|
10
|
+
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
11
|
+
const digits = "0123456789";
|
|
12
|
+
const all = letters + digits;
|
|
13
|
+
const chars = [randomChar(letters), randomChar(digits)];
|
|
14
|
+
for (let i = 0; i < 8; i++) {
|
|
15
|
+
chars.push(randomChar(all));
|
|
16
|
+
}
|
|
17
|
+
for (let i = chars.length - 1; i > 0; i--) {
|
|
18
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
19
|
+
[chars[i], chars[j]] = [chars[j], chars[i]];
|
|
20
|
+
}
|
|
21
|
+
return chars.join("");
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
if (!isPrivateChat) {
|
|
26
|
+
return "V2.0平台密码重置仅支持私聊使用,请私聊机器人进行操作";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!token) return "未配置车队平台V2.0 API认证令牌";
|
|
30
|
+
|
|
31
|
+
const baseUrl = url || "open.vtcm.link";
|
|
32
|
+
const queryQQ = currentUserQQ;
|
|
33
|
+
|
|
34
|
+
const memberUrl = `https://${baseUrl}/members/get?qq=${encodeURIComponent(queryQQ)}`;
|
|
35
|
+
log(`[V2] 获取成员信息: ${memberUrl}`);
|
|
36
|
+
const memberResponse = await ctx.http.get(memberUrl, { headers: { token } });
|
|
37
|
+
log(`[V2] 成员信息响应: ${JSON.stringify(memberResponse)}`);
|
|
38
|
+
|
|
39
|
+
if (memberResponse.code !== 200 || !memberResponse.data?.uid) {
|
|
40
|
+
return memberResponse.msg || "未找到与您QQ号关联的用户信息";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const memberUid = memberResponse.data.uid;
|
|
44
|
+
const newPassword = password?.trim() || generatePassword();
|
|
45
|
+
|
|
46
|
+
if (newPassword.length < 6 || newPassword.length > 16) {
|
|
47
|
+
return "密码长度需为6-16位";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const resetUrl = `https://${baseUrl}/members/${memberUid}/password`;
|
|
51
|
+
log(`[V2] 重置密码请求: ${resetUrl}`);
|
|
52
|
+
const resetResponse = await ctx.http.post(resetUrl, { password: newPassword }, { headers: { token } });
|
|
53
|
+
log(`[V2] 重置密码响应: ${JSON.stringify(resetResponse)}`);
|
|
54
|
+
|
|
55
|
+
if (resetResponse.code !== 200) {
|
|
56
|
+
return resetResponse.msg || "密码重置失败";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return `密码重置成功!\n您的UID: ${memberUid}\n新密码: ${newPassword}\n请妥善保管好您的密码,以防泄露`;
|
|
60
|
+
|
|
61
|
+
} catch (error) {
|
|
62
|
+
ctx.logger.error(`[V2] 密码重置错误: ${error.message}`);
|
|
63
|
+
if (error.response) {
|
|
64
|
+
return `请求失败: ${error.response.status} ${error.response.statusText}`;
|
|
65
|
+
} else if (error.code) {
|
|
66
|
+
return `网络错误: ${error.code}`;
|
|
67
|
+
} else {
|
|
68
|
+
return "系统错误,请稍后重试";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
async updateTodayActivities() {
|
|
3
|
+
try {
|
|
4
|
+
this.todayActivities = [];
|
|
5
|
+
|
|
6
|
+
const baseUrl = this.cfg.mainSettings?.url || "open.vtcm.link";
|
|
7
|
+
const token = this.cfg.mainSettings?.token || "";
|
|
8
|
+
const fullUrl = `https://${baseUrl}/api/activity/info/list?token=${token}&page=1&limit=100&themeName=`;
|
|
9
|
+
this.logger.api(`请求V1车队平台API: ${fullUrl.replace(token, "***")}`);
|
|
10
|
+
|
|
11
|
+
const startTime = Date.now();
|
|
12
|
+
const response = await this.ctx.http.get(fullUrl, { timeout: 10000 });
|
|
13
|
+
const duration = Date.now() - startTime;
|
|
14
|
+
this.logger.api(`V1车队平台API响应耗时: ${duration}ms, 状态码: ${response.code}`);
|
|
15
|
+
|
|
16
|
+
if (this.cfg.debug?.logApiResponses) {
|
|
17
|
+
this.logger.api("V1车队平台API响应详情:", {
|
|
18
|
+
code: response.code,
|
|
19
|
+
totalCount: response.data?.totalCount,
|
|
20
|
+
listCount: response.data?.list?.length
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (response.code === 0 && response.data?.list) {
|
|
25
|
+
const now = new Date();
|
|
26
|
+
const today = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`;
|
|
27
|
+
this.logger.debug(`[V1活动更新] 当前本地日期: ${today}, UTC日期: ${new Date().toISOString().split("T")[0]}`);
|
|
28
|
+
|
|
29
|
+
const originalCount = response.data.list.length;
|
|
30
|
+
this.logger.debug(`[V1活动更新] API返回活动总数: ${originalCount}`);
|
|
31
|
+
|
|
32
|
+
this.todayActivities = response.data.list.filter((activity) => {
|
|
33
|
+
const activityDate = activity.startTime?.split(" ")[0];
|
|
34
|
+
return activityDate === today;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.logger.info(`[V1活动更新] 从车队平台找到 ${this.todayActivities.length}/${originalCount} 个今日活动`);
|
|
38
|
+
} else {
|
|
39
|
+
this.logger.error(`[V1活动更新] 车队平台API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
|
|
40
|
+
this.todayActivities = [];
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
this.logger.error("[V1活动更新] 获取车队平台活动列表失败:", error.message);
|
|
44
|
+
this.todayActivities = [];
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
async checkAutoClock(activity) {
|
|
49
|
+
try {
|
|
50
|
+
const baseUrl = this.cfg.mainSettings?.url || "open.vtcm.link";
|
|
51
|
+
const token = this.cfg.mainSettings?.token || "";
|
|
52
|
+
const fullUrl = `https://${baseUrl}/api/activity/info/info/${activity.id}?token=${token}`;
|
|
53
|
+
this.logger.api(`请求V1活动详情API: ${fullUrl.replace(token, "***")}`);
|
|
54
|
+
|
|
55
|
+
const response = await this.ctx.http.get(fullUrl, { timeout: 10000 });
|
|
56
|
+
this.logger.api(`V1活动详情API响应:`, response);
|
|
57
|
+
|
|
58
|
+
if (response.code === 0 && response.data) {
|
|
59
|
+
const enableAutoClock = response.data.enableAutoClock;
|
|
60
|
+
this.logger.debug(`活动 "${activity.themeName}" V1自动打卡状态: ${enableAutoClock}`);
|
|
61
|
+
return enableAutoClock === 1;
|
|
62
|
+
} else {
|
|
63
|
+
this.logger.error(`V1活动详情API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
this.logger.error(`检查V1活动 "${activity.themeName}" 自动打卡状态失败:`, error.message);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
async updateTodayActivities() {
|
|
3
|
+
try {
|
|
4
|
+
this.todayActivities = [];
|
|
5
|
+
|
|
6
|
+
const baseUrl = this.cfg.mainSettings?.url || "open.vtcm.link";
|
|
7
|
+
const token = this.cfg.mainSettings?.token || "";
|
|
8
|
+
const fullUrl = `https://${baseUrl}/events`;
|
|
9
|
+
this.logger.api(`请求V2车队平台API: ${fullUrl}`);
|
|
10
|
+
|
|
11
|
+
const startTime = Date.now();
|
|
12
|
+
const response = await this.ctx.http.get(fullUrl, {
|
|
13
|
+
timeout: 10000,
|
|
14
|
+
headers: { token }
|
|
15
|
+
});
|
|
16
|
+
const duration = Date.now() - startTime;
|
|
17
|
+
this.logger.api(`V2车队平台API响应耗时: ${duration}ms, 状态码: ${response.code}`);
|
|
18
|
+
|
|
19
|
+
if (this.cfg.debug?.logApiResponses) {
|
|
20
|
+
this.logger.api("V2车队平台API响应详情:", {
|
|
21
|
+
code: response.code,
|
|
22
|
+
totalCount: response.total,
|
|
23
|
+
listCount: response.rows?.length
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (response.code === 200 && response.rows) {
|
|
28
|
+
const now = new Date();
|
|
29
|
+
const today = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`;
|
|
30
|
+
this.logger.debug(`[V2活动更新] 当前本地日期: ${today}, UTC日期: ${new Date().toISOString().split("T")[0]}`);
|
|
31
|
+
|
|
32
|
+
const originalCount = response.rows.length;
|
|
33
|
+
this.logger.debug(`[V2活动更新] API返回活动总数: ${originalCount}`);
|
|
34
|
+
|
|
35
|
+
this.todayActivities = response.rows.filter((activity) => {
|
|
36
|
+
return activity.eventDate === today;
|
|
37
|
+
}).map((activity) => ({
|
|
38
|
+
...activity,
|
|
39
|
+
themeName: activity.eventName,
|
|
40
|
+
startTime: activity.eventDate,
|
|
41
|
+
profileFile: activity.saveFileId
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
this.logger.info(`[V2活动更新] 从车队平台找到 ${this.todayActivities.length}/${originalCount} 个今日活动`);
|
|
45
|
+
if (this.cfg.debug?.debugMode && this.todayActivities.length > 0) {
|
|
46
|
+
const todayActivityNames = this.todayActivities.map(a => `${a.themeName}: ${a.startTime}`);
|
|
47
|
+
this.logger.debug(`[V2活动更新] 今日活动详情:`, todayActivityNames);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
this.logger.error(`[V2活动更新] 车队平台API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
|
|
51
|
+
this.todayActivities = [];
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
this.logger.error("[V2活动更新] 获取车队平台活动列表失败:", error.message);
|
|
55
|
+
this.todayActivities = [];
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
async checkAutoClock(activity) {
|
|
60
|
+
try {
|
|
61
|
+
const baseUrl = this.cfg.mainSettings?.url || "open.vtcm.link";
|
|
62
|
+
const token = this.cfg.mainSettings?.token || "";
|
|
63
|
+
const fullUrl = `https://${baseUrl}/events/${activity.id}`;
|
|
64
|
+
this.logger.api(`请求V2活动详情API: ${fullUrl}`);
|
|
65
|
+
|
|
66
|
+
const response = await this.ctx.http.get(fullUrl, {
|
|
67
|
+
timeout: 10000,
|
|
68
|
+
headers: { token }
|
|
69
|
+
});
|
|
70
|
+
this.logger.api(`V2活动详情API响应:`, response);
|
|
71
|
+
|
|
72
|
+
if (response.code === 200 && response.data) {
|
|
73
|
+
const autoCheckInEnable = response.data.autoCheckInEnable;
|
|
74
|
+
this.logger.debug(`活动 "${activity.themeName}" V2自动打卡状态: ${autoCheckInEnable}`);
|
|
75
|
+
return autoCheckInEnable === 1;
|
|
76
|
+
} else {
|
|
77
|
+
this.logger.error(`V2活动详情API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
this.logger.error(`检查V2活动 "${activity.themeName}" 自动打卡状态失败:`, error.message);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
@@ -4,6 +4,9 @@ exports.ActivityService = void 0;
|
|
|
4
4
|
|
|
5
5
|
const koishi_1 = require("koishi");
|
|
6
6
|
|
|
7
|
+
const v1Methods = require('./tmpActivityService/tmpActivityServiceV1');
|
|
8
|
+
const v2Methods = require('./tmpActivityService/tmpActivityServiceV2');
|
|
9
|
+
|
|
7
10
|
class ActivityService {
|
|
8
11
|
constructor(ctx, config) {
|
|
9
12
|
this.ctx = ctx;
|
|
@@ -14,6 +17,13 @@ class ActivityService {
|
|
|
14
17
|
this.sentNoActivityNotification = false;
|
|
15
18
|
this.timers = [];
|
|
16
19
|
this.logger = this.initLogger();
|
|
20
|
+
|
|
21
|
+
const platformVersion = (this.cfg.mainSettings?.platformVersion || "v1").toLowerCase();
|
|
22
|
+
if (platformVersion === "v2") {
|
|
23
|
+
Object.assign(this, v2Methods);
|
|
24
|
+
} else {
|
|
25
|
+
Object.assign(this, v1Methods);
|
|
26
|
+
}
|
|
17
27
|
}
|
|
18
28
|
|
|
19
29
|
initLogger() {
|
|
@@ -215,63 +225,6 @@ class ActivityService {
|
|
|
215
225
|
}
|
|
216
226
|
}
|
|
217
227
|
|
|
218
|
-
async updateTodayActivities() {
|
|
219
|
-
try {
|
|
220
|
-
this.todayActivities = [];
|
|
221
|
-
|
|
222
|
-
const protocol = this.cfg.api.useHttps ? "https://" : "http://";
|
|
223
|
-
const fullUrl = `${protocol}${this.cfg.api.url}/api/activity/info/list?token=${this.cfg.api.token}&page=1&limit=100&themeName=`;
|
|
224
|
-
this.logger.api(`请求车队平台API: ${fullUrl.replace(this.cfg.api.token, "***")}`);
|
|
225
|
-
|
|
226
|
-
const startTime = Date.now();
|
|
227
|
-
const response = await this.ctx.http.get(fullUrl, { timeout: 10000 });
|
|
228
|
-
const duration = Date.now() - startTime;
|
|
229
|
-
this.logger.api(`车队平台API响应耗时: ${duration}ms, 状态码: ${response.code}`);
|
|
230
|
-
|
|
231
|
-
if (this.cfg.debug?.logApiResponses) {
|
|
232
|
-
this.logger.api("车队平台API响应详情:", {
|
|
233
|
-
code: response.code,
|
|
234
|
-
totalCount: response.data?.totalCount,
|
|
235
|
-
listCount: response.data?.list?.length
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (response.code === 0 && response.data?.list) {
|
|
240
|
-
const now = new Date();
|
|
241
|
-
const today = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`;
|
|
242
|
-
this.logger.debug(`[活动更新] 当前本地日期: ${today}, UTC日期: ${new Date().toISOString().split("T")[0]}`);
|
|
243
|
-
|
|
244
|
-
const originalCount = response.data.list.length;
|
|
245
|
-
this.logger.debug(`[活动更新] API返回活动总数: ${originalCount}`);
|
|
246
|
-
if (this.cfg.debug?.debugMode && originalCount > 0) {
|
|
247
|
-
const activityDates = response.data.list.map(a => `${a.themeName}: ${a.startTime?.split(" ")[0]}`);
|
|
248
|
-
this.logger.debug(`[活动更新] 所有活动日期:`, activityDates);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
this.todayActivities = response.data.list.filter((activity) => {
|
|
252
|
-
const activityDate = activity.startTime?.split(" ")[0];
|
|
253
|
-
const isToday = activityDate === today;
|
|
254
|
-
if (!isToday && this.cfg.debug?.debugMode) {
|
|
255
|
-
this.logger.debug(`[活动更新] 跳过非今日活动: ${activity.themeName}, 日期: ${activityDate}, 当前日期: ${today}`);
|
|
256
|
-
}
|
|
257
|
-
return isToday;
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
this.logger.info(`[活动更新] 从车队平台找到 ${this.todayActivities.length}/${originalCount} 个今日活动`);
|
|
261
|
-
if (this.cfg.debug?.debugMode && this.todayActivities.length > 0) {
|
|
262
|
-
const todayActivityNames = this.todayActivities.map(a => `${a.themeName}: ${a.startTime}`);
|
|
263
|
-
this.logger.debug(`[活动更新] 今日活动详情:`, todayActivityNames);
|
|
264
|
-
}
|
|
265
|
-
} else {
|
|
266
|
-
this.logger.error(`[活动更新] 车队平台API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
|
|
267
|
-
this.todayActivities = [];
|
|
268
|
-
}
|
|
269
|
-
} catch (error) {
|
|
270
|
-
this.logger.error("[活动更新] 获取车队平台活动列表失败:", error.message);
|
|
271
|
-
this.todayActivities = [];
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
228
|
async updateTodayTMPEvents() {
|
|
276
229
|
try {
|
|
277
230
|
this.todayTMPEvents = [];
|
|
@@ -603,19 +556,11 @@ class ActivityService {
|
|
|
603
556
|
|
|
604
557
|
this.logger.debug(`开始检查 ${this.todayActivities.length} 个活动的自动打卡状态`);
|
|
605
558
|
|
|
606
|
-
const platformVersion = (this.cfg.mainSettings?.platformVersion || "v1").toLowerCase();
|
|
607
|
-
|
|
608
559
|
for (const activity of this.todayActivities) {
|
|
609
560
|
try {
|
|
610
|
-
|
|
561
|
+
const autoClockEnabled = await this.checkAutoClock(activity);
|
|
611
562
|
|
|
612
|
-
|
|
613
|
-
autoClockEnabled = await this.checkV1AutoClock(activity);
|
|
614
|
-
} else if (platformVersion === "v2") {
|
|
615
|
-
autoClockEnabled = await this.checkV2AutoClock(activity);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
const message = autoClockEnabled
|
|
563
|
+
const message = autoClockEnabled
|
|
619
564
|
? `今日活动自动打卡已设置`
|
|
620
565
|
: `请注意,今日活动打卡未设置。\n 活动名称: ${activity.themeName || '未知活动'}`;
|
|
621
566
|
|
|
@@ -635,58 +580,6 @@ class ActivityService {
|
|
|
635
580
|
}
|
|
636
581
|
}
|
|
637
582
|
|
|
638
|
-
async checkV1AutoClock(activity) {
|
|
639
|
-
try {
|
|
640
|
-
const protocol = this.cfg.api.useHttps ? "https://" : "http://";
|
|
641
|
-
const fullUrl = `${protocol}${this.cfg.api.url}/api/activity/info/info/${activity.id}?token=${this.cfg.api.token}`;
|
|
642
|
-
this.logger.api(`请求V1活动详情API: ${fullUrl.replace(this.cfg.api.token, "***")}`);
|
|
643
|
-
|
|
644
|
-
const response = await this.ctx.http.get(fullUrl, { timeout: 10000 });
|
|
645
|
-
this.logger.api(`V1活动详情API响应:`, response);
|
|
646
|
-
|
|
647
|
-
if (response.code === 0 && response.data) {
|
|
648
|
-
const enableAutoClock = response.data.enableAutoClock;
|
|
649
|
-
this.logger.debug(`活动 "${activity.themeName}" V1自动打卡状态: ${enableAutoClock}`);
|
|
650
|
-
return enableAutoClock === 1;
|
|
651
|
-
} else {
|
|
652
|
-
this.logger.error(`V1活动详情API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
|
|
653
|
-
return false;
|
|
654
|
-
}
|
|
655
|
-
} catch (error) {
|
|
656
|
-
this.logger.error(`检查V1活动 "${activity.themeName}" 自动打卡状态失败:`, error.message);
|
|
657
|
-
return false;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
async checkV2AutoClock(activity) {
|
|
662
|
-
try {
|
|
663
|
-
const protocol = this.cfg.api.useHttps ? "https://" : "http://";
|
|
664
|
-
const fullUrl = `${protocol}${this.cfg.api.url}/activity/list?token=${this.cfg.api.token}`;
|
|
665
|
-
this.logger.api(`请求V2活动列表API: ${fullUrl.replace(this.cfg.api.token, "***")}`);
|
|
666
|
-
|
|
667
|
-
const response = await this.ctx.http.get(fullUrl, { timeout: 10000 });
|
|
668
|
-
this.logger.api(`V2活动列表API响应:`, response);
|
|
669
|
-
|
|
670
|
-
if (response.code === 200 && response.data?.rows) {
|
|
671
|
-
const activityDetail = response.data.rows.find(row => row.id === activity.id);
|
|
672
|
-
if (activityDetail) {
|
|
673
|
-
const autoCheckInEnable = activityDetail.autoCheckInEnable;
|
|
674
|
-
this.logger.debug(`活动 "${activity.themeName}" V2自动打卡状态: ${autoCheckInEnable}`);
|
|
675
|
-
return autoCheckInEnable === 1;
|
|
676
|
-
} else {
|
|
677
|
-
this.logger.warn(`在V2活动列表中未找到活动ID: ${activity.id}`);
|
|
678
|
-
return false;
|
|
679
|
-
}
|
|
680
|
-
} else {
|
|
681
|
-
this.logger.error(`V2活动列表API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
|
|
682
|
-
return false;
|
|
683
|
-
}
|
|
684
|
-
} catch (error) {
|
|
685
|
-
this.logger.error(`检查V2活动 "${activity.themeName}" 自动打卡状态失败:`, error.message);
|
|
686
|
-
return false;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
583
|
cleanup() {
|
|
691
584
|
this.logger.debug("插件卸载,开始清理资源");
|
|
692
585
|
this.todayActivities = [];
|
|
@@ -701,4 +594,4 @@ class ActivityService {
|
|
|
701
594
|
}
|
|
702
595
|
}
|
|
703
596
|
|
|
704
|
-
exports.ActivityService = ActivityService;
|
|
597
|
+
exports.ActivityService = ActivityService;
|
package/lib/index.js
CHANGED
|
@@ -17,8 +17,10 @@ const commands = {
|
|
|
17
17
|
tmpVersion: require('./command/tmpVersion'),
|
|
18
18
|
tmpDlcMap: require('./command/tmpDlcMap'),
|
|
19
19
|
tmpMileageRanking: require('./command/tmpMileageRanking'),
|
|
20
|
-
resetPassword: require('./command/ets-app/resetPassword'),
|
|
21
|
-
queryPoint: require('./command/ets-app/queryPoint'),
|
|
20
|
+
resetPassword: require('./command/ets-app/resetPassword/resetPassword'),
|
|
21
|
+
queryPoint: require('./command/ets-app/queryPoint/queryPoint'),
|
|
22
|
+
changePoint: require('./command/ets-app/changePoint/changePoint'),
|
|
23
|
+
addMember: require('./command/ets-app/addMember/addMemberV2'),
|
|
22
24
|
tmpVtc: require('./command/tmpVtc'),
|
|
23
25
|
tmpFootprint: require('./command/tmpFootprint')
|
|
24
26
|
};
|
|
@@ -77,6 +79,8 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
77
79
|
tmpFootprint: koishi_1.Schema.boolean().default(true).description('是否启用足迹查询'),
|
|
78
80
|
mainSettings: koishi_1.Schema.boolean().default(false).description('是否启用车队平台积分查询功能'),
|
|
79
81
|
resetPassword: koishi_1.Schema.boolean().default(false).description('是否启用车队平台重置密码功能'),
|
|
82
|
+
changePoint: koishi_1.Schema.boolean().default(false).description('是否启用车队平台积分修改功能'),
|
|
83
|
+
addMember: koishi_1.Schema.boolean().default(false).description('是否启用车队平台新增成员功能'),
|
|
80
84
|
tmpActivityService: koishi_1.Schema.boolean().default(false).description('是否启用车队活动查询'),
|
|
81
85
|
tmpVersionCheck: koishi_1.Schema.boolean().default(false).description('是否启用TMP版本更新查询'),
|
|
82
86
|
tmpQueryGameTime: koishi_1.Schema.boolean().default(true).description('是否启用游戏时长查询功能'),
|
|
@@ -122,26 +126,34 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
122
126
|
koishi_1.Schema.const("v1").description("车队平台V1.0"),
|
|
123
127
|
koishi_1.Schema.const("v2").description("车队平台V2.0")
|
|
124
128
|
]).default("v1").description("车队平台版本"),
|
|
125
|
-
url: koishi_1.Schema.string().description("API
|
|
126
|
-
token: koishi_1.Schema.string().description("API
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}).description("
|
|
129
|
+
url: koishi_1.Schema.string().description("API服务器地址(不含协议)"),
|
|
130
|
+
token: koishi_1.Schema.string().description("API认证令牌"),
|
|
131
|
+
logOutput: koishi_1.Schema.boolean().description("是否输出日志").default(true),
|
|
132
|
+
mailEnabled: koishi_1.Schema.boolean().description("【V2.0】重置密码后发送邮件(需启用 adapter-mail)").default(false),
|
|
133
|
+
mailTo: koishi_1.Schema.string().description("【V2.0】重置密码通知邮箱(自定义邮箱地址)").default(""),
|
|
134
|
+
mailSubject: koishi_1.Schema.string().description("【V2.0】重置密码邮件标题").default("重置密码通知"),
|
|
135
|
+
mailTemplate: koishi_1.Schema.string().description("【V2.0】重置密码邮件内容模板,支持变量:{uid} {qq} {password} {psw}").default("我们已将您平台的密码进行重置,您的账号:{uid},新的密码为:{psw} 请妥善保管好您的密码,以防泄露"),
|
|
136
|
+
mailFromName: koishi_1.Schema.string().description("【V2.0】重置密码邮件发件人名字(覆盖 adapter-mail 的 name)").default(""),
|
|
137
|
+
}).description("车队平台配置")
|
|
134
138
|
}).description("车队平台配置"),
|
|
135
139
|
resetPassword: koishi_1.Schema.object({
|
|
136
140
|
settings: koishi_1.Schema.object({
|
|
137
141
|
adminUsers: koishi_1.Schema.array(koishi_1.Schema.string()).description("管理员用户ID(拥有重置任意teamId权限)").default([])
|
|
138
142
|
})
|
|
139
143
|
}).description("重置密码功能配置"),
|
|
144
|
+
changePoint: koishi_1.Schema.object({
|
|
145
|
+
settings: koishi_1.Schema.object({
|
|
146
|
+
adminUsers: koishi_1.Schema.array(koishi_1.Schema.string()).description("管理员用户ID(拥有积分修改权限)").default([])
|
|
147
|
+
})
|
|
148
|
+
}).description("积分修改功能配置"),
|
|
149
|
+
addMember: koishi_1.Schema.object({
|
|
150
|
+
settings: koishi_1.Schema.object({
|
|
151
|
+
adminUsers: koishi_1.Schema.array(koishi_1.Schema.string()).description("管理员用户ID(拥有新增成员权限)").default([]),
|
|
152
|
+
teamNumberGenerateEnable: koishi_1.Schema.boolean().default(true).description("自动生成车队编号(关闭后需手动输入车队编号)")
|
|
153
|
+
})
|
|
154
|
+
}).description("新增成员功能配置"),
|
|
140
155
|
tmpActivityService: koishi_1.Schema.object({
|
|
141
156
|
api: koishi_1.Schema.object({
|
|
142
|
-
useHttps: koishi_1.Schema.boolean().description("使用HTTPS协议").default(true),
|
|
143
|
-
url: koishi_1.Schema.string().description("车队平台URL(不包含协议)").default(""),
|
|
144
|
-
token: koishi_1.Schema.string().description("车队平台TOKEN").default(""),
|
|
145
157
|
vtcId: koishi_1.Schema.string().description("VTC ID(用于TMP API)").default("")
|
|
146
158
|
}).description("API配置"),
|
|
147
159
|
admin: koishi_1.Schema.object({
|
|
@@ -338,6 +350,22 @@ function registerBaseCommands(ctx, cfg) {
|
|
|
338
350
|
.action(async ({ session }, targetQQ) => await commands.queryPoint(ctx, cfg, session, targetQQ));
|
|
339
351
|
}
|
|
340
352
|
|
|
353
|
+
if (cfg.commands?.changePoint) {
|
|
354
|
+
ctx.command(`积分修改 <target:string> <changeType:string> <quantity:string> <reason:string>`, "修改欧卡车队平台积分")
|
|
355
|
+
.usage("管理员专用。changeType: 增加 或 减少。target可输入车队编号(V1)或UID(V2)或@群成员。reason为备注原因(V1必填)")
|
|
356
|
+
.example(`积分修改 @某人 增加 10 活动奖励 - 增加@某人10积分,备注活动奖励`)
|
|
357
|
+
.example(`积分修改 5 减少 5 违规扣分 - 减少车队编号5的用户5积分,备注违规扣分`)
|
|
358
|
+
.action(async ({ session }, target, changeType, quantity, reason) => await commands.changePoint(ctx, cfg, session, target, changeType, quantity, reason));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (cfg.commands?.addMember) {
|
|
362
|
+
ctx.command(`新增成员 <tmpId:string> <qq:string> [teamNumber:string]`, "新增车队平台成员")
|
|
363
|
+
.usage("管理员专用。qq可输入QQ号或@群成员。若关闭自动生成车队编号则需手动输入teamNumber")
|
|
364
|
+
.example(`新增成员 12345 79887143 - 自动生成车队编号`)
|
|
365
|
+
.example(`新增成员 12345 @某人 5 - 手动指定车队编号为5`)
|
|
366
|
+
.action(async ({ session }, tmpId, qq, teamNumber) => await commands.addMember(ctx, cfg, session, tmpId, qq, teamNumber));
|
|
367
|
+
}
|
|
368
|
+
|
|
341
369
|
ctx.command('规则查询')
|
|
342
370
|
.action(async () => 'TruckersMP官方规则链接:https://truckersmp.com/knowledge-base/article/746');
|
|
343
371
|
}
|