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  }