koishi-plugin-aktmp 1.0.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.
Files changed (63) hide show
  1. package/lib/api/evmOpenApi.d.ts +45 -0
  2. package/lib/api/evmOpenApi.js +158 -0
  3. package/lib/api/truckersMpApi.d.ts +34 -0
  4. package/lib/api/truckersMpApi.js +110 -0
  5. package/lib/api/truckersMpMapApi.d.ts +6 -0
  6. package/lib/api/truckersMpMapApi.js +25 -0
  7. package/lib/api/truckyAppApi.d.ts +12 -0
  8. package/lib/api/truckyAppApi.js +32 -0
  9. package/lib/command/tmpBind.d.ts +2 -0
  10. package/lib/command/tmpBind.js +19 -0
  11. package/lib/command/tmpDlcMap.d.ts +3 -0
  12. package/lib/command/tmpDlcMap.js +33 -0
  13. package/lib/command/tmpFootprint.d.ts +3 -0
  14. package/lib/command/tmpFootprint.js +82 -0
  15. package/lib/command/tmpMileageRanking.d.ts +3 -0
  16. package/lib/command/tmpMileageRanking.js +55 -0
  17. package/lib/command/tmpPosition.d.ts +3 -0
  18. package/lib/command/tmpPosition.js +95 -0
  19. package/lib/command/tmpQuery.d.ts +2 -0
  20. package/lib/command/tmpQuery.js +148 -0
  21. package/lib/command/tmpServer/tmpServer.d.ts +2 -0
  22. package/lib/command/tmpServer/tmpServer.js +15 -0
  23. package/lib/command/tmpServer/tmpServerImg.d.ts +3 -0
  24. package/lib/command/tmpServer/tmpServerImg.js +35 -0
  25. package/lib/command/tmpServer/tmpServerText.d.ts +2 -0
  26. package/lib/command/tmpServer/tmpServerText.js +40 -0
  27. package/lib/command/tmpTraffic/tmpTraffic.d.ts +2 -0
  28. package/lib/command/tmpTraffic/tmpTraffic.js +15 -0
  29. package/lib/command/tmpTraffic/tmpTrafficMap.d.ts +3 -0
  30. package/lib/command/tmpTraffic/tmpTrafficMap.js +119 -0
  31. package/lib/command/tmpTraffic/tmpTrafficText.d.ts +2 -0
  32. package/lib/command/tmpTraffic/tmpTrafficText.js +122 -0
  33. package/lib/command/tmpUnbind.js +17 -0
  34. package/lib/command/tmpVersion.d.ts +2 -0
  35. package/lib/command/tmpVersion.js +31 -0
  36. package/lib/database/guildBind.d.ts +22 -0
  37. package/lib/database/guildBind.js +55 -0
  38. package/lib/database/model.d.ts +2 -0
  39. package/lib/database/model.js +65 -0
  40. package/lib/database/translateCache.d.ts +14 -0
  41. package/lib/database/translateCache.js +31 -0
  42. package/lib/index.d.ts +14 -0
  43. package/lib/index.js +59 -0
  44. package/lib/resource/dlc.html +115 -0
  45. package/lib/resource/footprint.html +241 -0
  46. package/lib/resource/mileage-leaderboard.html +363 -0
  47. package/lib/resource/package/SEGUIEMJ.TTF +0 -0
  48. package/lib/resource/package/ets-map.js +63 -0
  49. package/lib/resource/package/leaflet/heatmap.min.js +9 -0
  50. package/lib/resource/package/leaflet/leaflet-heatmap.js +246 -0
  51. package/lib/resource/package/leaflet/leaflet.min.css +1 -0
  52. package/lib/resource/package/leaflet/leaflet.min.js +1 -0
  53. package/lib/resource/position.html +229 -0
  54. package/lib/resource/server-list.html +309 -0
  55. package/lib/resource/traffic.html +207 -0
  56. package/lib/util/baiduTranslate.d.ts +2 -0
  57. package/lib/util/baiduTranslate.js +30 -0
  58. package/lib/util/common.d.ts +1 -0
  59. package/lib/util/common.js +5 -0
  60. package/lib/util/constant.d.ts +19 -0
  61. package/lib/util/constant.js +36 -0
  62. package/package.json +48 -0
  63. package/readme.md +86 -0
@@ -0,0 +1,95 @@
1
+ const { segment } = require('koishi');
2
+ const { resolve } = require('path');
3
+ const guildBind = require('../database/guildBind');
4
+ const truckyAppApi = require('../api/truckyAppApi');
5
+ const truckersMpApi = require('../api/truckersMpApi');
6
+ const evmOpenApi = require('../api/evmOpenApi');
7
+ const baiduTranslate = require('../util/baiduTranslate');
8
+ const common = require('../util/common');
9
+ /**
10
+ * 定位
11
+ */
12
+ module.exports = async (ctx, cfg, session, tmpId) => {
13
+ if (ctx.puppeteer) {
14
+ if (tmpId && isNaN(tmpId)) {
15
+ return `🔄请输入正确的玩家编号,或绑定玩家编号`;
16
+ }
17
+ // 如果没有传入tmpId,尝试从数据库查询绑定信息
18
+ if (!tmpId) {
19
+ let guildBindData = await guildBind.get(ctx.database, session.platform, session.userId);
20
+ if (!guildBindData) {
21
+ return `🔄请输入正确的玩家编号,或绑定玩家编号`;
22
+ }
23
+ tmpId = guildBindData.tmp_id;
24
+ }
25
+ // 查询玩家信息
26
+ let playerInfo = await truckersMpApi.player(ctx.http, tmpId);
27
+ if (playerInfo.error) {
28
+ return '❌查询玩家信息失败,请重试';
29
+ }
30
+ // 查询线上信息
31
+ let playerMapInfo = await truckyAppApi.online(ctx.http, tmpId);
32
+ if (playerMapInfo.error) {
33
+ return '❌查询玩家位置信息失败,请重试';
34
+ }
35
+ if (!playerMapInfo.data.online) {
36
+ return '🔄玩家离线';
37
+ }
38
+ // 查询周边玩家,并处理数据
39
+ let areaPlayersData = await evmOpenApi.mapPlayerList(ctx.http, playerMapInfo.data.server, playerMapInfo.data.x - 4000, playerMapInfo.data.y + 2500, playerMapInfo.data.x + 4000, playerMapInfo.data.y - 2500);
40
+ let areaPlayerList = [];
41
+ if (!areaPlayersData.error) {
42
+ areaPlayerList = areaPlayersData.data;
43
+ let index = areaPlayerList.findIndex((player) => {
44
+ return player.tmpId.toString() === tmpId.toString();
45
+ });
46
+ if (index !== -1) {
47
+ areaPlayerList.splice(index, 1);
48
+ }
49
+ }
50
+ areaPlayerList.push({
51
+ axisX: playerMapInfo.data.x,
52
+ axisY: playerMapInfo.data.y,
53
+ tmpId
54
+ });
55
+ // promods服ID集合
56
+ let promodsServerIdList = [50, 51];
57
+ // 构建地图数据
58
+ let data = {
59
+ mapType: promodsServerIdList.indexOf(playerMapInfo.data.server) !== -1 ? 'promods' : 'ets',
60
+ avatar: playerInfo.data.smallAvatar,
61
+ username: playerInfo.data.name,
62
+ serverName: playerMapInfo.data.serverDetails.name,
63
+ country: await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.country),
64
+ realName: await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.realName),
65
+ currentPlayerId: tmpId,
66
+ centerX: playerMapInfo.data.x,
67
+ centerY: playerMapInfo.data.y,
68
+ playerList: areaPlayerList
69
+ };
70
+ let page;
71
+ try {
72
+ page = await ctx.puppeteer.page();
73
+ await page.setViewport({ width: 1000, height: 1000, deviceScaleFactor: 2 });
74
+ await page.goto(`file:///${resolve(__dirname, '../resource/position.html')}`);
75
+ await page.evaluate(`setData(${JSON.stringify(data)})`);
76
+ await common.sleep(100);
77
+ await page.waitForNetworkIdle();
78
+ const element = await page.$("#container");
79
+ return (segment.image(await element.screenshot({
80
+ encoding: "binary"
81
+ }), "image/jpg"));
82
+ }
83
+ catch (e) {
84
+ return '渲染异常,请重试';
85
+ }
86
+ finally {
87
+ if (page) {
88
+ await page.close();
89
+ }
90
+ }
91
+ }
92
+ else {
93
+ return '⚠️未启用 puppeteer 服务';
94
+ }
95
+ };
@@ -0,0 +1,2 @@
1
+ declare function _exports(ctx: any, cfg: any, session: any, tmpId: any): Promise<string>;
2
+ export = _exports;
@@ -0,0 +1,148 @@
1
+ const dayjs = require('dayjs');
2
+ const dayjsRelativeTime = require('dayjs/plugin/relativeTime');
3
+ const dayjsLocaleZhCn = require('dayjs/locale/zh-cn');
4
+ const guildBind = require('../database/guildBind');
5
+ const truckyAppApi = require('../api/truckyAppApi');
6
+ const evmOpenApi = require('../api/evmOpenApi');
7
+ const baiduTranslate = require('../util/baiduTranslate');
8
+ dayjs.extend(dayjsRelativeTime);
9
+ dayjs.locale(dayjsLocaleZhCn);
10
+ /**
11
+ * 用户组
12
+ */
13
+ const userGroup = {
14
+ 'Player': '玩家',
15
+ 'Retired Legend': '退役',
16
+ 'Game Developer': '游戏开发者',
17
+ 'Retired Team Member': '退休团队成员',
18
+ 'Add-On Team': '附加组件团队',
19
+ 'Game Moderator': '游戏管理员'
20
+ };
21
+ /**
22
+ * 查询玩家信息
23
+ */
24
+ module.exports = async (ctx, cfg, session, tmpId) => {
25
+ if (tmpId && isNaN(tmpId)) {
26
+ return `🔄请输入正确的玩家编号,或绑定玩家编号`;
27
+ }
28
+ // 如果没有传入tmpId,尝试从数据库查询绑定信息
29
+ if (!tmpId) {
30
+ let guildBindData = await guildBind.get(ctx.database, session.platform, session.userId);
31
+ if (!guildBindData) {
32
+ return `🔄请输入正确的玩家编号,或绑定玩家编号`;
33
+ }
34
+ tmpId = guildBindData.tmp_id;
35
+ }
36
+ // 查询玩家信息
37
+ let playerInfo = await evmOpenApi.playerInfo(ctx.http, tmpId);
38
+ if (playerInfo.error && playerInfo.code === 10001) {
39
+ return '❌玩家不存在';
40
+ }
41
+ else if (playerInfo.error) {
42
+ return '❌查询玩家信息失败,请重试';
43
+ }
44
+ // 查询线上信息
45
+ let playerMapInfo = await truckyAppApi.online(ctx.http, tmpId);
46
+ // 拼接消息模板
47
+ let message = '';
48
+ if (cfg.queryShowAvatarEnable) {
49
+ message += `<img src="${playerInfo.data.avatarUrl}"/>\n`;
50
+ }
51
+ message += '🆔TMP编号: ' + playerInfo.data.tmpId;
52
+ message += '\n😀玩家名称: ' + playerInfo.data.name;
53
+ message += '\n🎮SteamID: ' + playerInfo.data.steamId;
54
+ let registerDate = dayjs(playerInfo.data.registerTime);
55
+ message += '\n📅注册日期: ' + registerDate.format('YYYY年MM月DD日');
56
+ message += `\n📑注册天数: ${dayjs().diff(registerDate, 'day')}天`;
57
+ message += '\n🎖️所属分组: ' + (userGroup[playerInfo.data.groupName] || playerInfo.data.groupName);
58
+ if (playerInfo.data.isJoinVtc) {
59
+ message += '\n🚚当前车队: ' + playerInfo.data.vtcName;
60
+ const vtcRoleMap = {
61
+ 'Owner': '队长',
62
+ 'Co-Owner': '副队长',
63
+ 'Chairman': '主席',
64
+ 'Vice Chairman': '副主席',
65
+ 'Manager': '管理员',
66
+ 'Deputy Manager': '副管理员',
67
+ 'Captain': '主管',
68
+ 'Lieutenant': '副主管',
69
+ 'Recruiter': '招聘员',
70
+ 'Driver': '司机',
71
+ 'Trainee': '实习司机',
72
+ 'Member': '成员',
73
+ 'Veteran Driver': '资深司机',
74
+ 'Legend Driver': '传奇司机',
75
+ 'Pilot': '引航员',
76
+ 'Event Manager': '活动管理员',
77
+ 'Media Manager': '媒体管理员',
78
+ 'HR': '人事部',
79
+ };
80
+ message += '\n🚚车队职位: ' + (vtcRoleMap[playerInfo.data.vtcRole] || playerInfo.data.vtcRole);
81
+ }
82
+ message += '\n🚫玩家封禁: ' + (playerInfo.data.isBan ? '✅' : '❌');
83
+ if (playerInfo.data.isBan) {
84
+ message += '\n🚫截止日期: ';
85
+ if (playerInfo.data.banHide) {
86
+ message += '玩家已隐藏⚠️';
87
+ }
88
+ else {
89
+ if (!playerInfo.data.banUntil) {
90
+ message += '⚠️永久';
91
+ }
92
+ else {
93
+ message += dayjs(playerInfo.data.banUntil).format('YYYY年MM月DD日 HH:mm');
94
+ }
95
+ message += "\n🚫封号原因: " + (playerInfo.data.banReasonZh || playerInfo.data.banReason);
96
+ }
97
+ }
98
+ message += '\n🚫封禁次数: ' + (playerInfo.data.banCount || 0) + '次';
99
+ if (playerInfo.data.mileage) {
100
+ let mileage = playerInfo.data.mileage;
101
+ let mileageUnit = '米';
102
+ if (mileage > 1000) {
103
+ mileage = (mileage / 1000).toFixed(1);
104
+ mileageUnit = '公里';
105
+ }
106
+ message += '\n🚩历史里程: ' + mileage + mileageUnit;
107
+ }
108
+ if (playerInfo.data.todayMileage) {
109
+ let todayMileage = playerInfo.data.todayMileage;
110
+ let mileageUnit = '米';
111
+ if (todayMileage > 1000) {
112
+ todayMileage = (todayMileage / 1000).toFixed(1);
113
+ mileageUnit = '公里';
114
+ }
115
+ message += '\n🚩今日里程: ' + todayMileage + mileageUnit;
116
+ }
117
+ message += '\n💎是否赞助商: ' + (playerInfo.data.isSponsor ? '✅' : '❌');
118
+ if (playerInfo.data.isSponsor) {
119
+ if (playerInfo.data.sponsorAmount) {
120
+ message += '\n🎁当前赞助: $' + (playerInfo.data.sponsorAmount / 100);
121
+ }
122
+ if (playerInfo.data.sponsorCumulativeAmount) {
123
+ message += '\n🎁累计赞助: $' + (playerInfo.data.sponsorCumulativeAmount / 100);
124
+ }
125
+ }
126
+ message += '\n🖥️在线状态: ' + ((playerMapInfo && !playerMapInfo.error && playerMapInfo.data.online) ? '✅' : '❌');
127
+ if (playerMapInfo && !playerMapInfo.error && playerMapInfo.data.online) {
128
+ const serverNameMap = {
129
+ 'Simulation 1': 'S1 服',
130
+ 'Simulation 2': 'S2 服',
131
+ 'Arcade': 'A 服',
132
+ 'ProMods': 'P 服',
133
+ 'ProMods Arcade': 'P 服(街机)',
134
+ 'US Simulation': '美 服',
135
+ 'Asia Simulation': '亚 服',
136
+ };
137
+ const rawServerName = playerMapInfo.data.serverDetails.name;
138
+ const shortServerName = serverNameMap[rawServerName] || rawServerName;
139
+ message += '\n📡在线服务器: ' + shortServerName + ' 🟢';
140
+ message += '\n📊在线国家: ';
141
+ message += await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.country);
142
+ message += '\n📶在线城市: ';
143
+ message += await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.realName);
144
+ } else if (playerInfo.data.lastOnlineTime) {
145
+ message += '\n📶上次在线: ' + dayjs(playerInfo.data.lastOnlineTime).fromNow(false);
146
+ }
147
+ return message;
148
+ };
@@ -0,0 +1,2 @@
1
+ declare function _exports(ctx: any, cfg: any): Promise<string | import("koishi").Element>;
2
+ export = _exports;
@@ -0,0 +1,15 @@
1
+ const tmpServerText = require("./tmpServerText");
2
+ const tmpServerImg = require("./tmpServerImg");
3
+ /**
4
+ * 查询服务器列表
5
+ */
6
+ module.exports = async (ctx, cfg) => {
7
+ switch (cfg.tmpServerType) {
8
+ case 1:
9
+ return await tmpServerText(ctx);
10
+ case 2:
11
+ return await tmpServerImg(ctx);
12
+ default:
13
+ return '指令配置错误';
14
+ }
15
+ };
@@ -0,0 +1,3 @@
1
+ declare function _exports(ctx: any): Promise<segment | "渲染异常,请重试" | "未启用 puppeteer 服务" | "查询服务器失败,请稍后重试">;
2
+ export = _exports;
3
+ import { segment } from "@koishijs/core";
@@ -0,0 +1,35 @@
1
+ const evmOpenApi = require('../../api/evmOpenApi');
2
+ const { resolve } = require("path");
3
+ const common = require("../../util/common");
4
+ const { segment } = require("koishi");
5
+ module.exports = async (ctx) => {
6
+ if (!ctx.puppeteer) {
7
+ return '⚠️未启用 puppeteer 服务';
8
+ }
9
+ // 查询服务器信息
10
+ let serverData = await evmOpenApi.serverList(ctx.http);
11
+ if (serverData.error) {
12
+ return '❌查询服务器失败,请稍后重试';
13
+ }
14
+ let page;
15
+ try {
16
+ page = await ctx.puppeteer.page();
17
+ await page.setViewport({ width: 380, height: 1000, deviceScaleFactor: 2 });
18
+ await page.goto(`file:///${resolve(__dirname, '../../resource/server-list.html')}`);
19
+ await page.evaluate(`setData(${JSON.stringify(serverData)})`);
20
+ await common.sleep(100);
21
+ await page.waitForNetworkIdle();
22
+ const element = await page.$("#container");
23
+ return (segment.image(await element.screenshot({
24
+ encoding: "binary"
25
+ }), "image/jpg"));
26
+ }
27
+ catch {
28
+ return '⚠️渲染异常,请重试';
29
+ }
30
+ finally {
31
+ if (page) {
32
+ await page.close();
33
+ }
34
+ }
35
+ };
@@ -0,0 +1,2 @@
1
+ declare function _exports(ctx: any): Promise<string>;
2
+ export = _exports;
@@ -0,0 +1,40 @@
1
+ const evmOpenApi = require('../../api/evmOpenApi');
2
+ module.exports = async (ctx) => {
3
+ // 查询服务器信息
4
+ let serverData = await evmOpenApi.serverList(ctx.http);
5
+ if (serverData.error) {
6
+ return '⚠️查询服务器失败,请稍后重试';
7
+ }
8
+ let serverList = serverData.data;
9
+ // 计算总在线人数
10
+ let totalOnline = serverList.reduce((sum, s) => sum + (s.playerCount || 0), 0);
11
+ // 固定宽度填充函数
12
+ const padRight = (str, len) => str + ' '.repeat(Math.max(0, len - str.length));
13
+ // 构建消息
14
+ let message = `🌍 欧卡2 服务器状态\n`;
15
+ message += `📊 总在线人数 : ${totalOnline}人\n`;
16
+ message += `═══════════════════════\n`;
17
+ let first = true;
18
+ for (let server of serverList) {
19
+ if (!first) message += '\n';
20
+ message += `🖥️ 服务器 : ${server.serverName || ''}${server.isOnline === 1 ? '🟢' : '⚫'}\n`;
21
+ message += `📊 在线人数 : ${server.playerCount || 0}/${server.maxPlayer || 0}`;
22
+ if (server.queue) {
23
+ message += ` (队列: ${server.queueCount || 0})`;
24
+ }
25
+ // 服务器特性
26
+ let characteristicList = [];
27
+ if (server.afkEnable !== 1) {
28
+ characteristicList.push('⏱挂机');
29
+ }
30
+ if (server.collisionsEnable === 1) {
31
+ characteristicList.push('💥碰撞');
32
+ }
33
+ if (characteristicList.length > 0) {
34
+ message += '\n🚔 服务器特性: ' + characteristicList.join(' ');
35
+ }
36
+ message += '\n═══════════════════════';
37
+ first = false;
38
+ }
39
+ return message;
40
+ };
@@ -0,0 +1,2 @@
1
+ declare function _exports(ctx: any, cfg: any, serverName: any): Promise<string | import("koishi").Element>;
2
+ export = _exports;
@@ -0,0 +1,15 @@
1
+ const tmpTrafficMap = require("./tmpTrafficMap");
2
+ const tmpTrafficText = require("./tmpTrafficText");
3
+ /**
4
+ * 查询路况
5
+ */
6
+ module.exports = async (ctx, cfg, serverName) => {
7
+ switch (cfg.tmpTrafficType) {
8
+ case 1:
9
+ return await tmpTrafficText(ctx, cfg, serverName);
10
+ case 2:
11
+ return await tmpTrafficMap(ctx, cfg, serverName);
12
+ default:
13
+ return '指令配置错误';
14
+ }
15
+ };
@@ -0,0 +1,3 @@
1
+ declare function _exports(ctx: any, cfg: any, serverName: any): Promise<segment | "渲染异常,请重试" | "未启用 puppeteer 服务" | "请输入正确的服务器名称 (s1, s2, p, a)" | "查询路况信息失败">;
2
+ export = _exports;
3
+ import { segment } from "@koishijs/core";
@@ -0,0 +1,119 @@
1
+ const truckyAppApi = require('../../api/truckyAppApi');
2
+ const evmOpenApi = require('../../api/evmOpenApi');
3
+ const baiduTranslate = require('../../util/baiduTranslate');
4
+ const { resolve } = require("path");
5
+ const common = require("../../util/common");
6
+ const { segment } = require("koishi");
7
+ /**
8
+ * 服务器别名
9
+ */
10
+ const serverAlias = {
11
+ 's1': {
12
+ name: 'sim1',
13
+ mapType: 'ets',
14
+ serverId: 2,
15
+ bounds: [[-94189, 93775], [79264, -78999]]
16
+ },
17
+ 's2': {
18
+ name: 'sim2',
19
+ mapType: 'ets',
20
+ serverId: 41,
21
+ bounds: [[-94189, 93775], [79264, -78999]]
22
+ },
23
+ 'p': {
24
+ name: 'eupromods1',
25
+ mapType: 'promods',
26
+ serverId: 50,
27
+ bounds: [[-96355, 16381], [205581, -70750]]
28
+ },
29
+ 'a': {
30
+ name: 'arc1',
31
+ mapType: 'ets',
32
+ serverId: 7,
33
+ bounds: [[-94189, 93775], [79264, -78999]]
34
+ }
35
+ };
36
+ /**
37
+ * 路况程度转中文
38
+ */
39
+ const severityToZh = {
40
+ 'Fluid': {
41
+ text: '畅通',
42
+ color: '#00d26a'
43
+ },
44
+ 'Moderate': {
45
+ text: '正常',
46
+ color: '#ff6723'
47
+ },
48
+ 'Congested': {
49
+ text: '缓慢',
50
+ color: '#f8312f'
51
+ },
52
+ 'Heavy': {
53
+ text: '拥堵',
54
+ color: '#8d67c5'
55
+ }
56
+ };
57
+ /**
58
+ * 位置类型转中文
59
+ */
60
+ const typeToZh = {
61
+ 'City': '城市',
62
+ 'Road': '公路',
63
+ 'Intersection': '十字路口'
64
+ };
65
+ /**
66
+ * 查询路况
67
+ */
68
+ module.exports = async (ctx, cfg, serverName) => {
69
+ if (!ctx.puppeteer) {
70
+ return '未启用 puppeteer 服务';
71
+ }
72
+ // 根据别名获取服务器信息
73
+ let serverInfo = serverAlias[serverName];
74
+ if (!serverInfo) {
75
+ return '请输入正确的服务器名称 (s1, s2, p, a)';
76
+ }
77
+ // 查询路况信息
78
+ let trafficData = await truckyAppApi.trafficTop(ctx.http, serverInfo.name);
79
+ if (trafficData.error) {
80
+ return '查询路况信息失败';
81
+ }
82
+ // 查询地图玩家数据
83
+ let mapData = await evmOpenApi.mapPlayerList(ctx.http, serverInfo.serverId, serverInfo.bounds[0][0], serverInfo.bounds[0][1], serverInfo.bounds[1][0], serverInfo.bounds[1][1]);
84
+ // 构建路况数据
85
+ let data = {
86
+ mapType: serverInfo.mapType,
87
+ trafficList: [],
88
+ playerCoordinateList: mapData.error && mapData.data ? [] : mapData.data.map(item => [item.axisX, item.axisY])
89
+ };
90
+ for (const traffic of trafficData.data) {
91
+ data.trafficList.push({
92
+ country: await baiduTranslate(ctx, cfg, traffic.country),
93
+ province: await baiduTranslate(ctx, cfg, traffic.name.substring(0, traffic.name.lastIndexOf('(') - 1)),
94
+ playerCount: traffic.players,
95
+ severity: severityToZh[traffic.newSeverity] || { text: '未知', color: '#ffffff' }
96
+ });
97
+ }
98
+ let page;
99
+ try {
100
+ page = await ctx.puppeteer.page();
101
+ await page.setViewport({ width: 1000, height: 1000, deviceScaleFactor: 2 });
102
+ await page.goto(`file:///${resolve(__dirname, '../../resource/traffic.html')}`);
103
+ await page.evaluate(`setData(${JSON.stringify(data)})`);
104
+ await common.sleep(100);
105
+ await page.waitForNetworkIdle();
106
+ const element = await page.$("#container");
107
+ return (segment.image(await element.screenshot({
108
+ encoding: "binary"
109
+ }), "image/jpg"));
110
+ }
111
+ catch {
112
+ return '渲染异常,请重试';
113
+ }
114
+ finally {
115
+ if (page) {
116
+ await page.close();
117
+ }
118
+ }
119
+ };
@@ -0,0 +1,2 @@
1
+ declare function _exports(ctx: any, cfg: any, serverName: any): Promise<string>;
2
+ export = _exports;
@@ -0,0 +1,122 @@
1
+ const truckyAppApi = require('../../api/truckyAppApi');
2
+ const baiduTranslate = require('../../util/baiduTranslate');
3
+ /**
4
+ * 服务器别名
5
+ */
6
+ const serverNameAlias = {
7
+ 's1': 'sim1',
8
+ 's2': 'sim2',
9
+ 'p': 'eupromods1',
10
+ 'a': 'arc1'
11
+ };
12
+ /**
13
+ * 服务器中文名
14
+ */
15
+ const serverNameZh = {
16
+ 'sim1': 'S1',
17
+ 'sim2': 'S2',
18
+ 'eupromods1': 'P',
19
+ 'arc1': 'A'
20
+ };
21
+ /**
22
+ * 路况程度转中文
23
+ */
24
+ const severityToZh = {
25
+ 'Fluid': '🟢畅通',
26
+ 'Moderate': '🟠正常',
27
+ 'Congested': '🔴缓慢',
28
+ 'Heavy': '🟣拥堵',
29
+ 'Low': '🟢畅通',
30
+ 'green': '🟢畅通',
31
+ 'yellow': '🟡缓行',
32
+ 'orange': '🟠正常',
33
+ 'red': '🔴拥堵',
34
+ 'black': '🟣封闭'
35
+ };
36
+ /**
37
+ * 位置类型转中文
38
+ */
39
+ const typeToZh = {
40
+ 'City': '城市',
41
+ 'Road': '公路',
42
+ 'Intersection': '十字路口',
43
+ 'crash': '车祸',
44
+ 'closed': '封路',
45
+ 'event': '活动',
46
+ 'speedcamera': '测速',
47
+ 'policing': '警察',
48
+ 'overweight': '超重'
49
+ };
50
+ /**
51
+ * 过滤标识符 - 这些类型不显示
52
+ */
53
+ const filteredTypes = [];
54
+ /**
55
+ * 本地翻译字典 - 优先使用此字典的翻译
56
+ */
57
+ const localDict = {
58
+ 'Alpen Road': '阿尔卑斯山脉公路',
59
+ 'Alpen road': '阿尔卑斯山脉公路',
60
+ 'alpen road': '阿尔卑斯山脉公路',
61
+ 'Alpen': '阿尔卑斯山脉公路',
62
+ 'alpen': '阿尔卑斯山脉公路',
63
+ 'Alps': '阿尔卑斯山脉',
64
+ 'alps': '阿尔卑斯山脉',
65
+ 'Alpenstraße': '阿尔卑斯山路',
66
+ 'alpenstraße': '阿尔卑斯山路'
67
+ };
68
+ /**
69
+ * 本地翻译函数
70
+ */
71
+ function localTranslate(text) {
72
+ if (localDict[text]) {
73
+ return localDict[text];
74
+ }
75
+ return null;
76
+ }
77
+ /**
78
+ * 获取路况等级文本
79
+ */
80
+ function getSeverityFromData(traffic) {
81
+ if (traffic.newSeverity && severityToZh[traffic.newSeverity]) {
82
+ return severityToZh[traffic.newSeverity];
83
+ }
84
+ if (traffic.severity && severityToZh[traffic.severity]) {
85
+ return severityToZh[traffic.severity];
86
+ }
87
+ return traffic.color || '⚪异常';
88
+ }
89
+ /**
90
+ * 查询路况
91
+ */
92
+ module.exports = async (ctx, cfg, serverName) => {
93
+ // 转换服务器别名
94
+ let serverQueryName = serverNameAlias[serverName];
95
+ if (!serverQueryName) {
96
+ return '🔄请输入服务器名称 (s1, s2, p, a)注意+空格';
97
+ }
98
+ let trafficData = await truckyAppApi.trafficTop(ctx.http, serverQueryName);
99
+ if (trafficData.error) {
100
+ return '⚠️查询路况信息失败';
101
+ }
102
+ // 构建消息
103
+ let message = `🚛 ${serverNameZh[serverQueryName]} 服务器热门路况交通\n`;
104
+ message += `━━━━━━━━━━━━━━━━━━\n`;
105
+ let first = true;
106
+ for (const traffic of trafficData.data) {
107
+ const countryZh = await baiduTranslate(ctx, cfg, traffic.country);
108
+ let name = traffic.name.substring(0, traffic.name.lastIndexOf('(') - 1);
109
+ let type = traffic.name.substring(traffic.name.lastIndexOf('(') + 1, traffic.name.lastIndexOf(')'));
110
+ // 优先使用本地翻译,其次用百度翻译
111
+ const nameZh = localTranslate(name) || await baiduTranslate(ctx, cfg, name);
112
+ // 如果是地理位置标识符,不显示类型
113
+ const showType = !filteredTypes.includes(type.toLowerCase());
114
+ if (!first) message += '\n';
115
+ message += `${countryZh} ${nameZh}${showType ? ' (' + (typeToZh[type] || type) + ')' : ''}`;
116
+ message += `\n路况: ${getSeverityFromData(traffic)} | 人数: ${traffic.players}`;
117
+ message += '\n────────────';
118
+ first = false;
119
+ }
120
+ message += '\n━━━━━━━━━━━━━━━━━━';
121
+ return message || '⚠️暂无路况数据';
122
+ };
@@ -0,0 +1,17 @@
1
+ const guildBind = require('../database/guildBind');
2
+ /**
3
+ * 解绑 TMP ID
4
+ */
5
+ module.exports = async (ctx, session) => {
6
+ // 查询当前绑定信息
7
+ let bindInfo = await guildBind.get(ctx.database, session.platform, session.userId);
8
+ if (!bindInfo) {
9
+ return `❌当前未绑定任何账号`;
10
+ }
11
+ // 删除绑定
12
+ let result = await guildBind.remove(ctx.database, session.platform, session.userId);
13
+ if (result) {
14
+ return `✅解绑成功`;
15
+ }
16
+ return `❌解绑失败`;
17
+ };
@@ -0,0 +1,2 @@
1
+ declare function _exports(ctx: any): Promise<string>;
2
+ export = _exports;