diff --git a/Makefile b/Makefile index 9f98d5bb..0b7f6d41 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,6 @@ PROTO_FILES := \ $(PROTO_DIR)/service/*.proto \ $(PROTO_DIR)/service/highway/*.proto \ $(PROTO_DIR)/service/oidb/*.proto \ - $(PROTO_DIR)/status/*.proto \ $(PROTO_DIR)/*.proto diff --git a/client/base.go b/client/base.go index 7a3a8af1..a338e362 100644 --- a/client/base.go +++ b/client/base.go @@ -57,15 +57,15 @@ type QQClient struct { friendCache map[uint32]*entity.Friend groupCache map[uint32]map[uint32]*entity.GroupMember - GroupMessageEvent EventHandle[*message.GroupMessage] - PrivateMessageEvent EventHandle[*message.PrivateMessage] - TempMessageEvent EventHandle[*message.TempMessage] - GroupInvitedEvent EventHandle[*event.GroupMemberJoinRequest] - GroupJoinEvent EventHandle[*event.GroupMemberJoined] - GroupLeaveEvent EventHandle[*event.GroupMemberQuit] - GroupMemberJoinEvent EventHandle[*event.GroupMemberJoined] - GroupMemberLeaveEvent EventHandle[*event.GroupMemberQuit] - // GroupMuteEvent EventHandle[*event.GroupMuteMember] TODO: empty implementation now + GroupMessageEvent EventHandle[*message.GroupMessage] + PrivateMessageEvent EventHandle[*message.PrivateMessage] + TempMessageEvent EventHandle[*message.TempMessage] + GroupInvitedEvent EventHandle[*event.GroupInvite] // 邀请入群 + GroupMemberJoinRequestEvent EventHandle[*event.GroupMemberJoinRequest] // 加群申请 + GroupMemberJoinEvent EventHandle[*event.GroupMemberIncrease] // 成员入群 + GroupMemberLeaveEvent EventHandle[*event.GroupMemberDecrease] // 成员退群 + GroupMuteEvent EventHandle[*event.GroupMute] + GroupRecallEvent EventHandle[*event.GroupRecall] } func (c *QQClient) SendOidbPacket(pkt *oidb.OidbPacket) error { diff --git a/client/event.go b/client/event.go index d437738a..6fff8af0 100644 --- a/client/event.go +++ b/client/event.go @@ -54,20 +54,18 @@ func OnEvent(client *QQClient, msg any) { client.GroupMessageEvent.dispatch(client, msg) case *message.TempMessage: client.TempMessageEvent.dispatch(client, msg) - case *event.GroupMemberJoinRequest: + case *event.GroupInvite: client.GroupInvitedEvent.dispatch(client, msg) - case *event.GroupMemberJoined: - if client.uin == msg.Uin { - client.GroupJoinEvent.dispatch(client, msg) - } else { - client.GroupMemberJoinEvent.dispatch(client, msg) - } - case *event.GroupMemberQuit: - if client.uin == msg.Uin { - client.GroupLeaveEvent.dispatch(client, msg) - } else { - client.GroupMemberLeaveEvent.dispatch(client, msg) - } + case *event.GroupMemberJoinRequest: + client.GroupMemberJoinRequestEvent.dispatch(client, msg) + case *event.GroupMemberIncrease: + client.GroupMemberJoinEvent.dispatch(client, msg) + case *event.GroupMemberDecrease: + client.GroupMemberLeaveEvent.dispatch(client, msg) + case *event.GroupMute: + client.GroupMuteEvent.dispatch(client, msg) + case *event.GroupRecall: + client.GroupRecallEvent.dispatch(client, msg) case nil: networkLogger.Errorf("nil event msg, ignore") default: diff --git a/client/listener.go b/client/listener.go index f7f84819..33762907 100644 --- a/client/listener.go +++ b/client/listener.go @@ -9,8 +9,8 @@ import ( eventConverter "github.com/LagrangeDev/LagrangeGo/event" msgConverter "github.com/LagrangeDev/LagrangeGo/message" "github.com/LagrangeDev/LagrangeGo/packets/pb/message" - "github.com/LagrangeDev/LagrangeGo/packets/pb/status" "github.com/LagrangeDev/LagrangeGo/packets/wtlogin" + "github.com/LagrangeDev/LagrangeGo/utils/binary" ) var listeners = map[string]func(*QQClient, *wtlogin.SSOPacket) (any, error){ @@ -24,58 +24,78 @@ func decodeOlPushServicePacket(c *QQClient, pkt *wtlogin.SSOPacket) (any, error) if err != nil { return nil, err } - + pkg := msg.Message + typ := pkg.ContentHead.Type defer func() { if r := recover(); r != nil { networkLogger.Errorf("Recovered from panic: %v\n%s", r, debug.Stack()) networkLogger.Errorf("protobuf data: %x", pkt.Data) } }() - if msg.Message.Body == nil { + if pkg.Body == nil { return nil, errors.New("message body is empty") } - switch msg.Message.ContentHead.Type { + switch typ { case 166: // private msg return msgConverter.ParsePrivateMessage(&msg), nil case 82: // group msg return msgConverter.ParseGroupMessage(&msg), nil case 141: // temp msg return msgConverter.ParseTempMessage(&msg), nil - case 33: // member joined - pb := status.MemberChanged{} - err := proto.Unmarshal(msg.Message.Body.MsgContent, &pb) + case 33: // member increase + pb := message.GroupChange{} + err = proto.Unmarshal(pkg.Body.MsgContent, &pb) + if err != nil { + return nil, err + } + return eventConverter.ParseMemberIncreaseEvent(&pb), nil + case 34: // member decrease + pb := message.GroupChange{} + err = proto.Unmarshal(pkg.Body.MsgContent, &pb) if err != nil { return nil, err } - return eventConverter.ParseMemberJoined(&msg, &pb), nil - case 34: // member exit - pb := status.MemberChanged{} - err := proto.Unmarshal(msg.Message.Body.MsgContent, &pb) + return eventConverter.ParseMemberDecreaseEvent(&pb), nil + case 84: // group request join notice + pb := message.GroupJoin{} + err = proto.Unmarshal(pkg.Body.MsgContent, &pb) if err != nil { return nil, err } - return eventConverter.ParseMemberQuit(&msg, &pb), nil - case 84: - pb := status.MemberJoinRequest{} - err := proto.Unmarshal(msg.Message.Body.MsgContent, &pb) + return eventConverter.ParseRequestJoinNotice(&pb), nil + case 525: // group request invitation notice + pb := message.GroupInvitation{} + err = proto.Unmarshal(pkg.Body.MsgContent, &pb) if err != nil { return nil, err } - return eventConverter.ParseMemberJoinRequest(&pb), nil - case 525: - pb := status.MemberInviteRequest{} - err := proto.Unmarshal(msg.Message.Body.MsgContent, &pb) + return eventConverter.ParseRequestInvitationNotice(&pb), nil + case 87: // group invite notice + pb := message.GroupInvite{} + err = proto.Unmarshal(pkg.Body.MsgContent, &pb) if err != nil { return nil, err } - return eventConverter.ParseMemberJoinRequestFromInvite(&pb), nil + return eventConverter.ParseInviteNotice(&pb), nil case 0x2DC: // grp event, 732 - subType := msg.Message.ContentHead.SubType.Unwrap() + subType := pkg.ContentHead.SubType.Unwrap() switch subType { + case 20: // nudget(grp_id only) + return nil, nil + case 17: // recall + reader := binary.NewReader(pkg.Body.MsgContent) + _ = reader.ReadU32() // group uin + _ = reader.ReadU8() // reserve + pb := message.NotifyMessageBody{} + err = proto.Unmarshal(reader.ReadBytesWithLength("u16", false), &pb) + if err != nil { + return nil, err + } + return eventConverter.ParseGroupRecallEvent(&pb), nil case 12: // mute - pb := map[int]any{} - err := proto.Unmarshal(msg.Message.Body.MsgContent, &pb) + pb := message.GroupMute{} + err = proto.Unmarshal(pkg.Body.MsgContent, &pb) if err != nil { return nil, err } @@ -84,7 +104,7 @@ func decodeOlPushServicePacket(c *QQClient, pkt *wtlogin.SSOPacket) (any, error) networkLogger.Warningf("Unsupported group event, subType: %v", subType) } default: - networkLogger.Warningf("Unsupported message type: %v", msg.Message.ContentHead.Type) + networkLogger.Warningf("Unsupported message type: %v", typ) } return nil, nil diff --git a/event/group.go b/event/group.go index 7a23cf6f..a521221a 100644 --- a/event/group.go +++ b/event/group.go @@ -2,95 +2,138 @@ package event import ( "github.com/LagrangeDev/LagrangeGo/packets/pb/message" - "github.com/LagrangeDev/LagrangeGo/packets/pb/status" ) type ( GroupEvent struct { - GroupID uint32 + GroupUin uint32 } - GroupMuteMember struct { + GroupMute struct { GroupEvent OperatorUid string TargetUid string // when TargetUid is empty, mute all members - Duration uint32 + Duration uint32 // Duration == math.MaxUint32 when means mute all + } + + GroupRecall struct { + GroupEvent + AuthorUid string + OperatorUid string + Sequence uint64 + Time uint32 + Random uint32 } GroupMemberJoinRequest struct { GroupEvent - Uid string + TargetUid string InvitorUid string Answer string // 问题:(.*)答案:(.*) } - GroupMemberJoined struct { + GroupMemberIncrease struct { GroupEvent - Uin uint32 - Uid string - JoinType int32 + MemberUid string + InvitorUid string + JoinType uint32 } - GroupMemberQuit struct { + GroupMemberDecrease struct { GroupEvent - Uin uint32 - Uid string - ExitType int32 + MemberUid string OperatorUid string + ExitType uint32 } ) -func (gmq *GroupMemberQuit) IsKicked() bool { - return gmq.ExitType == 131 +type GroupInvite struct { + GroupUin uint32 + InvitorUid string } -func ParseMemberJoinRequest(event *status.MemberJoinRequest) *GroupMemberJoinRequest { +func (gmd *GroupMemberDecrease) IsKicked() bool { + return gmd.ExitType == 131 +} + +// ParseRequestJoinNotice 主动加群 +func ParseRequestJoinNotice(event *message.GroupJoin) *GroupMemberJoinRequest { return &GroupMemberJoinRequest{ GroupEvent: GroupEvent{ - GroupID: event.GroupID, + GroupUin: event.GroupUin, }, - Uid: event.Uid, - Answer: event.RequestField.Unwrap(), + TargetUid: event.TargetUid, + Answer: event.RequestField.Unwrap(), } } -func ParseMemberJoinRequestFromInvite(event *status.MemberInviteRequest) *GroupMemberJoinRequest { + +// ParseRequestInvitationNotice 邀请加群 +func ParseRequestInvitationNotice(event *message.GroupInvitation) *GroupMemberJoinRequest { if event.Cmd == 87 { inn := event.Info.Inner return &GroupMemberJoinRequest{ GroupEvent: GroupEvent{ - GroupID: inn.GroupID, + GroupUin: inn.GroupUin, }, - Uid: inn.Uid, + TargetUid: inn.TargetUid, InvitorUid: inn.InvitorUid, } } return nil } -func ParseMemberJoined(msg *message.PushMsg, event *status.MemberChanged) *GroupMemberJoined { - return &GroupMemberJoined{ +// ParseInviteNotice 被邀请加群 +func ParseInviteNotice(event *message.GroupInvite) *GroupInvite { + return &GroupInvite{ + GroupUin: event.GroupUin, + InvitorUid: event.InvitorUid, + } +} + +func ParseMemberIncreaseEvent(event *message.GroupChange) *GroupMemberIncrease { + return &GroupMemberIncrease{ GroupEvent: GroupEvent{ - GroupID: msg.Message.ResponseHead.FromUin, + GroupUin: event.GroupUin, }, - Uin: event.Uin, - Uid: event.Uid, - JoinType: event.JoinType.Unwrap(), + MemberUid: event.MemberUid, + InvitorUid: event.OperatorUid.Unwrap(), + JoinType: event.IncreaseType, } } -func ParseMemberQuit(msg *message.PushMsg, event *status.MemberChanged) *GroupMemberQuit { - return &GroupMemberQuit{ +func ParseMemberDecreaseEvent(event *message.GroupChange) *GroupMemberDecrease { + return &GroupMemberDecrease{ GroupEvent: GroupEvent{ - GroupID: msg.Message.ResponseHead.FromUin, + GroupUin: event.GroupUin, }, - Uin: event.Uin, - Uid: event.Uid, + MemberUid: event.MemberUid, OperatorUid: event.OperatorUid.Unwrap(), - ExitType: event.ExitType.Unwrap(), + ExitType: event.DecreaseType, } } -func ParseGroupMuteEvent(event *map[int]any) *GroupMuteMember { - // TODO Parse GroupMuteEvent - return nil +func ParseGroupRecallEvent(event *message.NotifyMessageBody) *GroupRecall { + info := event.Recall.RecallMessages[0] + result := GroupRecall{ + GroupEvent: GroupEvent{ + GroupUin: event.GroupUin, + }, + AuthorUid: info.AuthorUid, + OperatorUid: event.Recall.OperatorUid.Unwrap(), + Sequence: info.Sequence, + Time: info.Time, + Random: info.Random, + } + return &result +} + +func ParseGroupMuteEvent(event *message.GroupMute) *GroupMute { + return &GroupMute{ + GroupEvent: GroupEvent{ + GroupUin: event.GroupUin, + }, + OperatorUid: event.OperatorUid.Unwrap(), + TargetUid: event.Data.State.TargetUid.Unwrap(), + Duration: event.Data.State.Duration, + } } diff --git a/packets/pb/message/notify.pb.go b/packets/pb/message/notify.pb.go index 0b54afa6..1c07ca66 100644 --- a/packets/pb/message/notify.pb.go +++ b/packets/pb/message/notify.pb.go @@ -97,23 +97,24 @@ type InvitationInner struct { } type GroupInvite struct { - GroupInvite uint32 `protobuf:"varint,1,opt"` - Field2 uint32 `protobuf:"varint,2,opt"` // 1 - Field3 uint32 `protobuf:"varint,3,opt"` // 4 - Field4 uint32 `protobuf:"varint,4,opt"` // 0 - InvitorUid string `protobuf:"bytes,5,opt"` - Hashes []byte `protobuf:"bytes,6,opt"` + GroupUin uint32 `protobuf:"varint,1,opt"` + Field2 uint32 `protobuf:"varint,2,opt"` // 1 + Field3 uint32 `protobuf:"varint,3,opt"` // 4 + Field4 uint32 `protobuf:"varint,4,opt"` // 0 + InvitorUid string `protobuf:"bytes,5,opt"` + Hashes []byte `protobuf:"bytes,6,opt"` } type GroupJoin struct { - GroupUin uint32 `protobuf:"varint,1,opt"` - Field2 uint32 `protobuf:"varint,2,opt"` - TargetUid string `protobuf:"bytes,3,opt"` - Field4 uint32 `protobuf:"varint,4,opt"` - Field6 uint32 `protobuf:"varint,6,opt"` - Field7 string `protobuf:"bytes,7,opt"` - Field8 uint32 `protobuf:"varint,8,opt"` - Field9 []byte `protobuf:"bytes,9,opt"` + GroupUin uint32 `protobuf:"varint,1,opt"` + Field2 uint32 `protobuf:"varint,2,opt"` + TargetUid string `protobuf:"bytes,3,opt"` + Field4 uint32 `protobuf:"varint,4,opt"` + RequestField proto.Option[string] `protobuf:"bytes,5,opt"` + Field6 uint32 `protobuf:"varint,6,opt"` + Field7 string `protobuf:"bytes,7,opt"` + Field8 uint32 `protobuf:"varint,8,opt"` + Field9 []byte `protobuf:"bytes,9,opt"` } type GroupMute struct { diff --git a/packets/pb/message/notify.proto b/packets/pb/message/notify.proto index 2e903bfd..3150543a 100644 --- a/packets/pb/message/notify.proto +++ b/packets/pb/message/notify.proto @@ -84,7 +84,7 @@ message InvitationInner { } message GroupInvite { - uint32 GroupInvite = 1; + uint32 GroupUin = 1; uint32 Field2 = 2; // 1 uint32 Field3 = 3; // 4 uint32 Field4 = 4; // 0 @@ -97,6 +97,7 @@ message GroupJoin { uint32 Field2 = 2; string TargetUid = 3; uint32 Field4 = 4; + optional string RequestField = 5; uint32 Field6 = 6; string Field7 = 7; uint32 Field8 = 8; diff --git a/packets/pb/status/group.pb.go b/packets/pb/status/group.pb.go deleted file mode 100644 index 9905b847..00000000 --- a/packets/pb/status/group.pb.go +++ /dev/null @@ -1,43 +0,0 @@ -// Code generated by protoc-gen-golite. DO NOT EDIT. -// source: pb/status/group.proto - -package status - -import ( - proto "github.com/RomiChan/protobuf/proto" -) - -type MemberChanged struct { - Uin uint32 `protobuf:"varint,1,opt"` - Uid string `protobuf:"bytes,3,opt"` - ExitType proto.Option[int32] `protobuf:"varint,4,opt"` // 131 kick 130 exit - OperatorUid proto.Option[string] `protobuf:"bytes,5,opt"` - JoinType proto.Option[int32] `protobuf:"varint,6,opt"` // 6 scan qr - _ [0]func() -} - -type MemberJoinRequest struct { - GroupID uint32 `protobuf:"varint,1,opt"` - Uid string `protobuf:"bytes,3,opt"` - Src int32 `protobuf:"varint,4,opt"` - RequestField proto.Option[string] `protobuf:"bytes,5,opt"` - Field9 []byte `protobuf:"bytes,9,opt"` -} - -type InviteInner struct { - GroupID uint32 `protobuf:"varint,1,opt"` - Uid string `protobuf:"bytes,5,opt"` - InvitorUid string `protobuf:"bytes,6,opt"` - _ [0]func() -} - -type InviteInfo struct { - Inner *InviteInner `protobuf:"bytes,1,opt"` - _ [0]func() -} - -type MemberInviteRequest struct { - Cmd int32 `protobuf:"varint,1,opt"` - Info *InviteInfo `protobuf:"bytes,2,opt"` - _ [0]func() -} diff --git a/packets/pb/status/group.proto b/packets/pb/status/group.proto deleted file mode 100644 index fd50ea9b..00000000 --- a/packets/pb/status/group.proto +++ /dev/null @@ -1,38 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/LagrangeDev/LagrangeGo/packets/pb/status"; - -message MemberChanged { - uint32 Uin = 1; - string Uid = 3; - optional int32 ExitType = 4; // 131 kick 130 exit - optional string OperatorUid = 5; - optional int32 JoinType = 6; // 6 scan qr -} - -message MemberJoinRequest { - // JoinType: Direct(scan qrcode or search GroupID) - - uint32 GroupID = 1; - string Uid = 3; - int32 Src = 4; - optional string RequestField = 5; - optional bytes Field9 = 9; -} - -message InviteInner { - uint32 GroupID = 1; - string Uid = 5; - string InvitorUid = 6; -} - -message InviteInfo { - InviteInner Inner = 1; -} - -message MemberInviteRequest { - // JoinType: Direct(scan qrcode or search GroupID) - - int32 Cmd = 1; - InviteInfo Info = 2; -} \ No newline at end of file