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 +1 -0
- package/lib/client.js +50 -45
- 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 +20 -1
- package/lib/commands/wiki.js +2 -0
- package/lib/index.js +88 -72
- 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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
251
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
445
|
-
|
|
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 });
|
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
|
@@ -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 =
|
|
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 '此指令仅限管理员使用。';
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
459
|
+
const { status, data } = await this.requestIngameWithFallback(
|
|
452
460
|
ctx,
|
|
453
461
|
"/api/v1/games/rocom/ingame/merchant/info",
|
|
454
|
-
|
|
455
|
-
params,
|
|
456
|
-
{ silentFailureDetails: true }
|
|
462
|
+
params
|
|
457
463
|
);
|
|
458
|
-
|
|
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 =
|
|
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
|
|
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
|
-
<
|
|
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