package message import ( "crypto/md5" "math" "reflect" "regexp" "strconv" "strings" "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/utils" "github.com/golang/protobuf/proto" "github.com/tidwall/gjson" ) type ( PrivateMessage struct { Id int32 InternalId int32 Target int64 Time int32 Sender *Sender Elements []IMessageElement } TempMessage struct { Id int32 GroupCode int64 GroupName string Sender *Sender Elements []IMessageElement } GroupMessage struct { Id int32 InternalId int32 GroupCode int64 GroupName string Sender *Sender Time int32 Elements []IMessageElement //OriginalElements []*msg.Elem } SendingMessage struct { Elements []IMessageElement } ForwardMessage struct { Nodes []*ForwardNode } ForwardNode struct { SenderId int64 SenderName string Time int32 Message []IMessageElement } RichMessage struct { Title string Summary string Brief string Url string PictureUrl string MusicUrl string } Sender struct { Uin int64 Nickname string CardName string IsFriend bool } IMessageElement interface { Type() ElementType } IRichMessageElement interface { Pack() []*msg.Elem } ElementType int GroupGift int ) const ( Text ElementType = iota Image Face At Reply Service Forward File Voice Video LightApp RedBag SweetWink GroupGift = 285 HappyCola GroupGift = 289 LuckyBracelet GroupGift = 290 Cappuccino GroupGift = 299 CatWatch GroupGift = 302 FleeceGloves GroupGift = 307 RainbowCandy GroupGift = 308 Stronger GroupGift = 313 LoveMicrophone GroupGift = 367 ) func (s *Sender) IsAnonymous() bool { return s.Uin == 80000000 } func NewSendingMessage() *SendingMessage { return &SendingMessage{} } func (msg *PrivateMessage) ToString() (res string) { for _, elem := range msg.Elements { switch e := elem.(type) { case *TextElement: res += e.Content case *ImageElement: res += "[Image:" + e.Filename + "]" case *FaceElement: res += "[" + e.Name + "]" case *AtElement: res += e.Display } } return } func (msg *TempMessage) ToString() (res string) { for _, elem := range msg.Elements { switch e := elem.(type) { case *TextElement: res += e.Content case *ImageElement: res += "[Image:" + e.Filename + "]" case *FaceElement: res += "[" + e.Name + "]" case *AtElement: res += e.Display } } return } func (msg *GroupMessage) ToString() (res string) { for _, elem := range msg.Elements { 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 *AtElement: res += e.Display case *RedBagElement: res += "[RedBag:" + e.Title + "]" case *ReplyElement: res += "[Reply:" + strconv.FormatInt(int64(e.ReplySeq), 10) + "]" } } return } func (msg *SendingMessage) Append(e IMessageElement) *SendingMessage { v := reflect.ValueOf(e) if v.Kind() == reflect.Ptr && !v.IsNil() { msg.Elements = append(msg.Elements, e) } return msg } func (msg *SendingMessage) Any(filter func(e IMessageElement) bool) bool { for _, e := range msg.Elements { if filter(e) { return true } } return false } func (msg *SendingMessage) FirstOrNil(filter func(e IMessageElement) bool) IMessageElement { for _, e := range msg.Elements { if filter(e) { return e } } return nil } func (msg *SendingMessage) Count(filter func(e IMessageElement) bool) (c int) { for _, e := range msg.Elements { if filter(e) { c++ } } return } func (msg *SendingMessage) ToFragmented() [][]IMessageElement { var fragmented [][]IMessageElement for _, elem := range msg.Elements { switch o := elem.(type) { case *TextElement: for _, text := range utils.ChunkString(o.Content, 220) { fragmented = append(fragmented, []IMessageElement{NewText(text)}) } default: fragmented = append(fragmented, []IMessageElement{o}) } } return fragmented } func EstimateLength(elems []IMessageElement, limit int) int { sum := 0 for _, elem := range elems { if sum >= limit { break } left := int(math.Max(float64(limit-sum), 0)) switch e := elem.(type) { case *TextElement: sum += utils.ChineseLength(e.Content, left) case *AtElement: sum += utils.ChineseLength(e.Display, left) case *ReplyElement: sum += 444 + EstimateLength(e.Elements, left) case *ImageElement, *GroupImageElement, *FriendImageElement: sum += 260 default: sum += utils.ChineseLength(ToReadableString([]IMessageElement{elem}), left) } } return sum } func (s *Sender) DisplayName() string { if s.CardName == "" { return s.Nickname } return s.CardName } func ToProtoElems(elems []IMessageElement, generalFlags bool) (r []*msg.Elem) { if len(elems) == 0 { return nil } for _, elem := range elems { if reply, ok := elem.(*ReplyElement); ok { r = append(r, &msg.Elem{ SrcMsg: &msg.SourceMsg{ OrigSeqs: []int32{reply.ReplySeq}, SenderUin: reply.Sender, Time: reply.Time, Flag: 1, Elems: ToSrcProtoElems(reply.Elements), RichMsg: []byte{}, PbReserve: []byte{}, SrcMsg: []byte{}, TroopName: []byte{}, }, }) } } for _, elem := range elems { if e, ok := elem.(IRichMessageElement); ok { r = append(r, e.Pack()...) } } if generalFlags { L: for _, elem := range elems { switch e := elem.(type) { case *ServiceElement: if e.SubType == "Long" { r = append(r, &msg.Elem{ GeneralFlags: &msg.GeneralFlags{ LongTextFlag: 1, LongTextResid: e.ResId, PbReserve: []byte{0x78, 0x00, 0xF8, 0x01, 0x00, 0xC8, 0x02, 0x00}, }, }) break L } //d, _ := hex.DecodeString("08097800C80100F00100F80100900200C80200980300A00320B00300C00300D00300E803008A04020803900480808010B80400C00400") r = append(r, &msg.Elem{ GeneralFlags: &msg.GeneralFlags{ PbReserve: []byte{ 0x08, 0x09, 0x78, 0x00, 0xC8, 0x01, 0x00, 0xF0, 0x01, 0x00, 0xF8, 0x01, 0x00, 0x90, 0x02, 0x00, 0xC8, 0x02, 0x00, 0x98, 0x03, 0x00, 0xA0, 0x03, 0x20, 0xB0, 0x03, 0x00, 0xC0, 0x03, 0x00, 0xD0, 0x03, 0x00, 0xE8, 0x03, 0x00, 0x8A, 0x04, 0x02, 0x08, 0x03, 0x90, 0x04, 0x80, 0x80, 0x80, 0x10, 0xB8, 0x04, 0x00, 0xC0, 0x04, 0x00, }, }, }) break L } } } return } func ToSrcProtoElems(elems []IMessageElement) (r []*msg.Elem) { for _, elem := range elems { switch e := elem.(type) { case *ImageElement, *GroupImageElement, *FriendImageElement: r = append(r, &msg.Elem{ Text: &msg.Text{ Str: "[图片]", }, }) default: r = append(r, ToProtoElems([]IMessageElement{e}, false)...) } } return } func ParseMessageElems(elems []*msg.Elem) []IMessageElement { var res []IMessageElement for _, elem := range elems { if elem.SrcMsg != nil { if len(elem.SrcMsg.OrigSeqs) != 0 { r := &ReplyElement{ ReplySeq: elem.SrcMsg.OrigSeqs[0], Time: elem.SrcMsg.Time, Sender: elem.SrcMsg.SenderUin, Elements: ParseMessageElems(elem.SrcMsg.Elems), } res = append(res, r) } continue } if elem.TransElemInfo != nil { if elem.TransElemInfo.ElemType == 24 { // QFile i3 := len(elem.TransElemInfo.ElemValue) r := binary.NewReader(elem.TransElemInfo.ElemValue) if i3 > 3 { if r.ReadByte() == 1 { pb := r.ReadBytes(int(r.ReadUInt16())) objMsg := msg.ObjMsg{} if err := proto.Unmarshal(pb, &objMsg); err == nil && len(objMsg.MsgContentInfo) > 0 { info := objMsg.MsgContentInfo[0] res = append(res, &GroupFileElement{ Name: info.MsgFile.FileName, Size: info.MsgFile.FileSize, Path: string(info.MsgFile.FilePath), Busid: info.MsgFile.BusId, }) } } } } } if elem.LightApp != nil && len(elem.LightApp.Data) > 1 { var content string if elem.LightApp.Data[0] == 0 { content = string(elem.LightApp.Data[1:]) } if elem.LightApp.Data[0] == 1 { content = string(binary.ZlibUncompress(elem.LightApp.Data[1:])) } if content != "" { // TODO: 解析具体的APP return append(res, &LightAppElement{Content: content}) } } if elem.VideoFile != nil { return append(res, &ShortVideoElement{ Name: string(elem.VideoFile.FileName), Uuid: elem.VideoFile.FileUuid, Size: elem.VideoFile.FileSize, Md5: elem.VideoFile.FileMd5, }) } if elem.Text != nil { if len(elem.Text.Attr6Buf) == 0 { res = append(res, NewText(func() string { // 这么处理应该没问题 if strings.Contains(elem.Text.Str, "\r") && !strings.Contains(elem.Text.Str, "\r\n") { return strings.ReplaceAll(elem.Text.Str, "\r", "\r\n") } return elem.Text.Str }())) } else { att6 := binary.NewReader(elem.Text.Attr6Buf) att6.ReadBytes(7) target := int64(uint32(att6.ReadInt32())) res = append(res, NewAt(target, elem.Text.Str)) } } if elem.RichMsg != nil { var content string if elem.RichMsg.Template1[0] == 0 { content = string(elem.RichMsg.Template1[1:]) } if elem.RichMsg.Template1[0] == 1 { content = string(binary.ZlibUncompress(elem.RichMsg.Template1[1:])) } if content != "" { if elem.RichMsg.ServiceId == 35 { reg := regexp.MustCompile(`m_resid="(\w+?.*?)"`) res = append(res, &ForwardElement{ResId: reg.FindAllStringSubmatch(content, -1)[0][1]}) continue } if elem.RichMsg.ServiceId == 33 { continue // 前面一个 elem 已经解析到链接 } if isOk := strings.Contains(content, "