From f65181792a4be207d2864b31a84221621065d0c1 Mon Sep 17 00:00:00 2001 From: Szzrain Date: Fri, 19 Apr 2024 17:03:29 -0400 Subject: [PATCH 1/3] feat: BuildGroupImageUploadReq() --- packets/oidb/GroupImageUpload.go | 108 +++++++++++++++++++++++++++++++ utils/hash.go | 7 ++ utils/image_resolver.go | 95 +++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 packets/oidb/GroupImageUpload.go create mode 100644 utils/image_resolver.go diff --git a/packets/oidb/GroupImageUpload.go b/packets/oidb/GroupImageUpload.go new file mode 100644 index 00000000..3c479922 --- /dev/null +++ b/packets/oidb/GroupImageUpload.go @@ -0,0 +1,108 @@ +package oidb + +import ( + "encoding/hex" + "errors" + "fmt" + "github.com/LagrangeDev/LagrangeGo/packets/pb/service/oidb" + "github.com/LagrangeDev/LagrangeGo/utils" + "io" + "math/rand/v2" +) + +func BuildGroupImageUploadReq(groupUin uint32, stream io.Reader) (*OidbPacket, error) { + // OidbSvcTrpcTcp.0x11c4_100 + data, err := io.ReadAll(stream) + if err != nil { + return nil, err + } + md5Hash := utils.Md5Digest(data) + sha1Hash := utils.Sha1Digest(data) + format, size, err := utils.ImageResolve(data) + if err != nil { + return nil, err + } + imageExt := utils.GetImageExt(format) + + hexString := "0800180020004a00500062009201009a0100aa010c080012001800200028003a00" + bytesPbReserveTroop, err := hex.DecodeString(hexString) + if err != nil { + return nil, err + } + + body := &oidb.NTV2RichMediaReq{ + ReqHead: &oidb.MultiMediaReqHead{ + Common: &oidb.CommonHead{ + RequestId: 1, + Command: 100, + }, + Scene: &oidb.SceneInfo{ + RequestType: 2, + BusinessType: 1, + SceneType: 2, + Group: &oidb.NTGroupInfo{ + GroupUin: groupUin, + }, + }, + Client: &oidb.ClientMeta{ + AgentType: 2, + }, + }, + Upload: &oidb.UploadReq{ + UploadInfo: []*oidb.UploadInfo{ + { + FileInfo: &oidb.FileInfo{ + FileSize: uint32(len(data)), + FileHash: fmt.Sprintf("%x", md5Hash), + FileSha1: fmt.Sprintf("%x", sha1Hash), + FileName: fmt.Sprintf("%x.%s", md5Hash, imageExt), + Type: &oidb.FileType{ + Type: 1, + PicFormat: uint32(format), + VideoFormat: 0, + VoiceFormat: 0, + }, + Width: size.X, + Height: size.Y, + Time: 0, + Original: 1, + }, + SubFileType: 0, + }, + }, + TryFastUploadCompleted: true, + SrvSendMsg: false, + ClientRandomId: rand.Uint64(), + CompatQMsgSceneType: 2, + ExtBizInfo: &oidb.ExtBizInfo{ + Pic: &oidb.PicExtBizInfo{ + BytesPbReserveTroop: bytesPbReserveTroop, + }, + Video: &oidb.VideoExtBizInfo{ + BytesPbReserve: []byte{}, + }, + Ptt: &oidb.PttExtBizInfo{ + BytesReserve: []byte{}, + BytesPbReserve: []byte{}, + BytesGeneralFlags: []byte{}, + }, + }, + ClientSeq: 0, + NoNeedCompatMsg: false, + }, + } + return BuildOidbPacket(0x11c4, 100, body, false, true) +} + +func ParseGroupImageUploadResp(data []byte) (*oidb.NTV2RichMediaResp, error) { // TODO: return proper response + var resp oidb.NTV2RichMediaResp + baseResp, err := ParseOidbPacket(data, &resp) + if err != nil { + return nil, err + } + if baseResp.ErrorCode != 0 { + return nil, errors.New(baseResp.ErrorMsg) + } + + return &resp, nil +} diff --git a/utils/hash.go b/utils/hash.go index 1c7f3f29..4b3342b9 100644 --- a/utils/hash.go +++ b/utils/hash.go @@ -2,6 +2,7 @@ package utils import ( "crypto/md5" + "crypto/sha1" "crypto/sha256" ) @@ -16,3 +17,9 @@ func Sha256Digest(v []byte) []byte { sha256er.Write(v) return sha256er.Sum(nil) } + +func Sha1Digest(v []byte) []byte { + sha1er := sha1.New() + sha1er.Write(v) + return sha1er.Sum(nil) +} diff --git a/utils/image_resolver.go b/utils/image_resolver.go new file mode 100644 index 00000000..9a1b098f --- /dev/null +++ b/utils/image_resolver.go @@ -0,0 +1,95 @@ +package utils + +import ( + "bytes" + "encoding/binary" + "errors" +) + +// Vector2 二维向量 +type Vector2 struct { + X uint32 + Y uint32 +} + +type ImageFormat uint32 + +const ( + Unknown ImageFormat = iota + Jpeg ImageFormat = 1000 + Png ImageFormat = 1001 + Gif ImageFormat = 2000 + Webp ImageFormat = 1002 + Bmp ImageFormat = 1005 + Tiff ImageFormat = 1006 +) + +func GetImageExt(format ImageFormat) string { + switch format { + case Jpeg: + return "jpg" + case Png: + return "png" + case Gif: + return "gif" + case Webp: + return "webp" + case Bmp: + return "bmp" + case Tiff: + return "tiff" + default: + return "unknown" + } +} + +func ImageResolve(image []byte) (ImageFormat, Vector2, error) { + if len(image) < 10 { // 最小长度检查 + return Unknown, Vector2{}, errors.New("image data is too short") + } + + size := Vector2{} + format := Unknown + + switch { + case bytes.Equal(image[:6], []byte{0x47, 0x49, 0x46, 0x38, 0x39, 0x61}) || bytes.Equal(image[:6], []byte{0x47, 0x49, 0x46, 0x38, 0x37, 0x61}): // GIF + size = Vector2{X: uint32(binary.LittleEndian.Uint16(image[6:8])), Y: uint32(binary.LittleEndian.Uint16(image[8:10]))} + format = Gif + + case bytes.Equal(image[:2], []byte{0xFF, 0xD8}): // JPEG + for i := 2; i < len(image)-10; i++ { + if binary.LittleEndian.Uint16(image[i:i+2])&0xFCFF == 0xC0FF { // SOF0 ~ SOF3 + size = Vector2{X: uint32(binary.BigEndian.Uint16(image[i+7 : i+9])), Y: uint32(binary.BigEndian.Uint16(image[i+5 : i+7]))} + break + } + } + format = Jpeg + + case bytes.Equal(image[:8], []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}): // PNG + size = Vector2{X: binary.BigEndian.Uint32(image[16:20]), Y: binary.BigEndian.Uint32(image[20:24])} + format = Png + + case bytes.Equal(image[:4], []byte{0x52, 0x49, 0x46, 0x46}) && bytes.Equal(image[8:12], []byte{0x57, 0x45, 0x42, 0x50}): // WEBP + if bytes.Equal(image[12:16], []byte{0x56, 0x50, 0x38, 0x58}) { // VP8X + size = Vector2{X: uint32(binary.LittleEndian.Uint16(image[24:27]) + 1), Y: uint32(binary.LittleEndian.Uint16(image[27:30]) + 1)} + } else if bytes.Equal(image[12:16], []byte{0x56, 0x50, 0x38, 0x4C}) { // VP8L + size = Vector2{X: uint32(int32(binary.LittleEndian.Uint32(image[21:25]))&0x3FFF) + 1, Y: uint32(int32(binary.LittleEndian.Uint32(image[20:22])&0xFFFC000)>>0x0E) + 1} + } else { + size = Vector2{X: uint32(binary.LittleEndian.Uint16(image[26:28])), Y: uint32(binary.LittleEndian.Uint16(image[28:30]))} + } + format = Webp + + case bytes.Equal(image[:2], []byte{0x42, 0x4D}): // BMP + size = Vector2{X: binary.LittleEndian.Uint32(image[18:22]), Y: binary.LittleEndian.Uint32(image[22:26])} + format = Bmp + + case bytes.Equal(image[:2], []byte{0x49, 0x49}) || bytes.Equal(image[:2], []byte{0x4D, 0x4D}): // TIFF + size = Vector2{X: uint32(binary.LittleEndian.Uint16(image[18:20])), Y: uint32(binary.LittleEndian.Uint16(image[30:32]))} + format = Tiff + + default: + return Unknown, Vector2{}, nil + } + + return format, size, nil +} From c94065a53b9d958f9d5964386a8ca9a17939c54b Mon Sep 17 00:00:00 2001 From: Szzrain Date: Fri, 19 Apr 2024 17:19:11 -0400 Subject: [PATCH 2/3] fix: use rand v1 --- packets/oidb/GroupImageUpload.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packets/oidb/GroupImageUpload.go b/packets/oidb/GroupImageUpload.go index 3c479922..7771a663 100644 --- a/packets/oidb/GroupImageUpload.go +++ b/packets/oidb/GroupImageUpload.go @@ -7,7 +7,7 @@ import ( "github.com/LagrangeDev/LagrangeGo/packets/pb/service/oidb" "github.com/LagrangeDev/LagrangeGo/utils" "io" - "math/rand/v2" + "math/rand" ) func BuildGroupImageUploadReq(groupUin uint32, stream io.Reader) (*OidbPacket, error) { @@ -72,7 +72,7 @@ func BuildGroupImageUploadReq(groupUin uint32, stream io.Reader) (*OidbPacket, e }, TryFastUploadCompleted: true, SrvSendMsg: false, - ClientRandomId: rand.Uint64(), + ClientRandomId: uint64(rand.Int63()), CompatQMsgSceneType: 2, ExtBizInfo: &oidb.ExtBizInfo{ Pic: &oidb.PicExtBizInfo{ From c3c3ec1f5324357761b52bf19178a6a470b14ad4 Mon Sep 17 00:00:00 2001 From: Szzrain Date: Fri, 19 Apr 2024 17:23:06 -0400 Subject: [PATCH 3/3] chore: make lint happy --- utils/image_resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/image_resolver.go b/utils/image_resolver.go index 9a1b098f..bba522d6 100644 --- a/utils/image_resolver.go +++ b/utils/image_resolver.go @@ -49,7 +49,7 @@ func ImageResolve(image []byte) (ImageFormat, Vector2, error) { } size := Vector2{} - format := Unknown + var format ImageFormat switch { case bytes.Equal(image[:6], []byte{0x47, 0x49, 0x46, 0x38, 0x39, 0x61}) || bytes.Equal(image[:6], []byte{0x47, 0x49, 0x46, 0x38, 0x37, 0x61}): // GIF