Compare commits

..

7 Commits

Author SHA1 Message Date
手瓜一十雪
05043f8dfc fix: code 2024-12-03 21:41:53 +08:00
手瓜一十雪
dc2b45fa00 fix: 优化处理 2024-12-03 21:36:55 +08:00
手瓜一十雪
cc70fc766a fix 2024-12-03 21:14:18 +08:00
手瓜一十雪
d2e9db5571 fix: 临时的抽象方案 2024-12-03 20:55:24 +08:00
手瓜一十雪
8e01638a36 fix: error 2024-12-03 19:50:47 +08:00
手瓜一十雪
e8b8eae8a9 refactor: GroupAdminChange 2024-12-03 19:44:38 +08:00
手瓜一十雪
0f0275243b feat: 迁移事件解析原理 2024-12-03 19:28:51 +08:00
13 changed files with 133 additions and 95 deletions

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ",
"slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现",
"version": "4.2.16",
"version": "4.2.12",
"icon": "./logo.png",
"authors": [
{

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.2.16",
"version": "4.2.12",
"scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",

View File

@@ -1 +1 @@
export const napCatVersion = '4.2.16';
export const napCatVersion = '4.2.12';

View File

@@ -1,5 +1,6 @@
import {
GeneralCallResult,
Group,
GroupMember,
NTGroupMemberRole,
NTGroupRequestOperateTypes,
@@ -15,22 +16,35 @@ import { NTEventWrapper } from '@/common/event';
export class NTQQGroupApi {
context: InstanceContext;
core: NapCatCore;
groupCache: Map<string, Group> = new Map<string, Group>();
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
groups: Group[] = [];
essenceLRU = new LimitedHashTable<number, string>(1000);
session: any;
constructor(context: InstanceContext, core: NapCatCore) {
this.context = context;
this.core = core;
}
async initApi() {
this.initCache().then().catch(e => this.context.logger.logError(e));
}
async initCache() {
for (const group of await this.getGroups(true)) {
this.refreshGroupMemberCache(group.groupCode).then().catch();
this.groups = await this.getGroups();
for (const group of this.groups) {
this.groupCache.set(group.groupCode, group);
this.refreshGroupMemberCache(group.groupCode).then().catch(e => this.context.logger.logError(e));
}
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
// process.pid 调试点
}
async getCoreAndBaseInfo(uids: string[]) {
return await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelProfileService/getCoreAndBaseInfo',
'nodeStore',
uids,
);
}
async fetchGroupEssenceList(groupCode: string) {
@@ -48,15 +62,15 @@ export class NTQQGroupApi {
return (await data)[1];
}
async clearGroupNotifiesUnreadCount(doubt: boolean) {
return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(doubt);
async clearGroupNotifiesUnreadCount(uk: boolean) {
return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(uk);
}
async setGroupAvatar(groupCode: string, filePath: string) {
return this.context.session.getGroupService().setHeader(groupCode, filePath);
async setGroupAvatar(gc: string, filePath: string) {
return this.context.session.getGroupService().setHeader(gc, filePath);
}
async getGroups(forced: boolean = false) {
async getGroups(forced = false) {
const [, , groupList] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getGroupList',
'NodeIKernelGroupListener/onGroupListUpdate',
@@ -65,9 +79,9 @@ export class NTQQGroupApi {
return groupList;
}
async getGroupExtFE0Info(groupCodes: Array<string>, forced = true) {
async getGroupExtFE0Info(groupCode: string[], forced = true) {
return this.context.session.getGroupService().getGroupExt0xEF0Info(
groupCodes,
groupCode,
[],
{
bindGuildId: 1,
@@ -107,6 +121,24 @@ export class NTQQGroupApi {
);
}
async getGroup(groupCode: string, forced = false) {
let group = this.groupCache.get(groupCode.toString());
if (!group) {
try {
const groupList = await this.getGroups(forced);
if (groupList.length) {
groupList.forEach(g => {
this.groupCache.set(g.groupCode, g);
});
}
} catch (e) {
return undefined;
}
}
group = this.groupCache.get(groupCode.toString());
return group;
}
async getGroupMemberAll(groupCode: string, forced = false) {
return this.context.session.getGroupService().getAllMemberList(groupCode, forced);
}
@@ -114,35 +146,56 @@ export class NTQQGroupApi {
async refreshGroupMemberCache(groupCode: string) {
try {
const members = await this.getGroupMemberAll(groupCode, true);
this.groupMemberCache.set(groupCode, members.result.infos);
// 首先填入基础信息
const existingMembers = this.groupMemberCache.get(groupCode) ?? new Map<string, GroupMember>();
members.result.infos.forEach((value, key) => {
existingMembers.set(value.uid, { ...value, ...existingMembers.get(value.uid) });
});
// 后台补全复杂信息
let event = (async () => {
let data = (await Promise.allSettled(members.result.ids.map(e => this.core.apis.UserApi.getUserDetailInfo(e.uid)))).filter(e => e.status === 'fulfilled').map(e => e.value);
data.forEach(e => {
const existingMember = members.result.infos.get(e.uid);
if (existingMember) {
members.result.infos.set(e.uid, { ...existingMember, ...e });
}
});
this.groupMemberCache.set(groupCode, members.result.infos);
})().then().catch(e => this.context.logger.logError(e));
// 处理首次空缺
if (!this.groupMemberCache.get(groupCode)) {
await event;
}
} catch (e) {
this.context.logger.logError(`刷新群成员缓存失败, 群号: ${groupCode}, 错误: ${e}`);
this.context.logger.logError(`刷新群成员缓存失败, ${e}`);
}
return this.groupMemberCache;
}
async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) {
const groupCodeStr = groupCode.toString();
const memberUinOrUidStr = memberUinOrUid.toString();
// 获取群成员缓存
// 检查群缓存
let members = this.groupMemberCache.get(groupCodeStr);
if (!members) {
members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr);
await this.refreshGroupMemberCache(groupCodeStr);
}
const getMember = () => {
function getMember() {
let member: GroupMember | undefined;
if (isNumeric(memberUinOrUidStr)) {
return Array.from(members!.values()).find(member => member.uin === memberUinOrUidStr);
member = Array.from(members!.values()).find(member => member.uin === memberUinOrUidStr);
} else {
return members!.get(memberUinOrUidStr);
member = members!.get(memberUinOrUidStr);
}
};
return member;
}
let member = getMember();
// 如果缓存中不存在该成员,尝试刷新缓存
// 不存在群友缓存 尝试刷新
if (!member) {
members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr);
await this.refreshGroupMemberCache(groupCode.toString());
member = getMember();
}
return member;
@@ -152,26 +205,26 @@ export class NTQQGroupApi {
return this.context.session.getGroupService().getGroupRecommendContactArkJson(groupCode);
}
async creatGroupFileFolder(groupCode: string, folderName: string) {
async CreatGroupFileFolder(groupCode: string, folderName: string) {
return this.context.session.getRichMediaService().createGroupFolder(groupCode, folderName);
}
async delGroupFile(groupCode: string, files: Array<string>) {
async DelGroupFile(groupCode: string, files: string[]) {
return this.context.session.getRichMediaService().deleteGroupFile(groupCode, [102], files);
}
async delGroupFileFolder(groupCode: string, folderId: string) {
async DelGroupFileFolder(groupCode: string, folderId: string) {
return this.context.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
}
async addGroupEssence(groupCode: string, msgId: string) {
async addGroupEssence(GroupCode: string, msgId: string) {
const MsgData = await this.context.session.getMsgService().getMsgsIncludeSelf({
chatType: 2,
guildId: '',
peerUid: groupCode,
peerUid: GroupCode,
}, msgId, 1, false);
const param = {
groupCode: groupCode,
groupCode: GroupCode,
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
msgSeq: parseInt(MsgData.msgList[0].msgSeq),
};
@@ -182,9 +235,9 @@ export class NTQQGroupApi {
return this.context.session.getGroupService().kickMemberV2(param);
}
async deleteGroupBulletin(groupCode: string, noticeId: string) {
async deleteGroupBulletin(GroupCode: string, noticeId: string) {
const psKey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
return this.context.session.getGroupService().deleteGroupBulletin(groupCode, psKey, noticeId);
return this.context.session.getGroupService().deleteGroupBulletin(GroupCode, psKey, noticeId);
}
async quitGroupV2(GroupCode: string, needDeleteLocalMsg: boolean) {
@@ -195,37 +248,37 @@ export class NTQQGroupApi {
return this.context.session.getGroupService().quitGroupV2(param);
}
async removeGroupEssenceBySeq(groupCode: string, msgRandom: string, msgSeq: string) {
async removeGroupEssenceBySeq(GroupCode: string, msgRandom: string, msgSeq: string) {
const param = {
groupCode: groupCode,
groupCode: GroupCode,
msgRandom: parseInt(msgRandom),
msgSeq: parseInt(msgSeq),
};
return this.context.session.getGroupService().removeGroupEssence(param);
}
async removeGroupEssence(groupCode: string, msgId: string) {
async removeGroupEssence(GroupCode: string, msgId: string) {
const MsgData = await this.context.session.getMsgService().getMsgsIncludeSelf({
chatType: 2,
guildId: '',
peerUid: groupCode,
peerUid: GroupCode,
}, msgId, 1, false);
const param = {
groupCode: groupCode,
groupCode: GroupCode,
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
msgSeq: parseInt(MsgData.msgList[0].msgSeq),
};
return this.context.session.getGroupService().removeGroupEssence(param);
}
async getSingleScreenNotifies(doubt: boolean, count: number) {
async getSingleScreenNotifies(doubt: boolean, num: number) {
const [, , , notifies] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getSingleScreenNotifies',
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
[
doubt,
'',
count,
num,
],
);
return notifies;
@@ -249,43 +302,44 @@ export class NTQQGroupApi {
return ret.groupInfos.find(g => g.groupCode === groupCode);
}
async getGroupMemberEx(groupCode: string, uid: string, forced: boolean = false, retry: number = 2) {
async getGroupMemberEx(GroupCode: string, uid: string, forced = false, retry = 2) {
const data = await solveAsyncProblem((eventWrapper: NTEventWrapper, GroupCode: string, uid: string, forced = false) => {
return eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getMemberInfo',
'NodeIKernelGroupListener/onMemberInfoChange',
[groupCode, [uid], forced],
[GroupCode, [uid], forced],
(ret) => ret.result === 0,
(params, _, members) => params === GroupCode && members.size > 0 && members.has(uid),
1,
forced ? 2500 : 250
);
}, this.core.eventWrapper, groupCode, uid, forced);
}, this.core.eventWrapper, GroupCode, uid, forced);
if (data && data[3] instanceof Map && data[3].has(uid)) {
return data[3].get(uid);
}
if (retry > 0) {
const trydata = await this.getGroupMemberEx(groupCode, uid, true, retry - 1) as GroupMember | undefined;
const trydata = await this.getGroupMemberEx(GroupCode, uid, true, retry - 1) as GroupMember | undefined;
if (trydata) return trydata;
}
return undefined;
}
async getGroupFileCount(groupCodes: Array<string>) {
return this.context.session.getRichMediaService().batchGetGroupFileCount(groupCodes);
async getGroupFileCount(group_ids: Array<string>) {
return this.context.session.getRichMediaService().batchGetGroupFileCount(group_ids);
}
async getArkJsonGroupShare(groupCode: string) {
async getArkJsonGroupShare(GroupCode: string) {
const ret = await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelGroupService/getGroupRecommendContactArkJson',
groupCode,
GroupCode,
) as GeneralCallResult & { arkJson: string };
return ret.arkJson;
}
async uploadGroupBulletinPic(groupCode: string, imageurl: string) {
//需要异常处理
async uploadGroupBulletinPic(GroupCode: string, imageurl: string) {
const _Pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
return this.context.session.getGroupService().uploadGroupBulletinPic(groupCode, _Pskey, imageurl);
return this.context.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
}
async handleGroupRequest(flag: string, operateType: NTGroupRequestOperateTypes, reason?: string) {
@@ -307,36 +361,36 @@ export class NTQQGroupApi {
});
}
async quitGroup(groupCode: string) {
return this.context.session.getGroupService().quitGroup(groupCode);
async quitGroup(groupQQ: string) {
return this.context.session.getGroupService().quitGroup(groupQQ);
}
async kickMember(groupCode: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') {
return this.context.session.getGroupService().kickMember(groupCode, kickUids, refuseForever, kickReason);
async kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') {
return this.context.session.getGroupService().kickMember(groupQQ, kickUids, refuseForever, kickReason);
}
async banMember(groupCode: string, memList: Array<{ uid: string, timeStamp: number }>) {
async banMember(groupQQ: string, memList: Array<{ uid: string, timeStamp: number }>) {
// timeStamp为秒数, 0为解除禁言
return this.context.session.getGroupService().setMemberShutUp(groupCode, memList);
return this.context.session.getGroupService().setMemberShutUp(groupQQ, memList);
}
async banGroup(groupCode: string, shutUp: boolean) {
return this.context.session.getGroupService().setGroupShutUp(groupCode, shutUp);
async banGroup(groupQQ: string, shutUp: boolean) {
return this.context.session.getGroupService().setGroupShutUp(groupQQ, shutUp);
}
async setMemberCard(groupCode: string, memberUid: string, cardName: string) {
return this.context.session.getGroupService().modifyMemberCardName(groupCode, memberUid, cardName);
async setMemberCard(groupQQ: string, memberUid: string, cardName: string) {
return this.context.session.getGroupService().modifyMemberCardName(groupQQ, memberUid, cardName);
}
async setMemberRole(groupCode: string, memberUid: string, role: NTGroupMemberRole) {
return this.context.session.getGroupService().modifyMemberRole(groupCode, memberUid, role);
async setMemberRole(groupQQ: string, memberUid: string, role: NTGroupMemberRole) {
return this.context.session.getGroupService().modifyMemberRole(groupQQ, memberUid, role);
}
async setGroupName(groupCode: string, groupName: string) {
return this.context.session.getGroupService().modifyGroupName(groupCode, groupName, false);
async setGroupName(groupQQ: string, groupName: string) {
return this.context.session.getGroupService().modifyGroupName(groupQQ, groupName, false);
}
async publishGroupBulletin(groupCode: string, content: string, picInfo: {
async publishGroupBulletin(groupQQ: string, content: string, picInfo: {
id: string,
width: number,
height: number
@@ -350,11 +404,11 @@ export class NTQQGroupApi {
pinned: pinned,
confirmRequired: confirmRequired,
};
return this.context.session.getGroupService().publishGroupBulletin(groupCode, psKey!, data);
return this.context.session.getGroupService().publishGroupBulletin(groupQQ, psKey!, data);
}
async getGroupRemainAtTimes(groupCode: string) {
return this.context.session.getGroupService().getGroupRemainAtTimes(groupCode);
async getGroupRemainAtTimes(GroupCode: string) {
return this.context.session.getGroupService().getGroupRemainAtTimes(GroupCode);
}
async getMemberExtInfo(groupCode: string, uin: string) {

View File

@@ -18,15 +18,6 @@ export class NTQQUserApi {
async getStatusByUid(uid: string) {
return this.context.session.getProfileService().getStatus(uid);
}
async getCoreAndBaseInfo(uids: string[]) {
return await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelProfileService/getCoreAndBaseInfo',
'nodeStore',
uids,
);
}
// 默认获取自己的 type = 2 获取别人 type = 1
async getProfileLike(uid: string, start: number, count: number, type: number = 2) {
return this.context.session.getProfileLikeService().getBuddyProfileLike({

View File

@@ -193,7 +193,7 @@ export interface NodeIKernelGroupService {
getGroupNotifiesUnreadCount(unknown: boolean): Promise<GeneralCallResult>;
clearGroupNotifiesUnreadCount(doubt: boolean): void;
clearGroupNotifiesUnreadCount(unknown: boolean): void;
operateSysNotify(
doubt: boolean,

View File

@@ -18,7 +18,7 @@ export default class GetGroupAddRequest extends OneBotAction<null, OB11GroupRequ
ignoredNotifies
.filter(notify => notify.type === 7)
.map(async SSNotify => ({
request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type,
request_id: SSNotify.seq,
requester_uin: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1?.uid),
requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,

View File

@@ -13,6 +13,6 @@ export class CreateGroupFileFolder extends OneBotAction<Payload, any> {
actionName = ActionName.GoCQHTTP_CreateGroupFileFolder;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
return (await this.core.apis.GroupApi.creatGroupFileFolder(payload.group_id.toString(), payload.folder_name)).resultWithGroupItem;
return (await this.core.apis.GroupApi.CreatGroupFileFolder(payload.group_id.toString(), payload.folder_name)).resultWithGroupItem;
}
}

View File

@@ -17,6 +17,6 @@ export class DeleteGroupFile extends OneBotAction<Payload, any> {
async _handle(payload: Payload) {
const data = FileNapCatOneBotUUID.decodeModelId(payload.file_id);
if (!data) throw new Error('Invalid file_id');
return await this.core.apis.GroupApi.delGroupFile(payload.group_id.toString(), [data.fileId]);
return await this.core.apis.GroupApi.DelGroupFile(payload.group_id.toString(), [data.fileId]);
}
}

View File

@@ -14,7 +14,7 @@ export class DeleteGroupFileFolder extends OneBotAction<Payload, any> {
actionName = ActionName.GoCQHTTP_DeleteGroupFileFolder;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
return (await this.core.apis.GroupApi.delGroupFileFolder(
return (await this.core.apis.GroupApi.DelGroupFileFolder(
payload.group_id.toString(), payload.folder ?? payload.folder_id ?? '')).groupFileCommonResult;
}
}

View File

@@ -11,7 +11,7 @@ export class GetGroupIgnoredNotifies extends OneBotAction<void, any> {
ignoredNotifies
.filter(notify => notify.type === 7)
.map(async SSNotify => ({
request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type,
request_id: SSNotify.seq,
requester_uin: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1?.uid),
requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,

View File

@@ -8,12 +8,12 @@ export class GetGroupSystemMsg extends OneBotAction<void, any> {
const NTQQUserApi = this.core.apis.UserApi;
const NTQQGroupApi = this.core.apis.GroupApi;
// 默认10条 该api未完整实现 包括响应数据规范化 类型规范化
const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false, 10);
const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false,10);
const retData: any = { InvitedRequest: [], join_requests: [] };
for (const SSNotify of SingleScreenNotifies) {
if (SSNotify.type == 1) {
retData.InvitedRequest.push({
request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type,
request_id: SSNotify.seq,
invitor_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid),
invitor_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,
@@ -23,7 +23,7 @@ export class GetGroupSystemMsg extends OneBotAction<void, any> {
});
} else if (SSNotify.type == 7) {
retData.join_requests.push({
request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type,
request_id: SSNotify.seq,
requester_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid),
requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,

View File

@@ -980,14 +980,7 @@ export class OneBotMsgApi {
);
} else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) {
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
if (groupChange.memberUid === this.core.selfInfo.uid) {
setTimeout(() => {
this.core.apis.GroupApi.groupMemberCache.delete(groupChange.groupUin.toString());
}, 5000);
// 自己被踢了 5S后回收
} else {
this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch();
}
this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch();
return new OB11GroupDecreaseEvent(
this.core,
groupChange.groupUin,