From 2a1fd324cabc4702d015441c6403a68a6b4ef7ce Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 19 Jun 2022 22:58:52 +0800 Subject: [PATCH] client: add FileResource --- client/group_file.go | 136 +++++++++++----------- client/internal/highway/highway.go | 3 +- client/pb/cmd0x346/cmd0x346.pb.go | 86 +++++++------- client/pb/cmd0x346/cmd0x346.proto | 4 + client/pb/exciting/group.pb.go | 13 ++- client/pb/exciting/group.proto | 7 +- client/pb/oidb/oidb0xeb7.pb.go | 2 +- client/upload_file.go | 176 +++++++++++++++++++++++++++++ internal/proto/wrapper.go | 4 + 9 files changed, 307 insertions(+), 124 deletions(-) create mode 100644 client/upload_file.go diff --git a/client/group_file.go b/client/group_file.go index cc976d64..c43c90e4 100644 --- a/client/group_file.go +++ b/client/group_file.go @@ -1,10 +1,8 @@ package client import ( - "crypto/sha1" "encoding/hex" "fmt" - "io" "math/rand" "os" "runtime/debug" @@ -171,75 +169,71 @@ func (fs *GroupFileSystem) UploadFile(p, name, folderId string) error { return errors.Wrap(err, "open file error") } defer func() { _ = file.Close() }() - md5Hash, size := utils.ComputeMd5AndLength(file) - _, _ = file.Seek(0, io.SeekStart) - sha1H := sha1.New() - _, _ = io.Copy(sha1H, file) - sha1Hash := sha1H.Sum(nil) - _, _ = file.Seek(0, io.SeekStart) - i, err := fs.client.sendAndWait(fs.client.buildGroupFileUploadReqPacket(folderId, name, fs.GroupCode, size, md5Hash, sha1Hash)) + f := &FileResource{ + FileName: name, + Body: file, + } + f.init() + i, err := fs.client.sendAndWait(fs.client.buildGroupFileUploadReqPacket(folderId, fs.GroupCode, f)) if err != nil { return errors.Wrap(err, "query upload failed") } rsp := i.(*oidb.UploadFileRspBody) - if rsp.BoolFileExist.Unwrap() { - _, pkt := fs.client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.FileId.Unwrap(), rsp.BusId.Unwrap(), rand.Int31()) - return fs.client.sendPacket(pkt) - } - if len(rsp.UploadIpLanV4) == 0 { - return errors.New("server requires unsupported ftn upload") - } - ext, _ := proto.Marshal(&exciting.GroupFileUploadExt{ - Unknown1: proto.Int32(100), - Unknown2: proto.Int32(1), - Entry: &exciting.GroupFileUploadEntry{ - BusiBuff: &exciting.ExcitingBusiInfo{ - BusId: rsp.BusId, - SenderUin: proto.Some(fs.client.Uin), - ReceiverUin: proto.Some(fs.GroupCode), - GroupCode: proto.Some(fs.GroupCode), - }, - FileEntry: &exciting.ExcitingFileEntry{ - FileSize: proto.Some(size), - Md5: md5Hash, - Sha1: sha1Hash, - FileId: []byte(rsp.FileId.Unwrap()), - UploadKey: rsp.CheckKey, - }, - ClientInfo: &exciting.ExcitingClientInfo{ - ClientType: proto.Int32(2), - AppId: proto.String(fmt.Sprint(fs.client.version.AppId)), - TerminalType: proto.Int32(2), - ClientVer: proto.String("9e9c09dc"), - Unknown: proto.Int32(4), - }, - FileNameInfo: &exciting.ExcitingFileNameInfo{FileName: proto.Some(name)}, - Host: &exciting.ExcitingHostConfig{Hosts: []*exciting.ExcitingHostInfo{ - { - Url: &exciting.ExcitingUrlInfo{ - Unknown: proto.Int32(1), - Host: proto.Some(rsp.UploadIpLanV4[0]), - }, - Port: rsp.UploadPort, + if !rsp.BoolFileExist.Unwrap() { + if len(rsp.UploadIpLanV4) == 0 { + return errors.New("server requires unsupported ftn upload") + } + ext, _ := proto.Marshal(&exciting.FileUploadExt{ + Unknown1: proto.Int32(100), + Unknown2: proto.Int32(1), + Entry: &exciting.FileUploadEntry{ + BusiBuff: &exciting.ExcitingBusiInfo{ + BusId: rsp.BusId, + SenderUin: proto.Some(fs.client.Uin), + ReceiverUin: proto.Some(fs.GroupCode), + GroupCode: proto.Some(fs.GroupCode), }, - }}, - }, - Unknown3: proto.Int32(0), - }) - client := fs.client - input := highway.Transaction{ - CommandID: 71, - Body: file, - Size: size, - Sum: md5Hash, - Ticket: fs.client.highwaySession.SigSession, - Ext: ext, + FileEntry: &exciting.ExcitingFileEntry{ + FileSize: proto.Some(f.size), + Md5: f.md5, + Sha1: f.sha1, + FileId: []byte(rsp.FileId.Unwrap()), + UploadKey: rsp.CheckKey, + }, + ClientInfo: &exciting.ExcitingClientInfo{ + ClientType: proto.Int32(2), + AppId: proto.String(fmt.Sprint(fs.client.version.AppId)), + TerminalType: proto.Int32(2), + ClientVer: proto.String("9e9c09dc"), + Unknown: proto.Int32(4), + }, + FileNameInfo: &exciting.ExcitingFileNameInfo{FileName: proto.Some(name)}, + Host: &exciting.ExcitingHostConfig{Hosts: []*exciting.ExcitingHostInfo{ + { + Url: &exciting.ExcitingUrlInfo{ + Unknown: proto.Int32(1), + Host: proto.Some(rsp.UploadIpLanV4[0]), + }, + Port: rsp.UploadPort, + }, + }}, + }, + Unknown3: proto.Int32(0), + }) + input := highway.Transaction{ + CommandID: 71, + Body: file, + Size: f.size, + Sum: f.md5, + Ticket: fs.client.highwaySession.SigSession, + Ext: ext, + } + if _, err = fs.client.highwaySession.UploadExciting(input); err != nil { + return errors.Wrap(err, "upload failed") + } } - if _, err = fs.client.highwaySession.UploadExciting(input); err != nil { - return errors.Wrap(err, "upload failed") - } - _, pkt := client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.FileId.Unwrap(), rsp.BusId.Unwrap(), rand.Int31()) - return client.sendPacket(pkt) + _, pkt := fs.client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.FileId.Unwrap(), rsp.BusId.Unwrap(), rand.Int31()) + return fs.client.sendPacket(pkt) } func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string { @@ -277,18 +271,18 @@ func (fs *GroupFileSystem) DeleteFile(parentFolderID, fileId string, busId int32 return i.(string) } -func (c *QQClient) buildGroupFileUploadReqPacket(parentFolderID, fileName string, groupCode, fileSize int64, md5, sha1 []byte) (uint16, []byte) { +func (c *QQClient) buildGroupFileUploadReqPacket(parentFolderID string, groupCode int64, file *FileResource) (uint16, []byte) { body := &oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{ GroupCode: proto.Some(groupCode), AppId: proto.Int32(3), BusId: proto.Int32(102), Entrance: proto.Int32(5), ParentFolderId: proto.Some(parentFolderID), - FileName: proto.Some(fileName), - LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + fileName), - Int64FileSize: proto.Some(fileSize), - Sha: sha1, - Md5: md5, + FileName: proto.Some(file.FileName), + LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + file.FileName), + Int64FileSize: proto.Some(file.size), + Sha: file.sha1, + Md5: file.md5, SupportMultiUpload: proto.Bool(true), }} payload := c.packOIDBPackageProto(1750, 0, body) diff --git a/client/internal/highway/highway.go b/client/internal/highway/highway.go index 91cf492d..d40dda8c 100644 --- a/client/internal/highway/highway.go +++ b/client/internal/highway/highway.go @@ -142,9 +142,8 @@ func (s *Session) UploadExciting(trans Transaction) ([]byte, error) { r := binary.NewReader(body) r.ReadByte() hl := r.ReadInt32() - a2 := r.ReadInt32() + _ = r.ReadInt32() h := r.ReadBytes(int(hl)) - r.ReadBytes(int(a2)) rspHead := new(pb.RspDataHighwayHead) if err = proto.Unmarshal(h, rspHead); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/pb/cmd0x346/cmd0x346.pb.go b/client/pb/cmd0x346/cmd0x346.pb.go index 7747e190..40ffc1fc 100644 --- a/client/pb/cmd0x346/cmd0x346.pb.go +++ b/client/pb/cmd0x346/cmd0x346.pb.go @@ -221,6 +221,8 @@ type ApplyUploadReqV3 struct { LocalFilepath string `protobuf:"bytes,70,opt"` DangerLevel int32 `protobuf:"varint,80,opt"` TotalSpace int64 `protobuf:"varint,90,opt"` + Md5 []byte `protobuf:"bytes,110,opt"` + X3Sha []byte `protobuf:"bytes,120,opt"` } type ApplyUploadRsp struct { @@ -258,23 +260,24 @@ type ApplyUploadRspV2 struct { } type ApplyUploadRspV3 struct { - RetCode int32 `protobuf:"varint,10,opt"` - RetMsg string `protobuf:"bytes,20,opt"` - TotalSpace int64 `protobuf:"varint,30,opt"` - UsedSpace int64 `protobuf:"varint,40,opt"` - UploadedSize int64 `protobuf:"varint,50,opt"` - UploadIp string `protobuf:"bytes,60,opt"` - UploadDomain string `protobuf:"bytes,70,opt"` - UploadPort int32 `protobuf:"varint,80,opt"` - Uuid []byte `protobuf:"bytes,90,opt"` - UploadKey []byte `protobuf:"bytes,100,opt"` - BoolFileExist bool `protobuf:"varint,110,opt"` - PackSize int32 `protobuf:"varint,120,opt"` - UploadIpList []string `protobuf:"bytes,130,rep"` - UploadHttpsPort int32 `protobuf:"varint,140,opt"` - UploadHttpsDomain string `protobuf:"bytes,150,opt"` - UploadDns string `protobuf:"bytes,160,opt"` - UploadLanip string `protobuf:"bytes,170,opt"` + RetCode int32 `protobuf:"varint,10,opt"` + RetMsg string `protobuf:"bytes,20,opt"` + TotalSpace int64 `protobuf:"varint,30,opt"` + UsedSpace int64 `protobuf:"varint,40,opt"` + UploadedSize int64 `protobuf:"varint,50,opt"` + UploadIp string `protobuf:"bytes,60,opt"` + UploadDomain string `protobuf:"bytes,70,opt"` + UploadPort int32 `protobuf:"varint,80,opt"` + Uuid []byte `protobuf:"bytes,90,opt"` + UploadKey []byte `protobuf:"bytes,100,opt"` + BoolFileExist bool `protobuf:"varint,110,opt"` + PackSize int32 `protobuf:"varint,120,opt"` + UploadIpList []string `protobuf:"bytes,130,rep"` + UploadHttpsPort int32 `protobuf:"varint,140,opt"` + UploadHttpsDomain string `protobuf:"bytes,150,opt"` + UploadDns string `protobuf:"bytes,160,opt"` + UploadLanip string `protobuf:"bytes,170,opt"` + MediaPlateformUploadKey []byte `protobuf:"bytes,220,opt"` } type DelMessageReq struct { @@ -404,30 +407,31 @@ type RenewFileRsp struct { } type C346ReqBody struct { - Cmd int32 `protobuf:"varint,1,opt"` - Seq int32 `protobuf:"varint,2,opt"` - RecvListQueryReq *RecvListQueryReq `protobuf:"bytes,3,opt"` - SendListQueryReq *SendListQueryReq `protobuf:"bytes,4,opt"` - RenewFileReq *RenewFileReq `protobuf:"bytes,5,opt"` - RecallFileReq *RecallFileReq `protobuf:"bytes,6,opt"` - ApplyUploadReq *ApplyUploadReq `protobuf:"bytes,7,opt"` - ApplyUploadHitReq *ApplyUploadHitReq `protobuf:"bytes,8,opt"` - ApplyForwardFileReq *ApplyForwardFileReq `protobuf:"bytes,9,opt"` - UploadSuccReq *UploadSuccReq `protobuf:"bytes,10,opt"` - DeleteFileReq *DeleteFileReq `protobuf:"bytes,11,opt"` - DownloadSuccReq *DownloadSuccReq `protobuf:"bytes,12,opt"` - ApplyDownloadAbsReq *ApplyDownloadAbsReq `protobuf:"bytes,13,opt"` - ApplyDownloadReq *ApplyDownloadReq `protobuf:"bytes,14,opt"` - ApplyListDownloadReq *ApplyListDownloadReq `protobuf:"bytes,15,opt"` - FileQueryReq *FileQueryReq `protobuf:"bytes,16,opt"` - ApplyCopyFromReq *ApplyCopyFromReq `protobuf:"bytes,17,opt"` - ApplyUploadReqV2 *ApplyUploadReqV2 `protobuf:"bytes,18,opt"` - ApplyUploadReqV3 *ApplyUploadReqV3 `protobuf:"bytes,19,opt"` - ApplyUploadHitReqV2 *ApplyUploadHitReqV2 `protobuf:"bytes,20,opt"` - ApplyUploadHitReqV3 *ApplyUploadHitReqV3 `protobuf:"bytes,21,opt"` - BusinessId int32 `protobuf:"varint,101,opt"` - ClientType int32 `protobuf:"varint,102,opt"` - ApplyCopyToReq *ApplyCopyToReq `protobuf:"bytes,90000,opt"` + Cmd int32 `protobuf:"varint,1,opt"` + Seq int32 `protobuf:"varint,2,opt"` + RecvListQueryReq *RecvListQueryReq `protobuf:"bytes,3,opt"` + SendListQueryReq *SendListQueryReq `protobuf:"bytes,4,opt"` + RenewFileReq *RenewFileReq `protobuf:"bytes,5,opt"` + RecallFileReq *RecallFileReq `protobuf:"bytes,6,opt"` + ApplyUploadReq *ApplyUploadReq `protobuf:"bytes,7,opt"` + ApplyUploadHitReq *ApplyUploadHitReq `protobuf:"bytes,8,opt"` + ApplyForwardFileReq *ApplyForwardFileReq `protobuf:"bytes,9,opt"` + UploadSuccReq *UploadSuccReq `protobuf:"bytes,10,opt"` + DeleteFileReq *DeleteFileReq `protobuf:"bytes,11,opt"` + DownloadSuccReq *DownloadSuccReq `protobuf:"bytes,12,opt"` + ApplyDownloadAbsReq *ApplyDownloadAbsReq `protobuf:"bytes,13,opt"` + ApplyDownloadReq *ApplyDownloadReq `protobuf:"bytes,14,opt"` + ApplyListDownloadReq *ApplyListDownloadReq `protobuf:"bytes,15,opt"` + FileQueryReq *FileQueryReq `protobuf:"bytes,16,opt"` + ApplyCopyFromReq *ApplyCopyFromReq `protobuf:"bytes,17,opt"` + ApplyUploadReqV2 *ApplyUploadReqV2 `protobuf:"bytes,18,opt"` + ApplyUploadReqV3 *ApplyUploadReqV3 `protobuf:"bytes,19,opt"` + ApplyUploadHitReqV2 *ApplyUploadHitReqV2 `protobuf:"bytes,20,opt"` + ApplyUploadHitReqV3 *ApplyUploadHitReqV3 `protobuf:"bytes,21,opt"` + BusinessId int32 `protobuf:"varint,101,opt"` + ClientType int32 `protobuf:"varint,102,opt"` + FlagSupportMediaplatform uint32 `protobuf:"varint,200,opt"` + ApplyCopyToReq *ApplyCopyToReq `protobuf:"bytes,90000,opt"` //ApplyCleanTrafficReq applyCleanTrafficReq = 90001; empty message ApplyGetTrafficReq *ApplyGetTrafficReq `protobuf:"bytes,90002,opt"` ExtensionReq *ExtensionReq `protobuf:"bytes,99999,opt"` diff --git a/client/pb/cmd0x346/cmd0x346.proto b/client/pb/cmd0x346/cmd0x346.proto index 65fbb554..8b29eab2 100644 --- a/client/pb/cmd0x346/cmd0x346.proto +++ b/client/pb/cmd0x346/cmd0x346.proto @@ -197,6 +197,8 @@ message ApplyUploadReqV3 { string localFilepath = 70; int32 dangerLevel = 80; int64 totalSpace = 90; + bytes md5 = 110; + bytes _3Sha = 120; } message ApplyUploadRsp { int32 retCode = 10; @@ -248,6 +250,7 @@ message ApplyUploadRspV3 { string uploadHttpsDomain = 150; string uploadDns = 160; string uploadLanip = 170; + bytes mediaPlateformUploadKey = 220; } message DelMessageReq { int64 uinSender = 1; @@ -382,6 +385,7 @@ message C346ReqBody { ApplyUploadHitReqV3 applyUploadHitReqV3 = 21; int32 businessId = 101; int32 clientType = 102; + uint32 flagSupportMediaplatform = 200; ApplyCopyToReq applyCopyToReq = 90000; //ApplyCleanTrafficReq applyCleanTrafficReq = 90001; empty message ApplyGetTrafficReq applyGetTrafficReq = 90002; diff --git a/client/pb/exciting/group.pb.go b/client/pb/exciting/group.pb.go index 59725880..650d4afe 100644 --- a/client/pb/exciting/group.pb.go +++ b/client/pb/exciting/group.pb.go @@ -7,14 +7,15 @@ import ( proto "github.com/RomiChan/protobuf/proto" ) -type GroupFileUploadExt struct { - Unknown1 proto.Option[int32] `protobuf:"varint,1,opt"` - Unknown2 proto.Option[int32] `protobuf:"varint,2,opt"` - Entry *GroupFileUploadEntry `protobuf:"bytes,100,opt"` - Unknown3 proto.Option[int32] `protobuf:"varint,3,opt"` +type FileUploadExt struct { + Unknown1 proto.Option[int32] `protobuf:"varint,1,opt"` + Unknown2 proto.Option[int32] `protobuf:"varint,2,opt"` + Unknown3 proto.Option[int32] `protobuf:"varint,3,opt"` + Entry *FileUploadEntry `protobuf:"bytes,100,opt"` + Unknown200 proto.Option[int32] `protobuf:"varint,200,opt"` } -type GroupFileUploadEntry struct { +type FileUploadEntry struct { BusiBuff *ExcitingBusiInfo `protobuf:"bytes,100,opt"` FileEntry *ExcitingFileEntry `protobuf:"bytes,200,opt"` ClientInfo *ExcitingClientInfo `protobuf:"bytes,300,opt"` diff --git a/client/pb/exciting/group.proto b/client/pb/exciting/group.proto index 9466bef3..ef2a3be8 100644 --- a/client/pb/exciting/group.proto +++ b/client/pb/exciting/group.proto @@ -2,14 +2,15 @@ syntax = "proto2"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/exciting"; -message GroupFileUploadExt { +message FileUploadExt { optional int32 unknown1 = 1; optional int32 unknown2 = 2; - optional GroupFileUploadEntry entry = 100; optional int32 unknown3 = 3; + optional FileUploadEntry entry = 100; + optional int32 unknown200 = 200; } -message GroupFileUploadEntry { +message FileUploadEntry { optional ExcitingBusiInfo busiBuff = 100; optional ExcitingFileEntry fileEntry = 200; optional ExcitingClientInfo clientInfo = 300; diff --git a/client/pb/oidb/oidb0xeb7.pb.go b/client/pb/oidb/oidb0xeb7.pb.go index 3887535d..29d90d52 100644 --- a/client/pb/oidb/oidb0xeb7.pb.go +++ b/client/pb/oidb/oidb0xeb7.pb.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-golite. DO NOT EDIT. -// source: oidb0xeb7.proto +// source: pb/oidb/oidb0xeb7.proto package oidb diff --git a/client/upload_file.go b/client/upload_file.go new file mode 100644 index 00000000..5574f602 --- /dev/null +++ b/client/upload_file.go @@ -0,0 +1,176 @@ +package client + +import ( + "crypto/md5" + "crypto/sha1" + "fmt" + "io" + + "github.com/pkg/errors" + + "github.com/Mrs4s/MiraiGo/client/internal/highway" + "github.com/Mrs4s/MiraiGo/client/internal/network" + "github.com/Mrs4s/MiraiGo/client/pb/cmd0x346" + "github.com/Mrs4s/MiraiGo/client/pb/exciting" + "github.com/Mrs4s/MiraiGo/internal/proto" + "github.com/Mrs4s/MiraiGo/message" +) + +func init() { + decoders["OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700"] = decodePrivateFileUploadReq +} + +type FileResource struct { + FileName string + Body io.ReadSeeker // FileResource content body + size int64 + md5 []byte + sha1 []byte +} + +type fileUploadRsp struct { + existed bool + busid int32 + uuid []byte + uploadKey []byte + + // upload group file need + UploadIpLanV4 []string + UploadPort int32 +} + +func (f *FileResource) init() { + md5H := md5.New() + sha1H := sha1.New() + + whence, _ := f.Body.Seek(0, io.SeekCurrent) + f.size, _ = io.Copy(io.MultiWriter(md5H, sha1H), f.Body) + _, _ = f.Body.Seek(whence, io.SeekStart) // restore + + // calculate md5&sha1 hash + f.md5 = md5H.Sum(nil) + f.sha1 = sha1H.Sum(nil) +} + +func (c *QQClient) _UploadFile(target message.Source, file *FileResource) error { + file.init() + // 同文件等待其他线程上传 + fkey := string(file.sha1) + fsWaiter.Wait(fkey) + defer fsWaiter.Done(fkey) + + var rsp *fileUploadRsp + if target.SourceType == message.SourcePrivate { + i, err := c.sendAndWait(c.buildPrivateFileUploadReqPacket(target, file)) + if err != nil { + return err + } + rsp = i.(*fileUploadRsp) + } + if !rsp.existed { + ext := &exciting.FileUploadExt{ + Unknown1: proto.Int32(100), + Unknown2: proto.Int32(2), + Entry: &exciting.FileUploadEntry{ + BusiBuff: &exciting.ExcitingBusiInfo{ + BusId: proto.Int32(rsp.busid), + SenderUin: proto.Some(c.Uin), + ReceiverUin: proto.Some(target.PrimaryID), + GroupCode: proto.Int64(0), + }, + FileEntry: &exciting.ExcitingFileEntry{ + FileSize: proto.Some(file.size), + Md5: file.md5, + Sha1: file.sha1, + FileId: rsp.uuid, + UploadKey: rsp.uploadKey, + }, + ClientInfo: &exciting.ExcitingClientInfo{ + ClientType: proto.Int32(2), + AppId: proto.String(fmt.Sprint(c.version.AppId)), + TerminalType: proto.Int32(2), + ClientVer: proto.String("d92615c5"), + Unknown: proto.Int32(4), + }, + FileNameInfo: &exciting.ExcitingFileNameInfo{ + FileName: proto.Some(file.FileName), + }, + }, + Unknown200: proto.Int32(1), + } + if target.SourceType == message.SourceGroup { + if len(rsp.UploadIpLanV4) == 0 { + return errors.New("server requires unsupported ftn upload") + } + ext.Unknown3 = proto.Int32(0) + ext.Unknown200 = proto.None[int32]() + ext.Entry.BusiBuff.GroupCode = proto.Int64(target.PrimaryID) + ext.Entry.Host = &exciting.ExcitingHostConfig{ + Hosts: []*exciting.ExcitingHostInfo{ + { + Url: &exciting.ExcitingUrlInfo{ + Unknown: proto.Int32(1), + Host: proto.Some(rsp.UploadIpLanV4[0]), + }, + Port: proto.Some(rsp.UploadPort), + }, + }, + } + } + extPkt, _ := proto.Marshal(ext) + input := highway.Transaction{ + CommandID: 71, + Body: file.Body, + Size: file.size, + Sum: file.md5, + Ticket: c.highwaySession.SigSession, + Ext: extPkt, + } + if target.SourceType == message.SourcePrivate { + input.CommandID = 69 + } + if _, err := c.highwaySession.UploadExciting(input); err != nil { + return errors.Wrap(err, "upload failed") + } + } + return nil +} + +func (c *QQClient) buildPrivateFileUploadReqPacket(target message.Source, file *FileResource) (uint16, []byte) { + req := cmd0x346.C346ReqBody{ + Cmd: 1700, + Seq: c.nextFriendSeq(), + ApplyUploadReqV3: &cmd0x346.ApplyUploadReqV3{ + SenderUin: c.Uin, + RecverUin: target.PrimaryID, + FileSize: file.size, + FileName: file.FileName, + Bytes_10MMd5: file.md5, // TODO: investigate this + Sha: file.sha1, + LocalFilepath: "/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/" + file.FileName, + Md5: file.md5, + }, + BusinessId: 3, + ClientType: 104, + FlagSupportMediaplatform: 1, + } + pkg, _ := proto.Marshal(&req) + return c.uniPacket("OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700", pkg) +} + +// OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700 +func decodePrivateFileUploadReq(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { + var rsp cmd0x346.C346RspBody + err := proto.Unmarshal(payload, &rsp) + if err != nil { + return nil, err + } + v3 := rsp.ApplyUploadRspV3 + r := &fileUploadRsp{ + existed: v3.BoolFileExist, + busid: 3, + uuid: v3.Uuid, + uploadKey: v3.MediaPlateformUploadKey, + } + return r, nil +} diff --git a/internal/proto/wrapper.go b/internal/proto/wrapper.go index b71046b5..213ae8de 100644 --- a/internal/proto/wrapper.go +++ b/internal/proto/wrapper.go @@ -38,6 +38,10 @@ func Some[T any](val T) proto.Option[T] { return proto.Some(val) } +func None[T any]() proto.Option[T] { + return proto.None[T]() +} + // Bool stores v in a new bool value and returns a pointer to it. func Bool(v bool) proto.Option[bool] { return proto.Some(v) }