koishi-plugin-rocom 1.0.2 → 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
@@ -19,6 +19,7 @@ export declare class RocomClient {
19
19
  private post;
20
20
  private delete;
21
21
  private requestWithStatus;
22
+ private requestIngameWithFallback;
22
23
  private getIngameTask;
23
24
  qqQrLogin(ctx: Context, userIdentifier: string): Promise<any>;
24
25
  qqQrStatus(ctx: Context, fwToken: string, userIdentifier: string): Promise<any>;
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;
@@ -230,25 +230,60 @@ class RocomClient {
230
230
  const body = response?.data !== undefined ? response.data : response;
231
231
  if (body?.code !== undefined && body.code !== 0) {
232
232
  this.setLastError(body.message || body.msg || '接口返回异常');
233
- 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
+ }
234
236
  return { status: null, data: null };
235
237
  }
236
238
  const data = body?.code !== undefined ? (body.data ?? {}) : (body ?? {});
237
239
  if (!acceptedStatuses.includes(status)) {
238
240
  this.setLastError(`HTTP ${status}`);
239
- 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 };
240
245
  }
241
246
  return { status, data };
242
247
  }
243
248
  catch (e) {
244
249
  const message = this.formatHttpError(e);
245
250
  this.setLastError(message);
246
- 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
+ }
247
254
  return { status: null, data: null };
248
255
  }
249
256
  }
250
- async getIngameTask(ctx, taskId) {
251
- return this.requestWithStatus(ctx, 'GET', `/api/v1/games/rocom/ingame/tasks/${taskId}`, this.wegameHeaders(), { acceptedStatuses: [200, 202] });
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
+ }
285
+ async getIngameTask(ctx, taskId, includeApiKey = true) {
286
+ return this.requestWithStatus(ctx, 'GET', `/api/v1/games/rocom/ingame/tasks/${taskId}`, this.wegameHeaders('', '', '', '', includeApiKey), { acceptedStatuses: [200, 202] });
252
287
  }
253
288
  async qqQrLogin(ctx, userIdentifier) {
254
289
  const params = { client_type: 'bot', client_id: 'koishi', provider: 'rocom' };
@@ -334,28 +369,14 @@ class RocomClient {
334
369
  async ingamePlayerSearch(ctx, uid) {
335
370
  const sanitizedUid = this.sanitizeUid(uid);
336
371
  if (!sanitizedUid) {
337
- this.setLastError('UID 涓嶈兘涓虹┖');
372
+ this.setLastError('UID 不能为空');
338
373
  return null;
339
374
  }
340
375
  const path = '/api/v1/games/rocom/ingame/player/search';
341
- const headers = this.wegameHeaders();
342
376
  const payload = { uid: sanitizedUid, wait_ms: 5000 };
343
- let { status, data } = await this.requestWithStatus(ctx, 'POST', path, headers, {
344
- json: payload,
345
- acceptedStatuses: [200, 202],
346
- });
377
+ const { status, data, usedApiKey } = await this.requestIngameWithFallback(ctx, path, payload);
347
378
  if (status === 200 && data && this.isIngamePlayerPayload(data))
348
379
  return data;
349
- if (status === null) {
350
- const fallback = await this.requestWithStatus(ctx, 'GET', path, headers, {
351
- params: payload,
352
- acceptedStatuses: [200, 202],
353
- });
354
- status = fallback.status;
355
- data = fallback.data;
356
- if (status === 200 && data && this.isIngamePlayerPayload(data))
357
- return data;
358
- }
359
380
  if (!data)
360
381
  return null;
361
382
  if (this.isIngamePlayerPayload(data))
@@ -368,7 +389,7 @@ class RocomClient {
368
389
  }
369
390
  for (let i = 0; i < 8; i++) {
370
391
  await new Promise(resolve => setTimeout(resolve, 1000));
371
- const task = await this.getIngameTask(ctx, taskId);
392
+ const task = await this.getIngameTask(ctx, taskId, usedApiKey);
372
393
  if (task.status === 200)
373
394
  return task.data;
374
395
  if (task.status === null)
@@ -400,28 +421,14 @@ class RocomClient {
400
421
  async ingameHomeInfo(ctx, uid, waitMs = 5000) {
401
422
  const sanitizedUid = this.sanitizeUid(uid);
402
423
  if (!sanitizedUid) {
403
- this.setLastError('UID 涓嶈兘涓虹┖');
424
+ this.setLastError('UID 不能为空');
404
425
  return null;
405
426
  }
406
427
  const path = '/api/v1/games/rocom/ingame/home/info';
407
- const headers = this.wegameHeaders();
408
428
  const payload = { uid: sanitizedUid, wait_ms: waitMs };
409
- let { status, data } = await this.requestWithStatus(ctx, 'POST', path, headers, {
410
- json: payload,
411
- acceptedStatuses: [200, 202],
412
- });
429
+ const { status, data, usedApiKey } = await this.requestIngameWithFallback(ctx, path, payload);
413
430
  if (status === 200 && data && !(data.task_id || data.taskId || data.taskID))
414
431
  return data;
415
- if (status === null) {
416
- const fallback = await this.requestWithStatus(ctx, 'GET', path, headers, {
417
- params: payload,
418
- acceptedStatuses: [200, 202],
419
- });
420
- status = fallback.status;
421
- data = fallback.data;
422
- if (status === 200 && data && !(data.task_id || data.taskId || data.taskID))
423
- return data;
424
- }
425
432
  const taskId = data?.task_id || data?.taskId || data?.taskID;
426
433
  if (!taskId) {
427
434
  if (status === 202)
@@ -430,7 +437,7 @@ class RocomClient {
430
437
  }
431
438
  for (let i = 0; i < 10; i++) {
432
439
  await new Promise(resolve => setTimeout(resolve, 1000));
433
- const task = await this.getIngameTask(ctx, taskId);
440
+ const task = await this.getIngameTask(ctx, taskId, usedApiKey);
434
441
  if (task.status === 200)
435
442
  return task.data;
436
443
  if (task.status === null)
@@ -441,10 +448,8 @@ class RocomClient {
441
448
  }
442
449
  async ingameMerchantInfo(ctx, shopId) {
443
450
  const params = { shop_id: shopId };
444
- const data = await this.get(ctx, '/api/v1/games/rocom/ingame/merchant/info', this.wegameHeaders(), params, { silentFailureDetails: true });
445
- if (data)
446
- return data;
447
- return this.post(ctx, '/api/v1/games/rocom/ingame/merchant/info', this.wegameHeaders(), params);
451
+ const { status, data } = await this.requestIngameWithFallback(ctx, '/api/v1/games/rocom/ingame/merchant/info', params);
452
+ return status === null ? null : data;
448
453
  }
449
454
  async getFriendship(ctx, fwToken, userIds, userIdentifier = '') {
450
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 '此指令仅限管理员使用。';
@@ -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 : {};
@@ -769,6 +777,7 @@ async function checkHomeSubscriptions(deps) {
769
777
  function register(deps) {
770
778
  const { ctx, client } = deps;
771
779
  ctx.command('洛克').subcommand('.档案', '查看个人档案')
780
+ .alias('洛克档案')
772
781
  .action(async ({ session }) => {
773
782
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
774
783
  if (!fwToken)
@@ -944,6 +953,7 @@ function register(deps) {
944
953
  await sendImage(deps, session, 'personal-card', data, fallback);
945
954
  });
946
955
  ctx.command('洛克').subcommand('.战绩 [page:number]', '查看对战战绩')
956
+ .alias('洛克战绩')
947
957
  .action(async ({ session }, _page = 1) => {
948
958
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
949
959
  if (!fwToken)
@@ -991,6 +1001,7 @@ function register(deps) {
991
1001
  await sendImage(deps, session, 'record', data, `【${role.name}的战绩】胜率:${bo.win_rate || 0}% 场次:${bo.total_match || 0}`);
992
1002
  });
993
1003
  ctx.command('洛克').subcommand('.背包 [arg1:string] [arg2:string]', '查看精灵背包')
1004
+ .alias('洛克背包')
994
1005
  .action(async ({ session }, arg1, arg2) => {
995
1006
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
996
1007
  if (!fwToken)
@@ -1050,6 +1061,7 @@ function register(deps) {
1050
1061
  await sendImage(deps, session, 'package', data, `【背包 - ${category}精灵】共${petRes.total || 0}只`);
1051
1062
  });
1052
1063
  ctx.command('洛克').subcommand('.阵容 [arg1:string] [arg2:string]', '查看阵容推荐')
1064
+ .alias('洛克阵容')
1053
1065
  .action(async ({ session }, arg1, arg2) => {
1054
1066
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
1055
1067
  if (!fwToken)
@@ -1139,6 +1151,7 @@ function register(deps) {
1139
1151
  await sendImage(deps, session, 'lineup-detail', data, fallback);
1140
1152
  });
1141
1153
  ctx.command('洛克').subcommand('.交换大厅 [page:number]', '查看交换大厅')
1154
+ .alias('洛克交换大厅')
1142
1155
  .action(async ({ session }, page = 1) => {
1143
1156
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
1144
1157
  if (!fwToken)
@@ -1179,6 +1192,7 @@ function register(deps) {
1179
1192
  await sendImage(deps, session, 'exchange-hall', data, `【交换大厅】第${page}页`);
1180
1193
  });
1181
1194
  ctx.command('洛克').subcommand('.玩家 <uid:string>', '通过 ingame 接口查询玩家基础资料')
1195
+ .alias('洛克玩家')
1182
1196
  .action(async ({ session }, uid) => {
1183
1197
  if (!uid)
1184
1198
  return '请提供玩家 UID。用法:洛克.玩家 <UID>';
@@ -1188,6 +1202,7 @@ function register(deps) {
1188
1202
  await sendImage(deps, session, 'player-search', buildPlayerSearchRenderData(res, uid), `【洛克玩家】UID ${uid}`);
1189
1203
  });
1190
1204
  ctx.command('洛克').subcommand('.家园 [uid:string]', '通过 UID 查询家园菜园、守卫和室内精灵')
1205
+ .alias('洛克家园')
1191
1206
  .action(async ({ session }, uid = '') => {
1192
1207
  let targetUid = String(uid || '').trim();
1193
1208
  if (!targetUid) {
@@ -1202,6 +1217,7 @@ function register(deps) {
1202
1217
  await sendImage(deps, session, 'home', buildHomeRenderData(deps, res, targetUid), `【洛克家园】UID ${targetUid}`);
1203
1218
  });
1204
1219
  ctx.command('洛克').subcommand('.商店 <shopId:string>', '通过 ingame 接口查询商店信息')
1220
+ .alias('洛克商店')
1205
1221
  .action(async ({ session }, shopId) => {
1206
1222
  if (!shopId)
1207
1223
  return '请提供商店 ID。用法:洛克.商店 <shop_id>';
@@ -1211,6 +1227,7 @@ function register(deps) {
1211
1227
  await sendImage(deps, session, 'ingame-shop', buildShopRenderData(res, shopId), `【洛克商店】shop_id=${shopId}`);
1212
1228
  });
1213
1229
  ctx.command('洛克').subcommand('.好友关系 <userIds:string>', '查询好友关系')
1230
+ .alias('洛克好友关系')
1214
1231
  .action(async ({ session }, userIds) => {
1215
1232
  if (!userIds)
1216
1233
  return '请提供要查询的用户 ID 列表。用法:洛克.好友关系 <id1,id2>';
@@ -1223,6 +1240,7 @@ function register(deps) {
1223
1240
  await sendImage(deps, session, 'friendship', buildFriendshipRenderData(res, userIds), `【好友关系】${userIds}`);
1224
1241
  });
1225
1242
  ctx.command('洛克').subcommand('.学生 [area:number] [accountType:number]', '查询学生认证状态与学生活动福利')
1243
+ .alias('洛克学生')
1226
1244
  .action(async ({ session }, area = 101, accountType = 0) => {
1227
1245
  const fwToken = await (0, account_1.getPrimaryToken)(deps, session.userId);
1228
1246
  if (!fwToken)
@@ -1252,6 +1270,7 @@ function register(deps) {
1252
1270
  return deleted ? `已取消 ${deleted} 条家园订阅。` : '当前会话没有匹配的家园订阅。';
1253
1271
  });
1254
1272
  ctx.command('洛克').subcommand('.调试家园订阅', '立即执行一次家园订阅检查')
1273
+ .alias('洛克调试家园订阅')
1255
1274
  .action(async ({ session }) => {
1256
1275
  if (!isBotAdmin(session, deps.config.adminUserIds))
1257
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
@@ -59,9 +60,9 @@ var RocomClient = class {
59
60
  if (!uid) return "";
60
61
  return uid.trim().replace(/[^a-zA-Z0-9_\- \u4e00-\u9fa5]/g, "").trim();
61
62
  }
62
- wegameHeaders(fwToken = "", userIdentifier = "", clientType = "", clientId = "") {
63
+ wegameHeaders(fwToken = "", userIdentifier = "", clientType = "", clientId = "", includeApiKey = true) {
63
64
  const headers = {};
64
- if (this.apiKey) headers["X-API-Key"] = this.apiKey;
65
+ if (includeApiKey && this.apiKey) headers["X-API-Key"] = this.apiKey;
65
66
  if (fwToken) headers["X-Framework-Token"] = fwToken;
66
67
  if (userIdentifier) headers["X-User-Identifier"] = this.sanitizeUid(userIdentifier);
67
68
  if (clientType) headers["X-Client-Type"] = clientType;
@@ -249,28 +250,61 @@ ${this.stringifyForLog(details)}`);
249
250
  const body = response?.data !== void 0 ? response.data : response;
250
251
  if (body?.code !== void 0 && body.code !== 0) {
251
252
  this.setLastError(body.message || body.msg || "接口返回异常");
252
- 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
+ }
253
256
  return { status: null, data: null };
254
257
  }
255
258
  const data = body?.code !== void 0 ? body.data ?? {} : body ?? {};
256
259
  if (!acceptedStatuses.includes(status)) {
257
260
  this.setLastError(`HTTP ${status}`);
258
- 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 };
259
265
  }
260
266
  return { status, data };
261
267
  } catch (e) {
262
268
  const message = this.formatHttpError(e);
263
269
  this.setLastError(message);
264
- 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
+ }
265
273
  return { status: null, data: null };
266
274
  }
267
275
  }
268
- async getIngameTask(ctx, taskId) {
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
+ }
302
+ async getIngameTask(ctx, taskId, includeApiKey = true) {
269
303
  return this.requestWithStatus(
270
304
  ctx,
271
305
  "GET",
272
306
  `/api/v1/games/rocom/ingame/tasks/${taskId}`,
273
- this.wegameHeaders(),
307
+ this.wegameHeaders("", "", "", "", includeApiKey),
274
308
  { acceptedStatuses: [200, 202] }
275
309
  );
276
310
  }
@@ -354,26 +388,13 @@ ${this.stringifyForLog(details)}`);
354
388
  async ingamePlayerSearch(ctx, uid) {
355
389
  const sanitizedUid = this.sanitizeUid(uid);
356
390
  if (!sanitizedUid) {
357
- this.setLastError("UID 涓嶈兘涓虹┖");
391
+ this.setLastError("UID 不能为空");
358
392
  return null;
359
393
  }
360
394
  const path6 = "/api/v1/games/rocom/ingame/player/search";
361
- const headers = this.wegameHeaders();
362
395
  const payload = { uid: sanitizedUid, wait_ms: 5e3 };
363
- let { status, data } = await this.requestWithStatus(ctx, "POST", path6, headers, {
364
- json: payload,
365
- acceptedStatuses: [200, 202]
366
- });
396
+ const { status, data, usedApiKey } = await this.requestIngameWithFallback(ctx, path6, payload);
367
397
  if (status === 200 && data && this.isIngamePlayerPayload(data)) return data;
368
- if (status === null) {
369
- const fallback = await this.requestWithStatus(ctx, "GET", path6, headers, {
370
- params: payload,
371
- acceptedStatuses: [200, 202]
372
- });
373
- status = fallback.status;
374
- data = fallback.data;
375
- if (status === 200 && data && this.isIngamePlayerPayload(data)) return data;
376
- }
377
398
  if (!data) return null;
378
399
  if (this.isIngamePlayerPayload(data)) return data;
379
400
  const taskId = data.task_id || data.taskId || data.taskID;
@@ -383,7 +404,7 @@ ${this.stringifyForLog(details)}`);
383
404
  }
384
405
  for (let i = 0; i < 8; i++) {
385
406
  await new Promise((resolve) => setTimeout(resolve, 1e3));
386
- const task = await this.getIngameTask(ctx, taskId);
407
+ const task = await this.getIngameTask(ctx, taskId, usedApiKey);
387
408
  if (task.status === 200) return task.data;
388
409
  if (task.status === null) return null;
389
410
  }
@@ -412,26 +433,13 @@ ${this.stringifyForLog(details)}`);
412
433
  async ingameHomeInfo(ctx, uid, waitMs = 5e3) {
413
434
  const sanitizedUid = this.sanitizeUid(uid);
414
435
  if (!sanitizedUid) {
415
- this.setLastError("UID 涓嶈兘涓虹┖");
436
+ this.setLastError("UID 不能为空");
416
437
  return null;
417
438
  }
418
439
  const path6 = "/api/v1/games/rocom/ingame/home/info";
419
- const headers = this.wegameHeaders();
420
440
  const payload = { uid: sanitizedUid, wait_ms: waitMs };
421
- let { status, data } = await this.requestWithStatus(ctx, "POST", path6, headers, {
422
- json: payload,
423
- acceptedStatuses: [200, 202]
424
- });
441
+ const { status, data, usedApiKey } = await this.requestIngameWithFallback(ctx, path6, payload);
425
442
  if (status === 200 && data && !(data.task_id || data.taskId || data.taskID)) return data;
426
- if (status === null) {
427
- const fallback = await this.requestWithStatus(ctx, "GET", path6, headers, {
428
- params: payload,
429
- acceptedStatuses: [200, 202]
430
- });
431
- status = fallback.status;
432
- data = fallback.data;
433
- if (status === 200 && data && !(data.task_id || data.taskId || data.taskID)) return data;
434
- }
435
443
  const taskId = data?.task_id || data?.taskId || data?.taskID;
436
444
  if (!taskId) {
437
445
  if (status === 202) this.setLastError("家园查询任务已入队,但未返回 task_id");
@@ -439,7 +447,7 @@ ${this.stringifyForLog(details)}`);
439
447
  }
440
448
  for (let i = 0; i < 10; i++) {
441
449
  await new Promise((resolve) => setTimeout(resolve, 1e3));
442
- const task = await this.getIngameTask(ctx, taskId);
450
+ const task = await this.getIngameTask(ctx, taskId, usedApiKey);
443
451
  if (task.status === 200) return task.data;
444
452
  if (task.status === null) return null;
445
453
  }
@@ -448,15 +456,12 @@ ${this.stringifyForLog(details)}`);
448
456
  }
449
457
  async ingameMerchantInfo(ctx, shopId) {
450
458
  const params = { shop_id: shopId };
451
- const data = await this.get(
459
+ const { status, data } = await this.requestIngameWithFallback(
452
460
  ctx,
453
461
  "/api/v1/games/rocom/ingame/merchant/info",
454
- this.wegameHeaders(),
455
- params,
456
- { silentFailureDetails: true }
462
+ params
457
463
  );
458
- if (data) return data;
459
- return this.post(ctx, "/api/v1/games/rocom/ingame/merchant/info", this.wegameHeaders(), params);
464
+ return status === null ? null : data;
460
465
  }
461
466
  async getFriendship(ctx, fwToken, userIds, userIdentifier = "") {
462
467
  return this.get(
@@ -1429,6 +1434,7 @@ var Renderer = class {
1429
1434
  ".student-state-page",
1430
1435
  ".student-perks-page",
1431
1436
  ".student-page",
1437
+ ".merchant-page",
1432
1438
  ".home-page"
1433
1439
  ];
1434
1440
  let target = null;
@@ -1816,7 +1822,7 @@ async function saveBindingWithRoleInfo(deps, session, fwToken, loginType, userId
1816
1822
  __name(saveBindingWithRoleInfo, "saveBindingWithRoleInfo");
1817
1823
  function register(deps) {
1818
1824
  const { ctx, client, userMgr } = deps;
1819
- ctx.command("洛克").subcommand(".QQ登录", "QQ 扫码登录").action(async ({ session }) => {
1825
+ ctx.command("洛克").subcommand(".QQ登录", "QQ 扫码登录").alias("洛克QQ登录").action(async ({ session }) => {
1820
1826
  const userId = session.userId;
1821
1827
  const qrData = await client.qqQrLogin(ctx, userId);
1822
1828
  if (!qrData?.qr_image) return "获取 QQ 二维码失败。";
@@ -1843,7 +1849,7 @@ function register(deps) {
1843
1849
  }
1844
1850
  return "登录超时或失败,请重试。";
1845
1851
  });
1846
- ctx.command("洛克").subcommand(".微信登录", "微信扫码登录").action(async ({ session }) => {
1852
+ ctx.command("洛克").subcommand(".微信登录", "微信扫码登录").alias("洛克微信登录").action(async ({ session }) => {
1847
1853
  const userId = session.userId;
1848
1854
  const qrData = await client.wechatQrLogin(ctx, userId);
1849
1855
  if (!qrData?.qr_image) return "获取微信登录链接失败。";
@@ -1865,14 +1871,14 @@ ${qrUrl}`);
1865
1871
  }
1866
1872
  return "登录超时或失败,请重试。";
1867
1873
  });
1868
- 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) => {
1869
1875
  if (!tgpId || !tgpTicket) return "用法:洛克.导入 <tgp_id> <tgp_ticket>";
1870
1876
  const userId = session.userId;
1871
1877
  const res = await client.importToken(ctx, tgpId, tgpTicket, userId);
1872
1878
  if (!res?.frameworkToken) return "凭证导入失败。";
1873
1879
  await saveBindingWithRoleInfo(deps, session, res.frameworkToken, "manual", userId);
1874
1880
  });
1875
- ctx.command("洛克").subcommand(".绑定列表", "查看已绑定账号").action(async ({ session }) => {
1881
+ ctx.command("洛克").subcommand(".绑定列表", "查看已绑定账号").alias("洛克绑定列表").action(async ({ session }) => {
1876
1882
  const bindings = userMgr.getUserBindings(session.userId);
1877
1883
  if (!bindings.length) return "暂无绑定账号。";
1878
1884
  const bindItems = bindings.map((binding, index) => ({
@@ -1898,11 +1904,11 @@ ${qrUrl}`);
1898
1904
  const png = await deps.renderer.renderHtml(ctx, "bind-list", data);
1899
1905
  await sendImageWithFallback(session, png, fallbackLines.join("\n"), "account:bind-list", deps.config);
1900
1906
  });
1901
- ctx.command("洛克").subcommand(".切换 <index:number>", "切换主账号").action(async ({ session }, index) => {
1907
+ ctx.command("洛克").subcommand(".切换 <index:number>", "切换主账号").alias("洛克切换").action(async ({ session }, index) => {
1902
1908
  if (!index) return "用法:洛克.切换 <序号>";
1903
1909
  return userMgr.switchPrimary(session.userId, index) ? `成功切换到序号 ${index} 账号。` : "序号无效。";
1904
1910
  });
1905
- ctx.command("洛克").subcommand(".解绑 <index:number>", "解绑账号").action(async ({ session }, index) => {
1911
+ ctx.command("洛克").subcommand(".解绑 <index:number>", "解绑账号").alias("洛克解绑").action(async ({ session }, index) => {
1906
1912
  if (!index) return "用法:洛克.解绑 <序号>";
1907
1913
  const removed = userMgr.deleteUserBinding(session.userId, index);
1908
1914
  if (!removed) return "序号无效。";
@@ -1917,7 +1923,7 @@ ${qrUrl}`);
1917
1923
  }
1918
1924
  return `已解绑账号:${removed.nickname}`;
1919
1925
  });
1920
- ctx.command("洛克").subcommand(".刷新", "刷新当前主账号凭证").action(async ({ session }) => {
1926
+ ctx.command("洛克").subcommand(".刷新", "刷新当前主账号凭证").alias("洛克刷新").action(async ({ session }) => {
1921
1927
  const userId = session.userId;
1922
1928
  const binding = userMgr.getPrimaryBinding(userId);
1923
1929
  if (!binding) return notLoggedInHint();
@@ -2323,9 +2329,18 @@ function homePlantIcon(deps, iconId) {
2323
2329
  }
2324
2330
  __name(homePlantIcon, "homePlantIcon");
2325
2331
  var homePlantMapCache = null;
2332
+ var HOME_PLANT_MAP_RELATIVE_PATH = import_node_path4.default.join("render-templates", "home", "data", "home_item_list.json");
2333
+ function resolveHomePlantMapPath() {
2334
+ const candidates = [
2335
+ import_node_path4.default.resolve(__dirname, "..", HOME_PLANT_MAP_RELATIVE_PATH),
2336
+ import_node_path4.default.resolve(__dirname, HOME_PLANT_MAP_RELATIVE_PATH)
2337
+ ];
2338
+ return candidates.find((candidate) => import_node_fs4.default.existsSync(candidate)) || candidates[0];
2339
+ }
2340
+ __name(resolveHomePlantMapPath, "resolveHomePlantMapPath");
2326
2341
  function loadHomePlantMap() {
2327
2342
  if (homePlantMapCache) return homePlantMapCache;
2328
- const filePath = import_node_path4.default.resolve(__dirname, "..", "render-templates", "home", "data", "home_item_list.json");
2343
+ const filePath = resolveHomePlantMapPath();
2329
2344
  try {
2330
2345
  const data = JSON.parse(import_node_fs4.default.readFileSync(filePath, "utf-8"));
2331
2346
  homePlantMapCache = data && typeof data === "object" ? data : {};
@@ -2699,7 +2714,7 @@ async function checkHomeSubscriptions(deps) {
2699
2714
  __name(checkHomeSubscriptions, "checkHomeSubscriptions");
2700
2715
  function register2(deps) {
2701
2716
  const { ctx, client } = deps;
2702
- ctx.command("洛克").subcommand(".档案", "查看个人档案").action(async ({ session }) => {
2717
+ ctx.command("洛克").subcommand(".档案", "查看个人档案").alias("洛克档案").action(async ({ session }) => {
2703
2718
  const fwToken = await getPrimaryToken(deps, session.userId);
2704
2719
  if (!fwToken) return notLoggedInHint();
2705
2720
  const userIdentifier = session.userId;
@@ -2867,7 +2882,7 @@ function register2(deps) {
2867
2882
  评分:${ev.score || "0"} 收藏:${cl.current_collection_count || 0}/${cl.total_collection_count || 0}`;
2868
2883
  await sendImage(deps, session, "personal-card", data, fallback);
2869
2884
  });
2870
- ctx.command("洛克").subcommand(".战绩 [page:number]", "查看对战战绩").action(async ({ session }, _page = 1) => {
2885
+ ctx.command("洛克").subcommand(".战绩 [page:number]", "查看对战战绩").alias("洛克战绩").action(async ({ session }, _page = 1) => {
2871
2886
  const fwToken = await getPrimaryToken(deps, session.userId);
2872
2887
  if (!fwToken) return notLoggedInHint();
2873
2888
  const userIdentifier = session.userId;
@@ -2911,7 +2926,7 @@ function register2(deps) {
2911
2926
  };
2912
2927
  await sendImage(deps, session, "record", data, `【${role.name}的战绩】胜率:${bo.win_rate || 0}% 场次:${bo.total_match || 0}`);
2913
2928
  });
2914
- 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) => {
2915
2930
  const fwToken = await getPrimaryToken(deps, session.userId);
2916
2931
  if (!fwToken) return notLoggedInHint();
2917
2932
  const catMap = { "全部": 0, "了不起": 1, "异色": 2, "炫彩": 3 };
@@ -2963,7 +2978,7 @@ function register2(deps) {
2963
2978
  };
2964
2979
  await sendImage(deps, session, "package", data, `【背包 - ${category}精灵】共${petRes.total || 0}只`);
2965
2980
  });
2966
- 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) => {
2967
2982
  const fwToken = await getPrimaryToken(deps, session.userId);
2968
2983
  if (!fwToken) return notLoggedInHint();
2969
2984
  const userIdentifier = session.userId;
@@ -3040,7 +3055,7 @@ function register2(deps) {
3040
3055
  const fallback = `【阵容详情】${targetLineup.name || "未知阵容"} | 阵容码: ${normalizedLineupId}`;
3041
3056
  await sendImage(deps, session, "lineup-detail", data, fallback);
3042
3057
  });
3043
- ctx.command("洛克").subcommand(".交换大厅 [page:number]", "查看交换大厅").action(async ({ session }, page = 1) => {
3058
+ ctx.command("洛克").subcommand(".交换大厅 [page:number]", "查看交换大厅").alias("洛克交换大厅").action(async ({ session }, page = 1) => {
3044
3059
  const fwToken = await getPrimaryToken(deps, session.userId);
3045
3060
  if (!fwToken) return notLoggedInHint();
3046
3061
  const userIdentifier = session.userId;
@@ -3079,13 +3094,13 @@ function register2(deps) {
3079
3094
  };
3080
3095
  await sendImage(deps, session, "exchange-hall", data, `【交换大厅】第${page}页`);
3081
3096
  });
3082
- ctx.command("洛克").subcommand(".玩家 <uid:string>", "通过 ingame 接口查询玩家基础资料").action(async ({ session }, uid) => {
3097
+ ctx.command("洛克").subcommand(".玩家 <uid:string>", "通过 ingame 接口查询玩家基础资料").alias("洛克玩家").action(async ({ session }, uid) => {
3083
3098
  if (!uid) return "请提供玩家 UID。用法:洛克.玩家 <UID>";
3084
3099
  const res = await client.ingamePlayerSearch(ctx, uid);
3085
3100
  if (!res) return `玩家搜索失败:${client.getLastError()}`;
3086
3101
  await sendImage(deps, session, "player-search", buildPlayerSearchRenderData(res, uid), `【洛克玩家】UID ${uid}`);
3087
3102
  });
3088
- ctx.command("洛克").subcommand(".家园 [uid:string]", "通过 UID 查询家园菜园、守卫和室内精灵").action(async ({ session }, uid = "") => {
3103
+ ctx.command("洛克").subcommand(".家园 [uid:string]", "通过 UID 查询家园菜园、守卫和室内精灵").alias("洛克家园").action(async ({ session }, uid = "") => {
3089
3104
  let targetUid = String(uid || "").trim();
3090
3105
  if (!targetUid) {
3091
3106
  const binding = deps.userMgr.getPrimaryBinding(session.userId);
@@ -3096,13 +3111,13 @@ function register2(deps) {
3096
3111
  if (!res) return `家园查询失败:${client.getLastError()}`;
3097
3112
  await sendImage(deps, session, "home", buildHomeRenderData(deps, res, targetUid), `【洛克家园】UID ${targetUid}`);
3098
3113
  });
3099
- ctx.command("洛克").subcommand(".商店 <shopId:string>", "通过 ingame 接口查询商店信息").action(async ({ session }, shopId) => {
3114
+ ctx.command("洛克").subcommand(".商店 <shopId:string>", "通过 ingame 接口查询商店信息").alias("洛克商店").action(async ({ session }, shopId) => {
3100
3115
  if (!shopId) return "请提供商店 ID。用法:洛克.商店 <shop_id>";
3101
3116
  const res = await client.ingameMerchantInfo(ctx, shopId);
3102
3117
  if (!res) return `商店查询失败:${client.getLastError()}`;
3103
3118
  await sendImage(deps, session, "ingame-shop", buildShopRenderData(res, shopId), `【洛克商店】shop_id=${shopId}`);
3104
3119
  });
3105
- ctx.command("洛克").subcommand(".好友关系 <userIds:string>", "查询好友关系").action(async ({ session }, userIds) => {
3120
+ ctx.command("洛克").subcommand(".好友关系 <userIds:string>", "查询好友关系").alias("洛克好友关系").action(async ({ session }, userIds) => {
3106
3121
  if (!userIds) return "请提供要查询的用户 ID 列表。用法:洛克.好友关系 <id1,id2>";
3107
3122
  const fwToken = await getPrimaryToken(deps, session.userId);
3108
3123
  if (!fwToken) return notLoggedInHint();
@@ -3110,7 +3125,7 @@ function register2(deps) {
3110
3125
  if (!res) return `好友关系查询失败:${client.getLastError()}`;
3111
3126
  await sendImage(deps, session, "friendship", buildFriendshipRenderData(res, userIds), `【好友关系】${userIds}`);
3112
3127
  });
3113
- 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) => {
3114
3129
  const fwToken = await getPrimaryToken(deps, session.userId);
3115
3130
  if (!fwToken) return notLoggedInHint();
3116
3131
  const userIdentifier = session.userId;
@@ -3131,7 +3146,7 @@ function register2(deps) {
3131
3146
  const deleted = deps.homeSubMgr.deleteMatching(target, kindMap[String(kind || "全部")] ?? "", String(uid || "").trim());
3132
3147
  return deleted ? `已取消 ${deleted} 条家园订阅。` : "当前会话没有匹配的家园订阅。";
3133
3148
  });
3134
- ctx.command("洛克").subcommand(".调试家园订阅", "立即执行一次家园订阅检查").action(async ({ session }) => {
3149
+ ctx.command("洛克").subcommand(".调试家园订阅", "立即执行一次家园订阅检查").alias("洛克调试家园订阅").action(async ({ session }) => {
3135
3150
  if (!isBotAdmin(session, deps.config.adminUserIds)) return "此指令仅限管理员使用。";
3136
3151
  const result = await checkHomeSubscriptions(deps);
3137
3152
  return `家园订阅检查完成:订阅 ${result.subscriptions} 条,检查 ${result.checked} 条,推送 ${result.pushed} 档提醒。`;
@@ -3361,7 +3376,7 @@ function register3(deps) {
3361
3376
  merchantSubMgr.delete(target.key);
3362
3377
  return "✅ 已取消远行商人订阅。";
3363
3378
  });
3364
- ctx.command("洛克").subcommand(".调试远行商人订阅", "立即执行一次远行商人订阅检查").action(async ({ session }) => {
3379
+ ctx.command("洛克").subcommand(".调试远行商人订阅", "立即执行一次远行商人订阅检查").alias("洛克调试远行商人订阅").action(async ({ session }) => {
3365
3380
  if (!isBotAdmin2(session, config.adminUserIds)) return "此指令仅限管理员使用。";
3366
3381
  const result = await checkMerchantSubscriptions(deps);
3367
3382
  return `远行商人订阅检查完成:订阅 ${result.subscriptions} 条,命中 ${result.matched} 条,推送 ${result.pushed} 条。`;
@@ -3378,8 +3393,8 @@ __name(register3, "register");
3378
3393
  var WIKI_CLOSED_MESSAGE = "该功能暂时关闭";
3379
3394
  function register4(deps) {
3380
3395
  const { ctx } = deps;
3381
- ctx.command("洛克").subcommand(".wiki <name:text>", "查询精灵 wiki").action(() => WIKI_CLOSED_MESSAGE);
3382
- 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);
3383
3398
  }
3384
3399
  __name(register4, "register");
3385
3400
 
@@ -3424,7 +3439,7 @@ async function sendEggImage(deps, session, templateName, data, fallback) {
3424
3439
  __name(sendEggImage, "sendEggImage");
3425
3440
  function register5(deps) {
3426
3441
  const { ctx, client, eggService } = deps;
3427
- 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) => {
3428
3443
  if (!arg1) {
3429
3444
  return [
3430
3445
  "查蛋用法:",
@@ -3515,7 +3530,7 @@ function register5(deps) {
3515
3530
  ` : "";
3516
3531
  await sendEggImage(deps, session, "searcheggs", data, hint + eggService.buildSearchText(pet));
3517
3532
  });
3518
- 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) => {
3519
3534
  if (!nameA) {
3520
3535
  return [
3521
3536
  "配种用法:",
@@ -3574,7 +3589,7 @@ function register6(deps) {
3574
3589
  return config.adminUserIds.includes(userId);
3575
3590
  }
3576
3591
  __name(isAdmin, "isAdmin");
3577
- ctx.command("洛克").subcommand(".刷新所有凭证", "刷新所有用户凭证(管理员)").action(async ({ session }) => {
3592
+ ctx.command("洛克").subcommand(".刷新所有凭证", "刷新所有用户凭证(管理员)").alias("洛克刷新所有凭证").action(async ({ session }) => {
3578
3593
  if (!isAdmin(session.userId)) return "此指令仅限管理员使用。";
3579
3594
  await session.send("正在刷新所有用户的凭证...");
3580
3595
  const allUsers = userMgr.getAllUsersBindings();
@@ -3603,7 +3618,7 @@ function register6(deps) {
3603
3618
  }
3604
3619
  return `刷新完成:成功 ${success},失败 ${fail}`;
3605
3620
  });
3606
- ctx.command("洛克").subcommand(".删除失效绑定", "清理失效绑定(管理员)").action(async ({ session }) => {
3621
+ ctx.command("洛克").subcommand(".删除失效绑定", "清理失效绑定(管理员)").alias("洛克删除失效绑定").action(async ({ session }) => {
3607
3622
  if (!isAdmin(session.userId)) return "此指令仅限管理员使用。";
3608
3623
  await session.send("正在检查所有用户的绑定有效性...");
3609
3624
  const allUsers = userMgr.getAllUsersBindings();
@@ -3776,7 +3791,8 @@ function apply(ctx, config) {
3776
3791
  const homeSubMgr = new HomeSubscriptionManager(dataDir);
3777
3792
  const resPath = import_node_path5.default.resolve(__dirname, "..");
3778
3793
  const renderer = new Renderer(resPath);
3779
- 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");
3780
3796
  const eggService = new EggService(searcheggsDir);
3781
3797
  const deps = { ctx, config, client, userMgr, merchantSubMgr, homeSubMgr, eggService, renderer };
3782
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.2",
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",