github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/message/message.go (about)

     1  package message
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/Mrs4s/MiraiGo/binary"
    11  	"github.com/Mrs4s/MiraiGo/client/pb/msg"
    12  	"github.com/Mrs4s/MiraiGo/internal/proto"
    13  	"github.com/Mrs4s/MiraiGo/utils"
    14  )
    15  
    16  type IMessage interface {
    17  	GetElements() []IMessageElement
    18  	Chat() int64
    19  	ToString() string
    20  	Texts() []string
    21  }
    22  
    23  type (
    24  	PrivateMessage struct {
    25  		Id         int32
    26  		InternalId int32
    27  		Self       int64
    28  		Target     int64
    29  		Time       int32
    30  		Sender     *Sender
    31  		Elements   []IMessageElement
    32  	}
    33  
    34  	TempMessage struct {
    35  		Id        int32
    36  		GroupCode int64
    37  		GroupName string
    38  		Self      int64
    39  		Sender    *Sender
    40  		Elements  []IMessageElement
    41  	}
    42  
    43  	GroupMessage struct {
    44  		Id             int32
    45  		InternalId     int32
    46  		GroupCode      int64
    47  		GroupName      string
    48  		Sender         *Sender
    49  		Time           int32
    50  		Elements       []IMessageElement
    51  		OriginalObject *msg.Message
    52  		// OriginalElements []*msg.Elem
    53  	}
    54  
    55  	SendingMessage struct {
    56  		Elements []IMessageElement
    57  	}
    58  
    59  	Sender struct {
    60  		Uin           int64
    61  		Nickname      string
    62  		CardName      string
    63  		AnonymousInfo *AnonymousInfo
    64  		IsFriend      bool
    65  	}
    66  
    67  	AnonymousInfo struct {
    68  		AnonymousId   string
    69  		AnonymousNick string
    70  	}
    71  
    72  	IMessageElement interface {
    73  		Type() ElementType
    74  	}
    75  
    76  	IRichMessageElement interface {
    77  		Pack() []*msg.Elem
    78  	}
    79  
    80  	ElementType int
    81  )
    82  
    83  // MusicType values.
    84  const (
    85  	QQMusic    = iota // QQ音乐
    86  	CloudMusic        // 网易云音乐
    87  	MiguMusic         // 咪咕音乐
    88  	KugouMusic        // 酷狗音乐
    89  	KuwoMusic         // 酷我音乐
    90  )
    91  
    92  //go:generate stringer -type ElementType -linecomment
    93  const (
    94  	Text     ElementType = iota // 文本
    95  	Image                       // 图片
    96  	Face                        // 表情
    97  	At                          // 艾特
    98  	Reply                       // 回复
    99  	Service                     // 服务
   100  	Forward                     // 转发
   101  	File                        // 文件
   102  	Voice                       // 语音
   103  	Video                       // 视频
   104  	LightApp                    // 轻应用
   105  	RedBag                      // 红包
   106  )
   107  
   108  func (s *Sender) IsAnonymous() bool {
   109  	return s.Uin == 80000000
   110  }
   111  
   112  func NewSendingMessage() *SendingMessage {
   113  	return &SendingMessage{}
   114  }
   115  
   116  func (msg *PrivateMessage) ToString() (res string) {
   117  	for _, elem := range msg.GetElements() {
   118  		switch e := elem.(type) {
   119  		case *TextElement:
   120  			res += e.Content
   121  		case *FaceElement:
   122  			res += "[" + e.Name + "]"
   123  		case *AtElement:
   124  			res += e.Display
   125  		}
   126  	}
   127  	return
   128  }
   129  
   130  func (msg *PrivateMessage) Chat() int64 {
   131  	return msg.Sender.Uin
   132  }
   133  
   134  func (msg *PrivateMessage) GetElements() []IMessageElement {
   135  	return msg.Elements
   136  }
   137  
   138  func (msg *PrivateMessage) Texts() []string {
   139  	return parseTexts(msg.GetElements())
   140  }
   141  
   142  func (msg *GroupMessage) Chat() int64 {
   143  	return msg.GroupCode
   144  }
   145  
   146  func (msg *GroupMessage) GetElements() []IMessageElement {
   147  	return msg.Elements
   148  }
   149  
   150  func (msg *TempMessage) Chat() int64 {
   151  	return msg.GroupCode
   152  }
   153  
   154  func (msg *TempMessage) GetElements() []IMessageElement {
   155  	return msg.Elements
   156  }
   157  
   158  func (msg *TempMessage) Texts() []string {
   159  	return parseTexts(msg.GetElements())
   160  }
   161  
   162  func (msg *TempMessage) ToString() (res string) {
   163  	var strBuilder strings.Builder
   164  	for _, elem := range msg.Elements {
   165  		switch e := elem.(type) {
   166  		case *TextElement:
   167  			strBuilder.WriteString(e.Content)
   168  		case *FaceElement:
   169  			strBuilder.WriteString("[")
   170  			strBuilder.WriteString(e.Name)
   171  			strBuilder.WriteString("]")
   172  		case *AtElement:
   173  			strBuilder.WriteString(e.Display)
   174  		}
   175  	}
   176  	res = strBuilder.String()
   177  	return
   178  }
   179  
   180  func (msg *GroupMessage) ToString() (res string) {
   181  	var strBuilder strings.Builder
   182  	for _, elem := range msg.GetElements() {
   183  		switch e := elem.(type) {
   184  		case *TextElement:
   185  			strBuilder.WriteString(e.Content)
   186  		case *FaceElement:
   187  			strBuilder.WriteString("[")
   188  			strBuilder.WriteString(e.Name)
   189  			strBuilder.WriteString("]")
   190  		case *MarketFaceElement:
   191  			strBuilder.WriteString("[")
   192  			strBuilder.WriteString(e.Name)
   193  			strBuilder.WriteString("]")
   194  		case *GroupImageElement:
   195  			strBuilder.WriteString("Image: ")
   196  			strBuilder.WriteString(e.ImageId)
   197  			strBuilder.WriteString("]")
   198  		case *AtElement:
   199  			strBuilder.WriteString(e.Display)
   200  		case *RedBagElement:
   201  			strBuilder.WriteString("[RedBag: ")
   202  			strBuilder.WriteString(e.Title)
   203  			strBuilder.WriteString("]")
   204  		case *ReplyElement:
   205  			strBuilder.WriteString("[Reply: ")
   206  			strBuilder.WriteString(strconv.FormatInt(int64(e.ReplySeq), 10))
   207  			strBuilder.WriteString("]")
   208  		}
   209  	}
   210  	res = strBuilder.String()
   211  	return
   212  }
   213  
   214  func (msg *GroupMessage) Texts() []string {
   215  	return parseTexts(msg.GetElements())
   216  }
   217  
   218  func parseTexts(elements []IMessageElement) []string {
   219  	texts := make([]string, 0, 4)
   220  	for _, elem := range elements {
   221  		if elem.Type() == Text {
   222  			texts = append(texts, elem.(*TextElement).Content)
   223  		}
   224  	}
   225  	return texts
   226  }
   227  
   228  func (msg *SendingMessage) Append(e IMessageElement) *SendingMessage {
   229  	v := reflect.ValueOf(e)
   230  	if v.Kind() == reflect.Ptr && !v.IsNil() {
   231  		msg.Elements = append(msg.Elements, e)
   232  	}
   233  	return msg
   234  }
   235  
   236  func (msg *SendingMessage) Any(filter func(e IMessageElement) bool) bool {
   237  	for _, e := range msg.Elements {
   238  		if filter(e) {
   239  			return true
   240  		}
   241  	}
   242  	return false
   243  }
   244  
   245  func (msg *SendingMessage) FirstOrNil(filter func(e IMessageElement) bool) IMessageElement {
   246  	for _, e := range msg.Elements {
   247  		if filter(e) {
   248  			return e
   249  		}
   250  	}
   251  	return nil
   252  }
   253  
   254  func (msg *SendingMessage) Count(filter func(e IMessageElement) bool) (c int) {
   255  	for _, e := range msg.Elements {
   256  		if filter(e) {
   257  			c++
   258  		}
   259  	}
   260  	return
   261  }
   262  
   263  func (msg *SendingMessage) ToFragmented() [][]IMessageElement {
   264  	var fragmented [][]IMessageElement
   265  	for _, elem := range msg.Elements {
   266  		switch o := elem.(type) {
   267  		case *TextElement:
   268  			for _, text := range utils.ChunkString(o.Content, 80) {
   269  				fragmented = append(fragmented, []IMessageElement{NewText(text)})
   270  			}
   271  		default:
   272  			fragmented = append(fragmented, []IMessageElement{o})
   273  		}
   274  	}
   275  	return fragmented
   276  }
   277  
   278  // 单条消息发送的大小限制(预估)
   279  const MaxMessageSize = 5000
   280  
   281  func EstimateLength(elems []IMessageElement) int {
   282  	sum := 0
   283  	for _, elem := range elems {
   284  		switch e := elem.(type) {
   285  		case *TextElement:
   286  			sum += len(e.Content)
   287  		case *AtElement:
   288  			sum += len(e.Display)
   289  		case *ReplyElement:
   290  			sum += 444 + EstimateLength(e.Elements)
   291  		case *GroupImageElement, *FriendImageElement:
   292  			sum += 100
   293  		default:
   294  			sum += len(ToReadableString([]IMessageElement{elem}))
   295  		}
   296  	}
   297  	return sum
   298  }
   299  
   300  func (s *Sender) DisplayName() string {
   301  	if s.CardName == "" {
   302  		return s.Nickname
   303  	}
   304  	return s.CardName
   305  }
   306  
   307  func ToProtoElems(elems []IMessageElement, generalFlags bool) (r []*msg.Elem) {
   308  	if len(elems) == 0 {
   309  		return nil
   310  	}
   311  	for _, elem := range elems {
   312  		if reply, ok := elem.(*ReplyElement); ok {
   313  			r = append(r, &msg.Elem{
   314  				SrcMsg: &msg.SourceMsg{
   315  					OrigSeqs:  []int32{reply.ReplySeq},
   316  					SenderUin: proto.Some(reply.Sender),
   317  					Time:      proto.Some(reply.Time),
   318  					Flag:      proto.Int32(1),
   319  					Elems:     ToSrcProtoElems(reply.Elements),
   320  					RichMsg:   []byte{},
   321  					PbReserve: []byte{},
   322  					SrcMsg:    []byte{},
   323  					TroopName: []byte{},
   324  				},
   325  			})
   326  			if len(elems) > 1 {
   327  				if elems[0].Type() == Image || elems[1].Type() == Image {
   328  					r = append(r, &msg.Elem{Text: &msg.Text{Str: proto.String(" ")}})
   329  				}
   330  			}
   331  		}
   332  	}
   333  	for _, elem := range elems {
   334  		if e, ok := elem.(*ShortVideoElement); ok {
   335  			r = e.Pack()
   336  			break
   337  		}
   338  		if e, ok := elem.(IRichMessageElement); ok {
   339  			r = append(r, e.Pack()...)
   340  		}
   341  	}
   342  	if generalFlags {
   343  	L:
   344  		for _, elem := range elems {
   345  			switch e := elem.(type) {
   346  			case *ServiceElement:
   347  				if e.SubType == "Long" {
   348  					r = append(r, &msg.Elem{
   349  						GeneralFlags: &msg.GeneralFlags{
   350  							LongTextFlag:  proto.Int32(1),
   351  							LongTextResid: proto.Some(e.ResId),
   352  							PbReserve:     []byte{0x78, 0x00, 0xF8, 0x01, 0x00, 0xC8, 0x02, 0x00},
   353  						},
   354  					})
   355  					break L
   356  				}
   357  				// d, _ := hex.DecodeString("08097800C80100F00100F80100900200C80200980300A00320B00300C00300D00300E803008A04020803900480808010B80400C00400")
   358  				r = append(r, &msg.Elem{
   359  					GeneralFlags: &msg.GeneralFlags{
   360  						PbReserve: []byte{
   361  							0x08, 0x09, 0x78, 0x00, 0xC8, 0x01, 0x00, 0xF0, 0x01, 0x00, 0xF8, 0x01, 0x00, 0x90, 0x02, 0x00,
   362  							0xC8, 0x02, 0x00, 0x98, 0x03, 0x00, 0xA0, 0x03, 0x20, 0xB0, 0x03, 0x00, 0xC0, 0x03, 0x00, 0xD0,
   363  							0x03, 0x00, 0xE8, 0x03, 0x00, 0x8A, 0x04, 0x02, 0x08, 0x03, 0x90, 0x04, 0x80, 0x80, 0x80, 0x10,
   364  							0xB8, 0x04, 0x00, 0xC0, 0x04, 0x00,
   365  						},
   366  					},
   367  				})
   368  				break L
   369  			}
   370  		}
   371  	}
   372  	return
   373  }
   374  
   375  var photoTextElem IMessageElement = NewText("[图片]")
   376  
   377  func ToSrcProtoElems(elems []IMessageElement) []*msg.Elem {
   378  	elems2 := make([]IMessageElement, len(elems))
   379  	copy(elems2, elems)
   380  	for i, elem := range elems2 {
   381  		if elem.Type() == Image {
   382  			elems2[i] = photoTextElem
   383  		}
   384  	}
   385  	return ToProtoElems(elems2, false)
   386  }
   387  
   388  func ParseMessageElems(elems []*msg.Elem) []IMessageElement {
   389  	var res []IMessageElement
   390  	var newImg = false
   391  	for _, elem := range elems {
   392  		if elem.SrcMsg != nil && len(elem.SrcMsg.OrigSeqs) != 0 {
   393  			r := &ReplyElement{
   394  				ReplySeq: elem.SrcMsg.OrigSeqs[0],
   395  				Time:     elem.SrcMsg.Time.Unwrap(),
   396  				Sender:   elem.SrcMsg.SenderUin.Unwrap(),
   397  				GroupID:  elem.SrcMsg.ToUin.Unwrap(),
   398  				Elements: ParseMessageElems(elem.SrcMsg.Elems),
   399  			}
   400  			res = append(res, r)
   401  		}
   402  		if elem.TransElemInfo != nil {
   403  			if elem.TransElemInfo.ElemType.Unwrap() == 24 { // QFile
   404  				i3 := len(elem.TransElemInfo.ElemValue)
   405  				r := binary.NewReader(elem.TransElemInfo.ElemValue)
   406  				if i3 > 3 {
   407  					if r.ReadByte() == 1 {
   408  						pb := r.ReadBytes(int(r.ReadUInt16()))
   409  						objMsg := msg.ObjMsg{}
   410  						if err := proto.Unmarshal(pb, &objMsg); err == nil && len(objMsg.MsgContentInfo) > 0 {
   411  							info := objMsg.MsgContentInfo[0]
   412  							res = append(res, &GroupFileElement{
   413  								Name:  info.MsgFile.FileName,
   414  								Size:  info.MsgFile.FileSize,
   415  								Path:  string(info.MsgFile.FilePath),
   416  								Busid: info.MsgFile.BusId,
   417  							})
   418  						}
   419  					}
   420  				}
   421  			}
   422  		}
   423  		if elem.LightApp != nil && len(elem.LightApp.Data) > 1 {
   424  			var content []byte
   425  			if elem.LightApp.Data[0] == 0 {
   426  				content = elem.LightApp.Data[1:]
   427  			}
   428  			if elem.LightApp.Data[0] == 1 {
   429  				content = binary.ZlibUncompress(elem.LightApp.Data[1:])
   430  			}
   431  			if len(content) > 0 && len(content) < 1024*1024*1024 { // 解析出错 or 非法内容
   432  				// TODO: 解析具体的APP
   433  				return append(res, &LightAppElement{Content: string(content)})
   434  			}
   435  		}
   436  		if elem.VideoFile != nil {
   437  			return []IMessageElement{
   438  				&ShortVideoElement{
   439  					Name:      string(elem.VideoFile.FileName),
   440  					Uuid:      elem.VideoFile.FileUuid,
   441  					Size:      elem.VideoFile.FileSize.Unwrap(),
   442  					ThumbSize: elem.VideoFile.ThumbFileSize.Unwrap(),
   443  					Md5:       elem.VideoFile.FileMd5,
   444  					ThumbMd5:  elem.VideoFile.ThumbFileMd5,
   445  				},
   446  			}
   447  		}
   448  		if elem.Text != nil {
   449  			switch {
   450  			case len(elem.Text.Attr6Buf) > 0:
   451  				att6 := binary.NewReader(elem.Text.Attr6Buf)
   452  				att6.ReadBytes(7)
   453  				target := int64(uint32(att6.ReadInt32()))
   454  				at := NewAt(target, elem.Text.Str.Unwrap())
   455  				at.SubType = AtTypeGroupMember
   456  				res = append(res, at)
   457  			case len(elem.Text.PbReserve) > 0:
   458  				resv := new(msg.TextResvAttr)
   459  				_ = proto.Unmarshal(elem.Text.PbReserve, resv)
   460  				if resv.AtType.Unwrap() == 2 {
   461  					at := NewAt(int64(resv.AtMemberTinyid.Unwrap()), elem.Text.Str.Unwrap())
   462  					at.SubType = AtTypeGuildMember
   463  					res = append(res, at)
   464  					break
   465  				}
   466  				if resv.AtType.Unwrap() == 4 {
   467  					at := NewAt(int64(resv.AtChannelInfo.ChannelId.Unwrap()), elem.Text.Str.Unwrap())
   468  					at.SubType = AtTypeGuildChannel
   469  					res = append(res, at)
   470  					break
   471  				}
   472  				fallthrough
   473  			default:
   474  				res = append(res, NewText(func() string {
   475  					// 这么处理应该没问题
   476  					if strings.Contains(elem.Text.Str.Unwrap(), "\r") && !strings.Contains(elem.Text.Str.Unwrap(), "\r\n") {
   477  						return strings.ReplaceAll(elem.Text.Str.Unwrap(), "\r", "\r\n")
   478  					}
   479  					return elem.Text.Str.Unwrap()
   480  				}()))
   481  			}
   482  		}
   483  		if elem.RichMsg != nil {
   484  			var content string
   485  			if elem.RichMsg.Template1[0] == 0 {
   486  				content = string(elem.RichMsg.Template1[1:])
   487  			}
   488  			if elem.RichMsg.Template1[0] == 1 {
   489  				content = string(binary.ZlibUncompress(elem.RichMsg.Template1[1:]))
   490  			}
   491  			if content != "" {
   492  				if elem.RichMsg.ServiceId.Unwrap() == 35 {
   493  					elem := forwardMsgFromXML(content)
   494  					if elem != nil {
   495  						res = append(res, elem)
   496  						continue
   497  					}
   498  				}
   499  				if elem.RichMsg.ServiceId.Unwrap() == 33 {
   500  					continue // 前面一个 elem 已经解析到链接
   501  				}
   502  				if isOk := strings.Contains(content, "<?xml"); isOk {
   503  					res = append(res, NewRichXml(content, int64(elem.RichMsg.ServiceId.Unwrap())))
   504  					continue
   505  				} else if json.Valid(utils.S2B(content)) {
   506  					res = append(res, NewRichJson(content))
   507  					continue
   508  				}
   509  				res = append(res, NewText(content))
   510  			}
   511  		}
   512  		if elem.CustomFace != nil {
   513  			if len(elem.CustomFace.Md5) == 0 {
   514  				continue
   515  			}
   516  			var url string
   517  			if elem.CustomFace.OrigUrl.Unwrap() == "" {
   518  				url = fmt.Sprintf("https://gchat.qpic.cn/gchatpic_new/0/0-0-%X/0?term=2", elem.CustomFace.Md5)
   519  			} else {
   520  				url = "https://gchat.qpic.cn" + elem.CustomFace.OrigUrl.Unwrap()
   521  			}
   522  			if strings.Contains(elem.CustomFace.OrigUrl.Unwrap(), "qmeet") {
   523  				res = append(res, &GuildImageElement{
   524  					FileId:   int64(elem.CustomFace.FileId.Unwrap()),
   525  					FilePath: elem.CustomFace.FilePath.Unwrap(),
   526  					Size:     elem.CustomFace.Size.Unwrap(),
   527  					Width:    elem.CustomFace.Width.Unwrap(),
   528  					Height:   elem.CustomFace.Height.Unwrap(),
   529  					Url:      url,
   530  					Md5:      elem.CustomFace.Md5,
   531  				})
   532  				continue
   533  			}
   534  			bizType := UnknownBizType
   535  			if len(elem.CustomFace.PbReserve) != 0 {
   536  				attr := new(msg.ResvAttr)
   537  				if proto.Unmarshal(elem.CustomFace.PbReserve, attr) == nil {
   538  					bizType = ImageBizType(attr.ImageBizType.Unwrap())
   539  				}
   540  			}
   541  			if !newImg {
   542  				res = append(res, &GroupImageElement{
   543  					FileId:       int64(elem.CustomFace.FileId.Unwrap()),
   544  					ImageId:      elem.CustomFace.FilePath.Unwrap(),
   545  					Size:         elem.CustomFace.Size.Unwrap(),
   546  					Width:        elem.CustomFace.Width.Unwrap(),
   547  					Height:       elem.CustomFace.Height.Unwrap(),
   548  					Url:          url,
   549  					ImageBizType: bizType,
   550  					Md5:          elem.CustomFace.Md5,
   551  				})
   552  			}
   553  		}
   554  		if elem.MarketFace != nil {
   555  			face := &MarketFaceElement{
   556  				Name:       utils.B2S(elem.MarketFace.FaceName),
   557  				FaceId:     elem.MarketFace.FaceId,
   558  				TabId:      int32(elem.MarketFace.TabId.Unwrap()),
   559  				ItemType:   int32(elem.MarketFace.ItemType.Unwrap()),
   560  				SubType:    int32(elem.MarketFace.SubType.Unwrap()),
   561  				MediaType:  int32(elem.MarketFace.MediaType.Unwrap()),
   562  				EncryptKey: elem.MarketFace.Key,
   563  				MagicValue: utils.B2S(elem.MarketFace.Mobileparam),
   564  			}
   565  			if face.Name == "[骰子]" || face.Name == "[随机骰子]" {
   566  				_, v, _ := strings.Cut(face.MagicValue, "=")
   567  				t, _ := strconv.ParseInt(v, 10, 32)
   568  				return []IMessageElement{
   569  					&DiceElement{
   570  						MarketFaceElement: face,
   571  						Value:             int32(t) + 1,
   572  					},
   573  				}
   574  			}
   575  			if face.Name == "[猜拳]" {
   576  				_, v, _ := strings.Cut(face.MagicValue, "=")
   577  				t, _ := strconv.ParseInt(v, 10, 32)
   578  				return []IMessageElement{
   579  					&FingerGuessingElement{
   580  						MarketFaceElement: face,
   581  						Value:             int32(t),
   582  						Name:              fingerGuessingName[int32(t)],
   583  					},
   584  				}
   585  			}
   586  			return []IMessageElement{face}
   587  		}
   588  
   589  		if elem.NotOnlineImage != nil {
   590  			img := elem.NotOnlineImage
   591  
   592  			var url string
   593  			switch {
   594  			case img.PbReserve != nil && img.PbReserve.Url.Unwrap() != "":
   595  				url = fmt.Sprintf("https://c2cpicdw.qpic.cn%s&spec=0&rf=naio", img.PbReserve.Url.Unwrap())
   596  			case img.OrigUrl.Unwrap() != "":
   597  				url = "https://c2cpicdw.qpic.cn" + img.OrigUrl.Unwrap()
   598  			default:
   599  				url = "https://c2cpicdw.qpic.cn/offpic_new/0"
   600  				downloadPath := img.ResId.Unwrap()
   601  				if img.DownloadPath.Unwrap() != "" {
   602  					downloadPath = img.DownloadPath.Unwrap()
   603  				}
   604  				if !strings.HasPrefix(downloadPath, "/") {
   605  					url += "/"
   606  				}
   607  				url += downloadPath + "/0?term=3"
   608  			}
   609  
   610  			if !newImg {
   611  				res = append(res, &FriendImageElement{
   612  					ImageId: img.FilePath.Unwrap(),
   613  					Size:    img.FileLen.Unwrap(),
   614  					Url:     url,
   615  					Md5:     img.PicMd5,
   616  				})
   617  			}
   618  
   619  		}
   620  
   621  		if elem.QQWalletMsg != nil && elem.QQWalletMsg.AioBody != nil {
   622  			// /com/tencent/mobileqq/data/MessageForQQWalletMsg.java#L366
   623  			msgType := elem.QQWalletMsg.AioBody.MsgType.Unwrap()
   624  			if msgType <= 1000 && elem.QQWalletMsg.AioBody.RedType.IsSome() {
   625  				return []IMessageElement{
   626  					&RedBagElement{
   627  						MsgType: RedBagMessageType(msgType),
   628  						Title:   elem.QQWalletMsg.AioBody.Receiver.Title.Unwrap(),
   629  					},
   630  				}
   631  			}
   632  		}
   633  		if elem.Face != nil {
   634  			res = append(res, NewFace(elem.Face.Index.Unwrap()))
   635  		}
   636  		if elem.CommonElem != nil {
   637  			switch elem.CommonElem.ServiceType.Unwrap() {
   638  			case 3:
   639  				flash := &msg.MsgElemInfoServtype3{}
   640  				_ = proto.Unmarshal(elem.CommonElem.PbElem, flash)
   641  				if flash.FlashTroopPic != nil {
   642  					res = append(res, &GroupImageElement{
   643  						FileId:  int64(flash.FlashTroopPic.FileId.Unwrap()),
   644  						ImageId: flash.FlashTroopPic.FilePath.Unwrap(),
   645  						Size:    flash.FlashTroopPic.Size.Unwrap(),
   646  						Width:   flash.FlashTroopPic.Width.Unwrap(),
   647  						Height:  flash.FlashTroopPic.Height.Unwrap(),
   648  						Md5:     flash.FlashTroopPic.Md5,
   649  						Flash:   true,
   650  					})
   651  					return res
   652  				}
   653  				if flash.FlashC2CPic != nil {
   654  					res = append(res, &FriendImageElement{
   655  						ImageId: flash.FlashC2CPic.FilePath.Unwrap(),
   656  						Size:    flash.FlashC2CPic.FileLen.Unwrap(),
   657  						Md5:     flash.FlashC2CPic.PicMd5,
   658  						Flash:   true,
   659  					})
   660  					return res
   661  				}
   662  			case 33:
   663  				newSysFaceMsg := &msg.MsgElemInfoServtype33{}
   664  				_ = proto.Unmarshal(elem.CommonElem.PbElem, newSysFaceMsg)
   665  				res = append(res, NewFace(int32(newSysFaceMsg.Index.Unwrap())))
   666  			case 37:
   667  				animatedStickerMsg := &msg.MsgElemInfoServtype37{}
   668  				_ = proto.Unmarshal(elem.CommonElem.PbElem, animatedStickerMsg)
   669  				sticker := &AnimatedSticker{
   670  					ID:   int32(animatedStickerMsg.Qsid.Unwrap()),
   671  					Name: strings.TrimPrefix(string(animatedStickerMsg.Text), "/"),
   672  				}
   673  				return []IMessageElement{sticker} // sticker 永远为单独消息
   674  			case 48:
   675  				img := &msg.PbMultiMediaElement{}
   676  				_ = proto.Unmarshal(elem.CommonElem.PbElem, img)
   677  				domain := img.Elem1.Data.Domain.Unwrap()
   678  				imgURL := img.Elem1.Data.ImgURL.Unwrap()
   679  
   680  				if img.Elem2.Data.Friend != nil {
   681  					rKey := img.Elem2.Data.Friend.RKey.Unwrap()
   682  					url := fmt.Sprintf("https://%s%s%s&spec=0&rf=naio", domain, imgURL, rKey)
   683  					res = append(res, &FriendImageElement{
   684  						ImageId: img.Elem1.Meta.FilePath.Unwrap(),
   685  						Size:    img.Elem1.Meta.Data.FileLen.Unwrap(),
   686  						Url:     url,
   687  						Md5:     img.Elem1.Meta.Data.PicMd5,
   688  					})
   689  					newImg = true
   690  				}
   691  				if img.Elem2.Data.Group != nil {
   692  					rKey := img.Elem2.Data.Group.RKey.Unwrap()
   693  					url := fmt.Sprintf("https://%s%s%s&spec=0&rf=naio", domain, imgURL, rKey)
   694  					res = append(res, &GroupImageElement{
   695  						ImageId: img.Elem1.Meta.FilePath.Unwrap(),
   696  						Size:    img.Elem1.Meta.Data.FileLen.Unwrap(),
   697  						Url:     url,
   698  						Md5:     img.Elem1.Meta.Data.PicMd5,
   699  					})
   700  					newImg = true
   701  				}
   702  			}
   703  
   704  		}
   705  	}
   706  	return res
   707  }
   708  
   709  func ToReadableString(m []IMessageElement) string {
   710  	sb := new(strings.Builder)
   711  	for _, elem := range m {
   712  		switch e := elem.(type) {
   713  		case *TextElement:
   714  			sb.WriteString(e.Content)
   715  		case *GroupImageElement, *FriendImageElement:
   716  			sb.WriteString("[图片]")
   717  		case *FaceElement:
   718  			sb.WriteByte('/')
   719  			sb.WriteString(e.Name)
   720  		case *ForwardElement:
   721  			sb.WriteString("[聊天记录]")
   722  		// NOTE: flash pic is singular
   723  		// To be clarified
   724  		// case *GroupFlashImgElement:
   725  		// 	return "[闪照]"
   726  		// case *FriendFlashImgElement:
   727  		// 	return "[闪照]"
   728  		case *AtElement:
   729  			sb.WriteString(e.Display)
   730  		}
   731  	}
   732  	return sb.String()
   733  }
   734  
   735  func FaceNameById(id int) string {
   736  	if name, ok := faceMap[id]; ok {
   737  		return name
   738  	}
   739  	return "未知表情"
   740  }
   741  
   742  // SplitLongMessage 将过长的消息分割为若干个适合发送的消息
   743  func SplitLongMessage(sendingMessage *SendingMessage) []*SendingMessage {
   744  	// 合并连续文本消息
   745  	sendingMessage = mergeContinuousTextMessages(sendingMessage)
   746  
   747  	// 分割过长元素
   748  	sendingMessage = splitElements(sendingMessage)
   749  
   750  	// 将元素分为多组,确保各组不超过单条消息的上限
   751  	splitMessages := splitMessages(sendingMessage)
   752  
   753  	return splitMessages
   754  }
   755  
   756  // mergeContinuousTextMessages 预先将所有连续的文本消息合并为到一起,方便后续统一切割
   757  func mergeContinuousTextMessages(sendingMessage *SendingMessage) *SendingMessage {
   758  	// 检查下是否有连续的文本消息,若没有,则可以直接返回
   759  	lastIsText := false
   760  	hasContinuousText := false
   761  	for _, message := range sendingMessage.Elements {
   762  		if message.Type() == Text {
   763  			if lastIsText {
   764  				// 有连续的文本消息,需要进行处理
   765  				hasContinuousText = true
   766  				break
   767  			}
   768  
   769  			// 遇到文本元素先存放起来,方便将连续的文本元素合并
   770  			lastIsText = true
   771  			continue
   772  		} else {
   773  			lastIsText = false
   774  		}
   775  	}
   776  	if !hasContinuousText {
   777  		return sendingMessage
   778  	}
   779  
   780  	// 存在连续的文本消息,需要进行合并处理
   781  	textBuffer := strings.Builder{}
   782  	lastIsText = false
   783  	totalMessageCount := 0
   784  	for _, message := range sendingMessage.Elements {
   785  		if msgVal, ok := message.(*TextElement); ok {
   786  			// 遇到文本元素先存放起来,方便将连续的文本元素合并
   787  			textBuffer.WriteString(msgVal.Content)
   788  			lastIsText = true
   789  			continue
   790  		}
   791  
   792  		// 如果之前的是文本元素(可能是多个合并起来的),则在这里将其实际放入消息中
   793  		if lastIsText {
   794  			sendingMessage.Elements[totalMessageCount] = NewText(textBuffer.String())
   795  			totalMessageCount += 1
   796  			textBuffer.Reset()
   797  		}
   798  		lastIsText = false
   799  
   800  		// 非文本元素则直接处理
   801  		sendingMessage.Elements[totalMessageCount] = message
   802  		totalMessageCount += 1
   803  	}
   804  	// 处理最后几个元素是文本的情况
   805  	if textBuffer.Len() != 0 {
   806  		sendingMessage.Elements[totalMessageCount] = NewText(textBuffer.String())
   807  		totalMessageCount += 1
   808  		textBuffer.Reset()
   809  	}
   810  	sendingMessage.Elements = sendingMessage.Elements[:totalMessageCount]
   811  
   812  	return sendingMessage
   813  }
   814  
   815  // splitElements 将原有消息的各个元素先尝试处理,如过长的文本消息按需分割为多个元素
   816  func splitElements(sendingMessage *SendingMessage) *SendingMessage {
   817  	// 检查下是否存在需要文本消息,若不存在,则直接返回
   818  	needSplit := false
   819  	for _, message := range sendingMessage.Elements {
   820  		if msgVal, ok := message.(*TextElement); ok {
   821  			if textNeedSplit(msgVal.Content) {
   822  				needSplit = true
   823  				break
   824  			}
   825  		}
   826  	}
   827  	if !needSplit {
   828  		return sendingMessage
   829  	}
   830  
   831  	// 开始尝试切割
   832  	messageParts := NewSendingMessage()
   833  
   834  	for _, message := range sendingMessage.Elements {
   835  		switch msgVal := message.(type) {
   836  		case *TextElement:
   837  			messageParts.Elements = append(messageParts.Elements, splitPlainMessage(msgVal.Content)...)
   838  		default:
   839  			messageParts.Append(message)
   840  		}
   841  	}
   842  
   843  	return messageParts
   844  }
   845  
   846  // splitMessages 根据大小分为多个消息进行发送
   847  func splitMessages(sendingMessage *SendingMessage) []*SendingMessage {
   848  	var splitMessages []*SendingMessage
   849  
   850  	messagePart := NewSendingMessage()
   851  	msgSize := 0
   852  	for _, part := range sendingMessage.Elements {
   853  		estimateSize := EstimateLength([]IMessageElement{part})
   854  		// 若当前分消息加上新的元素后大小会超限,且已经有元素(确保不会无限循环),则开始切分为新的一个元素
   855  		if msgSize+estimateSize > MaxMessageSize && len(messagePart.Elements) > 0 {
   856  			splitMessages = append(splitMessages, messagePart)
   857  
   858  			messagePart = NewSendingMessage()
   859  			msgSize = 0
   860  		}
   861  
   862  		// 加上新的元素
   863  		messagePart.Append(part)
   864  		msgSize += estimateSize
   865  	}
   866  	// 将最后一个分片加上
   867  	if len(messagePart.Elements) != 0 {
   868  		splitMessages = append(splitMessages, messagePart)
   869  	}
   870  
   871  	return splitMessages
   872  }
   873  
   874  func splitPlainMessage(content string) []IMessageElement {
   875  	if !textNeedSplit(content) {
   876  		return []IMessageElement{NewText(content)}
   877  	}
   878  
   879  	splittedMessage := make([]IMessageElement, 0, (len(content)+MaxMessageSize-1)/MaxMessageSize)
   880  
   881  	last := 0
   882  	for runeIndex, runeValue := range content {
   883  		// 如果加上新的这个字符后,会超出大小,则从这个字符前分一次片
   884  		if runeIndex+len(string(runeValue))-last > MaxMessageSize {
   885  			splittedMessage = append(splittedMessage, NewText(content[last:runeIndex]))
   886  			last = runeIndex
   887  		}
   888  	}
   889  	if last != len(content) {
   890  		splittedMessage = append(splittedMessage, NewText(content[last:]))
   891  	}
   892  
   893  	return splittedMessage
   894  }
   895  
   896  func textNeedSplit(content string) bool {
   897  	return len(content) > MaxMessageSize
   898  }