Skip to content

Commit

Permalink
feat: 实现拉取和缓存rkey (#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
pk5ls20 authored Sep 22, 2024
1 parent 7e6af37 commit 994d662
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 11 deletions.
30 changes: 30 additions & 0 deletions client/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,26 @@ func (c *QQClient) GetCachedMembersInfo(groupUin uint32) map[uint32]*entity.Grou
return c.cache.GetGroupMembers(groupUin)
}

// GetCachedRkeyInfo 获取指定类型的RKey信息(缓存)
func (c *QQClient) GetCachedRkeyInfo(rkeyType entity.RKeyType) *entity.RKeyInfo {
if c.cache.RkeyInfoCacheIsEmpty() || c.cache.RkeyInfoCacheIsExpired() {
if err := c.RefreshAllRkeyInfoCache(); err != nil {
return nil
}
}
return c.cache.GetRKeyInfo(rkeyType)
}

// GetCachedRkeyInfos 获取所有RKey信息(缓存)
func (c *QQClient) GetCachedRkeyInfos() map[entity.RKeyType]*entity.RKeyInfo {
if c.cache.RkeyInfoCacheIsEmpty() || c.cache.RkeyInfoCacheIsExpired() {
if err := c.RefreshAllRkeyInfoCache(); err != nil {
return nil
}
}
return c.cache.GetAllRkeyInfo()
}

// RefreshFriendCache 刷新好友缓存
func (c *QQClient) RefreshFriendCache() error {
friendsData, err := c.GetFriendsData()
Expand Down Expand Up @@ -143,6 +163,16 @@ func (c *QQClient) RefreshAllGroupsInfo() error {
return nil
}

// RefreshAllRkeyInfoCache 刷新RKey缓存
func (c *QQClient) RefreshAllRkeyInfoCache() error {
rkeyInfo, err := c.FetchRkey()
if err != nil {
return err
}
c.cache.RefreshAllRKeyInfo(rkeyInfo)
return nil
}

// GetFriendsData 获取好友列表数据
func (c *QQClient) GetFriendsData() (map[uint32]*entity.Friend, error) {
friendsData := make(map[uint32]*entity.Friend)
Expand Down
18 changes: 18 additions & 0 deletions client/entity/rkey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package entity

type (
RKeyType uint32
RKeyMap map[RKeyType]*RKeyInfo
)

const (
FriendRKey RKeyType = 10
GroupRKey RKeyType = 20
)

type RKeyInfo struct {
RKeyType RKeyType
RKey string
CreateTime uint64
ExpireTime uint64
}
41 changes: 32 additions & 9 deletions client/internal/cache/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@ package cache

import (
"reflect"
"time"
"unsafe"

"github.com/LagrangeDev/LagrangeGo/client/entity"
"github.com/RomiChan/syncx"
)

type cacheType uint32
type (
cacheType uint32
KeyType interface {
~uint32
}
)

const (
cacheTypeCache cacheType = 1 << iota
cacheTypeFriend
cacheTypeGroupInfo
cacheTypeGroupMember
cacheTypeRKey
)

func typenameof[T any]() string {
Expand All @@ -26,11 +33,13 @@ var cacheTypesMap = map[string]cacheType{
typenameof[entity.Friend](): cacheTypeFriend,
typenameof[entity.Group](): cacheTypeGroupInfo,
typenameof[entity.GroupMember](): cacheTypeGroupMember,
typenameof[entity.RKeyInfo](): cacheTypeRKey,
}

type Cache struct {
m syncx.Map[uint64, unsafe.Pointer]
refreshed syncx.Map[cacheType, struct{}]
m syncx.Map[uint64, unsafe.Pointer]
refreshed syncx.Map[cacheType, struct{}]
expiredTime syncx.Map[cacheType, int64]
}

func hasRefreshed[T any](c *Cache) bool {
Expand All @@ -42,7 +51,16 @@ func hasRefreshed[T any](c *Cache) bool {
return ok
}

func refreshAllCacheOf[T any](c *Cache, newcache map[uint32]*T) {
func hasExpired[T any](c *Cache) bool {
typ := cacheTypesMap[reflect.ValueOf((*T)(nil)).Type().String()]
if typ == 0 {
return false
}
v, ok := c.expiredTime.Load(typ)
return ok && v <= time.Now().Unix()
}

func refreshAllCacheOf[T any, K KeyType](c *Cache, newcache map[K]*T) {
typstr := reflect.ValueOf((*T)(nil)).Type().String()
typ := cacheTypesMap[typstr]
if typ == 0 {
Expand All @@ -53,7 +71,7 @@ func refreshAllCacheOf[T any](c *Cache, newcache map[uint32]*T) {
dellst := make([]uint64, 0, 64)
c.m.Range(func(k uint64, v unsafe.Pointer) bool {
if k&key != 0 {
if _, ok := newcache[uint32(k)]; !ok {
if _, ok := newcache[K(uint32(k))]; !ok {
dellst = append(dellst, k)
}
}
Expand All @@ -67,7 +85,12 @@ func refreshAllCacheOf[T any](c *Cache, newcache map[uint32]*T) {
}
}

func setCacheOf[T any](c *Cache, k uint32, v *T) {
func refreshAllExpiredCacheOf[T any, K KeyType](c *Cache, newcache map[K]*T, newextime int64) {
refreshAllCacheOf(c, newcache)
c.expiredTime.Store(cacheTypesMap[reflect.ValueOf((*T)(nil)).Type().String()], newextime)
}

func setCacheOf[T any, K KeyType](c *Cache, k K, v *T) {
typstr := reflect.ValueOf(v).Type().String()
typ := cacheTypesMap[typstr]
if typ == 0 {
Expand All @@ -77,7 +100,7 @@ func setCacheOf[T any](c *Cache, k uint32, v *T) {
c.m.Store(key, unsafe.Pointer(v))
}

func getCacheOf[T any](c *Cache, k uint32) (v *T, ok bool) {
func getCacheOf[T any, K KeyType](c *Cache, k K) (v *T, ok bool) {
typstr := reflect.ValueOf(v).Type().String()
typ := cacheTypesMap[typstr]
if typ == 0 {
Expand All @@ -91,15 +114,15 @@ func getCacheOf[T any](c *Cache, k uint32) (v *T, ok bool) {
return
}

func rangeCacheOf[T any](c *Cache, iter func(k uint32, v *T) bool) {
func rangeCacheOf[T any, K KeyType](c *Cache, iter func(k K, v *T) bool) {
typ := cacheTypesMap[reflect.ValueOf((*T)(nil)).Type().String()]
if typ == 0 {
return
}
key := uint64(typ) << 32
c.m.Range(func(k uint64, v unsafe.Pointer) bool {
if k&key != 0 {
return iter(uint32(k), (*T)(v))
return iter(K(uint32(k)), (*T)(v))
}
return true
})
Expand Down
25 changes: 25 additions & 0 deletions client/internal/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,22 @@ func (c *Cache) GetGroupMembers(groupUin uint32) map[uint32]*entity.GroupMember
return members
}

// GetRKeyInfo 获取指定类型的RKey信息
func (c *Cache) GetRKeyInfo(rkeyType entity.RKeyType) *entity.RKeyInfo {
v, _ := getCacheOf[entity.RKeyInfo](c, rkeyType)
return v
}

// GetAllRkeyInfo 获取所有RKey信息
func (c *Cache) GetAllRkeyInfo() entity.RKeyMap {
infos := make(map[entity.RKeyType]*entity.RKeyInfo, 2)
rangeCacheOf[entity.RKeyInfo](c, func(k entity.RKeyType, v *entity.RKeyInfo) bool {
infos[k] = v
return true
})
return infos
}

// FriendCacheIsEmpty 好友信息缓存是否为空
func (c *Cache) FriendCacheIsEmpty() bool {
return !hasRefreshed[entity.Friend](c)
Expand All @@ -118,3 +134,12 @@ func (c *Cache) GroupMemberCacheIsEmpty(groupUin uint32) bool {
func (c *Cache) GroupInfoCacheIsEmpty() bool {
return !hasRefreshed[entity.Group](c)
}

// RkeyInfoCacheIsEmpty RKey缓存是否为空
func (c *Cache) RkeyInfoCacheIsEmpty() bool {
return !hasRefreshed[entity.RKeyInfo](c)
}

func (c *Cache) RkeyInfoCacheIsExpired() bool {
return hasExpired[entity.RKeyInfo](c)
}
14 changes: 13 additions & 1 deletion client/internal/cache/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package cache

import (
"github.com/LagrangeDev/LagrangeGo/client/entity"
"math"
)

func (c *Cache) RefreshAll(friendCache map[uint32]*entity.Friend, groupCache map[uint32]*entity.Group, groupMemberCache map[uint32]map[uint32]*entity.GroupMember) {
func (c *Cache) RefreshAll(friendCache map[uint32]*entity.Friend, groupCache map[uint32]*entity.Group, groupMemberCache map[uint32]map[uint32]*entity.GroupMember, rkeyCache entity.RKeyMap) {
c.RefreshAllFriend(friendCache)
c.RefreshAllGroup(groupCache)
c.RefreshAllGroupMembers(groupMemberCache)
c.RefreshAllRKeyInfo(rkeyCache)
}

// RefreshFriend 刷新一个好友的缓存
Expand Down Expand Up @@ -59,3 +61,13 @@ func (c *Cache) RefreshGroup(group *entity.Group) {
func (c *Cache) RefreshAllGroup(groupCache map[uint32]*entity.Group) {
refreshAllCacheOf(c, groupCache)
}

// RefreshAllRKeyInfo 刷新所有RKey缓存
func (c *Cache) RefreshAllRKeyInfo(rkeyCache entity.RKeyMap) {
var expTime int64 = math.MaxInt64
for _, ext := range rkeyCache {
expTime = int64(ext.ExpireTime)
break
}
refreshAllExpiredCacheOf(c, rkeyCache, expTime)
}
15 changes: 14 additions & 1 deletion client/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,19 @@ func (c *QQClient) SetFriendRequest(accept bool, targetUid string) error {
return oidb2.ParseSetFriendRequestResp(resp)
}

// FetchRkey 获取Rkey
func (c *QQClient) FetchRkey() (entity.RKeyMap, error) {
pkt, err := oidb2.BuildFetchRKeyReq()
if err != nil {
return nil, err
}
resp, err := c.sendOidbPacketAndWait(pkt)
if err != nil {
return nil, err
}
return oidb2.ParseFetchRKeyResp(resp)
}

// FetchClientKey 获取ClientKey
func (c *QQClient) FetchClientKey() (string, error) {
pkt, err := oidb2.BuildFetchClientKeyReq()
Expand All @@ -483,7 +496,7 @@ func (c *QQClient) FetchClientKey() (string, error) {
return oidb2.ParseFetchClientKeyResp(resp)
}

// FetchCookies 获取cooikes
// FetchCookies 获取cookies
func (c *QQClient) FetchCookies(domains []string) ([]string, error) {
pkt, err := oidb2.BuildFetchCookieReq(domains)
if err != nil {
Expand Down
48 changes: 48 additions & 0 deletions client/packets/oidb/fetch_rkey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package oidb

import (
"github.com/LagrangeDev/LagrangeGo/client/entity"
"github.com/LagrangeDev/LagrangeGo/client/packets/pb/service/oidb"
)

func BuildFetchRKeyReq() (*OidbPacket, error) {
body := &oidb.NTV2RichMediaReq{
ReqHead: &oidb.MultiMediaReqHead{
Common: &oidb.CommonHead{
RequestId: 1,
Command: 202,
},
Scene: &oidb.SceneInfo{
RequestType: 2,
BusinessType: 1,
SceneType: 0,
},
Client: &oidb.ClientMeta{
AgentType: 2,
},
},
DownloadRKey: &oidb.DownloadRKeyReq{
Types: []int32{10, 20, 2},
},
}
return BuildOidbPacket(0x9067, 202, body, false, false)
}

func ParseFetchRKeyResp(data []byte) (entity.RKeyMap, error) {
resp := &oidb.NTV2RichMediaResp{}
_, err := ParseOidbPacket(data, resp)
if err != nil {
return nil, err
}
var rKeyInfo = entity.RKeyMap{}
for _, rkey := range resp.DownloadRKey.RKeys {
typ := entity.RKeyType(rkey.Type.Unwrap())
rKeyInfo[typ] = &entity.RKeyInfo{
RKey: rkey.Rkey,
RKeyType: typ,
CreateTime: uint64(rkey.RkeyCreateTime.Unwrap()),
ExpireTime: uint64(rkey.RkeyCreateTime.Unwrap()) + rkey.RkeyTtlSec,
}
}
return rKeyInfo, nil
}

0 comments on commit 994d662

Please sign in to comment.