diff --git a/client/image.go b/client/image.go index 2b1662db..838f3d63 100644 --- a/client/image.go +++ b/client/image.go @@ -150,19 +150,6 @@ func (c *QQClient) ImageOcr(img interface{}) (*OcrResponse, error) { return nil, err } return rsp.(*OcrResponse), nil - case *message.ImageElement: - url = e.Url - if b, err := utils.HTTPGetReadCloser(e.Url, ""); err == nil { - if url, err = c.uploadOcrImage(b, int64(e.Size), e.Md5); err != nil { - url = e.Url - } - _ = b.Close() - } - rsp, err := c.sendAndWait(c.buildImageOcrRequestPacket(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") } diff --git a/message/elements.go b/message/elements.go index ba41cee8..0910d47b 100644 --- a/message/elements.go +++ b/message/elements.go @@ -3,9 +3,7 @@ package message import ( "fmt" "strconv" - "strings" - "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client/pb/msg" ) @@ -15,27 +13,6 @@ type TextElement struct { Content string } -type ImageElement struct { - Filename string - Size int32 - Width int32 - Height int32 - Url string - Md5 []byte - Data []byte -} - -type GroupImageElement struct { - ImageId string - FileId int64 - ImageType int32 - Size int32 - Width int32 - Height int32 - Md5 []byte - Url string -} - type VoiceElement struct { Name string Md5 []byte @@ -56,12 +33,6 @@ type PrivateVoiceElement struct { Ptt *msg.Ptt } -type FriendImageElement struct { - ImageId string - Md5 []byte - Url string -} - type FaceElement struct { Index int32 Name string @@ -127,29 +98,6 @@ type MusicShareElement struct { MusicUrl string // 音乐播放链接 } -// TODO: 总之就是非常傻逼 - -type GroupFlashImgElement struct { - ImageElement -} - -type GroupFlashPicElement struct { - GroupImageElement -} - -type GroupShowPicElement struct { - GroupImageElement - EffectId int32 -} - -type FriendFlashImgElement struct { - ImageElement -} - -type FriendFlashPicElement struct { - FriendImageElement -} - type RedBagMessageType int // /com/tencent/mobileqq/data/MessageForQQWalletMsg.java @@ -180,25 +128,6 @@ func NewText(s string) *TextElement { return &TextElement{Content: s} } -func NewImage(data []byte) *ImageElement { - return &ImageElement{ - Data: data, - } -} - -func NewGroupImage(id string, md5 []byte, fid int64, size, width, height, imageType int32) *GroupImageElement { - return &GroupImageElement{ - ImageId: id, - FileId: fid, - Md5: md5, - Size: size, - ImageType: imageType, - Width: width, - Height: height, - Url: "https://gchat.qpic.cn/gchatpic_new/1/0-0-" + strings.ReplaceAll(binary.CalculateImageResourceId(md5)[1:37], "-", "") + "/0?term=2", - } -} - func NewFace(index int32) *FaceElement { name := faceMap[int(index)] if name == "" { @@ -291,30 +220,10 @@ func (e *TextElement) Type() ElementType { return Text } -func (e *ImageElement) Type() ElementType { - return Image -} - -func (e *GroupFlashImgElement) Type() ElementType { - return Image -} - -func (e *FriendFlashImgElement) Type() ElementType { - return Image -} - func (e *FaceElement) Type() ElementType { return Face } -func (e *GroupImageElement) Type() ElementType { - return Image -} - -func (e *FriendImageElement) Type() ElementType { - return Image -} - func (e *AtElement) Type() ElementType { return At } diff --git a/message/image.go b/message/image.go new file mode 100644 index 00000000..8c1cdec4 --- /dev/null +++ b/message/image.go @@ -0,0 +1,138 @@ +package message + +import ( + "strings" + + "google.golang.org/protobuf/proto" + + "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/MiraiGo/client/pb/msg" +) + +/* -------- Definitions -------- */ + +type GroupImageElement struct { + ImageId string + FileId int64 + ImageType int32 + Size int32 + Width int32 + Height int32 + Md5 []byte + Url string + + // EffectID show pic effect id. + EffectID int32 + Flash bool +} + +type FriendImageElement struct { + ImageId string + Md5 []byte + Size int32 + Url string + + Flash bool +} + +/* ------ Implementations ------ */ + +func NewGroupImage(id string, md5 []byte, fid int64, size, width, height, imageType int32) *GroupImageElement { + return &GroupImageElement{ + ImageId: id, + FileId: fid, + Md5: md5, + Size: size, + ImageType: imageType, + Width: width, + Height: height, + Url: "https://gchat.qpic.cn/gchatpic_new/1/0-0-" + strings.ReplaceAll(binary.CalculateImageResourceId(md5)[1:37], "-", "") + "/0?term=2", + } +} + +func (e *GroupImageElement) Type() ElementType { + return Image +} + +func (e *FriendImageElement) Type() ElementType { + return Image +} + +func (e *GroupImageElement) Pack() (r []*msg.Elem) { + cface := &msg.CustomFace{ + FileType: proto.Int32(66), + Useful: proto.Int32(1), + // Origin: 1, + BizType: proto.Int32(5), + Width: &e.Width, + Height: &e.Height, + FileId: proto.Int32(int32(e.FileId)), + FilePath: &e.ImageId, + ImageType: &e.ImageType, + Size: &e.Size, + Md5: e.Md5, + Flag: make([]byte, 4), + // OldData: imgOld, + } + + if e.Flash { // resolve flash pic + flash := &msg.MsgElemInfoServtype3{FlashTroopPic: cface} + data, _ := proto.Marshal(flash) + flashElem := &msg.Elem{ + CommonElem: &msg.CommonElem{ + ServiceType: proto.Int32(3), + PbElem: data, + }, + } + textHint := &msg.Elem{ + Text: &msg.Text{ + Str: proto.String("[闪照]请使用新版手机QQ查看闪照。"), + }, + } + return []*msg.Elem{flashElem, textHint} + } + + if e.EffectID != 0 { // resolve show pic + res := &msg.ResvAttr{ImageShow: &msg.AnimationImageShow{ + EffectId: &e.EffectID, + AnimationParam: []byte("{}"), + }} + cface.PbReserve, _ = proto.Marshal(res) + cface.Flag = []byte{0x11, 0x00, 0x00, 0x00} + } + elem := &msg.Elem{CustomFace: cface} + return []*msg.Elem{elem} +} + +func (e *FriendImageElement) Pack() (r []*msg.Elem) { + r = []*msg.Elem{} + image := &msg.NotOnlineImage{ + FilePath: &e.ImageId, + ResId: &e.ImageId, + OldPicMd5: proto.Bool(false), + PicMd5: e.Md5, + DownloadPath: &e.ImageId, + Original: proto.Int32(1), + PbReserve: []byte{0x78, 0x02}, + } + + if e.Flash { + flash := &msg.MsgElemInfoServtype3{FlashC2CPic: image} + data, _ := proto.Marshal(flash) + flashElem := &msg.Elem{ + CommonElem: &msg.CommonElem{ + ServiceType: proto.Int32(3), + PbElem: data, + }, + } + textHint := &msg.Elem{ + Text: &msg.Text{ + Str: proto.String("[闪照]请使用新版手机QQ查看闪照。"), + }, + } + return []*msg.Elem{flashElem, textHint} + } + + elem := &msg.Elem{NotOnlineImage: image} + return []*msg.Elem{elem} +} diff --git a/message/message.go b/message/message.go index ca60aeb1..95f807c6 100644 --- a/message/message.go +++ b/message/message.go @@ -131,11 +131,6 @@ func (msg *PrivateMessage) ToString() (res string) { switch e := elem.(type) { case *TextElement: res += e.Content - case *ImageElement: - res += "[Image:" + e.Filename + "]" - case *FriendFlashImgElement: - // NOTE: ignore other components - return "[Image (flash):" + e.Filename + "]" case *FaceElement: res += "[" + e.Name + "]" case *AtElement: @@ -150,8 +145,6 @@ func (msg *TempMessage) ToString() (res string) { switch e := elem.(type) { case *TextElement: res += e.Content - case *ImageElement: - res += "[Image:" + e.Filename + "]" case *FaceElement: res += "[" + e.Name + "]" case *AtElement: @@ -166,15 +159,10 @@ func (msg *GroupMessage) ToString() (res string) { switch e := elem.(type) { case *TextElement: res += e.Content - case *ImageElement: - res += "[Image:" + e.Filename + "]" case *FaceElement: res += "[" + e.Name + "]" case *GroupImageElement: res += "[Image: " + e.ImageId + "]" - case *GroupFlashImgElement: - // NOTE: ignore other components - return "[Image (flash):" + e.Filename + "]" case *AtElement: res += e.Display case *RedBagElement: @@ -246,7 +234,7 @@ func EstimateLength(elems []IMessageElement) int { sum += len(e.Display) case *ReplyElement: sum += 444 + EstimateLength(e.Elements) - case *ImageElement, *GroupImageElement, *FriendImageElement: + case *GroupImageElement, *FriendImageElement: sum += 100 default: sum += len(ToReadableString([]IMessageElement{elem})) @@ -332,15 +320,15 @@ func ToProtoElems(elems []IMessageElement, generalFlags bool) (r []*msg.Elem) { func ToSrcProtoElems(elems []IMessageElement) (r []*msg.Elem) { for _, elem := range elems { - switch e := elem.(type) { - case *ImageElement, *GroupImageElement, *FriendImageElement: + switch elem.Type() { + case Image: r = append(r, &msg.Elem{ Text: &msg.Text{ Str: proto.String("[图片]"), }, }) default: - r = append(r, ToProtoElems([]IMessageElement{e}, false)...) + r = append(r, ToProtoElems([]IMessageElement{elem}, false)...) } } return @@ -456,11 +444,11 @@ func ParseMessageElems(elems []*msg.Elem) []IMessageElement { if len(elem.CustomFace.Md5) == 0 { continue } - res = append(res, &ImageElement{ - Filename: elem.CustomFace.GetFilePath(), - Size: elem.CustomFace.GetSize(), - Width: elem.CustomFace.GetWidth(), - Height: elem.CustomFace.GetHeight(), + res = append(res, &GroupImageElement{ + ImageId: elem.CustomFace.GetFilePath(), + Size: elem.CustomFace.GetSize(), + Width: elem.CustomFace.GetWidth(), + Height: elem.CustomFace.GetHeight(), Url: func() string { if elem.CustomFace.GetOrigUrl() == "" { return "https://gchat.qpic.cn/gchatpic_new/0/0-0-" + strings.ReplaceAll(binary.CalculateImageResourceId(elem.CustomFace.Md5)[1:37], "-", "") + "/0?term=2" @@ -477,11 +465,11 @@ func ParseMessageElems(elems []*msg.Elem) []IMessageElement { } else { img = "https://c2cpicdw.qpic.cn/offpic_new/0/" + elem.NotOnlineImage.GetResId() + "/0?term=2" } - res = append(res, &ImageElement{ - Filename: elem.NotOnlineImage.GetFilePath(), - Size: elem.NotOnlineImage.GetFileLen(), - Url: img, - Md5: elem.NotOnlineImage.PicMd5, + res = append(res, &FriendImageElement{ + ImageId: elem.NotOnlineImage.GetFilePath(), + Size: elem.NotOnlineImage.GetFileLen(), + Url: img, + Md5: elem.NotOnlineImage.PicMd5, }) } if elem.QQWalletMsg != nil && elem.QQWalletMsg.AioBody != nil { @@ -505,24 +493,22 @@ func ParseMessageElems(elems []*msg.Elem) []IMessageElement { flash := &msg.MsgElemInfoServtype3{} _ = proto.Unmarshal(elem.CommonElem.PbElem, flash) if flash.FlashTroopPic != nil { - res = append(res, &GroupFlashImgElement{ - ImageElement{ - Filename: flash.FlashTroopPic.GetFilePath(), - Size: flash.FlashTroopPic.GetSize(), - Width: flash.FlashTroopPic.GetWidth(), - Height: flash.FlashTroopPic.GetHeight(), - Md5: flash.FlashTroopPic.Md5, - }, + res = append(res, &GroupImageElement{ + ImageId: flash.FlashTroopPic.GetFilePath(), + Size: flash.FlashTroopPic.GetSize(), + Width: flash.FlashTroopPic.GetWidth(), + Height: flash.FlashTroopPic.GetHeight(), + Md5: flash.FlashTroopPic.Md5, + Flash: true, }) return res } if flash.FlashC2CPic != nil { - res = append(res, &GroupFlashImgElement{ - ImageElement{ - Filename: flash.FlashC2CPic.GetFilePath(), - Size: flash.FlashC2CPic.GetFileLen(), - Md5: flash.FlashC2CPic.PicMd5, - }, + res = append(res, &FriendImageElement{ + ImageId: flash.FlashC2CPic.GetFilePath(), + Size: flash.FlashC2CPic.GetFileLen(), + Md5: flash.FlashC2CPic.PicMd5, + Flash: true, }) return res } @@ -536,19 +522,19 @@ func ParseMessageElems(elems []*msg.Elem) []IMessageElement { return res } -func ToReadableString(m []IMessageElement) (r string) { +func ToReadableString(m []IMessageElement) string { + sb := new(strings.Builder) for _, elem := range m { switch e := elem.(type) { case *TextElement: - r += e.Content - case *ImageElement: - r += "[图片]" + sb.WriteString(e.Content) + case *GroupImageElement, *FriendImageElement: + sb.WriteString("[图片]") case *FaceElement: - r += "/" + e.Name - case *GroupImageElement: - r += "[图片]" + sb.WriteByte('/') + sb.WriteString(e.Name) case *ForwardElement: - r += "[聊天记录]" + sb.WriteString("[聊天记录]") // NOTE: flash pic is singular // To be clarified // case *GroupFlashImgElement: @@ -556,10 +542,10 @@ func ToReadableString(m []IMessageElement) (r string) { // case *FriendFlashImgElement: // return "[闪照]" case *AtElement: - r += e.Display + sb.WriteString(e.Display) } } - return + return sb.String() } func FaceNameById(id int) string { diff --git a/message/pack.go b/message/pack.go index ac416a43..f2ea0b0e 100644 --- a/message/pack.go +++ b/message/pack.go @@ -9,11 +9,13 @@ import ( "github.com/Mrs4s/MiraiGo/client/pb/msg" ) +/* var imgOld = []byte{ 0x15, 0x36, 0x20, 0x39, 0x32, 0x6B, 0x41, 0x31, 0x00, 0x38, 0x37, 0x32, 0x66, 0x30, 0x36, 0x36, 0x30, 0x33, 0x61, 0x65, 0x31, 0x30, 0x33, 0x62, 0x37, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x35, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x30, 0x31, 0x45, 0x39, 0x34, 0x35, 0x31, 0x42, 0x2D, 0x37, 0x30, 0x45, 0x44, 0x2D, 0x45, 0x41, 0x45, 0x33, 0x2D, 0x42, 0x33, 0x37, 0x43, 0x2D, 0x31, 0x30, 0x31, 0x46, 0x31, 0x45, 0x45, 0x42, 0x46, 0x35, 0x42, 0x35, 0x7D, 0x2E, 0x70, 0x6E, 0x67, 0x41, } +*/ func (e *TextElement) Pack() (r []*msg.Elem) { r = append(r, &msg.Elem{ @@ -76,58 +78,6 @@ func (e *AtElement) Pack() (r []*msg.Elem) { return } -func (e *ImageElement) Pack() (r []*msg.Elem) { - r = []*msg.Elem{} - r = append(r, &msg.Elem{ - CustomFace: &msg.CustomFace{ - FilePath: &e.Filename, - Md5: e.Md5, - Size: &e.Size, - Flag: make([]byte, 4), - OldData: imgOld, - }, - }) - return -} - -func (e *GroupImageElement) Pack() (r []*msg.Elem) { - r = []*msg.Elem{} - r = append(r, &msg.Elem{ - CustomFace: &msg.CustomFace{ - FileType: proto.Int32(66), - Useful: proto.Int32(1), - // Origin: 1, - BizType: proto.Int32(5), - Width: &e.Width, - Height: &e.Height, - FileId: proto.Int32(int32(e.FileId)), - FilePath: &e.ImageId, - ImageType: &e.ImageType, - Size: &e.Size, - Md5: e.Md5, - Flag: make([]byte, 4), - // OldData: imgOld, - }, - }) - return -} - -func (e *FriendImageElement) Pack() (r []*msg.Elem) { - r = []*msg.Elem{} - r = append(r, &msg.Elem{ - NotOnlineImage: &msg.NotOnlineImage{ - FilePath: &e.ImageId, - ResId: &e.ImageId, - OldPicMd5: proto.Bool(false), - PicMd5: e.Md5, - DownloadPath: &e.ImageId, - Original: proto.Int32(1), - PbReserve: []byte{0x78, 0x02}, - }, - }) - return -} - func (e *ServiceElement) Pack() (r []*msg.Elem) { r = []*msg.Elem{} // id =35 已移至 ForwardElement @@ -156,86 +106,6 @@ func (e *LightAppElement) Pack() (r []*msg.Elem) { return } -func (e *FriendFlashPicElement) Pack() (r []*msg.Elem) { - r = []*msg.Elem{} - flash := &msg.MsgElemInfoServtype3{ - FlashC2CPic: &msg.NotOnlineImage{ - FilePath: &e.ImageId, - ResId: &e.ImageId, - OldPicMd5: proto.Bool(false), - PicMd5: e.Md5, - DownloadPath: &e.ImageId, - Original: proto.Int32(1), - PbReserve: []byte{0x78, 0x02}, - }, - } - data, _ := proto.Marshal(flash) - r = append(r, &msg.Elem{ - CommonElem: &msg.CommonElem{ - ServiceType: proto.Int32(3), - PbElem: data, - }, - }) - r = append(r, &msg.Elem{ - Text: &msg.Text{ - Str: proto.String("[闪照]请使用新版手机QQ查看闪照。"), - }, - }) - return -} - -func (e *GroupFlashPicElement) Pack() (r []*msg.Elem) { - r = []*msg.Elem{} - flash := &msg.MsgElemInfoServtype3{ - FlashTroopPic: &msg.CustomFace{ - FileType: proto.Int32(66), - Useful: proto.Int32(1), - Origin: proto.Int32(1), - FileId: proto.Int32(int32(e.FileId)), - FilePath: &e.ImageId, - Size: &e.Size, - Md5: e.Md5, - Flag: make([]byte, 4), - }, - } - data, _ := proto.Marshal(flash) - r = append(r, &msg.Elem{ - CommonElem: &msg.CommonElem{ - ServiceType: proto.Int32(3), - PbElem: data, - }, - }) - r = append(r, &msg.Elem{ - Text: &msg.Text{ - Str: proto.String("[闪照]请使用新版手机QQ查看闪照。"), - }, - }) - return -} - -func (e *GroupShowPicElement) Pack() (r []*msg.Elem) { - r = []*msg.Elem{} - res := &msg.ResvAttr{ImageShow: &msg.AnimationImageShow{ - EffectId: &e.EffectId, - AnimationParam: []byte("{}"), - }} - reserve, _ := proto.Marshal(res) - r = append(r, &msg.Elem{ - CustomFace: &msg.CustomFace{ - FileType: proto.Int32(0), - Useful: proto.Int32(1), - ImageType: proto.Int32(1001), - FileId: proto.Int32(int32(e.FileId)), - FilePath: &e.ImageId, - Size: &e.Size, - Md5: e.Md5, - Flag: []byte{0x11, 0x00, 0x00, 0x00}, - PbReserve: reserve, - }, - }) - return -} - func (e *ShortVideoElement) Pack() (r []*msg.Elem) { r = append(r, &msg.Elem{ Text: &msg.Text{