github.com/LagrangeDev/LagrangeGo@v0.0.0-20240512064304-ad4a85e10cb4/message/message.go (about)

     1  package message
     2  
     3  // 部分借鉴 https://github.com/Mrs4s/MiraiGo/blob/master/message/message.go
     4  
     5  import (
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/LagrangeDev/LagrangeGo/client/packets/pb/message"
    11  	"github.com/LagrangeDev/LagrangeGo/internal/proto"
    12  	"github.com/LagrangeDev/LagrangeGo/utils"
    13  	"github.com/LagrangeDev/LagrangeGo/utils/binary"
    14  )
    15  
    16  type IMessage interface {
    17  	GetElements() []IMessageElement
    18  	Chat() int64
    19  	ToString() string
    20  	Texts() []string
    21  }
    22  
    23  const (
    24  	Text     ElementType = iota // 文本
    25  	Image                       // 图片
    26  	Face                        // 表情
    27  	At                          // 艾特
    28  	Reply                       // 回复
    29  	Service                     // 服务
    30  	Forward                     // 转发
    31  	File                        // 文件
    32  	Voice                       // 语音
    33  	Video                       // 视频
    34  	LightApp                    // 轻应用
    35  	RedBag                      // 红包
    36  )
    37  
    38  type (
    39  	PrivateMessage struct {
    40  		Id         int32
    41  		InternalId int32
    42  		Self       int64
    43  		Target     int64
    44  		Time       int32
    45  		Sender     *Sender
    46  		Elements   []IMessageElement
    47  	}
    48  
    49  	TempMessage struct {
    50  		Id        int32
    51  		GroupCode uint32
    52  		GroupName string
    53  		Self      uint32
    54  		Sender    *Sender
    55  		Elements  []IMessageElement
    56  	}
    57  
    58  	GroupMessage struct {
    59  		Id             int32
    60  		InternalId     int32
    61  		GroupCode      uint32
    62  		GroupName      string
    63  		Sender         *Sender
    64  		Time           uint64
    65  		Elements       []IMessageElement
    66  		OriginalObject *message.PushMsgBody
    67  	}
    68  
    69  	SendingMessage struct {
    70  		Elements []IMessageElement
    71  	}
    72  
    73  	Sender struct {
    74  		Uin           uint32
    75  		Uid           string
    76  		Nickname      string
    77  		CardName      string
    78  		AnonymousInfo *AnonymousInfo
    79  		IsFriend      bool
    80  	}
    81  
    82  	AnonymousInfo struct {
    83  		AnonymousId   string
    84  		AnonymousNick string
    85  	}
    86  
    87  	IMessageElement interface {
    88  		Type() ElementType
    89  	}
    90  
    91  	ElementType int
    92  )
    93  
    94  func (s *Sender) IsAnonymous() bool {
    95  	return s.Uin == 80000000
    96  }
    97  
    98  func ParsePrivateMessage(msg *message.PushMsg) *PrivateMessage {
    99  	return &PrivateMessage{
   100  		Id:     int32(msg.Message.ContentHead.Sequence.Unwrap()),
   101  		Self:   int64(msg.Message.ResponseHead.ToUin),
   102  		Target: int64(msg.Message.ResponseHead.FromUin),
   103  		Sender: &Sender{
   104  			Uin:      msg.Message.ResponseHead.FromUin,
   105  			Uid:      msg.Message.ResponseHead.FromUid.Unwrap(),
   106  			IsFriend: true,
   107  		},
   108  		Time:     int32(msg.Message.ContentHead.TimeStamp.Unwrap()),
   109  		Elements: parseMessageElements(msg.Message.Body.RichText.Elems),
   110  	}
   111  }
   112  
   113  func ParseGroupMessage(msg *message.PushMsg) *GroupMessage {
   114  	return &GroupMessage{
   115  		Id:        int32(msg.Message.ContentHead.Sequence.Unwrap()),
   116  		GroupCode: msg.Message.ResponseHead.Grp.GroupUin,
   117  		GroupName: msg.Message.ResponseHead.Grp.GroupName,
   118  		Sender: &Sender{
   119  			Uin:      msg.Message.ResponseHead.FromUin,
   120  			Uid:      msg.Message.ResponseHead.FromUid.Unwrap(),
   121  			Nickname: msg.Message.ResponseHead.Grp.MemberName,
   122  			CardName: msg.Message.ResponseHead.Grp.MemberName,
   123  			IsFriend: false,
   124  		},
   125  		Time:           msg.Message.ContentHead.TimeStamp.Unwrap(),
   126  		Elements:       parseMessageElements(msg.Message.Body.RichText.Elems),
   127  		OriginalObject: msg.Message,
   128  	}
   129  }
   130  
   131  func ParseTempMessage(msg *message.PushMsg) *TempMessage {
   132  	return &TempMessage{
   133  		Elements: parseMessageElements(msg.Message.Body.RichText.Elems),
   134  	}
   135  }
   136  
   137  func parseMessageElements(msg []*message.Elem) []IMessageElement {
   138  	var res []IMessageElement
   139  
   140  	for _, elem := range msg {
   141  		if elem.SrcMsg != nil && len(elem.SrcMsg.OrigSeqs) != 0 {
   142  			r := &ReplyElement{
   143  				ReplySeq: int32(elem.SrcMsg.OrigSeqs[0]),
   144  				Time:     elem.SrcMsg.Time.Unwrap(),
   145  				Sender:   elem.SrcMsg.SenderUin,
   146  				GroupID:  elem.SrcMsg.ToUin.Unwrap(),
   147  				Elements: parseMessageElements(elem.SrcMsg.Elems),
   148  			}
   149  			res = append(res, r)
   150  		}
   151  
   152  		if elem.Text != nil {
   153  			switch {
   154  			case len(elem.Text.Attr6Buf) > 0:
   155  				att6 := binary.NewReader(elem.Text.Attr6Buf)
   156  				att6.SkipBytes(7)
   157  				target := att6.ReadU32()
   158  				at := NewAt(target, elem.Text.Str.Unwrap())
   159  				at.SubType = AtTypeGroupMember
   160  				res = append(res, at)
   161  			default:
   162  				res = append(res, NewText(func() string {
   163  					if strings.Contains(elem.Text.Str.Unwrap(), "\r") && !strings.Contains(elem.Text.Str.Unwrap(), "\r\n") {
   164  						return strings.ReplaceAll(elem.Text.Str.Unwrap(), "\r", "\r\n")
   165  					}
   166  					return elem.Text.Str.Unwrap()
   167  				}()))
   168  			}
   169  		}
   170  
   171  		if elem.Face != nil {
   172  			if len(elem.Face.Old) > 0 {
   173  				faceId := elem.Face.Index
   174  				if faceId.IsSome() {
   175  					res = append(res, &FaceElement{FaceID: uint16(faceId.Unwrap())})
   176  				}
   177  			} else if elem.CommonElem.ServiceType == 37 && elem.CommonElem.PbElem != nil {
   178  				qFace := message.QFaceExtra{}
   179  				err := proto.Unmarshal(elem.CommonElem.PbElem, &qFace)
   180  				if err == nil {
   181  					faceId := qFace.FaceId
   182  					if faceId.IsSome() {
   183  						res = append(res, &FaceElement{FaceID: uint16(faceId.Unwrap()), isLargeFace: true})
   184  					}
   185  				}
   186  			} else if elem.CommonElem.ServiceType == 33 && elem.CommonElem.PbElem != nil {
   187  				qFace := message.QSmallFaceExtra{}
   188  				err := proto.Unmarshal(elem.CommonElem.PbElem, &qFace)
   189  				if err == nil {
   190  					res = append(res, &FaceElement{FaceID: uint16(qFace.FaceId), isLargeFace: false})
   191  				}
   192  			}
   193  		}
   194  
   195  		if elem.VideoFile != nil {
   196  			return []IMessageElement{
   197  				&ShortVideoElement{
   198  					Name:      elem.VideoFile.FileName,
   199  					Uuid:      utils.S2B(elem.VideoFile.FileUuid),
   200  					Size:      elem.VideoFile.FileSize,
   201  					ThumbSize: elem.VideoFile.ThumbFileSize,
   202  					Md5:       elem.VideoFile.FileMd5,
   203  					ThumbMd5:  elem.VideoFile.ThumbFileMd5,
   204  				},
   205  			}
   206  		}
   207  
   208  		if elem.CustomFace != nil {
   209  			if len(elem.CustomFace.Md5) == 0 {
   210  				continue
   211  			}
   212  
   213  			var url string
   214  			if strings.Contains(elem.CustomFace.OrigUrl, "rkey") {
   215  				url = "https://multimedia.nt.qq.com.cn" + elem.CustomFace.OrigUrl
   216  			} else {
   217  				url = "http://gchat.qpic.cn" + elem.CustomFace.OrigUrl
   218  			}
   219  
   220  			res = append(res, &GroupImageElement{
   221  				FileId:  int64(elem.CustomFace.FileId),
   222  				ImageId: elem.CustomFace.FilePath,
   223  				Size:    elem.CustomFace.Size,
   224  				Width:   elem.CustomFace.Width,
   225  				Height:  elem.CustomFace.Height,
   226  				Url:     url,
   227  				Md5:     elem.CustomFace.Md5,
   228  			})
   229  		}
   230  
   231  		if elem.NotOnlineImage != nil {
   232  			if len(elem.NotOnlineImage.PicMd5) == 0 {
   233  				continue
   234  			}
   235  
   236  			var url string
   237  			if strings.Contains(elem.NotOnlineImage.OrigUrl, "rkey") {
   238  				url = "https://multimedia.nt.qq.com.cn" + elem.NotOnlineImage.OrigUrl
   239  			} else {
   240  				url = "http://gchat.qpic.cn" + elem.NotOnlineImage.OrigUrl
   241  			}
   242  
   243  			res = append(res, &FriendImageElement{
   244  				ImageId: elem.NotOnlineImage.FilePath,
   245  				Size:    elem.NotOnlineImage.FileLen,
   246  				Url:     url,
   247  				Md5:     elem.NotOnlineImage.PicMd5,
   248  			})
   249  		}
   250  	}
   251  
   252  	return res
   253  }
   254  
   255  func (msg *GroupMessage) ToString() (res string) {
   256  	var strBuilder strings.Builder
   257  	for _, elem := range msg.Elements {
   258  		switch e := elem.(type) {
   259  		case *TextElement:
   260  			strBuilder.WriteString(e.Content)
   261  		case *GroupImageElement:
   262  			strBuilder.WriteString("[Image: ")
   263  			strBuilder.WriteString(e.ImageId)
   264  			strBuilder.WriteString("]")
   265  		case *AtElement:
   266  			strBuilder.WriteString(e.Display)
   267  		case *ReplyElement:
   268  			strBuilder.WriteString("[Reply: ")
   269  			strBuilder.WriteString(strconv.FormatInt(int64(e.ReplySeq), 10))
   270  			strBuilder.WriteString("]")
   271  		case *FaceElement:
   272  			strBuilder.WriteString("[Face: ")
   273  			strBuilder.WriteString(strconv.FormatInt(int64(e.FaceID), 10))
   274  			if e.isLargeFace {
   275  				strBuilder.WriteString(", isLargeFace: true]")
   276  			}
   277  			strBuilder.WriteString("]")
   278  		}
   279  	}
   280  	res = strBuilder.String()
   281  	return
   282  }
   283  
   284  func ToReadableString(m []IMessageElement) string {
   285  	sb := new(strings.Builder)
   286  	for _, elem := range m {
   287  		switch e := elem.(type) {
   288  		case *TextElement:
   289  			sb.WriteString(e.Content)
   290  		case *GroupImageElement, *FriendImageElement:
   291  			sb.WriteString("[图片]")
   292  		case *AtElement:
   293  			sb.WriteString(e.Display)
   294  		case *ReplyElement:
   295  			sb.WriteString("[回复]")
   296  		case *FaceElement:
   297  			sb.WriteString("[表情:")
   298  			sb.WriteString(strconv.FormatInt(int64(e.FaceID), 10))
   299  			sb.WriteString("]")
   300  		}
   301  	}
   302  	return sb.String()
   303  }
   304  
   305  func NewSendingMessage() *SendingMessage {
   306  	return &SendingMessage{}
   307  }
   308  
   309  func (msg *SendingMessage) GetElems() []IMessageElement {
   310  	return msg.Elements
   311  }
   312  
   313  // Append 要传入msg的引用
   314  func (msg *SendingMessage) Append(e IMessageElement) *SendingMessage {
   315  	v := reflect.ValueOf(e)
   316  	if v.Kind() == reflect.Ptr && !v.IsNil() {
   317  		msg.Elements = append(msg.Elements, e)
   318  	}
   319  	return msg
   320  }
   321  
   322  func (msg *SendingMessage) FirstOrNil(f func(element IMessageElement) bool) IMessageElement {
   323  	for _, elem := range msg.Elements {
   324  		if f(elem) {
   325  			return elem
   326  		}
   327  	}
   328  	return nil
   329  }
   330  
   331  func PackElementsToBody(msgElems []IMessageElement) (msgBody *message.MessageBody) {
   332  	if len(msgElems) == 0 {
   333  		return
   334  	}
   335  	elems := make([]*message.Elem, 0, len(msgElems))
   336  	for _, elem := range msgElems {
   337  		bd, ok := elem.(ElementBuilder)
   338  		if !ok {
   339  			continue
   340  		}
   341  		elems = append(elems, bd.BuildElement()...)
   342  	}
   343  	msgBody = &message.MessageBody{
   344  		RichText: &message.RichText{Elems: elems},
   345  	}
   346  	return
   347  }