github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/client/client.go (about) 1 package client 2 3 import ( 4 "crypto/md5" 5 "fmt" 6 "math/rand" 7 "net/netip" 8 "sort" 9 "strconv" 10 "sync" 11 "sync/atomic" 12 "time" 13 14 "github.com/pkg/errors" 15 16 "github.com/RomiChan/syncx" 17 18 "github.com/Mrs4s/MiraiGo/binary" 19 "github.com/Mrs4s/MiraiGo/client/internal/auth" 20 "github.com/Mrs4s/MiraiGo/client/internal/highway" 21 "github.com/Mrs4s/MiraiGo/client/internal/intern" 22 "github.com/Mrs4s/MiraiGo/client/internal/network" 23 "github.com/Mrs4s/MiraiGo/client/internal/oicq" 24 "github.com/Mrs4s/MiraiGo/client/pb/msg" 25 "github.com/Mrs4s/MiraiGo/message" 26 "github.com/Mrs4s/MiraiGo/utils" 27 ) 28 29 type QQClient struct { 30 Uin int64 31 PasswordMd5 [16]byte 32 33 stat Statistics 34 once sync.Once 35 36 // option 37 AllowSlider bool 38 UseFragmentMessage bool 39 40 // account info 41 Online atomic.Bool 42 Nickname string 43 Age uint16 44 Gender uint16 45 FriendList []*FriendInfo 46 GroupList []*GroupInfo 47 OnlineClients []*OtherClientInfo 48 QiDian *QiDianAccountInfo 49 GuildService *GuildService 50 51 // protocol public field 52 SequenceId atomic.Int32 53 SessionId []byte 54 TCP *network.TCPClient // todo: combine other protocol state into one struct 55 ConnectTime time.Time 56 57 transport *network.Transport 58 oicq *oicq.Codec 59 logger Logger 60 61 // internal state 62 handlers syncx.Map[uint16, *handlerInfo] 63 waiters syncx.Map[string, func(any, error)] 64 initServerOnce sync.Once 65 servers []netip.AddrPort 66 currServerIndex int 67 retryTimes int 68 alive bool 69 70 // session info 71 qwebSeq atomic.Int64 72 sig *auth.SigInfo 73 highwaySession *highway.Session 74 // pwdFlag bool 75 // timeDiff int64 76 77 // address 78 // otherSrvAddrs []string 79 // fileStorageInfo *jce.FileStoragePushFSSvcList 80 81 // event handles 82 eventHandlers eventHandlers 83 PrivateMessageEvent EventHandle[*message.PrivateMessage] 84 TempMessageEvent EventHandle[*TempMessageEvent] 85 GroupMessageEvent EventHandle[*message.GroupMessage] 86 SelfPrivateMessageEvent EventHandle[*message.PrivateMessage] 87 SelfGroupMessageEvent EventHandle[*message.GroupMessage] 88 GroupMuteEvent EventHandle[*GroupMuteEvent] 89 GroupMessageRecalledEvent EventHandle[*GroupMessageRecalledEvent] 90 FriendMessageRecalledEvent EventHandle[*FriendMessageRecalledEvent] 91 GroupJoinEvent EventHandle[*GroupInfo] 92 GroupLeaveEvent EventHandle[*GroupLeaveEvent] 93 GroupMemberJoinEvent EventHandle[*MemberJoinGroupEvent] 94 GroupMemberLeaveEvent EventHandle[*MemberLeaveGroupEvent] 95 MemberCardUpdatedEvent EventHandle[*MemberCardUpdatedEvent] 96 GroupNameUpdatedEvent EventHandle[*GroupNameUpdatedEvent] 97 GroupMemberPermissionChangedEvent EventHandle[*MemberPermissionChangedEvent] 98 GroupInvitedEvent EventHandle[*GroupInvitedRequest] 99 UserWantJoinGroupEvent EventHandle[*UserJoinGroupRequest] 100 NewFriendEvent EventHandle[*NewFriendEvent] 101 NewFriendRequestEvent EventHandle[*NewFriendRequest] 102 DisconnectedEvent EventHandle[*ClientDisconnectedEvent] 103 GroupNotifyEvent EventHandle[INotifyEvent] 104 FriendNotifyEvent EventHandle[INotifyEvent] 105 MemberSpecialTitleUpdatedEvent EventHandle[*MemberSpecialTitleUpdatedEvent] 106 GroupDigestEvent EventHandle[*GroupDigestEvent] 107 OtherClientStatusChangedEvent EventHandle[*OtherClientStatusChangedEvent] 108 OfflineFileEvent EventHandle[*OfflineFileEvent] 109 GroupDisbandEvent EventHandle[*GroupDisbandEvent] 110 DeleteFriendEvent EventHandle[*DeleteFriendEvent] 111 112 // message state 113 msgSvcCache *utils.Cache[unit] 114 lastC2CMsgTime int64 115 transCache *utils.Cache[unit] 116 groupSysMsgCache *GroupSystemMessages 117 msgBuilders syncx.Map[int32, *messageBuilder] 118 onlinePushCache *utils.Cache[unit] 119 heartbeatEnabled bool 120 requestPacketRequestID atomic.Int32 121 groupSeq atomic.Int32 122 friendSeq atomic.Int32 123 highwayApplyUpSeq atomic.Int32 124 125 groupListLock sync.Mutex 126 } 127 128 type QiDianAccountInfo struct { 129 MasterUin int64 130 ExtName string 131 CreateTime int64 132 133 bigDataReqAddrs []string 134 bigDataReqSession *bigDataSessionInfo 135 } 136 137 type handlerInfo struct { 138 fun func(i any, err error) 139 dynamic bool 140 params network.RequestParams 141 } 142 143 func (h *handlerInfo) getParams() network.RequestParams { 144 if h == nil { 145 return nil 146 } 147 return h.params 148 } 149 150 var decoders = map[string]func(*QQClient, *network.Packet) (any, error){ 151 "wtlogin.login": decodeLoginResponse, 152 "wtlogin.exchange_emp": decodeExchangeEmpResponse, 153 "wtlogin.trans_emp": decodeTransEmpResponse, 154 "StatSvc.register": decodeClientRegisterResponse, 155 "StatSvc.ReqMSFOffline": decodeMSFOfflinePacket, 156 "MessageSvc.PushNotify": decodeSvcNotify, 157 "OnlinePush.ReqPush": decodeOnlinePushReqPacket, 158 "OnlinePush.PbPushTransMsg": decodeOnlinePushTransPacket, 159 "OnlinePush.SidTicketExpired": decodeSidExpiredPacket, 160 "ConfigPushSvc.PushReq": decodePushReqPacket, 161 "MessageSvc.PbGetMsg": decodeMessageSvcPacket, 162 "MessageSvc.PushForceOffline": decodeForceOfflinePacket, 163 "PbMessageSvc.PbMsgWithDraw": decodeMsgWithDrawResponse, 164 "friendlist.getFriendGroupList": decodeFriendGroupListResponse, 165 "friendlist.delFriend": decodeFriendDeleteResponse, 166 "friendlist.GetTroopListReqV2": decodeGroupListResponse, 167 "friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse, 168 "group_member_card.get_group_member_card_info": decodeGroupMemberInfoResponse, 169 "LongConn.OffPicUp": decodeOffPicUpResponse, 170 "ProfileService.Pb.ReqSystemMsgNew.Group": decodeSystemMsgGroupPacket, 171 "ProfileService.Pb.ReqSystemMsgNew.Friend": decodeSystemMsgFriendPacket, 172 "OidbSvc.0xd79": decodeWordSegmentation, 173 "OidbSvc.0x990": decodeTranslateResponse, 174 "SummaryCard.ReqSummaryCard": decodeSummaryCardResponse, 175 } 176 177 // NewClient create new qq client 178 func NewClient(uin int64, password string) *QQClient { 179 return NewClientMd5(uin, md5.Sum([]byte(password))) 180 } 181 182 func NewClientEmpty() *QQClient { 183 return NewClientMd5(0, [16]byte{}) 184 } 185 186 func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { 187 cli := &QQClient{ 188 Uin: uin, 189 PasswordMd5: passwordMd5, 190 AllowSlider: true, 191 TCP: &network.TCPClient{}, 192 sig: &auth.SigInfo{ 193 OutPacketSessionID: []byte{0x02, 0xB0, 0x5B, 0x8B}, 194 }, 195 msgSvcCache: utils.NewCache[unit](time.Second * 15), 196 transCache: utils.NewCache[unit](time.Second * 15), 197 onlinePushCache: utils.NewCache[unit](time.Second * 15), 198 alive: true, 199 highwaySession: new(highway.Session), 200 } 201 202 cli.transport = &network.Transport{Sig: cli.sig} 203 cli.oicq = oicq.NewCodec(cli.Uin) 204 { // init atomic values 205 cli.SequenceId.Store(int32(rand.Intn(100000))) 206 cli.requestPacketRequestID.Store(1921334513) 207 cli.groupSeq.Store(int32(rand.Intn(20000))) 208 cli.friendSeq.Store(22911) 209 cli.highwayApplyUpSeq.Store(77918) 210 } 211 cli.highwaySession.Uin = strconv.FormatInt(cli.Uin, 10) 212 cli.GuildService = &GuildService{c: cli} 213 cli.TCP.PlannedDisconnect(cli.plannedDisconnect) 214 cli.TCP.UnexpectedDisconnect(cli.unexpectedDisconnect) 215 return cli 216 } 217 218 func (c *QQClient) version() *auth.AppVersion { 219 return c.transport.Version 220 } 221 222 func (c *QQClient) Device() *DeviceInfo { 223 return c.transport.Device 224 } 225 226 func (c *QQClient) UseDevice(info *auth.Device) { 227 c.transport.Version = info.Protocol.Version() 228 c.transport.Device = info 229 c.highwaySession.AppID = int32(c.version().AppId) 230 c.sig.Ksid = []byte(fmt.Sprintf("|%s|A8.2.7.27f6ea96", info.IMEI)) 231 } 232 233 func (c *QQClient) Release() { 234 if c.Online.Load() { 235 c.Disconnect() 236 } 237 c.alive = false 238 } 239 240 // Login send login request 241 func (c *QQClient) Login() (*LoginResponse, error) { 242 if c.Online.Load() { 243 return nil, ErrAlreadyOnline 244 } 245 err := c.connect() 246 if err != nil { 247 return nil, err 248 } 249 rsp, err := c.sendAndWait(c.buildLoginPacket()) 250 if err != nil { 251 c.Disconnect() 252 return nil, err 253 } 254 l := rsp.(LoginResponse) 255 if l.Success { 256 err = c.init(false) 257 } 258 return &l, err 259 } 260 261 func (c *QQClient) TokenLogin(token []byte) error { 262 if c.Online.Load() { 263 return ErrAlreadyOnline 264 } 265 err := c.connect() 266 if err != nil { 267 return err 268 } 269 { 270 r := binary.NewReader(token) 271 c.Uin = r.ReadInt64() 272 c.sig.D2 = r.ReadBytesShort() 273 c.sig.D2Key = r.ReadBytesShort() 274 c.sig.TGT = r.ReadBytesShort() 275 c.sig.SrmToken = r.ReadBytesShort() 276 c.sig.T133 = r.ReadBytesShort() 277 c.sig.EncryptedA1 = r.ReadBytesShort() 278 c.oicq.WtSessionTicketKey = r.ReadBytesShort() 279 c.sig.OutPacketSessionID = r.ReadBytesShort() 280 // SystemDeviceInfo.TgtgtKey = r.ReadBytesShort() 281 c.Device().TgtgtKey = r.ReadBytesShort() 282 } 283 _, err = c.sendAndWait(c.buildRequestChangeSigPacket(true)) 284 if err != nil { 285 return err 286 } 287 return c.init(true) 288 } 289 290 func (c *QQClient) FetchQRCode() (*QRCodeLoginResponse, error) { 291 return c.FetchQRCodeCustomSize(3, 4, 2) 292 } 293 294 func (c *QQClient) FetchQRCodeCustomSize(size, margin, ecLevel uint32) (*QRCodeLoginResponse, error) { 295 if c.Online.Load() { 296 return nil, ErrAlreadyOnline 297 } 298 err := c.connect() 299 if err != nil { 300 return nil, err 301 } 302 i, err := c.sendAndWait(c.buildQRCodeFetchRequestPacket(size, margin, ecLevel)) 303 if err != nil { 304 return nil, errors.Wrap(err, "fetch qrcode error") 305 } 306 return i.(*QRCodeLoginResponse), nil 307 } 308 309 func (c *QQClient) QueryQRCodeStatus(sig []byte) (*QRCodeLoginResponse, error) { 310 i, err := c.sendAndWait(c.buildQRCodeResultQueryRequestPacket(sig)) 311 if err != nil { 312 return nil, errors.Wrap(err, "query result error") 313 } 314 return i.(*QRCodeLoginResponse), nil 315 } 316 317 func (c *QQClient) QRCodeLogin(info *QRCodeLoginInfo) (*LoginResponse, error) { 318 i, err := c.sendAndWait(c.buildQRCodeLoginPacket(info.tmpPwd, info.tmpNoPicSig, info.tgtQR)) 319 if err != nil { 320 return nil, errors.Wrap(err, "qrcode login error") 321 } 322 rsp := i.(LoginResponse) 323 if rsp.Success { 324 err = c.init(false) 325 } 326 return &rsp, err 327 } 328 329 // SubmitCaptcha send captcha to server 330 func (c *QQClient) SubmitCaptcha(result string, sign []byte) (*LoginResponse, error) { 331 seq, packet := c.buildCaptchaPacket(result, sign) 332 rsp, err := c.sendAndWait(seq, packet) 333 if err != nil { 334 c.Disconnect() 335 return nil, err 336 } 337 l := rsp.(LoginResponse) 338 if l.Success { 339 err = c.init(false) 340 } 341 return &l, err 342 } 343 344 func (c *QQClient) SubmitTicket(ticket string) (*LoginResponse, error) { 345 seq, packet := c.buildTicketSubmitPacket(ticket) 346 rsp, err := c.sendAndWait(seq, packet) 347 if err != nil { 348 c.Disconnect() 349 return nil, err 350 } 351 l := rsp.(LoginResponse) 352 if l.Success { 353 err = c.init(false) 354 } 355 return &l, err 356 } 357 358 func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) { 359 rsp, err := c.sendAndWait(c.buildSMSCodeSubmitPacket(code)) 360 if err != nil { 361 c.Disconnect() 362 return nil, err 363 } 364 l := rsp.(LoginResponse) 365 if l.Success { 366 err = c.init(false) 367 } 368 return &l, err 369 } 370 371 func (c *QQClient) RequestSMS() bool { 372 rsp, err := c.sendAndWait(c.buildSMSRequestPacket()) 373 if err != nil { 374 c.error("request sms error: %v", err) 375 return false 376 } 377 return rsp.(LoginResponse).Error == SMSNeededError 378 } 379 380 func (c *QQClient) init(tokenLogin bool) error { 381 if len(c.sig.G) == 0 { 382 c.warning("device lock is disabled. HTTP API may fail.") 383 } 384 c.highwaySession.Uin = strconv.FormatInt(c.Uin, 10) 385 if err := c.registerClient(); err != nil { 386 return errors.Wrap(err, "register error") 387 } 388 if tokenLogin { 389 notify := make(chan struct{}, 2) 390 d := c.waitPacket("StatSvc.ReqMSFOffline", func(i any, err error) { 391 notify <- struct{}{} 392 }) 393 d2 := c.waitPacket("MessageSvc.PushForceOffline", func(i any, err error) { 394 notify <- struct{}{} 395 }) 396 select { 397 case <-notify: 398 d() 399 d2() 400 return errors.New("token failed") 401 case <-time.After(time.Second): 402 d() 403 d2() 404 } 405 } 406 c.groupSysMsgCache, _ = c.GetGroupSystemMessages() 407 if !c.heartbeatEnabled { 408 go c.doHeartbeat() 409 } 410 _ = c.RefreshStatus() 411 if c.version().Protocol == auth.QiDian { 412 _, _ = c.sendAndWait(c.buildLoginExtraPacket()) // 小登录 413 _, _ = c.sendAndWait(c.buildConnKeyRequestPacket()) // big data key 如果等待 config push 的话时间来不及 414 } 415 seq, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix()) 416 _, _ = c.sendAndWait(seq, pkt, network.RequestParams{"used_reg_proxy": true, "init": true}) 417 c.syncChannelFirstView() 418 return nil 419 } 420 421 func (c *QQClient) GenToken() []byte { 422 return binary.NewWriterF(func(w *binary.Writer) { 423 w.WriteUInt64(uint64(c.Uin)) 424 w.WriteBytesShort(c.sig.D2) 425 w.WriteBytesShort(c.sig.D2Key) 426 w.WriteBytesShort(c.sig.TGT) 427 w.WriteBytesShort(c.sig.SrmToken) 428 w.WriteBytesShort(c.sig.T133) 429 w.WriteBytesShort(c.sig.EncryptedA1) 430 w.WriteBytesShort(c.oicq.WtSessionTicketKey) 431 w.WriteBytesShort(c.sig.OutPacketSessionID) 432 w.WriteBytesShort(c.Device().TgtgtKey) 433 }) 434 } 435 436 func (c *QQClient) SetOnlineStatus(s UserOnlineStatus) { 437 if s < 1000 { 438 _, _ = c.sendAndWait(c.buildStatusSetPacket(int32(s), 0)) 439 return 440 } 441 _, _ = c.sendAndWait(c.buildStatusSetPacket(11, int32(s))) 442 } 443 444 func (c *QQClient) GetWordSegmentation(text string) ([]string, error) { 445 rsp, err := c.sendAndWait(c.buildWordSegmentationPacket([]byte(text))) 446 if err != nil { 447 return nil, err 448 } 449 if data, ok := rsp.([][]byte); ok { 450 var ret []string 451 for _, val := range data { 452 ret = append(ret, string(val)) 453 } 454 return ret, nil 455 } 456 return nil, errors.New("decode error") 457 } 458 459 func (c *QQClient) GetSummaryInfo(target int64) (*SummaryCardInfo, error) { 460 rsp, err := c.sendAndWait(c.buildSummaryCardRequestPacket(target)) 461 if err != nil { 462 return nil, err 463 } 464 return rsp.(*SummaryCardInfo), nil 465 } 466 467 // ReloadFriendList refresh QQClient.FriendList field via GetFriendList() 468 func (c *QQClient) ReloadFriendList() error { 469 rsp, err := c.GetFriendList() 470 if err != nil { 471 return err 472 } 473 c.FriendList = rsp.List 474 return nil 475 } 476 477 // GetFriendList 478 // 当使用普通QQ时: 请求好友列表 479 // 当使用企点QQ时: 请求外部联系人列表 480 func (c *QQClient) GetFriendList() (*FriendListResponse, error) { 481 if c.version().Protocol == auth.QiDian { 482 rsp, err := c.getQiDianAddressDetailList() 483 if err != nil { 484 return nil, err 485 } 486 return &FriendListResponse{TotalCount: int32(len(rsp)), List: rsp}, nil 487 } 488 curFriendCount := 0 489 r := &FriendListResponse{} 490 for { 491 rsp, err := c.sendAndWait(c.buildFriendGroupListRequestPacket(int16(curFriendCount), 150, 0, 0)) 492 if err != nil { 493 return nil, err 494 } 495 list := rsp.(*FriendListResponse) 496 r.TotalCount = list.TotalCount 497 r.List = append(r.List, list.List...) 498 curFriendCount += len(list.List) 499 if int32(len(r.List)) >= r.TotalCount { 500 break 501 } 502 } 503 return r, nil 504 } 505 506 func (c *QQClient) SendGroupPoke(groupCode, target int64) { 507 _, _ = c.sendAndWait(c.buildGroupPokePacket(groupCode, target)) 508 } 509 510 func (c *QQClient) SendFriendPoke(target int64) { 511 _, _ = c.sendAndWait(c.buildFriendPokePacket(target)) 512 } 513 514 func (c *QQClient) ReloadGroupList() error { 515 c.groupListLock.Lock() 516 defer c.groupListLock.Unlock() 517 list, err := c.GetGroupList() 518 if err != nil { 519 return err 520 } 521 c.GroupList = list 522 return nil 523 } 524 525 func (c *QQClient) GetGroupList() ([]*GroupInfo, error) { 526 rsp, err := c.sendAndWait(c.buildGroupListRequestPacket(EmptyBytes)) 527 if err != nil { 528 return nil, err 529 } 530 interner := intern.NewStringInterner() 531 r := rsp.([]*GroupInfo) 532 wg := sync.WaitGroup{} 533 batch := 50 534 for i := 0; i < len(r); i += batch { 535 k := i + batch 536 if k > len(r) { 537 k = len(r) 538 } 539 wg.Add(k - i) 540 for j := i; j < k; j++ { 541 go func(g *GroupInfo, wg *sync.WaitGroup) { 542 defer wg.Done() 543 m, err := c.getGroupMembers(g, interner) 544 if err != nil { 545 return 546 } 547 g.Members = m 548 g.Name = interner.Intern(g.Name) 549 }(r[j], &wg) 550 } 551 wg.Wait() 552 } 553 return r, nil 554 } 555 556 func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]*GroupMemberInfo, error) { 557 interner := intern.NewStringInterner() 558 return c.getGroupMembers(group, interner) 559 } 560 561 func (c *QQClient) getGroupMembers(group *GroupInfo, interner *intern.StringInterner) ([]*GroupMemberInfo, error) { 562 var nextUin int64 563 var list []*GroupMemberInfo 564 for { 565 data, err := c.sendAndWait(c.buildGroupMemberListRequestPacket(group.Uin, group.Code, nextUin)) 566 if err != nil { 567 return nil, err 568 } 569 if data == nil { 570 return nil, errors.New("group members list is unavailable: rsp is nil") 571 } 572 rsp := data.(*groupMemberListResponse) 573 nextUin = rsp.NextUin 574 for _, m := range rsp.list { 575 m.Group = group 576 if m.Uin == group.OwnerUin { 577 m.Permission = Owner 578 } 579 m.CardName = interner.Intern(m.CardName) 580 m.Nickname = interner.Intern(m.Nickname) 581 m.SpecialTitle = interner.Intern(m.SpecialTitle) 582 } 583 list = append(list, rsp.list...) 584 if nextUin == 0 { 585 sort.Slice(list, func(i, j int) bool { 586 return list[i].Uin < list[j].Uin 587 }) 588 return list, nil 589 } 590 } 591 } 592 593 func (c *QQClient) GetMemberInfo(groupCode, memberUin int64) (*GroupMemberInfo, error) { 594 info, err := c.sendAndWait(c.buildGroupMemberInfoRequestPacket(groupCode, memberUin)) 595 if err != nil { 596 return nil, err 597 } 598 return info.(*GroupMemberInfo), nil 599 } 600 601 func (c *QQClient) FindFriend(uin int64) *FriendInfo { 602 if uin == c.Uin { 603 return &FriendInfo{ 604 Uin: uin, 605 Nickname: c.Nickname, 606 } 607 } 608 for _, t := range c.FriendList { 609 f := t 610 if f.Uin == uin { 611 return f 612 } 613 } 614 return nil 615 } 616 617 func (c *QQClient) DeleteFriend(uin int64) error { 618 if c.FindFriend(uin) == nil { 619 return errors.New("friend not found") 620 } 621 _, err := c.sendAndWait(c.buildFriendDeletePacket(uin)) 622 return errors.Wrap(err, "delete friend error") 623 } 624 625 func (c *QQClient) FindGroupByUin(uin int64) *GroupInfo { 626 for _, g := range c.GroupList { 627 f := g 628 if f.Uin == uin { 629 return f 630 } 631 } 632 return nil 633 } 634 635 func (c *QQClient) FindGroup(code int64) *GroupInfo { 636 for _, g := range c.GroupList { 637 if g.Code == code { 638 return g 639 } 640 } 641 return nil 642 } 643 644 func (c *QQClient) SolveGroupJoinRequest(i any, accept, block bool, reason string) { 645 if accept { 646 block = false 647 reason = "" 648 } 649 650 switch req := i.(type) { 651 case *UserJoinGroupRequest: 652 _, pkt := c.buildSystemMsgGroupActionPacket(req.RequestId, req.RequesterUin, req.GroupCode, func() int32 { 653 if req.Suspicious { 654 return 2 655 } else { 656 return 1 657 } 658 }(), false, accept, block, reason) 659 _ = c.sendPacket(pkt) 660 case *GroupInvitedRequest: 661 _, pkt := c.buildSystemMsgGroupActionPacket(req.RequestId, req.InvitorUin, req.GroupCode, 1, true, accept, block, reason) 662 _ = c.sendPacket(pkt) 663 } 664 } 665 666 func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) { 667 _, pkt := c.buildSystemMsgFriendActionPacket(req.RequestId, req.RequesterUin, accept) 668 _ = c.sendPacket(pkt) 669 } 670 671 func (c *QQClient) getSKey() string { 672 if c.sig.SKeyExpiredTime < time.Now().Unix() && len(c.sig.G) > 0 { 673 c.debug("skey expired. refresh...") 674 _, _ = c.sendAndWait(c.buildRequestTgtgtNopicsigPacket()) 675 } 676 return string(c.sig.SKey) 677 } 678 679 func (c *QQClient) getCookies() string { 680 return fmt.Sprintf("uin=o%d; skey=%s;", c.Uin, c.getSKey()) 681 } 682 683 func (c *QQClient) getCookiesWithDomain(domain string) string { 684 cookie := c.getCookies() 685 686 if psKey, ok := c.sig.PsKeyMap[domain]; ok { 687 return fmt.Sprintf("%s p_uin=o%d; p_skey=%s;", cookie, c.Uin, psKey) 688 } else { 689 return cookie 690 } 691 } 692 693 func (c *QQClient) getCSRFToken() int { 694 accu := 5381 695 for _, b := range []byte(c.getSKey()) { 696 accu = accu + (accu << 5) + int(b) 697 } 698 return 2147483647 & accu 699 } 700 701 func (c *QQClient) editMemberCard(groupCode, memberUin int64, card string) { 702 _, _ = c.sendAndWait(c.buildEditGroupTagPacket(groupCode, memberUin, card)) 703 } 704 705 func (c *QQClient) editMemberSpecialTitle(groupCode, memberUin int64, title string) { 706 _, _ = c.sendAndWait(c.buildEditSpecialTitlePacket(groupCode, memberUin, title)) 707 } 708 709 func (c *QQClient) setGroupAdmin(groupCode, memberUin int64, flag bool) { 710 _, _ = c.sendAndWait(c.buildGroupAdminSetPacket(groupCode, memberUin, flag)) 711 } 712 713 func (c *QQClient) updateGroupName(groupCode int64, newName string) { 714 _, _ = c.sendAndWait(c.buildGroupNameUpdatePacket(groupCode, newName)) 715 } 716 717 func (c *QQClient) groupMuteAll(groupCode int64, mute bool) { 718 _, _ = c.sendAndWait(c.buildGroupMuteAllPacket(groupCode, mute)) 719 } 720 721 func (c *QQClient) groupMute(groupCode, memberUin int64, time uint32) { 722 _, _ = c.sendAndWait(c.buildGroupMutePacket(groupCode, memberUin, time)) 723 } 724 725 func (c *QQClient) quitGroup(groupCode int64) { 726 _, _ = c.sendAndWait(c.buildQuitGroupPacket(groupCode)) 727 } 728 729 func (c *QQClient) KickGroupMembers(groupCode int64, msg string, block bool, memberUins ...int64) { 730 _, _ = c.sendAndWait(c.buildGroupKickPacket(groupCode, msg, block, memberUins...)) 731 } 732 733 func (g *GroupInfo) removeMember(uin int64) { 734 g.Update(func(info *GroupInfo) { 735 i := sort.Search(len(info.Members), func(i int) bool { 736 return info.Members[i].Uin >= uin 737 }) 738 if i >= len(info.Members) || info.Members[i].Uin != uin { // not found 739 return 740 } 741 info.Members = append(info.Members[:i], info.Members[i+1:]...) 742 }) 743 } 744 745 func (c *QQClient) setGroupAnonymous(groupCode int64, enable bool) { 746 _, _ = c.sendAndWait(c.buildSetGroupAnonymous(groupCode, enable)) 747 } 748 749 // UpdateProfile 修改个人资料 750 func (c *QQClient) UpdateProfile(profile ProfileDetailUpdate) { 751 _, _ = c.sendAndWait(c.buildUpdateProfileDetailPacket(profile)) 752 } 753 754 func (c *QQClient) SetCustomServer(servers []netip.AddrPort) { 755 c.servers = append(servers, c.servers...) 756 } 757 758 func (c *QQClient) registerClient() error { 759 _, err := c.sendAndWait(c.buildClientRegisterPacket()) 760 if err == nil { 761 c.Online.Store(true) 762 } 763 return err 764 } 765 766 func (c *QQClient) nextSeq() uint16 { 767 seq := c.SequenceId.Add(1) 768 if seq > 1000000 { 769 seq = int32(rand.Intn(100000)) + 60000 770 c.SequenceId.Store(seq) 771 } 772 return uint16(seq) 773 } 774 775 func (c *QQClient) nextPacketSeq() int32 { 776 return c.requestPacketRequestID.Add(2) 777 } 778 779 func (c *QQClient) nextGroupSeq() int32 { 780 return c.groupSeq.Add(2) 781 } 782 783 func (c *QQClient) nextFriendSeq() int32 { 784 return c.friendSeq.Add(1) 785 } 786 787 func (c *QQClient) nextQWebSeq() int64 { 788 return c.qwebSeq.Add(1) 789 } 790 791 func (c *QQClient) nextHighwayApplySeq() int32 { 792 return c.highwayApplyUpSeq.Add(2) 793 } 794 795 func (c *QQClient) doHeartbeat() { 796 c.heartbeatEnabled = true 797 times := 0 798 for c.Online.Load() { 799 time.Sleep(time.Second * 30) 800 seq := c.nextSeq() 801 req := network.Request{ 802 Type: network.RequestTypeLogin, 803 EncryptType: network.EncryptTypeNoEncrypt, 804 SequenceID: int32(seq), 805 Uin: c.Uin, 806 CommandName: "Heartbeat.Alive", 807 Body: EmptyBytes, 808 } 809 packet := c.transport.PackPacket(&req) 810 _, err := c.sendAndWait(seq, packet) 811 if errors.Is(err, network.ErrConnectionClosed) { 812 continue 813 } 814 times++ 815 if times >= 7 { 816 _ = c.registerClient() 817 times = 0 818 } 819 } 820 c.heartbeatEnabled = false 821 }