koishi-plugin-rocom 1.0.1
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 +53 -0
- package/lib/client.js +473 -0
- package/lib/commands/account.d.ts +5 -0
- package/lib/commands/account.js +205 -0
- package/lib/commands/admin.d.ts +2 -0
- package/lib/commands/admin.js +117 -0
- package/lib/commands/egg.d.ts +2 -0
- package/lib/commands/egg.js +196 -0
- package/lib/commands/merchant.d.ts +2 -0
- package/lib/commands/merchant.js +242 -0
- package/lib/commands/query.d.ts +2 -0
- package/lib/commands/query.js +1264 -0
- package/lib/commands/wiki.d.ts +2 -0
- package/lib/commands/wiki.js +11 -0
- package/lib/egg-service.d.ts +229 -0
- package/lib/egg-service.js +705 -0
- package/lib/index.d.ts +24 -0
- package/lib/index.js +3746 -0
- package/lib/render-templates/bind-list/index.html +51 -0
- package/lib/render-templates/bind-list/style.css +178 -0
- package/lib/render-templates/exchange-hall/css/_@astro-renderers.0KDkAyVb.css +1 -0
- package/lib/render-templates/exchange-hall/css/index.B3tv56V6.css +1 -0
- package/lib/render-templates/exchange-hall/css/index.D2LGPudy.css +1 -0
- package/lib/render-templates/exchange-hall/extracted.css +393 -0
- package/lib/render-templates/exchange-hall/index.html +99 -0
- package/lib/render-templates/exchange-hall/style.css +267 -0
- package/lib/render-templates/friendship/index.html +58 -0
- package/lib/render-templates/friendship/style.css +182 -0
- package/lib/render-templates/home/data/home_item_list.json +122 -0
- package/lib/render-templates/home/img/home_icon/100604.png +0 -0
- package/lib/render-templates/home/img/home_icon/100604_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100604_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100605.png +0 -0
- package/lib/render-templates/home/img/home_icon/100605_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100605_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100606.png +0 -0
- package/lib/render-templates/home/img/home_icon/100606_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100606_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100607.png +0 -0
- package/lib/render-templates/home/img/home_icon/100607_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100607_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100608.png +0 -0
- package/lib/render-templates/home/img/home_icon/100608_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100608_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100622.png +0 -0
- package/lib/render-templates/home/img/home_icon/100622_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100622_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100623.png +0 -0
- package/lib/render-templates/home/img/home_icon/100623_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100623_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100624.png +0 -0
- package/lib/render-templates/home/img/home_icon/100624_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100624_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100627.png +0 -0
- package/lib/render-templates/home/img/home_icon/100627_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100627_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100684.png +0 -0
- package/lib/render-templates/home/img/home_icon/100684_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100684_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100686.png +0 -0
- package/lib/render-templates/home/img/home_icon/100686_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100686_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100687.png +0 -0
- package/lib/render-templates/home/img/home_icon/100687_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100687_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100689.png +0 -0
- package/lib/render-templates/home/img/home_icon/100689_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100689_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100690.png +0 -0
- package/lib/render-templates/home/img/home_icon/100690_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100690_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100691.png +0 -0
- package/lib/render-templates/home/img/home_icon/100691_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100691_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100692.png +0 -0
- package/lib/render-templates/home/img/home_icon/100692_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100692_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100693.png +0 -0
- package/lib/render-templates/home/img/home_icon/100693_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100693_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100694.png +0 -0
- package/lib/render-templates/home/img/home_icon/100694_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100694_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100706.png +0 -0
- package/lib/render-templates/home/img/home_icon/100706_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100706_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100751.png +0 -0
- package/lib/render-templates/home/img/home_icon/100751_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100751_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100755.png +0 -0
- package/lib/render-templates/home/img/home_icon/100755_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100755_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100762.png +0 -0
- package/lib/render-templates/home/img/home_icon/100762_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100762_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100764.png +0 -0
- package/lib/render-templates/home/img/home_icon/100764_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100764_2.png +0 -0
- package/lib/render-templates/home/img/home_icon/100869.png +0 -0
- package/lib/render-templates/home/img/home_icon/100869_1.png +0 -0
- package/lib/render-templates/home/img/home_icon/100869_2.png +0 -0
- package/lib/render-templates/home/img/img_HomeVisit_Icon1.png +0 -0
- package/lib/render-templates/home/img/img_LevelReward_Bg2.png +0 -0
- package/lib/render-templates/home/index.html +139 -0
- package/lib/render-templates/home/style.css +537 -0
- package/lib/render-templates/ingame-shop/index.html +87 -0
- package/lib/render-templates/ingame-shop/style.css +220 -0
- package/lib/render-templates/inspect/index.html +47 -0
- package/lib/render-templates/inspect/style.css +149 -0
- package/lib/render-templates/lineup/index.html +77 -0
- package/lib/render-templates/lineup/style.css +255 -0
- package/lib/render-templates/lineup-detail/index.html +63 -0
- package/lib/render-templates/lineup-detail/style.css +218 -0
- package/lib/render-templates/menu/index.html +36 -0
- package/lib/render-templates/menu/style.css +126 -0
- package/lib/render-templates/package/index.html +115 -0
- package/lib/render-templates/package/style.css +352 -0
- package/lib/render-templates/personal-card/index.html +292 -0
- package/lib/render-templates/personal-card/style.css +2114 -0
- package/lib/render-templates/pet-wiki/index.html +118 -0
- package/lib/render-templates/pet-wiki/style.css +382 -0
- package/lib/render-templates/player-search/index.html +60 -0
- package/lib/render-templates/player-search/style.css +192 -0
- package/lib/render-templates/record/index.html +86 -0
- package/lib/render-templates/record/style.css +322 -0
- package/lib/render-templates/searcheggs/Pets.json +104328 -0
- package/lib/render-templates/searcheggs/candidates.html +52 -0
- package/lib/render-templates/searcheggs/eggs.py +599 -0
- package/lib/render-templates/searcheggs/index.html +198 -0
- package/lib/render-templates/searcheggs/pair.html +81 -0
- package/lib/render-templates/searcheggs/size.html +82 -0
- package/lib/render-templates/searcheggs/style.css +586 -0
- package/lib/render-templates/searcheggs/want.html +63 -0
- package/lib/render-templates/skill-wiki/index.html +68 -0
- package/lib/render-templates/skill-wiki/style.css +182 -0
- package/lib/render-templates/student/index.html +95 -0
- package/lib/render-templates/student/style.css +255 -0
- package/lib/render-templates/student-perks/index.html +78 -0
- package/lib/render-templates/student-perks/style.css +238 -0
- package/lib/render-templates/student-state/index.html +52 -0
- package/lib/render-templates/student-state/style.css +157 -0
- package/lib/render-templates/yuanxing-shangren/index.html +371 -0
- package/lib/render-templates/yuanxing-shangren/style.css +371 -0
- package/lib/render.d.ts +11 -0
- package/lib/render.js +226 -0
- package/lib/role-token.d.ts +27 -0
- package/lib/role-token.js +137 -0
- package/lib/send-image.d.ts +3 -0
- package/lib/send-image.js +135 -0
- package/lib/subscription-send.d.ts +8 -0
- package/lib/subscription-send.js +48 -0
- package/lib/types.d.ts +32 -0
- package/lib/types.js +2 -0
- package/lib/user.d.ts +67 -0
- package/lib/user.js +176 -0
- package/package.json +58 -0
- package/readme.md +575 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPrimaryToken = getPrimaryToken;
|
|
4
|
+
exports.notLoggedInHint = notLoggedInHint;
|
|
5
|
+
exports.saveBindingWithRoleInfo = saveBindingWithRoleInfo;
|
|
6
|
+
exports.register = register;
|
|
7
|
+
const koishi_1 = require("koishi");
|
|
8
|
+
const role_token_1 = require("../role-token");
|
|
9
|
+
const send_image_1 = require("../send-image");
|
|
10
|
+
async function getPrimaryToken(deps, userId) {
|
|
11
|
+
const token = await (0, role_token_1.getRoleToken)(deps.ctx, userId);
|
|
12
|
+
return token?.fwt || '';
|
|
13
|
+
}
|
|
14
|
+
function notLoggedInHint() {
|
|
15
|
+
return '您尚未绑定洛克王国账号,请先使用“洛克.QQ登录”或“洛克.微信登录”进行绑定。';
|
|
16
|
+
}
|
|
17
|
+
function formatBindTime(bindTime) {
|
|
18
|
+
if (!bindTime || bindTime <= 0)
|
|
19
|
+
return '未知';
|
|
20
|
+
const date = new Date(bindTime);
|
|
21
|
+
const pad = (value) => String(value).padStart(2, '0');
|
|
22
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
23
|
+
}
|
|
24
|
+
function formatLoginType(loginType) {
|
|
25
|
+
const typeMap = {
|
|
26
|
+
qq: 'QQ',
|
|
27
|
+
wechat: '微信',
|
|
28
|
+
manual: '手动导入',
|
|
29
|
+
};
|
|
30
|
+
return typeMap[loginType] || loginType || '未知';
|
|
31
|
+
}
|
|
32
|
+
async function saveBindingWithRoleInfo(deps, session, fwToken, loginType, userId) {
|
|
33
|
+
const { ctx, client, userMgr } = deps;
|
|
34
|
+
await session.send('登录成功,正在调用绑定接口...');
|
|
35
|
+
const bindRes = await client.createBinding(ctx, fwToken, userId);
|
|
36
|
+
const bindingId = String(bindRes?.binding?.id || fwToken || '').trim();
|
|
37
|
+
if (!bindRes?.binding || !bindingId) {
|
|
38
|
+
await session.send('绑定接口调用失败,请稍后重试。');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
await session.send('绑定成功,正在获取角色信息...');
|
|
42
|
+
const roleRes = await client.getRole(ctx, fwToken, undefined, userId);
|
|
43
|
+
if (!roleRes?.role) {
|
|
44
|
+
await session.send('绑定成功,但获取角色信息失败,请尝试重新登录。');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const role = roleRes.role;
|
|
48
|
+
const binding = {
|
|
49
|
+
binding_id: bindingId,
|
|
50
|
+
login_type: loginType,
|
|
51
|
+
role_id: role.id || 'unknown',
|
|
52
|
+
nickname: role.name || '洛克',
|
|
53
|
+
bind_time: Date.now(),
|
|
54
|
+
is_primary: true,
|
|
55
|
+
};
|
|
56
|
+
userMgr.addBinding(userId, binding);
|
|
57
|
+
await (0, role_token_1.upsertRoleToken)(ctx, {
|
|
58
|
+
userId,
|
|
59
|
+
fwt: fwToken,
|
|
60
|
+
bindingId,
|
|
61
|
+
roleId: binding.role_id,
|
|
62
|
+
loginType,
|
|
63
|
+
});
|
|
64
|
+
await session.send(`绑定成功!当前账号:${binding.nickname} (ID: ${binding.role_id})`);
|
|
65
|
+
}
|
|
66
|
+
function register(deps) {
|
|
67
|
+
const { ctx, client, userMgr } = deps;
|
|
68
|
+
ctx.command('洛克').subcommand('.QQ登录', 'QQ 扫码登录')
|
|
69
|
+
.action(async ({ session }) => {
|
|
70
|
+
const userId = session.userId;
|
|
71
|
+
const qrData = await client.qqQrLogin(ctx, userId);
|
|
72
|
+
if (!qrData?.qr_image)
|
|
73
|
+
return '获取 QQ 二维码失败。';
|
|
74
|
+
const fwToken = qrData.frameworkToken;
|
|
75
|
+
const qrB64 = qrData.qr_image;
|
|
76
|
+
const imgData = qrB64.includes(',') ? qrB64.split(',')[1] : qrB64;
|
|
77
|
+
await session.send((0, koishi_1.h)('message', {}, koishi_1.h.at(userId), koishi_1.h.text('\n请使用 QQ 扫描二维码登录(有效时间 2 分钟)\n注意需要双设备扫码。\n'), koishi_1.h.image(`data:image/png;base64,${imgData}`)));
|
|
78
|
+
const startTime = Date.now();
|
|
79
|
+
while (Date.now() - startTime < 115000) {
|
|
80
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
81
|
+
const status = await client.qqQrStatus(ctx, fwToken, userId);
|
|
82
|
+
if (!status)
|
|
83
|
+
continue;
|
|
84
|
+
if (status.status === 'done') {
|
|
85
|
+
await saveBindingWithRoleInfo(deps, session, fwToken, 'qq', userId);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (['expired', 'failed', 'canceled'].includes(status.status))
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
return '登录超时或失败,请重试。';
|
|
92
|
+
});
|
|
93
|
+
ctx.command('洛克').subcommand('.微信登录', '微信扫码登录')
|
|
94
|
+
.action(async ({ session }) => {
|
|
95
|
+
const userId = session.userId;
|
|
96
|
+
const qrData = await client.wechatQrLogin(ctx, userId);
|
|
97
|
+
if (!qrData?.qr_image)
|
|
98
|
+
return '获取微信登录链接失败。';
|
|
99
|
+
const fwToken = qrData.frameworkToken;
|
|
100
|
+
const qrUrl = qrData.qr_image;
|
|
101
|
+
await session.send(`请使用微信打开以下链接扫码登录(有效时间 2 分钟)\n注意需要双设备扫码。\n${qrUrl}`);
|
|
102
|
+
const startTime = Date.now();
|
|
103
|
+
while (Date.now() - startTime < 115000) {
|
|
104
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
105
|
+
const status = await client.wechatQrStatus(ctx, fwToken, userId);
|
|
106
|
+
if (!status)
|
|
107
|
+
continue;
|
|
108
|
+
if (status.status === 'done') {
|
|
109
|
+
await saveBindingWithRoleInfo(deps, session, fwToken, 'wechat', userId);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (['expired', 'failed'].includes(status.status))
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
return '登录超时或失败,请重试。';
|
|
116
|
+
});
|
|
117
|
+
ctx.command('洛克').subcommand('.导入 <tgpId:string> <tgpTicket:string>', '导入 WeGame 凭证')
|
|
118
|
+
.action(async ({ session }, tgpId, tgpTicket) => {
|
|
119
|
+
if (!tgpId || !tgpTicket)
|
|
120
|
+
return '用法:洛克.导入 <tgp_id> <tgp_ticket>';
|
|
121
|
+
const userId = session.userId;
|
|
122
|
+
const res = await client.importToken(ctx, tgpId, tgpTicket, userId);
|
|
123
|
+
if (!res?.frameworkToken)
|
|
124
|
+
return '凭证导入失败。';
|
|
125
|
+
await saveBindingWithRoleInfo(deps, session, res.frameworkToken, 'manual', userId);
|
|
126
|
+
});
|
|
127
|
+
ctx.command('洛克').subcommand('.绑定列表', '查看已绑定账号')
|
|
128
|
+
.action(async ({ session }) => {
|
|
129
|
+
const bindings = userMgr.getUserBindings(session.userId);
|
|
130
|
+
if (!bindings.length)
|
|
131
|
+
return '暂无绑定账号。';
|
|
132
|
+
const bindItems = bindings.map((binding, index) => ({
|
|
133
|
+
index: index + 1,
|
|
134
|
+
nickname: binding.nickname || '未知',
|
|
135
|
+
isPrimary: Boolean(binding.is_primary),
|
|
136
|
+
role_id: binding.role_id || '未知',
|
|
137
|
+
type_label: formatLoginType(binding.login_type),
|
|
138
|
+
created_at: formatBindTime(binding.bind_time),
|
|
139
|
+
}));
|
|
140
|
+
const data = {
|
|
141
|
+
title: '绑定账号列表',
|
|
142
|
+
subtitle: `共找到 ${bindings.length} 个有效绑定账号`,
|
|
143
|
+
bindings: bindItems,
|
|
144
|
+
commandHint: '洛克.切换 <序号> 切换主账号 | 洛克.解绑 <序号> 移除绑定',
|
|
145
|
+
copyright: 'Koishi & WeGame 洛克王国插件',
|
|
146
|
+
};
|
|
147
|
+
const fallbackLines = ['【绑定账号列表】'];
|
|
148
|
+
bindItems.forEach((binding) => {
|
|
149
|
+
const mark = binding.isPrimary ? '(主账号)' : '';
|
|
150
|
+
fallbackLines.push(`[${binding.index}] ${binding.nickname} (ID: ${binding.role_id}) ${binding.type_label}${mark} · ${binding.created_at}`);
|
|
151
|
+
});
|
|
152
|
+
const png = await deps.renderer.renderHtml(ctx, 'bind-list', data);
|
|
153
|
+
await (0, send_image_1.sendImageWithFallback)(session, png, fallbackLines.join('\n'), 'account:bind-list', deps.config);
|
|
154
|
+
});
|
|
155
|
+
ctx.command('洛克').subcommand('.切换 <index:number>', '切换主账号')
|
|
156
|
+
.action(async ({ session }, index) => {
|
|
157
|
+
if (!index)
|
|
158
|
+
return '用法:洛克.切换 <序号>';
|
|
159
|
+
return userMgr.switchPrimary(session.userId, index)
|
|
160
|
+
? `成功切换到序号 ${index} 账号。`
|
|
161
|
+
: '序号无效。';
|
|
162
|
+
});
|
|
163
|
+
ctx.command('洛克').subcommand('.解绑 <index:number>', '解绑账号')
|
|
164
|
+
.action(async ({ session }, index) => {
|
|
165
|
+
if (!index)
|
|
166
|
+
return '用法:洛克.解绑 <序号>';
|
|
167
|
+
const removed = userMgr.deleteUserBinding(session.userId, index);
|
|
168
|
+
if (!removed)
|
|
169
|
+
return '序号无效。';
|
|
170
|
+
if (removed.binding_id) {
|
|
171
|
+
try {
|
|
172
|
+
await client.deleteBinding(ctx, removed.binding_id, session.userId);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// 解绑本地记录优先,服务端删除失败不阻断结果。
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (userMgr.getUserBindings(session.userId).length === 0) {
|
|
179
|
+
await (0, role_token_1.removeRoleToken)(ctx, session.userId);
|
|
180
|
+
}
|
|
181
|
+
return `已解绑账号:${removed.nickname}`;
|
|
182
|
+
});
|
|
183
|
+
ctx.command('洛克').subcommand('.刷新', '刷新当前主账号凭证')
|
|
184
|
+
.action(async ({ session }) => {
|
|
185
|
+
const userId = session.userId;
|
|
186
|
+
const binding = userMgr.getPrimaryBinding(userId);
|
|
187
|
+
if (!binding)
|
|
188
|
+
return notLoggedInHint();
|
|
189
|
+
if (!binding.binding_id)
|
|
190
|
+
return '绑定 ID 无效,请重新绑定账号。';
|
|
191
|
+
await session.send('正在刷新凭证,服务端会自动处理,无需手动操作。');
|
|
192
|
+
const res = await client.refreshBinding(ctx, binding.binding_id, userId);
|
|
193
|
+
if (res?.framework_token) {
|
|
194
|
+
await (0, role_token_1.upsertRoleToken)(ctx, {
|
|
195
|
+
userId,
|
|
196
|
+
fwt: res.framework_token,
|
|
197
|
+
bindingId: binding.binding_id,
|
|
198
|
+
roleId: binding.role_id,
|
|
199
|
+
loginType: binding.login_type,
|
|
200
|
+
});
|
|
201
|
+
return '当前账号凭证刷新成功。';
|
|
202
|
+
}
|
|
203
|
+
return '凭证刷新失败,可能已过期或不支持刷新(仅 QQ 扫码支持)。';
|
|
204
|
+
});
|
|
205
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.register = register;
|
|
4
|
+
const koishi_1 = require("koishi");
|
|
5
|
+
const role_token_1 = require("../role-token");
|
|
6
|
+
const logger = new koishi_1.Logger('rocom-admin');
|
|
7
|
+
function register(deps) {
|
|
8
|
+
const { ctx, config, client, userMgr } = deps;
|
|
9
|
+
function isAdmin(userId) {
|
|
10
|
+
return config.adminUserIds.includes(userId);
|
|
11
|
+
}
|
|
12
|
+
ctx.command('洛克').subcommand('.刷新所有凭证', '刷新所有用户凭证(管理员)')
|
|
13
|
+
.action(async ({ session }) => {
|
|
14
|
+
if (!isAdmin(session.userId))
|
|
15
|
+
return '此指令仅限管理员使用。';
|
|
16
|
+
await session.send('正在刷新所有用户的凭证...');
|
|
17
|
+
const allUsers = userMgr.getAllUsersBindings();
|
|
18
|
+
let success = 0;
|
|
19
|
+
let fail = 0;
|
|
20
|
+
for (const [userId] of Object.entries(allUsers)) {
|
|
21
|
+
const binding = userMgr.getPrimaryBinding(userId);
|
|
22
|
+
if (!binding || binding.login_type !== 'qq' || !binding.binding_id)
|
|
23
|
+
continue;
|
|
24
|
+
try {
|
|
25
|
+
const res = await client.refreshBinding(ctx, binding.binding_id, userId);
|
|
26
|
+
if (res?.framework_token) {
|
|
27
|
+
await (0, role_token_1.upsertRoleToken)(ctx, {
|
|
28
|
+
userId,
|
|
29
|
+
fwt: res.framework_token,
|
|
30
|
+
bindingId: binding.binding_id,
|
|
31
|
+
roleId: binding.role_id,
|
|
32
|
+
loginType: binding.login_type,
|
|
33
|
+
});
|
|
34
|
+
success++;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
fail++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
fail++;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return `刷新完成:成功 ${success},失败 ${fail}`;
|
|
45
|
+
});
|
|
46
|
+
ctx.command('洛克').subcommand('.删除失效绑定', '清理失效绑定(管理员)')
|
|
47
|
+
.action(async ({ session }) => {
|
|
48
|
+
if (!isAdmin(session.userId))
|
|
49
|
+
return '此指令仅限管理员使用。';
|
|
50
|
+
await session.send('正在检查所有用户的绑定有效性...');
|
|
51
|
+
const allUsers = userMgr.getAllUsersBindings();
|
|
52
|
+
let totalInvalid = 0;
|
|
53
|
+
let totalValid = 0;
|
|
54
|
+
for (const [userId, bindings] of Object.entries(allUsers)) {
|
|
55
|
+
const token = await (0, role_token_1.getRoleToken)(ctx, userId);
|
|
56
|
+
if (!token?.fwt) {
|
|
57
|
+
logger.warn(`用户 ${userId} 没有可用的 fwt,跳过失效检测`);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const fwToken = token.fwt;
|
|
61
|
+
const valid = [];
|
|
62
|
+
for (const binding of bindings) {
|
|
63
|
+
const roleRes = await client.getRole(ctx, fwToken, undefined, userId);
|
|
64
|
+
if (roleRes?.role) {
|
|
65
|
+
valid.push(binding);
|
|
66
|
+
totalValid++;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (binding.binding_id) {
|
|
70
|
+
try {
|
|
71
|
+
await client.deleteBinding(ctx, binding.binding_id, userId);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
logger.warn(`删除用户 ${userId} 服务端绑定 ${binding.binding_id} 失败:${e}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
totalInvalid++;
|
|
78
|
+
}
|
|
79
|
+
userMgr.saveUserBindings(userId, valid);
|
|
80
|
+
if (valid.length === 0) {
|
|
81
|
+
await (0, role_token_1.removeRoleToken)(ctx, userId);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return totalInvalid > 0
|
|
85
|
+
? `清理完成:移除 ${totalInvalid} 个无效绑定,剩余 ${totalValid} 个有效绑定。`
|
|
86
|
+
: `所有绑定均有效,无需清理。共 ${totalValid} 个有效绑定。`;
|
|
87
|
+
});
|
|
88
|
+
if (config.autoRefreshEnabled) {
|
|
89
|
+
ctx.setInterval(async () => {
|
|
90
|
+
const now = new Date();
|
|
91
|
+
const timeStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
|
|
92
|
+
if (!config.autoRefreshTime.includes(timeStr))
|
|
93
|
+
return;
|
|
94
|
+
const allUsers = userMgr.getAllUsersBindings();
|
|
95
|
+
for (const [userId] of Object.entries(allUsers)) {
|
|
96
|
+
const binding = userMgr.getPrimaryBinding(userId);
|
|
97
|
+
if (!binding || binding.login_type !== 'qq' || !binding.binding_id)
|
|
98
|
+
continue;
|
|
99
|
+
try {
|
|
100
|
+
const res = await client.refreshBinding(ctx, binding.binding_id, userId);
|
|
101
|
+
if (res?.framework_token) {
|
|
102
|
+
await (0, role_token_1.upsertRoleToken)(ctx, {
|
|
103
|
+
userId,
|
|
104
|
+
fwt: res.framework_token,
|
|
105
|
+
bindingId: binding.binding_id,
|
|
106
|
+
roleId: binding.role_id,
|
|
107
|
+
loginType: binding.login_type,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
logger.warn(`自动刷新用户 ${userId} 失败: ${e}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}, 60000);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.register = register;
|
|
4
|
+
const send_image_1 = require("../send-image");
|
|
5
|
+
function petName(p) {
|
|
6
|
+
return p?.localized?.zh?.name || p?.name || '未知精灵';
|
|
7
|
+
}
|
|
8
|
+
function parseHeightValue(raw) {
|
|
9
|
+
const text = String(raw ?? '')
|
|
10
|
+
.trim()
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.replace(/^(身高|高度|h)\s*/i, '')
|
|
13
|
+
.trim();
|
|
14
|
+
const match = text.match(/^([0-9]+(?:\.[0-9]+)?)(?:\s*(m|米))?$/);
|
|
15
|
+
if (!match)
|
|
16
|
+
return null;
|
|
17
|
+
const meterValue = Number(match[1]);
|
|
18
|
+
if (!Number.isFinite(meterValue))
|
|
19
|
+
return null;
|
|
20
|
+
return {
|
|
21
|
+
dataValue: meterValue * 100,
|
|
22
|
+
meterValue,
|
|
23
|
+
display: `${formatNumber(meterValue)} m`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function parseWeightValue(raw) {
|
|
27
|
+
const text = String(raw ?? '')
|
|
28
|
+
.trim()
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/^(体重|重量|w)\s*/i, '')
|
|
31
|
+
.trim();
|
|
32
|
+
const match = text.match(/^([0-9]+(?:\.[0-9]+)?)(?:\s*(kg|千克|公斤))?$/);
|
|
33
|
+
if (!match)
|
|
34
|
+
return null;
|
|
35
|
+
const value = Number(match[1]);
|
|
36
|
+
return Number.isFinite(value) ? value : null;
|
|
37
|
+
}
|
|
38
|
+
function formatNumber(value) {
|
|
39
|
+
return Number.isInteger(value) ? String(value) : String(Number(value.toFixed(4)));
|
|
40
|
+
}
|
|
41
|
+
function searchResultCandidates(result) {
|
|
42
|
+
return result.candidates || [];
|
|
43
|
+
}
|
|
44
|
+
async function sendEggImage(deps, session, templateName, data, fallback) {
|
|
45
|
+
const png = await deps.renderer.renderHtml(deps.ctx, templateName, data);
|
|
46
|
+
await (0, send_image_1.sendImageWithFallback)(session, png, fallback, `egg:${templateName}`, deps.config);
|
|
47
|
+
}
|
|
48
|
+
function register(deps) {
|
|
49
|
+
const { ctx, client, eggService } = deps;
|
|
50
|
+
ctx.command('洛克').subcommand('.查蛋 [arg1:string] [arg2:string]', '查询精灵蛋组')
|
|
51
|
+
.action(async ({ session }, arg1, arg2) => {
|
|
52
|
+
if (!arg1) {
|
|
53
|
+
return [
|
|
54
|
+
'查蛋用法:',
|
|
55
|
+
' 洛克.查蛋 <精灵名>',
|
|
56
|
+
' 洛克.查蛋 0.18 1.5',
|
|
57
|
+
' 洛克.查蛋 0.18m 1.5kg',
|
|
58
|
+
' 洛克.查蛋 身高0.18m 体重1.5kg',
|
|
59
|
+
].join('\n');
|
|
60
|
+
}
|
|
61
|
+
let height;
|
|
62
|
+
let heightMeters;
|
|
63
|
+
let heightDisplay;
|
|
64
|
+
let weight;
|
|
65
|
+
const nameParts = [];
|
|
66
|
+
const numericArgs = [];
|
|
67
|
+
for (const rawArg of [arg1, arg2]) {
|
|
68
|
+
if (!rawArg)
|
|
69
|
+
continue;
|
|
70
|
+
const arg = String(rawArg).trim();
|
|
71
|
+
const explicitHeight = /^(身高|高度|h)/i.test(arg);
|
|
72
|
+
const explicitWeight = /^(体重|重量|w)/i.test(arg);
|
|
73
|
+
if (explicitHeight) {
|
|
74
|
+
const parsed = parseHeightValue(arg);
|
|
75
|
+
if (parsed) {
|
|
76
|
+
height = parsed.dataValue;
|
|
77
|
+
heightMeters = parsed.meterValue;
|
|
78
|
+
heightDisplay = parsed.display;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (explicitWeight) {
|
|
83
|
+
const parsed = parseWeightValue(arg);
|
|
84
|
+
if (parsed != null) {
|
|
85
|
+
weight = parsed;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const heightCandidate = parseHeightValue(arg);
|
|
90
|
+
const weightCandidate = parseWeightValue(arg);
|
|
91
|
+
if (heightCandidate || weightCandidate != null) {
|
|
92
|
+
numericArgs.push({ height: heightCandidate, weight: weightCandidate });
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
nameParts.push(arg);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (numericArgs.length) {
|
|
99
|
+
if (height == null && numericArgs[0]?.height) {
|
|
100
|
+
height = numericArgs[0].height.dataValue;
|
|
101
|
+
heightMeters = numericArgs[0].height.meterValue;
|
|
102
|
+
heightDisplay = numericArgs[0].height.display;
|
|
103
|
+
}
|
|
104
|
+
if (weight == null && numericArgs[1]?.weight != null) {
|
|
105
|
+
weight = numericArgs[1].weight;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (height != null || weight != null) {
|
|
109
|
+
let data = null;
|
|
110
|
+
let fallback = '';
|
|
111
|
+
if (height != null && weight != null) {
|
|
112
|
+
const backendResults = await client.queryPetSize(ctx, heightMeters ?? height / 100, weight);
|
|
113
|
+
if (backendResults) {
|
|
114
|
+
data = eggService.buildSizeSearchDataFromApi(height, weight, backendResults, heightDisplay);
|
|
115
|
+
fallback = eggService.buildSizeSearchTextFromApi(height, weight, backendResults, heightDisplay);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!data) {
|
|
119
|
+
const results = eggService.searchBySize(height, weight);
|
|
120
|
+
data = eggService.buildSizeSearchData(height, weight, results, heightDisplay);
|
|
121
|
+
fallback = eggService.buildSizeSearchText(height, weight, results, heightDisplay);
|
|
122
|
+
}
|
|
123
|
+
await sendEggImage(deps, session, 'searcheggs/size', data, fallback);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const name = nameParts.join(' ');
|
|
127
|
+
if (!name)
|
|
128
|
+
return '请输入精灵名称。用法:洛克.查蛋 <精灵名>';
|
|
129
|
+
const sr = eggService.search(name);
|
|
130
|
+
if (sr.matchType === 'multi') {
|
|
131
|
+
const candidates = searchResultCandidates(sr);
|
|
132
|
+
const data = eggService.buildCandidatesRenderData(name, candidates);
|
|
133
|
+
await sendEggImage(deps, session, 'searcheggs/candidates', data, eggService.buildCandidatesText(name, candidates));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (sr.matchType === 'not_found')
|
|
137
|
+
return `未找到名为「${name}」的精灵,请检查名称后重试。`;
|
|
138
|
+
const pet = sr.pet;
|
|
139
|
+
const data = eggService.buildSearchData(pet);
|
|
140
|
+
data.commandHint = '洛克.查蛋 <名称> | 洛克.查蛋 身高0.18m 体重1.5kg | 洛克.配种 <父体> <母体>';
|
|
141
|
+
data.copyright = 'Koishi & WeGame 洛克王国插件';
|
|
142
|
+
const hint = sr.matchType === 'fuzzy' ? `模糊匹配到「${petName(pet)}」\n` : '';
|
|
143
|
+
await sendEggImage(deps, session, 'searcheggs', data, hint + eggService.buildSearchText(pet));
|
|
144
|
+
});
|
|
145
|
+
ctx.command('洛克').subcommand('.配种 <nameA:string> [nameB:string]', '配种查询')
|
|
146
|
+
.action(async ({ session }, nameA, nameB) => {
|
|
147
|
+
if (!nameA) {
|
|
148
|
+
return [
|
|
149
|
+
'配种用法:',
|
|
150
|
+
' 洛克.配种 <精灵名>',
|
|
151
|
+
' 查询想孵出这个精灵时可选哪些父体。',
|
|
152
|
+
' 洛克.配种 <父体> <母体>',
|
|
153
|
+
' 判断两只精灵是否可以配种,默认前父后母,孵蛋结果跟随母体。',
|
|
154
|
+
'示例:',
|
|
155
|
+
' 洛克.配种 喵喵',
|
|
156
|
+
' 洛克.配种 父体名称 母体名称',
|
|
157
|
+
].join('\n');
|
|
158
|
+
}
|
|
159
|
+
if (!nameB) {
|
|
160
|
+
const sr = eggService.search(nameA);
|
|
161
|
+
if (sr.matchType === 'multi') {
|
|
162
|
+
const candidates = searchResultCandidates(sr);
|
|
163
|
+
const data = eggService.buildCandidatesRenderData(nameA, candidates);
|
|
164
|
+
await sendEggImage(deps, session, 'searcheggs/candidates', data, eggService.buildCandidatesText(nameA, candidates));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (sr.matchType === 'not_found')
|
|
168
|
+
return `未找到名为「${nameA}」的精灵。`;
|
|
169
|
+
const data = eggService.buildWantPetData(sr.pet);
|
|
170
|
+
await sendEggImage(deps, session, 'searcheggs/want', data, eggService.buildWantPetText(sr.pet));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const srA = eggService.search(nameA);
|
|
174
|
+
if (srA.matchType === 'multi') {
|
|
175
|
+
const candidates = searchResultCandidates(srA);
|
|
176
|
+
const data = eggService.buildCandidatesRenderData(nameA, candidates);
|
|
177
|
+
await sendEggImage(deps, session, 'searcheggs/candidates', data, eggService.buildCandidatesText(nameA, candidates));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (srA.matchType === 'not_found')
|
|
181
|
+
return `未找到名为「${nameA}」的精灵。`;
|
|
182
|
+
const srB = eggService.search(nameB);
|
|
183
|
+
if (srB.matchType === 'multi') {
|
|
184
|
+
const candidates = searchResultCandidates(srB);
|
|
185
|
+
const data = eggService.buildCandidatesRenderData(nameB, candidates);
|
|
186
|
+
await sendEggImage(deps, session, 'searcheggs/candidates', data, eggService.buildCandidatesText(nameB, candidates));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (srB.matchType === 'not_found')
|
|
190
|
+
return `未找到名为「${nameB}」的精灵。`;
|
|
191
|
+
const data = eggService.buildPairData(srB.pet, srA.pet);
|
|
192
|
+
data.commandHint = '默认前父后母,孵蛋结果跟随母体 | 洛克.配种 <精灵名> 查询怎么孵';
|
|
193
|
+
data.copyright = 'Koishi & WeGame 洛克王国插件';
|
|
194
|
+
await sendEggImage(deps, session, 'searcheggs/pair', data, eggService.buildPairText(srB.pet, srA.pet));
|
|
195
|
+
});
|
|
196
|
+
}
|