Skip to content

Commit

Permalink
feat: BuildGroupImageUploadReq()
Browse files Browse the repository at this point in the history
  • Loading branch information
Szzrain committed Apr 19, 2024
1 parent 9dc68ba commit f651817
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 0 deletions.
108 changes: 108 additions & 0 deletions packets/oidb/GroupImageUpload.go
Original file line number Diff line number Diff line change
@@ -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
}
7 changes: 7 additions & 0 deletions utils/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utils

import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
)

Expand All @@ -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)
}
95 changes: 95 additions & 0 deletions utils/image_resolver.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit f651817

Please sign in to comment.