koishi-plugin-tmp-bot 1.8.2 → 1.8.4

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.
@@ -0,0 +1,73 @@
1
+ const BASE_API = 'https://api.truckersmp.com/v2'
2
+
3
+ module.exports = {
4
+ /**
5
+ * 查询玩家信息
6
+ */
7
+ async player (http, tmpId) {
8
+ let result = null
9
+ try {
10
+ result = await http.get(`${BASE_API}/player/${tmpId}`)
11
+ } catch {
12
+ return {
13
+ error: true
14
+ }
15
+ }
16
+
17
+ // 拼接返回数据
18
+ let data = {
19
+ error: JSON.parse(result.error)
20
+ }
21
+ if (!data.error) {
22
+ data.data = result.response
23
+ }
24
+
25
+ return data
26
+ },
27
+ /**
28
+ * 查询服务器列表
29
+ */
30
+ async servers (http) {
31
+ let result = null
32
+ try {
33
+ result = await http.get(`${BASE_API}/servers`)
34
+ } catch {
35
+ return {
36
+ error: true
37
+ }
38
+ }
39
+
40
+ // 拼接返回数据
41
+ let data = {
42
+ error: JSON.parse(result.error)
43
+ }
44
+ if (!data.error) {
45
+ data.data = result.response
46
+ }
47
+
48
+ return data
49
+ },
50
+ /**
51
+ * 查询玩家封禁信息
52
+ */
53
+ async bans (http, tmpId) {
54
+ let result = null
55
+ try {
56
+ result = await http.get(`${BASE_API}/bans/${tmpId}`)
57
+ } catch {
58
+ return {
59
+ error: true
60
+ }
61
+ }
62
+
63
+ // 拼接返回数据
64
+ let data = {
65
+ error: JSON.parse(result.error)
66
+ }
67
+ if (!data.error) {
68
+ data.data = result.response
69
+ }
70
+
71
+ return data
72
+ }
73
+ }
@@ -0,0 +1,26 @@
1
+ const BASE_API = 'https://tracker.ets2map.com'
2
+
3
+ module.exports = {
4
+ /**
5
+ * 区域查询玩家
6
+ */
7
+ async area (http, serverId, x1, y1, x2, y2) {
8
+ let result = null
9
+ try {
10
+ result = await http.get(`${BASE_API}/v3/area?x1=${x1}&y1=${y1}&x2=${x2}&y2=${y2}&server=${serverId}`)
11
+ } catch {
12
+ return {
13
+ error: true
14
+ }
15
+ }
16
+
17
+ // 拼接返回数据
18
+ let data = {
19
+ error: !result || !result.Success
20
+ }
21
+ if (!data.error) {
22
+ data.data = result.Data
23
+ }
24
+ return data
25
+ }
26
+ }
@@ -0,0 +1,49 @@
1
+ const BASE_API_V3 = 'https://api.truckyapp.com/v3'
2
+ const BASE_API_V2 = 'https://api.truckyapp.com/v2'
3
+
4
+ module.exports = {
5
+ /**
6
+ * 查询线上信息
7
+ */
8
+ async online (http, tmpId) {
9
+ let result = null
10
+ try {
11
+ result = await http.get(`${BASE_API_V3}/map/online?playerID=${tmpId}`)
12
+ } catch {
13
+ return {
14
+ error: true
15
+ }
16
+ }
17
+
18
+ // 拼接返回数据
19
+ let data = {
20
+ error: !result || !result.response || result.response.error
21
+ }
22
+ if (!data.error) {
23
+ data.data = result.response
24
+ }
25
+ return data
26
+ },
27
+ /**
28
+ * 查询热门交通数据
29
+ */
30
+ async trafficTop (http, serverName) {
31
+ let result = null
32
+ try {
33
+ result = await http.get(`${BASE_API_V2}/traffic/top?game=ets2&server=${serverName}`)
34
+ } catch {
35
+ return {
36
+ error: true
37
+ }
38
+ }
39
+
40
+ // 拼接返回数据
41
+ let data = {
42
+ error: !result || !result.response || result.response.length <= 0
43
+ }
44
+ if (!data.error) {
45
+ data.data = result.response
46
+ }
47
+ return data
48
+ }
49
+ }
@@ -0,0 +1,22 @@
1
+ const guildBind = require('../database/guildBind')
2
+ const truckersMpApi = require("../api/truckersMpApi");
3
+
4
+ /**
5
+ * 绑定 TMP ID
6
+ */
7
+ module.exports = async (ctx, cfg, session, tmpId) => {
8
+ if (!tmpId || isNaN(tmpId)) {
9
+ return `请输入正确的玩家编号`
10
+ }
11
+
12
+ // 查询玩家信息
13
+ let playerInfo = await truckersMpApi.player(ctx.http, tmpId)
14
+ if (playerInfo.error) {
15
+ return '绑定失败 (查询玩家信息失败)'
16
+ }
17
+
18
+ // 更新数据库
19
+ guildBind.saveOrUpdate(ctx.database, session.platform, session.userId, session.author.username, tmpId)
20
+
21
+ return `绑定成功 ( ${playerInfo.data.name} )`
22
+ }
@@ -0,0 +1,107 @@
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 truckersMpMapApi = require('../api/truckersMpMapApi')
7
+ const baiduTranslate = require('../util/baiduTranslate')
8
+ const common = require('../util/common')
9
+
10
+ /**
11
+ * 定位
12
+ */
13
+ module.exports = async (ctx, cfg, session, tmpId) => {
14
+ if (ctx.puppeteer) {
15
+ if (tmpId && isNaN(tmpId)) {
16
+ return `请输入正确的玩家编号`
17
+ }
18
+
19
+ // 如果没有传入tmpId,尝试从数据库查询绑定信息
20
+ if (!tmpId) {
21
+ let guildBindData = await guildBind.get(ctx.database, session.platform, session.userId)
22
+ if (!guildBindData) {
23
+ return `请输入正确的玩家编号`
24
+ }
25
+ tmpId = guildBindData.tmp_id
26
+ }
27
+
28
+ // 查询玩家信息
29
+ let playerInfo = await truckersMpApi.player(ctx.http, tmpId)
30
+ if (playerInfo.error) {
31
+ return '查询玩家信息失败,请重试'
32
+ }
33
+
34
+ // 查询线上信息
35
+ let playerMapInfo = await truckyAppApi.online(ctx.http, tmpId)
36
+ if (playerMapInfo.error) {
37
+ return '查询玩家信息失败,请重试'
38
+ }
39
+ if (!playerMapInfo.data.online) {
40
+ return '玩家离线'
41
+ }
42
+
43
+ // 查询周边玩家,并处理数据
44
+ let areaPlayersData = await truckersMpMapApi.area(ctx.http, playerMapInfo.data.server,
45
+ playerMapInfo.data.x - 4000,
46
+ playerMapInfo.data.y + 2500,
47
+ playerMapInfo.data.x + 4000,
48
+ playerMapInfo.data.y - 2500)
49
+ let areaPlayerList = []
50
+ if (!areaPlayersData.error) {
51
+ areaPlayerList = areaPlayersData.data
52
+ let index = areaPlayerList.findIndex((player) => {
53
+ return player.MpId.toString() === tmpId
54
+ })
55
+ if (index !== -1) {
56
+ areaPlayerList.splice(index, 1)
57
+ }
58
+ }
59
+ areaPlayerList.push({
60
+ X: playerMapInfo.data.x,
61
+ Y: playerMapInfo.data.y,
62
+ MpId: tmpId
63
+ })
64
+
65
+ // promods服ID集合
66
+ let promodsServerIdList = [50, 51]
67
+
68
+ // 构建地图数据
69
+ let data = {
70
+ mapType: promodsServerIdList.indexOf(playerMapInfo.data.server) !== -1 ? 'promods' : 'ets',
71
+ avatar: playerInfo.data.smallAvatar,
72
+ username: playerInfo.data.name,
73
+ serverName: playerMapInfo.data.serverDetails.name,
74
+ country: await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.country),
75
+ realName: await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.realName),
76
+ currentPlayerId: tmpId,
77
+ centerX: playerMapInfo.data.x,
78
+ centerY: playerMapInfo.data.y,
79
+ playerList: areaPlayerList
80
+ }
81
+
82
+ let page
83
+ try {
84
+ page = await ctx.puppeteer.page()
85
+ await page.setViewport({ width: 1000, height: 1000 })
86
+ await page.goto(`file:///${resolve(__dirname, '../resource/position.html')}`)
87
+ await page.evaluate(`setData(${JSON.stringify(data)})`)
88
+ await common.sleep(100)
89
+ await page.waitForNetworkIdle()
90
+ const element = await page.$("#container");
91
+ return (
92
+ segment.image(await element.screenshot({
93
+ encoding: "binary"
94
+ }), "image/jpg")
95
+ )
96
+ } catch (e) {
97
+ return '渲染异常,请重试'
98
+ } finally {
99
+ if (page) {
100
+ await page.close()
101
+ }
102
+ }
103
+
104
+ } else {
105
+ return '未启用 puppeteer 服务'
106
+ }
107
+ }
@@ -0,0 +1,85 @@
1
+ const dayjs = require('dayjs')
2
+ const guildBind = require('../database/guildBind')
3
+ const truckersMpApi = require('../api/truckersMpApi')
4
+ const truckyAppApi = require('../api/truckyAppApi')
5
+ const baiduTranslate = require('../util/baiduTranslate')
6
+
7
+ /**
8
+ * 用户组
9
+ */
10
+ const userGroup = {
11
+ 'Player': '玩家',
12
+ 'Retired Legend': '退役',
13
+ 'Game Developer': '游戏开发者',
14
+ 'Retired Team Member': '退休团队成员',
15
+ 'Add-On Team': 'Add-On Team',
16
+ 'Game Moderator': 'Game Moderator'
17
+ }
18
+
19
+ /**
20
+ * 查询玩家信息
21
+ */
22
+ module.exports = async (ctx, cfg, session, tmpId) => {
23
+ if (tmpId && isNaN(tmpId)) {
24
+ return `请输入正确的玩家编号`
25
+ }
26
+
27
+ // 如果没有传入tmpId,尝试从数据库查询绑定信息
28
+ if (!tmpId) {
29
+ let guildBindData = await guildBind.get(ctx.database, session.platform, session.userId)
30
+ if (!guildBindData) {
31
+ return `请输入正确的玩家编号`
32
+ }
33
+ tmpId = guildBindData.tmp_id
34
+ }
35
+
36
+ // 查询玩家信息
37
+ let playerInfo = await truckersMpApi.player(ctx.http, tmpId)
38
+ if (playerInfo.error) {
39
+ return '查询玩家信息失败,请重试'
40
+ }
41
+
42
+ // 查询线上信息
43
+ let playerMapInfo = await truckyAppApi.online(ctx.http, tmpId)
44
+ if (playerMapInfo.error) {
45
+ return '查询玩家信息失败,请重试'
46
+ }
47
+
48
+ // 拼接消息模板
49
+ let message = `<img src="${playerInfo.data.avatar}"/>`
50
+ message += '\n😀玩家名称: ' + playerInfo.data.name
51
+ message += '\n📑注册日期: ' + dayjs(playerInfo.data.joinDate + 'Z').format('YYYY年MM月DD日')
52
+ message += '\n💼所属分组: ' + (userGroup[playerInfo.data.groupName] || playerInfo.data.groupName) // 🪪💼📂🚹
53
+ if (playerInfo.data.vtc && playerInfo.data.vtc.inVTC) {
54
+ message += '\n🚚所属车队: ' + playerInfo.data.vtc.name
55
+ }
56
+ message += '\n🚫是否封禁: ' + (playerInfo.data.banned ? '是' : '否')
57
+ if (playerInfo.data.banned) {
58
+ message += '\n🚫封禁截止: '
59
+ if (!playerInfo.data.displayBans) {
60
+ message += '隐藏'
61
+ } else {
62
+ let banData = await truckersMpApi.bans(ctx.http, tmpId)
63
+ if (!banData.error) {
64
+ let ban = banData.data[0]
65
+ if (!ban.expiration) {
66
+ message += '永久'
67
+ } else {
68
+ message += dayjs(ban.expiration + 'Z').format('YYYY年MM月DD日 HH:mm')
69
+ }
70
+ message += "\n🚫封禁原因: " + await baiduTranslate(ctx, cfg, ban.reason, false)
71
+ } else {
72
+ message += '查询失败'
73
+ }
74
+ }
75
+ }
76
+ message += '\n🚫封禁次数: ' + playerInfo.data.bansCount || 0
77
+ message += '\n📶在线状态: ' + (playerMapInfo.data.online ? `在线🟢 (${playerMapInfo.data.serverDetails.name})` : '离线⚫')
78
+ if (playerMapInfo.data.online) {
79
+ message += '\n🌍线上位置: '
80
+ message += await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.country)
81
+ message += ' - '
82
+ message += await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.realName)
83
+ }
84
+ return message
85
+ }
@@ -0,0 +1,39 @@
1
+ const truckersMpApi = require('../api/truckersMpApi')
2
+
3
+ module.exports = async (ctx, cfg, game) => {
4
+ // 查询服务器信息
5
+ let serverData = await truckersMpApi.servers(ctx.http)
6
+ if (serverData.error) {
7
+ return '查询服务器失败,请稍后重试'
8
+ }
9
+
10
+ // 过滤服务器
11
+ let etsServerList = serverData.data.filter(server => server.game === game)
12
+
13
+ // 构建消息
14
+ let message = ''
15
+ for (let server of etsServerList) {
16
+ // 如果前面有内容,换行
17
+ if (message) {
18
+ message += '\n\n'
19
+ }
20
+
21
+ message += '服务器: ' + ( server.online ? '🟢' : '⚫' ) + server.name
22
+ message += `\n玩家人数: ${server.players}/${server.maxplayers}`
23
+ if (server.queue) {
24
+ message += ` (队列: ${server.queue})`
25
+ }
26
+ // 服务器特性
27
+ let characteristicList = []
28
+ if (!server.afkenabled) {
29
+ characteristicList.push('⏱挂机')
30
+ }
31
+ if (server.collisions) {
32
+ characteristicList.push('💥碰撞')
33
+ }
34
+ if (characteristicList && characteristicList.length > 0) {
35
+ message += '\n服务器特性: ' + characteristicList.join(' ')
36
+ }
37
+ }
38
+ return message
39
+ }
@@ -0,0 +1,16 @@
1
+ const tmpTrafficMap = require("./tmpTrafficMap");
2
+ const tmpTrafficText = require("./tmpTrafficText");
3
+
4
+ /**
5
+ * 查询路况
6
+ */
7
+ module.exports = async (ctx, cfg, serverName) => {
8
+ switch (cfg.tmpTrafficType) {
9
+ case 1:
10
+ return await tmpTrafficText(ctx, cfg, serverName)
11
+ case 2:
12
+ return await tmpTrafficMap(ctx, cfg, serverName)
13
+ default:
14
+ return '指令配置错误'
15
+ }
16
+ }
@@ -0,0 +1,128 @@
1
+ const truckyAppApi = require('../../api/truckyAppApi')
2
+ const truckersMpMapApi = require('../../api/truckersMpMapApi')
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
+ */
11
+ const serverAlias = {
12
+ 's1': {
13
+ name: 'sim1',
14
+ mapType: 'ets',
15
+ serverId: 2,
16
+ bounds: [[-94189, 93775], [79264, -78999]]
17
+ },
18
+ 's2': {
19
+ name: 'sim2',
20
+ mapType: 'ets',
21
+ serverId: 41,
22
+ bounds: [[-94189, 93775], [79264, -78999]]
23
+ },
24
+ 'p': {
25
+ name: 'eupromods1',
26
+ mapType: 'promods',
27
+ serverId: 50,
28
+ bounds: [[-96355, 16381], [205581, -70750]]
29
+ },
30
+ 'a': {
31
+ name: 'arc1',
32
+ mapType: 'ets',
33
+ serverId: 7,
34
+ bounds: [[-94189, 93775], [79264, -78999]]
35
+ }
36
+ }
37
+
38
+ /**
39
+ * 路况程度转中文
40
+ */
41
+ const severityToZh = {
42
+ 'Fluid': {
43
+ text: '畅通',
44
+ color: '#00d26a'
45
+ },
46
+ 'Moderate': {
47
+ text: '正常',
48
+ color: '#ff6723'
49
+ },
50
+ 'Congested': {
51
+ text: '缓慢',
52
+ color: '#f8312f'
53
+ },
54
+ 'Heavy': {
55
+ text: '拥堵',
56
+ color: '#8d67c5'
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 位置类型转中文
62
+ */
63
+ const typeToZh = {
64
+ 'City': '城市',
65
+ 'Road': '公路',
66
+ 'Intersection': '十字路口'
67
+ }
68
+
69
+ /**
70
+ * 查询路况
71
+ */
72
+ module.exports = async (ctx, cfg, serverName) => {
73
+ if (!ctx.puppeteer) {
74
+ return '未启用 puppeteer 服务'
75
+ }
76
+
77
+ // 根据别名获取服务器信息
78
+ let serverInfo = serverAlias[serverName]
79
+ if (!serverInfo) {
80
+ return '请输入正确的服务器名称 (s1, s2, p, a)'
81
+ }
82
+
83
+ // 查询路况信息
84
+ let trafficData = await truckyAppApi.trafficTop(ctx.http, serverInfo.name)
85
+ if (trafficData.error) {
86
+ return '查询路况信息失败'
87
+ }
88
+
89
+ // 查询地图玩家数据
90
+ let mapData = await truckersMpMapApi.area(ctx.http, serverInfo.serverId, serverInfo.bounds[0][0], serverInfo.bounds[0][1], serverInfo.bounds[1][0], serverInfo.bounds[1][1])
91
+
92
+ // 构建路况数据
93
+ let data = {
94
+ mapType: serverInfo.mapType,
95
+ trafficList: [],
96
+ playerCoordinateList: mapData.error && mapData.data ? [] : mapData.data.map(item => [item.X, item.Y])
97
+ }
98
+ for (const traffic of trafficData.data) {
99
+ data.trafficList.push({
100
+ country: await baiduTranslate(ctx, cfg, traffic.country),
101
+ province: await baiduTranslate(ctx, cfg, traffic.name.substring(0, traffic.name.lastIndexOf('(') - 1)),
102
+ playerCount: traffic.players,
103
+ severity: severityToZh[traffic.newSeverity] || { text: '未知', color: '#ffffff' }
104
+ })
105
+ }
106
+
107
+ let page
108
+ try {
109
+ page = await ctx.puppeteer.page()
110
+ await page.setViewport({ width: 1000, height: 1000 })
111
+ await page.goto(`file:///${resolve(__dirname, '../../resource/traffic.html')}`)
112
+ await page.evaluate(`setData(${JSON.stringify(data)})`)
113
+ await common.sleep(100)
114
+ await page.waitForNetworkIdle()
115
+ const element = await page.$("#container");
116
+ return (
117
+ segment.image(await element.screenshot({
118
+ encoding: "binary"
119
+ }), "image/jpg")
120
+ )
121
+ } catch {
122
+ return '渲染异常,请重试'
123
+ } finally {
124
+ if (page) {
125
+ await page.close()
126
+ }
127
+ }
128
+ }
@@ -0,0 +1,66 @@
1
+ const truckyAppApi = require('../../api/truckyAppApi')
2
+ const baiduTranslate = require('../../util/baiduTranslate')
3
+
4
+ /**
5
+ * 服务器别名
6
+ */
7
+ const serverNameAlias = {
8
+ 's1': 'sim1',
9
+ 's2': 'sim2',
10
+ 'p': 'eupromods1',
11
+ 'a': 'arc1'
12
+ }
13
+
14
+ /**
15
+ * 路况程度转中文
16
+ */
17
+ const severityToZh = {
18
+ 'Fluid': '🟢畅通',
19
+ 'Moderate': '🟠正常',
20
+ 'Congested': '🔴缓慢',
21
+ 'Heavy': '🟣拥堵'
22
+ }
23
+
24
+ /**
25
+ * 位置类型转中文
26
+ */
27
+ const typeToZh = {
28
+ 'City': '城市',
29
+ 'Road': '公路',
30
+ 'Intersection': '十字路口'
31
+ }
32
+
33
+ /**
34
+ * 查询路况
35
+ */
36
+ module.exports = async (ctx, cfg, serverName) => {
37
+ // 转换服务器别名
38
+ let serverQueryName = serverNameAlias[serverName]
39
+ if (!serverQueryName) {
40
+ return '请输入正确的服务器名称 (s1, s2, p, a)'
41
+ }
42
+
43
+ let trafficData = await truckyAppApi.trafficTop(ctx.http, serverQueryName)
44
+ if (trafficData.error) {
45
+ return '查询路况信息失败'
46
+ }
47
+
48
+ // 构建消息
49
+ let message = ''
50
+ for (const traffic of trafficData.data) {
51
+ // 如果已有内容,换行
52
+ if (message) {
53
+ message += '\n\n'
54
+ }
55
+
56
+ message += await baiduTranslate(ctx, cfg, traffic.country)
57
+ message += ' - '
58
+ let name = traffic.name.substring(0, traffic.name.lastIndexOf('(') - 1)
59
+ let type = traffic.name.substring(traffic.name.lastIndexOf('(') + 1, traffic.name.lastIndexOf(')'))
60
+ message += await baiduTranslate(ctx, cfg, name) + ` (${typeToZh[type] || type})`
61
+ message += '\n路况: ' + (severityToZh[traffic.newSeverity] || traffic.color)
62
+ message += ' | 人数: ' + traffic.players
63
+ }
64
+
65
+ return message
66
+ }
@@ -0,0 +1,45 @@
1
+ module.exports = {
2
+ /**
3
+ * 获取绑定信息
4
+ * @param db 数据源
5
+ * @param platform 平台
6
+ * @param userId 用户编号
7
+ */
8
+ async get (db, platform, userId) {
9
+ const guildBindList = await db.get('tmp_guild_bind', {
10
+ platform,
11
+ user_id: userId
12
+ })
13
+
14
+ if (guildBindList && guildBindList.length > 0) {
15
+ return guildBindList[0]
16
+ }
17
+
18
+ return null
19
+ },
20
+ /**
21
+ * 新增或更新绑定信息
22
+ * @param db 数据源
23
+ * @param platform 平台
24
+ * @param userId 用户编号
25
+ * @param userName 用户昵称
26
+ * @param tmpId TMP ID
27
+ */
28
+ saveOrUpdate (db, platform, userId, userName, tmpId) {
29
+ this.get(db, platform, userId).then((data) => {
30
+ if (data) {
31
+ db.set('tmp_guild_bind', data.id, {
32
+ tmp_id: tmpId,
33
+ user_name: userName
34
+ })
35
+ } else {
36
+ db.create('tmp_guild_bind', {
37
+ platform: platform,
38
+ user_id: userId,
39
+ user_name: userName,
40
+ tmp_id: tmpId
41
+ })
42
+ }
43
+ })
44
+ }
45
+ }