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

     1  package message
     2  
     3  import (
     4  	"crypto/md5"
     5  	"regexp"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/Mrs4s/MiraiGo/binary"
    10  	"github.com/Mrs4s/MiraiGo/client/pb/msg"
    11  	"github.com/Mrs4s/MiraiGo/internal/proto"
    12  	"github.com/Mrs4s/MiraiGo/utils"
    13  )
    14  
    15  // *----- Definitions -----* //
    16  
    17  // ForwardMessage 合并转发消息
    18  type ForwardMessage struct {
    19  	Nodes []*ForwardNode
    20  }
    21  
    22  type ForwardNode struct { // todo 加一个group_id
    23  	GroupId    int64
    24  	SenderId   int64
    25  	SenderName string
    26  	Time       int32
    27  	Message    []IMessageElement
    28  }
    29  
    30  type ForwardElement struct {
    31  	FileName string
    32  	Content  string
    33  	ResId    string
    34  	Items    []*msg.PbMultiMsgItem
    35  }
    36  
    37  // *----- Implementations -----* //
    38  
    39  func (f *ForwardMessage) Type() ElementType {
    40  	return Forward
    41  }
    42  
    43  // Type impl IMessageElement
    44  func (e *ForwardElement) Type() ElementType {
    45  	return Forward
    46  }
    47  
    48  func (e *ForwardElement) Pack() []*msg.Elem {
    49  	rich := &msg.Elem{
    50  		RichMsg: &msg.RichMsg{
    51  			Template1: append([]byte{1}, binary.ZlibCompress(utils.S2B(e.Content))...),
    52  			ServiceId: proto.Int32(35),
    53  			MsgResId:  []byte{},
    54  		},
    55  	}
    56  	txt := &msg.Elem{
    57  		Text: &msg.Text{
    58  			Str: proto.String("你的QQ暂不支持查看[转发多条消息],请期待后续版本。"),
    59  		},
    60  	}
    61  	return []*msg.Elem{rich, txt}
    62  }
    63  
    64  func NewForwardMessage() *ForwardMessage {
    65  	return &ForwardMessage{}
    66  }
    67  
    68  // AddNode adds a node to the forward message. return for method chaining.
    69  func (f *ForwardMessage) AddNode(node *ForwardNode) *ForwardMessage {
    70  	f.Nodes = append(f.Nodes, node)
    71  	for _, item := range node.Message {
    72  		if item.Type() != Forward { // quick path
    73  			continue
    74  		}
    75  	}
    76  	return f
    77  }
    78  
    79  // Length return the length of Nodes.
    80  func (f *ForwardMessage) Length() int { return len(f.Nodes) }
    81  
    82  func (f *ForwardMessage) Brief() string {
    83  	var brief strings.Builder
    84  	for _, n := range f.Nodes {
    85  		brief.WriteString(ToReadableString(n.Message))
    86  		if brief.Len() >= 27 {
    87  			break
    88  		}
    89  	}
    90  	return brief.String()
    91  }
    92  
    93  func (f *ForwardMessage) Preview() string {
    94  	var pv strings.Builder
    95  	for i, node := range f.Nodes {
    96  		if i >= 4 {
    97  			break
    98  		}
    99  		pv.WriteString(`<title size="26" color="#777777">`)
   100  		pv.WriteString(utils.XmlEscape(node.SenderName))
   101  		pv.WriteString(": ")
   102  		pv.WriteString(utils.XmlEscape(ToReadableString(node.Message)))
   103  		pv.WriteString("</title>")
   104  	}
   105  	return pv.String()
   106  }
   107  
   108  func (f *ForwardMessage) CalculateValidationData(seq, random int32, groupCode int64) ([]byte, []byte) {
   109  	msgs := f.PackForwardMessage(seq, random, groupCode)
   110  	trans := &msg.PbMultiMsgTransmit{Msg: msgs, PbItemList: []*msg.PbMultiMsgItem{
   111  		{
   112  			FileName: proto.String("MultiMsg"),
   113  			Buffer:   &msg.PbMultiMsgNew{Msg: msgs},
   114  		},
   115  	}}
   116  	b, _ := proto.Marshal(trans)
   117  	data := binary.GZipCompress(b)
   118  	hash := md5.Sum(data)
   119  	return data, hash[:]
   120  }
   121  
   122  func (f *ForwardMessage) PackForwardMessage(seq int32, random int32, groupCode int64) []*msg.Message {
   123  	ml := make([]*msg.Message, 0, len(f.Nodes))
   124  	for _, node := range f.Nodes {
   125  		ml = append(ml, &msg.Message{
   126  			Head: &msg.MessageHead{
   127  				FromUin: proto.Some(node.SenderId),
   128  				MsgSeq:  proto.Some(seq),
   129  				MsgTime: proto.Some(node.Time),
   130  				MsgUid:  proto.Int64(0x0100_0000_0000_0000 | (int64(random) & 0xFFFFFFFF)),
   131  				MutiltransHead: &msg.MutilTransHead{
   132  					MsgId: proto.Int32(1),
   133  				},
   134  				MsgType: proto.Int32(82),
   135  				GroupInfo: &msg.GroupInfo{
   136  					GroupCode: proto.Some(groupCode),
   137  					GroupRank: []byte{},
   138  					GroupName: []byte{},
   139  					GroupCard: proto.Some(node.SenderName),
   140  				},
   141  			},
   142  			Body: &msg.MessageBody{
   143  				RichText: &msg.RichText{
   144  					Elems: ToProtoElems(node.Message, false),
   145  				},
   146  			},
   147  		})
   148  	}
   149  	return ml
   150  }
   151  
   152  type lazyRegex struct {
   153  	once    sync.Once
   154  	reg     *regexp.Regexp
   155  	Pattern string
   156  }
   157  
   158  func (l *lazyRegex) init() {
   159  	l.reg = regexp.MustCompile(l.Pattern)
   160  }
   161  
   162  func (l *lazyRegex) findMatch1(content string) string {
   163  	l.once.Do(l.init)
   164  	matches := l.reg.FindStringSubmatch(content)
   165  	if matches == nil {
   166  		return ""
   167  	}
   168  	return matches[1]
   169  }
   170  
   171  var (
   172  	mResID    = lazyRegex{Pattern: `m_resid="(.*?)"`}
   173  	mFileName = lazyRegex{Pattern: `m_fileName="(.*?)"`}
   174  )
   175  
   176  func forwardMsgFromXML(xml string) *ForwardElement {
   177  	resid := mResID.findMatch1(xml)
   178  	fileName := mFileName.findMatch1(xml)
   179  	if resid == "" && fileName == "" {
   180  		return nil
   181  	}
   182  	return &ForwardElement{FileName: fileName, ResId: resid}
   183  }