github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/client/qidian.go (about) 1 // 企点协议相关特殊逻辑 2 3 package client 4 5 import ( 6 "bytes" 7 "fmt" 8 "io" 9 "net/http" 10 11 "github.com/pkg/errors" 12 13 "github.com/Mrs4s/MiraiGo/binary" 14 "github.com/Mrs4s/MiraiGo/client/internal/network" 15 "github.com/Mrs4s/MiraiGo/client/pb/cmd0x3f6" 16 "github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff" 17 "github.com/Mrs4s/MiraiGo/client/pb/msg" 18 "github.com/Mrs4s/MiraiGo/internal/proto" 19 "github.com/Mrs4s/MiraiGo/utils" 20 ) 21 22 func init() { 23 decoders["qidianservice.69"] = decodeLoginExtraResponse 24 decoders["HttpConn.0x6ff_501"] = decodeConnKeyResponse 25 } 26 27 // getQiDianAddressDetailList 外部联系人列表 28 func (c *QQClient) getQiDianAddressDetailList() ([]*FriendInfo, error) { 29 req := &cmd0x6ff.C519ReqBody{ 30 SubCmd: proto.Uint32(33), 31 CrmCommonHead: &cmd0x6ff.C519CRMMsgHead{ 32 KfUin: proto.Uint64(uint64(c.QiDian.MasterUin)), 33 VerNo: proto.Uint32(uint32(utils.ConvertSubVersionToInt(c.version().SortVersionName))), 34 CrmSubCmd: proto.Uint32(33), 35 LaborUin: proto.Uint64(uint64(c.Uin)), 36 }, 37 GetAddressDetailListReqBody: &cmd0x6ff.GetAddressDetailListReqBody{ 38 Timestamp2: proto.Uint64(0), 39 }, 40 } 41 rspData, err := c.bigDataRequest(0x519, req) 42 if err != nil { 43 return nil, errors.Wrap(err, "request error") 44 } 45 rsp := &cmd0x6ff.C519RspBody{} 46 if err = proto.Unmarshal(rspData, rsp); err != nil { 47 return nil, errors.Wrap(err, "unmarshal error") 48 } 49 if rsp.GetAddressDetailListRspBody == nil { 50 return nil, errors.New("rsp body is nil") 51 } 52 ret := []*FriendInfo{} 53 for _, detail := range rsp.GetAddressDetailListRspBody.AddressDetail { 54 if len(detail.Qq) == 0 { 55 c.warning("address detail %v QQ is 0", string(detail.Name)) 56 continue 57 } 58 ret = append(ret, &FriendInfo{ 59 Uin: int64(detail.Qq[0].Account.Unwrap()), 60 Nickname: string(detail.Name), 61 }) 62 } 63 return ret, nil 64 } 65 66 func (c *QQClient) buildLoginExtraPacket() (uint16, []byte) { 67 req := &cmd0x3f6.C3F6ReqBody{ 68 SubCmd: proto.Uint32(69), 69 CrmCommonHead: &cmd0x3f6.C3F6CRMMsgHead{ 70 CrmSubCmd: proto.Uint32(69), 71 VerNo: proto.Uint32(uint32(utils.ConvertSubVersionToInt(c.version().SortVersionName))), 72 Clienttype: proto.Uint32(2), 73 }, 74 SubcmdLoginProcessCompleteReqBody: &cmd0x3f6.QDUserLoginProcessCompleteReqBody{ 75 Kfext: proto.Uint64(uint64(c.Uin)), 76 Pubno: proto.Some(c.version().AppId), 77 Buildno: proto.Uint32(uint32(utils.ConvertSubVersionToInt(c.version().SortVersionName))), 78 TerminalType: proto.Uint32(2), 79 Status: proto.Uint32(10), 80 LoginTime: proto.Uint32(5), 81 HardwareInfo: proto.String(string(c.Device().Model)), 82 SoftwareInfo: proto.String(string(c.Device().Version.Release)), 83 Guid: c.Device().Guid, 84 AppName: proto.Some(c.version().ApkId), 85 SubAppId: proto.Some(c.version().AppId), 86 }, 87 } 88 payload, _ := proto.Marshal(req) 89 return c.uniPacket("qidianservice.69", payload) 90 } 91 92 func (c *QQClient) buildConnKeyRequestPacket() (uint16, []byte) { 93 req := &cmd0x6ff.C501ReqBody{ 94 ReqBody: &cmd0x6ff.SubCmd0X501ReqBody{ 95 Uin: proto.Uint64(uint64(c.Uin)), 96 IdcId: proto.Uint32(0), 97 Appid: proto.Uint32(16), 98 LoginSigType: proto.Uint32(1), 99 RequestFlag: proto.Uint32(3), 100 ServiceTypes: []uint32{1}, 101 }, 102 } 103 payload, _ := proto.Marshal(req) 104 return c.uniPacket("HttpConn.0x6ff_501", payload) 105 } 106 107 func (c *QQClient) bigDataRequest(subCmd uint32, req proto.Message) ([]byte, error) { 108 if c.QiDian.bigDataReqSession == nil { 109 return nil, errors.New("please call conn key request method before") 110 } 111 data, _ := proto.Marshal(req) 112 head, _ := proto.Marshal(&msg.IMHead{ 113 HeadType: proto.Uint32(4), 114 HttpconnHead: &msg.HttpConnHead{ 115 Uin: proto.Uint64(uint64(c.Uin)), 116 Command: proto.Uint32(1791), 117 SubCommand: proto.Some(subCmd), 118 Seq: proto.Uint32(uint32(c.nextHighwayApplySeq())), 119 Version: proto.Uint32(500), // todo: short version convert 120 Flag: proto.Uint32(1), 121 CompressType: proto.Uint32(0), 122 ErrorCode: proto.Uint32(0), 123 }, 124 LoginSig: &msg.LoginSig{ 125 Type: proto.Uint32(22), 126 Sig: c.QiDian.bigDataReqSession.SigSession, 127 }, 128 }) 129 tea := binary.NewTeaCipher(c.QiDian.bigDataReqSession.SessionKey) 130 body := tea.Encrypt(data) 131 url := fmt.Sprintf("http://%v/cgi-bin/httpconn", c.QiDian.bigDataReqAddrs[0]) 132 httpReq, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(binary.NewWriterF(func(w *binary.Writer) { 133 w.WriteByte(40) 134 w.WriteUInt32(uint32(len(head))) 135 w.WriteUInt32(uint32(len(body))) 136 w.Write(head) 137 w.Write(body) 138 w.WriteByte(41) 139 }))) 140 rsp, err := http.DefaultClient.Do(httpReq) 141 if err != nil { 142 return nil, errors.Wrap(err, "request error") 143 } 144 defer func() { _ = rsp.Body.Close() }() 145 rspBody, _ := io.ReadAll(rsp.Body) 146 if len(rspBody) == 0 { 147 return nil, errors.Wrap(err, "request error") 148 } 149 r := binary.NewReader(rspBody) 150 r.ReadByte() 151 l1 := int(r.ReadInt32()) 152 l2 := int(r.ReadInt32()) 153 r.ReadBytes(l1) 154 payload := r.ReadBytes(l2) 155 return tea.Decrypt(payload), nil 156 } 157 158 func decodeLoginExtraResponse(c *QQClient, pkt *network.Packet) (any, error) { 159 rsp := cmd0x3f6.C3F6RspBody{} 160 if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil { 161 return nil, errors.Wrap(err, "failed to unmarshal protobuf message") 162 } 163 if rsp.SubcmdLoginProcessCompleteRspBody == nil { 164 return nil, errors.New("login process resp is nil") 165 } 166 c.QiDian = &QiDianAccountInfo{ 167 MasterUin: int64(rsp.SubcmdLoginProcessCompleteRspBody.Corpuin.Unwrap()), 168 ExtName: rsp.SubcmdLoginProcessCompleteRspBody.ExtuinName.Unwrap(), 169 CreateTime: int64(rsp.SubcmdLoginProcessCompleteRspBody.OpenAccountTime.Unwrap()), 170 } 171 return nil, nil 172 } 173 174 func decodeConnKeyResponse(c *QQClient, pkt *network.Packet) (any, error) { 175 rsp := cmd0x6ff.C501RspBody{} 176 if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil { 177 return nil, errors.Wrap(err, "failed to unmarshal protobuf message") 178 } 179 if c.QiDian == nil { 180 return nil, errors.New("please call login extra before") 181 } 182 c.QiDian.bigDataReqSession = &bigDataSessionInfo{ 183 SigSession: rsp.RspBody.SigSession, 184 SessionKey: rsp.RspBody.SessionKey, 185 } 186 for _, srv := range rsp.RspBody.Addrs { 187 if srv.ServiceType.Unwrap() == 1 { 188 for _, addr := range srv.Addrs { 189 c.QiDian.bigDataReqAddrs = append(c.QiDian.bigDataReqAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr.Ip.Unwrap()), addr.Port.Unwrap())) 190 } 191 } 192 } 193 return nil, nil 194 }