mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
194 lines
8.4 KiB
TypeScript
194 lines
8.4 KiB
TypeScript
import { NodeQQNTWrapperUtil, NTApiContext, WrapperNodeApi } from '@/core/wrapper';
|
||
import path from 'node:path';
|
||
import fs from 'node:fs';
|
||
import { InstanceContext } from './wrapper';
|
||
import { proxiedListenerOf } from '@/common/utils/proxy-handler';
|
||
import { GroupListener, MsgListener, ProfileListener } from './listeners';
|
||
import { GroupMember, SelfInfo, SelfStatusInfo } from './entities';
|
||
import { LegacyNTEventWrapper } from '@/common/framework/event-legacy';
|
||
import { NTQQFileApi, NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQSystemApi, NTQQUserApi, NTQQWebApi } from './apis';
|
||
import os from 'node:os';
|
||
import { NTQQCollectionApi } from './apis/collection';
|
||
import { OB11Config } from '@/onebot/helper/config';
|
||
import { NapCatConfig } from './helper/config';
|
||
|
||
export enum NapCatCoreWorkingEnv {
|
||
Unknown = 0,
|
||
Shell = 1,
|
||
Framework = 2,
|
||
}
|
||
|
||
export function loadQQWrapper(QQVersion: string): WrapperNodeApi {
|
||
let wrapperNodePath = path.resolve(path.dirname(process.execPath), './resources/app/wrapper.node');
|
||
if (!fs.existsSync(wrapperNodePath)) {
|
||
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${QQVersion}/wrapper.node`);
|
||
}
|
||
const nativemodule: any = { exports: {} };
|
||
process.dlopen(nativemodule, wrapperNodePath);
|
||
return nativemodule.exports;
|
||
}
|
||
|
||
export class NapCatCore {
|
||
readonly context: InstanceContext;
|
||
readonly apis: NTApiContext;
|
||
readonly eventWrapper: LegacyNTEventWrapper;
|
||
// readonly eventChannel: NTEventChannel;
|
||
NapCatDataPath: string;
|
||
NapCatTempPath: string;
|
||
// runtime info, not readonly
|
||
selfInfo: SelfInfo;
|
||
util: NodeQQNTWrapperUtil;
|
||
config: any;
|
||
|
||
// 通过构造器递过去的 runtime info 应该尽量少
|
||
constructor(context: InstanceContext, selfInfo: SelfInfo) {
|
||
this.selfInfo = selfInfo;
|
||
this.context = context;
|
||
this.util = new this.context.wrapper.NodeQQNTWrapperUtil();
|
||
this.eventWrapper = new LegacyNTEventWrapper(context.wrapper, context.session);
|
||
this.apis = {
|
||
FileApi: new NTQQFileApi(this.context, this),
|
||
SystemApi: new NTQQSystemApi(this.context, this),
|
||
CollectionApi: new NTQQCollectionApi(this.context, this),
|
||
WebApi: new NTQQWebApi(this.context, this),
|
||
FriendApi: new NTQQFriendApi(this.context, this),
|
||
MsgApi: new NTQQMsgApi(this.context, this),
|
||
UserApi: new NTQQUserApi(this.context, this),
|
||
GroupApi: new NTQQGroupApi(this.context, this),
|
||
};
|
||
this.config = new NapCatConfig(this,this.context.pathWrapper.cachePath);
|
||
this.NapCatDataPath = path.join(this.dataPath, 'NapCat');
|
||
fs.mkdirSync(this.NapCatDataPath, { recursive: true });
|
||
this.NapCatTempPath = path.join(this.NapCatDataPath, 'temp');
|
||
// 创建临时目录
|
||
if (!fs.existsSync(this.NapCatTempPath)) {
|
||
fs.mkdirSync(this.NapCatTempPath, { recursive: true });
|
||
}
|
||
this.initNapCatCoreListeners().then().catch(this.context.logger.logError);
|
||
}
|
||
|
||
get dataPath(): string {
|
||
let result = this.util.getNTUserDataInfoConfig();
|
||
if (!result) {
|
||
result = path.resolve(os.homedir(), './.config/QQ');
|
||
fs.mkdirSync(result, { recursive: true });
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// Renamed from 'InitDataListener'
|
||
async initNapCatCoreListeners() {
|
||
const msgListener = new MsgListener();
|
||
msgListener.onRecvMsg = (msg) => {
|
||
//console.log('RecvMsg', msg);
|
||
};
|
||
//await sleep(2500);
|
||
this.context.session.getMsgService().addKernelMsgListener(
|
||
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)),
|
||
);
|
||
|
||
const profileListener = new ProfileListener();
|
||
profileListener.onProfileDetailInfoChanged = (profile) => {
|
||
if (profile.uid === this.selfInfo.uid) {
|
||
Object.assign(this.selfInfo, profile);
|
||
}
|
||
};
|
||
profileListener.onSelfStatusChanged = (Info: SelfStatusInfo) => {
|
||
// if (Info.status == 20) {
|
||
// log("账号状态变更为离线")
|
||
// }
|
||
};
|
||
this.context.session.getProfileService().addKernelProfileListener(
|
||
new this.context.wrapper.NodeIKernelProfileListener(proxiedListenerOf(profileListener, this.context.logger)),
|
||
);
|
||
|
||
// 群相关
|
||
const groupListener = new GroupListener();
|
||
groupListener.onGroupListUpdate = (updateType, groupList) => {
|
||
// console.log("onGroupListUpdate", updateType, groupList)
|
||
groupList.map(g => {
|
||
const existGroup = this.apis.GroupApi.groupCache.get(g.groupCode);
|
||
//群成员数量变化 应该刷新缓存
|
||
if (existGroup && g.memberCount === existGroup.memberCount) {
|
||
Object.assign(existGroup, g);
|
||
}
|
||
else {
|
||
this.apis.GroupApi.groupCache.set(g.groupCode, g);
|
||
// 获取群成员
|
||
}
|
||
const sceneId = this.context.session.getGroupService().createMemberListScene(g.groupCode, 'groupMemberList_MainWindow');
|
||
this.context.session.getGroupService().getNextMemberList(sceneId!, undefined, 3000).then(r => {
|
||
// console.log(`get group ${g.groupCode} members`, r);
|
||
// r.result.infos.forEach(member => {
|
||
// });
|
||
// groupMembers.set(g.groupCode, r.result.infos);
|
||
});
|
||
});
|
||
};
|
||
groupListener.onMemberListChange = (arg) => {
|
||
// todo: 应该加一个内部自己维护的成员变动callback,用于判断成员变化通知
|
||
const groupCode = arg.sceneId.split('_')[0];
|
||
if (this.apis.GroupApi.groupMemberCache.has(groupCode)) {
|
||
const existMembers = this.apis.GroupApi.groupMemberCache.get(groupCode)!;
|
||
arg.infos.forEach((member, uid) => {
|
||
//console.log('onMemberListChange', member);
|
||
const existMember = existMembers.get(uid);
|
||
if (existMember) {
|
||
Object.assign(existMember, member);
|
||
}
|
||
else {
|
||
existMembers!.set(uid, member);
|
||
}
|
||
//移除成员
|
||
if (member.isDelete) {
|
||
existMembers.delete(uid);
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
this.apis.GroupApi.groupMemberCache.set(groupCode, arg.infos);
|
||
}
|
||
// console.log('onMemberListChange', groupCode, arg);
|
||
};
|
||
groupListener.onMemberInfoChange = (groupCode, changeType, members) => {
|
||
//console.log('onMemberInfoChange', groupCode, changeType, members);
|
||
if (changeType === 0 && members.get(this.selfInfo.uid)?.isDelete) {
|
||
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
|
||
setTimeout(() => {
|
||
this.apis.GroupApi.groupCache.delete(groupCode);
|
||
}, 5000);
|
||
|
||
}
|
||
const existMembers = this.apis.GroupApi.groupMemberCache.get(groupCode);
|
||
if (existMembers) {
|
||
members.forEach((member, uid) => {
|
||
const existMember = existMembers.get(uid);
|
||
if (existMember) {
|
||
// 检查管理变动
|
||
member.isChangeRole = this.checkAdminEvent(groupCode, member, existMember);
|
||
// 更新成员信息
|
||
Object.assign(existMember, member);
|
||
}
|
||
else {
|
||
existMembers.set(uid, member);
|
||
}
|
||
//移除成员
|
||
if (member.isDelete) {
|
||
existMembers.delete(uid);
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
this.apis.GroupApi.groupMemberCache.set(groupCode, members);
|
||
}
|
||
};
|
||
}
|
||
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
|
||
if (memberNew.role !== memberOld?.role) {
|
||
this.context.logger.log(`群 ${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
}
|