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 }