From 1f0dad786c509899235defa40717b13964132de1 Mon Sep 17 00:00:00 2001 From: linyuchen Date: Fri, 23 Feb 2024 04:08:20 +0800 Subject: [PATCH] feat: group admin change notice --- src/common/config.ts | 2 + src/common/data.ts | 2 +- src/main/main.ts | 68 ++++++++++++++++--- src/ntqqapi/hook.ts | 24 +++---- src/ntqqapi/ntcall.ts | 48 ++++++++++--- src/ntqqapi/types.ts | 27 ++++++++ src/onebot11/constructor.ts | 2 +- .../event/notice/OB11GroupAdminNoticeEvent.ts | 5 +- src/onebot11/server/postevent.ts | 4 +- 9 files changed, 146 insertions(+), 36 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index 366011c..8829fa7 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -2,6 +2,8 @@ import fs from "fs"; import {Config, OB11Config} from "./types"; import {mergeNewProperties} from "./utils"; +export const HOOK_LOG = false; + export class ConfigUtil { private readonly configPath: string; private config: Config | null = null; diff --git a/src/common/data.ts b/src/common/data.ts index 3422cab..a443a31 100644 --- a/src/common/data.ts +++ b/src/common/data.ts @@ -43,7 +43,7 @@ export async function getGroup(qq: string): Promise { return group } -export async function getGroupMember(groupQQ: string, memberQQ: string=null, memberUid: string=null) { +export async function getGroupMember(groupQQ: string, memberQQ: string, memberUid: string=null) { const group = await getGroup(groupQQ) if (group) { let filterFunc: (member: GroupMember) => boolean diff --git a/src/main/main.ts b/src/main/main.ts index b0f9b78..b8153f8 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -6,17 +6,17 @@ import {Config} from "../common/types"; import {CHANNEL_GET_CONFIG, CHANNEL_LOG, CHANNEL_SET_CONFIG,} from "../common/channels"; import {ob11WebsocketServer} from "../onebot11/server/ws/WebsocketServer"; import {CONFIG_DIR, getConfigUtil, log} from "../common/utils"; -import {addHistoryMsg, getGroupMember, msgHistory, selfInfo} from "../common/data"; +import {addHistoryMsg, getGroup, getGroupMember, msgHistory, selfInfo} from "../common/data"; import {hookNTQQApiCall, hookNTQQApiReceive, ReceiveCmd, registerReceiveHook} from "../ntqqapi/hook"; import {OB11Constructor} from "../onebot11/constructor"; import {NTQQApi} from "../ntqqapi/ntcall"; -import {ChatType, RawMessage} from "../ntqqapi/types"; +import {ChatType, GroupNotify, GroupNotifyTypes, RawMessage} from "../ntqqapi/types"; import {ob11HTTPServer} from "../onebot11/server/http"; import {OB11FriendRecallNoticeEvent} from "../onebot11/event/notice/OB11FriendRecallNoticeEvent"; import {OB11GroupRecallNoticeEvent} from "../onebot11/event/notice/OB11GroupRecallNoticeEvent"; import {postEvent} from "../onebot11/server/postevent"; import {ob11ReverseWebsockets} from "../onebot11/server/ws/ReverseWebsocket"; -import {EventType} from "../onebot11/event/OB11BaseEvent"; +import {OB11GroupAdminNoticeEvent} from "../onebot11/event/notice/OB11GroupAdminNoticeEvent"; let running = false; @@ -105,8 +105,7 @@ function onLoad() { } } - - async function start() { + async function startReceiveHook() { registerReceiveHook<{ msgList: Array }>(ReceiveCmd.NEW_MSG, (payload) => { try { postReceiveMsg(payload.msgList); @@ -159,6 +158,55 @@ function onLoad() { log("report self message error: ", e.toString()); } }) + registerReceiveHook<{ + "doubt": boolean, + "oldestUnreadSeq": string, + "unreadCount": number + }>(ReceiveCmd.UNREAD_GROUP_NOTIFY, async (payload) => { + if (payload.unreadCount) { + log("开始获取群通知详情") + let notify: GroupNotify; + try { + notify = await NTQQApi.getGroupNotifies(); + }catch (e) { + // log("获取群通知详情失败", e); + return + } + + const notifies = notify.notifies.slice(0, payload.unreadCount) + log("获取群通知详情完成", notifies, payload); + try { + for (const notify of notifies) { + if (parseInt(notify.seq) / 1000 < startTime){ + continue; + } + if ([GroupNotifyTypes.ADMIN_SET, GroupNotifyTypes.ADMIN_UNSET].includes(notify.type)) { + log("有管理员变动通知"); + let groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent() + groupAdminNoticeEvent.group_id = parseInt(notify.group.groupCode); + log("开始获取变动的管理员") + const member = await getGroupMember(notify.group.groupCode, null, notify.user1.uid); + if(member){ + log("变动管理员获取成功") + groupAdminNoticeEvent.user_id = parseInt(member.uin); + groupAdminNoticeEvent.sub_type = notify.type == GroupNotifyTypes.ADMIN_UNSET ? "unset" : "set"; + postEvent(groupAdminNoticeEvent, true); + } + else{ + log("获取群通知的成员信息失败", notify, getGroup(notify.group.groupCode)); + } + } + } + }catch (e) { + log("解析群通知失败", e.stack); + } + } + }) + } + let startTime = 0; + async function start() { + startTime = Date.now(); + startReceiveHook().then(); NTQQApi.getGroups(true).then() const config = getConfigUtil().getConfig() if (config.ob11.enableHttp) { @@ -168,16 +216,17 @@ function onLoad() { log("http server start failed", e); } } - if (config.ob11.enableWs){ + if (config.ob11.enableWs) { ob11WebsocketServer.start(config.ob11.wsPort); } - if (config.ob11.enableWsReverse){ + if (config.ob11.enableWsReverse) { ob11ReverseWebsockets.start(); } log("LLOneBot start") } + let getSelfNickCount = 0; const init = async () => { try { const _ = await NTQQApi.getSelfInfo(); @@ -194,7 +243,10 @@ function onLoad() { if (userInfo) { selfInfo.nick = userInfo.nick; } else { - return setTimeout(init, 1000); + getSelfNickCount++; + if (getSelfNickCount < 10){ + return setTimeout(init, 1000); + } } } catch (e) { log("get self nickname failed", e.toString()); diff --git a/src/ntqqapi/hook.ts b/src/ntqqapi/hook.ts index 9e4fd95..67b1528 100644 --- a/src/ntqqapi/hook.ts +++ b/src/ntqqapi/hook.ts @@ -7,6 +7,7 @@ import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecrease import {OB11GroupIncreaseEvent} from "../onebot11/event/notice/OB11GroupIncreaseEvent"; import {v4 as uuidv4} from "uuid" import {postEvent} from "../onebot11/server/postevent"; +import {HOOK_LOG} from "../common/config"; export let hookApiCallbacks: Record void> = {} @@ -19,7 +20,8 @@ export enum ReceiveCmd { GROUPS_UNIX = "onGroupListUpdate", FRIENDS = "onBuddyListChange", MEDIA_DOWNLOAD_COMPLETE = "nodeIKernelMsgListener/onRichMediaDownloadComplete", - UNREAD_GROUP_NOTICE = "nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated" + UNREAD_GROUP_NOTIFY = "nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated", + GROUP_NOTIFY = "nodeIKernelGroupListener/onGroupSingleScreenNotifies" } interface NTQQApiReturnData extends Array { @@ -45,7 +47,7 @@ let receiveHooks: Array<{ export function hookNTQQApiReceive(window: BrowserWindow) { const originalSend = window.webContents.send; const patchSend = (channel: string, ...args: NTQQApiReturnData) => { - // log(`received ntqq api message: ${channel}`, JSON.stringify(args)) + HOOK_LOG && log(`received ntqq api message: ${channel}`, JSON.stringify(args)) if (args?.[1] instanceof Array) { for (let receiveData of args?.[1]) { const ntQQApiMethodName = receiveData.cmdName; @@ -89,15 +91,15 @@ export function hookNTQQApiCall(window: BrowserWindow) { const proxyIpcMsg = new Proxy(ipc_message_proxy, { apply(target, thisArg, args) { - log("call NTQQ api", thisArg, args); + HOOK_LOG && log("call NTQQ api", thisArg, args); return target.apply(thisArg, args); }, }); - // if (webContents._events["-ipc-message"]?.[0]) { - // webContents._events["-ipc-message"][0] = proxyIpcMsg; - // } else { - // webContents._events["-ipc-message"] = proxyIpcMsg; - // } + if (webContents._events["-ipc-message"]?.[0]) { + webContents._events["-ipc-message"][0] = proxyIpcMsg; + } else { + webContents._events["-ipc-message"] = proxyIpcMsg; + } } export function registerReceiveHook(method: ReceiveCmd, hookFunc: (payload: PayloadType) => void): string { @@ -253,9 +255,3 @@ registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmd.SELF_SEND_MSG, ({msgRe } }) -registerReceiveHook<{"doubt": boolean,"oldestUnreadSeq": string,"unreadCount": number}>(ReceiveCmd.UNREAD_GROUP_NOTICE, (payload)=>{ - log("收到群通知", payload); - if (payload.unreadCount){ - - } -}) \ No newline at end of file diff --git a/src/ntqqapi/ntcall.ts b/src/ntqqapi/ntcall.ts index 719a29f..29d96b3 100644 --- a/src/ntqqapi/ntcall.ts +++ b/src/ntqqapi/ntcall.ts @@ -1,7 +1,17 @@ import {ipcMain} from "electron"; import {hookApiCallbacks, ReceiveCmd, registerReceiveHook, removeReceiveHook} from "./hook"; import {log} from "../common/utils"; -import {ChatType, Friend, Group, GroupMember, RawMessage, SelfInfo, SendMessageElement, User} from "./types"; +import { + ChatType, + Friend, + Group, + GroupMember, + GroupNotify, + RawMessage, + SelfInfo, + SendMessageElement, + User +} from "./types"; import * as fs from "fs"; import {addHistoryMsg, msgHistory, selfInfo} from "../common/data"; import {v4 as uuidv4} from "uuid" @@ -42,7 +52,7 @@ export enum NTQQApiMethod { SEND_MSG = "nodeIKernelMsgService/sendMsg", DOWNLOAD_MEDIA = "nodeIKernelMsgService/downloadRichMedia", MULTI_FORWARD_MSG = "nodeIKernelMsgService/multiForwardMsgWithComment", // 合并转发 - GET_GROUP_NOTICE = "nodeIKernelGroupListener/onGroupSingleScreenNotifies", + GET_GROUP_NOTICE = "nodeIKernelGroupService/getSingleScreenNotifies", } enum NTQQApiChannel { @@ -63,7 +73,7 @@ enum CallBackType { } interface NTQQApiParams { - methodName: NTQQApiMethod, + methodName: NTQQApiMethod | string, className?: NTQQApiClass, channel?: NTQQApiChannel, classNameIsRegister?: boolean @@ -77,7 +87,7 @@ function callNTQQApi(params: NTQQApiParams) { let { className, methodName, channel, args, cbCmd, timeoutSecond: timeout, - cmdCB + classNameIsRegister, cmdCB } = params; className = className ?? NTQQApiClass.NT_API; channel = channel ?? NTQQApiChannel.IPC_UP_2; @@ -89,6 +99,11 @@ function callNTQQApi(params: NTQQApiParams) { // log("callNTQQApiPromise", channel, className, methodName, args, uuid) const _timeout = timeout * 1000 let success = false + let eventName = className + "-" + channel[channel.length - 1]; + if (classNameIsRegister) { + eventName += "-register"; + } + const apiArgs = [methodName, ...args] if (!cbCmd) { // QQ后端会返回结果,并且可以插根据uuid识别 hookApiCallbacks[uuid] = (r: ReturnType) => { @@ -123,12 +138,11 @@ function callNTQQApi(params: NTQQApiParams) { setTimeout(() => { // log("ntqq api timeout", success, channel, className, methodName) if (!success) { - log(`ntqq api timeout ${channel}, ${className}, ${methodName}`) - reject(`ntqq api timeout ${channel}, ${className}, ${methodName}`) + log(`ntqq api timeout ${channel}, ${eventName}, ${methodName}`, apiArgs); + reject(`ntqq api timeout ${channel}, ${eventName}, ${methodName}, ${apiArgs}`) } }, _timeout) - const eventName = className + "-" + channel[channel.length - 1]; - const apiArgs = [methodName, ...args] + ipcMain.emit( channel, {}, @@ -242,6 +256,7 @@ export class NTQQApi { } // log(uidMaps); // log("members info", values); + log(`get group ${groupQQ} members success`) return members } catch (e) { log(`get group ${groupQQ} members failed`, e) @@ -481,4 +496,21 @@ export class NTQQApi { }) }) } + + static async getGroupNotifies() { + // 获取管理员变更 + // 加群通知,退出通知,需要管理员权限 + await callNTQQApi({ + methodName: ReceiveCmd.GROUP_NOTIFY, + classNameIsRegister: true, + }) + return await callNTQQApi({ + methodName: NTQQApiMethod.GET_GROUP_NOTICE, + cbCmd: ReceiveCmd.GROUP_NOTIFY, + args:[ + {"doubt":false,"startSeq":"","number":14}, + null + ] + }); + } } \ No newline at end of file diff --git a/src/ntqqapi/types.ts b/src/ntqqapi/types.ts index af63b4c..8f57d80 100644 --- a/src/ntqqapi/types.ts +++ b/src/ntqqapi/types.ts @@ -241,3 +241,30 @@ export interface RawMessage { faceElement: FaceElement; }[]; } + +export enum GroupNotifyTypes{ + ADMIN_SET = 8, + ADMIN_UNSET = 12 +} + +export interface GroupNotify { + doubt: boolean, + nextStartSeq: string, + notifies: [{ + seq: string, // 转成数字,再除以1000应该就是时间戳? + type: GroupNotifyTypes, + status: 0, // 未知 + group: { groupCode: string, groupName: string }, + user1: { uid: string, nickName: string }, // 被设置管理员的人 + user2: { uid: string, nickName: string }, // 操作者 + actionUser: { uid: string, nickName: string }, //未知 + actionTime: string, + invitationExt: { + srcType: number, // 0?未知 + groupCode: string, waitStatus: number + }, + postscript: string, + repeatSeqs: [], + warningTips: string + }] +} \ No newline at end of file diff --git a/src/onebot11/constructor.ts b/src/onebot11/constructor.ts index 79b155d..7c33848 100644 --- a/src/onebot11/constructor.ts +++ b/src/onebot11/constructor.ts @@ -114,7 +114,7 @@ export class OB11Constructor { message_data["type"] = "reply" const replyMsg = getHistoryMsgBySeq(element.replyElement.replayMsgSeq) if (replyMsg) { - message_data["data"]["id"] = replyMsg.msgShortId + message_data["data"]["id"] = replyMsg.msgShortId.toString() } else { continue } diff --git a/src/onebot11/event/notice/OB11GroupAdminNoticeEvent.ts b/src/onebot11/event/notice/OB11GroupAdminNoticeEvent.ts index f30739e..a7c3ff1 100644 --- a/src/onebot11/event/notice/OB11GroupAdminNoticeEvent.ts +++ b/src/onebot11/event/notice/OB11GroupAdminNoticeEvent.ts @@ -1,6 +1,7 @@ import {OB11BaseNoticeEvent} from "./OB11BaseNoticeEvent"; +import {OB11GroupNoticeEvent} from "./OB11GroupNoticeEvent"; -export class OB11GroupAdminNoticeEvent extends OB11BaseNoticeEvent { +export class OB11GroupAdminNoticeEvent extends OB11GroupNoticeEvent { notice_type = "group_admin" - sub_type: string // "set" | "unset" + sub_type: "set" | "unset" // "set" | "unset" } \ No newline at end of file diff --git a/src/onebot11/server/postevent.ts b/src/onebot11/server/postevent.ts index 0db474b..ef63fcb 100644 --- a/src/onebot11/server/postevent.ts +++ b/src/onebot11/server/postevent.ts @@ -29,10 +29,10 @@ export function postWsEvent(event: PostEventType) { } } -export function postEvent(msg: PostEventType) { +export function postEvent(msg: PostEventType, reportSelf=false) { const config = getConfigUtil().getConfig(); // 判断msg是否是event - if (!config.reportSelfMessage) { + if (!config.reportSelfMessage && !reportSelf) { if ((msg as OB11Message).user_id.toString() == selfInfo.uin) { return }