koishi-plugin-ets2-tools-tmp 1.3.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/evmOpenApi.d.ts +6 -0
- package/lib/api/evmOpenApi.js +23 -1
- package/lib/command/ets-app/queryPoint.js +5 -2
- package/lib/command/ets-app/resetPassword.js +162 -3
- package/lib/command/tmpBind.js +3 -2
- package/lib/command/tmpFootprint.d.ts +3 -0
- package/lib/command/tmpFootprint.js +68 -0
- package/lib/command/tmpPosition.js +19 -3
- package/lib/command/tmpQuery/tmpQueryText.js +5 -1
- package/lib/index.js +55 -9
- package/lib/resource/footprint.html +241 -0
- package/lib/resource/package/ets-map.js +63 -0
- package/lib/util/baiduTranslate.js +5 -5
- package/lib/util/constant.d.ts +7 -0
- package/lib/util/constant.js +14 -1
- package/package.json +1 -1
package/lib/api/evmOpenApi.d.ts
CHANGED
|
@@ -31,3 +31,9 @@ export function dlcList(http: any, type: any): Promise<{
|
|
|
31
31
|
export function mileageRankingList(http: any, rankingType: any, tmpId: any): Promise<{
|
|
32
32
|
error: boolean;
|
|
33
33
|
}>;
|
|
34
|
+
/**
|
|
35
|
+
* 查询玩家历史数据
|
|
36
|
+
*/
|
|
37
|
+
export function mapPlayerHistory(http: any, tmpId: any, serverId: any, startTime: any, endTime: any): Promise<{
|
|
38
|
+
error: boolean;
|
|
39
|
+
}>;
|
package/lib/api/evmOpenApi.js
CHANGED
|
@@ -50,7 +50,7 @@ module.exports = {
|
|
|
50
50
|
async playerInfo(http, tmpId) {
|
|
51
51
|
let result = null;
|
|
52
52
|
try {
|
|
53
|
-
result = await http.get(
|
|
53
|
+
result = await http.get(`${BASE_API}/player/info?tmpId=${tmpId}`);
|
|
54
54
|
}
|
|
55
55
|
catch {
|
|
56
56
|
return {
|
|
@@ -110,5 +110,27 @@ module.exports = {
|
|
|
110
110
|
data.data = result.data;
|
|
111
111
|
}
|
|
112
112
|
return data;
|
|
113
|
+
},
|
|
114
|
+
/**
|
|
115
|
+
* 查询玩家历史数据
|
|
116
|
+
*/
|
|
117
|
+
async mapPlayerHistory(http, tmpId, serverId, startTime, endTime) {
|
|
118
|
+
let result = null;
|
|
119
|
+
try {
|
|
120
|
+
result = await http.get(`${BASE_API}/map/playerHistory?tmpId=${tmpId}&serverId=${serverId}&startTime=${startTime}&endTime=${endTime}`);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return {
|
|
124
|
+
error: true
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// 拼接返回数据
|
|
128
|
+
let data = {
|
|
129
|
+
error: result.code !== 200
|
|
130
|
+
};
|
|
131
|
+
if (!data.error) {
|
|
132
|
+
data.data = result.data;
|
|
133
|
+
}
|
|
134
|
+
return data;
|
|
113
135
|
}
|
|
114
136
|
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
module.exports = async (ctx, cfg, session, targetQQ) => {
|
|
2
|
-
const { url, token, logOutput } = cfg.mainSettings?.settings || {};
|
|
2
|
+
const { url, token, logOutput, platformVersion } = cfg.mainSettings?.settings || {};
|
|
3
|
+
if ((platformVersion || "v1").toLowerCase() === "v2") {
|
|
4
|
+
return "车队平台V2.0暂不支持积分查询";
|
|
5
|
+
}
|
|
3
6
|
let queryQQ = targetQQ;
|
|
4
7
|
if (!queryQQ) {
|
|
5
8
|
queryQQ = session.userId;
|
|
@@ -71,4 +74,4 @@ module.exports = async (ctx, cfg, session, targetQQ) => {
|
|
|
71
74
|
return "系统错误,请稍后重试";
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
|
-
};
|
|
77
|
+
};
|
|
@@ -1,12 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const { h } = require("koishi");
|
|
2
|
+
|
|
3
|
+
module.exports = async (ctx, cfg, session, targetTeamId, password) => {
|
|
4
|
+
const { url, token, logOutput, platformVersion, mailEnabled, mailTo, mailSubject, mailTemplate, mailFromName } = cfg.mainSettings?.settings || {};
|
|
3
5
|
const { adminUsers } = cfg.resetPassword?.settings || {};
|
|
4
6
|
const currentUserQQ = session.userId;
|
|
5
7
|
const isAdmin = adminUsers.includes(currentUserQQ);
|
|
6
8
|
const isPrivateChat = session.channelId === `private:${currentUserQQ}`;
|
|
9
|
+
const platform = (platformVersion || "v1").toLowerCase();
|
|
7
10
|
|
|
8
11
|
// 日志工具函数
|
|
9
12
|
const log = (msg) => logOutput && ctx.logger.info(msg);
|
|
13
|
+
|
|
14
|
+
const normalizeBaseUrl = (baseUrl, fallback) => {
|
|
15
|
+
if (!baseUrl) return fallback;
|
|
16
|
+
if (baseUrl.startsWith("http://") || baseUrl.startsWith("https://")) return baseUrl;
|
|
17
|
+
return `https://${baseUrl}`;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const parseAtQQ = (raw) => {
|
|
21
|
+
if (!raw?.startsWith("<at ")) return raw;
|
|
22
|
+
const idStart = raw.indexOf('id="');
|
|
23
|
+
if (idStart === -1) return "";
|
|
24
|
+
const idEnd = raw.indexOf('"', idStart + 4);
|
|
25
|
+
if (idEnd === -1) return "";
|
|
26
|
+
return raw.substring(idStart + 4, idEnd);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const normalizeEmail = (value) => {
|
|
30
|
+
if (!value) return "";
|
|
31
|
+
const trimmed = value.trim();
|
|
32
|
+
const bracketMatch = trimmed.match(/<([^>]+)>/);
|
|
33
|
+
const raw = bracketMatch ? bracketMatch[1] : trimmed;
|
|
34
|
+
return raw.replace(/[\s\u200B-\u200D\uFEFF]+/g, "");
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const sendMail = async (targetMail, subject, content) => {
|
|
38
|
+
const mailBot = ctx.bots.find((bot) => bot.platform === "mail");
|
|
39
|
+
if (!mailBot) {
|
|
40
|
+
log("[V2] 未发现可用的 mail 适配器,无法发送邮件");
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const normalizedTarget = targetMail && targetMail.includes(":")
|
|
44
|
+
? targetMail
|
|
45
|
+
: `private:${targetMail}`;
|
|
46
|
+
const fromAddress = mailBot.user?.id || mailBot.selfId || "mail";
|
|
47
|
+
log(`[V2] 邮件发送状态: 由 ${fromAddress} 发送到 ${targetMail} (channelId=${normalizedTarget}, subject=${subject || ""})`);
|
|
48
|
+
log(`[V2] 邮件发送详情: mailFromName=${mailFromName || ""}, rawContentLength=${(content || "").length}`);
|
|
49
|
+
try {
|
|
50
|
+
const rawMessage = content || "";
|
|
51
|
+
const message = h("message", { subject, fromName: mailFromName || undefined }, rawMessage);
|
|
52
|
+
log(`[V2] 邮件发送详情: rawMessage=${rawMessage}`);
|
|
53
|
+
log(`[V2] 邮件发送详情: messageElement=${JSON.stringify(message)}`);
|
|
54
|
+
await mailBot.sendMessage(normalizedTarget, message);
|
|
55
|
+
log(`[V2] 邮件发送成功: 由 ${fromAddress} 发送到 ${targetMail} (channelId=${normalizedTarget})`);
|
|
56
|
+
return true;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
log(`[V2] 邮件发送失败: 由 ${fromAddress} 发送到 ${targetMail} (channelId=${normalizedTarget}), 错误: ${error.message}`);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const randomChar = (chars) => chars[Math.floor(Math.random() * chars.length)];
|
|
64
|
+
const generatePassword = () => {
|
|
65
|
+
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
66
|
+
const digits = "0123456789";
|
|
67
|
+
const all = letters + digits;
|
|
68
|
+
const chars = [randomChar(letters), randomChar(digits)];
|
|
69
|
+
for (let i = 0; i < 8; i++) {
|
|
70
|
+
chars.push(randomChar(all));
|
|
71
|
+
}
|
|
72
|
+
for (let i = chars.length - 1; i > 0; i--) {
|
|
73
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
74
|
+
[chars[i], chars[j]] = [chars[j], chars[i]];
|
|
75
|
+
}
|
|
76
|
+
return chars.join("");
|
|
77
|
+
};
|
|
10
78
|
|
|
11
79
|
// 通用HTTP请求函数
|
|
12
80
|
const fetchData = async (apiUrl) => {
|
|
@@ -46,6 +114,97 @@ module.exports = async (ctx, cfg, session, targetTeamId) => {
|
|
|
46
114
|
};
|
|
47
115
|
|
|
48
116
|
try {
|
|
117
|
+
if (platform === "v2") {
|
|
118
|
+
if (!token) return "未配置车队平台V2.0 API认证令牌";
|
|
119
|
+
const baseUrl = normalizeBaseUrl(url, "https://open.vtcm.link");
|
|
120
|
+
|
|
121
|
+
if (!isAdmin && !isPrivateChat) {
|
|
122
|
+
return "您没有权限重置其他用户的密码,请联系管理员重置,或私聊机器人重置";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let targetQQ = "";
|
|
126
|
+
let memberUid = "";
|
|
127
|
+
let userEmail = "";
|
|
128
|
+
const rawTarget = targetTeamId ? targetTeamId.trim() : "";
|
|
129
|
+
const uidMatch = rawTarget.match(/^uid\s*=\s*(\d+)$/i);
|
|
130
|
+
|
|
131
|
+
if (uidMatch) {
|
|
132
|
+
if (!isAdmin) return "仅管理员可使用 uid 参数重置他人密码";
|
|
133
|
+
memberUid = uidMatch[1];
|
|
134
|
+
log(`[V2] 使用 UID 方式重置密码,uid=${memberUid}`);
|
|
135
|
+
const memberUrl = `${baseUrl}/members/get?uid=${encodeURIComponent(memberUid)}`;
|
|
136
|
+
log(`获取成员信息: ${memberUrl}`);
|
|
137
|
+
const memberResponse = await ctx.http.get(memberUrl, { headers: { token } });
|
|
138
|
+
log(`成员信息响应: ${JSON.stringify(memberResponse)}`);
|
|
139
|
+
if (memberResponse.code !== 200 || !memberResponse.data?.uid) {
|
|
140
|
+
return memberResponse.msg || "未找到用户信息";
|
|
141
|
+
}
|
|
142
|
+
targetQQ = memberResponse.data.qq || targetQQ;
|
|
143
|
+
const rawEmail = memberResponse.data.email || "";
|
|
144
|
+
log(`[V2] 成员邮箱原始值: ${rawEmail}`);
|
|
145
|
+
userEmail = normalizeEmail(rawEmail);
|
|
146
|
+
log(`[V2] 成员邮箱规范化后: ${userEmail}`);
|
|
147
|
+
} else {
|
|
148
|
+
targetQQ = rawTarget ? parseAtQQ(rawTarget) : currentUserQQ;
|
|
149
|
+
log(`[V2] 使用 QQ 方式重置密码,rawTarget=${rawTarget}, parsedQQ=${targetQQ}`);
|
|
150
|
+
if (!targetQQ) return "获取QQ号错误,请使用QQ号重置";
|
|
151
|
+
if (!/^\d+$/.test(targetQQ)) return "QQ号格式不正确,请输入纯数字QQ号";
|
|
152
|
+
if (!isAdmin && targetQQ !== currentUserQQ) {
|
|
153
|
+
return "您没有权限重置其他成员的密码,请联系管理员";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const memberUrl = `${baseUrl}/members/get?qq=${encodeURIComponent(targetQQ)}`;
|
|
157
|
+
log(`获取成员信息: ${memberUrl}`);
|
|
158
|
+
const memberResponse = await ctx.http.get(memberUrl, { headers: { token } });
|
|
159
|
+
log(`成员信息响应: ${JSON.stringify(memberResponse)}`);
|
|
160
|
+
|
|
161
|
+
if (memberResponse.code !== 200 || !memberResponse.data?.uid) {
|
|
162
|
+
return memberResponse.msg || "未找到用户信息";
|
|
163
|
+
}
|
|
164
|
+
memberUid = memberResponse.data.uid;
|
|
165
|
+
targetQQ = memberResponse.data.qq || targetQQ;
|
|
166
|
+
const rawEmail = memberResponse.data.email || "";
|
|
167
|
+
log(`[V2] 成员邮箱原始值: ${rawEmail}`);
|
|
168
|
+
userEmail = normalizeEmail(rawEmail);
|
|
169
|
+
log(`[V2] 成员邮箱规范化后: ${userEmail}`);
|
|
170
|
+
}
|
|
171
|
+
if (userEmail) {
|
|
172
|
+
log(`[V2] 成员邮箱: ${userEmail}`);
|
|
173
|
+
} else {
|
|
174
|
+
log("[V2] 成员邮箱为空,可能无法发送到用户邮箱");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const newPassword = password?.trim() || generatePassword();
|
|
178
|
+
log(`[V2] 即将重置密码,uid=${memberUid}, newPassword=${newPassword}`);
|
|
179
|
+
const resetUrl = `${baseUrl}/members/${memberUid}/password`;
|
|
180
|
+
log(`重置密码请求: ${resetUrl}`);
|
|
181
|
+
const resetResponse = await ctx.http.post(resetUrl, { password: newPassword }, { headers: { token } });
|
|
182
|
+
log(`重置密码响应: ${JSON.stringify(resetResponse)}`);
|
|
183
|
+
|
|
184
|
+
if (resetResponse.code !== 200) {
|
|
185
|
+
return resetResponse.msg || "未知错误";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const effectiveMailTo = normalizeEmail(userEmail || mailTo);
|
|
189
|
+
if (mailEnabled && effectiveMailTo) {
|
|
190
|
+
const template = mailTemplate || "车队平台重置密码成功,uid={uid},qq={qq},新密码:{password}";
|
|
191
|
+
const body = template
|
|
192
|
+
.replace(/{uid}/g, memberUid || "")
|
|
193
|
+
.replace(/{qq}/g, targetQQ || "")
|
|
194
|
+
.replace(/{password}/g, newPassword)
|
|
195
|
+
.replace(/{psw}/g, newPassword);
|
|
196
|
+
const subject = mailSubject || "重置密码通知";
|
|
197
|
+
await sendMail(effectiveMailTo, subject, body);
|
|
198
|
+
} else if (mailEnabled) {
|
|
199
|
+
log("[V2] 已开启邮件发送,但未找到用户邮箱或 mailTo");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const isAdminOp = isAdmin && (targetQQ || memberUid);
|
|
203
|
+
return isAdminOp
|
|
204
|
+
? `管理员操作:车队编号 ${memberUid} 的密码重置成功!新密码已发送到用户邮箱。`
|
|
205
|
+
: "密码重置成功!新密码已发送到您的邮箱,请查收。";
|
|
206
|
+
}
|
|
207
|
+
|
|
49
208
|
let teamId, targetQQ;
|
|
50
209
|
|
|
51
210
|
// 处理@提及的QQ号
|
|
@@ -108,4 +267,4 @@ module.exports = async (ctx, cfg, session, targetTeamId) => {
|
|
|
108
267
|
return error.message.includes("未找到") ? error.message : "系统错误,请稍后重试";
|
|
109
268
|
}
|
|
110
269
|
}
|
|
111
|
-
};
|
|
270
|
+
};
|
package/lib/command/tmpBind.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const guildBind = require('../database/guildBind');
|
|
2
2
|
const truckersMpApi = require("../api/truckersMpApi");
|
|
3
|
+
const evmOpenApi = require('../api/evmOpenApi');
|
|
3
4
|
/**
|
|
4
5
|
* 绑定 TMP ID
|
|
5
6
|
*/
|
|
@@ -8,11 +9,11 @@ module.exports = async (ctx, cfg, session, tmpId) => {
|
|
|
8
9
|
return `请输入正确的玩家编号`;
|
|
9
10
|
}
|
|
10
11
|
// 查询玩家信息
|
|
11
|
-
let playerInfo = await
|
|
12
|
+
let playerInfo = await evmOpenApi.playerInfo(ctx.http, tmpId);
|
|
12
13
|
if (playerInfo.error) {
|
|
13
14
|
return '绑定失败 (查询玩家信息失败)';
|
|
14
15
|
}
|
|
15
16
|
// 更新数据库
|
|
16
|
-
guildBind.saveOrUpdate(ctx.database, session.platform, session.userId, tmpId);
|
|
17
|
+
guildBind.saveOrUpdate(ctx.database, session.platform, session.userId, playerInfo.data.tmpId);
|
|
17
18
|
return `绑定成功 ( ${playerInfo.data.name} )`;
|
|
18
19
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const { segment } = require('koishi');
|
|
2
|
+
const dayjs = require('dayjs');
|
|
3
|
+
const { resolve } = require('path');
|
|
4
|
+
const common = require('../util/common');
|
|
5
|
+
const { ServerAliasToId, PromodsIds } = require('../util/constant');
|
|
6
|
+
const evmOpenApi = require('../api/evmOpenApi');
|
|
7
|
+
const guildBind = require('../database/guildBind');
|
|
8
|
+
module.exports = async (ctx, session, serverName) => {
|
|
9
|
+
if (!ctx.puppeteer) {
|
|
10
|
+
return '未启用 puppeteer 服务';
|
|
11
|
+
}
|
|
12
|
+
// 转换服务器别名到ID
|
|
13
|
+
let serverId = ServerAliasToId[serverName];
|
|
14
|
+
if (!serverId) {
|
|
15
|
+
return '请输入正确的服务器名称 (s1, s2, p, a)';
|
|
16
|
+
}
|
|
17
|
+
// 尝试从数据库查询绑定信息
|
|
18
|
+
let guildBindData = await guildBind.get(ctx.database, session.platform, session.userId);
|
|
19
|
+
if (!guildBindData) {
|
|
20
|
+
return `请先绑定玩家编号`;
|
|
21
|
+
}
|
|
22
|
+
let tmpId = guildBindData.tmp_id;
|
|
23
|
+
// 查询玩家信息
|
|
24
|
+
let playerInfo = await evmOpenApi.playerInfo(ctx.http, tmpId);
|
|
25
|
+
if (playerInfo.error && playerInfo.code === 10001) {
|
|
26
|
+
return '玩家不存在';
|
|
27
|
+
}
|
|
28
|
+
else if (playerInfo.error) {
|
|
29
|
+
return '查询玩家信息失败,请重试';
|
|
30
|
+
}
|
|
31
|
+
// 查询当日历史位置数据
|
|
32
|
+
const startTime = dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
|
33
|
+
const endTime = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
|
34
|
+
let mapPlayerHistory = await evmOpenApi.mapPlayerHistory(ctx.http, tmpId, serverId, startTime, endTime);
|
|
35
|
+
if (mapPlayerHistory.data.length === 0) {
|
|
36
|
+
return `当日暂无数据`;
|
|
37
|
+
}
|
|
38
|
+
// 拼接数据
|
|
39
|
+
let data = {
|
|
40
|
+
mapType: PromodsIds.indexOf(serverId) !== -1 ? 'promods' : 'ets',
|
|
41
|
+
name: playerInfo.data.name,
|
|
42
|
+
smallAvatarUrl: playerInfo.data.smallAvatarUrl,
|
|
43
|
+
todayMileage: playerInfo.data.todayMileage,
|
|
44
|
+
points: mapPlayerHistory.data
|
|
45
|
+
};
|
|
46
|
+
let page;
|
|
47
|
+
try {
|
|
48
|
+
page = await ctx.puppeteer.page();
|
|
49
|
+
await page.setViewport({ width: 1000, height: 1000 });
|
|
50
|
+
await page.goto(`file:///${resolve(__dirname, '../resource/footprint.html')}`);
|
|
51
|
+
await page.evaluate(`init(${JSON.stringify(data)})`);
|
|
52
|
+
await common.sleep(100);
|
|
53
|
+
await page.waitForNetworkIdle();
|
|
54
|
+
const element = await page.$("#container");
|
|
55
|
+
return (segment.image(await element.screenshot({
|
|
56
|
+
encoding: "binary"
|
|
57
|
+
}), "image/jpg"));
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return '渲染异常,请重试';
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
if (page) {
|
|
64
|
+
await page.close();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return `OK: ` + playerInfo.data.name;
|
|
68
|
+
};
|
|
@@ -12,10 +12,26 @@ const common = require('../util/common')
|
|
|
12
12
|
*/
|
|
13
13
|
module.exports = async (ctx, cfg, session, tmpId) => {
|
|
14
14
|
if (ctx.puppeteer) {
|
|
15
|
-
if (tmpId &&
|
|
16
|
-
|
|
15
|
+
if (tmpId && tmpId.startsWith("<at ")) {
|
|
16
|
+
if (tmpId.startsWith('<at ')) {
|
|
17
|
+
queryQQ = tmpId.replace('<at ', '');
|
|
18
|
+
}
|
|
19
|
+
let id = '';
|
|
20
|
+
const idStart = queryQQ.indexOf('id="');
|
|
21
|
+
if (idStart !== -1) {
|
|
22
|
+
const valueStart = idStart + 4;
|
|
23
|
+
const valueEnd = queryQQ.indexOf('"', valueStart);
|
|
24
|
+
if (valueEnd !== -1) {
|
|
25
|
+
id = queryQQ.substring(valueStart, valueEnd);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
queryQQ = id;
|
|
29
|
+
let guildBindData = await guildBind.get(ctx.database, session.platform, queryQQ);
|
|
30
|
+
if (!guildBindData) {
|
|
31
|
+
return `该用户没有绑定玩家编号`;
|
|
32
|
+
}
|
|
33
|
+
tmpId = guildBindData.tmp_id;
|
|
17
34
|
}
|
|
18
|
-
|
|
19
35
|
// 如果没有传入tmpId,尝试从数据库查询绑定信息
|
|
20
36
|
if (!tmpId) {
|
|
21
37
|
let guildBindData = await guildBind.get(ctx.database, session.platform, session.userId)
|
|
@@ -80,7 +80,10 @@ module.exports = async (ctx, cfg, session, tmpId) => {
|
|
|
80
80
|
}
|
|
81
81
|
message += '\n🚚车队角色: ' + playerInfo.data.vtcRole;
|
|
82
82
|
if (playerInfo.data.vtcId == 86009) {
|
|
83
|
-
const { url, token, logOutput } = cfg.mainSettings?.settings || {};
|
|
83
|
+
const { url, token, logOutput, platformVersion } = cfg.mainSettings?.settings || {};
|
|
84
|
+
if ((platformVersion || "v1").toLowerCase() === "v2") {
|
|
85
|
+
message += "\n车队平台V2.0暂不支持积分查询";
|
|
86
|
+
} else {
|
|
84
87
|
try {
|
|
85
88
|
if (logOutput) {
|
|
86
89
|
ctx.logger.info(`tmpQuery:开始查询TmpID ${tmpId} 的积分`);
|
|
@@ -103,6 +106,7 @@ module.exports = async (ctx, cfg, session, tmpId) => {
|
|
|
103
106
|
message += '查询出错';
|
|
104
107
|
}
|
|
105
108
|
}
|
|
109
|
+
}
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
message += '\n\n🚫是否封禁: ' + (playerInfo.data.isBan ? '是' : '否');
|
package/lib/index.js
CHANGED
|
@@ -18,7 +18,8 @@ const commands = {
|
|
|
18
18
|
tmpMileageRanking: require('./command/tmpMileageRanking'),
|
|
19
19
|
resetPassword: require('./command/ets-app/resetPassword'),
|
|
20
20
|
queryPoint: require('./command/ets-app/queryPoint'),
|
|
21
|
-
tmpVtc: require('./command/tmpVtc')
|
|
21
|
+
tmpVtc: require('./command/tmpVtc'),
|
|
22
|
+
tmpFootprint: require('./command/tmpFootprint')
|
|
22
23
|
};
|
|
23
24
|
const { ActivityService } = require('./command/tmpActivityService');
|
|
24
25
|
|
|
@@ -72,6 +73,7 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
72
73
|
tmpDlcMap: koishi_1.Schema.boolean().default(true).description('是否启用DLC地图查询'),
|
|
73
74
|
tmpMileageRanking: koishi_1.Schema.boolean().default(true).description('是否启用里程排行榜'),
|
|
74
75
|
tmpVtc: koishi_1.Schema.boolean().default(true).description('是否启用VTC查询'),
|
|
76
|
+
tmpFootprint: koishi_1.Schema.boolean().default(true).description('是否启用今日足迹'),
|
|
75
77
|
mainSettings: koishi_1.Schema.boolean().default(false).description('是否启用车队平台积分查询功能'),
|
|
76
78
|
resetPassword: koishi_1.Schema.boolean().default(false).description('是否启用车队平台重置密码功能'),
|
|
77
79
|
tmpActivityService: koishi_1.Schema.boolean().default(false).description('是否启用车队活动查询')
|
|
@@ -97,10 +99,19 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
97
99
|
}).description('路况查询配置'),
|
|
98
100
|
mainSettings: koishi_1.Schema.object({
|
|
99
101
|
settings: koishi_1.Schema.object({
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
platformVersion: koishi_1.Schema.union([
|
|
103
|
+
koishi_1.Schema.const("v1").description("车队平台V1.0"),
|
|
104
|
+
koishi_1.Schema.const("v2").description("车队平台V2.0")
|
|
105
|
+
]).default("v1").description("车队平台版本"),
|
|
106
|
+
url: koishi_1.Schema.string().description("API服务器地址(V1平台地址 / V2 OpenAPI地址,不含协议)"),
|
|
107
|
+
token: koishi_1.Schema.string().description("API认证令牌(V1平台Token / V2 OpenAPI Token)"),
|
|
108
|
+
mailEnabled: koishi_1.Schema.boolean().description("V2.0重置密码后发送邮件(需启用 adapter-mail)").default(false),
|
|
109
|
+
mailTo: koishi_1.Schema.string().description("V2.0重置密码通知邮箱(自定义邮箱地址)").default(""),
|
|
110
|
+
mailSubject: koishi_1.Schema.string().description("V2.0重置密码邮件标题").default("重置密码通知"),
|
|
111
|
+
mailTemplate: koishi_1.Schema.string().description("V2.0重置密码邮件内容模板,支持变量:{uid} {qq} {password} {psw}").default("我们已将您平台的密码进行重置,您的账号:{uid},新的密码为:{psw} 请妥善保管好您的密码,以防泄露"),
|
|
112
|
+
mailFromName: koishi_1.Schema.string().description("V2.0重置密码邮件发件人名字(覆盖 adapter-mail 的 name)").default(""),
|
|
102
113
|
logOutput: koishi_1.Schema.boolean().description("是否输出日志").default(true)
|
|
103
|
-
})
|
|
114
|
+
}).description("V1/V2通用配置")
|
|
104
115
|
}).description("车队平台配置"),
|
|
105
116
|
resetPassword: koishi_1.Schema.object({
|
|
106
117
|
settings: koishi_1.Schema.object({
|
|
@@ -160,6 +171,31 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
160
171
|
}).description('功能配置')
|
|
161
172
|
]);
|
|
162
173
|
|
|
174
|
+
|
|
175
|
+
function logDisabledCommands(ctx, cfg) {
|
|
176
|
+
const commandFlags = cfg.commands || {};
|
|
177
|
+
const disabled = [];
|
|
178
|
+
const commandList = [
|
|
179
|
+
{ key: 'tmpQuery', label: '??/??' },
|
|
180
|
+
{ key: 'tmpServer', label: '?????/?????' },
|
|
181
|
+
{ key: 'tmpTraffic', label: '??' },
|
|
182
|
+
{ key: 'tmpPosition', label: '??' },
|
|
183
|
+
{ key: 'tmpVersion', label: 'tmp??' },
|
|
184
|
+
{ key: 'tmpDlcMap', label: '??dlc??' },
|
|
185
|
+
{ key: 'tmpMileageRanking', label: '?????/???????' },
|
|
186
|
+
{ key: 'tmpVtc', label: 'vtc??' },
|
|
187
|
+
{ key: 'tmpFootprint', label: '???' },
|
|
188
|
+
{ key: 'resetPassword', label: '????' },
|
|
189
|
+
{ key: 'mainSettings', label: '????' }
|
|
190
|
+
];
|
|
191
|
+
for (const item of commandList) {
|
|
192
|
+
if (commandFlags[item.key] === false) disabled.push(item.label);
|
|
193
|
+
}
|
|
194
|
+
if (disabled.length) {
|
|
195
|
+
ctx.logger.info(`[TMP-BOT] ??????????? commands ?????${disabled.join('?')}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
163
199
|
function registerBaseCommands(ctx, cfg) {
|
|
164
200
|
if (cfg.commands?.tmpQuery) {
|
|
165
201
|
ctx.command('查询 <tmpId>')
|
|
@@ -224,12 +260,20 @@ function registerBaseCommands(ctx, cfg) {
|
|
|
224
260
|
.action(async ({ session }, vtcid) => await commands.tmpVtc(ctx, cfg, session, vtcid));
|
|
225
261
|
}
|
|
226
262
|
|
|
263
|
+
if (cfg.commands?.tmpFootprint) {
|
|
264
|
+
ctx.command('今日足迹 <serverName>')
|
|
265
|
+
.usage("查询今日足迹")
|
|
266
|
+
.example("今日足迹 s1")
|
|
267
|
+
.action(async ({ session }, serverName) => await commands.tmpFootprint(ctx, session, serverName));
|
|
268
|
+
}
|
|
269
|
+
|
|
227
270
|
if (cfg.commands?.resetPassword) {
|
|
228
|
-
ctx.command(`重置密码 [targetTeamId:string]`, "重置欧卡车队平台密码")
|
|
229
|
-
.usage("
|
|
271
|
+
ctx.command(`重置密码 [targetTeamId:string] [password:string]`, "重置欧卡车队平台密码")
|
|
272
|
+
.usage("V1.0使用teamId,V2.0使用QQ号;管理员可用 uid=12345;V2.0可指定新密码")
|
|
230
273
|
.example(`重置密码 - 重置自己的密码`)
|
|
231
|
-
.example(`重置密码 - 管理员重置指定teamId的密码`)
|
|
232
|
-
.
|
|
274
|
+
.example(`重置密码 789 - 管理员重置指定teamId的密码`)
|
|
275
|
+
.example(`重置密码 123456 Abc123def4 - V2.0指定QQ与新密码`)
|
|
276
|
+
.action(async ({ session }, targetTeamId, password) => await commands.resetPassword(ctx, cfg, session, targetTeamId, password));
|
|
233
277
|
}
|
|
234
278
|
|
|
235
279
|
if (cfg.commands?.mainSettings) {
|
|
@@ -257,6 +301,8 @@ function apply(ctx, cfg) {
|
|
|
257
301
|
|
|
258
302
|
registerBaseCommands(ctx, cfg);
|
|
259
303
|
|
|
304
|
+
logDisabledCommands(ctx, cfg);
|
|
305
|
+
|
|
260
306
|
if (cfg.commands?.tmpActivityService) {
|
|
261
307
|
const activityConfig = {
|
|
262
308
|
...cfg.tmpActivityService,
|
|
@@ -274,4 +320,4 @@ __name(apply, "apply");
|
|
|
274
320
|
Config,
|
|
275
321
|
apply,
|
|
276
322
|
name
|
|
277
|
-
});
|
|
323
|
+
});
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>玩家足迹展示</title>
|
|
6
|
+
<link href="./package/leaflet/leaflet.min.css" rel="stylesheet">
|
|
7
|
+
<script src="./package/leaflet/leaflet.min.js"></script>
|
|
8
|
+
<style>
|
|
9
|
+
@font-face {
|
|
10
|
+
font-family: 'segui-emj';
|
|
11
|
+
src: url('./package/SEGUIEMJ.TTF');
|
|
12
|
+
font-weight: normal;
|
|
13
|
+
font-style: normal;
|
|
14
|
+
}
|
|
15
|
+
body, html {
|
|
16
|
+
margin: 0;
|
|
17
|
+
padding: 0;
|
|
18
|
+
font-family: 'segui-emj', "微软雅黑", serif;
|
|
19
|
+
}
|
|
20
|
+
#container {
|
|
21
|
+
width: 800px;
|
|
22
|
+
height: 600px;
|
|
23
|
+
background: #1a1a1a;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
position: relative;
|
|
26
|
+
}
|
|
27
|
+
.status-bar {
|
|
28
|
+
position: absolute;
|
|
29
|
+
bottom: 0;
|
|
30
|
+
left: 0;
|
|
31
|
+
right: 0;
|
|
32
|
+
height: 32px;
|
|
33
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
34
|
+
backdrop-filter: blur(10px);
|
|
35
|
+
-webkit-backdrop-filter: blur(10px);
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
padding: 0 12px;
|
|
39
|
+
box-shadow: 0 -2px 10px rgba(0, 0, 0, .5);
|
|
40
|
+
z-index: 1001;
|
|
41
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
42
|
+
font-size: 13px;
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
}
|
|
45
|
+
.status-bar .avatar {
|
|
46
|
+
width: 20px;
|
|
47
|
+
height: 20px;
|
|
48
|
+
border-radius: 4px;
|
|
49
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
50
|
+
margin-right: 8px;
|
|
51
|
+
}
|
|
52
|
+
.status-bar .info {
|
|
53
|
+
flex: 1;
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
}
|
|
57
|
+
.status-bar .info .name {
|
|
58
|
+
color: #b0c7ff;
|
|
59
|
+
font-weight: 600;
|
|
60
|
+
}
|
|
61
|
+
.status-bar .stats {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
color: #aaaaaa;
|
|
65
|
+
}
|
|
66
|
+
.status-bar .stats .label {
|
|
67
|
+
margin-right: 6px;
|
|
68
|
+
}
|
|
69
|
+
.status-bar .stats .value {
|
|
70
|
+
font-weight: bold;
|
|
71
|
+
color: #54d354;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#map-box {
|
|
75
|
+
width: 100%;
|
|
76
|
+
height: 100%;
|
|
77
|
+
}
|
|
78
|
+
#map {
|
|
79
|
+
width: 100%;
|
|
80
|
+
height: 100%;
|
|
81
|
+
background-color: rgba(0, 0, 0, 0.25);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.marker-label {
|
|
85
|
+
background: rgba(0, 0, 0, 0.6);
|
|
86
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
87
|
+
border-radius: 4px;
|
|
88
|
+
color: #fff;
|
|
89
|
+
padding: 2px 6px;
|
|
90
|
+
font-size: 12px;
|
|
91
|
+
white-space: nowrap;
|
|
92
|
+
}
|
|
93
|
+
</style>
|
|
94
|
+
</head>
|
|
95
|
+
<body>
|
|
96
|
+
<div id="container">
|
|
97
|
+
<div id="map-box">
|
|
98
|
+
<div id="map"></div>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="status-bar">
|
|
101
|
+
<img class="avatar" id="avatar" src="https://static.truckersmp.com/avatarsN/small/defaultavatar.png" alt="avatar"/>
|
|
102
|
+
<div class="info">
|
|
103
|
+
<div class="name" id="username">测试玩家</div>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="stats" id="stats-box">
|
|
106
|
+
<span class="label">今日里程</span>
|
|
107
|
+
<span class="value" id="distance">0.0 km</span>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<script src="./package/ets-map.js"></script>
|
|
113
|
+
<script>
|
|
114
|
+
function calculateDistance(p1, p2) {
|
|
115
|
+
return Math.sqrt(Math.pow(p1.axisX - p2.axisX, 2) + Math.pow(p1.axisY - p2.axisY, 2));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function parseTime(timeStr) {
|
|
119
|
+
return new Date(timeStr.replace(/-/g, '/')).getTime();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function init(data) {
|
|
123
|
+
if (!data) return;
|
|
124
|
+
|
|
125
|
+
document.getElementById('username').innerText = (data.name || 'Unknown') + ' 的今日行驶足迹';
|
|
126
|
+
if (data.smallAvatarUrl) {
|
|
127
|
+
document.getElementById('avatar').src = data.smallAvatarUrl;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const points = (data.points || []).filter(p => !(p.axisX === 0 && p.axisY === 0 && p.heading === 0));
|
|
131
|
+
// 使用传入的今日里程数据(米转千米)
|
|
132
|
+
const mileage = data.todayMileage || 0;
|
|
133
|
+
const km = (mileage / 1000).toFixed(1);
|
|
134
|
+
document.getElementById('distance').innerText = `${km} km`;
|
|
135
|
+
|
|
136
|
+
if (points.length === 0) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const lines = [];
|
|
141
|
+
let currentLine = [];
|
|
142
|
+
|
|
143
|
+
if (points.length > 0) {
|
|
144
|
+
let first = points[0];
|
|
145
|
+
currentLine.push({ x: first.axisX, y: first.axisY });
|
|
146
|
+
|
|
147
|
+
for (let i = 1; i < points.length; i++) {
|
|
148
|
+
const prev = points[i - 1];
|
|
149
|
+
const curr = points[i];
|
|
150
|
+
|
|
151
|
+
let dist = calculateDistance(prev, curr);
|
|
152
|
+
dist = dist * 19;
|
|
153
|
+
const isDistJump = dist > 50000; // > 50km
|
|
154
|
+
|
|
155
|
+
let timeDiff = 0;
|
|
156
|
+
try {
|
|
157
|
+
timeDiff = (parseTime(curr.updateTime) - parseTime(prev.updateTime)) / 1000;
|
|
158
|
+
} catch (e) { }
|
|
159
|
+
|
|
160
|
+
const isTimeJump = timeDiff > 90;
|
|
161
|
+
const isServerJump = prev.serverId !== curr.serverId;
|
|
162
|
+
|
|
163
|
+
if (isDistJump || isTimeJump || isServerJump) {
|
|
164
|
+
if (currentLine.length > 0) lines.push(currentLine);
|
|
165
|
+
currentLine = [];
|
|
166
|
+
}
|
|
167
|
+
currentLine.push({ x: curr.axisX, y: curr.axisY });
|
|
168
|
+
}
|
|
169
|
+
if (currentLine.length > 0) lines.push(currentLine);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
render(lines, data.mapType);
|
|
173
|
+
}
|
|
174
|
+
function render(lines, mapType) {
|
|
175
|
+
const config = mapConfig[mapType];
|
|
176
|
+
|
|
177
|
+
// 边界
|
|
178
|
+
let bounds = L.latLngBounds(
|
|
179
|
+
map.unproject([0, config.bounds.y], config.maxZoom),
|
|
180
|
+
map.unproject([config.bounds.x, 0], config.maxZoom)
|
|
181
|
+
);
|
|
182
|
+
map.setMaxBounds(bounds);
|
|
183
|
+
|
|
184
|
+
// 瓦片
|
|
185
|
+
L.tileLayer(config.tileUrl, {
|
|
186
|
+
minZoom: 0,
|
|
187
|
+
maxZoom: 8,
|
|
188
|
+
minNativeZoom: 2,
|
|
189
|
+
maxNativeZoom: 8,
|
|
190
|
+
tileSize: 512,
|
|
191
|
+
bounds: bounds,
|
|
192
|
+
reuseTiles: true
|
|
193
|
+
}).addTo(map);
|
|
194
|
+
|
|
195
|
+
let allLatlngs = [];
|
|
196
|
+
|
|
197
|
+
lines.forEach(points => {
|
|
198
|
+
if (!points || points.length === 0) return;
|
|
199
|
+
|
|
200
|
+
let latlngs = [];
|
|
201
|
+
points.forEach(xy => {
|
|
202
|
+
let unprojected = map.unproject(config.calculateMapCoordinate(xy.x, xy.y), 8);
|
|
203
|
+
latlngs.push([unprojected.lat, unprojected.lng]);
|
|
204
|
+
allLatlngs.push([unprojected.lat, unprojected.lng]);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// 轨迹线
|
|
208
|
+
L.polyline(latlngs, {
|
|
209
|
+
color: '#3498db',
|
|
210
|
+
weight: 4,
|
|
211
|
+
opacity: 0.8,
|
|
212
|
+
lineJoin: 'round'
|
|
213
|
+
}).addTo(map);
|
|
214
|
+
|
|
215
|
+
// 起点
|
|
216
|
+
L.circleMarker(latlngs[0], {
|
|
217
|
+
radius: 4,
|
|
218
|
+
fillColor: '#2ecc71',
|
|
219
|
+
color: '#fff',
|
|
220
|
+
weight: 2,
|
|
221
|
+
fillOpacity: 1
|
|
222
|
+
}).addTo(map);
|
|
223
|
+
|
|
224
|
+
// 终点
|
|
225
|
+
L.circleMarker(latlngs[latlngs.length - 1], {
|
|
226
|
+
radius: 4,
|
|
227
|
+
fillColor: '#e74c3c',
|
|
228
|
+
color: '#fff',
|
|
229
|
+
weight: 2,
|
|
230
|
+
fillOpacity: 1
|
|
231
|
+
}).addTo(map);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// 自动适应
|
|
235
|
+
if (allLatlngs.length > 0) {
|
|
236
|
+
map.fitBounds(L.latLngBounds(allLatlngs), { padding: [50, 50] });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
</script>
|
|
240
|
+
</body>
|
|
241
|
+
</html>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
let mapConfig = {
|
|
2
|
+
ets: {
|
|
3
|
+
tileUrl: 'https://ets-map.oss-cn-beijing.aliyuncs.com/ets2/05102019/{z}/{x}/{y}.png',
|
|
4
|
+
multipliers: {
|
|
5
|
+
x: 70272,
|
|
6
|
+
y: 76157
|
|
7
|
+
},
|
|
8
|
+
breakpoints: {
|
|
9
|
+
uk: {
|
|
10
|
+
x: -31056.8,
|
|
11
|
+
y: -5832.867
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
bounds: {
|
|
15
|
+
y: 131072,
|
|
16
|
+
x: 131072
|
|
17
|
+
},
|
|
18
|
+
maxZoom: 8,
|
|
19
|
+
minZoom: 2,
|
|
20
|
+
// 游戏地转地图坐标
|
|
21
|
+
calculateMapCoordinate (x, y) {
|
|
22
|
+
return [
|
|
23
|
+
x / 1.609055 + mapConfig.ets.multipliers.x,
|
|
24
|
+
y / 1.609055 + mapConfig.ets.multipliers.y
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
promods: {
|
|
29
|
+
tileUrl: 'https://ets-map.oss-cn-beijing.aliyuncs.com/promods/05102019/{z}/{x}/{y}.png',
|
|
30
|
+
multipliers: {
|
|
31
|
+
x: 51953,
|
|
32
|
+
y: 76024
|
|
33
|
+
},
|
|
34
|
+
breakpoints: {
|
|
35
|
+
uk: {
|
|
36
|
+
x: -31056.8,
|
|
37
|
+
y: -5832.867
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
bounds: {
|
|
41
|
+
y: 131072,
|
|
42
|
+
x: 131072
|
|
43
|
+
},
|
|
44
|
+
maxZoom: 8,
|
|
45
|
+
minZoom: 2,
|
|
46
|
+
// 游戏地转地图坐标
|
|
47
|
+
calculateMapCoordinate (x, y) {
|
|
48
|
+
return [
|
|
49
|
+
x / 2.598541 + mapConfig.promods.multipliers.x,
|
|
50
|
+
y / 2.598541 + mapConfig.promods.multipliers.y
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 定义地图
|
|
57
|
+
let map = L.map('map', {
|
|
58
|
+
attributionControl: false,
|
|
59
|
+
crs: L.CRS.Simple,
|
|
60
|
+
zoomControl: false,
|
|
61
|
+
zoomSnap: 0.2,
|
|
62
|
+
zoomDelta: 0.2
|
|
63
|
+
});
|
|
@@ -3,11 +3,11 @@ const translateCache = require('../database/translateCache');
|
|
|
3
3
|
const TRANSLATE_API = 'https://fanyi-api.baidu.com/api/trans/vip/translate';
|
|
4
4
|
module.exports = async (ctx, cfg, content, cache = true) => {
|
|
5
5
|
// 没有开启百度翻译功能,直接返回文本
|
|
6
|
-
if (!cfg.
|
|
6
|
+
if (!cfg.baiduTranslateEnable) {
|
|
7
7
|
return content;
|
|
8
8
|
}
|
|
9
9
|
// 如果开启了缓存,尝试从缓存中查询翻译
|
|
10
|
-
if (cfg.
|
|
10
|
+
if (cfg.baiduTranslateCacheEnable && cache) {
|
|
11
11
|
let translateContent = await translateCache.getTranslate(ctx.database, md5(content));
|
|
12
12
|
if (translateContent) {
|
|
13
13
|
return translateContent;
|
|
@@ -15,15 +15,15 @@ module.exports = async (ctx, cfg, content, cache = true) => {
|
|
|
15
15
|
}
|
|
16
16
|
// 创建请求秘钥
|
|
17
17
|
let randomInt = Math.floor(Math.random() * 10000);
|
|
18
|
-
let sign = md5(cfg.
|
|
18
|
+
let sign = md5(cfg.baiduTranslateAppId + content + randomInt + cfg.baiduTranslateKey);
|
|
19
19
|
// 调用请求
|
|
20
|
-
let result = await ctx.http.get(`${TRANSLATE_API}?q=${encodeURI(content)}&from=auto&to=zh&appid=${cfg.
|
|
20
|
+
let result = await ctx.http.get(`${TRANSLATE_API}?q=${encodeURI(content)}&from=auto&to=zh&appid=${cfg.baiduTranslateAppId}&salt=${randomInt}&sign=${sign}`);
|
|
21
21
|
// 如果翻译失败,直接返回内容
|
|
22
22
|
if (result.error_code) {
|
|
23
23
|
return content;
|
|
24
24
|
}
|
|
25
25
|
// 如果开启了缓存,将翻译内容缓存到数据库
|
|
26
|
-
if (cfg.
|
|
26
|
+
if (cfg.baiduTranslateCacheEnable && cache) {
|
|
27
27
|
translateCache.save(ctx.database, md5(content), content, result.trans_result[0].dst);
|
|
28
28
|
}
|
|
29
29
|
return result.trans_result[0].dst;
|
package/lib/util/constant.d.ts
CHANGED
package/lib/util/constant.js
CHANGED
|
@@ -12,5 +12,18 @@ module.exports = {
|
|
|
12
12
|
MileageRankingType: {
|
|
13
13
|
total: 1,
|
|
14
14
|
today: 2
|
|
15
|
-
}
|
|
15
|
+
},
|
|
16
|
+
/**
|
|
17
|
+
* 服务器别名映射ID
|
|
18
|
+
*/
|
|
19
|
+
ServerAliasToId: {
|
|
20
|
+
's1': 2,
|
|
21
|
+
's2': 41,
|
|
22
|
+
'p': 50,
|
|
23
|
+
'a': 7
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* P服务器ID集合
|
|
27
|
+
*/
|
|
28
|
+
PromodsIds: [50, 51]
|
|
16
29
|
};
|