koishi-plugin-rocom 1.0.3 → 1.0.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.
package/lib/client.d.ts CHANGED
@@ -14,12 +14,12 @@ export declare class RocomClient {
14
14
  private sanitizeForLog;
15
15
  private headersForLog;
16
16
  private stringifyForLog;
17
- private isApiKeyPermissionUndeclaredError;
18
17
  private logRequestFailureDetails;
19
18
  private get;
20
19
  private post;
21
20
  private delete;
22
21
  private requestWithStatus;
22
+ private requestIngameWithFallback;
23
23
  private getIngameTask;
24
24
  qqQrLogin(ctx: Context, userIdentifier: string): Promise<any>;
25
25
  qqQrStatus(ctx: Context, fwToken: string, userIdentifier: string): Promise<any>;
package/lib/client.js CHANGED
@@ -99,11 +99,6 @@ 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
- }
107
102
  logRequestFailureDetails(method, path, headers, params, body, errorOrResponse) {
108
103
  const err = errorOrResponse;
109
104
  const response = err?.response;
@@ -235,23 +230,58 @@ class RocomClient {
235
230
  const body = response?.data !== undefined ? response.data : response;
236
231
  if (body?.code !== undefined && body.code !== 0) {
237
232
  this.setLastError(body.message || body.msg || '接口返回异常');
238
- this.logRequestFailureDetails(method, path, headers, options.params, options.json, body);
233
+ if (!options.silentFailureDetails) {
234
+ this.logRequestFailureDetails(method, path, headers, options.params, options.json, body);
235
+ }
239
236
  return { status: null, data: null };
240
237
  }
241
238
  const data = body?.code !== undefined ? (body.data ?? {}) : (body ?? {});
242
239
  if (!acceptedStatuses.includes(status)) {
243
240
  this.setLastError(`HTTP ${status}`);
244
- this.logRequestFailureDetails(method, path, headers, options.params, options.json, response);
241
+ if (!options.silentFailureDetails) {
242
+ this.logRequestFailureDetails(method, path, headers, options.params, options.json, response);
243
+ }
244
+ return { status: null, data: null };
245
245
  }
246
246
  return { status, data };
247
247
  }
248
248
  catch (e) {
249
249
  const message = this.formatHttpError(e);
250
250
  this.setLastError(message);
251
- this.logRequestFailureDetails(method, path, headers, options.params, options.json, e);
251
+ if (!options.silentFailureDetails) {
252
+ this.logRequestFailureDetails(method, path, headers, options.params, options.json, e);
253
+ }
252
254
  return { status: null, data: null };
253
255
  }
254
256
  }
257
+ async requestIngameWithFallback(ctx, path, payload, options = {}) {
258
+ const acceptedStatuses = options.acceptedStatuses || [200, 202];
259
+ const requestOnce = async (includeApiKey, silentFailureDetails) => {
260
+ const headers = this.wegameHeaders('', '', '', '', includeApiKey);
261
+ let result = await this.requestWithStatus(ctx, 'POST', path, headers, {
262
+ json: payload,
263
+ acceptedStatuses,
264
+ silentFailureDetails,
265
+ });
266
+ if (result.status === null) {
267
+ result = await this.requestWithStatus(ctx, 'GET', path, headers, {
268
+ params: payload,
269
+ acceptedStatuses,
270
+ silentFailureDetails,
271
+ });
272
+ }
273
+ return result;
274
+ };
275
+ let result = await requestOnce(false, Boolean(this.apiKey));
276
+ if (result.status !== null)
277
+ return { ...result, usedApiKey: false };
278
+ if (this.apiKey) {
279
+ result = await requestOnce(true, false);
280
+ if (result.status !== null)
281
+ return { ...result, usedApiKey: true };
282
+ }
283
+ return { status: null, data: null, usedApiKey: false };
284
+ }
255
285
  async getIngameTask(ctx, taskId, includeApiKey = true) {
256
286
  return this.requestWithStatus(ctx, 'GET', `/api/v1/games/rocom/ingame/tasks/${taskId}`, this.wegameHeaders('', '', '', '', includeApiKey), { acceptedStatuses: [200, 202] });
257
287
  }
@@ -339,52 +369,14 @@ class RocomClient {
339
369
  async ingamePlayerSearch(ctx, uid) {
340
370
  const sanitizedUid = this.sanitizeUid(uid);
341
371
  if (!sanitizedUid) {
342
- this.setLastError('UID 涓嶈兘涓虹┖');
372
+ this.setLastError('UID 不能为空');
343
373
  return null;
344
374
  }
345
375
  const path = '/api/v1/games/rocom/ingame/player/search';
346
- let includeApiKey = true;
347
- let headers = this.wegameHeaders();
348
376
  const payload = { uid: sanitizedUid, wait_ms: 5000 };
349
- let { status, data } = await this.requestWithStatus(ctx, 'POST', path, headers, {
350
- json: payload,
351
- acceptedStatuses: [200, 202],
352
- });
377
+ const { status, data, usedApiKey } = await this.requestIngameWithFallback(ctx, path, payload);
353
378
  if (status === 200 && data && this.isIngamePlayerPayload(data))
354
379
  return data;
355
- if (status === null) {
356
- const fallback = await this.requestWithStatus(ctx, 'GET', path, headers, {
357
- params: payload,
358
- acceptedStatuses: [200, 202],
359
- });
360
- status = fallback.status;
361
- data = fallback.data;
362
- if (status === 200 && data && this.isIngamePlayerPayload(data))
363
- return data;
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
- }
388
380
  if (!data)
389
381
  return null;
390
382
  if (this.isIngamePlayerPayload(data))
@@ -397,7 +389,7 @@ class RocomClient {
397
389
  }
398
390
  for (let i = 0; i < 8; i++) {
399
391
  await new Promise(resolve => setTimeout(resolve, 1000));
400
- const task = await this.getIngameTask(ctx, taskId, includeApiKey);
392
+ const task = await this.getIngameTask(ctx, taskId, usedApiKey);
401
393
  if (task.status === 200)
402
394
  return task.data;
403
395
  if (task.status === null)
@@ -429,52 +421,14 @@ class RocomClient {
429
421
  async ingameHomeInfo(ctx, uid, waitMs = 5000) {
430
422
  const sanitizedUid = this.sanitizeUid(uid);
431
423
  if (!sanitizedUid) {
432
- this.setLastError('UID 涓嶈兘涓虹┖');
424
+ this.setLastError('UID 不能为空');
433
425
  return null;
434
426
  }
435
427
  const path = '/api/v1/games/rocom/ingame/home/info';
436
- let includeApiKey = true;
437
- let headers = this.wegameHeaders();
438
428
  const payload = { uid: sanitizedUid, wait_ms: waitMs };
439
- let { status, data } = await this.requestWithStatus(ctx, 'POST', path, headers, {
440
- json: payload,
441
- acceptedStatuses: [200, 202],
442
- });
429
+ const { status, data, usedApiKey } = await this.requestIngameWithFallback(ctx, path, payload);
443
430
  if (status === 200 && data && !(data.task_id || data.taskId || data.taskID))
444
431
  return data;
445
- if (status === null) {
446
- const fallback = await this.requestWithStatus(ctx, 'GET', path, headers, {
447
- params: payload,
448
- acceptedStatuses: [200, 202],
449
- });
450
- status = fallback.status;
451
- data = fallback.data;
452
- if (status === 200 && data && !(data.task_id || data.taskId || data.taskID))
453
- return data;
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
- }
478
432
  const taskId = data?.task_id || data?.taskId || data?.taskID;
479
433
  if (!taskId) {
480
434
  if (status === 202)
@@ -483,7 +437,7 @@ class RocomClient {
483
437
  }
484
438
  for (let i = 0; i < 10; i++) {
485
439
  await new Promise(resolve => setTimeout(resolve, 1000));
486
- const task = await this.getIngameTask(ctx, taskId, includeApiKey);
440
+ const task = await this.getIngameTask(ctx, taskId, usedApiKey);
487
441
  if (task.status === 200)
488
442
  return task.data;
489
443
  if (task.status === null)
@@ -494,22 +448,8 @@ class RocomClient {
494
448
  }
495
449
  async ingameMerchantInfo(ctx, shopId) {
496
450
  const params = { shop_id: shopId };
497
- let headers = this.wegameHeaders();
498
- const data = await this.get(ctx, '/api/v1/games/rocom/ingame/merchant/info', headers, params, { silentFailureDetails: true });
499
- if (data)
500
- return data;
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;
451
+ const { status, data } = await this.requestIngameWithFallback(ctx, '/api/v1/games/rocom/ingame/merchant/info', params);
452
+ return status === null ? null : data;
513
453
  }
514
454
  async getFriendship(ctx, fwToken, userIds, userIdentifier = '') {
515
455
  return this.get(ctx, '/api/v1/games/rocom/social/friendship', this.rocomHeaders(fwToken, userIdentifier), { user_ids: userIds });
@@ -66,6 +66,7 @@ async function saveBindingWithRoleInfo(deps, session, fwToken, loginType, userId
66
66
  function register(deps) {
67
67
  const { ctx, client, userMgr } = deps;
68
68
  ctx.command('洛克').subcommand('.QQ登录', 'QQ 扫码登录')
69
+ .alias('洛克QQ登录')
69
70
  .action(async ({ session }) => {
70
71
  const userId = session.userId;
71
72
  const qrData = await client.qqQrLogin(ctx, userId);
@@ -91,6 +92,7 @@ function register(deps) {
91
92
  return '登录超时或失败,请重试。';
92
93
  });
93
94
  ctx.command('洛克').subcommand('.微信登录', '微信扫码登录')
95
+ .alias('洛克微信登录')
94
96
  .action(async ({ session }) => {
95
97
  const userId = session.userId;
96
98
  const qrData = await client.wechatQrLogin(ctx, userId);
@@ -115,6 +117,7 @@ function register(deps) {
115
117
  return '登录超时或失败,请重试。';
116
118
  });
117
119
  ctx.command('洛克').subcommand('.导入 <tgpId:string> <tgpTicket:string>', '导入 WeGame 凭证')
120
+ .alias('洛克导入')
118
121
  .action(async ({ session }, tgpId, tgpTicket) => {
119
122
  if (!tgpId || !tgpTicket)
120
123
  return '用法:洛克.导入 <tgp_id> <tgp_ticket>';
@@ -125,6 +128,7 @@ function register(deps) {
125
128
  await saveBindingWithRoleInfo(deps, session, res.frameworkToken, 'manual', userId);
126
129
  });
127
130
  ctx.command('洛克').subcommand('.绑定列表', '查看已绑定账号')
131
+ .alias('洛克绑定列表')
128
132
  .action(async ({ session }) => {
129
133
  const bindings = userMgr.getUserBindings(session.userId);
130
134
  if (!bindings.length)
@@ -153,6 +157,7 @@ function register(deps) {
153
157
  await (0, send_image_1.sendImageWithFallback)(session, png, fallbackLines.join('\n'), 'account:bind-list', deps.config);
154
158
  });
155
159
  ctx.command('洛克').subcommand('.切换 <index:number>', '切换主账号')
160
+ .alias('洛克切换')
156
161
  .action(async ({ session }, index) => {
157
162
  if (!index)
158
163
  return '用法:洛克.切换 <序号>';
@@ -161,6 +166,7 @@ function register(deps) {
161
166
  : '序号无效。';
162
167
  });
163
168
  ctx.command('洛克').subcommand('.解绑 <index:number>', '解绑账号')
169
+ .alias('洛克解绑')
164
170
  .action(async ({ session }, index) => {
165
171
  if (!index)
166
172
  return '用法:洛克.解绑 <序号>';
@@ -181,6 +187,7 @@ function register(deps) {
181
187
  return `已解绑账号:${removed.nickname}`;
182
188
  });
183
189
  ctx.command('洛克').subcommand('.刷新', '刷新当前主账号凭证')
190
+ .alias('洛克刷新')
184
191
  .action(async ({ session }) => {
185
192
  const userId = session.userId;
186
193
  const binding = userMgr.getPrimaryBinding(userId);
@@ -10,6 +10,7 @@ function register(deps) {
10
10
  return config.adminUserIds.includes(userId);
11
11
  }
12
12
  ctx.command('洛克').subcommand('.刷新所有凭证', '刷新所有用户凭证(管理员)')
13
+ .alias('洛克刷新所有凭证')
13
14
  .action(async ({ session }) => {
14
15
  if (!isAdmin(session.userId))
15
16
  return '此指令仅限管理员使用。';
@@ -44,6 +45,7 @@ function register(deps) {
44
45
  return `刷新完成:成功 ${success},失败 ${fail}`;
45
46
  });
46
47
  ctx.command('洛克').subcommand('.删除失效绑定', '清理失效绑定(管理员)')
48
+ .alias('洛克删除失效绑定')
47
49
  .action(async ({ session }) => {
48
50
  if (!isAdmin(session.userId))
49
51
  return '此指令仅限管理员使用。';
@@ -48,6 +48,7 @@ async function sendEggImage(deps, session, templateName, data, fallback) {
48
48
  function register(deps) {
49
49
  const { ctx, client, eggService } = deps;
50
50
  ctx.command('洛克').subcommand('.查蛋 [arg1:string] [arg2:string]', '查询精灵蛋组')
51
+ .alias('洛克查蛋')
51
52
  .action(async ({ session }, arg1, arg2) => {
52
53
  if (!arg1) {
53
54
  return [
@@ -143,6 +144,7 @@ function register(deps) {
143
144
  await sendEggImage(deps, session, 'searcheggs', data, hint + eggService.buildSearchText(pet));
144
145
  });
145
146
  ctx.command('洛克').subcommand('.配种 <nameA:string> [nameB:string]', '配种查询')
147
+ .alias('洛克配种')
146
148
  .action(async ({ session }, nameA, nameB) => {
147
149
  if (!nameA) {
148
150
  return [
@@ -228,6 +228,7 @@ function register(deps) {
228
228
  return '\u2705 \u5df2\u53d6\u6d88\u8fdc\u884c\u5546\u4eba\u8ba2\u9605\u3002';
229
229
  });
230
230
  ctx.command('洛克').subcommand('.调试远行商人订阅', '立即执行一次远行商人订阅检查')
231
+ .alias('洛克调试远行商人订阅')
231
232
  .action(async ({ session }) => {
232
233
  if (!isBotAdmin(session, config.adminUserIds))
233
234
  return '此指令仅限管理员使用。';
@@ -777,6 +777,7 @@ async function checkHomeSubscriptions(deps) {
777
777
  function register(deps) {
778
778
  const { ctx, client } = deps;
779
779
  ctx.command('洛克').subcommand('.档案', '查看个人档案')
780
+ .alias('洛克档案')
780
781
  .action(async ({ session }) => {
781
782
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
782
783
  if (!fwToken)
@@ -952,6 +953,7 @@ function register(deps) {
952
953
  await sendImage(deps, session, 'personal-card', data, fallback);
953
954
  });
954
955
  ctx.command('洛克').subcommand('.战绩 [page:number]', '查看对战战绩')
956
+ .alias('洛克战绩')
955
957
  .action(async ({ session }, _page = 1) => {
956
958
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
957
959
  if (!fwToken)
@@ -999,6 +1001,7 @@ function register(deps) {
999
1001
  await sendImage(deps, session, 'record', data, `【${role.name}的战绩】胜率:${bo.win_rate || 0}% 场次:${bo.total_match || 0}`);
1000
1002
  });
1001
1003
  ctx.command('洛克').subcommand('.背包 [arg1:string] [arg2:string]', '查看精灵背包')
1004
+ .alias('洛克背包')
1002
1005
  .action(async ({ session }, arg1, arg2) => {
1003
1006
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
1004
1007
  if (!fwToken)
@@ -1058,6 +1061,7 @@ function register(deps) {
1058
1061
  await sendImage(deps, session, 'package', data, `【背包 - ${category}精灵】共${petRes.total || 0}只`);
1059
1062
  });
1060
1063
  ctx.command('洛克').subcommand('.阵容 [arg1:string] [arg2:string]', '查看阵容推荐')
1064
+ .alias('洛克阵容')
1061
1065
  .action(async ({ session }, arg1, arg2) => {
1062
1066
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
1063
1067
  if (!fwToken)
@@ -1147,6 +1151,7 @@ function register(deps) {
1147
1151
  await sendImage(deps, session, 'lineup-detail', data, fallback);
1148
1152
  });
1149
1153
  ctx.command('洛克').subcommand('.交换大厅 [page:number]', '查看交换大厅')
1154
+ .alias('洛克交换大厅')
1150
1155
  .action(async ({ session }, page = 1) => {
1151
1156
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
1152
1157
  if (!fwToken)
@@ -1187,6 +1192,7 @@ function register(deps) {
1187
1192
  await sendImage(deps, session, 'exchange-hall', data, `【交换大厅】第${page}页`);
1188
1193
  });
1189
1194
  ctx.command('洛克').subcommand('.玩家 <uid:string>', '通过 ingame 接口查询玩家基础资料')
1195
+ .alias('洛克玩家')
1190
1196
  .action(async ({ session }, uid) => {
1191
1197
  if (!uid)
1192
1198
  return '请提供玩家 UID。用法:洛克.玩家 <UID>';
@@ -1196,6 +1202,7 @@ function register(deps) {
1196
1202
  await sendImage(deps, session, 'player-search', buildPlayerSearchRenderData(res, uid), `【洛克玩家】UID ${uid}`);
1197
1203
  });
1198
1204
  ctx.command('洛克').subcommand('.家园 [uid:string]', '通过 UID 查询家园菜园、守卫和室内精灵')
1205
+ .alias('洛克家园')
1199
1206
  .action(async ({ session }, uid = '') => {
1200
1207
  let targetUid = String(uid || '').trim();
1201
1208
  if (!targetUid) {
@@ -1210,6 +1217,7 @@ function register(deps) {
1210
1217
  await sendImage(deps, session, 'home', buildHomeRenderData(deps, res, targetUid), `【洛克家园】UID ${targetUid}`);
1211
1218
  });
1212
1219
  ctx.command('洛克').subcommand('.商店 <shopId:string>', '通过 ingame 接口查询商店信息')
1220
+ .alias('洛克商店')
1213
1221
  .action(async ({ session }, shopId) => {
1214
1222
  if (!shopId)
1215
1223
  return '请提供商店 ID。用法:洛克.商店 <shop_id>';
@@ -1219,6 +1227,7 @@ function register(deps) {
1219
1227
  await sendImage(deps, session, 'ingame-shop', buildShopRenderData(res, shopId), `【洛克商店】shop_id=${shopId}`);
1220
1228
  });
1221
1229
  ctx.command('洛克').subcommand('.好友关系 <userIds:string>', '查询好友关系')
1230
+ .alias('洛克好友关系')
1222
1231
  .action(async ({ session }, userIds) => {
1223
1232
  if (!userIds)
1224
1233
  return '请提供要查询的用户 ID 列表。用法:洛克.好友关系 <id1,id2>';
@@ -1231,6 +1240,7 @@ function register(deps) {
1231
1240
  await sendImage(deps, session, 'friendship', buildFriendshipRenderData(res, userIds), `【好友关系】${userIds}`);
1232
1241
  });
1233
1242
  ctx.command('洛克').subcommand('.学生 [area:number] [accountType:number]', '查询学生认证状态与学生活动福利')
1243
+ .alias('洛克学生')
1234
1244
  .action(async ({ session }, area = 101, accountType = 0) => {
1235
1245
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
1236
1246
  if (!fwToken)
@@ -1260,6 +1270,7 @@ function register(deps) {
1260
1270
  return deleted ? `已取消 ${deleted} 条家园订阅。` : '当前会话没有匹配的家园订阅。';
1261
1271
  });
1262
1272
  ctx.command('洛克').subcommand('.调试家园订阅', '立即执行一次家园订阅检查')
1273
+ .alias('洛克调试家园订阅')
1263
1274
  .action(async ({ session }) => {
1264
1275
  if (!isBotAdmin(session, deps.config.adminUserIds))
1265
1276
  return '此指令仅限管理员使用。';
@@ -5,7 +5,9 @@ const WIKI_CLOSED_MESSAGE = '该功能暂时关闭';
5
5
  function register(deps) {
6
6
  const { ctx } = deps;
7
7
  ctx.command('洛克').subcommand('.wiki <name:text>', '查询精灵 wiki')
8
+ .alias('洛克wiki')
8
9
  .action(() => WIKI_CLOSED_MESSAGE);
9
10
  ctx.command('洛克').subcommand('.技能 <name:text>', '查询技能 wiki')
11
+ .alias('洛克技能')
10
12
  .action(() => WIKI_CLOSED_MESSAGE);
11
13
  }
package/lib/index.js CHANGED
@@ -37,6 +37,7 @@ __export(src_exports, {
37
37
  });
38
38
  module.exports = __toCommonJS(src_exports);
39
39
  var import_koishi11 = require("koishi");
40
+ var import_node_fs5 = __toESM(require("node:fs"));
40
41
  var import_node_path5 = __toESM(require("node:path"));
41
42
 
42
43
  // src/client.ts
@@ -123,10 +124,6 @@ var RocomClient = class {
123
124
  return String(value);
124
125
  }
125
126
  }
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
- }
130
127
  logRequestFailureDetails(method, path6, headers, params, body, errorOrResponse) {
131
128
  const err = errorOrResponse;
132
129
  const response = err?.response;
@@ -253,22 +250,55 @@ ${this.stringifyForLog(details)}`);
253
250
  const body = response?.data !== void 0 ? response.data : response;
254
251
  if (body?.code !== void 0 && body.code !== 0) {
255
252
  this.setLastError(body.message || body.msg || "接口返回异常");
256
- this.logRequestFailureDetails(method, path6, headers, options.params, options.json, body);
253
+ if (!options.silentFailureDetails) {
254
+ this.logRequestFailureDetails(method, path6, headers, options.params, options.json, body);
255
+ }
257
256
  return { status: null, data: null };
258
257
  }
259
258
  const data = body?.code !== void 0 ? body.data ?? {} : body ?? {};
260
259
  if (!acceptedStatuses.includes(status)) {
261
260
  this.setLastError(`HTTP ${status}`);
262
- this.logRequestFailureDetails(method, path6, headers, options.params, options.json, response);
261
+ if (!options.silentFailureDetails) {
262
+ this.logRequestFailureDetails(method, path6, headers, options.params, options.json, response);
263
+ }
264
+ return { status: null, data: null };
263
265
  }
264
266
  return { status, data };
265
267
  } catch (e) {
266
268
  const message = this.formatHttpError(e);
267
269
  this.setLastError(message);
268
- this.logRequestFailureDetails(method, path6, headers, options.params, options.json, e);
270
+ if (!options.silentFailureDetails) {
271
+ this.logRequestFailureDetails(method, path6, headers, options.params, options.json, e);
272
+ }
269
273
  return { status: null, data: null };
270
274
  }
271
275
  }
276
+ async requestIngameWithFallback(ctx, path6, payload, options = {}) {
277
+ const acceptedStatuses = options.acceptedStatuses || [200, 202];
278
+ const requestOnce = /* @__PURE__ */ __name(async (includeApiKey, silentFailureDetails) => {
279
+ const headers = this.wegameHeaders("", "", "", "", includeApiKey);
280
+ let result2 = await this.requestWithStatus(ctx, "POST", path6, headers, {
281
+ json: payload,
282
+ acceptedStatuses,
283
+ silentFailureDetails
284
+ });
285
+ if (result2.status === null) {
286
+ result2 = await this.requestWithStatus(ctx, "GET", path6, headers, {
287
+ params: payload,
288
+ acceptedStatuses,
289
+ silentFailureDetails
290
+ });
291
+ }
292
+ return result2;
293
+ }, "requestOnce");
294
+ let result = await requestOnce(false, Boolean(this.apiKey));
295
+ if (result.status !== null) return { ...result, usedApiKey: false };
296
+ if (this.apiKey) {
297
+ result = await requestOnce(true, false);
298
+ if (result.status !== null) return { ...result, usedApiKey: true };
299
+ }
300
+ return { status: null, data: null, usedApiKey: false };
301
+ }
272
302
  async getIngameTask(ctx, taskId, includeApiKey = true) {
273
303
  return this.requestWithStatus(
274
304
  ctx,
@@ -358,48 +388,13 @@ ${this.stringifyForLog(details)}`);
358
388
  async ingamePlayerSearch(ctx, uid) {
359
389
  const sanitizedUid = this.sanitizeUid(uid);
360
390
  if (!sanitizedUid) {
361
- this.setLastError("UID 涓嶈兘涓虹┖");
391
+ this.setLastError("UID 不能为空");
362
392
  return null;
363
393
  }
364
394
  const path6 = "/api/v1/games/rocom/ingame/player/search";
365
- let includeApiKey = true;
366
- let headers = this.wegameHeaders();
367
395
  const payload = { uid: sanitizedUid, wait_ms: 5e3 };
368
- let { status, data } = await this.requestWithStatus(ctx, "POST", path6, headers, {
369
- json: payload,
370
- acceptedStatuses: [200, 202]
371
- });
396
+ const { status, data, usedApiKey } = await this.requestIngameWithFallback(ctx, path6, payload);
372
397
  if (status === 200 && data && this.isIngamePlayerPayload(data)) return data;
373
- if (status === null) {
374
- const fallback = await this.requestWithStatus(ctx, "GET", path6, headers, {
375
- params: payload,
376
- acceptedStatuses: [200, 202]
377
- });
378
- status = fallback.status;
379
- data = fallback.data;
380
- if (status === 200 && data && this.isIngamePlayerPayload(data)) return data;
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
- }
403
398
  if (!data) return null;
404
399
  if (this.isIngamePlayerPayload(data)) return data;
405
400
  const taskId = data.task_id || data.taskId || data.taskID;
@@ -409,7 +404,7 @@ ${this.stringifyForLog(details)}`);
409
404
  }
410
405
  for (let i = 0; i < 8; i++) {
411
406
  await new Promise((resolve) => setTimeout(resolve, 1e3));
412
- const task = await this.getIngameTask(ctx, taskId, includeApiKey);
407
+ const task = await this.getIngameTask(ctx, taskId, usedApiKey);
413
408
  if (task.status === 200) return task.data;
414
409
  if (task.status === null) return null;
415
410
  }
@@ -438,48 +433,13 @@ ${this.stringifyForLog(details)}`);
438
433
  async ingameHomeInfo(ctx, uid, waitMs = 5e3) {
439
434
  const sanitizedUid = this.sanitizeUid(uid);
440
435
  if (!sanitizedUid) {
441
- this.setLastError("UID 涓嶈兘涓虹┖");
436
+ this.setLastError("UID 不能为空");
442
437
  return null;
443
438
  }
444
439
  const path6 = "/api/v1/games/rocom/ingame/home/info";
445
- let includeApiKey = true;
446
- let headers = this.wegameHeaders();
447
440
  const payload = { uid: sanitizedUid, wait_ms: waitMs };
448
- let { status, data } = await this.requestWithStatus(ctx, "POST", path6, headers, {
449
- json: payload,
450
- acceptedStatuses: [200, 202]
451
- });
441
+ const { status, data, usedApiKey } = await this.requestIngameWithFallback(ctx, path6, payload);
452
442
  if (status === 200 && data && !(data.task_id || data.taskId || data.taskID)) return data;
453
- if (status === null) {
454
- const fallback = await this.requestWithStatus(ctx, "GET", path6, headers, {
455
- params: payload,
456
- acceptedStatuses: [200, 202]
457
- });
458
- status = fallback.status;
459
- data = fallback.data;
460
- if (status === 200 && data && !(data.task_id || data.taskId || data.taskID)) return data;
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
- }
483
443
  const taskId = data?.task_id || data?.taskId || data?.taskID;
484
444
  if (!taskId) {
485
445
  if (status === 202) this.setLastError("家园查询任务已入队,但未返回 task_id");
@@ -487,7 +447,7 @@ ${this.stringifyForLog(details)}`);
487
447
  }
488
448
  for (let i = 0; i < 10; i++) {
489
449
  await new Promise((resolve) => setTimeout(resolve, 1e3));
490
- const task = await this.getIngameTask(ctx, taskId, includeApiKey);
450
+ const task = await this.getIngameTask(ctx, taskId, usedApiKey);
491
451
  if (task.status === 200) return task.data;
492
452
  if (task.status === null) return null;
493
453
  }
@@ -496,31 +456,12 @@ ${this.stringifyForLog(details)}`);
496
456
  }
497
457
  async ingameMerchantInfo(ctx, shopId) {
498
458
  const params = { shop_id: shopId };
499
- let headers = this.wegameHeaders();
500
- const data = await this.get(
459
+ const { status, data } = await this.requestIngameWithFallback(
501
460
  ctx,
502
461
  "/api/v1/games/rocom/ingame/merchant/info",
503
- headers,
504
- params,
505
- { silentFailureDetails: true }
462
+ params
506
463
  );
507
- if (data) return data;
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;
464
+ return status === null ? null : data;
524
465
  }
525
466
  async getFriendship(ctx, fwToken, userIds, userIdentifier = "") {
526
467
  return this.get(
@@ -1493,6 +1434,7 @@ var Renderer = class {
1493
1434
  ".student-state-page",
1494
1435
  ".student-perks-page",
1495
1436
  ".student-page",
1437
+ ".merchant-page",
1496
1438
  ".home-page"
1497
1439
  ];
1498
1440
  let target = null;
@@ -1880,7 +1822,7 @@ async function saveBindingWithRoleInfo(deps, session, fwToken, loginType, userId
1880
1822
  __name(saveBindingWithRoleInfo, "saveBindingWithRoleInfo");
1881
1823
  function register(deps) {
1882
1824
  const { ctx, client, userMgr } = deps;
1883
- ctx.command("洛克").subcommand(".QQ登录", "QQ 扫码登录").action(async ({ session }) => {
1825
+ ctx.command("洛克").subcommand(".QQ登录", "QQ 扫码登录").alias("洛克QQ登录").action(async ({ session }) => {
1884
1826
  const userId = session.userId;
1885
1827
  const qrData = await client.qqQrLogin(ctx, userId);
1886
1828
  if (!qrData?.qr_image) return "获取 QQ 二维码失败。";
@@ -1907,7 +1849,7 @@ function register(deps) {
1907
1849
  }
1908
1850
  return "登录超时或失败,请重试。";
1909
1851
  });
1910
- ctx.command("洛克").subcommand(".微信登录", "微信扫码登录").action(async ({ session }) => {
1852
+ ctx.command("洛克").subcommand(".微信登录", "微信扫码登录").alias("洛克微信登录").action(async ({ session }) => {
1911
1853
  const userId = session.userId;
1912
1854
  const qrData = await client.wechatQrLogin(ctx, userId);
1913
1855
  if (!qrData?.qr_image) return "获取微信登录链接失败。";
@@ -1929,14 +1871,14 @@ ${qrUrl}`);
1929
1871
  }
1930
1872
  return "登录超时或失败,请重试。";
1931
1873
  });
1932
- ctx.command("洛克").subcommand(".导入 <tgpId:string> <tgpTicket:string>", "导入 WeGame 凭证").action(async ({ session }, tgpId, tgpTicket) => {
1874
+ ctx.command("洛克").subcommand(".导入 <tgpId:string> <tgpTicket:string>", "导入 WeGame 凭证").alias("洛克导入").action(async ({ session }, tgpId, tgpTicket) => {
1933
1875
  if (!tgpId || !tgpTicket) return "用法:洛克.导入 <tgp_id> <tgp_ticket>";
1934
1876
  const userId = session.userId;
1935
1877
  const res = await client.importToken(ctx, tgpId, tgpTicket, userId);
1936
1878
  if (!res?.frameworkToken) return "凭证导入失败。";
1937
1879
  await saveBindingWithRoleInfo(deps, session, res.frameworkToken, "manual", userId);
1938
1880
  });
1939
- ctx.command("洛克").subcommand(".绑定列表", "查看已绑定账号").action(async ({ session }) => {
1881
+ ctx.command("洛克").subcommand(".绑定列表", "查看已绑定账号").alias("洛克绑定列表").action(async ({ session }) => {
1940
1882
  const bindings = userMgr.getUserBindings(session.userId);
1941
1883
  if (!bindings.length) return "暂无绑定账号。";
1942
1884
  const bindItems = bindings.map((binding, index) => ({
@@ -1962,11 +1904,11 @@ ${qrUrl}`);
1962
1904
  const png = await deps.renderer.renderHtml(ctx, "bind-list", data);
1963
1905
  await sendImageWithFallback(session, png, fallbackLines.join("\n"), "account:bind-list", deps.config);
1964
1906
  });
1965
- ctx.command("洛克").subcommand(".切换 <index:number>", "切换主账号").action(async ({ session }, index) => {
1907
+ ctx.command("洛克").subcommand(".切换 <index:number>", "切换主账号").alias("洛克切换").action(async ({ session }, index) => {
1966
1908
  if (!index) return "用法:洛克.切换 <序号>";
1967
1909
  return userMgr.switchPrimary(session.userId, index) ? `成功切换到序号 ${index} 账号。` : "序号无效。";
1968
1910
  });
1969
- ctx.command("洛克").subcommand(".解绑 <index:number>", "解绑账号").action(async ({ session }, index) => {
1911
+ ctx.command("洛克").subcommand(".解绑 <index:number>", "解绑账号").alias("洛克解绑").action(async ({ session }, index) => {
1970
1912
  if (!index) return "用法:洛克.解绑 <序号>";
1971
1913
  const removed = userMgr.deleteUserBinding(session.userId, index);
1972
1914
  if (!removed) return "序号无效。";
@@ -1981,7 +1923,7 @@ ${qrUrl}`);
1981
1923
  }
1982
1924
  return `已解绑账号:${removed.nickname}`;
1983
1925
  });
1984
- ctx.command("洛克").subcommand(".刷新", "刷新当前主账号凭证").action(async ({ session }) => {
1926
+ ctx.command("洛克").subcommand(".刷新", "刷新当前主账号凭证").alias("洛克刷新").action(async ({ session }) => {
1985
1927
  const userId = session.userId;
1986
1928
  const binding = userMgr.getPrimaryBinding(userId);
1987
1929
  if (!binding) return notLoggedInHint();
@@ -2772,7 +2714,7 @@ async function checkHomeSubscriptions(deps) {
2772
2714
  __name(checkHomeSubscriptions, "checkHomeSubscriptions");
2773
2715
  function register2(deps) {
2774
2716
  const { ctx, client } = deps;
2775
- ctx.command("洛克").subcommand(".档案", "查看个人档案").action(async ({ session }) => {
2717
+ ctx.command("洛克").subcommand(".档案", "查看个人档案").alias("洛克档案").action(async ({ session }) => {
2776
2718
  const fwToken = await getPrimaryToken(deps, session.userId);
2777
2719
  if (!fwToken) return notLoggedInHint();
2778
2720
  const userIdentifier = session.userId;
@@ -2940,7 +2882,7 @@ function register2(deps) {
2940
2882
  评分:${ev.score || "0"} 收藏:${cl.current_collection_count || 0}/${cl.total_collection_count || 0}`;
2941
2883
  await sendImage(deps, session, "personal-card", data, fallback);
2942
2884
  });
2943
- ctx.command("洛克").subcommand(".战绩 [page:number]", "查看对战战绩").action(async ({ session }, _page = 1) => {
2885
+ ctx.command("洛克").subcommand(".战绩 [page:number]", "查看对战战绩").alias("洛克战绩").action(async ({ session }, _page = 1) => {
2944
2886
  const fwToken = await getPrimaryToken(deps, session.userId);
2945
2887
  if (!fwToken) return notLoggedInHint();
2946
2888
  const userIdentifier = session.userId;
@@ -2984,7 +2926,7 @@ function register2(deps) {
2984
2926
  };
2985
2927
  await sendImage(deps, session, "record", data, `【${role.name}的战绩】胜率:${bo.win_rate || 0}% 场次:${bo.total_match || 0}`);
2986
2928
  });
2987
- ctx.command("洛克").subcommand(".背包 [arg1:string] [arg2:string]", "查看精灵背包").action(async ({ session }, arg1, arg2) => {
2929
+ ctx.command("洛克").subcommand(".背包 [arg1:string] [arg2:string]", "查看精灵背包").alias("洛克背包").action(async ({ session }, arg1, arg2) => {
2988
2930
  const fwToken = await getPrimaryToken(deps, session.userId);
2989
2931
  if (!fwToken) return notLoggedInHint();
2990
2932
  const catMap = { "全部": 0, "了不起": 1, "异色": 2, "炫彩": 3 };
@@ -3036,7 +2978,7 @@ function register2(deps) {
3036
2978
  };
3037
2979
  await sendImage(deps, session, "package", data, `【背包 - ${category}精灵】共${petRes.total || 0}只`);
3038
2980
  });
3039
- ctx.command("洛克").subcommand(".阵容 [arg1:string] [arg2:string]", "查看阵容推荐").action(async ({ session }, arg1, arg2) => {
2981
+ ctx.command("洛克").subcommand(".阵容 [arg1:string] [arg2:string]", "查看阵容推荐").alias("洛克阵容").action(async ({ session }, arg1, arg2) => {
3040
2982
  const fwToken = await getPrimaryToken(deps, session.userId);
3041
2983
  if (!fwToken) return notLoggedInHint();
3042
2984
  const userIdentifier = session.userId;
@@ -3113,7 +3055,7 @@ function register2(deps) {
3113
3055
  const fallback = `【阵容详情】${targetLineup.name || "未知阵容"} | 阵容码: ${normalizedLineupId}`;
3114
3056
  await sendImage(deps, session, "lineup-detail", data, fallback);
3115
3057
  });
3116
- ctx.command("洛克").subcommand(".交换大厅 [page:number]", "查看交换大厅").action(async ({ session }, page = 1) => {
3058
+ ctx.command("洛克").subcommand(".交换大厅 [page:number]", "查看交换大厅").alias("洛克交换大厅").action(async ({ session }, page = 1) => {
3117
3059
  const fwToken = await getPrimaryToken(deps, session.userId);
3118
3060
  if (!fwToken) return notLoggedInHint();
3119
3061
  const userIdentifier = session.userId;
@@ -3152,13 +3094,13 @@ function register2(deps) {
3152
3094
  };
3153
3095
  await sendImage(deps, session, "exchange-hall", data, `【交换大厅】第${page}页`);
3154
3096
  });
3155
- ctx.command("洛克").subcommand(".玩家 <uid:string>", "通过 ingame 接口查询玩家基础资料").action(async ({ session }, uid) => {
3097
+ ctx.command("洛克").subcommand(".玩家 <uid:string>", "通过 ingame 接口查询玩家基础资料").alias("洛克玩家").action(async ({ session }, uid) => {
3156
3098
  if (!uid) return "请提供玩家 UID。用法:洛克.玩家 <UID>";
3157
3099
  const res = await client.ingamePlayerSearch(ctx, uid);
3158
3100
  if (!res) return `玩家搜索失败:${client.getLastError()}`;
3159
3101
  await sendImage(deps, session, "player-search", buildPlayerSearchRenderData(res, uid), `【洛克玩家】UID ${uid}`);
3160
3102
  });
3161
- ctx.command("洛克").subcommand(".家园 [uid:string]", "通过 UID 查询家园菜园、守卫和室内精灵").action(async ({ session }, uid = "") => {
3103
+ ctx.command("洛克").subcommand(".家园 [uid:string]", "通过 UID 查询家园菜园、守卫和室内精灵").alias("洛克家园").action(async ({ session }, uid = "") => {
3162
3104
  let targetUid = String(uid || "").trim();
3163
3105
  if (!targetUid) {
3164
3106
  const binding = deps.userMgr.getPrimaryBinding(session.userId);
@@ -3169,13 +3111,13 @@ function register2(deps) {
3169
3111
  if (!res) return `家园查询失败:${client.getLastError()}`;
3170
3112
  await sendImage(deps, session, "home", buildHomeRenderData(deps, res, targetUid), `【洛克家园】UID ${targetUid}`);
3171
3113
  });
3172
- ctx.command("洛克").subcommand(".商店 <shopId:string>", "通过 ingame 接口查询商店信息").action(async ({ session }, shopId) => {
3114
+ ctx.command("洛克").subcommand(".商店 <shopId:string>", "通过 ingame 接口查询商店信息").alias("洛克商店").action(async ({ session }, shopId) => {
3173
3115
  if (!shopId) return "请提供商店 ID。用法:洛克.商店 <shop_id>";
3174
3116
  const res = await client.ingameMerchantInfo(ctx, shopId);
3175
3117
  if (!res) return `商店查询失败:${client.getLastError()}`;
3176
3118
  await sendImage(deps, session, "ingame-shop", buildShopRenderData(res, shopId), `【洛克商店】shop_id=${shopId}`);
3177
3119
  });
3178
- ctx.command("洛克").subcommand(".好友关系 <userIds:string>", "查询好友关系").action(async ({ session }, userIds) => {
3120
+ ctx.command("洛克").subcommand(".好友关系 <userIds:string>", "查询好友关系").alias("洛克好友关系").action(async ({ session }, userIds) => {
3179
3121
  if (!userIds) return "请提供要查询的用户 ID 列表。用法:洛克.好友关系 <id1,id2>";
3180
3122
  const fwToken = await getPrimaryToken(deps, session.userId);
3181
3123
  if (!fwToken) return notLoggedInHint();
@@ -3183,7 +3125,7 @@ function register2(deps) {
3183
3125
  if (!res) return `好友关系查询失败:${client.getLastError()}`;
3184
3126
  await sendImage(deps, session, "friendship", buildFriendshipRenderData(res, userIds), `【好友关系】${userIds}`);
3185
3127
  });
3186
- ctx.command("洛克").subcommand(".学生 [area:number] [accountType:number]", "查询学生认证状态与学生活动福利").action(async ({ session }, area = 101, accountType = 0) => {
3128
+ ctx.command("洛克").subcommand(".学生 [area:number] [accountType:number]", "查询学生认证状态与学生活动福利").alias("洛克学生").action(async ({ session }, area = 101, accountType = 0) => {
3187
3129
  const fwToken = await getPrimaryToken(deps, session.userId);
3188
3130
  if (!fwToken) return notLoggedInHint();
3189
3131
  const userIdentifier = session.userId;
@@ -3204,7 +3146,7 @@ function register2(deps) {
3204
3146
  const deleted = deps.homeSubMgr.deleteMatching(target, kindMap[String(kind || "全部")] ?? "", String(uid || "").trim());
3205
3147
  return deleted ? `已取消 ${deleted} 条家园订阅。` : "当前会话没有匹配的家园订阅。";
3206
3148
  });
3207
- ctx.command("洛克").subcommand(".调试家园订阅", "立即执行一次家园订阅检查").action(async ({ session }) => {
3149
+ ctx.command("洛克").subcommand(".调试家园订阅", "立即执行一次家园订阅检查").alias("洛克调试家园订阅").action(async ({ session }) => {
3208
3150
  if (!isBotAdmin(session, deps.config.adminUserIds)) return "此指令仅限管理员使用。";
3209
3151
  const result = await checkHomeSubscriptions(deps);
3210
3152
  return `家园订阅检查完成:订阅 ${result.subscriptions} 条,检查 ${result.checked} 条,推送 ${result.pushed} 档提醒。`;
@@ -3434,7 +3376,7 @@ function register3(deps) {
3434
3376
  merchantSubMgr.delete(target.key);
3435
3377
  return "✅ 已取消远行商人订阅。";
3436
3378
  });
3437
- ctx.command("洛克").subcommand(".调试远行商人订阅", "立即执行一次远行商人订阅检查").action(async ({ session }) => {
3379
+ ctx.command("洛克").subcommand(".调试远行商人订阅", "立即执行一次远行商人订阅检查").alias("洛克调试远行商人订阅").action(async ({ session }) => {
3438
3380
  if (!isBotAdmin2(session, config.adminUserIds)) return "此指令仅限管理员使用。";
3439
3381
  const result = await checkMerchantSubscriptions(deps);
3440
3382
  return `远行商人订阅检查完成:订阅 ${result.subscriptions} 条,命中 ${result.matched} 条,推送 ${result.pushed} 条。`;
@@ -3451,8 +3393,8 @@ __name(register3, "register");
3451
3393
  var WIKI_CLOSED_MESSAGE = "该功能暂时关闭";
3452
3394
  function register4(deps) {
3453
3395
  const { ctx } = deps;
3454
- ctx.command("洛克").subcommand(".wiki <name:text>", "查询精灵 wiki").action(() => WIKI_CLOSED_MESSAGE);
3455
- ctx.command("洛克").subcommand(".技能 <name:text>", "查询技能 wiki").action(() => WIKI_CLOSED_MESSAGE);
3396
+ ctx.command("洛克").subcommand(".wiki <name:text>", "查询精灵 wiki").alias("洛克wiki").action(() => WIKI_CLOSED_MESSAGE);
3397
+ ctx.command("洛克").subcommand(".技能 <name:text>", "查询技能 wiki").alias("洛克技能").action(() => WIKI_CLOSED_MESSAGE);
3456
3398
  }
3457
3399
  __name(register4, "register");
3458
3400
 
@@ -3497,7 +3439,7 @@ async function sendEggImage(deps, session, templateName, data, fallback) {
3497
3439
  __name(sendEggImage, "sendEggImage");
3498
3440
  function register5(deps) {
3499
3441
  const { ctx, client, eggService } = deps;
3500
- ctx.command("洛克").subcommand(".查蛋 [arg1:string] [arg2:string]", "查询精灵蛋组").action(async ({ session }, arg1, arg2) => {
3442
+ ctx.command("洛克").subcommand(".查蛋 [arg1:string] [arg2:string]", "查询精灵蛋组").alias("洛克查蛋").action(async ({ session }, arg1, arg2) => {
3501
3443
  if (!arg1) {
3502
3444
  return [
3503
3445
  "查蛋用法:",
@@ -3588,7 +3530,7 @@ function register5(deps) {
3588
3530
  ` : "";
3589
3531
  await sendEggImage(deps, session, "searcheggs", data, hint + eggService.buildSearchText(pet));
3590
3532
  });
3591
- ctx.command("洛克").subcommand(".配种 <nameA:string> [nameB:string]", "配种查询").action(async ({ session }, nameA, nameB) => {
3533
+ ctx.command("洛克").subcommand(".配种 <nameA:string> [nameB:string]", "配种查询").alias("洛克配种").action(async ({ session }, nameA, nameB) => {
3592
3534
  if (!nameA) {
3593
3535
  return [
3594
3536
  "配种用法:",
@@ -3647,7 +3589,7 @@ function register6(deps) {
3647
3589
  return config.adminUserIds.includes(userId);
3648
3590
  }
3649
3591
  __name(isAdmin, "isAdmin");
3650
- ctx.command("洛克").subcommand(".刷新所有凭证", "刷新所有用户凭证(管理员)").action(async ({ session }) => {
3592
+ ctx.command("洛克").subcommand(".刷新所有凭证", "刷新所有用户凭证(管理员)").alias("洛克刷新所有凭证").action(async ({ session }) => {
3651
3593
  if (!isAdmin(session.userId)) return "此指令仅限管理员使用。";
3652
3594
  await session.send("正在刷新所有用户的凭证...");
3653
3595
  const allUsers = userMgr.getAllUsersBindings();
@@ -3676,7 +3618,7 @@ function register6(deps) {
3676
3618
  }
3677
3619
  return `刷新完成:成功 ${success},失败 ${fail}`;
3678
3620
  });
3679
- ctx.command("洛克").subcommand(".删除失效绑定", "清理失效绑定(管理员)").action(async ({ session }) => {
3621
+ ctx.command("洛克").subcommand(".删除失效绑定", "清理失效绑定(管理员)").alias("洛克删除失效绑定").action(async ({ session }) => {
3680
3622
  if (!isAdmin(session.userId)) return "此指令仅限管理员使用。";
3681
3623
  await session.send("正在检查所有用户的绑定有效性...");
3682
3624
  const allUsers = userMgr.getAllUsersBindings();
@@ -3849,7 +3791,8 @@ function apply(ctx, config) {
3849
3791
  const homeSubMgr = new HomeSubscriptionManager(dataDir);
3850
3792
  const resPath = import_node_path5.default.resolve(__dirname, "..");
3851
3793
  const renderer = new Renderer(resPath);
3852
- const searcheggsDir = import_node_path5.default.join(resPath, "src", "render-templates", "searcheggs");
3794
+ const renderTemplateRoot = import_node_fs5.default.existsSync(import_node_path5.default.join(resPath, "lib", "render-templates")) ? import_node_path5.default.join(resPath, "lib", "render-templates") : import_node_path5.default.join(resPath, "src", "render-templates");
3795
+ const searcheggsDir = import_node_path5.default.join(renderTemplateRoot, "searcheggs");
3853
3796
  const eggService = new EggService(searcheggsDir);
3854
3797
  const deps = { ctx, config, client, userMgr, merchantSubMgr, homeSubMgr, eggService, renderer };
3855
3798
  ctx.on("ready", () => {
@@ -6,12 +6,11 @@
6
6
  <title>{{pageTitle}}</title>
7
7
  <link rel="stylesheet" href="{{_res_path}}render/menu/style.css">
8
8
  </head>
9
- <body class="page-out-client">
10
- <div class="menu-cont page-section-main">
11
- <img class="xiaoluoke-overlay" src="{{_res_path}}img/xueyingwawa.png" onerror="this.style.display='none'">
12
- <div class="menu-page">
13
- <div class="header-center">
14
- <div class="page-title">{{pageTitle}}</div>
9
+ <body class="page-out-client">
10
+ <div class="menu-cont page-section-main">
11
+ <div class="menu-page">
12
+ <div class="header-center">
13
+ <div class="page-title">{{pageTitle}}</div>
15
14
  <div class="page-subtitle">{{pageSubtitle}}</div>
16
15
  </div>
17
16
  <main class="menu-content">
@@ -8,22 +8,24 @@
8
8
  --helper-font-family-mianfeiziti: 'mianfeiziti', system-ui, "calibri", "Roboto", verdana, "PingFang SC", sans-serif;
9
9
  }
10
10
 
11
- body {
12
- margin: 0;
13
- padding: 30px;
14
- background-color: #FAF6ED;
15
- display: inline-block;
16
- font-family: var(--helper-font-family-mianfeiziti);
17
- }
18
-
19
- .menu-cont {
20
- width: 780px;
21
- background: url("../../img/ercode-bg.D1ccSQKH.png") no-repeat center center;
22
- background-size: 100% 100%;
23
- position: relative;
24
- padding: 40px 50px;
25
- box-sizing: border-box;
26
- }
11
+ html,
12
+ body {
13
+ margin: 0;
14
+ padding: 30px;
15
+ background-color: #f3eee2;
16
+ display: inline-block;
17
+ font-family: var(--helper-font-family-mianfeiziti);
18
+ }
19
+
20
+ .menu-cont {
21
+ width: 780px;
22
+ background: url("../../img/ercode-bg.D1ccSQKH.png") center/cover no-repeat;
23
+ background-color: #f3eee2;
24
+ position: relative;
25
+ padding: 40px 50px;
26
+ box-sizing: border-box;
27
+ overflow: hidden;
28
+ }
27
29
 
28
30
  .menu-cont::before {
29
31
  content: "";
@@ -34,20 +36,10 @@ body {
34
36
  pointer-events: none;
35
37
  }
36
38
 
37
- .xiaoluoke-overlay {
38
- position: absolute;
39
- bottom: -15px;
40
- right: -30px;
41
- width: 320px;
42
- opacity: 0.15;
43
- z-index: 2;
44
- pointer-events: none;
45
- }
46
-
47
- .menu-page {
48
- position: relative;
49
- z-index: 3;
50
- }
39
+ .menu-page {
40
+ position: relative;
41
+ z-index: 3;
42
+ }
51
43
 
52
44
  .header-center {
53
45
  text-align: center;
@@ -86,28 +78,34 @@ body {
86
78
  border-left: 5px solid #ffc65f;
87
79
  }
88
80
 
89
- .group-items {
90
- display: grid;
91
- grid-template-columns: repeat(2, 1fr);
92
- gap: 15px;
93
- }
94
-
95
- .menu-item {
96
- width: 100%;
81
+ .group-items {
82
+ display: grid;
83
+ grid-template-columns: repeat(2, minmax(0, 1fr));
84
+ gap: 15px;
85
+ }
86
+
87
+ .menu-item {
88
+ width: 100%;
97
89
  min-height: 85px;
98
90
  background: url("../../img/player-bg.png") no-repeat center center;
99
91
  background-size: 100% 100%;
100
92
  display: flex;
101
93
  flex-direction: column;
102
94
  justify-content: center;
103
- padding: 12px 50px 12px 25px;
104
- box-sizing: border-box;
105
- }
106
-
107
- .menu-item-cmd {
108
- font-size: 22px;
109
- font-weight: bold;
110
- color: #5a3e1b;
95
+ padding: 12px 50px 12px 25px;
96
+ box-sizing: border-box;
97
+ }
98
+
99
+ .group-items > .menu-item:last-child:nth-child(odd) {
100
+ grid-column: 1 / -1;
101
+ width: calc((100% - 15px) / 2);
102
+ justify-self: center;
103
+ }
104
+
105
+ .menu-item-cmd {
106
+ font-size: 22px;
107
+ font-weight: bold;
108
+ color: #5a3e1b;
111
109
  margin-bottom: 8px;
112
110
  line-height: 1.2;
113
111
  }
package/lib/render.js CHANGED
@@ -151,6 +151,7 @@ class Renderer {
151
151
  '.student-state-page',
152
152
  '.student-perks-page',
153
153
  '.student-page',
154
+ '.merchant-page',
154
155
  '.home-page',
155
156
  ];
156
157
  let target = null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-rocom",
3
3
  "description": "洛克王国查询与订阅插件",
4
- "version": "1.0.3",
4
+ "version": "1.0.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "homepage": "https://github.com/staytomorrow/koishi-plugin-rocom",