diff --git a/client/c2c_processor.go b/client/c2c_processor.go index ce69979b..87ac410e 100644 --- a/client/c2c_processor.go +++ b/client/c2c_processor.go @@ -5,25 +5,31 @@ import ( "sync/atomic" "time" + "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/pkg/errors" "google.golang.org/protobuf/proto" ) -var c2cDecoders = map[int32]func(*QQClient, *msg.Message){ +var c2cDecoders = map[int32]func(*QQClient, *msg.Message, *incomingPacketInfo){ 33: troopAddMemberBroadcastDecoder, 35: troopSystemMessageDecoder, 36: troopSystemMessageDecoder, 37: troopSystemMessageDecoder, 45: troopSystemMessageDecoder, 46: troopSystemMessageDecoder, 84: troopSystemMessageDecoder, 85: troopSystemMessageDecoder, 86: troopSystemMessageDecoder, 87: troopSystemMessageDecoder, 140: tempSessionDecoder, 141: tempSessionDecoder, - 166: privateMessageDecoder, 208: privateMessageDecoder, + 9: privateMessageDecoder, 10: privateMessageDecoder, 31: privateMessageDecoder, + 79: privateMessageDecoder, 97: privateMessageDecoder, 120: privateMessageDecoder, + 132: privateMessageDecoder, 133: privateMessageDecoder, 166: privateMessageDecoder, + 167: privateMessageDecoder, + 208: privatePttDecoder, 187: systemMessageDecoder, 188: systemMessageDecoder, 189: systemMessageDecoder, 190: systemMessageDecoder, 191: systemMessageDecoder, 529: msgType0x211Decoder, } -func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse) { +func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info *incomingPacketInfo) { c.syncCookie = rsp.SyncCookie c.pubAccountCookie = rsp.PubAccountCookie c.msgCtrlBuf = rsp.MsgCtrlBuf @@ -50,11 +56,17 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse) { } strKey := fmt.Sprintf("%d%d%d%d", pMsg.Head.GetFromUin(), pMsg.Head.GetToUin(), pMsg.Head.GetMsgSeq(), pMsg.Head.GetMsgUid()) if _, ok := c.msgSvcCache.GetAndUpdate(strKey, time.Minute*5); ok { + c.Debug("c2c msg %v already exists in cache. skip.", pMsg.Head.GetMsgUid()) continue } c.msgSvcCache.Add(strKey, "", time.Minute*5) + if info.Params.bool("init") { + continue + } if decoder, ok := c2cDecoders[pMsg.Head.GetMsgType()]; ok { - decoder(c, pMsg) + decoder(c, pMsg, info) + } else { + c.Debug("unknown msg type on c2c processor: %v", pMsg.Head.GetMsgType()) } /* switch pMsg.Head.GetMsgType() { @@ -144,44 +156,63 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse) { _, _ = c.sendAndWait(c.buildDeleteMessageRequestPacket(delItems)) if rsp.GetSyncFlag() != msg.SyncFlag_STOP { c.Debug("continue sync with flag: %v", rsp.SyncFlag.String()) - _, _ = c.sendAndWait(c.buildGetMessageRequestPacket(rsp.GetSyncFlag(), time.Now().Unix())) + seq, pkt := c.buildGetMessageRequestPacket(rsp.GetSyncFlag(), time.Now().Unix()) + _, _ = c.sendAndWait(seq, pkt, info.Params) } } -func privateMessageDecoder(c *QQClient, pMsg *msg.Message) { - 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()) { +func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) { + switch pMsg.Head.GetC2CCmd() { + case 11, 175: // friend msg + 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 } - } else { - break } } + if pMsg.Body.RichText == nil || pMsg.Body.RichText.Elems == nil { + return + } + c.dispatchFriendMessage(c.parsePrivateMessage(pMsg)) + default: + c.Debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.GetC2CCmd()) } - if pMsg.Body.RichText == nil || pMsg.Body.RichText.Elems == nil { +} + +func privatePttDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) { + if pMsg.Body == nil || pMsg.Body.RichText == nil || pMsg.Body.RichText.Ptt == nil { return } + if len(pMsg.Body.RichText.Ptt.Reserve) != 0 { + // m := binary.NewReader(pMsg.Body.RichText.Ptt.Reserve[1:]).ReadTlvMap(1) + // T3 -> timestamp T8 -> voiceType T9 -> voiceLength T10 -> PbReserveStruct + } c.dispatchFriendMessage(c.parsePrivateMessage(pMsg)) } -func tempSessionDecoder(c *QQClient, pMsg *msg.Message) { - if pMsg.Head.C2CTmpMsgHead == nil { +func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) { + if pMsg.Head.C2CTmpMsgHead == nil || pMsg.Body == nil { return } - group := c.FindGroupByUin(pMsg.Head.C2CTmpMsgHead.GetGroupUin()) - if group == nil { - return + if pMsg.Head.GetMsgType() == 529 && pMsg.Head.GetC2CCmd() == 6 { + group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GetGroupCode()) + if group == nil { + return + } + if pMsg.Head.GetFromUin() == c.Uin { + return + } + c.dispatchTempMessage(c.parseTempMessage(pMsg)) } - if pMsg.Head.GetFromUin() == c.Uin { - return - } - c.dispatchTempMessage(c.parseTempMessage(pMsg)) } -func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message) { +func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) { groupJoinLock.Lock() defer groupJoinLock.Unlock() group := c.FindGroupByUin(pMsg.Head.GetFromUin()) @@ -207,16 +238,27 @@ func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message) { } } -func systemMessageDecoder(c *QQClient, pMsg *msg.Message) { +func systemMessageDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) { _, pkt := c.buildSystemMsgNewFriendPacket() _ = c.send(pkt) } -func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message) { - c.exceptAndDispatchGroupSysMsg() +func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message, info *incomingPacketInfo) { + if !info.Params.bool("used_reg_proxy") && pMsg.Head.GetMsgType() != 85 && pMsg.Head.GetMsgType() != 36 { + c.exceptAndDispatchGroupSysMsg() + } + if len(pMsg.Body.GetMsgContent()) == 0 { + return + } + reader := binary.NewReader(pMsg.GetBody().GetMsgContent()) + groupCode := uint32(reader.ReadInt32()) + if info := c.FindGroup(int64(groupCode)); info != nil && pMsg.Head.GetGroupName() != "" && info.Name != pMsg.Head.GetGroupName() { + c.Debug("group %v name updated. %v -> %v", groupCode, info.Name, pMsg.Head.GetGroupName()) + info.Name = pMsg.Head.GetGroupName() + } } -func msgType0x211Decoder(c *QQClient, pMsg *msg.Message) { +func msgType0x211Decoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) { sub4 := msg.SubMsgType0X4Body{} if err := proto.Unmarshal(pMsg.Body.MsgContent, &sub4); err != nil { err = errors.Wrap(err, "unmarshal sub msg 0x4 error") diff --git a/client/client.go b/client/client.go index 9f770fd9..010e26e7 100644 --- a/client/client.go +++ b/client/client.go @@ -28,7 +28,7 @@ import ( var json = jsoniter.ConfigFastest -//go:generate go run github.com/a8m/syncmap -o "handler_map_gen.go" -pkg client -name HandlerMap "map[uint16]func(i interface{}, err error)" +//go:generate go run github.com/a8m/syncmap -o "handler_map_gen.go" -pkg client -name HandlerMap "map[uint16]*handlerInfo" type QQClient struct { Uin int64 @@ -121,32 +121,36 @@ type loginSigInfo struct { pt4TokenMap map[string][]byte } -var decoders = map[string]func(*QQClient, uint16, []byte) (interface{}, error){ - "wtlogin.login": decodeLoginResponse, - "wtlogin.exchange_emp": decodeExchangeEmpResponse, - "StatSvc.register": decodeClientRegisterResponse, - "StatSvc.ReqMSFOffline": decodeMSFOfflinePacket, - "MessageSvc.PushNotify": decodeSvcNotify, - "OnlinePush.ReqPush": decodeOnlinePushReqPacket, - "OnlinePush.PbPushTransMsg": decodeOnlinePushTransPacket, - "ConfigPushSvc.PushReq": decodePushReqPacket, - "MessageSvc.PbGetMsg": decodeMessageSvcPacket, - "MessageSvc.PushForceOffline": decodeForceOfflinePacket, - "PbMessageSvc.PbMsgWithDraw": decodeMsgWithDrawResponse, - "friendlist.getFriendGroupList": decodeFriendGroupListResponse, - "friendlist.GetTroopListReqV2": decodeGroupListResponse, - "friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse, - "group_member_card.get_group_member_card_info": decodeGroupMemberInfoResponse, - "PttStore.GroupPttUp": decodeGroupPttStoreResponse, - "LongConn.OffPicUp": decodeOffPicUpResponse, - "ProfileService.Pb.ReqSystemMsgNew.Group": decodeSystemMsgGroupPacket, - "ProfileService.Pb.ReqSystemMsgNew.Friend": decodeSystemMsgFriendPacket, - "OidbSvc.0xe07_0": decodeImageOcrResponse, - "OidbSvc.0xd79": decodeWordSegmentation, - "OidbSvc.0x990": decodeTranslateResponse, - "SummaryCard.ReqSummaryCard": decodeSummaryCardResponse, - "LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse, - "PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500": decodePrivatePttStoreResponse, +type handlerInfo struct { + fun func(i interface{}, err error) + params requestParams +} + +var decoders = map[string]func(*QQClient, *incomingPacketInfo, []byte) (interface{}, error){ + "wtlogin.login": decodeLoginResponse, + "wtlogin.exchange_emp": decodeExchangeEmpResponse, + "StatSvc.register": decodeClientRegisterResponse, + "StatSvc.ReqMSFOffline": decodeMSFOfflinePacket, + "MessageSvc.PushNotify": decodeSvcNotify, + "OnlinePush.ReqPush": decodeOnlinePushReqPacket, + "OnlinePush.PbPushTransMsg": decodeOnlinePushTransPacket, + "ConfigPushSvc.PushReq": decodePushReqPacket, + "MessageSvc.PbGetMsg": decodeMessageSvcPacket, + "MessageSvc.PushForceOffline": decodeForceOfflinePacket, + "PbMessageSvc.PbMsgWithDraw": decodeMsgWithDrawResponse, + "friendlist.getFriendGroupList": decodeFriendGroupListResponse, + "friendlist.GetTroopListReqV2": decodeGroupListResponse, + "friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse, + "group_member_card.get_group_member_card_info": decodeGroupMemberInfoResponse, + "PttStore.GroupPttUp": decodeGroupPttStoreResponse, + "LongConn.OffPicUp": decodeOffPicUpResponse, + "ProfileService.Pb.ReqSystemMsgNew.Group": decodeSystemMsgGroupPacket, + "ProfileService.Pb.ReqSystemMsgNew.Friend": decodeSystemMsgFriendPacket, + "OidbSvc.0xe07_0": decodeImageOcrResponse, + "OidbSvc.0xd79": decodeWordSegmentation, + "OidbSvc.0x990": decodeTranslateResponse, + "SummaryCard.ReqSummaryCard": decodeSummaryCardResponse, + "LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse, } func init() { @@ -304,6 +308,9 @@ func (c *QQClient) RequestSMS() bool { } func (c *QQClient) init() { + if len(c.g) == 0 { + c.Warning("device lock is disable. http api may fail.") + } c.Online = true _ = c.registerClient() c.groupSysMsgCache, _ = c.GetGroupSystemMessages() @@ -311,7 +318,8 @@ func (c *QQClient) init() { go c.doHeartbeat() } _ = c.RefreshStatus() - _, _ = c.SyncSessions() + seq, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix()) + _, _ = c.sendAndWait(seq, pkt, requestParams{"used_reg_proxy": true, "init": true}) c.stat.once.Do(func() { c.OnGroupMessage(func(_ *QQClient, _ *message.GroupMessage) { c.stat.MessageReceived++ @@ -629,7 +637,7 @@ func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) { } func (c *QQClient) getSKey() string { - if c.sigInfo.sKeyExpiredTime < time.Now().Unix() { + if c.sigInfo.sKeyExpiredTime < time.Now().Unix() && len(c.g) > 0 { c.Debug("skey expired. refresh...") _, _ = c.sendAndWait(c.buildRequestTgtgtNopicsigPacket()) } @@ -793,7 +801,7 @@ func (c *QQClient) send(pkt []byte) error { return errors.Wrap(err, "Packet failed to send") } -func (c *QQClient) sendAndWait(seq uint16, pkt []byte) (interface{}, error) { +func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...requestParams) (interface{}, error) { type T struct { Response interface{} Error error @@ -806,12 +814,20 @@ func (c *QQClient) sendAndWait(seq uint16, pkt []byte) (interface{}, error) { ch := make(chan T) defer close(ch) - c.handlers.Store(seq, func(i interface{}, err error) { + + p := func() requestParams { + if len(params) == 0 { + return nil + } + return params[0] + }() + + c.handlers.Store(seq, &handlerInfo{fun: func(i interface{}, err error) { ch <- T{ Response: i, Error: err, } - }) + }, params: p}) retry := 0 for true { @@ -913,18 +929,28 @@ func (c *QQClient) netLoop() { if decoder, ok := decoders[pkt.CommandName]; ok { // found predefined decoder - rsp, err := decoder(c, pkt.SequenceId, payload) + info, ok := c.handlers.LoadAndDelete(pkt.SequenceId) + rsp, err := decoder(c, &incomingPacketInfo{ + SequenceId: pkt.SequenceId, + CommandName: pkt.CommandName, + Params: func() requestParams { + if !ok { + return nil + } + return info.params + }(), + }, payload) if err != nil { c.Debug("decode pkt %v error: %+v", pkt.CommandName, err) } - if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok { - f(rsp, err) + if ok { + info.fun(rsp, err) } else if f, ok := c.waiters.Load(pkt.CommandName); ok { // 在不存在handler的情况下触发wait f.(func(interface{}, error))(rsp, err) } } else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok { // does not need decoder - f(nil, nil) + f.fun(nil, nil) } else { c.Debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId) } diff --git a/client/decoders.go b/client/decoders.go index 26efd817..caaa341b 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -4,7 +4,6 @@ import ( "crypto/md5" "encoding/hex" "fmt" - log "github.com/sirupsen/logrus" "net" "strconv" "strings" @@ -33,7 +32,7 @@ var ( ) // wtlogin.login -func decodeLoginResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeLoginResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { reader := binary.NewReader(payload) reader.ReadUInt16() // sub command t := reader.ReadByte() @@ -182,7 +181,7 @@ func decodeLoginResponse(c *QQClient, _ uint16, payload []byte) (interface{}, er } // StatSvc.register -func decodeClientRegisterResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeClientRegisterResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -199,7 +198,7 @@ func decodeClientRegisterResponse(c *QQClient, _ uint16, payload []byte) (interf } // wtlogin.exchange_emp -func decodeExchangeEmpResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeExchangeEmpResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { reader := binary.NewReader(payload) cmd := reader.ReadUInt16() t := reader.ReadByte() @@ -216,7 +215,7 @@ func decodeExchangeEmpResponse(c *QQClient, _ uint16, payload []byte) (interface } // ConfigPushSvc.PushReq -func decodePushReqPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodePushReqPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -289,24 +288,24 @@ func decodePushReqPacket(c *QQClient, _ uint16, payload []byte) (interface{}, er } // MessageSvc.PbGetMsg -func decodeMessageSvcPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeMessageSvcPacket(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := msg.GetMessageResponse{} err := proto.Unmarshal(payload, &rsp) if err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") } - c.c2cMessageSyncProcessor(&rsp) + c.c2cMessageSyncProcessor(&rsp, info) return nil, nil } // MessageSvc.PushNotify -func decodeSvcNotify(c *QQClient, _ uint16, _ []byte) (interface{}, error) { +func decodeSvcNotify(c *QQClient, _ *incomingPacketInfo, _ []byte) (interface{}, error) { _, err := c.sendAndWait(c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix())) return nil, err } // SummaryCard.ReqSummaryCard -func decodeSummaryCardResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeSummaryCardResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -355,7 +354,7 @@ func decodeSummaryCardResponse(_ *QQClient, _ uint16, payload []byte) (interface } // friendlist.getFriendGroupList -func decodeFriendGroupListResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeFriendGroupListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion3{} @@ -381,7 +380,7 @@ func decodeFriendGroupListResponse(_ *QQClient, _ uint16, payload []byte) (inter } // friendlist.GetTroopListReqV2 -func decodeGroupListResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupListResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion3{} @@ -415,7 +414,7 @@ func decodeGroupListResponse(c *QQClient, _ uint16, payload []byte) (interface{} } // friendlist.GetTroopMemberListReq -func decodeGroupMemberListResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupMemberListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion3{} @@ -451,7 +450,7 @@ func decodeGroupMemberListResponse(_ *QQClient, _ uint16, payload []byte) (inter } // group_member_card.get_group_member_card_info -func decodeGroupMemberInfoResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupMemberInfoResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := pb.GroupMemberRspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -484,7 +483,7 @@ func decodeGroupMemberInfoResponse(c *QQClient, _ uint16, payload []byte) (inter } // LongConn.OffPicUp -func decodeOffPicUpResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeOffPicUpResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := cmd0x352.RspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -522,7 +521,7 @@ func decodeOffPicUpResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, } // OnlinePush.ReqPush -func decodeOnlinePushReqPacket(c *QQClient, seq uint16, payload []byte) (interface{}, error) { +func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -531,7 +530,7 @@ func decodeOnlinePushReqPacket(c *QQClient, seq uint16, payload []byte) (interfa msgInfos := []jce.PushMessageInfo{} uin := jr.ReadInt64(0) jr.ReadSlice(&msgInfos, 2) - _ = c.send(c.buildDeleteOnlinePushPacket(uin, seq, msgInfos)) + _ = 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 { @@ -657,7 +656,6 @@ func decodeOnlinePushReqPacket(c *QQClient, seq uint16, payload []byte) (interfa if m.DelFriend != nil { frdUin := m.DelFriend.Uins[0] if frd := c.FindFriend(int64(frdUin)); frd != nil { - log.Infof("好友被删除: %v(%v)", frd.Nickname, frd.Uin) if err := c.ReloadFriendList(); err != nil { return nil, errors.Wrap(err, "failed to reload friend list") } @@ -722,7 +720,7 @@ func decodeOnlinePushReqPacket(c *QQClient, seq uint16, payload []byte) (interfa } // OnlinePush.PbPushTransMsg -func decodeOnlinePushTransPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeOnlinePushTransPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { info := msg.TransMsgInfo{} err := proto.Unmarshal(payload, &info) if err != nil { @@ -829,7 +827,7 @@ func decodeOnlinePushTransPacket(c *QQClient, _ uint16, payload []byte) (interfa } // ProfileService.Pb.ReqSystemMsgNew.Friend -func decodeSystemMsgFriendPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeSystemMsgFriendPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := structmsg.RspSystemMsgNew{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -851,7 +849,7 @@ func decodeSystemMsgFriendPacket(c *QQClient, _ uint16, payload []byte) (interfa } // MessageSvc.PushForceOffline -func decodeForceOfflinePacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeForceOfflinePacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -865,7 +863,7 @@ func decodeForceOfflinePacket(c *QQClient, _ uint16, payload []byte) (interface{ } // StatSvc.ReqMSFOffline -func decodeMSFOfflinePacket(c *QQClient, _ uint16, _ []byte) (interface{}, error) { +func decodeMSFOfflinePacket(c *QQClient, _ *incomingPacketInfo, _ []byte) (interface{}, error) { c.lastLostMsg = "服务器端强制下线." c.NetLooping = false c.Online = false @@ -873,7 +871,7 @@ func decodeMSFOfflinePacket(c *QQClient, _ uint16, _ []byte) (interface{}, error } // OidbSvc.0xd79 -func decodeWordSegmentation(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeWordSegmentation(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := &oidb.D79RspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { @@ -889,7 +887,7 @@ func decodeWordSegmentation(_ *QQClient, _ uint16, payload []byte) (interface{}, } // OidbSvc.0xe07_0 -func decodeImageOcrResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeImageOcrResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := oidb.DE07RspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { @@ -926,7 +924,7 @@ func decodeImageOcrResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, } // LightAppSvc.mini_app_info.GetAppInfoById -func decodeAppInfoResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := qweb.QWebRsp{} rsp := qweb.GetAppInfoByIdRsp{} if err := proto.Unmarshal(payload, &pkg); err != nil { @@ -941,6 +939,6 @@ func decodeAppInfoResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, return rsp.AppInfo, nil } -func ignoreDecoder(_ *QQClient, _ uint16, _ []byte) (interface{}, error) { +func ignoreDecoder(_ *QQClient, _ *incomingPacketInfo, _ []byte) (interface{}, error) { return nil, nil } diff --git a/client/global.go b/client/global.go index 7f83313a..89e66ef4 100644 --- a/client/global.go +++ b/client/global.go @@ -24,94 +24,104 @@ import ( "google.golang.org/protobuf/proto" ) -type DeviceInfo struct { - Display []byte - Product []byte - Device []byte - Board []byte - Brand []byte - Model []byte - Bootloader []byte - FingerPrint []byte - BootId []byte - ProcVersion []byte - BaseBand []byte - SimInfo []byte - OSType []byte - MacAddress []byte - IpAddress []byte - WifiBSSID []byte - WifiSSID []byte - IMSIMd5 []byte - IMEI string - AndroidId []byte - APN []byte - VendorName []byte - VendorOSName []byte - Guid []byte - TgtgtKey []byte - Protocol ClientProtocol - Version *Version -} +type ( + DeviceInfo struct { + Display []byte + Product []byte + Device []byte + Board []byte + Brand []byte + Model []byte + Bootloader []byte + FingerPrint []byte + BootId []byte + ProcVersion []byte + BaseBand []byte + SimInfo []byte + OSType []byte + MacAddress []byte + IpAddress []byte + WifiBSSID []byte + WifiSSID []byte + IMSIMd5 []byte + IMEI string + AndroidId []byte + APN []byte + VendorName []byte + VendorOSName []byte + Guid []byte + TgtgtKey []byte + Protocol ClientProtocol + Version *Version + } -type Version struct { - Incremental []byte - Release []byte - CodeName []byte - Sdk uint32 -} + Version struct { + Incremental []byte + Release []byte + CodeName []byte + Sdk uint32 + } -type DeviceInfoFile struct { - Display string `json:"display"` - Product string `json:"product"` - Device string `json:"device"` - Board string `json:"board"` - Model string `json:"model"` - FingerPrint string `json:"finger_print"` - BootId string `json:"boot_id"` - ProcVersion string `json:"proc_version"` - Protocol int `json:"protocol"` // 0: Pad 1: Phone 2: Watch - IMEI string `json:"imei"` - Brand string `json:"brand"` - Bootloader string `json:"bootloader"` - BaseBand string `json:"base_band"` - Version *VersionFile `json:"version"` - SimInfo string `json:"sim_info"` - OsType string `json:"os_type"` - MacAddress string `json:"mac_address"` - IpAddress []int32 `json:"ip_address"` - WifiBSSID string `json:"wifi_bssid"` - WifiSSID string `json:"wifi_ssid"` - ImsiMd5 string `json:"imsi_md5"` - AndroidId string `json:"android_id"` - Apn string `json:"apn"` - VendorName string `json:"vendor_name"` - VendorOSName string `json:"vendor_os_name"` -} + DeviceInfoFile struct { + Display string `json:"display"` + Product string `json:"product"` + Device string `json:"device"` + Board string `json:"board"` + Model string `json:"model"` + FingerPrint string `json:"finger_print"` + BootId string `json:"boot_id"` + ProcVersion string `json:"proc_version"` + Protocol int `json:"protocol"` // 0: Pad 1: Phone 2: Watch + IMEI string `json:"imei"` + Brand string `json:"brand"` + Bootloader string `json:"bootloader"` + BaseBand string `json:"base_band"` + Version *VersionFile `json:"version"` + SimInfo string `json:"sim_info"` + OsType string `json:"os_type"` + MacAddress string `json:"mac_address"` + IpAddress []int32 `json:"ip_address"` + WifiBSSID string `json:"wifi_bssid"` + WifiSSID string `json:"wifi_ssid"` + ImsiMd5 string `json:"imsi_md5"` + AndroidId string `json:"android_id"` + Apn string `json:"apn"` + VendorName string `json:"vendor_name"` + VendorOSName string `json:"vendor_os_name"` + } -type VersionFile struct { - Incremental string `json:"incremental"` - Release string `json:"release"` - Codename string `json:"codename"` - Sdk uint32 `json:"sdk"` -} + VersionFile struct { + Incremental string `json:"incremental"` + Release string `json:"release"` + Codename string `json:"codename"` + Sdk uint32 `json:"sdk"` + } -type groupMessageBuilder struct { - MessageSlices []*msg.Message -} + groupMessageBuilder struct { + MessageSlices []*msg.Message + } -type versionInfo struct { - ApkSign []byte - ApkId string - SortVersionName string - SdkVersion string - AppId uint32 - BuildTime uint32 - SSOVersion uint32 - MiscBitmap uint32 - SubSigmap uint32 - MainSigMap uint32 -} + versionInfo struct { + ApkSign []byte + ApkId string + SortVersionName string + SdkVersion string + AppId uint32 + BuildTime uint32 + SSOVersion uint32 + MiscBitmap uint32 + SubSigmap uint32 + MainSigMap uint32 + } + + incomingPacketInfo struct { + CommandName string + SequenceId uint16 + Params requestParams + } + + requestParams map[string]interface{} +) // default var SystemDeviceInfo = &DeviceInfo{ @@ -577,6 +587,17 @@ func genLongTemplate(resId, brief string, ts int64) *message.ServiceElement { } } +func (p requestParams) bool(k string) bool { + if p == nil { + return false + } + i, ok := p[k] + if !ok { + return false + } + return i.(bool) +} + func (c *QQClient) packOIDBPackage(cmd, serviceType int32, body []byte) []byte { pkg := &oidb.OIDBSSOPkg{ Command: cmd, diff --git a/client/group_file.go b/client/group_file.go index 04f0207f..d0aac56e 100644 --- a/client/group_file.go +++ b/client/group_file.go @@ -8,6 +8,7 @@ import ( "math/rand" "os" "runtime/debug" + "sync" "google.golang.org/protobuf/proto" @@ -54,6 +55,8 @@ type ( } ) +var fileSingleFlight = sync.Map{} + func init() { decoders["OidbSvc.0x6d8_1"] = decodeOIDB6d81Response decoders["OidbSvc.0x6d6_0"] = decodeOIDB6d60Response @@ -156,11 +159,22 @@ func (fs *GroupFileSystem) GetFilesByFolder(folderId string) ([]*GroupFile, []*G } func (fs *GroupFileSystem) UploadFile(p, name, folderId string) error { + // 同文件等待其他线程上传 + if wg, ok := fileSingleFlight.Load(p); ok { + wg.(*sync.WaitGroup).Wait() + } else { + wg := &sync.WaitGroup{} + wg.Add(1) + fileSingleFlight.Store(p, wg) + defer wg.Done() + defer fileSingleFlight.Delete(p) + } + file, err := os.OpenFile(p, os.O_RDONLY, 0666) if err != nil { return errors.Wrap(err, "open file error") } - defer file.Close() + defer func() { _ = file.Close() }() md5Hash, size := utils.ComputeMd5AndLength(file) _, _ = file.Seek(0, io.SeekStart) sha1H := sha1.New() @@ -387,7 +401,7 @@ func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId return seq, packet } -func decodeOIDB6d81Response(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeOIDB6d81Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := oidb.D6D8RspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { @@ -400,7 +414,7 @@ func decodeOIDB6d81Response(c *QQClient, _ uint16, payload []byte) (interface{}, } // OidbSvc.0x6d6_2 -func decodeOIDB6d62Response(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeOIDB6d62Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := oidb.D6D6RspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { @@ -417,7 +431,7 @@ func decodeOIDB6d62Response(_ *QQClient, _ uint16, payload []byte) (interface{}, return fmt.Sprintf("http://%s/ftn_handler/%s/", ip, url), nil } -func decodeOIDB6d63Response(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeOIDB6d63Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := oidb.D6D6RspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { @@ -432,7 +446,7 @@ func decodeOIDB6d63Response(_ *QQClient, _ uint16, payload []byte) (interface{}, return rsp.DeleteFileRsp.ClientWording, nil } -func decodeOIDB6d60Response(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeOIDB6d60Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := oidb.D6D6RspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { diff --git a/client/group_info.go b/client/group_info.go index 0655748c..8109e99e 100644 --- a/client/group_info.go +++ b/client/group_info.go @@ -183,7 +183,7 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) { } // SummaryCard.ReqSearch -func decodeGroupSearchResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupSearchResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -219,7 +219,7 @@ func decodeGroupSearchResponse(_ *QQClient, _ uint16, payload []byte) (interface } // OidbSvc.0x88d_0 -func decodeGroupInfoResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupInfoResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := oidb.D88DRspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { diff --git a/client/group_msg.go b/client/group_msg.go index a3951b2b..baf58b8a 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -304,7 +304,7 @@ func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byt } // OnlinePush.PbPushGroupMsg -func decodeGroupMessagePacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupMessagePacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkt := msg.PushMessagePacket{} err := proto.Unmarshal(payload, &pkt) if err != nil { @@ -345,7 +345,7 @@ func decodeGroupMessagePacket(c *QQClient, _ uint16, payload []byte) (interface{ return nil, nil } -func decodeMsgSendResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeMsgSendResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := msg.SendMessageResponse{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -356,7 +356,7 @@ func decodeMsgSendResponse(c *QQClient, _ uint16, payload []byte) (interface{}, return nil, nil } -func decodeGetGroupMsgResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGetGroupMsgResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := msg.GetGroupMsgResp{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -377,7 +377,7 @@ func decodeGetGroupMsgResponse(c *QQClient, _ uint16, payload []byte) (interface return ret, nil } -func decodeAtAllRemainResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeAtAllRemainResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := oidb.D8A7RspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { @@ -573,7 +573,7 @@ func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand } // OidbSvc.0xeac_1/2 -func decodeEssenceMsgResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeEssenceMsgResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := &oidb.EACRspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { diff --git a/client/handler_map_gen.go b/client/handler_map_gen.go index 2d9f5b8c..d88a52fb 100644 --- a/client/handler_map_gen.go +++ b/client/handler_map_gen.go @@ -70,9 +70,7 @@ type readOnlyHandlerMap struct { // expunged is an arbitrary pointer that marks entries which have been deleted // from the dirty map. -var expungedHandlerMap = unsafe.Pointer(new(func(i interface{}, err error, - -))) +var expungedHandlerMap = unsafe.Pointer(new(*handlerInfo)) // An entry is a slot in the map corresponding to a particular key. type entryHandlerMap struct { @@ -97,18 +95,14 @@ type entryHandlerMap struct { p unsafe.Pointer // *interface{} } -func newEntryHandlerMap(i func(i interface{}, err error, - -)) *entryHandlerMap { +func newEntryHandlerMap(i *handlerInfo) *entryHandlerMap { return &entryHandlerMap{p: unsafe.Pointer(&i)} } // Load returns the value stored in the map for a key, or nil if no // value is present. // The ok result indicates whether value was found in the map. -func (m *HandlerMap) Load(key uint16) (value func(i interface{}, err error, - -), ok bool) { +func (m *HandlerMap) Load(key uint16) (value *handlerInfo, ok bool) { read, _ := m.read.Load().(readOnlyHandlerMap) e, ok := read.m[key] if !ok && read.amended { @@ -133,22 +127,16 @@ func (m *HandlerMap) Load(key uint16) (value func(i interface{}, err error, return e.load() } -func (e *entryHandlerMap) load() (value func(i interface{}, err error, - -), ok bool) { +func (e *entryHandlerMap) load() (value *handlerInfo, ok bool) { p := atomic.LoadPointer(&e.p) if p == nil || p == expungedHandlerMap { return value, false } - return *(*func(i interface{}, err error, - - ))(p), true + return *(**handlerInfo)(p), true } // Store sets the value for a key. -func (m *HandlerMap) Store(key uint16, value func(i interface{}, err error, - -)) { +func (m *HandlerMap) Store(key uint16, value *handlerInfo) { read, _ := m.read.Load().(readOnlyHandlerMap) if e, ok := read.m[key]; ok && e.tryStore(&value) { return @@ -181,9 +169,7 @@ func (m *HandlerMap) Store(key uint16, value func(i interface{}, err error, // // If the entry is expunged, tryStore returns false and leaves the entry // unchanged. -func (e *entryHandlerMap) tryStore(i *func(i interface{}, err error, - -)) bool { +func (e *entryHandlerMap) tryStore(i **handlerInfo) bool { for { p := atomic.LoadPointer(&e.p) if p == expungedHandlerMap { @@ -206,20 +192,14 @@ func (e *entryHandlerMap) unexpungeLocked() (wasExpunged bool) { // storeLocked unconditionally stores a value to the entry. // // The entry must be known not to be expunged. -func (e *entryHandlerMap) storeLocked(i *func(i interface{}, err error, - -)) { +func (e *entryHandlerMap) storeLocked(i **handlerInfo) { atomic.StorePointer(&e.p, unsafe.Pointer(i)) } // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. -func (m *HandlerMap) LoadOrStore(key uint16, value func(i interface{}, err error, - -)) (actual func(i interface{}, err error, - -), loaded bool) { +func (m *HandlerMap) LoadOrStore(key uint16, value *handlerInfo) (actual *handlerInfo, loaded bool) { // Avoid locking if it's a clean hit. read, _ := m.read.Load().(readOnlyHandlerMap) if e, ok := read.m[key]; ok { @@ -259,19 +239,13 @@ func (m *HandlerMap) LoadOrStore(key uint16, value func(i interface{}, err error // // If the entry is expunged, tryLoadOrStore leaves the entry unchanged and // returns with ok==false. -func (e *entryHandlerMap) tryLoadOrStore(i func(i interface{}, err error, - -)) (actual func(i interface{}, err error, - -), loaded, ok bool) { +func (e *entryHandlerMap) tryLoadOrStore(i *handlerInfo) (actual *handlerInfo, loaded, ok bool) { p := atomic.LoadPointer(&e.p) if p == expungedHandlerMap { return actual, false, false } if p != nil { - return *(*func(i interface{}, err error, - - ))(p), true, true + return *(**handlerInfo)(p), true, true } // Copy the interface after the first load to make this method more amenable @@ -287,18 +261,14 @@ func (e *entryHandlerMap) tryLoadOrStore(i func(i interface{}, err error, return actual, false, false } if p != nil { - return *(*func(i interface{}, err error, - - ))(p), true, true + return *(**handlerInfo)(p), true, true } } } // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. -func (m *HandlerMap) LoadAndDelete(key uint16) (value func(i interface{}, err error, - -), loaded bool) { +func (m *HandlerMap) LoadAndDelete(key uint16) (value *handlerInfo, loaded bool) { read, _ := m.read.Load().(readOnlyHandlerMap) e, ok := read.m[key] if !ok && read.amended { @@ -326,18 +296,14 @@ func (m *HandlerMap) Delete(key uint16) { m.LoadAndDelete(key) } -func (e *entryHandlerMap) delete() (value func(i interface{}, err error, - -), ok bool) { +func (e *entryHandlerMap) delete() (value *handlerInfo, ok bool) { for { p := atomic.LoadPointer(&e.p) if p == nil || p == expungedHandlerMap { return value, false } if atomic.CompareAndSwapPointer(&e.p, p, nil) { - return *(*func(i interface{}, err error, - - ))(p), true + return *(**handlerInfo)(p), true } } } @@ -352,9 +318,7 @@ func (e *entryHandlerMap) delete() (value func(i interface{}, err error, // // Range may be O(N) with the number of elements in the map even if f returns // false after a constant number of calls. -func (m *HandlerMap) Range(f func(key uint16, value func(i interface{}, err error, - -)) bool) { +func (m *HandlerMap) Range(f func(key uint16, value *handlerInfo) bool) { // We need to be able to iterate over all of the keys that were already // present at the start of the call to Range. // If read.amended is false, then read.m satisfies that property without diff --git a/client/image.go b/client/image.go index 522392ce..b27c2eae 100644 --- a/client/image.go +++ b/client/image.go @@ -65,7 +65,7 @@ func (c *QQClient) UploadGroupImageByFile(groupCode int64, path string) (*messag if err != nil { return nil, err } - defer img.Close() + defer func() { _ = img.Close() }() fh, length := utils.ComputeMd5AndLength(img) seq, pkt := c.buildGroupImageStorePacket(groupCode, fh[:], int32(length)) r, err := c.sendAndWait(seq, pkt) @@ -213,7 +213,7 @@ func (c *QQClient) buildGroupImageStorePacket(groupCode int64, md5 []byte, size } // ImgStore.GroupPicUp -func decodeGroupImageStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupImageStoreResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkt := pb.D388RespBody{} err := proto.Unmarshal(payload, &pkt) if err != nil { diff --git a/client/multimsg.go b/client/multimsg.go index 1afabe30..33bf62c3 100644 --- a/client/multimsg.go +++ b/client/multimsg.go @@ -43,7 +43,7 @@ func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, grou } // MultiMsg.ApplyUp -func decodeMultiApplyUpResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeMultiApplyUpResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { body := multimsg.MultiRspBody{} if err := proto.Unmarshal(payload, &body); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -85,7 +85,7 @@ func (c *QQClient) buildMultiApplyDownPacket(resId string) (uint16, []byte) { } // MultiMsg.ApplyDown -func decodeMultiApplyDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeMultiApplyDownResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { body := multimsg.MultiRspBody{} if err := proto.Unmarshal(payload, &body); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/offline_file.go b/client/offline_file.go index 63d2db09..9cf33a70 100644 --- a/client/offline_file.go +++ b/client/offline_file.go @@ -32,7 +32,7 @@ func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) (uint16, [ return seq, packet } -func decodeOfflineFileDownloadResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeOfflineFileDownloadResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := cmd0x346.C346RspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { c.Error("unmarshal cmd0x346 rsp body error: %v", err) diff --git a/client/pb/data.pb.go b/client/pb/data.pb.go index d00a7138..17e11ed2 100644 --- a/client/pb/data.pb.go +++ b/client/pb/data.pb.go @@ -7,11 +7,12 @@ package pb import ( + reflect "reflect" + sync "sync" + proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/client/ptt.go b/client/ptt.go index ca2fc950..fc361c8e 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -1,6 +1,7 @@ package client import ( + "bytes" "crypto/md5" "encoding/hex" "io" @@ -60,33 +61,31 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*messag // UploadPrivatePtt 将语音数据使用好友语音通道上传到服务器, 返回 message.PrivateVoiceElement 可直接发送 func (c *QQClient) UploadPrivatePtt(target int64, voice []byte) (*message.PrivateVoiceElement, error) { h := md5.Sum(voice) - i, err := c.sendAndWait(c.buildPrivatePttStorePacket(target, h[:], int32(len(voice)), int32(len(voice)))) + ext := c.buildC2CPttStoreBDHExt(target, h[:], int32(len(voice)), int32(len(voice))) + rsp, err := c.highwayUploadByBDH(bytes.NewReader(voice), 26, c.highwaySession.SigSession, ext, false) if err != nil { return nil, err } - rsp := i.(pttUploadResponse) - if rsp.IsExists { - goto ok + if len(rsp) == 0 { + return nil, errors.New("miss rsp") } - for i, ip := range rsp.UploadIp { - err := c.uploadPtt(ip, rsp.UploadPort[i], rsp.UploadKey, rsp.FileKey, voice, h[:]) - if err != nil { - continue - } - goto ok + pkt := cmd0x346.C346RspBody{} + if err = proto.Unmarshal(rsp, &pkt); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + } + if pkt.ApplyUploadRsp == nil { + return nil, errors.New("miss apply upload rsp") } - return nil, errors.New("upload failed") -ok: return &message.PrivateVoiceElement{ Ptt: &msg.Ptt{ - FileType: proto.Int32(4), - SrcUin: &c.Uin, - FileMd5: h[:], - FileName: proto.String(hex.EncodeToString(h[:]) + ".amr"), - FileSize: proto.Int32(int32(len(voice))), - FileKey: rsp.FileKey, + FileType: proto.Int32(4), + SrcUin: &c.Uin, + FileUuid: pkt.ApplyUploadRsp.Uuid, + FileMd5: h[:], + FileName: proto.String(hex.EncodeToString(h[:]) + ".amr"), + FileSize: proto.Int32(int32(len(voice))), + // Reserve: constructPTTExtraInfo(1, int32(len(voice))), // todo length BoolValid: proto.Bool(true), - PbReserve: []byte{8, 0, 40, 0, 56, 0}, }}, nil } @@ -261,7 +260,7 @@ func (c *QQClient) buildPttGroupShortVideoUploadReqPacket(videoHash, thumbHash [ } // PttStore.GroupPttUp -func decodeGroupPttStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupPttStoreResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkt := pb.D388RespBody{} err := proto.Unmarshal(payload, &pkt) if err != nil { @@ -291,7 +290,7 @@ func decodeGroupPttStoreResponse(_ *QQClient, _ uint16, payload []byte) (interfa } // PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500 -func (c *QQClient) buildPrivatePttStorePacket(target int64, md5 []byte, size, voiceLength int32) (uint16, []byte) { +func (c *QQClient) buildC2CPttStoreBDHExt(target int64, md5 []byte, size, voiceLength int32) []byte { seq := c.nextSeq() req := &cmd0x346.C346ReqBody{ Cmd: 500, @@ -315,42 +314,11 @@ func (c *QQClient) buildPrivatePttStorePacket(target int64, md5 []byte, size, vo }, } payload, _ := proto.Marshal(req) - packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) - return seq, packet -} - -// PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500 -func decodePrivatePttStoreResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { - rsp := cmd0x346.C346RspBody{} - if err := proto.Unmarshal(payload, &rsp); err != nil { - c.Error("unmarshal cmd0x346 rsp body error: %v", err) - return nil, errors.Wrap(err, "unmarshal cmd0x346 rsp body error") - } - if rsp.ApplyUploadRsp == nil { - c.Error("decode apply upload 500 error: apply rsp is nil.") - return nil, errors.New("apply rsp is nil") - } - if rsp.ApplyUploadRsp.RetCode != 0 { - c.Error("decode apply upload 500 error: %v", rsp.ApplyUploadRsp.RetCode) - return nil, errors.Errorf("apply upload rsp error: %d", rsp.ApplyUploadRsp.RetCode) - } - if rsp.ApplyUploadRsp.BoolFileExist { - return pttUploadResponse{IsExists: true}, nil - } - var port []int32 - for range rsp.ApplyUploadRsp.UploadipList { - port = append(port, rsp.ApplyUploadRsp.UploadPort) - } - return pttUploadResponse{ - UploadKey: rsp.ApplyUploadRsp.UploadKey, - UploadIp: rsp.ApplyUploadRsp.UploadipList, - UploadPort: port, - FileKey: rsp.ApplyUploadRsp.Uuid, - }, nil + return payload } // PttCenterSvr.ShortVideoDownReq -func decodePttShortVideoDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodePttShortVideoDownResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := pttcenter.ShortVideoRspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -362,7 +330,7 @@ func decodePttShortVideoDownResponse(_ *QQClient, _ uint16, payload []byte) (int } // PttCenterSvr.GroupShortVideoUpReq -func decodeGroupShortVideoUploadResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeGroupShortVideoUploadResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := pttcenter.ShortVideoRspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -375,3 +343,19 @@ func decodeGroupShortVideoUploadResponse(_ *QQClient, _ uint16, payload []byte) } return rsp.PttShortVideoUploadRsp, nil } + +func constructPTTExtraInfo(codec, length int32) []byte { + return binary.NewWriterF(func(w *binary.Writer) { + w.WriteByte(3) + w.WriteByte(8) + w.WriteUInt16(4) + w.WriteUInt32(uint32(codec)) + w.WriteByte(9) + w.WriteUInt16(4) + w.WriteUInt32(uint32(14)) // length 时间 + w.WriteByte(10) + info := []byte{0x08, 0x00, 0x28, 0x00, 0x38, 0x00} // todo + w.WriteUInt16(uint16(len(info))) + w.Write(info) + }) +} diff --git a/client/recall.go b/client/recall.go index 7ed78fea..36c9fb24 100644 --- a/client/recall.go +++ b/client/recall.go @@ -89,7 +89,7 @@ func (c *QQClient) buildPrivateRecallPacket(uin, ts int64, msgSeq, random int32) return seq, packet } -func decodeMsgWithDrawResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeMsgWithDrawResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := msg.MsgWithDrawResp{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/security.go b/client/security.go index 3dbdbe5f..72421b61 100644 --- a/client/security.go +++ b/client/security.go @@ -49,7 +49,7 @@ func (c *QQClient) buildUrlCheckRequest(url string) (uint16, []byte) { return seq, packet } -func decodeUrlCheckResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeUrlCheckResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := &oidb.OIDBSSOPkg{} rsp := &oidb.DBCBRspBody{} if err := proto.Unmarshal(payload, pkg); err != nil { diff --git a/client/sync.go b/client/sync.go index 0ffb74d2..ffa3833f 100644 --- a/client/sync.go +++ b/client/sync.go @@ -29,7 +29,6 @@ func init() { } type ( - // SessionSyncResponse 会话同步结果 SessionSyncResponse struct { GroupSessions []*GroupSessionInfo @@ -101,7 +100,7 @@ func (c *QQClient) SyncSessions() (*SessionSyncResponse, error) { select { case <-notifyChan: stop() - case <-time.After(time.Second * 5): + case <-time.After(time.Second * 20): stop() } return ret, nil @@ -274,7 +273,7 @@ func (c *QQClient) buildGroupMsgReadedPacket(groupCode, msgSeq int64) (uint16, [ } // StatSvc.GetDevLoginInfo -func decodeDevListResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeDevListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -297,7 +296,7 @@ func decodeDevListResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, } // RegPrxySvc.PushParam -func decodePushParamPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodePushParamPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -342,7 +341,7 @@ func decodePushParamPacket(c *QQClient, _ uint16, payload []byte) (interface{}, } // RegPrxySvc.PbSyncMsg -func decodeMsgSyncResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeMsgSyncResponse(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := &msf.SvcRegisterProxyMsgResp{} if err := proto.Unmarshal(payload, rsp); err != nil { return nil, err @@ -376,13 +375,13 @@ func decodeMsgSyncResponse(c *QQClient, _ uint16, payload []byte) (interface{}, if len(rsp.C2CMsg) > 4 { c2cRsp := &msg.GetMessageResponse{} if proto.Unmarshal(rsp.C2CMsg[4:], c2cRsp) == nil { - c.c2cMessageSyncProcessor(c2cRsp) // todo rewrite c2c processor + c.c2cMessageSyncProcessor(c2cRsp, info) } } return ret, nil } -func decodeMsgReadedResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeMsgReadedResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := msg.PbMsgReadedReportResp{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -396,7 +395,7 @@ func decodeMsgReadedResponse(c *QQClient, _ uint16, payload []byte) (interface{} var loginNotifyLock sync.Mutex // StatSvc.SvcReqMSFLoginNotify -func decodeLoginNotifyPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeLoginNotifyPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} diff --git a/client/system_msg.go b/client/system_msg.go index 0dccfee0..97e13bcb 100644 --- a/client/system_msg.go +++ b/client/system_msg.go @@ -200,7 +200,7 @@ func (c *QQClient) buildSystemMsgFriendActionPacket(reqId, requester int64, acce } // ProfileService.Pb.ReqSystemMsgNew.Group -func decodeSystemMsgGroupPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeSystemMsgGroupPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { rsp := structmsg.RspSystemMsgNew{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, err diff --git a/client/translate.go b/client/translate.go index b4afd1a1..85ede66a 100644 --- a/client/translate.go +++ b/client/translate.go @@ -42,7 +42,7 @@ func (c *QQClient) Translate(src, dst, text string) (string, error) { } // OidbSvc.0x990 -func decodeTranslateResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { +func decodeTranslateResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := oidb.OIDBSSOPkg{} rsp := oidb.TranslateRspBody{} if err := proto.Unmarshal(payload, &pkg); err != nil { diff --git a/go.mod b/go.mod index 41ee143e..595b4c09 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Mrs4s/MiraiGo go 1.15 require ( + github.com/a8m/syncmap v0.0.0-20200818084611-4bbbd178de97 // indirect github.com/golang/protobuf v1.4.3 github.com/json-iterator/go v1.1.10 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 diff --git a/go.sum b/go.sum index 0f710ceb..99881c34 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/a8m/syncmap v0.0.0-20200818084611-4bbbd178de97 h1:QJIAdw5m5tNUy7fjBxgg73+YUs/AkeESeqdJ1L3lN10= +github.com/a8m/syncmap v0.0.0-20200818084611-4bbbd178de97/go.mod h1:f3iF7/3t9i9hsYF8DPgT0XeIVyNzevhMCKf2445Q6pE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -59,6 +61,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190501045030-23463209683d/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/message/elements.go b/message/elements.go index 324dbfbb..04647d03 100644 --- a/message/elements.go +++ b/message/elements.go @@ -253,11 +253,16 @@ func NewReply(m *GroupMessage) *ReplyElement { } func NewUrlShare(url, title, content, image string) *ServiceElement { - template := fmt.Sprintf(`%s%s`, - url, url, image, title, content, + template := fmt.Sprintf(`%v%v`, + title, url, image, title, content, ) + /* + template := fmt.Sprintf(`%s%s`, + url, url, image, title, content, + ) + */ return &ServiceElement{ - Id: 33, + Id: 1, Content: template, ResId: url, SubType: "UrlShare", diff --git a/message/pack.go b/message/pack.go index 2450cddf..b8a79232 100644 --- a/message/pack.go +++ b/message/pack.go @@ -128,7 +128,7 @@ func (e *FriendImageElement) Pack() (r []*msg.Elem) { func (e *ServiceElement) Pack() (r []*msg.Elem) { r = []*msg.Elem{} // id =35 已移至 ForwardElement - if e.Id == 33 { + if e.Id == 1 { r = append(r, &msg.Elem{ Text: &msg.Text{Str: &e.ResId}, })