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  }