crystelf-admin/apps/login.js
2025-09-28 15:29:25 +08:00

210 lines
6.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import plugin from '../../../lib/plugins/plugin.js';
import path from 'path';
import ConfigControl from '../lib/config/configControl.js';
import config from '../../../lib/config/config.js';
import configControl from '../lib/config/configControl.js';
import axios from 'axios';
import Meme from '../lib/core/meme.js';
const configPath = path.join(process.cwd(), 'data/crystelf/config');
const loginSessions = new Map(); //正在进行的登录会话
export default class LoginService extends plugin {
constructor() {
super({
name: 'Onebot登录相关服务',
dsc: '方便操作',
event: 'message',
priority: 50,
rule: [
{
reg: '^#登录(\\d+)?$',
fnc: 'loginHandler',
},
{
reg: '^#绑定账号\\s+\\d+$',
fnc: 'bindAccount',
},
{
reg: '^#解绑账号\\s+\\d+$',
fnc: 'unbindAccount',
},
],
});
}
/**
* 登录命令入口
* @param e
* @returns {Promise<boolean>}
*/
async loginHandler(e) {
let config = await configControl.get();
if (!config?.login?.allowGroups.includes(e.group_id)) {
const img = await Meme.getMeme('zhenxun', 'default');
return e.reply(segment.img(img)); //都不在群里玩什么;[
}
const isAdmin = e.isAdmin;
const userId = e.user_id;
const match = e.msg.match(/^#登录(\d+)?$/);
let targetQq = match[1];
if (!targetQq) {
const binds = config?.login?.userBinds[userId] || [];
if (binds.length === 0) {
if (isAdmin) {
e.reply('请告诉我要登录的 QQ 号', true);
loginSessions.set(userId, { step: 'askQq', admin: true });
} else {
return e.reply('你没有绑定可登录的账号,请联系管理员分配..', true);
}
} else if (binds.length === 1) {
targetQq = binds[0];
} else {
e.reply(`你绑定了多个账号,请选择要登录的 QQ:\n${binds.join('\n')}`, true);
loginSessions.set(userId, { step: 'chooseQq', options: binds });
}
}
if (isAdmin) {
await this.startAdminLogin(e, targetQq);
} else {
const binds = config?.login?.userBinds[userId] || [];
if (!binds.includes(targetQq)) {
return e.reply('你没有权限登录该账号,请联系管理员分配', true);
}
await this.startUserLogin(e, targetQq);
}
}
/** 管理员登录交互 */
async startAdminLogin(e, qq) {
loginSessions.set(e.user_id, {
step: 'askNickname',
qq,
admin: true,
});
e.reply(`请告诉我 QQ[${qq}] 的英文昵称`, true);
}
/** 普通用户登录 */
async startUserLogin(e, qq) {
loginSessions.set(e.user_id, {
step: 'askMethod',
qq,
admin: false,
});
e.reply(`请选择登录方式\nnc或lgr\n来登录 QQ[${qq}]`, true);
}
/** 绑定账号 */
async bindAccount(e) {
let config = await configControl.get()?.login;
if (!e.isAdmin) return;
const match = e.msg.match(/^#绑定账号\s+(\d+)$/);
if (!match) return;
const qq = match[1];
const at = e.at || e.user_id;
if (!config?.userBinds[at]) config.userBinds[at] = [];
if (!config?.userBinds[at].includes(qq)) {
config.userBinds[at].push(qq);
await ConfigControl.set('login', config);
e.reply(`已为 ${at} 绑定账号 ${qq}`, true);
} else {
e.reply(`该用户已绑定此账号`, true);
}
}
/** 解绑账号 */
async unbindAccount(e) {
if (!e.isAdmin) return false;
let config = await configControl.get()?.login;
const match = e.msg.match(/^#解绑账号\s+(\d+)$/);
if (!match) return;
const qq = match[1];
const at = e.at || e.user_id;
if (!config?.userBinds[at]) {
e.reply('该用户没有绑定账号', true);
return;
}
config.userBinds[at] = config.userBinds[at].filter((q) => q !== qq);
await ConfigControl.set('login', config);
e.reply(`已为 ${at} 解绑账号 ${qq}`, true);
}
/** 捕获消息继续交互 */
async accept(e) {
const session = loginSessions.get(e.user_id);
if (!session) return;
if (session.step === 'askQq') {
session.qq = e.msg.trim();
session.step = 'askNickname';
e.reply(`请告诉我 QQ[${session.qq}] 的英文昵称`, true);
return;
}
if (session.step === 'chooseQq') {
if (!session.options.includes(e.msg.trim())) {
e.reply('请选择列表中的 QQ', true);
return;
}
session.qq = e.msg.trim();
session.step = 'askMethod';
e.reply(`请选择登录方式(回复 "nc" 或 "lgr")来登录 QQ[${session.qq}]`, true);
return;
}
if (session.step === 'askNickname') {
session.nickname = e.msg.trim();
session.step = 'askMethod';
e.reply('请选择登录方式(回复 "nc" 或 "lgr"', true);
return;
}
if (session.step === 'askMethod') {
const method = e.msg.trim().toLowerCase();
if (!['nc', 'lgr'].includes(method)) {
e.reply('登录方式无效,请回复 "nc" 或 "lgr"', true);
return;
}
session.method = method;
loginSessions.delete(e.user_id);
await this.doLogin(e, session);
}
}
/** 执行登录 */
async doLogin(e, session) {
const { qq, method, nickname } = session;
e.reply(`开始使用 ${method} 登录 QQ[${qq}]`, true);
let loginInstance;
if (method === 'nc') {
loginInstance = NapcatService();
} else {
loginInstance = LgrService();
}
const qrPath = await loginInstance.login();
e.reply(segment.image(qrPath), true);
const timerKey = `login:timer:${qq}`;
redis.setex(timerKey, 120, 'pending');
const check = setInterval(async () => {
const status = await loginInstance.checkStatus();
if (status === 'success') {
clearInterval(check);
redis.del(timerKey);
e.reply(`QQ[${qq}] 登录成功!`, true);
}
const ttl = await redis.ttl(timerKey);
if (ttl <= 0) {
clearInterval(check);
await loginInstance.disconnect();
e.reply(`QQ[${qq}] 登录超时,已断开`, true);
}
}, 5000);
}
}