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

     1  package client
     2  
     3  import (
     4  	"math/rand"
     5  	"time"
     6  
     7  	"github.com/pkg/errors"
     8  
     9  	"github.com/Mrs4s/MiraiGo/client/pb/msg"
    10  	"github.com/Mrs4s/MiraiGo/internal/proto"
    11  	"github.com/Mrs4s/MiraiGo/message"
    12  )
    13  
    14  func (c *QQClient) SendPrivateMessage(target int64, m *message.SendingMessage) *message.PrivateMessage {
    15  	mr := int32(rand.Uint32())
    16  	var seq int32
    17  	t := time.Now().Unix()
    18  	imgCount := 0
    19  	frag := true
    20  	for _, e := range m.Elements {
    21  		switch e.Type() {
    22  		case message.Image:
    23  			imgCount++
    24  		case message.Reply:
    25  			frag = false
    26  		}
    27  	}
    28  	msgLen := message.EstimateLength(m.Elements)
    29  	if msgLen > message.MaxMessageSize || imgCount > 50 {
    30  		return nil
    31  	}
    32  	if frag && (msgLen > 300 || imgCount > 2) && target == c.Uin {
    33  		div := int32(rand.Uint32())
    34  		fragmented := m.ToFragmented()
    35  		for i, elems := range fragmented {
    36  			fseq := c.nextFriendSeq()
    37  			if i == 0 {
    38  				seq = fseq
    39  			}
    40  			_, pkt := c.buildFriendSendingPacket(target, fseq, mr, int32(len(fragmented)), int32(i), div, t, elems)
    41  			_ = c.sendPacket(pkt)
    42  		}
    43  	} else {
    44  		seq = c.nextFriendSeq()
    45  		if target != c.Uin {
    46  			_, pkt := c.buildFriendSendingPacket(target, seq, mr, 1, 0, 0, t, m.Elements)
    47  			_ = c.sendPacket(pkt)
    48  		}
    49  	}
    50  	c.stat.MessageSent.Add(1)
    51  	ret := &message.PrivateMessage{
    52  		Id:         seq,
    53  		InternalId: mr,
    54  		Self:       c.Uin,
    55  		Target:     target,
    56  		Time:       int32(t),
    57  		Sender: &message.Sender{
    58  			Uin:      c.Uin,
    59  			Nickname: c.Nickname,
    60  			IsFriend: true,
    61  		},
    62  		Elements: m.Elements,
    63  	}
    64  	go c.SelfPrivateMessageEvent.dispatch(c, ret)
    65  	return ret
    66  }
    67  
    68  func (c *QQClient) SendGroupTempMessage(groupCode, target int64, m *message.SendingMessage) *message.TempMessage {
    69  	group := c.FindGroup(groupCode)
    70  	if group == nil {
    71  		return nil
    72  	}
    73  	if c.FindFriend(target) != nil {
    74  		pm := c.SendPrivateMessage(target, m)
    75  		return &message.TempMessage{
    76  			Id:        pm.Id,
    77  			GroupCode: group.Code,
    78  			GroupName: group.Name,
    79  			Self:      c.Uin,
    80  			Sender:    pm.Sender,
    81  			Elements:  m.Elements,
    82  		}
    83  	}
    84  	mr := int32(rand.Uint32())
    85  	seq := c.nextFriendSeq()
    86  	t := time.Now().Unix()
    87  	_, pkt := c.buildGroupTempSendingPacket(group.Uin, target, seq, mr, t, m)
    88  	_ = c.sendPacket(pkt)
    89  	c.stat.MessageSent.Add(1)
    90  	return &message.TempMessage{
    91  		Id:        seq,
    92  		GroupCode: group.Code,
    93  		GroupName: group.Name,
    94  		Self:      c.Uin,
    95  		Sender: &message.Sender{
    96  			Uin:      c.Uin,
    97  			Nickname: c.Nickname,
    98  			IsFriend: true,
    99  		},
   100  		Elements: m.Elements,
   101  	}
   102  }
   103  
   104  func (c *QQClient) sendWPATempMessage(target int64, sig []byte, m *message.SendingMessage) *message.TempMessage {
   105  	mr := int32(rand.Uint32())
   106  	seq := c.nextFriendSeq()
   107  	t := time.Now().Unix()
   108  	_, pkt := c.buildWPATempSendingPacket(target, sig, seq, mr, t, m)
   109  	_ = c.sendPacket(pkt)
   110  	c.stat.MessageSent.Add(1)
   111  	return &message.TempMessage{
   112  		Id:   seq,
   113  		Self: c.Uin,
   114  		Sender: &message.Sender{
   115  			Uin:      c.Uin,
   116  			Nickname: c.Nickname,
   117  			IsFriend: true,
   118  		},
   119  		Elements: m.Elements,
   120  	}
   121  }
   122  
   123  func (s *TempSessionInfo) SendMessage(m *message.SendingMessage) (*message.TempMessage, error) {
   124  	switch s.Source {
   125  	case GroupSource:
   126  		return s.client.SendGroupTempMessage(s.GroupCode, s.Sender, m), nil
   127  	case ConsultingSource:
   128  		return s.client.sendWPATempMessage(s.Sender, s.sig, m), nil
   129  	default:
   130  		return nil, errors.New("unsupported message source")
   131  	}
   132  }
   133  
   134  /* this function is unused
   135  func (c *QQClient) buildGetOneDayRoamMsgRequest(target, lastMsgTime, random int64, count uint32) (uint16, []byte) {
   136  	seq := c.nextSeq()
   137  	req := &msg.PbGetOneDayRoamMsgReq{
   138  		PeerUin:     proto.Uint64(uint64(target)),
   139  		LastMsgTime: proto.Uint64(uint64(lastMsgTime)),
   140  		Random:      proto.Uint64(uint64(random)),
   141  		ReadCnt:     proto.Some(count),
   142  	}
   143  	payload, _ := proto.Marshal(req)
   144  	packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbGetOneDayRoamMsg", 1, c.SessionId, EmptyBytes, c.sigInfo.d2Key, payload)
   145  	return seq, packet
   146  }
   147  */
   148  
   149  // MessageSvc.PbSendMsg
   150  func (c *QQClient) buildFriendSendingPacket(target int64, msgSeq, r, pkgNum, pkgIndex, pkgDiv int32, time int64, m []message.IMessageElement) (uint16, []byte) {
   151  	var ptt *msg.Ptt
   152  	if len(m) > 0 {
   153  		if p, ok := m[0].(*message.PrivateVoiceElement); ok {
   154  			ptt = p.Ptt
   155  			m = []message.IMessageElement{}
   156  		}
   157  	}
   158  	req := &msg.SendMessageRequest{
   159  		RoutingHead: &msg.RoutingHead{C2C: &msg.C2C{ToUin: proto.Some(target)}},
   160  		ContentHead: &msg.ContentHead{PkgNum: proto.Some(pkgNum), PkgIndex: proto.Some(pkgIndex), DivSeq: proto.Some(pkgDiv)},
   161  		MsgBody: &msg.MessageBody{
   162  			RichText: &msg.RichText{
   163  				Elems: message.ToProtoElems(m, false),
   164  				Ptt:   ptt,
   165  			},
   166  		},
   167  		MsgSeq:     proto.Some(msgSeq),
   168  		MsgRand:    proto.Some(r),
   169  		SyncCookie: syncCookie(time),
   170  	}
   171  	payload, _ := proto.Marshal(req)
   172  	return c.uniPacket("MessageSvc.PbSendMsg", payload)
   173  }
   174  
   175  // MessageSvc.PbSendMsg
   176  func (c *QQClient) buildGroupTempSendingPacket(groupUin, target int64, msgSeq, r int32, time int64, m *message.SendingMessage) (uint16, []byte) {
   177  	req := &msg.SendMessageRequest{
   178  		RoutingHead: &msg.RoutingHead{GrpTmp: &msg.GrpTmp{
   179  			GroupUin: proto.Some(groupUin),
   180  			ToUin:    proto.Some(target),
   181  		}},
   182  		ContentHead: &msg.ContentHead{PkgNum: proto.Int32(1)},
   183  		MsgBody: &msg.MessageBody{
   184  			RichText: &msg.RichText{
   185  				Elems: message.ToProtoElems(m.Elements, false),
   186  			},
   187  		},
   188  		MsgSeq:     proto.Some(msgSeq),
   189  		MsgRand:    proto.Some(r),
   190  		SyncCookie: syncCookie(time),
   191  	}
   192  	payload, _ := proto.Marshal(req)
   193  	return c.uniPacket("MessageSvc.PbSendMsg", payload)
   194  }
   195  
   196  func (c *QQClient) buildWPATempSendingPacket(uin int64, sig []byte, msgSeq, r int32, time int64, m *message.SendingMessage) (uint16, []byte) {
   197  	req := &msg.SendMessageRequest{
   198  		RoutingHead: &msg.RoutingHead{WpaTmp: &msg.WPATmp{
   199  			ToUin: proto.Uint64(uint64(uin)),
   200  			Sig:   sig,
   201  		}},
   202  		ContentHead: &msg.ContentHead{PkgNum: proto.Int32(1)},
   203  		MsgBody: &msg.MessageBody{
   204  			RichText: &msg.RichText{
   205  				Elems: message.ToProtoElems(m.Elements, false),
   206  			},
   207  		},
   208  		MsgSeq:     proto.Some(msgSeq),
   209  		MsgRand:    proto.Some(r),
   210  		SyncCookie: syncCookie(time),
   211  	}
   212  	payload, _ := proto.Marshal(req)
   213  	return c.uniPacket("MessageSvc.PbSendMsg", payload)
   214  }
   215  
   216  func syncCookie(time int64) []byte {
   217  	cookie, _ := proto.Marshal(&msg.SyncCookie{
   218  		Time:   proto.Some(time),
   219  		Ran1:   proto.Int64(rand.Int63()),
   220  		Ran2:   proto.Int64(rand.Int63()),
   221  		Const1: proto.Some(syncConst1),
   222  		Const2: proto.Some(syncConst2),
   223  		Const3: proto.Int64(0x1d),
   224  	})
   225  	return cookie
   226  }