diff --git a/client/c2c_processor.go b/client/c2c_processor.go index 8753a0a5..34966cb2 100644 --- a/client/c2c_processor.go +++ b/client/c2c_processor.go @@ -68,89 +68,6 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info *in } else { c.Debug("unknown msg type on c2c processor: %v", pMsg.Head.GetMsgType()) } - /* - switch pMsg.Head.GetMsgType() { - case 33: // 加群同步 - func() { - groupJoinLock.Lock() - defer groupJoinLock.Unlock() - group := c.FindGroupByUin(pMsg.Head.GetFromUin()) - if pMsg.Head.GetAuthUin() == c.Uin { - if group == nil && c.ReloadGroupList() == nil { - c.dispatchJoinGroupEvent(c.FindGroupByUin(pMsg.Head.GetFromUin())) - } - } else { - if group != nil && group.FindMember(pMsg.Head.GetAuthUin()) == nil { - mem, err := c.getMemberInfo(group.Code, pMsg.Head.GetAuthUin()) - if err != nil { - c.Debug("error to fetch new member info: %v", err) - return - } - group.Update(func(info *GroupInfo) { - info.Members = append(info.Members, mem) - }) - c.dispatchNewMemberEvent(&MemberJoinGroupEvent{ - Group: group, - Member: mem, - }) - } - } - }() - case 84, 87: - c.exceptAndDispatchGroupSysMsg() - case 141: // 临时会话 - if pMsg.Head.C2CTmpMsgHead == nil { - continue - } - group := c.FindGroupByUin(pMsg.Head.C2CTmpMsgHead.GetGroupUin()) - if group == nil { - continue - } - if pMsg.Head.GetFromUin() == c.Uin { - continue - } - c.dispatchTempMessage(c.parseTempMessage(pMsg)) - case 166, 208: // 好友消息 - if pMsg.Head.GetFromUin() == c.Uin { - for { - frdSeq := atomic.LoadInt32(&c.friendSeq) - if frdSeq < pMsg.Head.GetMsgSeq() { - if atomic.CompareAndSwapInt32(&c.friendSeq, frdSeq, pMsg.Head.GetMsgSeq()) { - break - } - } else { - break - } - } - } - if pMsg.Body.RichText == nil || pMsg.Body.RichText.Elems == nil { - continue - } - c.dispatchFriendMessage(c.parsePrivateMessage(pMsg)) - case 187: - _, pkt := c.buildSystemMsgNewFriendPacket() - _ = c.send(pkt) - case 529: - sub4 := msg.SubMsgType0X4Body{} - if err := proto.Unmarshal(pMsg.Body.MsgContent, &sub4); err != nil { - err = errors.Wrap(err, "unmarshal sub msg 0x4 error") - c.Error("unmarshal sub msg 0x4 error: %v", err) - continue - } - if sub4.NotOnlineFile != nil { - rsp, err := c.sendAndWait(c.buildOfflineFileDownloadRequestPacket(sub4.NotOnlineFile.FileUuid)) // offline_file.go - if err != nil { - continue - } - c.dispatchOfflineFileEvent(&OfflineFileEvent{ - FileName: string(sub4.NotOnlineFile.FileName), - FileSize: sub4.NotOnlineFile.GetFileSize(), - Sender: pMsg.Head.GetFromUin(), - DownloadUrl: rsp.(string), - }) - } - } - */ } } if delItems != nil { diff --git a/client/decoders.go b/client/decoders.go index caaa341b..b86c4e92 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -16,7 +16,6 @@ import ( "github.com/Mrs4s/MiraiGo/client/pb/cmd0x352" "github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff" "github.com/Mrs4s/MiraiGo/client/pb/msg" - "github.com/Mrs4s/MiraiGo/client/pb/notify" "github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/client/pb/profilecard" "github.com/Mrs4s/MiraiGo/client/pb/qweb" @@ -520,205 +519,6 @@ func decodeOffPicUpResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) }, nil } -// OnlinePush.ReqPush -func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) { - request := &jce.RequestPacket{} - request.ReadFrom(jce.NewJceReader(payload)) - data := &jce.RequestDataVersion2{} - data.ReadFrom(jce.NewJceReader(request.SBuffer)) - jr := jce.NewJceReader(data.Map["req"]["OnlinePushPack.SvcReqPushMsg"][1:]) - msgInfos := []jce.PushMessageInfo{} - uin := jr.ReadInt64(0) - jr.ReadSlice(&msgInfos, 2) - _ = c.send(c.buildDeleteOnlinePushPacket(uin, info.SequenceId, msgInfos)) - for _, m := range msgInfos { - k := fmt.Sprintf("%v%v%v", m.MsgSeq, m.MsgTime, m.MsgUid) - if _, ok := c.onlinePushCache.Get(k); ok { - continue - } - c.onlinePushCache.Add(k, "", time.Second*30) - if m.MsgType == 732 { - r := binary.NewReader(m.VMsg) - groupId := int64(uint32(r.ReadInt32())) - iType := r.ReadByte() - r.ReadByte() - switch iType { - case 0x0c: // 群内禁言 - operator := int64(uint32(r.ReadInt32())) - if operator == c.Uin { - continue - } - r.ReadBytes(6) - target := int64(uint32(r.ReadInt32())) - t := r.ReadInt32() - c.dispatchGroupMuteEvent(&GroupMuteEvent{ - GroupCode: groupId, - OperatorUin: operator, - TargetUin: target, - Time: t, - }) - case 0x10, 0x11, 0x14, 0x15: // group notify msg - r.ReadByte() - b := notify.NotifyMsgBody{} - _ = proto.Unmarshal(r.ReadAvailable(), &b) - if b.OptMsgRecall != nil { - for _, rm := range b.OptMsgRecall.RecalledMsgList { - if rm.MsgType == 2 { - continue - } - c.dispatchGroupMessageRecalledEvent(&GroupMessageRecalledEvent{ - GroupCode: groupId, - OperatorUin: b.OptMsgRecall.Uin, - AuthorUin: rm.AuthorUin, - MessageId: rm.Seq, - Time: rm.Time, - }) - } - } - if b.OptGeneralGrayTip != nil { - c.grayTipProcessor(groupId, b.OptGeneralGrayTip) - } - if b.OptMsgRedTips != nil { - if b.OptMsgRedTips.LuckyFlag == 1 { // 运气王提示 - c.dispatchGroupNotifyEvent(&GroupRedBagLuckyKingNotifyEvent{ - GroupCode: groupId, - Sender: int64(b.OptMsgRedTips.SenderUin), - LuckyKing: int64(b.OptMsgRedTips.LuckyUin), - }) - } - } - if b.QqGroupDigestMsg != nil { - var digest = b.QqGroupDigestMsg - c.dispatchGroupDigestEvent(&GroupDigestEvent{ - GroupCode: int64(digest.GroupCode), - MessageID: int32(digest.Seq), - InternalMessageID: int32(digest.Random), - OperationType: digest.OpType, - OperateTime: digest.OpTime, - SenderUin: int64(digest.Sender), - OperatorUin: int64(digest.DigestOper), - SenderNick: string(digest.SenderNick), - OperatorNick: string(digest.OperNick), - }) - } - } - } - if m.MsgType == 528 { - vr := jce.NewJceReader(m.VMsg) - subType := vr.ReadInt64(0) - probuf := vr.ReadAny(10).([]byte) - switch subType { - case 0x8A, 0x8B: - s8a := pb.Sub8A{} - if err := proto.Unmarshal(probuf, &s8a); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - for _, m := range s8a.MsgInfo { - if m.ToUin == c.Uin { - c.dispatchFriendMessageRecalledEvent(&FriendMessageRecalledEvent{ - FriendUin: m.FromUin, - MessageId: m.MsgSeq, - Time: m.MsgTime, - }) - } - } - case 0xB3: - b3 := pb.SubB3{} - if err := proto.Unmarshal(probuf, &b3); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - frd := &FriendInfo{ - Uin: b3.MsgAddFrdNotify.Uin, - Nickname: b3.MsgAddFrdNotify.Nick, - } - c.FriendList = append(c.FriendList, frd) - c.dispatchNewFriendEvent(&NewFriendEvent{Friend: frd}) - case 0xD4: - d4 := pb.SubD4{} - if err := proto.Unmarshal(probuf, &d4); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - groupLeaveLock.Lock() - if g := c.FindGroup(d4.Uin); g != nil { - if err := c.ReloadGroupList(); err != nil { - groupLeaveLock.Unlock() - return nil, err - } - c.dispatchLeaveGroupEvent(&GroupLeaveEvent{Group: g}) - } - groupLeaveLock.Unlock() - case 0x27: - s27 := pb.Sub27{} - if err := proto.Unmarshal(probuf, &s27); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - for _, m := range s27.ModInfos { - if m.DelFriend != nil { - frdUin := m.DelFriend.Uins[0] - if frd := c.FindFriend(int64(frdUin)); frd != nil { - if err := c.ReloadFriendList(); err != nil { - return nil, errors.Wrap(err, "failed to reload friend list") - } - } - } - } - case 290: - t := ¬ify.GeneralGrayTipInfo{} - _ = proto.Unmarshal(probuf, t) - var sender int64 - for _, templ := range t.MsgTemplParam { - if templ.Name == "uin_str1" { - sender, _ = strconv.ParseInt(templ.Value, 10, 64) - } - } - if sender == 0 { - return nil, nil - } - c.dispatchFriendNotifyEvent(&FriendPokeNotifyEvent{ - Sender: sender, - Receiver: c.Uin, - }) - case 0x44: - s44 := pb.Sub44{} - if err := proto.Unmarshal(probuf, &s44); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if s44.GroupSyncMsg != nil { - func() { - groupJoinLock.Lock() - defer groupJoinLock.Unlock() - if s44.GroupSyncMsg.GetGrpCode() != 0 { // member sync - c.Debug("syncing members.") - if group := c.FindGroup(s44.GroupSyncMsg.GetGrpCode()); group != nil { - group.Update(func(_ *GroupInfo) { - var lastJoinTime int64 = 0 - for _, m := range group.Members { - if lastJoinTime < m.JoinTime { - lastJoinTime = m.JoinTime - } - } - if newMem, err := c.GetGroupMembers(group); err == nil { - group.Members = newMem - for _, m := range newMem { - if lastJoinTime < m.JoinTime { - go c.dispatchNewMemberEvent(&MemberJoinGroupEvent{ - Group: group, - Member: m, - }) - } - } - } - }) - } - } - }() - } - } - } - } - return nil, nil -} - // OnlinePush.PbPushTransMsg func decodeOnlinePushTransPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { info := msg.TransMsgInfo{} diff --git a/client/online_push.go b/client/online_push.go new file mode 100644 index 00000000..2401751c --- /dev/null +++ b/client/online_push.go @@ -0,0 +1,243 @@ +package client + +import ( + "fmt" + "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/MiraiGo/binary/jce" + "github.com/Mrs4s/MiraiGo/client/pb" + "github.com/Mrs4s/MiraiGo/client/pb/notify" + "github.com/pkg/errors" + "google.golang.org/protobuf/proto" + "strconv" + "time" +) + +var msg0x210Decoders = map[int64]func(*QQClient, []byte) error{ + 0x8A: msgType0x210Sub8ADecoder, 0x8B: msgType0x210Sub8ADecoder, 0xB3: msgType0x210SubB3Decoder, + 0xD4: msgType0x210SubD4Decoder, 0x27: msgType0x210Sub27Decoder, 0x122: msgType0x210Sub122Decoder, + 0x123: msgType0x210Sub122Decoder, 0x44: msgType0x210Sub44Decoder, +} + +// OnlinePush.ReqPush +func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) { + request := &jce.RequestPacket{} + request.ReadFrom(jce.NewJceReader(payload)) + data := &jce.RequestDataVersion2{} + data.ReadFrom(jce.NewJceReader(request.SBuffer)) + jr := jce.NewJceReader(data.Map["req"]["OnlinePushPack.SvcReqPushMsg"][1:]) + msgInfos := []jce.PushMessageInfo{} + uin := jr.ReadInt64(0) + jr.ReadSlice(&msgInfos, 2) + _ = c.send(c.buildDeleteOnlinePushPacket(uin, info.SequenceId, msgInfos)) + for _, m := range msgInfos { + k := fmt.Sprintf("%v%v%v", m.MsgSeq, m.MsgTime, m.MsgUid) + if _, ok := c.onlinePushCache.Get(k); ok { + continue + } + c.onlinePushCache.Add(k, "", time.Second*30) + // 0x2dc + if m.MsgType == 732 { + r := binary.NewReader(m.VMsg) + groupId := int64(uint32(r.ReadInt32())) + iType := r.ReadByte() + r.ReadByte() + switch iType { + case 0x0c: // 群内禁言 + operator := int64(uint32(r.ReadInt32())) + if operator == c.Uin { + continue + } + r.ReadBytes(6) + target := int64(uint32(r.ReadInt32())) + t := r.ReadInt32() + c.dispatchGroupMuteEvent(&GroupMuteEvent{ + GroupCode: groupId, + OperatorUin: operator, + TargetUin: target, + Time: t, + }) + case 0x10, 0x11, 0x14, 0x15: // group notify msg + r.ReadByte() + b := notify.NotifyMsgBody{} + _ = proto.Unmarshal(r.ReadAvailable(), &b) + if b.OptMsgRecall != nil { + for _, rm := range b.OptMsgRecall.RecalledMsgList { + if rm.MsgType == 2 { + continue + } + c.dispatchGroupMessageRecalledEvent(&GroupMessageRecalledEvent{ + GroupCode: groupId, + OperatorUin: b.OptMsgRecall.Uin, + AuthorUin: rm.AuthorUin, + MessageId: rm.Seq, + Time: rm.Time, + }) + } + } + if b.OptGeneralGrayTip != nil { + c.grayTipProcessor(groupId, b.OptGeneralGrayTip) + } + if b.OptMsgRedTips != nil { + if b.OptMsgRedTips.LuckyFlag == 1 { // 运气王提示 + c.dispatchGroupNotifyEvent(&GroupRedBagLuckyKingNotifyEvent{ + GroupCode: groupId, + Sender: int64(b.OptMsgRedTips.SenderUin), + LuckyKing: int64(b.OptMsgRedTips.LuckyUin), + }) + } + } + if b.QqGroupDigestMsg != nil { + var digest = b.QqGroupDigestMsg + c.dispatchGroupDigestEvent(&GroupDigestEvent{ + GroupCode: int64(digest.GroupCode), + MessageID: int32(digest.Seq), + InternalMessageID: int32(digest.Random), + OperationType: digest.OpType, + OperateTime: digest.OpTime, + SenderUin: int64(digest.Sender), + OperatorUin: int64(digest.DigestOper), + SenderNick: string(digest.SenderNick), + OperatorNick: string(digest.OperNick), + }) + } + } + } + // 0x210 + if m.MsgType == 528 { + vr := jce.NewJceReader(m.VMsg) + subType := vr.ReadInt64(0) + protobuf := vr.ReadAny(10).([]byte) + if decoder, ok := msg0x210Decoders[subType]; ok { + if err := decoder(c, protobuf); err != nil { + return nil, errors.Wrap(err, "decode online push 0x210 error") + } + } else { + c.Debug("unknown online push 0x210 sub type 0x%v", strconv.FormatInt(subType, 16)) + } + } + } + return nil, nil +} + +func msgType0x210Sub8ADecoder(c *QQClient, protobuf []byte) error { + s8a := pb.Sub8A{} + if err := proto.Unmarshal(protobuf, &s8a); err != nil { + return errors.Wrap(err, "failed to unmarshal protobuf message") + } + for _, m := range s8a.MsgInfo { + if m.ToUin == c.Uin { + c.dispatchFriendMessageRecalledEvent(&FriendMessageRecalledEvent{ + FriendUin: m.FromUin, + MessageId: m.MsgSeq, + Time: m.MsgTime, + }) + } + } + return nil +} + +func msgType0x210SubB3Decoder(c *QQClient, protobuf []byte) error { + b3 := pb.SubB3{} + if err := proto.Unmarshal(protobuf, &b3); err != nil { + return errors.Wrap(err, "failed to unmarshal protobuf message") + } + frd := &FriendInfo{ + Uin: b3.MsgAddFrdNotify.Uin, + Nickname: b3.MsgAddFrdNotify.Nick, + } + c.FriendList = append(c.FriendList, frd) + c.dispatchNewFriendEvent(&NewFriendEvent{Friend: frd}) + return nil +} + +func msgType0x210SubD4Decoder(c *QQClient, protobuf []byte) error { + d4 := pb.SubD4{} + if err := proto.Unmarshal(protobuf, &d4); err != nil { + return errors.Wrap(err, "failed to unmarshal protobuf message") + } + groupLeaveLock.Lock() + if g := c.FindGroup(d4.Uin); g != nil { + if err := c.ReloadGroupList(); err != nil { + groupLeaveLock.Unlock() + return err + } + c.dispatchLeaveGroupEvent(&GroupLeaveEvent{Group: g}) + } + groupLeaveLock.Unlock() + return nil +} + +func msgType0x210Sub27Decoder(c *QQClient, protobuf []byte) error { + s27 := pb.Sub27{} + if err := proto.Unmarshal(protobuf, &s27); err != nil { + return errors.Wrap(err, "failed to unmarshal protobuf message") + } + for _, m := range s27.ModInfos { + if m.DelFriend != nil { + frdUin := m.DelFriend.Uins[0] + if frd := c.FindFriend(int64(frdUin)); frd != nil { + if err := c.ReloadFriendList(); err != nil { + return errors.Wrap(err, "failed to reload friend list") + } + } + } + } + return nil +} + +func msgType0x210Sub122Decoder(c *QQClient, protobuf []byte) error { + t := ¬ify.GeneralGrayTipInfo{} + _ = proto.Unmarshal(protobuf, t) + var sender int64 + for _, templ := range t.MsgTemplParam { + if templ.Name == "uin_str1" { + sender, _ = strconv.ParseInt(templ.Value, 10, 64) + } + } + if sender == 0 { + return nil + } + c.dispatchFriendNotifyEvent(&FriendPokeNotifyEvent{ + Sender: sender, + Receiver: c.Uin, + }) + return nil +} + +func msgType0x210Sub44Decoder(c *QQClient, protobuf []byte) error { + s44 := pb.Sub44{} + if err := proto.Unmarshal(protobuf, &s44); err != nil { + return errors.Wrap(err, "failed to unmarshal protobuf message") + } + if s44.GroupSyncMsg != nil { + func() { + groupJoinLock.Lock() + defer groupJoinLock.Unlock() + if s44.GroupSyncMsg.GetGrpCode() != 0 { // member sync + c.Debug("syncing members.") + if group := c.FindGroup(s44.GroupSyncMsg.GetGrpCode()); group != nil { + group.Update(func(_ *GroupInfo) { + var lastJoinTime int64 = 0 + for _, m := range group.Members { + if lastJoinTime < m.JoinTime { + lastJoinTime = m.JoinTime + } + } + if newMem, err := c.GetGroupMembers(group); err == nil { + group.Members = newMem + for _, m := range newMem { + if lastJoinTime < m.JoinTime { + go c.dispatchNewMemberEvent(&MemberJoinGroupEvent{ + Group: group, + Member: m, + }) + } + } + } + }) + } + } + }() + } + return nil +} diff --git a/client/sync.go b/client/sync.go index dba3c2df..11016b99 100644 --- a/client/sync.go +++ b/client/sync.go @@ -111,6 +111,10 @@ func (c *QQClient) MarkGroupMessageReaded(groupCode, seq int64) { _, _ = c.sendAndWait(c.buildGroupMsgReadedPacket(groupCode, seq)) } +func (c *QQClient) MarkPrivateMessageReaded(uin, time int64) { + _, _ = c.sendAndWait(c.buildPrivateMsgReadedPacket(uin, time)) +} + // StatSvc.GetDevLoginInfo func (c *QQClient) buildDeviceListRequestPacket() (uint16, []byte) { seq := c.nextSeq() @@ -272,6 +276,18 @@ func (c *QQClient) buildGroupMsgReadedPacket(groupCode, msgSeq int64) (uint16, [ return seq, packet } +func (c *QQClient) buildPrivateMsgReadedPacket(uin, time int64) (uint16, []byte) { + seq := c.nextSeq() + req, _ := proto.Marshal(&msg.PbMsgReadedReportReq{C2CReadReport: &msg.PbC2CReadedReportReq{PairInfo: []*msg.UinPairReadInfo{ + { + PeerUin: proto.Uint64(uint64(uin)), + LastReadTime: proto.Uint32(uint32(time)), + }, + }, SyncCookie: c.syncCookie}}) + packet := packets.BuildUniPacket(c.Uin, seq, "PbMessageSvc.PbMsgReadedReport", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, req) + return seq, packet +} + // StatSvc.GetDevLoginInfo func decodeDevListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{}