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 +1 -1
- package/lib/client.js +46 -106
- package/lib/commands/account.js +7 -0
- package/lib/commands/admin.js +2 -0
- package/lib/commands/egg.js +2 -0
- package/lib/commands/merchant.js +1 -0
- package/lib/commands/query.js +11 -0
- package/lib/commands/wiki.js +2 -0
- package/lib/index.js +74 -131
- package/lib/render-templates/menu/index.html +5 -6
- package/lib/render-templates/menu/style.css +44 -46
- package/lib/render.js +1 -0
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
498
|
-
|
|
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 });
|
package/lib/commands/account.js
CHANGED
|
@@ -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);
|
package/lib/commands/admin.js
CHANGED
|
@@ -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 '此指令仅限管理员使用。';
|
package/lib/commands/egg.js
CHANGED
|
@@ -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 [
|
package/lib/commands/merchant.js
CHANGED
|
@@ -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 '此指令仅限管理员使用。';
|
package/lib/commands/query.js
CHANGED
|
@@ -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 '此指令仅限管理员使用。';
|
package/lib/commands/wiki.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
504
|
-
params,
|
|
505
|
-
{ silentFailureDetails: true }
|
|
462
|
+
params
|
|
506
463
|
);
|
|
507
|
-
|
|
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
|
|
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
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
background
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
.
|
|
38
|
-
position:
|
|
39
|
-
|
|
40
|
-
|
|
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-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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