diff --git a/client/auth/app.go b/client/auth/app.go index bc7a0a09..c50751fd 100644 --- a/client/auth/app.go +++ b/client/auth/app.go @@ -97,6 +97,27 @@ var AppList = map[string]map[string]*AppInfo{ SubSigmap: 0, NTLoginType: 1, }, + + "3.2.19-39038": { + OS: "Linux", + Kernel: "Linux", + VendorOS: "linux", + + CurrentVersion: "3.2.19-39038", + BuildVersion: 39038, + MiscBitmap: 32764, + PTVersion: "2.0.0", + PTOSVersion: 19, + PackageName: "com.tencent.qq", + WTLoginSDK: "nt.wtlogin.0.0.1", + AppID: 1600001615, + SubAppID: 537313942, + AppIDQrcode: 537313942, + AppClientVersion: 39038, + MainSigmap: 169742560, + SubSigmap: 0, + NTLoginType: 1, + }, }, "macos": { diff --git a/client/base.go b/client/base.go index 2009eb90..45ba00e3 100644 --- a/client/base.go +++ b/client/base.go @@ -137,6 +137,11 @@ func (c *QQClient) AddSignServer(signServers ...string) { c.signProvider.AddSignServer(signServers...) } +// GetSignServer 获得所有签名服务器 +func (c *QQClient) GetSignServer() []string { + return c.signProvider.GetSignServer() +} + // AddSignHeader 设置签名服务器签名时的额外http header func (c *QQClient) AddSignHeader(header map[string]string) { if c.signProvider == nil { diff --git a/client/cache.go b/client/cache.go index 87796e7b..a608b19c 100644 --- a/client/cache.go +++ b/client/cache.go @@ -1,16 +1,25 @@ package client import ( + "strconv" "time" "github.com/LagrangeDev/LagrangeGo/client/entity" ) +var selfUin string + // GetUID 获取缓存中对应uin的uid func (c *QQClient) GetUID(uin uint32, groupUin ...uint32) string { if uin == 0 { return "" } + if uin == c.Uin { + if selfUin == "" { + selfUin = strconv.FormatUint(uint64(uin), 10) + } + return selfUin + } if len(groupUin) == 0 && c.cache.FriendCacheIsEmpty() { if err := c.RefreshFriendCache(); err != nil { return "" diff --git a/client/packets/oidb/fetch_friends.go b/client/packets/oidb/fetch_friends.go index cebbaa82..47dd4e06 100644 --- a/client/packets/oidb/fetch_friends.go +++ b/client/packets/oidb/fetch_friends.go @@ -11,10 +11,8 @@ func BuildFetchFriendsReq(token uint32) (*Packet, error) { body := oidb.OidbSvcTrpcTcp0XFD4_1{ Field2: 300, Field4: 0, - NextUin: &oidb.OidbSvcTrpcTcp0XFD4_1Uin{ - Uin: token, - }, Field6: 1, + Field7: 2147483647, Body: []*oidb.OidbSvcTrpcTcp0XFD4_1Body{{ Type: 1, Number: &oidb.OidbNumber{Numbers: []uint32{103, 102, 20002, 27394}}, @@ -25,6 +23,11 @@ func BuildFetchFriendsReq(token uint32) (*Packet, error) { Field10002: []uint32{13578, 13579, 13573, 13572, 13568}, Field10003: 4051, } + if token != 0 { + body.NextUin = &oidb.OidbSvcTrpcTcp0XFD4_1Uin{ + Uin: token, + } + } /* * OidbNumber里面的东西代表你想要拿到的Property,这些Property将会在返回的数据里面的Preserve的Field, * 102:个性签名 diff --git a/client/packets/pb/service/oidb/OidbSvcTrpcTcp0xFD4_1.pb.go b/client/packets/pb/service/oidb/OidbSvcTrpcTcp0xFD4_1.pb.go index ca4e4819..4ba78e52 100644 --- a/client/packets/pb/service/oidb/OidbSvcTrpcTcp0xFD4_1.pb.go +++ b/client/packets/pb/service/oidb/OidbSvcTrpcTcp0xFD4_1.pb.go @@ -9,6 +9,7 @@ type OidbSvcTrpcTcp0XFD4_1 struct { Field4 uint32 `protobuf:"varint,4,opt"` // 0 NextUin *OidbSvcTrpcTcp0XFD4_1Uin `protobuf:"bytes,5,opt"` Field6 uint32 `protobuf:"varint,6,opt"` // 1 + Field7 uint32 `protobuf:"varint,7,opt"` // 2,147,483,647 Body []*OidbSvcTrpcTcp0XFD4_1Body `protobuf:"bytes,10001,rep"` Field10002 []uint32 `protobuf:"varint,10002,rep"` // [13578, 13579, 13573, 13572, 13568] Field10003 uint32 `protobuf:"varint,10003,opt"` diff --git a/client/packets/pb/service/oidb/OidbSvcTrpcTcp0xFD4_1.proto b/client/packets/pb/service/oidb/OidbSvcTrpcTcp0xFD4_1.proto index 56e61821..f1a189a1 100644 --- a/client/packets/pb/service/oidb/OidbSvcTrpcTcp0xFD4_1.proto +++ b/client/packets/pb/service/oidb/OidbSvcTrpcTcp0xFD4_1.proto @@ -10,6 +10,7 @@ message OidbSvcTrpcTcp0xFD4_1 { uint32 Field4 = 4; // 0 OidbSvcTrpcTcp0xFD4_1Uin NextUin = 5; uint32 Field6 = 6; // 1 + uint32 Field7 = 7; // 2,147,483,647 repeated OidbSvcTrpcTcp0xFD4_1Body Body = 10001; repeated uint32 Field10002 = 10002; // [13578, 13579, 13573, 13572, 13568] uint32 Field10003 = 10003; diff --git a/main.go b/main.go index 8c03faa3..012dded4 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ var ( ) func main() { - appInfo := auth.AppList["linux"]["3.2.15-30366"] + appInfo := auth.AppList["linux"]["3.2.19-39038"] deviceInfo := &auth.DeviceInfo{ GUID: "cfcd208495d565ef66e7dff9f98764da", DeviceName: "Lagrange-DCFCD07E", @@ -36,7 +36,7 @@ func main() { qqclient := client.NewClient(0, "") qqclient.SetLogger(protocolLogger{}) qqclient.UseVersion(appInfo) - qqclient.AddSignServer("https://sign.lagrangecore.org/api/sign/30366") + qqclient.AddSignServer("https://sign.lagrangecore.org/api/sign/39038") qqclient.UseDevice(deviceInfo) data, err := os.ReadFile("sig.bin") if err != nil { diff --git a/message/json.go b/message/json.go new file mode 100644 index 00000000..a4f02747 --- /dev/null +++ b/message/json.go @@ -0,0 +1,381 @@ +package message + +import ( + "encoding/json" + "fmt" +) + +type elementJSON struct { + Type ElementType `json:"type"` + Data json.RawMessage `json:"data"` +} + +func parseJSONMessageElement(elemJSON elementJSON) (IMessageElement, error) { + var elem IMessageElement + switch elemJSON.Type { + case Text: + var textElem *TextElement + if err := json.Unmarshal(elemJSON.Data, &textElem); err != nil { + return elem, fmt.Errorf("解析TextElement失败: %w", err) + } + elem = textElem + case Image: + var imgElem *ImageElement + if err := json.Unmarshal(elemJSON.Data, &imgElem); err != nil { + return elem, fmt.Errorf("解析ImageElement失败: %w", err) + } + elem = imgElem + case Face: + var faceElem *FaceElement + if err := json.Unmarshal(elemJSON.Data, &faceElem); err != nil { + return elem, fmt.Errorf("解析FaceElement失败: %w", err) + } + elem = faceElem + case At: + var atElem *AtElement + if err := json.Unmarshal(elemJSON.Data, &atElem); err != nil { + return elem, fmt.Errorf("解析AtElement失败: %w", err) + } + elem = atElem + case Reply: + var replyElem *ReplyElement + if err := json.Unmarshal(elemJSON.Data, &replyElem); err != nil { + return elem, fmt.Errorf("解析ReplyElement失败: %w", err) + } + elem = replyElem + case Service: + var serviceElem *XMLElement + if err := json.Unmarshal(elemJSON.Data, &serviceElem); err != nil { + return elem, fmt.Errorf("解析XMLElement失败: %w", err) + } + elem = serviceElem + case Forward: + var forwardElem *ForwardMessage + if err := json.Unmarshal(elemJSON.Data, &forwardElem); err != nil { + return elem, fmt.Errorf("解析ForwardMessage失败: %w", err) + } + elem = forwardElem + case File: + var fileElem *FileElement + if err := json.Unmarshal(elemJSON.Data, &fileElem); err != nil { + return elem, fmt.Errorf("解析FileElement失败: %w", err) + } + elem = fileElem + case Voice: + var voiceElem *VoiceElement + if err := json.Unmarshal(elemJSON.Data, &voiceElem); err != nil { + return elem, fmt.Errorf("解析VoiceElement失败: %w", err) + } + elem = voiceElem + case Video: + var videoElem *ShortVideoElement + if err := json.Unmarshal(elemJSON.Data, &videoElem); err != nil { + return elem, fmt.Errorf("解析ShortVideoElement失败: %w", err) + } + elem = videoElem + case LightApp: + var lightAppElem *LightAppElement + if err := json.Unmarshal(elemJSON.Data, &lightAppElem); err != nil { + return elem, fmt.Errorf("解析LightAppElementElement失败: %w", err) + } + elem = lightAppElem + case RedBag: + return elem, fmt.Errorf("未实现的元素类型: %v", elemJSON.Type) + case MarketFace: + var marketFaceElem *MarketFaceElement + if err := json.Unmarshal(elemJSON.Data, &marketFaceElem); err != nil { + return elem, fmt.Errorf("解析MarketFaceElement失败: %w", err) + } + elem = marketFaceElem + default: + return elem, fmt.Errorf("未知的元素类型: %v", elemJSON.Type) + } + return elem, nil +} + +func (g GroupMessage) MarshalJSON() ([]byte, error) { + type Temp GroupMessage + temp := struct { + *Temp + Elements []elementJSON + }{ + Temp: (*Temp)(&g), + Elements: make([]elementJSON, 0, len(g.Elements)), + } + + for _, elem := range g.Elements { + if elem == nil { + continue + } + elemData, err := json.Marshal(elem) + if err != nil { + return nil, fmt.Errorf("序列化元素失败: %w", err) + } + temp.Elements = append(temp.Elements, elementJSON{ + Type: elem.Type(), + Data: elemData, + }) + } + + return json.Marshal(temp) +} + +func (g *GroupMessage) UnmarshalJSON(data []byte) error { + type Temp GroupMessage + var temp struct { + *Temp + Elements []elementJSON + } + temp.Temp = (*Temp)(g) + + if err := json.Unmarshal(data, &temp); err != nil { + return fmt.Errorf("解析GroupMessage失败: %w", err) + } + + g.Elements = make([]IMessageElement, 0, len(temp.Elements)) + for _, elemJSON := range temp.Elements { + elem, err := parseJSONMessageElement(elemJSON) + if err != nil { + return err + } + g.Elements = append(g.Elements, elem) + } + + g.ID = temp.ID + g.InternalID = temp.InternalID + g.GroupUin = temp.GroupUin + g.GroupName = temp.GroupName + g.Sender = temp.Sender + g.Time = temp.Time + g.OriginalObject = temp.OriginalObject + + return nil +} + +func (g PrivateMessage) MarshalJSON() ([]byte, error) { + type Temp PrivateMessage + temp := struct { + *Temp + Elements []elementJSON + }{ + Temp: (*Temp)(&g), + Elements: make([]elementJSON, 0, len(g.Elements)), + } + + for _, elem := range g.Elements { + if elem == nil { + continue + } + elemData, err := json.Marshal(elem) + if err != nil { + return nil, fmt.Errorf("序列化元素失败: %w", err) + } + temp.Elements = append(temp.Elements, elementJSON{ + Type: elem.Type(), + Data: elemData, + }) + } + + return json.Marshal(temp) +} + +func (g *PrivateMessage) UnmarshalJSON(data []byte) error { + type Temp PrivateMessage + var temp struct { + *Temp + Elements []elementJSON + } + temp.Temp = (*Temp)(g) + + if err := json.Unmarshal(data, &temp); err != nil { + return fmt.Errorf("解析GroupMessage失败: %w", err) + } + + g.Elements = make([]IMessageElement, 0, len(temp.Elements)) + for _, elemJSON := range temp.Elements { + elem, err := parseJSONMessageElement(elemJSON) + if err != nil { + return err + } + g.Elements = append(g.Elements, elem) + } + + g.ID = temp.ID + g.InternalID = temp.InternalID + g.ClientSeq = temp.ClientSeq + g.Target = temp.Target + g.Time = temp.Time + g.Sender = temp.Sender + + return nil +} + +func (g TempMessage) MarshalJSON() ([]byte, error) { + type Temp TempMessage + temp := struct { + *Temp + Elements []elementJSON + }{ + Temp: (*Temp)(&g), + Elements: make([]elementJSON, 0, len(g.Elements)), + } + + for _, elem := range g.Elements { + if elem == nil { + continue + } + elemData, err := json.Marshal(elem) + if err != nil { + return nil, fmt.Errorf("序列化元素失败: %w", err) + } + temp.Elements = append(temp.Elements, elementJSON{ + Type: elem.Type(), + Data: elemData, + }) + } + + return json.Marshal(temp) +} + +func (g *TempMessage) UnmarshalJSON(data []byte) error { + type Temp TempMessage + var temp struct { + *Temp + Elements []elementJSON + } + temp.Temp = (*Temp)(g) + + if err := json.Unmarshal(data, &temp); err != nil { + return fmt.Errorf("解析GroupMessage失败: %w", err) + } + + g.Elements = make([]IMessageElement, 0, len(temp.Elements)) + for _, elemJSON := range temp.Elements { + elem, err := parseJSONMessageElement(elemJSON) + if err != nil { + return err + } + g.Elements = append(g.Elements, elem) + } + + g.ID = temp.ID + g.GroupUin = temp.GroupUin + g.GroupName = temp.GroupName + g.Self = temp.Self + g.Sender = temp.Sender + + return nil +} + +func (r ReplyElement) MarshalJSON() ([]byte, error) { + type Temp ReplyElement + temp := struct { + *Temp + Elements []elementJSON + }{ + Temp: (*Temp)(&r), + Elements: make([]elementJSON, 0, len(r.Elements)), + } + + for _, elem := range r.Elements { + if elem == nil { + continue + } + elemData, err := json.Marshal(elem) + if err != nil { + return nil, fmt.Errorf("序列化元素失败: %w", err) + } + temp.Elements = append(temp.Elements, elementJSON{ + Type: elem.Type(), + Data: elemData, + }) + } + + return json.Marshal(temp) +} + +func (r *ReplyElement) UnmarshalJSON(data []byte) error { + type Temp ReplyElement + var temp struct { + *Temp + Elements []elementJSON + } + temp.Temp = (*Temp)(r) + + if err := json.Unmarshal(data, &temp); err != nil { + return fmt.Errorf("解析GroupMessage失败: %w", err) + } + + r.Elements = make([]IMessageElement, 0, len(temp.Elements)) + for _, elemJSON := range temp.Elements { + elem, err := parseJSONMessageElement(elemJSON) + if err != nil { + return err + } + r.Elements = append(r.Elements, elem) + } + + r.ReplySeq = temp.ReplySeq + r.SenderUin = temp.SenderUin + r.SenderUID = temp.SenderUID + r.GroupUin = temp.GroupUin + r.Time = temp.Time + + return nil +} + +func (f ForwardNode) MarshalJSON() ([]byte, error) { + type Temp ForwardNode + temp := struct { + *Temp + Elements []elementJSON + }{ + Temp: (*Temp)(&f), + Elements: make([]elementJSON, 0, len(f.Message)), + } + + for _, elem := range f.Message { + if elem == nil { + continue + } + elemData, err := json.Marshal(elem) + if err != nil { + return nil, fmt.Errorf("序列化元素失败: %w", err) + } + temp.Elements = append(temp.Elements, elementJSON{ + Type: elem.Type(), + Data: elemData, + }) + } + + return json.Marshal(temp) +} + +func (f *ForwardNode) UnmarshalJSON(data []byte) error { + type Temp ForwardNode + var temp struct { + *Temp + Elements []elementJSON + } + temp.Temp = (*Temp)(f) + + if err := json.Unmarshal(data, &temp); err != nil { + return fmt.Errorf("解析GroupMessage失败: %w", err) + } + + f.Message = make([]IMessageElement, 0, len(temp.Elements)) + for _, elemJSON := range temp.Elements { + elem, err := parseJSONMessageElement(elemJSON) + if err != nil { + return err + } + f.Message = append(f.Message, elem) + } + + f.GroupID = temp.GroupID + f.SenderID = temp.SenderID + f.SenderName = temp.SenderName + f.Time = temp.Time + + return nil +} diff --git a/message/message.go b/message/message.go index 2c250db9..c1d57688 100644 --- a/message/message.go +++ b/message/message.go @@ -590,20 +590,24 @@ func ToReadableStringEle(elem IMessageElement) string { return e.Content case *ImageElement: return "[图片]" + case *FaceElement: + return "[表情]" case *AtElement: return e.Display case *ReplyElement: return "[回复]" // [Optional] + ToReadableString(e.Elements), 这里不破坏原义不添加 - case *FaceElement: - return "[表情]" + case *XMLElement: + return "[服务消息]" + case *ForwardMessage: + return "[转发消息]" + case *FileElement: + return "[文件]" case *VoiceElement: return "[语音]" case *ShortVideoElement: return "[视频]" case *LightAppElement: return "[卡片消息]" - case *ForwardMessage: - return "[转发消息]" case *MarketFaceElement: return "[魔法表情]" default: