diff --git a/client/guild_msg.go b/client/guild_msg.go index 2ddab4c6..1d29f776 100644 --- a/client/guild_msg.go +++ b/client/guild_msg.go @@ -2,6 +2,7 @@ package client import ( "bytes" + "crypto/md5" "encoding/hex" "fmt" "image" @@ -16,6 +17,7 @@ import ( "github.com/Mrs4s/MiraiGo/client/pb/channel" "github.com/Mrs4s/MiraiGo/client/pb/cmd0x388" "github.com/Mrs4s/MiraiGo/client/pb/msg" + "github.com/Mrs4s/MiraiGo/client/pb/pttcenter" "github.com/Mrs4s/MiraiGo/internal/packets" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" @@ -42,10 +44,7 @@ func init() { func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *message.SendingMessage) (*message.GuildChannelMessage, error) { mr := rand.Uint32() // 客户端似乎是生成的 u32 虽然类型是u64 - at := m.FirstOrNil(func(e message.IMessageElement) bool { - _, ok := e.(*message.AtElement) - return ok - }) + at := m.FirstOrNil(func(e message.IMessageElement) bool { return e.Type() == message.At }) if at != nil { at.(*message.AtElement).Guild = true } @@ -284,3 +283,72 @@ func (c *QQClient) parseGuildChannelMessage(msg *channel.ChannelMsgContent) *mes Elements: message.ParseMessageElems(msg.Body.RichText.Elems), } } + +// PttCenterSvr.GroupShortVideoUpReq +func (c *QQClient) buildPttGuildVideoUpReq(videoHash, thumbHash []byte, guildId, channelId int64, videoSize, thumbSize int64) (uint16, []byte) { + seq := c.nextSeq() + pb := c.buildPttGroupShortVideoProto(videoHash, thumbHash, guildId, videoSize, thumbSize, 4) + pb.PttShortVideoUploadReq.BusinessType = 4601 + pb.PttShortVideoUploadReq.ToUin = channelId + pb.ExtensionReq[0].SubBusiType = 4601 + payload, _ := proto.Marshal(pb) + packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.GroupShortVideoUpReq", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) + return seq, packet +} + +func (c *QQClient) UploadGuildShortVideo(guildId, channelId uint64, video, thumb io.ReadSeeker) (*message.ShortVideoElement, error) { + // todo: combine with group short video upload + videoHash, videoLen := utils.ComputeMd5AndLength(video) + thumbHash, thumbLen := utils.ComputeMd5AndLength(thumb) + + key := string(videoHash) + string(thumbHash) + pttWaiter.Wait(key) + defer pttWaiter.Done(key) + + i, err := c.sendAndWait(c.buildPttGuildVideoUpReq(videoHash, thumbHash, int64(guildId), int64(channelId), videoLen, thumbLen)) + if err != nil { + return nil, errors.Wrap(err, "upload req error") + } + rsp := i.(*pttcenter.ShortVideoUploadRsp) + if rsp.FileExists == 1 { + return &message.ShortVideoElement{ + Uuid: []byte(rsp.FileId), + Size: int32(videoLen), + ThumbSize: int32(thumbLen), + Md5: videoHash, + ThumbMd5: thumbHash, + Guild: true, + }, nil + } + req := c.buildPttGroupShortVideoProto(videoHash, thumbHash, int64(guildId), videoLen, thumbLen, 4).PttShortVideoUploadReq + req.BusinessType = 4601 + req.ToUin = int64(channelId) + ext, _ := proto.Marshal(req) + var hwRsp []byte + multi := utils.MultiReadSeeker(thumb, video) + h := md5.New() + length, _ := io.Copy(h, multi) + fh := h.Sum(nil) + _, _ = multi.Seek(0, io.SeekStart) + + hwRsp, err = c.highwayUploadByBDH(multi, length, 89, c.bigDataSession.SigSession, fh, ext, true) + if err != nil { + return nil, errors.Wrap(err, "upload video file error") + } + + if len(hwRsp) == 0 { + return nil, errors.New("resp is empty") + } + rsp = &pttcenter.ShortVideoUploadRsp{} + if err = proto.Unmarshal(hwRsp, rsp); err != nil { + return nil, errors.Wrap(err, "decode error") + } + return &message.ShortVideoElement{ + Uuid: []byte(rsp.FileId), + Size: int32(videoLen), + ThumbSize: int32(thumbLen), + Md5: videoHash, + ThumbMd5: thumbHash, + Guild: true, + }, nil +} diff --git a/client/ptt.go b/client/ptt.go index 6f8756b6..4703f805 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -134,7 +134,7 @@ func (c *QQClient) UploadGroupShortVideo(groupCode int64, video, thumb io.ReadSe ThumbMd5: thumbHash, }, nil } - ext, _ := proto.Marshal(c.buildPttGroupShortVideoProto(videoHash, thumbHash, groupCode, videoLen, thumbLen).PttShortVideoUploadReq) + ext, _ := proto.Marshal(c.buildPttGroupShortVideoProto(videoHash, thumbHash, groupCode, videoLen, thumbLen, 1).PttShortVideoUploadReq) var hwRsp []byte multi := utils.MultiReadSeeker(thumb, video) h := md5.New() @@ -245,7 +245,7 @@ func (c *QQClient) buildPttShortVideoDownReqPacket(uuid, md5 []byte) (uint16, [] return seq, packet } -func (c *QQClient) buildPttGroupShortVideoProto(videoHash, thumbHash []byte, toUin, videoSize, thumbSize int64) *pttcenter.ShortVideoReqBody { +func (c *QQClient) buildPttGroupShortVideoProto(videoHash, thumbHash []byte, toUin, videoSize, thumbSize int64, chattype int32) *pttcenter.ShortVideoReqBody { seq := c.nextSeq() return &pttcenter.ShortVideoReqBody{ Cmd: 300, @@ -253,7 +253,7 @@ func (c *QQClient) buildPttGroupShortVideoProto(videoHash, thumbHash []byte, toU PttShortVideoUploadReq: &pttcenter.ShortVideoUploadReq{ FromUin: c.Uin, ToUin: toUin, - ChatType: 1, + ChatType: chattype, ClientType: 2, Info: &pttcenter.ShortVideoFileInfo{ FileName: hex.EncodeToString(videoHash) + ".mp4", @@ -281,7 +281,7 @@ func (c *QQClient) buildPttGroupShortVideoProto(videoHash, thumbHash []byte, toU // PttCenterSvr.GroupShortVideoUpReq func (c *QQClient) buildPttGroupShortVideoUploadReqPacket(videoHash, thumbHash []byte, toUin, videoSize, thumbSize int64) (uint16, []byte) { seq := c.nextSeq() - payload, _ := proto.Marshal(c.buildPttGroupShortVideoProto(videoHash, thumbHash, toUin, videoSize, thumbSize)) + payload, _ := proto.Marshal(c.buildPttGroupShortVideoProto(videoHash, thumbHash, toUin, videoSize, thumbSize, 1)) packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.GroupShortVideoUpReq", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return seq, packet } diff --git a/message/elements.go b/message/elements.go index 85a04d1f..b8577eaa 100644 --- a/message/elements.go +++ b/message/elements.go @@ -69,6 +69,7 @@ type ShortVideoElement struct { Md5 []byte ThumbMd5 []byte Url string + Guild bool } type ServiceElement struct { diff --git a/message/pack.go b/message/pack.go index fd336dba..b3b0a9f0 100644 --- a/message/pack.go +++ b/message/pack.go @@ -72,12 +72,11 @@ func (e *AtElement) Pack() (r []*msg.Elem) { w.WriteUInt16(1) w.WriteUInt16(0) w.WriteUInt16(uint16(len([]rune(e.Display)))) - w.WriteByte(func() byte { - if e.Target == 0 { - return 1 - } - return 0 - }()) + if e.Target == 0 { + w.WriteByte(1) + } else { + w.WriteByte(0) + } w.WriteUInt32(uint32(e.Target)) w.WriteUInt16(0) }), @@ -122,25 +121,34 @@ func (e *ShortVideoElement) Pack() (r []*msg.Elem) { Str: proto.String("你的QQ暂不支持查看视频短片,请期待后续版本。"), }, }) - r = append(r, &msg.Elem{ - VideoFile: &msg.VideoFile{ - FileUuid: e.Uuid, - FileMd5: e.Md5, - FileName: []byte(hex.EncodeToString(e.Md5) + ".mp4"), - FileFormat: proto.Int32(3), - FileTime: proto.Int32(10), - FileSize: proto.Int32(e.Size), - ThumbWidth: proto.Int32(1280), - ThumbHeight: proto.Int32(720), - ThumbFileMd5: e.ThumbMd5, - ThumbFileSize: proto.Int32(e.ThumbSize), - BusiType: proto.Int32(0), - FromChatType: proto.Int32(-1), - ToChatType: proto.Int32(-1), - BoolSupportProgressive: proto.Bool(true), - FileWidth: proto.Int32(1280), - FileHeight: proto.Int32(720), - }, - }) + + video := &msg.VideoFile{ + FileUuid: e.Uuid, + FileMd5: e.Md5, + FileName: []byte(hex.EncodeToString(e.Md5) + ".mp4"), + FileFormat: proto.Int32(3), + FileTime: proto.Int32(10), + FileSize: proto.Int32(e.Size), + ThumbWidth: proto.Int32(1280), + ThumbHeight: proto.Int32(720), + ThumbFileMd5: e.ThumbMd5, + ThumbFileSize: proto.Int32(e.ThumbSize), + BusiType: proto.Int32(0), + FromChatType: proto.Int32(-1), + ToChatType: proto.Int32(-1), + BoolSupportProgressive: proto.Bool(true), + FileWidth: proto.Int32(1280), + FileHeight: proto.Int32(720), + } + + if e.Guild { + video.BusiType = proto.Int32(4601) + video.SubBusiType = proto.Int32(4601) + video.FileWidth = proto.Int32(0) + video.FileHeight = proto.Int32(0) + video.VideoAttr = proto.Int32(0) + } + + r = append(r, &msg.Elem{VideoFile: video}) return }