github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/client/sync.go (about)

     1  package client
     2  
     3  import (
     4  	"math/rand"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/pkg/errors"
     9  
    10  	"github.com/Mrs4s/MiraiGo/binary/jce"
    11  	"github.com/Mrs4s/MiraiGo/client/internal/network"
    12  	"github.com/Mrs4s/MiraiGo/client/pb/msf"
    13  	"github.com/Mrs4s/MiraiGo/client/pb/msg"
    14  	"github.com/Mrs4s/MiraiGo/client/pb/oidb"
    15  	"github.com/Mrs4s/MiraiGo/internal/proto"
    16  	"github.com/Mrs4s/MiraiGo/message"
    17  )
    18  
    19  func init() {
    20  	decoders["StatSvc.GetDevLoginInfo"] = decodeDevListResponse
    21  	decoders["StatSvc.SvcReqMSFLoginNotify"] = decodeLoginNotifyPacket
    22  	decoders["RegPrxySvc.getOffMsg"] = ignoreDecoder
    23  	decoders["RegPrxySvc.GetMsgV2"] = ignoreDecoder
    24  	decoders["RegPrxySvc.PbGetMsg"] = ignoreDecoder
    25  	decoders["RegPrxySvc.NoticeEnd"] = ignoreDecoder
    26  	decoders["RegPrxySvc.PushParam"] = decodePushParamPacket
    27  	decoders["RegPrxySvc.PbSyncMsg"] = decodeMsgSyncResponse
    28  	decoders["PbMessageSvc.PbMsgReadedReport"] = decodeMsgReadedResponse
    29  	decoders["MessageSvc.PushReaded"] = ignoreDecoder
    30  	decoders["OnlinePush.PbC2CMsgSync"] = decodeC2CSyncPacket
    31  }
    32  
    33  type (
    34  	// SessionSyncResponse 会话同步结果
    35  	SessionSyncResponse struct {
    36  		GroupSessions []*GroupSessionInfo
    37  	}
    38  
    39  	// GroupSessionInfo 群会话信息
    40  	GroupSessionInfo struct {
    41  		GroupCode      int64
    42  		UnreadCount    uint32
    43  		LatestMessages []*message.GroupMessage
    44  	}
    45  
    46  	sessionSyncEvent struct {
    47  		IsEnd         bool
    48  		GroupNum      int32
    49  		GroupSessions []*GroupSessionInfo
    50  	}
    51  )
    52  
    53  // GetAllowedClients 获取已允许的其他客户端
    54  func (c *QQClient) GetAllowedClients() ([]*OtherClientInfo, error) {
    55  	i, err := c.sendAndWait(c.buildDeviceListRequestPacket())
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	list := i.([]jce.SvcDevLoginInfo)
    60  	ret := make([]*OtherClientInfo, 0, len(list))
    61  	for _, l := range list {
    62  		ret = append(ret, &OtherClientInfo{
    63  			AppId:      l.AppId,
    64  			DeviceName: l.DeviceName,
    65  			DeviceKind: l.DeviceTypeInfo,
    66  		})
    67  	}
    68  	return ret, nil
    69  }
    70  
    71  // RefreshStatus 刷新客户端状态
    72  func (c *QQClient) RefreshStatus() error {
    73  	_, err := c.sendAndWait(c.buildGetOfflineMsgRequestPacket())
    74  	return err
    75  }
    76  
    77  // SyncSessions 同步会话列表
    78  func (c *QQClient) SyncSessions() (*SessionSyncResponse, error) {
    79  	ret := &SessionSyncResponse{}
    80  	notifyChan := make(chan bool, 4)
    81  	var groupNum int32 = -1
    82  	stop := c.waitPacket("RegPrxySvc.PbSyncMsg", func(i any, err error) {
    83  		if err != nil {
    84  			return
    85  		}
    86  		e := i.(*sessionSyncEvent)
    87  		if len(e.GroupSessions) > 0 {
    88  			ret.GroupSessions = append(ret.GroupSessions, e.GroupSessions...)
    89  		}
    90  		if e.GroupNum != -1 {
    91  			groupNum = e.GroupNum
    92  		}
    93  		c.debug("sync session %v/%v", len(ret.GroupSessions), groupNum)
    94  		if groupNum != -1 && len(ret.GroupSessions) >= int(groupNum) {
    95  			notifyChan <- true
    96  		}
    97  	})
    98  	_, pkt := c.buildSyncMsgRequestPacket()
    99  	if err := c.sendPacket(pkt); err != nil {
   100  		stop()
   101  		return nil, err
   102  	}
   103  	select {
   104  	case <-notifyChan:
   105  		stop()
   106  	case <-time.After(time.Second * 3):
   107  		stop()
   108  	}
   109  	return ret, nil
   110  }
   111  
   112  // MarkGroupMessageReaded 标记群消息已读, 适当调用应该能减少风控
   113  func (c *QQClient) MarkGroupMessageReaded(groupCode, seq int64) {
   114  	_, _ = c.sendAndWait(c.buildGroupMsgReadedPacket(groupCode, seq))
   115  }
   116  
   117  func (c *QQClient) MarkPrivateMessageReaded(uin, time int64) {
   118  	_, _ = c.sendAndWait(c.buildPrivateMsgReadedPacket(uin, time))
   119  }
   120  
   121  // StatSvc.GetDevLoginInfo
   122  func (c *QQClient) buildDeviceListRequestPacket() (uint16, []byte) {
   123  	req := &jce.SvcReqGetDevLoginInfo{
   124  		Guid:           c.Device().Guid,
   125  		LoginType:      1,
   126  		AppName:        "com.tencent.mobileqq",
   127  		RequireMax:     20,
   128  		GetDevListType: 2,
   129  	}
   130  	buf := &jce.RequestDataVersion3{Map: map[string][]byte{"SvcReqGetDevLoginInfo": packUniRequestData(req.ToBytes())}}
   131  	pkt := &jce.RequestPacket{
   132  		IVersion:     3,
   133  		SServantName: "StatSvc",
   134  		SFuncName:    "SvcReqGetDevLoginInfo",
   135  		SBuffer:      buf.ToBytes(),
   136  		Context:      make(map[string]string),
   137  		Status:       make(map[string]string),
   138  	}
   139  	return c.uniPacket("StatSvc.GetDevLoginInfo", pkt.ToBytes())
   140  }
   141  
   142  // RegPrxySvc.getOffMsg
   143  func (c *QQClient) buildGetOfflineMsgRequestPacket() (uint16, []byte) {
   144  	t := c.stat.LastMessageTime.Load()
   145  	if t == 0 {
   146  		t = 1
   147  	}
   148  	regReq := &jce.SvcReqRegisterNew{
   149  		RequestOptional: 0x101C2 | 32,
   150  		C2CMsg: &jce.SvcReqGetMsgV2{
   151  			Uin:              c.Uin,
   152  			DateTime:         int32(t),
   153  			RecivePic:        1,
   154  			Ability:          15,
   155  			Channel:          4,
   156  			Inst:             1,
   157  			ChannelEx:        1,
   158  			SyncCookie:       c.sig.SyncCookie,
   159  			SyncFlag:         0, // START
   160  			RambleFlag:       0,
   161  			GeneralAbi:       1,
   162  			PubAccountCookie: c.sig.PubAccountCookie,
   163  		},
   164  		GroupMsg: &jce.SvcReqPullGroupMsgSeq{
   165  			VerifyType: 0,
   166  			Filter:     1, // LIMIT_10_AND_IN_3_DAYS
   167  		},
   168  		EndSeq: time.Now().Unix(),
   169  	}
   170  	flag := msg.SyncFlag_START
   171  	msgReq, _ := proto.Marshal(&msg.GetMessageRequest{
   172  		SyncFlag:           proto.Some(flag),
   173  		SyncCookie:         c.sig.SyncCookie,
   174  		RambleFlag:         proto.Int32(0),
   175  		ContextFlag:        proto.Int32(1),
   176  		OnlineSyncFlag:     proto.Int32(0),
   177  		LatestRambleNumber: proto.Int32(20),
   178  		OtherRambleNumber:  proto.Int32(3),
   179  	})
   180  	buf := &jce.RequestDataVersion3{Map: map[string][]byte{
   181  		"req_PbOffMsg": jce.NewJceWriter().WriteBytes(append([]byte{0, 0, 0, 0}, msgReq...), 0).Bytes(),
   182  		"req_OffMsg":   packUniRequestData(regReq.ToBytes()),
   183  	}}
   184  	pkt := &jce.RequestPacket{
   185  		IVersion:     3,
   186  		SServantName: "RegPrxySvc",
   187  		SBuffer:      buf.ToBytes(),
   188  		Context:      make(map[string]string),
   189  		Status:       make(map[string]string),
   190  	}
   191  	return c.uniPacket("RegPrxySvc.getOffMsg", pkt.ToBytes())
   192  }
   193  
   194  // RegPrxySvc.PbSyncMsg
   195  func (c *QQClient) buildSyncMsgRequestPacket() (uint16, []byte) {
   196  	oidbReq, _ := proto.Marshal(&oidb.D769RspBody{
   197  		ConfigList: []*oidb.D769ConfigSeq{
   198  			{
   199  				Type:    proto.Uint32(46),
   200  				Version: proto.Uint32(0),
   201  			},
   202  			{
   203  				Type:    proto.Uint32(283),
   204  				Version: proto.Uint32(0),
   205  			},
   206  		},
   207  	})
   208  	t := c.stat.LastMessageTime.Load()
   209  	if t == 0 {
   210  		t = 1
   211  	}
   212  	regReq := &jce.SvcReqRegisterNew{
   213  		RequestOptional:   128 | 64 | 256 | 2 | 8192 | 16384 | 65536,
   214  		DisGroupMsgFilter: 1,
   215  		C2CMsg: &jce.SvcReqGetMsgV2{
   216  			Uin:              c.Uin,
   217  			DateTime:         int32(t),
   218  			RecivePic:        1,
   219  			Ability:          15,
   220  			Channel:          4,
   221  			Inst:             1,
   222  			ChannelEx:        1,
   223  			SyncCookie:       c.sig.SyncCookie,
   224  			SyncFlag:         0, // START
   225  			RambleFlag:       0,
   226  			GeneralAbi:       1,
   227  			PubAccountCookie: c.sig.PubAccountCookie,
   228  		},
   229  		GroupMask: 2,
   230  		EndSeq:    int64(rand.Uint32()),
   231  		O769Body:  oidbReq,
   232  	}
   233  	flag := msg.SyncFlag_START
   234  	msgReq := &msg.GetMessageRequest{
   235  		SyncFlag:           proto.Some(flag),
   236  		SyncCookie:         c.sig.SyncCookie,
   237  		RambleFlag:         proto.Int32(0),
   238  		ContextFlag:        proto.Int32(1),
   239  		OnlineSyncFlag:     proto.Int32(0),
   240  		LatestRambleNumber: proto.Int32(20),
   241  		OtherRambleNumber:  proto.Int32(3),
   242  		MsgReqType:         proto.Int32(1),
   243  	}
   244  	offMsg, _ := proto.Marshal(msgReq)
   245  	msgReq.MsgReqType = proto.Int32(2)
   246  	msgReq.SyncCookie = nil
   247  	msgReq.PubaccountCookie = c.sig.PubAccountCookie
   248  	pubMsg, _ := proto.Marshal(msgReq)
   249  	buf := &jce.RequestDataVersion3{Map: map[string][]byte{
   250  		"req_PbOffMsg": jce.NewJceWriter().WriteBytes(append([]byte{0, 0, 0, 0}, offMsg...), 0).Bytes(),
   251  		"req_PbPubMsg": jce.NewJceWriter().WriteBytes(append([]byte{0, 0, 0, 0}, pubMsg...), 0).Bytes(),
   252  		"req_OffMsg":   packUniRequestData(regReq.ToBytes()),
   253  	}}
   254  	pkt := &jce.RequestPacket{
   255  		IVersion:     3,
   256  		SServantName: "RegPrxySvc",
   257  		SBuffer:      buf.ToBytes(),
   258  		Context:      make(map[string]string),
   259  		Status:       make(map[string]string),
   260  	}
   261  	return c.uniPacket("RegPrxySvc.infoSync", pkt.ToBytes())
   262  }
   263  
   264  // PbMessageSvc.PbMsgReadedReport
   265  func (c *QQClient) buildGroupMsgReadedPacket(groupCode, msgSeq int64) (uint16, []byte) {
   266  	req, _ := proto.Marshal(&msg.PbMsgReadedReportReq{GrpReadReport: []*msg.PbGroupReadedReportReq{{
   267  		GroupCode:   proto.Uint64(uint64(groupCode)),
   268  		LastReadSeq: proto.Uint64(uint64(msgSeq)),
   269  	}}})
   270  	return c.uniPacket("PbMessageSvc.PbMsgReadedReport", req)
   271  }
   272  
   273  func (c *QQClient) buildPrivateMsgReadedPacket(uin, time int64) (uint16, []byte) {
   274  	req, _ := proto.Marshal(&msg.PbMsgReadedReportReq{C2CReadReport: &msg.PbC2CReadedReportReq{PairInfo: []*msg.UinPairReadInfo{
   275  		{
   276  			PeerUin:      proto.Uint64(uint64(uin)),
   277  			LastReadTime: proto.Uint32(uint32(time)),
   278  		},
   279  	}, SyncCookie: c.sig.SyncCookie}})
   280  	return c.uniPacket("PbMessageSvc.PbMsgReadedReport", req)
   281  }
   282  
   283  // StatSvc.GetDevLoginInfo
   284  func decodeDevListResponse(_ *QQClient, pkt *network.Packet) (any, error) {
   285  	request := &jce.RequestPacket{}
   286  	request.ReadFrom(jce.NewJceReader(pkt.Payload))
   287  	data := &jce.RequestDataVersion2{}
   288  	data.ReadFrom(jce.NewJceReader(request.SBuffer))
   289  	rsp := jce.NewJceReader(data.Map["SvcRspGetDevLoginInfo"]["QQService.SvcRspGetDevLoginInfo"][1:])
   290  	d := rsp.ReadSvcDevLoginInfos(4)
   291  	if len(d) > 0 {
   292  		return d, nil
   293  	}
   294  	d = rsp.ReadSvcDevLoginInfos(5)
   295  	if len(d) > 0 {
   296  		return d, nil
   297  	}
   298  	d = rsp.ReadSvcDevLoginInfos(6)
   299  	if len(d) > 0 {
   300  		return d, nil
   301  	}
   302  	return nil, errors.New("not any device")
   303  }
   304  
   305  // RegPrxySvc.PushParam
   306  func decodePushParamPacket(c *QQClient, pkt *network.Packet) (any, error) {
   307  	request := &jce.RequestPacket{}
   308  	request.ReadFrom(jce.NewJceReader(pkt.Payload))
   309  	data := &jce.RequestDataVersion2{}
   310  	data.ReadFrom(jce.NewJceReader(request.SBuffer))
   311  	reader := jce.NewJceReader(data.Map["SvcRespParam"]["RegisterProxySvcPack.SvcRespParam"][1:])
   312  	rsp := &jce.SvcRespParam{}
   313  	rsp.ReadFrom(reader)
   314  	allowedClients, _ := c.GetAllowedClients()
   315  	c.OnlineClients = []*OtherClientInfo{}
   316  	for _, i := range rsp.OnlineInfos {
   317  		name, kind := i.SubPlatform, i.SubPlatform
   318  		for _, ac := range allowedClients {
   319  			if ac.AppId == int64(i.InstanceId) {
   320  				name = ac.DeviceName
   321  			}
   322  		}
   323  		switch i.UClientType {
   324  		case 65793:
   325  			kind = "Windows"
   326  		case 65805, 68104:
   327  			kind = "aPad"
   328  		case 66818, 66831, 81154:
   329  			kind = "Mac"
   330  		case 68361, 72194:
   331  			kind = "iPad"
   332  		case 75023, 78082, 78096:
   333  			kind = "Watch"
   334  		case 77313:
   335  			kind = "Windows TIM"
   336  		}
   337  		c.OnlineClients = append(c.OnlineClients, &OtherClientInfo{
   338  			AppId:      int64(i.InstanceId),
   339  			DeviceName: name,
   340  			DeviceKind: kind,
   341  		})
   342  	}
   343  	return nil, nil
   344  }
   345  
   346  // RegPrxySvc.PbSyncMsg
   347  func decodeMsgSyncResponse(c *QQClient, pkt *network.Packet) (any, error) {
   348  	rsp := &msf.SvcRegisterProxyMsgResp{}
   349  	if err := proto.Unmarshal(pkt.Payload, rsp); err != nil {
   350  		return nil, err
   351  	}
   352  	ret := &sessionSyncEvent{
   353  		IsEnd:    (rsp.Flag.Unwrap() & 2) == 2,
   354  		GroupNum: -1,
   355  	}
   356  	if rsp.Info != nil {
   357  		ret.GroupNum = int32(rsp.Info.GroupNum.Unwrap())
   358  	}
   359  	if len(rsp.GroupMsg) > 0 {
   360  		for _, gm := range rsp.GroupMsg {
   361  			gmRsp := &msg.GetGroupMsgResp{}
   362  			if err := proto.Unmarshal(gm.Content[4:], gmRsp); err != nil {
   363  				continue
   364  			}
   365  			var latest []*message.GroupMessage
   366  			for _, m := range gmRsp.Msg {
   367  				if m.Head.FromUin.Unwrap() != 0 {
   368  					pm := c.parseGroupMessage(m)
   369  					if pm != nil {
   370  						latest = append(latest, pm)
   371  					}
   372  				}
   373  			}
   374  			ret.GroupSessions = append(ret.GroupSessions, &GroupSessionInfo{
   375  				GroupCode:      int64(gmRsp.GroupCode.Unwrap()),
   376  				UnreadCount:    uint32(gmRsp.ReturnEndSeq.Unwrap() - gm.MemberSeq.Unwrap()),
   377  				LatestMessages: latest,
   378  			})
   379  		}
   380  	}
   381  	if len(rsp.C2CMsg) > 4 {
   382  		c2cRsp := &msg.GetMessageResponse{}
   383  		if proto.Unmarshal(rsp.C2CMsg[4:], c2cRsp) == nil {
   384  			c.c2cMessageSyncProcessor(c2cRsp, pkt.Params)
   385  		}
   386  	}
   387  	return ret, nil
   388  }
   389  
   390  // OnlinePush.PbC2CMsgSync
   391  func decodeC2CSyncPacket(c *QQClient, pkt *network.Packet) (any, error) {
   392  	m := msg.PbPushMsg{}
   393  	if err := proto.Unmarshal(pkt.Payload, &m); err != nil {
   394  		return nil, err
   395  	}
   396  	_ = c.sendPacket(c.buildDeleteOnlinePushPacket(c.Uin, m.Svrip.Unwrap(), m.PushToken, pkt.SequenceId, nil))
   397  	c.commMsgProcessor(m.Msg, pkt.Params)
   398  	return nil, nil
   399  }
   400  
   401  func decodeMsgReadedResponse(_ *QQClient, pkt *network.Packet) (any, error) {
   402  	rsp := msg.PbMsgReadedReportResp{}
   403  	if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
   404  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   405  	}
   406  	if len(rsp.GrpReadReport) > 0 {
   407  		return rsp.GrpReadReport[0].Result.Unwrap() == 0, nil
   408  	}
   409  	return nil, nil
   410  }
   411  
   412  var loginNotifyLock sync.Mutex
   413  
   414  // StatSvc.SvcReqMSFLoginNotify
   415  func decodeLoginNotifyPacket(c *QQClient, pkt *network.Packet) (any, error) {
   416  	request := &jce.RequestPacket{}
   417  	request.ReadFrom(jce.NewJceReader(pkt.Payload))
   418  	data := &jce.RequestDataVersion2{}
   419  	data.ReadFrom(jce.NewJceReader(request.SBuffer))
   420  	reader := jce.NewJceReader(data.Map["SvcReqMSFLoginNotify"]["QQService.SvcReqMSFLoginNotify"][1:])
   421  	notify := &jce.SvcReqMSFLoginNotify{}
   422  	notify.ReadFrom(reader)
   423  	loginNotifyLock.Lock()
   424  	defer loginNotifyLock.Unlock()
   425  	if notify.Status == 1 {
   426  		found := false
   427  		for _, oc := range c.OnlineClients {
   428  			if oc.AppId == notify.AppId {
   429  				found = true
   430  			}
   431  		}
   432  		if !found {
   433  			allowedClients, _ := c.GetAllowedClients()
   434  			for _, ac := range allowedClients {
   435  				t := ac
   436  				if ac.AppId == notify.AppId {
   437  					c.OnlineClients = append(c.OnlineClients, t)
   438  					c.OtherClientStatusChangedEvent.dispatch(c, &OtherClientStatusChangedEvent{
   439  						Client: t,
   440  						Online: true,
   441  					})
   442  					break
   443  				}
   444  			}
   445  		}
   446  	}
   447  	if notify.Status == 2 {
   448  		rmi := -1
   449  		for i, oc := range c.OnlineClients {
   450  			if oc.AppId == notify.AppId {
   451  				rmi = i
   452  			}
   453  		}
   454  		if rmi != -1 {
   455  			rmc := c.OnlineClients[rmi]
   456  			c.OnlineClients = append(c.OnlineClients[:rmi], c.OnlineClients[rmi+1:]...)
   457  			c.OtherClientStatusChangedEvent.dispatch(c, &OtherClientStatusChangedEvent{
   458  				Client: rmc,
   459  				Online: false,
   460  			})
   461  		}
   462  	}
   463  	return nil, nil
   464  }