koishi-plugin-rocom 1.0.2 → 1.0.3

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/client.d.ts CHANGED
@@ -14,6 +14,7 @@ export declare class RocomClient {
14
14
  private sanitizeForLog;
15
15
  private headersForLog;
16
16
  private stringifyForLog;
17
+ private isApiKeyPermissionUndeclaredError;
17
18
  private logRequestFailureDetails;
18
19
  private get;
19
20
  private post;
package/lib/client.js CHANGED
@@ -18,9 +18,9 @@ class RocomClient {
18
18
  return '';
19
19
  return uid.trim().replace(/[^a-zA-Z0-9_\- \u4e00-\u9fa5]/g, '').trim();
20
20
  }
21
- wegameHeaders(fwToken = '', userIdentifier = '', clientType = '', clientId = '') {
21
+ wegameHeaders(fwToken = '', userIdentifier = '', clientType = '', clientId = '', includeApiKey = true) {
22
22
  const headers = {};
23
- if (this.apiKey)
23
+ if (includeApiKey && this.apiKey)
24
24
  headers['X-API-Key'] = this.apiKey;
25
25
  if (fwToken)
26
26
  headers['X-Framework-Token'] = fwToken;
@@ -99,6 +99,11 @@ class RocomClient {
99
99
  return String(value);
100
100
  }
101
101
  }
102
+ isApiKeyPermissionUndeclaredError(message) {
103
+ if (!message)
104
+ return false;
105
+ return /未声明\s*api\s*key\s*权限|api\s*key\s*permission|api key 权限/i.test(message);
106
+ }
102
107
  logRequestFailureDetails(method, path, headers, params, body, errorOrResponse) {
103
108
  const err = errorOrResponse;
104
109
  const response = err?.response;
@@ -247,8 +252,8 @@ class RocomClient {
247
252
  return { status: null, data: null };
248
253
  }
249
254
  }
250
- async getIngameTask(ctx, taskId) {
251
- return this.requestWithStatus(ctx, 'GET', `/api/v1/games/rocom/ingame/tasks/${taskId}`, this.wegameHeaders(), { acceptedStatuses: [200, 202] });
255
+ async getIngameTask(ctx, taskId, includeApiKey = true) {
256
+ return this.requestWithStatus(ctx, 'GET', `/api/v1/games/rocom/ingame/tasks/${taskId}`, this.wegameHeaders('', '', '', '', includeApiKey), { acceptedStatuses: [200, 202] });
252
257
  }
253
258
  async qqQrLogin(ctx, userIdentifier) {
254
259
  const params = { client_type: 'bot', client_id: 'koishi', provider: 'rocom' };
@@ -338,7 +343,8 @@ class RocomClient {
338
343
  return null;
339
344
  }
340
345
  const path = '/api/v1/games/rocom/ingame/player/search';
341
- const headers = this.wegameHeaders();
346
+ let includeApiKey = true;
347
+ let headers = this.wegameHeaders();
342
348
  const payload = { uid: sanitizedUid, wait_ms: 5000 };
343
349
  let { status, data } = await this.requestWithStatus(ctx, 'POST', path, headers, {
344
350
  json: payload,
@@ -356,6 +362,29 @@ class RocomClient {
356
362
  if (status === 200 && data && this.isIngamePlayerPayload(data))
357
363
  return data;
358
364
  }
365
+ if (status === null && includeApiKey && this.apiKey && this.isApiKeyPermissionUndeclaredError(this.getLastError(''))) {
366
+ logger.warn('ingame/player/search rejected X-API-Key, retrying without API key');
367
+ includeApiKey = false;
368
+ headers = this.wegameHeaders('', '', '', '', false);
369
+ const retry = await this.requestWithStatus(ctx, 'POST', path, headers, {
370
+ json: payload,
371
+ acceptedStatuses: [200, 202],
372
+ });
373
+ status = retry.status;
374
+ data = retry.data;
375
+ if (status === 200 && data && this.isIngamePlayerPayload(data))
376
+ return data;
377
+ if (status === null) {
378
+ const fallback = await this.requestWithStatus(ctx, 'GET', path, headers, {
379
+ params: payload,
380
+ acceptedStatuses: [200, 202],
381
+ });
382
+ status = fallback.status;
383
+ data = fallback.data;
384
+ if (status === 200 && data && this.isIngamePlayerPayload(data))
385
+ return data;
386
+ }
387
+ }
359
388
  if (!data)
360
389
  return null;
361
390
  if (this.isIngamePlayerPayload(data))
@@ -368,7 +397,7 @@ class RocomClient {
368
397
  }
369
398
  for (let i = 0; i < 8; i++) {
370
399
  await new Promise(resolve => setTimeout(resolve, 1000));
371
- const task = await this.getIngameTask(ctx, taskId);
400
+ const task = await this.getIngameTask(ctx, taskId, includeApiKey);
372
401
  if (task.status === 200)
373
402
  return task.data;
374
403
  if (task.status === null)
@@ -404,7 +433,8 @@ class RocomClient {
404
433
  return null;
405
434
  }
406
435
  const path = '/api/v1/games/rocom/ingame/home/info';
407
- const headers = this.wegameHeaders();
436
+ let includeApiKey = true;
437
+ let headers = this.wegameHeaders();
408
438
  const payload = { uid: sanitizedUid, wait_ms: waitMs };
409
439
  let { status, data } = await this.requestWithStatus(ctx, 'POST', path, headers, {
410
440
  json: payload,
@@ -422,6 +452,29 @@ class RocomClient {
422
452
  if (status === 200 && data && !(data.task_id || data.taskId || data.taskID))
423
453
  return data;
424
454
  }
455
+ if (status === null && includeApiKey && this.apiKey && this.isApiKeyPermissionUndeclaredError(this.getLastError(''))) {
456
+ logger.warn('ingame/home/info rejected X-API-Key, retrying without API key');
457
+ includeApiKey = false;
458
+ headers = this.wegameHeaders('', '', '', '', false);
459
+ const retry = await this.requestWithStatus(ctx, 'POST', path, headers, {
460
+ json: payload,
461
+ acceptedStatuses: [200, 202],
462
+ });
463
+ status = retry.status;
464
+ data = retry.data;
465
+ if (status === 200 && data && !(data.task_id || data.taskId || data.taskID))
466
+ return data;
467
+ if (status === null) {
468
+ const fallback = await this.requestWithStatus(ctx, 'GET', path, headers, {
469
+ params: payload,
470
+ acceptedStatuses: [200, 202],
471
+ });
472
+ status = fallback.status;
473
+ data = fallback.data;
474
+ if (status === 200 && data && !(data.task_id || data.taskId || data.taskID))
475
+ return data;
476
+ }
477
+ }
425
478
  const taskId = data?.task_id || data?.taskId || data?.taskID;
426
479
  if (!taskId) {
427
480
  if (status === 202)
@@ -430,7 +483,7 @@ class RocomClient {
430
483
  }
431
484
  for (let i = 0; i < 10; i++) {
432
485
  await new Promise(resolve => setTimeout(resolve, 1000));
433
- const task = await this.getIngameTask(ctx, taskId);
486
+ const task = await this.getIngameTask(ctx, taskId, includeApiKey);
434
487
  if (task.status === 200)
435
488
  return task.data;
436
489
  if (task.status === null)
@@ -441,10 +494,22 @@ class RocomClient {
441
494
  }
442
495
  async ingameMerchantInfo(ctx, shopId) {
443
496
  const params = { shop_id: shopId };
444
- const data = await this.get(ctx, '/api/v1/games/rocom/ingame/merchant/info', this.wegameHeaders(), params, { silentFailureDetails: true });
497
+ let headers = this.wegameHeaders();
498
+ const data = await this.get(ctx, '/api/v1/games/rocom/ingame/merchant/info', headers, params, { silentFailureDetails: true });
445
499
  if (data)
446
500
  return data;
447
- return this.post(ctx, '/api/v1/games/rocom/ingame/merchant/info', this.wegameHeaders(), params);
501
+ const postData = await this.post(ctx, '/api/v1/games/rocom/ingame/merchant/info', headers, params);
502
+ if (postData)
503
+ return postData;
504
+ if (this.apiKey && this.isApiKeyPermissionUndeclaredError(this.getLastError(''))) {
505
+ logger.warn('ingame/merchant/info rejected X-API-Key, retrying without API key');
506
+ headers = this.wegameHeaders('', '', '', '', false);
507
+ const fallbackData = await this.get(ctx, '/api/v1/games/rocom/ingame/merchant/info', headers, params, { silentFailureDetails: true });
508
+ if (fallbackData)
509
+ return fallbackData;
510
+ return this.post(ctx, '/api/v1/games/rocom/ingame/merchant/info', headers, params);
511
+ }
512
+ return null;
448
513
  }
449
514
  async getFriendship(ctx, fwToken, userIds, userIdentifier = '') {
450
515
  return this.get(ctx, '/api/v1/games/rocom/social/friendship', this.rocomHeaders(fwToken, userIdentifier), { user_ids: userIds });
@@ -373,10 +373,18 @@ function homePlantIcon(deps, iconId) {
373
373
  return deps.renderer.resourceUrl(`render-templates/home/img/home_icon/${text}_2.png`);
374
374
  }
375
375
  let homePlantMapCache = null;
376
+ const HOME_PLANT_MAP_RELATIVE_PATH = node_path_1.default.join('render-templates', 'home', 'data', 'home_item_list.json');
377
+ function resolveHomePlantMapPath() {
378
+ const candidates = [
379
+ node_path_1.default.resolve(__dirname, '..', HOME_PLANT_MAP_RELATIVE_PATH),
380
+ node_path_1.default.resolve(__dirname, HOME_PLANT_MAP_RELATIVE_PATH),
381
+ ];
382
+ return candidates.find(candidate => node_fs_1.default.existsSync(candidate)) || candidates[0];
383
+ }
376
384
  function loadHomePlantMap() {
377
385
  if (homePlantMapCache)
378
386
  return homePlantMapCache;
379
- const filePath = node_path_1.default.resolve(__dirname, '..', 'render-templates', 'home', 'data', 'home_item_list.json');
387
+ const filePath = resolveHomePlantMapPath();
380
388
  try {
381
389
  const data = JSON.parse(node_fs_1.default.readFileSync(filePath, 'utf-8'));
382
390
  homePlantMapCache = data && typeof data === 'object' ? data : {};
package/lib/index.js CHANGED
@@ -59,9 +59,9 @@ var RocomClient = class {
59
59
  if (!uid) return "";
60
60
  return uid.trim().replace(/[^a-zA-Z0-9_\- \u4e00-\u9fa5]/g, "").trim();
61
61
  }
62
- wegameHeaders(fwToken = "", userIdentifier = "", clientType = "", clientId = "") {
62
+ wegameHeaders(fwToken = "", userIdentifier = "", clientType = "", clientId = "", includeApiKey = true) {
63
63
  const headers = {};
64
- if (this.apiKey) headers["X-API-Key"] = this.apiKey;
64
+ if (includeApiKey && this.apiKey) headers["X-API-Key"] = this.apiKey;
65
65
  if (fwToken) headers["X-Framework-Token"] = fwToken;
66
66
  if (userIdentifier) headers["X-User-Identifier"] = this.sanitizeUid(userIdentifier);
67
67
  if (clientType) headers["X-Client-Type"] = clientType;
@@ -123,6 +123,10 @@ var RocomClient = class {
123
123
  return String(value);
124
124
  }
125
125
  }
126
+ isApiKeyPermissionUndeclaredError(message) {
127
+ if (!message) return false;
128
+ return /未声明\s*api\s*key\s*权限|api\s*key\s*permission|api key 权限/i.test(message);
129
+ }
126
130
  logRequestFailureDetails(method, path6, headers, params, body, errorOrResponse) {
127
131
  const err = errorOrResponse;
128
132
  const response = err?.response;
@@ -265,12 +269,12 @@ ${this.stringifyForLog(details)}`);
265
269
  return { status: null, data: null };
266
270
  }
267
271
  }
268
- async getIngameTask(ctx, taskId) {
272
+ async getIngameTask(ctx, taskId, includeApiKey = true) {
269
273
  return this.requestWithStatus(
270
274
  ctx,
271
275
  "GET",
272
276
  `/api/v1/games/rocom/ingame/tasks/${taskId}`,
273
- this.wegameHeaders(),
277
+ this.wegameHeaders("", "", "", "", includeApiKey),
274
278
  { acceptedStatuses: [200, 202] }
275
279
  );
276
280
  }
@@ -358,7 +362,8 @@ ${this.stringifyForLog(details)}`);
358
362
  return null;
359
363
  }
360
364
  const path6 = "/api/v1/games/rocom/ingame/player/search";
361
- const headers = this.wegameHeaders();
365
+ let includeApiKey = true;
366
+ let headers = this.wegameHeaders();
362
367
  const payload = { uid: sanitizedUid, wait_ms: 5e3 };
363
368
  let { status, data } = await this.requestWithStatus(ctx, "POST", path6, headers, {
364
369
  json: payload,
@@ -374,6 +379,27 @@ ${this.stringifyForLog(details)}`);
374
379
  data = fallback.data;
375
380
  if (status === 200 && data && this.isIngamePlayerPayload(data)) return data;
376
381
  }
382
+ if (status === null && includeApiKey && this.apiKey && this.isApiKeyPermissionUndeclaredError(this.getLastError(""))) {
383
+ logger.warn("ingame/player/search rejected X-API-Key, retrying without API key");
384
+ includeApiKey = false;
385
+ headers = this.wegameHeaders("", "", "", "", false);
386
+ const retry = await this.requestWithStatus(ctx, "POST", path6, headers, {
387
+ json: payload,
388
+ acceptedStatuses: [200, 202]
389
+ });
390
+ status = retry.status;
391
+ data = retry.data;
392
+ if (status === 200 && data && this.isIngamePlayerPayload(data)) return data;
393
+ if (status === null) {
394
+ const fallback = await this.requestWithStatus(ctx, "GET", path6, headers, {
395
+ params: payload,
396
+ acceptedStatuses: [200, 202]
397
+ });
398
+ status = fallback.status;
399
+ data = fallback.data;
400
+ if (status === 200 && data && this.isIngamePlayerPayload(data)) return data;
401
+ }
402
+ }
377
403
  if (!data) return null;
378
404
  if (this.isIngamePlayerPayload(data)) return data;
379
405
  const taskId = data.task_id || data.taskId || data.taskID;
@@ -383,7 +409,7 @@ ${this.stringifyForLog(details)}`);
383
409
  }
384
410
  for (let i = 0; i < 8; i++) {
385
411
  await new Promise((resolve) => setTimeout(resolve, 1e3));
386
- const task = await this.getIngameTask(ctx, taskId);
412
+ const task = await this.getIngameTask(ctx, taskId, includeApiKey);
387
413
  if (task.status === 200) return task.data;
388
414
  if (task.status === null) return null;
389
415
  }
@@ -416,7 +442,8 @@ ${this.stringifyForLog(details)}`);
416
442
  return null;
417
443
  }
418
444
  const path6 = "/api/v1/games/rocom/ingame/home/info";
419
- const headers = this.wegameHeaders();
445
+ let includeApiKey = true;
446
+ let headers = this.wegameHeaders();
420
447
  const payload = { uid: sanitizedUid, wait_ms: waitMs };
421
448
  let { status, data } = await this.requestWithStatus(ctx, "POST", path6, headers, {
422
449
  json: payload,
@@ -432,6 +459,27 @@ ${this.stringifyForLog(details)}`);
432
459
  data = fallback.data;
433
460
  if (status === 200 && data && !(data.task_id || data.taskId || data.taskID)) return data;
434
461
  }
462
+ if (status === null && includeApiKey && this.apiKey && this.isApiKeyPermissionUndeclaredError(this.getLastError(""))) {
463
+ logger.warn("ingame/home/info rejected X-API-Key, retrying without API key");
464
+ includeApiKey = false;
465
+ headers = this.wegameHeaders("", "", "", "", false);
466
+ const retry = await this.requestWithStatus(ctx, "POST", path6, headers, {
467
+ json: payload,
468
+ acceptedStatuses: [200, 202]
469
+ });
470
+ status = retry.status;
471
+ data = retry.data;
472
+ if (status === 200 && data && !(data.task_id || data.taskId || data.taskID)) return data;
473
+ if (status === null) {
474
+ const fallback = await this.requestWithStatus(ctx, "GET", path6, headers, {
475
+ params: payload,
476
+ acceptedStatuses: [200, 202]
477
+ });
478
+ status = fallback.status;
479
+ data = fallback.data;
480
+ if (status === 200 && data && !(data.task_id || data.taskId || data.taskID)) return data;
481
+ }
482
+ }
435
483
  const taskId = data?.task_id || data?.taskId || data?.taskID;
436
484
  if (!taskId) {
437
485
  if (status === 202) this.setLastError("家园查询任务已入队,但未返回 task_id");
@@ -439,7 +487,7 @@ ${this.stringifyForLog(details)}`);
439
487
  }
440
488
  for (let i = 0; i < 10; i++) {
441
489
  await new Promise((resolve) => setTimeout(resolve, 1e3));
442
- const task = await this.getIngameTask(ctx, taskId);
490
+ const task = await this.getIngameTask(ctx, taskId, includeApiKey);
443
491
  if (task.status === 200) return task.data;
444
492
  if (task.status === null) return null;
445
493
  }
@@ -448,15 +496,31 @@ ${this.stringifyForLog(details)}`);
448
496
  }
449
497
  async ingameMerchantInfo(ctx, shopId) {
450
498
  const params = { shop_id: shopId };
499
+ let headers = this.wegameHeaders();
451
500
  const data = await this.get(
452
501
  ctx,
453
502
  "/api/v1/games/rocom/ingame/merchant/info",
454
- this.wegameHeaders(),
503
+ headers,
455
504
  params,
456
505
  { silentFailureDetails: true }
457
506
  );
458
507
  if (data) return data;
459
- return this.post(ctx, "/api/v1/games/rocom/ingame/merchant/info", this.wegameHeaders(), params);
508
+ const postData = await this.post(ctx, "/api/v1/games/rocom/ingame/merchant/info", headers, params);
509
+ if (postData) return postData;
510
+ if (this.apiKey && this.isApiKeyPermissionUndeclaredError(this.getLastError(""))) {
511
+ logger.warn("ingame/merchant/info rejected X-API-Key, retrying without API key");
512
+ headers = this.wegameHeaders("", "", "", "", false);
513
+ const fallbackData = await this.get(
514
+ ctx,
515
+ "/api/v1/games/rocom/ingame/merchant/info",
516
+ headers,
517
+ params,
518
+ { silentFailureDetails: true }
519
+ );
520
+ if (fallbackData) return fallbackData;
521
+ return this.post(ctx, "/api/v1/games/rocom/ingame/merchant/info", headers, params);
522
+ }
523
+ return null;
460
524
  }
461
525
  async getFriendship(ctx, fwToken, userIds, userIdentifier = "") {
462
526
  return this.get(
@@ -2323,9 +2387,18 @@ function homePlantIcon(deps, iconId) {
2323
2387
  }
2324
2388
  __name(homePlantIcon, "homePlantIcon");
2325
2389
  var homePlantMapCache = null;
2390
+ var HOME_PLANT_MAP_RELATIVE_PATH = import_node_path4.default.join("render-templates", "home", "data", "home_item_list.json");
2391
+ function resolveHomePlantMapPath() {
2392
+ const candidates = [
2393
+ import_node_path4.default.resolve(__dirname, "..", HOME_PLANT_MAP_RELATIVE_PATH),
2394
+ import_node_path4.default.resolve(__dirname, HOME_PLANT_MAP_RELATIVE_PATH)
2395
+ ];
2396
+ return candidates.find((candidate) => import_node_fs4.default.existsSync(candidate)) || candidates[0];
2397
+ }
2398
+ __name(resolveHomePlantMapPath, "resolveHomePlantMapPath");
2326
2399
  function loadHomePlantMap() {
2327
2400
  if (homePlantMapCache) return homePlantMapCache;
2328
- const filePath = import_node_path4.default.resolve(__dirname, "..", "render-templates", "home", "data", "home_item_list.json");
2401
+ const filePath = resolveHomePlantMapPath();
2329
2402
  try {
2330
2403
  const data = JSON.parse(import_node_fs4.default.readFileSync(filePath, "utf-8"));
2331
2404
  homePlantMapCache = data && typeof data === "object" ? data : {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-rocom",
3
3
  "description": "洛克王国查询与订阅插件",
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "homepage": "https://github.com/staytomorrow/koishi-plugin-rocom",