feat: 管理员变动事件

feat: 加群事件
feat: 加群请求处理api
feat: 退群api
fix: 回复消息id改为string
This commit is contained in:
linyuchen
2024-02-23 19:56:20 +08:00
parent 67dfd7c22f
commit 829aba18f8
13 changed files with 213 additions and 52 deletions

View File

@@ -33,9 +33,13 @@ TG群<https://t.me/+nLZEnpne-pQ1OWFl>
- [x] 获取群列表 - [x] 获取群列表
- [x] 获取群成员列表 - [x] 获取群成员列表
- [x] 撤回消息 - [x] 撤回消息
- [x] 处理加群请求
- [x] 退群
- [x] 上报好友消息 - [x] 上报好友消息
- [x] 上报群消息 - [x] 上报群消息
- [x] 上报好友、群消息撤回 - [x] 上报好友、群消息撤回
- [x] 上报加群请求
- [x] 上报群员人数变动
消息格式支持: 消息格式支持:
- [x] cq码 - [x] cq码
@@ -63,6 +67,8 @@ TG群<https://t.me/+nLZEnpne-pQ1OWFl>
- [x] get_friend_list - [x] get_friend_list
- [x] get_msg - [x] get_msg
- [x] send_like - [x] send_like
- [x] set_group_add_request
- [x] set_group_leave
- [x] get_version_info - [x] get_version_info
- [x] get_status - [x] get_status
- [x] can_send_image - [x] can_send_image

View File

@@ -1,5 +1,5 @@
import {NTQQApi} from '../ntqqapi/ntcall'; import {NTQQApi} from '../ntqqapi/ntcall';
import {Friend, Group, GroupMember, RawMessage, SelfInfo} from "../ntqqapi/types"; import {Friend, Group, GroupMember, GroupNotify, RawMessage, SelfInfo} from "../ntqqapi/types";
export let groups: Group[] = [] export let groups: Group[] = []
export let friends: Friend[] = [] export let friends: Friend[] = []
@@ -7,9 +7,9 @@ export let msgHistory: Record<string, RawMessage> = {} // msgId: RawMessage
let globalMsgId = Math.floor(Date.now() / 1000); let globalMsgId = Math.floor(Date.now() / 1000);
export function addHistoryMsg(msg: RawMessage): boolean{ export function addHistoryMsg(msg: RawMessage): boolean {
let existMsg = msgHistory[msg.msgId] let existMsg = msgHistory[msg.msgId]
if (existMsg){ if (existMsg) {
Object.assign(existMsg, msg) Object.assign(existMsg, msg)
msg.msgShortId = existMsg.msgShortId; msg.msgShortId = existMsg.msgShortId;
return false return false
@@ -19,7 +19,7 @@ export function addHistoryMsg(msg: RawMessage): boolean{
return true return true
} }
export function getHistoryMsgByShortId(shortId: number | string){ export function getHistoryMsgByShortId(shortId: number | string) {
// log("getHistoryMsgByShortId", shortId, Object.values(msgHistory).map(m=>m.msgShortId)) // log("getHistoryMsgByShortId", shortId, Object.values(msgHistory).map(m=>m.msgShortId))
return Object.values(msgHistory).find(msg => msg.msgShortId.toString() == shortId.toString()) return Object.values(msgHistory).find(msg => msg.msgShortId.toString() == shortId.toString())
} }
@@ -43,20 +43,19 @@ export async function getGroup(qq: string): Promise<Group | undefined> {
return group return group
} }
export async function getGroupMember(groupQQ: string, memberQQ: string, memberUid: string=null) { export async function getGroupMember(groupQQ: string, memberQQ: string, memberUid: string = null) {
const group = await getGroup(groupQQ) const group = await getGroup(groupQQ)
if (group) { if (group) {
let filterFunc: (member: GroupMember) => boolean let filterFunc: (member: GroupMember) => boolean
if (memberQQ){ if (memberQQ) {
filterFunc = member => member.uin === memberQQ filterFunc = member => member.uin === memberQQ
} } else if (memberUid) {
else if (memberUid){
filterFunc = member => member.uid === memberUid filterFunc = member => member.uid === memberUid
} }
let member = group.members?.find(filterFunc) let member = group.members?.find(filterFunc)
if (!member){ if (!member) {
const _members = await NTQQApi.getGroupMembers(groupQQ) const _members = await NTQQApi.getGroupMembers(groupQQ)
if (_members.length){ if (_members.length) {
group.members = _members group.members = _members
} }
member = group.members?.find(filterFunc) member = group.members?.find(filterFunc)
@@ -77,7 +76,7 @@ export function getHistoryMsgBySeq(seq: string) {
} }
export let uidMaps:Record<string, string> = {} // 一串加密的字符串(uid) -> qq号 export let uidMaps: Record<string, string> = {} // 一串加密的字符串(uid) -> qq号
export function getUidByUin(uin: string) { export function getUidByUin(uin: string) {
for (const key in uidMaps) { for (const key in uidMaps) {
@@ -88,3 +87,5 @@ export function getUidByUin(uin: string) {
} }
export const version = "3.6.0" export const version = "3.6.0"
export let groupNotifies: Map<string, GroupNotify> = new Map();

View File

@@ -6,17 +6,19 @@ import {Config} from "../common/types";
import {CHANNEL_GET_CONFIG, CHANNEL_LOG, CHANNEL_SET_CONFIG,} from "../common/channels"; import {CHANNEL_GET_CONFIG, CHANNEL_LOG, CHANNEL_SET_CONFIG,} from "../common/channels";
import {ob11WebsocketServer} from "../onebot11/server/ws/WebsocketServer"; import {ob11WebsocketServer} from "../onebot11/server/ws/WebsocketServer";
import {CONFIG_DIR, getConfigUtil, log} from "../common/utils"; import {CONFIG_DIR, getConfigUtil, log} from "../common/utils";
import {addHistoryMsg, getGroup, getGroupMember, msgHistory, selfInfo} from "../common/data"; import {addHistoryMsg, getGroup, getGroupMember, groupNotifies, msgHistory, selfInfo} from "../common/data";
import {hookNTQQApiCall, hookNTQQApiReceive, ReceiveCmd, registerReceiveHook} from "../ntqqapi/hook"; import {hookNTQQApiCall, hookNTQQApiReceive, ReceiveCmd, registerReceiveHook} from "../ntqqapi/hook";
import {OB11Constructor} from "../onebot11/constructor"; import {OB11Constructor} from "../onebot11/constructor";
import {NTQQApi} from "../ntqqapi/ntcall"; import {NTQQApi} from "../ntqqapi/ntcall";
import {ChatType, GroupNotify, GroupNotifyTypes, RawMessage} from "../ntqqapi/types"; import {ChatType, GroupMember, GroupNotifies, GroupNotifyTypes, RawMessage} from "../ntqqapi/types";
import {ob11HTTPServer} from "../onebot11/server/http"; import {ob11HTTPServer} from "../onebot11/server/http";
import {OB11FriendRecallNoticeEvent} from "../onebot11/event/notice/OB11FriendRecallNoticeEvent"; import {OB11FriendRecallNoticeEvent} from "../onebot11/event/notice/OB11FriendRecallNoticeEvent";
import {OB11GroupRecallNoticeEvent} from "../onebot11/event/notice/OB11GroupRecallNoticeEvent"; import {OB11GroupRecallNoticeEvent} from "../onebot11/event/notice/OB11GroupRecallNoticeEvent";
import {postEvent} from "../onebot11/server/postevent"; import {postEvent} from "../onebot11/server/postevent";
import {ob11ReverseWebsockets} from "../onebot11/server/ws/ReverseWebsocket"; import {ob11ReverseWebsockets} from "../onebot11/server/ws/ReverseWebsocket";
import {OB11GroupAdminNoticeEvent} from "../onebot11/event/notice/OB11GroupAdminNoticeEvent"; import {OB11GroupAdminNoticeEvent} from "../onebot11/event/notice/OB11GroupAdminNoticeEvent";
import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecreaseEvent";
import {OB11GroupRequestEvent} from "../onebot11/event/request/OB11GroupRequest";
let running = false; let running = false;
@@ -165,7 +167,7 @@ function onLoad() {
}>(ReceiveCmd.UNREAD_GROUP_NOTIFY, async (payload) => { }>(ReceiveCmd.UNREAD_GROUP_NOTIFY, async (payload) => {
if (payload.unreadCount) { if (payload.unreadCount) {
log("开始获取群通知详情") log("开始获取群通知详情")
let notify: GroupNotify; let notify: GroupNotifies;
try { try {
notify = await NTQQApi.getGroupNotifies(); notify = await NTQQApi.getGroupNotifies();
}catch (e) { }catch (e) {
@@ -180,15 +182,19 @@ function onLoad() {
if (parseInt(notify.seq) / 1000 < startTime){ if (parseInt(notify.seq) / 1000 < startTime){
continue; continue;
} }
const member1 = await getGroupMember(notify.group.groupCode, null, notify.user1.uid);
let member2: GroupMember;
if (notify.user2.uid){
member2 = await getGroupMember(notify.group.groupCode, null, notify.user2.uid);
}
if ([GroupNotifyTypes.ADMIN_SET, GroupNotifyTypes.ADMIN_UNSET].includes(notify.type)) { if ([GroupNotifyTypes.ADMIN_SET, GroupNotifyTypes.ADMIN_UNSET].includes(notify.type)) {
log("有管理员变动通知"); log("有管理员变动通知");
let groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent() let groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent()
groupAdminNoticeEvent.group_id = parseInt(notify.group.groupCode); groupAdminNoticeEvent.group_id = parseInt(notify.group.groupCode);
log("开始获取变动的管理员") log("开始获取变动的管理员")
const member = await getGroupMember(notify.group.groupCode, null, notify.user1.uid); if(member1){
if(member){
log("变动管理员获取成功") log("变动管理员获取成功")
groupAdminNoticeEvent.user_id = parseInt(member.uin); groupAdminNoticeEvent.user_id = parseInt(member1.uin);
groupAdminNoticeEvent.sub_type = notify.type == GroupNotifyTypes.ADMIN_UNSET ? "unset" : "set"; groupAdminNoticeEvent.sub_type = notify.type == GroupNotifyTypes.ADMIN_UNSET ? "unset" : "set";
postEvent(groupAdminNoticeEvent, true); postEvent(groupAdminNoticeEvent, true);
} }
@@ -196,6 +202,28 @@ function onLoad() {
log("获取群通知的成员信息失败", notify, getGroup(notify.group.groupCode)); log("获取群通知的成员信息失败", notify, getGroup(notify.group.groupCode));
} }
} }
else if (notify.type == GroupNotifyTypes.MEMBER_EXIT){
log("有成员退出通知");
let groupDecreaseEvent = new OB11GroupDecreaseEvent(parseInt(notify.group.groupCode), parseInt(member1.uin))
// postEvent(groupDecreaseEvent, true);
}
else if ([GroupNotifyTypes.JOIN_REQUEST].includes(notify.type)){
log("有加群请求");
groupNotifies[notify.seq] = notify;
let groupRequestEvent = new OB11GroupRequestEvent();
groupRequestEvent.group_id = parseInt(notify.group.groupCode);
let requestQQ = ""
try {
requestQQ = (await NTQQApi.getUserInfo(notify.user1.uid)).uin;
}catch (e) {
log("获取加群人QQ号失败", e)
}
groupRequestEvent.user_id = parseInt(requestQQ) || 0;
groupRequestEvent.sub_type = "add"
groupRequestEvent.comment = notify.postscript;
groupRequestEvent.flag = notify.seq;
postEvent(groupRequestEvent);
}
} }
}catch (e) { }catch (e) {
log("解析群通知失败", e.stack); log("解析群通知失败", e.stack);

View File

@@ -6,14 +6,14 @@ import {
Friend, Friend,
Group, Group,
GroupMember, GroupMember,
GroupNotify, GroupNotifies, GroupNotify, GroupRequestOperateTypes,
RawMessage, RawMessage,
SelfInfo, SelfInfo,
SendMessageElement, SendMessageElement,
User User
} from "./types"; } from "./types";
import * as fs from "fs"; import * as fs from "fs";
import {addHistoryMsg, msgHistory, selfInfo} from "../common/data"; import {addHistoryMsg, groupNotifies, msgHistory, selfInfo} from "../common/data";
import {v4 as uuidv4} from "uuid" import {v4 as uuidv4} from "uuid"
interface IPCReceiveEvent { interface IPCReceiveEvent {
@@ -53,6 +53,8 @@ export enum NTQQApiMethod {
DOWNLOAD_MEDIA = "nodeIKernelMsgService/downloadRichMedia", DOWNLOAD_MEDIA = "nodeIKernelMsgService/downloadRichMedia",
MULTI_FORWARD_MSG = "nodeIKernelMsgService/multiForwardMsgWithComment", // 合并转发 MULTI_FORWARD_MSG = "nodeIKernelMsgService/multiForwardMsgWithComment", // 合并转发
GET_GROUP_NOTICE = "nodeIKernelGroupService/getSingleScreenNotifies", GET_GROUP_NOTICE = "nodeIKernelGroupService/getSingleScreenNotifies",
HANDLE_GROUP_REQUEST = "nodeIKernelGroupService/operateSysNotify",
QUIT_GROUP = "nodeIKernelGroupService/quitGroup",
} }
enum NTQQApiChannel { enum NTQQApiChannel {
@@ -67,11 +69,6 @@ export interface Peer {
guildId?: "" guildId?: ""
} }
enum CallBackType {
UUID,
METHOD
}
interface NTQQApiParams { interface NTQQApiParams {
methodName: NTQQApiMethod | string, methodName: NTQQApiMethod | string,
className?: NTQQApiClass, className?: NTQQApiClass,
@@ -504,13 +501,48 @@ export class NTQQApi {
methodName: ReceiveCmd.GROUP_NOTIFY, methodName: ReceiveCmd.GROUP_NOTIFY,
classNameIsRegister: true, classNameIsRegister: true,
}) })
return await callNTQQApi<GroupNotify>({ return await callNTQQApi<GroupNotifies>({
methodName: NTQQApiMethod.GET_GROUP_NOTICE, methodName: NTQQApiMethod.GET_GROUP_NOTICE,
cbCmd: ReceiveCmd.GROUP_NOTIFY, cbCmd: ReceiveCmd.GROUP_NOTIFY,
args:[ args: [
{"doubt":false,"startSeq":"","number":14}, {"doubt": false, "startSeq": "", "number": 14},
null null
] ]
}); });
} }
static async handleGroupRequest(seq: string, operateType: GroupRequestOperateTypes, reason?: string) {
const notify: GroupNotify = groupNotifies[seq];
if (!notify){
throw `${seq}对应的加群通知不存在`
}
return await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.HANDLE_GROUP_REQUEST,
args: [
{
"doubt": false,
"operateMsg": {
"operateType": operateType, // 2 拒绝
"targetMsg": {
"seq": seq, // 通知序列号
"type": notify.type,
"groupCode": notify.group.groupCode,
"postscript": reason
}
}
},
null
]
});
}
static async quitGroup(groupQQ: string){
await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.QUIT_GROUP,
args:[
{"groupCode": groupQQ},
null
]
})
}
} }

View File

@@ -242,29 +242,41 @@ export interface RawMessage {
}[]; }[];
} }
export enum GroupNotifyTypes{ export enum GroupNotifyTypes {
INVITED_JOIN = 4, // 有人接受了邀请入群
JOIN_REQUEST = 7,
ADMIN_SET = 8, ADMIN_SET = 8,
ADMIN_UNSET = 12 ADMIN_UNSET = 12,
MEMBER_EXIT = 11, // 主动退出?
}
export interface GroupNotifies {
doubt: boolean,
nextStartSeq: string,
notifies: GroupNotify[],
} }
export interface GroupNotify { export interface GroupNotify {
doubt: boolean,
nextStartSeq: string, seq: string, // 转成数字再除以1000应该就是时间戳
notifies: [{ type: GroupNotifyTypes,
seq: string, // 转成数字再除以1000应该就是时间戳 status: 0, // 未知
type: GroupNotifyTypes, group: { groupCode: string, groupName: string },
status: 0, // 未知 user1: { uid: string, nickName: string }, // 被设置管理员的人
group: { groupCode: string, groupName: string }, user2: { uid: string, nickName: string }, // 操作者
user1: { uid: string, nickName: string }, // 被设置管理员的人 actionUser: { uid: string, nickName: string }, //未知
user2: { uid: string, nickName: string }, // 操作者 actionTime: string,
actionUser: { uid: string, nickName: string }, //未知 invitationExt: {
actionTime: string, srcType: number, // 0?未知
invitationExt: { groupCode: string, waitStatus: number
srcType: number, // 0?未知 },
groupCode: string, waitStatus: number postscript: string, // 加群用户填写的验证信息
}, repeatSeqs: [],
postscript: string, warningTips: string
repeatSeqs: [], }
warningTips: string
}] export enum GroupRequestOperateTypes{
approve = 1,
reject = 2
} }

View File

@@ -0,0 +1,9 @@
import BaseAction from "./BaseAction";
import {ActionName} from "./types";
export default class GetGuildList extends BaseAction<null, null>{
actionName = ActionName.GetGuildList
protected async _handle(payload: null): Promise<null> {
return null;
}
}

View File

@@ -0,0 +1,30 @@
import BaseAction from "./BaseAction";
import {groupNotifies} from "../../common/data";
import {GroupNotify, GroupRequestOperateTypes} from "../../ntqqapi/types";
import {NTQQApi} from "../../ntqqapi/ntcall";
import {ActionName} from "./types";
interface Payload{
flag: string,
// sub_type: "add" | "invite",
// type: "add" | "invite"
approve: boolean,
reason: string
}
export default class SetGroupAddRequest extends BaseAction<Payload, null>{
actionName = ActionName.SetGroupAddRequest
protected async _handle(payload: Payload): Promise<null> {
const seq = payload.flag.toString();
const notify: GroupNotify = groupNotifies[seq]
try{
await NTQQApi.handleGroupRequest(seq,
payload.approve ? GroupRequestOperateTypes.approve: GroupRequestOperateTypes.reject,
payload.reason
)
}catch (e) {
throw e
}
return null
}
}

View File

@@ -0,0 +1,22 @@
import BaseAction from "./BaseAction";
import {NTQQApi} from "../../ntqqapi/ntcall";
import {log} from "../../common/utils";
import {ActionName} from "./types";
interface Payload{
group_id: number,
is_dismiss: boolean
}
export default class SetGroupLeave extends BaseAction<Payload, any>{
actionName = ActionName.SetGroupLeave
protected async _handle(payload: Payload): Promise<any> {
try{
await NTQQApi.quitGroup(payload.group_id.toString())
}
catch (e) {
log("退群失败", e)
throw e
}
}
}

View File

@@ -17,6 +17,9 @@ import GetStatus from "./GetStatus";
import {GoCQHTTPSendGroupForwardMsg, GoCQHTTPSendPrivateForwardMsg} from "./go-cqhttp/SendForwardMsg"; import {GoCQHTTPSendGroupForwardMsg, GoCQHTTPSendPrivateForwardMsg} from "./go-cqhttp/SendForwardMsg";
import GoCQHTTPGetStrangerInfo from "./go-cqhttp/GetStrangerInfo"; import GoCQHTTPGetStrangerInfo from "./go-cqhttp/GetStrangerInfo";
import SendLike from "./SendLike"; import SendLike from "./SendLike";
import SetGroupAddRequest from "./SetGroupAddRequest";
import SetGroupLeave from "./SetGroupLeave";
import GetGuildList from "./GetGuildList";
export const actionHandlers = [ export const actionHandlers = [
new SendLike(), new SendLike(),
@@ -26,6 +29,8 @@ export const actionHandlers = [
new GetGroupList(), new GetGroupInfo(), new GetGroupMemberList(), new GetGroupMemberInfo(), new GetGroupList(), new GetGroupInfo(), new GetGroupMemberList(), new GetGroupMemberInfo(),
new SendGroupMsg(), new SendPrivateMsg(), new SendMsg(), new SendGroupMsg(), new SendPrivateMsg(), new SendMsg(),
new DeleteMsg(), new DeleteMsg(),
new SetGroupAddRequest(),
new SetGroupLeave(),
new GetVersionInfo(), new GetVersionInfo(),
new CanSendRecord(), new CanSendRecord(),
new CanSendImage(), new CanSendImage(),
@@ -34,7 +39,8 @@ export const actionHandlers = [
//以下为go-cqhttp api //以下为go-cqhttp api
new GoCQHTTPSendGroupForwardMsg(), new GoCQHTTPSendGroupForwardMsg(),
new GoCQHTTPSendPrivateForwardMsg(), new GoCQHTTPSendPrivateForwardMsg(),
new GoCQHTTPGetStrangerInfo() new GoCQHTTPGetStrangerInfo(),
new GetGuildList()
] ]

View File

@@ -1,3 +1,5 @@
import GetGuildList from "./GetGuildList";
export type BaseCheckResult = ValidCheckResult | InvalidCheckResult export type BaseCheckResult = ValidCheckResult | InvalidCheckResult
export interface ValidCheckResult { export interface ValidCheckResult {
@@ -25,6 +27,8 @@ export enum ActionName {
SendGroupMsg = "send_group_msg", SendGroupMsg = "send_group_msg",
SendPrivateMsg = "send_private_msg", SendPrivateMsg = "send_private_msg",
DeleteMsg = "delete_msg", DeleteMsg = "delete_msg",
SetGroupAddRequest = "set_group_add_request",
SetGroupLeave = "set_group_leave",
GetVersionInfo = "get_version_info", GetVersionInfo = "get_version_info",
GetStatus = "get_status", GetStatus = "get_status",
CanSendRecord = "can_send_record", CanSendRecord = "can_send_record",
@@ -32,5 +36,6 @@ export enum ActionName {
// 以下为go-cqhttp api // 以下为go-cqhttp api
GoCQHTTP_SendGroupForwardMsg = "send_group_forward_msg", GoCQHTTP_SendGroupForwardMsg = "send_group_forward_msg",
GoCQHTTP_SendPrivateForwardMsg = "send_private_forward_msg", GoCQHTTP_SendPrivateForwardMsg = "send_private_forward_msg",
GoCQHTTP_GetStrangerInfo = "get_stranger_info" GoCQHTTP_GetStrangerInfo = "get_stranger_info",
GetGuildList = "get_guild_list",
} }

View File

@@ -194,7 +194,8 @@ export class OB11Constructor {
group_id: parseInt(group_id), group_id: parseInt(group_id),
user_id: parseInt(member.uin), user_id: parseInt(member.uin),
nickname: member.nick, nickname: member.nick,
card: member.cardName card: member.cardName,
role: OB11Constructor.groupMemberRole(member.role),
} }
} }

View File

@@ -2,7 +2,7 @@ import {OB11GroupNoticeEvent} from "./OB11GroupNoticeEvent";
export class OB11GroupDecreaseEvent extends OB11GroupNoticeEvent { export class OB11GroupDecreaseEvent extends OB11GroupNoticeEvent {
notice_type = "group_decrease"; notice_type = "group_decrease";
sub_type = "leave"; // TODO: 实现其他几种子类型的识别 ("leave" | "kick" | "kick_me") sub_type: "leave" | "kick" | "kick_me" = "leave"; // TODO: 实现其他几种子类型的识别 ("leave" | "kick" | "kick_me")
operate_id: number; operate_id: number;
constructor(groupId: number, userId: number) { constructor(groupId: number, userId: number) {

View File

@@ -0,0 +1,9 @@
import {OB11GroupNoticeEvent} from "../notice/OB11GroupNoticeEvent";
export class OB11GroupRequestEvent extends OB11GroupNoticeEvent{
request_type: "group" = "group";
sub_type: "add" | "invite" = "add";
comment: string;
flag: string;
}