From 36ed2b39025a5c6057987ed70c59d3a6b8d51dbb Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 5 Jan 2021 00:27:24 +0800 Subject: [PATCH] move func. --- client/builders.go | 32 ------ client/client.go | 165 ------------------------------ client/decoders.go | 28 ------ client/image.go | 243 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+), 225 deletions(-) create mode 100644 client/image.go diff --git a/client/builders.go b/client/builders.go index 5478c9b8..43077524 100644 --- a/client/builders.go +++ b/client/builders.go @@ -22,7 +22,6 @@ import ( "github.com/Mrs4s/MiraiGo/protocol/crypto" "github.com/Mrs4s/MiraiGo/protocol/packets" "github.com/Mrs4s/MiraiGo/protocol/tlv" - "github.com/Mrs4s/MiraiGo/utils" ) var ( @@ -667,37 +666,6 @@ func (c *QQClient) buildOffPicUpPacket(target int64, md5 []byte, size int32) (ui return seq, packet } -// ImgStore.GroupPicUp -func (c *QQClient) buildGroupImageStorePacket(groupCode int64, md5 []byte, size int32) (uint16, []byte) { - seq := c.nextSeq() - name := utils.RandomString(16) + ".gif" - req := &pb.D388ReqBody{ - NetType: 3, - Subcmd: 1, - MsgTryUpImgReq: []*pb.TryUpImgReq{ - { - GroupCode: groupCode, - SrcUin: c.Uin, - FileMd5: md5, - FileSize: int64(size), - FileName: name, - SrcTerm: 5, - PlatformType: 9, - BuType: 1, - PicType: 1000, - BuildVer: "8.2.7.4410", - AppPicType: 1006, - FileIndex: EmptyBytes, - TransferUrl: EmptyBytes, - }, - }, - Extension: EmptyBytes, - } - payload, _ := proto.Marshal(req) - packet := packets.BuildUniPacket(c.Uin, seq, "ImgStore.GroupPicUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) - return seq, packet -} - // ProfileService.Pb.ReqSystemMsgNew.Friend func (c *QQClient) buildSystemMsgNewFriendPacket() (uint16, []byte) { seq := c.nextSeq() diff --git a/client/client.go b/client/client.go index 5dd573c0..2a6aacf4 100644 --- a/client/client.go +++ b/client/client.go @@ -3,16 +3,13 @@ package client import ( "bytes" "crypto/md5" - "encoding/hex" "encoding/json" "fmt" "github.com/Mrs4s/MiraiGo/binary/jce" - "image" "io" "math" "math/rand" "net" - "os" "runtime/debug" "sort" "strconv" @@ -134,7 +131,6 @@ var decoders = map[string]func(*QQClient, uint16, []byte) (interface{}, error){ "friendlist.GetTroopListReqV2": decodeGroupListResponse, "friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse, "group_member_card.get_group_member_card_info": decodeGroupMemberInfoResponse, - "ImgStore.GroupPicUp": decodeGroupImageStoreResponse, "PttStore.GroupPttUp": decodeGroupPttStoreResponse, "LongConn.OffPicUp": decodeOffPicUpResponse, "ProfileService.Pb.ReqSystemMsgNew.Group": decodeSystemMsgGroupPacket, @@ -575,167 +571,6 @@ func (c *QQClient) SendFriendPoke(target int64) { _, _ = c.sendAndWait(c.buildFriendPokePacket(target)) } -func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*message.GroupImageElement, error) { - h := md5.New() - length, _ := io.Copy(h, img) - fh := h.Sum(nil) - seq, pkt := c.buildGroupImageStorePacket(groupCode, fh[:], int32(length)) - r, err := c.sendAndWait(seq, pkt) - if err != nil { - return nil, err - } - rsp := r.(imageUploadResponse) - if rsp.ResultCode != 0 { - return nil, errors.New(rsp.Message) - } - if rsp.IsExists { - goto ok - } - _, _ = img.Seek(0, io.SeekStart) - if len(c.srvSsoAddrs) == 0 { - for i, addr := range rsp.UploadIp { - c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(uint32(addr)), rsp.UploadPort[i])) - } - } - if _, err = c.highwayUploadByBDH(img, 2, rsp.UploadKey, EmptyBytes); err == nil { - goto ok - } - return nil, errors.New("upload failed") -ok: - _, _ = img.Seek(0, io.SeekStart) - i, _, _ := image.DecodeConfig(img) - var imageType int32 = 1000 - _, _ = img.Seek(0, io.SeekStart) - tmp := make([]byte, 4) - _, _ = img.Read(tmp) - if bytes.Equal(tmp, []byte{0x47, 0x49, 0x46, 0x38}) { - imageType = 2000 - } - return message.NewGroupImage(binary.CalculateImageResourceId(fh[:]), fh[:], rsp.FileId, int32(length), int32(i.Width), int32(i.Height), imageType), nil -} - -func (c *QQClient) UploadGroupImageByFile(groupCode int64, path string) (*message.GroupImageElement, error) { - img, err := os.OpenFile(path, os.O_RDONLY, 0666) - if err != nil { - return nil, err - } - h := md5.New() - length, _ := io.Copy(h, img) - fh := h.Sum(nil) - seq, pkt := c.buildGroupImageStorePacket(groupCode, fh[:], int32(length)) - r, err := c.sendAndWait(seq, pkt) - if err != nil { - return nil, err - } - rsp := r.(imageUploadResponse) - if rsp.ResultCode != 0 { - return nil, errors.New(rsp.Message) - } - if rsp.IsExists { - goto ok - } - if len(c.srvSsoAddrs) == 0 { - for i, addr := range rsp.UploadIp { - c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(uint32(addr)), rsp.UploadPort[i])) - } - } - if _, err = c.highwayUploadFileMultiThreadingByBDH(path, 2, 4, rsp.UploadKey, EmptyBytes); err == nil { - goto ok - } - return nil, errors.New("upload failed") -ok: - _, _ = img.Seek(0, io.SeekStart) - i, _, _ := image.DecodeConfig(img) - var imageType int32 = 1000 - _, _ = img.Seek(0, io.SeekStart) - tmp := make([]byte, 4) - _, _ = img.Read(tmp) - if bytes.Equal(tmp, []byte{0x47, 0x49, 0x46, 0x38}) { - imageType = 2000 - } - return message.NewGroupImage(binary.CalculateImageResourceId(fh[:]), fh[:], rsp.FileId, int32(length), int32(i.Width), int32(i.Height), imageType), nil -} - -func (c *QQClient) UploadPrivateImage(target int64, img io.ReadSeeker) (*message.FriendImageElement, error) { - return c.uploadPrivateImage(target, img, 0) -} - -func (c *QQClient) uploadPrivateImage(target int64, img io.ReadSeeker, count int) (*message.FriendImageElement, error) { - count++ - h := md5.New() - length, _ := io.Copy(h, img) - fh := h.Sum(nil) - e, err := c.QueryFriendImage(target, fh[:], int32(length)) - if errors.Is(err, ErrNotExists) { - // use group highway upload and query again for image id. - if _, err = c.UploadGroupImage(target, img); err != nil { - return nil, err - } - if count >= 5 { - return e, nil - } - return c.uploadPrivateImage(target, img, count) - } - if err != nil { - return nil, err - } - return e, nil -} - -func (c *QQClient) ImageOcr(img interface{}) (*OcrResponse, error) { - switch e := img.(type) { - case *message.GroupImageElement: - rsp, err := c.sendAndWait(c.buildImageOcrRequestPacket(e.Url, strings.ToUpper(hex.EncodeToString(e.Md5)), e.Size, e.Width, e.Height)) - if err != nil { - return nil, err - } - return rsp.(*OcrResponse), nil - case *message.ImageElement: - rsp, err := c.sendAndWait(c.buildImageOcrRequestPacket(e.Url, strings.ToUpper(hex.EncodeToString(e.Md5)), e.Size, e.Width, e.Height)) - if err != nil { - return nil, err - } - return rsp.(*OcrResponse), nil - } - return nil, errors.New("image error") -} - -func (c *QQClient) QueryGroupImage(groupCode int64, hash []byte, size int32) (*message.GroupImageElement, error) { - r, err := c.sendAndWait(c.buildGroupImageStorePacket(groupCode, hash, size)) - if err != nil { - return nil, err - } - rsp := r.(imageUploadResponse) - if rsp.ResultCode != 0 { - return nil, errors.New(rsp.Message) - } - if rsp.IsExists { - return message.NewGroupImage(binary.CalculateImageResourceId(hash), hash, rsp.FileId, size, rsp.Width, rsp.Height, 1000), nil - } - return nil, errors.New("image does not exist") -} - -func (c *QQClient) QueryFriendImage(target int64, hash []byte, size int32) (*message.FriendImageElement, error) { - i, err := c.sendAndWait(c.buildOffPicUpPacket(target, hash, size)) - if err != nil { - return nil, err - } - rsp := i.(imageUploadResponse) - if rsp.ResultCode != 0 { - return nil, errors.New(rsp.Message) - } - if !rsp.IsExists { - return &message.FriendImageElement{ - ImageId: rsp.ResourceId, - Md5: hash, - }, errors.WithStack(ErrNotExists) - } - return &message.FriendImageElement{ - ImageId: rsp.ResourceId, - Md5: hash, - }, nil -} - func (c *QQClient) ReloadGroupList() error { c.groupListLock.Lock() defer c.groupListLock.Unlock() diff --git a/client/decoders.go b/client/decoders.go index 0ea5eeaf..834ee50c 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -580,34 +580,6 @@ func decodeGroupMemberInfoResponse(c *QQClient, _ uint16, payload []byte) (inter }, nil } -// ImgStore.GroupPicUp -func decodeGroupImageStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { - pkt := pb.D388RespBody{} - err := proto.Unmarshal(payload, &pkt) - if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - rsp := pkt.MsgTryUpImgRsp[0] - if rsp.Result != 0 { - return imageUploadResponse{ - ResultCode: rsp.Result, - Message: rsp.FailMsg, - }, nil - } - if rsp.BoolFileExit { - if rsp.MsgImgInfo != nil { - return imageUploadResponse{IsExists: true, FileId: rsp.Fid, Width: rsp.MsgImgInfo.FileWidth, Height: rsp.MsgImgInfo.FileHeight}, nil - } - return imageUploadResponse{IsExists: true, FileId: rsp.Fid}, nil - } - return imageUploadResponse{ - FileId: rsp.Fid, - UploadKey: rsp.UpUkey, - UploadIp: rsp.Uint32UpIp, - UploadPort: rsp.Uint32UpPort, - }, nil -} - // LongConn.OffPicUp func decodeOffPicUpResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { rsp := cmd0x352.RspBody{} diff --git a/client/image.go b/client/image.go new file mode 100644 index 00000000..c9593417 --- /dev/null +++ b/client/image.go @@ -0,0 +1,243 @@ +package client + +import ( + "bytes" + "crypto/md5" + "encoding/hex" + "fmt" + "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/MiraiGo/client/pb" + "github.com/Mrs4s/MiraiGo/message" + "github.com/Mrs4s/MiraiGo/protocol/packets" + "github.com/Mrs4s/MiraiGo/utils" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" + "image" + "io" + "os" + "strings" +) + +func init() { + decoders["ImgStore.GroupPicUp"] = decodeGroupImageStoreResponse +} + +func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*message.GroupImageElement, error) { + h := md5.New() + length, _ := io.Copy(h, img) + fh := h.Sum(nil) + seq, pkt := c.buildGroupImageStorePacket(groupCode, fh[:], int32(length)) + r, err := c.sendAndWait(seq, pkt) + if err != nil { + return nil, err + } + rsp := r.(imageUploadResponse) + if rsp.ResultCode != 0 { + return nil, errors.New(rsp.Message) + } + if rsp.IsExists { + goto ok + } + _, _ = img.Seek(0, io.SeekStart) + if len(c.srvSsoAddrs) == 0 { + for i, addr := range rsp.UploadIp { + c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(uint32(addr)), rsp.UploadPort[i])) + } + } + if _, err = c.highwayUploadByBDH(img, 2, rsp.UploadKey, EmptyBytes); err == nil { + goto ok + } + return nil, errors.New("upload failed") +ok: + _, _ = img.Seek(0, io.SeekStart) + i, _, _ := image.DecodeConfig(img) + var imageType int32 = 1000 + _, _ = img.Seek(0, io.SeekStart) + tmp := make([]byte, 4) + _, _ = img.Read(tmp) + if bytes.Equal(tmp, []byte{0x47, 0x49, 0x46, 0x38}) { + imageType = 2000 + } + return message.NewGroupImage(binary.CalculateImageResourceId(fh[:]), fh[:], rsp.FileId, int32(length), int32(i.Width), int32(i.Height), imageType), nil +} + +func (c *QQClient) UploadGroupImageByFile(groupCode int64, path string) (*message.GroupImageElement, error) { + img, err := os.OpenFile(path, os.O_RDONLY, 0666) + if err != nil { + return nil, err + } + h := md5.New() + length, _ := io.Copy(h, img) + fh := h.Sum(nil) + seq, pkt := c.buildGroupImageStorePacket(groupCode, fh[:], int32(length)) + r, err := c.sendAndWait(seq, pkt) + if err != nil { + return nil, err + } + rsp := r.(imageUploadResponse) + if rsp.ResultCode != 0 { + return nil, errors.New(rsp.Message) + } + if rsp.IsExists { + goto ok + } + if len(c.srvSsoAddrs) == 0 { + for i, addr := range rsp.UploadIp { + c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(uint32(addr)), rsp.UploadPort[i])) + } + } + if _, err = c.highwayUploadFileMultiThreadingByBDH(path, 2, 4, rsp.UploadKey, EmptyBytes); err == nil { + goto ok + } + return nil, errors.New("upload failed") +ok: + _, _ = img.Seek(0, io.SeekStart) + i, _, _ := image.DecodeConfig(img) + var imageType int32 = 1000 + _, _ = img.Seek(0, io.SeekStart) + tmp := make([]byte, 4) + _, _ = img.Read(tmp) + if bytes.Equal(tmp, []byte{0x47, 0x49, 0x46, 0x38}) { + imageType = 2000 + } + return message.NewGroupImage(binary.CalculateImageResourceId(fh[:]), fh[:], rsp.FileId, int32(length), int32(i.Width), int32(i.Height), imageType), nil +} + +func (c *QQClient) UploadPrivateImage(target int64, img io.ReadSeeker) (*message.FriendImageElement, error) { + return c.uploadPrivateImage(target, img, 0) +} + +func (c *QQClient) uploadPrivateImage(target int64, img io.ReadSeeker, count int) (*message.FriendImageElement, error) { + count++ + h := md5.New() + length, _ := io.Copy(h, img) + fh := h.Sum(nil) + e, err := c.QueryFriendImage(target, fh[:], int32(length)) + if errors.Is(err, ErrNotExists) { + // use group highway upload and query again for image id. + if _, err = c.UploadGroupImage(target, img); err != nil { + return nil, err + } + if count >= 5 { + return e, nil + } + return c.uploadPrivateImage(target, img, count) + } + if err != nil { + return nil, err + } + return e, nil +} + +func (c *QQClient) ImageOcr(img interface{}) (*OcrResponse, error) { + switch e := img.(type) { + case *message.GroupImageElement: + rsp, err := c.sendAndWait(c.buildImageOcrRequestPacket(e.Url, strings.ToUpper(hex.EncodeToString(e.Md5)), e.Size, e.Width, e.Height)) + if err != nil { + return nil, err + } + return rsp.(*OcrResponse), nil + case *message.ImageElement: + rsp, err := c.sendAndWait(c.buildImageOcrRequestPacket(e.Url, strings.ToUpper(hex.EncodeToString(e.Md5)), e.Size, e.Width, e.Height)) + if err != nil { + return nil, err + } + return rsp.(*OcrResponse), nil + } + return nil, errors.New("image error") +} + +func (c *QQClient) QueryGroupImage(groupCode int64, hash []byte, size int32) (*message.GroupImageElement, error) { + r, err := c.sendAndWait(c.buildGroupImageStorePacket(groupCode, hash, size)) + if err != nil { + return nil, err + } + rsp := r.(imageUploadResponse) + if rsp.ResultCode != 0 { + return nil, errors.New(rsp.Message) + } + if rsp.IsExists { + return message.NewGroupImage(binary.CalculateImageResourceId(hash), hash, rsp.FileId, size, rsp.Width, rsp.Height, 1000), nil + } + return nil, errors.New("image does not exist") +} + +func (c *QQClient) QueryFriendImage(target int64, hash []byte, size int32) (*message.FriendImageElement, error) { + i, err := c.sendAndWait(c.buildOffPicUpPacket(target, hash, size)) + if err != nil { + return nil, err + } + rsp := i.(imageUploadResponse) + if rsp.ResultCode != 0 { + return nil, errors.New(rsp.Message) + } + if !rsp.IsExists { + return &message.FriendImageElement{ + ImageId: rsp.ResourceId, + Md5: hash, + }, errors.WithStack(ErrNotExists) + } + return &message.FriendImageElement{ + ImageId: rsp.ResourceId, + Md5: hash, + }, nil +} + +// ImgStore.GroupPicUp +func (c *QQClient) buildGroupImageStorePacket(groupCode int64, md5 []byte, size int32) (uint16, []byte) { + seq := c.nextSeq() + name := utils.RandomString(16) + ".gif" + req := &pb.D388ReqBody{ + NetType: 3, + Subcmd: 1, + MsgTryUpImgReq: []*pb.TryUpImgReq{ + { + GroupCode: groupCode, + SrcUin: c.Uin, + FileMd5: md5, + FileSize: int64(size), + FileName: name, + SrcTerm: 5, + PlatformType: 9, + BuType: 1, + PicType: 1000, + BuildVer: "8.2.7.4410", + AppPicType: 1006, + FileIndex: EmptyBytes, + TransferUrl: EmptyBytes, + }, + }, + Extension: EmptyBytes, + } + payload, _ := proto.Marshal(req) + packet := packets.BuildUniPacket(c.Uin, seq, "ImgStore.GroupPicUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) + return seq, packet +} + +// ImgStore.GroupPicUp +func decodeGroupImageStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) { + pkt := pb.D388RespBody{} + err := proto.Unmarshal(payload, &pkt) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + } + rsp := pkt.MsgTryUpImgRsp[0] + if rsp.Result != 0 { + return imageUploadResponse{ + ResultCode: rsp.Result, + Message: rsp.FailMsg, + }, nil + } + if rsp.BoolFileExit { + if rsp.MsgImgInfo != nil { + return imageUploadResponse{IsExists: true, FileId: rsp.Fid, Width: rsp.MsgImgInfo.FileWidth, Height: rsp.MsgImgInfo.FileHeight}, nil + } + return imageUploadResponse{IsExists: true, FileId: rsp.Fid}, nil + } + return imageUploadResponse{ + FileId: rsp.Fid, + UploadKey: rsp.UpUkey, + UploadIp: rsp.Uint32UpIp, + UploadPort: rsp.Uint32UpPort, + }, nil +}