diff --git a/package.json b/package.json index f963f1e..c269e75 100644 --- a/package.json +++ b/package.json @@ -16,15 +16,15 @@ "author": "", "license": "MIT", "dependencies": { - "@minatojs/driver-sqlite": "^4.4.1", + "@minatojs/driver-sqlite": "^4.5.0", "compressing": "^1.10.1", - "cordis": "^3.17.9", + "cordis": "^3.18.0", "cors": "^2.8.5", "express": "^4.19.2", "fast-xml-parser": "^4.4.1", - "file-type": "^19.4.0", + "file-type": "^19.4.1", "fluent-ffmpeg": "^2.1.3", - "minato": "^3.4.3", + "minato": "^3.5.0", "silk-wasm": "^3.6.1", "ws": "^8.18.0" }, @@ -37,7 +37,7 @@ "electron": "^29.1.4", "electron-vite": "^2.3.0", "typescript": "^5.5.4", - "vite": "^5.4.0", + "vite": "^5.4.1", "vite-plugin-cp": "^4.0.8" }, "packageManager": "yarn@4.4.0" diff --git a/src/common/data.ts b/src/common/data.ts index a875fc4..60b031e 100644 --- a/src/common/data.ts +++ b/src/common/data.ts @@ -1,6 +1,5 @@ import { type Friend, - type Group, type GroupMember, type SelfInfo, } from '../ntqqapi/types' @@ -11,8 +10,8 @@ import { isNumeric } from './utils/helper' import { NTQQFriendApi, NTQQUserApi } from '../ntqqapi/api' import { RawMessage } from '../ntqqapi/types' import { getConfigUtil } from './config' +import { getBuildVersion } from './utils/QQBasicInfo' -export let groups: Group[] = [] export let friends: Friend[] = [] export const llonebotError: LLOneBotError = { ffmpegError: '', @@ -24,10 +23,10 @@ export const llonebotError: LLOneBotError = { export const groupMembers: Map> = new Map>() export async function getFriend(uinOrUid: string): Promise { - let filterKey = isNumeric(uinOrUid.toString()) ? 'uin' : 'uid' - let filterValue = uinOrUid + const filterKey: 'uin' | 'uid' = isNumeric(uinOrUid.toString()) ? 'uin' : 'uid' + const filterValue = uinOrUid let friend = friends.find((friend) => friend[filterKey] === filterValue.toString()) - if (!friend) { + if (!friend && getBuildVersion() < 26702) { try { const _friends = await NTQQFriendApi.getFriends(true) friend = _friends.find((friend) => friend[filterKey] === filterValue.toString()) @@ -41,39 +40,15 @@ export async function getFriend(uinOrUid: string): Promise { return friend } -export async function getGroup(qq: string): Promise { - let group = groups.find((group) => group.groupCode === qq.toString()) - if (!group) { - try { - const _groups = await NTQQGroupApi.getGroups(true) - group = _groups.find((group) => group.groupCode === qq.toString()) - if (group) { - groups.push(group) - } - } catch (e) { - } - } - return group -} - -export function deleteGroup(groupCode: string) { - const groupIndex = groups.findIndex((group) => group.groupCode === groupCode.toString()) - // log(groups, groupCode, groupIndex); - if (groupIndex !== -1) { - log('删除群', groupCode) - groups.splice(groupIndex, 1) - } -} - -export async function getGroupMember(groupQQ: string | number, memberUinOrUid: string | number) { - groupQQ = groupQQ.toString() - memberUinOrUid = memberUinOrUid.toString() - let members = groupMembers.get(groupQQ) +export async function getGroupMember(groupCode: string | number, memberUinOrUid: string | number) { + const groupCodeStr = groupCode.toString() + const memberUinOrUidStr = memberUinOrUid.toString() + let members = groupMembers.get(groupCodeStr) if (!members) { try { - members = await NTQQGroupApi.getGroupMembers(groupQQ) + members = await NTQQGroupApi.getGroupMembers(groupCodeStr) // 更新群成员列表 - groupMembers.set(groupQQ, members) + groupMembers.set(groupCodeStr, members) } catch (e) { return null @@ -81,16 +56,16 @@ export async function getGroupMember(groupQQ: string | number, memberUinOrUid: s } const getMember = () => { let member: GroupMember | undefined = undefined - if (isNumeric(memberUinOrUid)) { - member = Array.from(members!.values()).find(member => member.uin === memberUinOrUid) + if (isNumeric(memberUinOrUidStr)) { + member = Array.from(members!.values()).find(member => member.uin === memberUinOrUidStr) } else { - member = members!.get(memberUinOrUid) + member = members!.get(memberUinOrUidStr) } return member } let member = getMember() if (!member) { - members = await NTQQGroupApi.getGroupMembers(groupQQ) + members = await NTQQGroupApi.getGroupMembers(groupCodeStr) member = getMember() } return member diff --git a/src/main/main.ts b/src/main/main.ts index 7d758be..1ad1773 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -16,7 +16,6 @@ import { import { ob11WebsocketServer } from '../onebot11/server/ws/WebsocketServer' import { DATA_DIR, TEMP_DIR } from '../common/utils' import { - getGroupMember, llonebotError, setSelfInfo, getSelfInfo, @@ -274,26 +273,24 @@ function onLoad() { const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type if (notify.type == GroupNotifyTypes.MEMBER_EXIT || notify.type == GroupNotifyTypes.KICK_MEMBER) { log('有成员退出通知', notify) - try { - const member1 = await NTQQUserApi.getUserDetailInfo(notify.user1.uid) - let operatorId = member1.uin - let subType: GroupDecreaseSubType = 'leave' - if (notify.user2.uid) { - // 是被踢的 - const member2 = await getGroupMember(notify.group.groupCode, notify.user2.uid) - operatorId = member2?.uin! - subType = 'kick' + const member1Uin = (await NTQQUserApi.getUinByUid(notify.user1.uid))! + let operatorId = member1Uin + let subType: GroupDecreaseSubType = 'leave' + if (notify.user2.uid) { + // 是被踢的 + const member2Uin = await NTQQUserApi.getUinByUid(notify.user2.uid) + if (member2Uin) { + operatorId = member2Uin } - let groupDecreaseEvent = new OB11GroupDecreaseEvent( - parseInt(notify.group.groupCode), - parseInt(member1.uin), - parseInt(operatorId), - subType, - ) - postOb11Event(groupDecreaseEvent, true) - } catch (e: any) { - log('获取群通知的成员信息失败', notify, e.stack.toString()) + subType = 'kick' } + const groupDecreaseEvent = new OB11GroupDecreaseEvent( + parseInt(notify.group.groupCode), + parseInt(member1Uin), + parseInt(operatorId), + subType, + ) + postOb11Event(groupDecreaseEvent, true) } else if ([GroupNotifyTypes.JOIN_REQUEST, GroupNotifyTypes.JOIN_REQUEST_BY_INVITED].includes(notify.type)) { log('有加群请求') diff --git a/src/ntqqapi/api/friend.ts b/src/ntqqapi/api/friend.ts index cb51f3e..fcab865 100644 --- a/src/ntqqapi/api/friend.ts +++ b/src/ntqqapi/api/friend.ts @@ -8,7 +8,7 @@ import { CacheClassFuncAsyncExtend } from '@/common/utils/helper' import { LimitedHashTable } from '@/common/utils/table' export class NTQQFriendApi { - /** >=26702 应使用 getBuddyV2 */ + /** 大于或等于 26702 应使用 getBuddyV2 */ static async getFriends(forced = false) { const data = await callNTQQApi<{ data: { diff --git a/src/ntqqapi/api/group.ts b/src/ntqqapi/api/group.ts index a1d6b2f..3567716 100644 --- a/src/ntqqapi/api/group.ts +++ b/src/ntqqapi/api/group.ts @@ -5,6 +5,7 @@ import { NTQQWindowApi, NTQQWindows } from './window' import { getSession } from '../wrapper' import { NTEventDispatch } from '@/common/utils/EventTask' import { NodeIKernelGroupListener } from '../listeners' +import { NodeIKernelGroupService } from '../services' export class NTQQGroupApi { static async activateMemberListChange() { @@ -45,12 +46,29 @@ export class NTQQGroupApi { 'NodeIKernelGroupListener/onGroupListUpdate', 1, 5000, - (updateType) => true, + () => true, forced ) return groupList } + async getGroupMemberV2(GroupCode: string, uid: string, forced = false) { + type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'] + type EventType = NodeIKernelGroupService['getMemberInfo'] + const [, , , _members] = await NTEventDispatch.CallNormalEvent + ( + 'NodeIKernelGroupService/getMemberInfo', + 'NodeIKernelGroupListener/onMemberInfoChange', + 1, + 5000, + (groupCode: string, changeType: number, members: Map) => { + return groupCode == GroupCode && members.has(uid) + }, + GroupCode, [uid], forced, + ) + return _members.get(uid) + } + static async getGroupMembers(groupQQ: string, num = 3000): Promise> { const session = getSession() const groupService = session?.getGroupService() diff --git a/src/ntqqapi/hook.ts b/src/ntqqapi/hook.ts index 6acd5e3..aa107cc 100644 --- a/src/ntqqapi/hook.ts +++ b/src/ntqqapi/hook.ts @@ -4,27 +4,20 @@ import { NTQQMsgApi } from './api/msg' import { CategoryFriend, ChatType, - FriendV2, - Group, GroupMember, GroupMemberRole, RawMessage, SimpleInfo, User, } from './types' import { - deleteGroup, friends, getFriend, getGroupMember, - groups, - getSelfUin, setSelfInfo } from '@/common/data' -import { OB11GroupDecreaseEvent } from '../onebot11/event/notice/OB11GroupDecreaseEvent' import { postOb11Event } from '../onebot11/server/post-ob11-event' import { getConfigUtil, HOOK_LOG } from '@/common/config' import fs from 'node:fs' -import { NTQQGroupApi } from './api/group' import { log } from '@/common/utils' import { randomUUID } from 'node:crypto' import { MessageUnique } from '../common/utils/MessageUnique' @@ -242,9 +235,9 @@ export function removeReceiveHook(id: string) { receiveHooks.splice(index, 1) } -let activatedGroups: string[] = [] +//let activatedGroups: string[] = [] -async function updateGroups(_groups: Group[], needUpdate: boolean = true) { +/*async function updateGroups(_groups: Group[], needUpdate: boolean = true) { for (let group of _groups) { log('update group', group.groupCode) if (group.privilegeFlag === 0) { @@ -269,9 +262,9 @@ async function updateGroups(_groups: Group[], needUpdate: boolean = true) { } } } -} +}*/ -async function processGroupEvent(payload: { groupList: Group[] }) { +/*async function processGroupEvent(payload: { groupList: Group[] }) { try { const newGroupList = payload.groupList for (const group of newGroupList) { @@ -322,12 +315,12 @@ async function processGroupEvent(payload: { groupList: Group[] }) { updateGroups(payload.groupList).then() log('更新群信息错误', e.stack.toString()) } -} +}*/ export async function startHook() { // 群列表变动 - registerReceiveHook<{ groupList: Group[]; updateType: number }>(ReceiveCmdS.GROUPS, (payload) => { + /*registerReceiveHook<{ groupList: Group[]; updateType: number }>(ReceiveCmdS.GROUPS, (payload) => { // updateType 3是群列表变动,2是群成员变动 // log("群列表变动", payload.updateType, payload.groupList) if (payload.updateType != 2) { @@ -350,7 +343,7 @@ export async function startHook() { processGroupEvent(payload).then() } } - }) + })*/ registerReceiveHook<{ groupCode: string diff --git a/src/onebot11/action/go-cqhttp/UploadFile.ts b/src/onebot11/action/go-cqhttp/UploadFile.ts index 08a0b69..c7cdd99 100644 --- a/src/onebot11/action/go-cqhttp/UploadFile.ts +++ b/src/onebot11/action/go-cqhttp/UploadFile.ts @@ -1,6 +1,5 @@ import fs from 'node:fs' import BaseAction from '../BaseAction' -import { getGroup } from '@/common/data' import { ActionName } from '../types' import { SendMsgElementConstructor } from '@/ntqqapi/constructor' import { ChatType, SendFileElement } from '@/ntqqapi/types' @@ -22,10 +21,6 @@ export class GoCQHTTPUploadGroupFile extends BaseAction { actionName = ActionName.GoCQHTTP_UploadGroupFile protected async _handle(payload: Payload): Promise { - const group = await getGroup(payload.group_id?.toString()!) - if (!group) { - throw new Error(`群组${payload.group_id}不存在`) - } let file = payload.file if (fs.existsSync(file)) { file = `file://${file}` @@ -34,8 +29,11 @@ export class GoCQHTTPUploadGroupFile extends BaseAction { if (!downloadResult.success) { throw new Error(downloadResult.errMsg) } - const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name, payload.folder_id) - await sendMsg({ chatType: ChatType.group, peerUid: group.groupCode }, [sendFileEle], [], true) + const sendFileEle = await SendMsgElementConstructor.file(downloadResult.path, payload.name, payload.folder_id) + await sendMsg({ + chatType: ChatType.group, + peerUid: payload.group_id?.toString()!, + }, [sendFileEle], [], true) return null } } diff --git a/src/onebot11/action/group/GetGroupInfo.ts b/src/onebot11/action/group/GetGroupInfo.ts index 8bfc219..d251514 100644 --- a/src/onebot11/action/group/GetGroupInfo.ts +++ b/src/onebot11/action/group/GetGroupInfo.ts @@ -1,18 +1,18 @@ import { OB11Group } from '../../types' -import { getGroup } from '../../../common/data' import { OB11Constructor } from '../../constructor' import BaseAction from '../BaseAction' import { ActionName } from '../types' +import { NTQQGroupApi } from '@/ntqqapi/api' -interface PayloadType { - group_id: number +interface Payload { + group_id: number | string } -class GetGroupInfo extends BaseAction { +class GetGroupInfo extends BaseAction { actionName = ActionName.GetGroupInfo - protected async _handle(payload: PayloadType) { - const group = await getGroup(payload.group_id.toString()) + protected async _handle(payload: Payload) { + const group = (await NTQQGroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString()) if (group) { return OB11Constructor.group(group) } else { diff --git a/src/onebot11/action/group/GetGroupList.ts b/src/onebot11/action/group/GetGroupList.ts index 8d1e0be..b071085 100644 --- a/src/onebot11/action/group/GetGroupList.ts +++ b/src/onebot11/action/group/GetGroupList.ts @@ -1,10 +1,8 @@ import { OB11Group } from '../../types' import { OB11Constructor } from '../../constructor' -import { groups } from '../../../common/data' import BaseAction from '../BaseAction' import { ActionName } from '../types' import { NTQQGroupApi } from '../../../ntqqapi/api' -import { log } from '../../../common/utils' interface Payload { no_cache: boolean | string @@ -14,14 +12,8 @@ class GetGroupList extends BaseAction { actionName = ActionName.GetGroupList protected async _handle(payload: Payload) { - if (groups.length === 0 || payload?.no_cache === true || payload?.no_cache === 'true') { - try { - const groups = await NTQQGroupApi.getGroups(true) - log('强制刷新群列表, 数量:', groups.length) - return OB11Constructor.groups(groups) - } catch (e) {} - } - return OB11Constructor.groups(groups) + const groupList = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true') + return OB11Constructor.groups(groupList) } } diff --git a/src/onebot11/action/group/GetGroupMemberList.ts b/src/onebot11/action/group/GetGroupMemberList.ts index 86e4456..507fb67 100644 --- a/src/onebot11/action/group/GetGroupMemberList.ts +++ b/src/onebot11/action/group/GetGroupMemberList.ts @@ -1,31 +1,59 @@ import { OB11GroupMember } from '../../types' -import { getGroup } from '../../../common/data' import { OB11Constructor } from '../../constructor' import BaseAction from '../BaseAction' import { ActionName } from '../types' -import { NTQQGroupApi } from '../../../ntqqapi/api/group' -import { log } from '../../../common/utils' +import { NTQQGroupApi, WebApi } from '@/ntqqapi/api' +import { getSelfUid } from '@/common/data' -export interface PayloadType { - group_id: number +interface Payload { + group_id: number | string no_cache: boolean | string } -class GetGroupMemberList extends BaseAction { +class GetGroupMemberList extends BaseAction { actionName = ActionName.GetGroupMemberList - protected async _handle(payload: PayloadType) { - const group = await getGroup(payload.group_id.toString()) - if (group) { - if (!group.members?.length || payload.no_cache === true || payload.no_cache === 'true') { - const members = await NTQQGroupApi.getGroupMembers(payload.group_id.toString()) - group.members = Array.from(members.values()) - log('强制刷新群成员列表, 数量: ', group.members.length) - } - return OB11Constructor.groupMembers(group) - } else { - throw `群${payload.group_id}不存在` + protected async _handle(payload: Payload) { + //const isNocache = payload.no_cache == true || payload.no_cache === 'true' //已强制无缓存 + const groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString()) + const groupMembersArr = Array.from(groupMembers.values()) + + let _groupMembers = groupMembersArr.map(item => { + return OB11Constructor.groupMember(payload.group_id.toString(), item) + }) + + const MemberMap: Map = new Map() + const date = Math.round(Date.now() / 1000) + + for (let i = 0, len = _groupMembers.length; i < len; i++) { + // 保证基础数据有这个 同时避免群管插件过于依赖这个杀了 + _groupMembers[i].join_time = date + _groupMembers[i].last_sent_time = date + MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]) } + + const selfRole = groupMembers.get(getSelfUid())?.role + const isPrivilege = selfRole === 3 || selfRole === 4 + + if (isPrivilege) { + const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString()) + for (let i = 0, len = webGroupMembers.length; i < len; i++) { + if (!webGroupMembers[i]?.uin) { + continue + } + const MemberData = MemberMap.get(webGroupMembers[i]?.uin) + if (MemberData) { + MemberData.join_time = webGroupMembers[i]?.join_time + MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time + MemberData.qage = webGroupMembers[i]?.qage + MemberData.level = webGroupMembers[i]?.lv.level.toString() + MemberMap.set(webGroupMembers[i]?.uin, MemberData) + } + } + } + + _groupMembers = Array.from(MemberMap.values()) + return _groupMembers } } diff --git a/src/onebot11/action/msg/SendMsg.ts b/src/onebot11/action/msg/SendMsg.ts index a08c3d5..f7f9b7d 100644 --- a/src/onebot11/action/msg/SendMsg.ts +++ b/src/onebot11/action/msg/SendMsg.ts @@ -6,7 +6,7 @@ import { RawMessage, SendMessageElement, } from '@/ntqqapi/types' -import { getGroup, getGroupMember, getSelfUid, getSelfUin } from '@/common/data' +import { getGroupMember, getSelfUid, getSelfUin } from '@/common/data' import { OB11MessageCustomMusic, OB11MessageData, @@ -305,10 +305,9 @@ async function createContext(payload: OB11PostSendMsg, contextMode: ContextMode) // This redundant design of Ob11 here should be blamed. if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) { - const group = (await getGroup(payload.group_id))! // checked before return { chatType: ChatType.group, - peerUid: group.groupCode + peerUid: payload.group_id.toString(), } } if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) { @@ -318,7 +317,7 @@ async function createContext(payload: OB11PostSendMsg, contextMode: ContextMode) return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid: Uid!, - guildId: payload.group_id || ''//临时主动发起时需要传入群号 + guildId: payload.group_id?.toString() || '' //临时主动发起时需要传入群号 } } throw '请指定 group_id 或 user_id' @@ -343,12 +342,6 @@ export class SendMsg extends BaseAction { message: '音乐消息不可以和其他消息混在一起发送', } } - if (payload.message_type !== 'private' && payload.group_id && !(await getGroup(payload.group_id))) { - return { - valid: false, - message: `群${payload.group_id}不存在`, - } - } if (payload.user_id && payload.message_type !== 'group') { const uid = await NTQQUserApi.getUidByUin(payload.user_id.toString()) const isBuddy = await NTQQFriendApi.isBuddy(uid!) diff --git a/src/onebot11/constructor.ts b/src/onebot11/constructor.ts index c94335f..e7982de 100644 --- a/src/onebot11/constructor.ts +++ b/src/onebot11/constructor.ts @@ -17,7 +17,6 @@ import { Group, Peer, GroupMember, - PicType, RawMessage, SelfInfo, Sex, @@ -26,7 +25,7 @@ import { FriendV2, ChatType2 } from '../ntqqapi/types' -import { deleteGroup, getGroupMember, getSelfUin } from '../common/data' +import { getGroupMember, getSelfUin } from '../common/data' import { EventType } from './event/OB11BaseEvent' import { encodeCQCode } from './cqcode' import { MessageUnique } from '../common/utils/MessageUnique' @@ -358,7 +357,7 @@ export class OB11Constructor { const groupElement = grayTipElement?.groupElement if (groupElement) { // log("收到群提示消息", groupElement) - if (groupElement.type == TipGroupElementType.memberIncrease) { + if (groupElement.type === TipGroupElementType.memberIncrease) { log('收到群成员增加消息', groupElement) await sleep(1000) const member = await getGroupMember(msg.peerUid, groupElement.memberUid) @@ -406,25 +405,26 @@ export class OB11Constructor { ) } } - else if (groupElement.type == TipGroupElementType.kicked) { + else if (groupElement.type === TipGroupElementType.kicked) { log(`收到我被踢出或退群提示, 群${msg.peerUid}`, groupElement) - deleteGroup(msg.peerUid) NTQQGroupApi.quitGroup(msg.peerUid).then() - const selfUin = getSelfUin() try { - const adminUin = - (await getGroupMember(msg.peerUid, groupElement.adminUid))?.uin || - (await NTQQUserApi.getUserDetailInfo(groupElement.adminUid))?.uin + const adminUin = (await getGroupMember(msg.peerUid, groupElement.adminUid))?.uin || (await NTQQUserApi.getUidByUin(groupElement.adminUid)) if (adminUin) { return new OB11GroupDecreaseEvent( parseInt(msg.peerUid), - parseInt(selfUin), + parseInt(getSelfUin()), parseInt(adminUin), - 'kick_me', + 'kick_me' ) } } catch (e) { - return new OB11GroupDecreaseEvent(parseInt(msg.peerUid), parseInt(selfUin), 0, 'leave') + return new OB11GroupDecreaseEvent( + parseInt(msg.peerUid), + parseInt(getSelfUin()), + 0, + 'leave' + ) } } } @@ -677,7 +677,7 @@ export class OB11Constructor { sex: OB11Constructor.sex(member.sex!), age: 0, area: '', - level: 0, + level: '0', qq_level: (member.qqLevel && calcQQLevel(member.qqLevel)) || 0, join_time: 0, // 暂时没法获取 last_sent_time: 0, // 暂时没法获取 diff --git a/src/onebot11/types.ts b/src/onebot11/types.ts index 789e64f..a771f42 100644 --- a/src/onebot11/types.ts +++ b/src/onebot11/types.ts @@ -36,7 +36,7 @@ export interface OB11GroupMember { age?: number join_time?: number last_sent_time?: number - level?: number + level?: string qq_level?: number role?: OB11GroupMemberRole title?: string @@ -48,6 +48,7 @@ export interface OB11GroupMember { shut_up_timestamp?: number // 以下为扩展字段 is_robot?: boolean + qage?: number } export interface OB11Group {