koishi-plugin-ets2-tools-tmp 3.3.3 → 3.3.5

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.
@@ -223,4 +223,34 @@ module.exports = {
223
223
  }
224
224
  return data
225
225
  },
226
+ /**
227
+ * 查询 Steam 游戏时长(ETS2 227300 / ATS 270880)
228
+ * 通过代理访问 Steam IPlayerService,返回格式化后的 { ets2, ats } 字符串
229
+ * 未配置 apiKey 或请求失败时对应字段为 null
230
+ */
231
+ async steamGameTime(http, steamApiKey, steamId) {
232
+ if (!steamApiKey || !steamId) {
233
+ return { ets2: null, ats: null }
234
+ }
235
+ const path = `/proxy/steam/IPlayerService/GetOwnedGames/v1?key=${encodeURIComponent(steamApiKey)}&steamid=${steamId}&appids_filter[0]=227300&appids_filter[1]=270880&include_played_free_games=1`
236
+ let response
237
+ try {
238
+ response = await apiLog.get(http, 'evm.steam.gameTime', `${BASE_APIS[0]}${path}`)
239
+ } catch {
240
+ return { ets2: null, ats: null }
241
+ }
242
+ const result = { ets2: null, ats: null }
243
+ if (response?.response?.game_count > 0 && Array.isArray(response.response.games)) {
244
+ for (const game of response.response.games) {
245
+ const minutes = game.playtime_forever
246
+ if (minutes == null) continue
247
+ const h = Math.floor(minutes / 60)
248
+ const m = minutes % 60
249
+ const playtime = `${h}小时${m}分钟`
250
+ if (game.appid === 227300) result.ets2 = playtime
251
+ else if (game.appid === 270880) result.ats = playtime
252
+ }
253
+ }
254
+ return result
255
+ },
226
256
  }
@@ -161,28 +161,12 @@ module.exports = async (ctx, cfg, session, tmpId) => {
161
161
  data.ets2GameTime = null;
162
162
  data.atsGameTime = null;
163
163
  if (cfg.commands?.tmpQueryGameTime) {
164
- try {
165
- const steamApiKey = cfg.steamApi?.key;
166
- if (steamApiKey) {
167
- const steamId = playerInfo.data.steamId;
168
- const url = `https://evmapi.cxnnn.cn/proxy/steam/IPlayerService/GetOwnedGames/v1?key=${steamApiKey}&steamid=${steamId}&appids_filter[0]=227300&appids_filter[1]=270880&include_played_free_games=1`;
169
- const response = await ctx.http.get(url);
170
- if (response.response && response.response.game_count > 0) {
171
- for (const game of response.response.games) {
172
- const playtimeMinutes = game.playtime_forever;
173
- const hours = Math.floor(playtimeMinutes / 60);
174
- const minutes = playtimeMinutes % 60;
175
- const playtime = `${hours}小时${minutes}分钟`;
176
- if (game.appid === 227300) {
177
- data.ets2GameTime = playtime;
178
- } else if (game.appid === 270880) {
179
- data.atsGameTime = playtime;
180
- }
181
- }
182
- }
183
- }
184
- } catch (error) {
185
- ctx.logger.error(`查询Steam游戏时长出错: ${error}`);
164
+ const steamApiKey = cfg.steamApi?.key;
165
+ if (steamApiKey) {
166
+ const steamId = playerInfo.data.steamId;
167
+ const gameTimes = await evmOpenApi.steamGameTime(ctx.http, steamApiKey, steamId);
168
+ data.ets2GameTime = gameTimes.ets2;
169
+ data.atsGameTime = gameTimes.ats;
186
170
  }
187
171
  }
188
172
  let page;
@@ -184,35 +184,11 @@ module.exports = async (ctx, cfg, session, tmpId) => {
184
184
 
185
185
  // 查询Steam游戏时长
186
186
  async function getSteamGameTimes(steamId) {
187
- try {
188
- const steamApiKey = cfg.steamApi?.key;
189
- if (!steamApiKey) {
190
- return { ets2: null, ats: null };
191
- }
192
- const url = `https://evmapi.cxnnn.cn/proxy/steam/IPlayerService/GetOwnedGames/v1?key=${steamApiKey}&steamid=${steamId}&appids_filter[0]=227300&appids_filter[1]=270880&include_played_free_games=1`;
193
- const response = await ctx.http.get(url);
194
- const result = { ets2: null, ats: null };
195
-
196
- if (response.response && response.response.game_count > 0) {
197
- for (const game of response.response.games) {
198
- const playtimeMinutes = game.playtime_forever;
199
- const hours = Math.floor(playtimeMinutes / 60);
200
- const minutes = playtimeMinutes % 60;
201
- const playtime = `${hours}小时${minutes}分钟`;
202
-
203
- if (game.appid === 227300) {
204
- result.ets2 = playtime;
205
- } else if (game.appid === 270880) {
206
- result.ats = playtime;
207
- }
208
- }
209
- }
210
-
211
- return result;
212
- } catch (error) {
213
- ctx.logger.error(`查询Steam游戏时长出错: ${error}`);
187
+ const steamApiKey = cfg.steamApi?.key;
188
+ if (!steamApiKey) {
214
189
  return { ets2: null, ats: null };
215
190
  }
191
+ return evmOpenApi.steamGameTime(ctx.http, steamApiKey, steamId);
216
192
  }
217
193
 
218
194
  // 获取欧卡和美卡游戏时长
@@ -4,6 +4,35 @@
4
4
  let logger = null
5
5
  let debugMode = false
6
6
 
7
+ // 从异常对象中尽可能提取错误信息(错误名、消息、HTTP 状态码、错误码、底层原因、响应体)
8
+ // 仅用 e.message 时,网络层异常可能只有空消息(如只显示 "Error"),需补充状态码等上下文
9
+ function formatError(e) {
10
+ if (e == null) return '未知错误'
11
+ const parts = []
12
+ if (e.name && e.name !== 'Error') parts.push(e.name)
13
+ if (e.message) parts.push(e.message)
14
+ if (e.response && e.response.status != null) {
15
+ parts.push(`HTTP ${e.response.status}${e.response.statusText ? ` ${e.response.statusText}` : ''}`)
16
+ } else if (e.status != null) {
17
+ parts.push(`HTTP ${e.status}`)
18
+ }
19
+ if (e.code != null && e.code !== e.status && e.code !== e.response?.status) parts.push(`code=${e.code}`)
20
+ if (e.cause) parts.push(`cause=${e.cause.message || e.cause.code || String(e.cause)}`)
21
+ // 响应体中的错误信息(如网关返回的 JSON 错误内容)
22
+ const body = e.response && e.response.data
23
+ if (body != null) {
24
+ const text = typeof body === 'string' ? body : JSON.stringify(body)
25
+ if (text) parts.push(`响应体: ${text.slice(0, 300)}`)
26
+ }
27
+ if (!parts.length) {
28
+ // 兜底:枚举异常对象上的自定义属性(Error 标准属性不可枚举)
29
+ const props = {}
30
+ for (const key of Object.keys(e)) props[key] = e[key]
31
+ parts.push(Object.keys(props).length ? JSON.stringify(props) : String(e))
32
+ }
33
+ return parts.join(' | ')
34
+ }
35
+
7
36
  module.exports = {
8
37
  /**
9
38
  * 初始化(在插件 apply 时调用)
@@ -29,7 +58,11 @@ module.exports = {
29
58
  }
30
59
  },
31
60
  /**
32
- * 发起 GET 请求并记录日志,失败时记录错误日志后抛出异常(由调用方原有 catch 处理)
61
+ * 格式化异常信息(提取错误名、HTTP 状态码、错误码、底层原因等),供其他模块复用
62
+ */
63
+ formatError,
64
+ /**
65
+ * 发起 GET 请求并记录日志,失败时记录含错误码等详细信息的错误日志后抛出异常(由调用方原有 catch 处理)
33
66
  */
34
67
  async get(http, tag, url) {
35
68
  if (debugMode && logger) {
@@ -44,7 +77,11 @@ module.exports = {
44
77
  return result
45
78
  } catch (e) {
46
79
  if (logger) {
47
- logger.error(`[TMP-BOT API] [${tag}] 请求失败: ${url} (${e.message || e})`)
80
+ logger.error(`[TMP-BOT API] [${tag}] 请求失败: ${url} (${formatError(e)})`)
81
+ // 调试模式下输出堆栈,便于定位异常来源
82
+ if (debugMode && e && e.stack) {
83
+ logger.error(`[TMP-BOT API] [${tag}] 堆栈: ${e.stack}`)
84
+ }
48
85
  }
49
86
  throw e
50
87
  }
@@ -1,5 +1,6 @@
1
1
  const md5 = require('js-md5');
2
2
  const translateCache = require('../database/translateCache');
3
+ const apiLog = require('./apiLog');
3
4
  const TRANSLATE_API = 'https://fanyi-api.baidu.com/api/trans/vip/translate';
4
5
  module.exports = async (ctx, cfg, content, cache = true) => {
5
6
  if (!cfg.baiduTranslate.enable) {
@@ -18,7 +19,7 @@ module.exports = async (ctx, cfg, content, cache = true) => {
18
19
  result = await ctx.http.get(`${TRANSLATE_API}?q=${encodeURI(content)}&from=auto&to=zh&appid=${cfg.baiduTranslate.appId}&salt=${randomInt}&sign=${sign}`);
19
20
  } catch (error) {
20
21
  // 翻译失败时降级返回原文,不影响主流程
21
- ctx.logger.error(`[TMP-BOT] 百度翻译请求失败: ${error.message || error}`);
22
+ ctx.logger.error(`[TMP-BOT] 百度翻译请求失败: ${apiLog.formatError(error)}`);
22
23
  return content;
23
24
  }
24
25
  if (result.error_code) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ets2-tools-tmp",
3
3
  "description": "欧卡2 TruckersMP信息查询、车队平台查询及活动提醒",
4
- "version": "3.3.3",
4
+ "version": "3.3.5",
5
5
  "contributors": [
6
6
  "opwop <slhp1013@qq.com>",
7
7
  "bot_actions <168329908@qq.com>"