feat:优化login逻辑

This commit is contained in:
Jerry 2025-10-01 01:21:02 +08:00
parent 822876be6b
commit c5abaf132d

View File

@ -1,15 +1,15 @@
import plugin from '../../../lib/plugins/plugin.js'; import plugin from '../../../lib/plugins/plugin.js';
import path from 'path'; import path from 'path';
import ConfigControl from '../lib/config/configControl.js'; import ConfigControl from '../lib/config/configControl.js';
import config from '../../../lib/config/config.js';
import configControl from '../lib/config/configControl.js'; import configControl from '../lib/config/configControl.js';
import axios from 'axios';
import Meme from '../lib/core/meme.js'; import Meme from '../lib/core/meme.js';
import NapcatService from '../lib/login/napcat.js'; import NapcatService from '../lib/login/napcat.js';
import LgrService from '../lib/login/lgr.js'; import LgrService from '../lib/login/lgr.js';
const configPath = path.join(process.cwd(), 'data/crystelf/config'); const configPath = path.join(process.cwd(), 'data/crystelf/config');
const loginSessions = new Map(); //正在进行的登录会话 const loginSessions = new Map(); //正在进行的登录会话
const bindSessions = new Map(); //正在进行的绑定会话
const activeLogins = new Map(); //在线登录实例
export default class LoginService extends plugin { export default class LoginService extends plugin {
constructor() { constructor() {
@ -24,13 +24,17 @@ export default class LoginService extends plugin {
fnc: 'loginHandler', fnc: 'loginHandler',
}, },
{ {
reg: '^#绑定账号\\s+\\d+$', reg: '^#绑定账号(\\s+\\d+)?$',
fnc: 'bindAccount', fnc: 'bindAccount',
}, },
{ {
reg: '^#解绑账号\\s+\\d+$', reg: '^#解绑账号\\s+\\d+$',
fnc: 'unbindAccount', fnc: 'unbindAccount',
}, },
{
reg: '^#退出登录\\s+\\d+$',
fnc: 'logoutHandler',
},
], ],
}); });
} }
@ -54,17 +58,15 @@ export default class LoginService extends plugin {
if (!targetQq) { if (!targetQq) {
const binds = config?.login?.userBinds[userId] || []; const binds = config?.login?.userBinds[userId] || [];
if (binds.length === 0) { if (binds.length === 0) {
if (isAdmin) { return e.reply('管理员似乎没有给你分配可用账户,请联系管理员添加..', true);
loginSessions.set(userId, { step: 'askQq', admin: true });
return e.reply('你想登哪个qq?', true);
} else {
return e.reply('管理员似乎没有给你分配可用账户,请联系管理员添加..', true);
}
} else if (binds.length === 1) { } else if (binds.length === 1) {
targetQq = binds[0]; targetQq = binds[0].qq;
} else { } else {
loginSessions.set(userId, { step: 'chooseQq', options: binds }); loginSessions.set(userId, { step: 'chooseQq', options: binds });
return e.reply(`你小子账号还挺多,选一个qq登录吧:\n${binds.join('\n')}`, true); return e.reply(
`你小子账号还挺多,选一个qq登录吧:\n${binds.map((b) => b.qq).join('\n')}`,
true
);
} }
} }
@ -72,10 +74,11 @@ export default class LoginService extends plugin {
await this.startAdminLogin(e, targetQq); await this.startAdminLogin(e, targetQq);
} else { } else {
const binds = config?.login?.userBinds[userId] || []; const binds = config?.login?.userBinds[userId] || [];
if (!binds.includes(targetQq)) { const bind = binds.find((b) => b.qq === targetQq);
if (!bind) {
return e.reply('你没有权限登录该账号,请联系管理员分配..', true); return e.reply('你没有权限登录该账号,请联系管理员分配..', true);
} }
await this.startUserLogin(e, targetQq); await this.startUserLogin(e, bind);
} }
} }
@ -97,16 +100,22 @@ export default class LoginService extends plugin {
/** /**
* 普通用户 * 普通用户
* @param e * @param e
* @param qq * @param bind
* @returns {Promise<*>} * @returns {Promise<*>}
*/ */
async startUserLogin(e, qq) { async startUserLogin(e, bind) {
loginSessions.set(e.user_id, { loginSessions.set(e.user_id, {
step: 'askMethod', step: 'autoLogin',
qq, qq: bind.qq,
nickname: bind.nickname,
method: bind.method,
admin: false, admin: false,
}); });
return e.reply(`请选择登录方式\nnc或lgr\n来登录 QQ[${qq}]`, true); return this.doLogin(e, {
qq: bind.qq,
nickname: bind.nickname,
method: bind.method,
});
} }
/** /**
@ -115,23 +124,13 @@ export default class LoginService extends plugin {
* @returns {Promise<*>} * @returns {Promise<*>}
*/ */
async bindAccount(e) { async bindAccount(e) {
let config = await configControl.get()?.login;
if (!e.isMaster) { if (!e.isMaster) {
const img = await Meme.getMeme('zhenxun', 'default'); const img = await Meme.getMeme('zhenxun', 'default');
return e.reply(segment.image(img)); return e.reply(segment.image(img));
} }
const match = e.msg.match(/^#绑定账号\s+(\d+)$/);
if (!match) return;
const qq = match[1];
const at = e.at || e.user_id; const at = e.at || e.user_id;
if (!config?.userBinds[at]) config.userBinds[at] = []; bindSessions.set(e.user_id, { step: 'askQq', targetUser: at });
if (!config?.userBinds[at].includes(qq)) { return e.reply('要绑定的QQ号是哪个?', true);
config.userBinds[at].push(qq);
await ConfigControl.set('login', config);
return e.reply(`已为 ${at} 绑定账号 ${qq}`, true);
} else {
return e.reply(`该用户已绑定此账号`, true);
}
} }
/** /**
@ -150,14 +149,38 @@ export default class LoginService extends plugin {
const qq = match[1]; const qq = match[1];
const at = e.at || e.user_id; const at = e.at || e.user_id;
if (!config?.userBinds[at]) { if (!config?.userBinds[at]) {
``; return e.reply('该用户没有绑定账号..', true);
return e.reply('该用户没有绑定账号', true);
} }
config.userBinds[at] = config.userBinds[at].filter((q) => q !== qq); config.userBinds[at] = config.userBinds[at].filter((q) => q.qq !== qq);
await ConfigControl.set('login', config); await ConfigControl.set('login', config);
return e.reply(`已为 ${at} 解绑账号 ${qq}`, true); return e.reply(`已为 ${at} 解绑账号 ${qq}`, true);
} }
/**
* 退出登录
* @param e
* @returns {Promise<*>}
*/
async logoutHandler(e) {
const match = e.msg.match(/^#退出登录\s+(\d+)$/);
if (!match) return;
const qq = match[1];
const instance = activeLogins.get(qq);
if (!instance) {
return e.reply(`QQ[${qq}] 没有活跃的登录会话..`, true);
}
let config = await configControl.get();
const isAdmin = e.isMaster;
const userId = e.user_id;
const binds = config?.login?.userBinds[userId] || [];
if (!isAdmin && !binds.includes(qq)) {
return e.reply(`你没有权限退出 QQ[${qq}] 的会话..`, true);
}
await instance.disconnect();
activeLogins.delete(qq);
return e.reply(`QQ[${qq}] 已退出登录..`, true);
}
/** /**
* 登录流程 * 登录流程
* @param e * @param e
@ -165,42 +188,76 @@ export default class LoginService extends plugin {
*/ */
async accept(e) { async accept(e) {
const session = loginSessions.get(e.user_id); const session = loginSessions.get(e.user_id);
if ( const bindSession = bindSessions.get(e.user_id);
!session || if (!session && !bindSession) return;
!e.group_id ||
!(await ConfigControl.get()?.login?.allowGroups.includes(e.group_id))
)
return;
if (session.step === 'askQq') { if (bindSession) {
session.qq = e.msg.trim(); if (bindSession.step === 'askQq') {
session.step = 'askNickname'; bindSession.qq = e.msg.trim();
return e.reply(`QQ[${session.qq}]的英文名叫什么?`, true); bindSession.step = 'askNickname';
} return e.reply(`QQ[${bindSession.qq}]的英文名叫什么?`, true);
if (session.step === 'chooseQq') {
if (!session.options.includes(e.msg.trim())) {
return e.reply('请选择列表中的 QQ', true);
} }
session.qq = e.msg.trim(); if (bindSession.step === 'askNickname') {
session.step = 'askMethod'; bindSession.nickname = e.msg.trim();
return e.reply(`请选择登录方式\n[nc]或[lgr]\n来登录 QQ[${session.qq}]`, true); bindSession.step = 'askMethod';
} return e.reply(`请选择登录方式\n[nc]或[lgr]\n来绑定 QQ[${bindSession.qq}]`, true);
}
if (session.step === 'askNickname') { if (bindSession.step === 'askMethod') {
session.nickname = e.msg.trim(); const method = e.msg.trim().toLowerCase();
session.step = 'askMethod'; if (!['nc', 'lgr'].includes(method)) {
return e.reply(`请选择登录方式\n[nc]或[lgr]\n来登录 QQ[${session.qq}]`, true); return e.reply('登录方式无效', true);
} }
bindSession.method = method;
if (session.step === 'askMethod') { let config = await configControl.get()?.login;
const method = e.msg.trim().toLowerCase(); if (!config.userBinds[bindSession.targetUser])
if (!['nc', 'lgr'].includes(method)) { config.userBinds[bindSession.targetUser] = [];
return e.reply('登录方式无效', true); config.userBinds[bindSession.targetUser].push({
qq: bindSession.qq,
nickname: bindSession.nickname,
method: bindSession.method,
});
await ConfigControl.set('login', config);
bindSessions.delete(e.user_id);
return e.reply(
`已为 ${bindSession.targetUser} 绑定账号 ${bindSession.qq} (${bindSession.nickname}, ${bindSession.method})`,
true
);
}
}
if (session) {
let config = await configControl.get();
if (!e.group_id || !config?.login?.allowGroups.includes(e.group_id)) return;
if (session.step === 'chooseQq') {
const chosen = e.msg.trim();
const bind = session.options.find((b) => b.qq === chosen);
if (!bind) {
return e.reply('请选择列表中的 QQ', true);
}
loginSessions.delete(e.user_id);
return this.doLogin(e, {
qq: bind.qq,
nickname: bind.nickname,
method: bind.method,
});
}
if (session.step === 'askNickname') {
session.nickname = e.msg.trim();
session.step = 'askMethod';
return e.reply(`请选择登录方式\n[nc]或[lgr]\n来登录 QQ[${session.qq}]`, true);
}
if (session.step === 'askMethod') {
const method = e.msg.trim().toLowerCase();
if (!['nc', 'lgr'].includes(method)) {
return e.reply('登录方式无效', true);
}
session.method = method;
loginSessions.delete(e.user_id);
await this.doLogin(e, session);
} }
session.method = method;
loginSessions.delete(e.user_id);
await this.doLogin(e, session);
} }
} }
@ -220,6 +277,7 @@ export default class LoginService extends plugin {
} else { } else {
loginInstance = new LgrService(); loginInstance = new LgrService();
} }
activeLogins.set(qq, loginInstance);
const qrPath = await loginInstance.login(qq, nickname); const qrPath = await loginInstance.login(qq, nickname);
const loginTimers = new Map(); const loginTimers = new Map();
if (qrPath && qrPath !== 'none') { if (qrPath && qrPath !== 'none') {
@ -243,6 +301,7 @@ export default class LoginService extends plugin {
clearInterval(check); clearInterval(check);
loginTimers.delete(timerKey); loginTimers.delete(timerKey);
await loginInstance.disconnect(nickname); await loginInstance.disconnect(nickname);
activeLogins.delete(qq);
return e.reply(`QQ[${qq}] 登录超时,已断开,请重新发起登录..`, true); return e.reply(`QQ[${qq}] 登录超时,已断开,请重新发起登录..`, true);
}, 120 * 1000); }, 120 * 1000);
const timerObj = { check, timeout }; const timerObj = { check, timeout };