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

     1  package client
     2  
     3  //go:generate go run github.com/Mrs4s/MiraiGo/internal/generator/c2c_switcher
     4  
     5  import (
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/pkg/errors"
    10  
    11  	"github.com/Mrs4s/MiraiGo/binary"
    12  	"github.com/Mrs4s/MiraiGo/client/internal/network"
    13  	"github.com/Mrs4s/MiraiGo/client/pb"
    14  	"github.com/Mrs4s/MiraiGo/client/pb/msg"
    15  	"github.com/Mrs4s/MiraiGo/internal/proto"
    16  )
    17  
    18  type (
    19  	TempSessionInfo struct {
    20  		Source    TempSessionSource
    21  		GroupCode int64
    22  		Sender    int64
    23  
    24  		sig    []byte
    25  		client *QQClient
    26  	}
    27  	TempSessionSource int
    28  )
    29  
    30  const (
    31  	GroupSource         TempSessionSource = iota // 来自群聊
    32  	ConsultingSource                             // 来自QQ咨询
    33  	SearchSource                                 // 来自查找
    34  	MovieSource                                  // 来自QQ电影
    35  	HotChatSource                                // 来自热聊
    36  	SystemMessageSource                          // 来自验证消息
    37  	MultiChatSource                              // 来自多人聊天
    38  	DateSource                                   // 来自约会
    39  	AddressBookSource                            // 来自通讯录
    40  )
    41  
    42  func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info network.RequestParams) {
    43  	c.sig.SyncCookie = rsp.SyncCookie
    44  	c.sig.PubAccountCookie = rsp.PubAccountCookie
    45  	// c.msgCtrlBuf = rsp.MsgCtrlBuf
    46  	if rsp.UinPairMsgs == nil {
    47  		return
    48  	}
    49  	var delItems []*pb.MessageItem
    50  	for _, pairMsg := range rsp.UinPairMsgs {
    51  		for _, pMsg := range pairMsg.Messages {
    52  			// delete message
    53  			delItem := &pb.MessageItem{
    54  				FromUin: pMsg.Head.FromUin.Unwrap(),
    55  				ToUin:   pMsg.Head.ToUin.Unwrap(),
    56  				MsgType: pMsg.Head.MsgType.Unwrap(),
    57  				MsgSeq:  pMsg.Head.MsgSeq.Unwrap(),
    58  				MsgUid:  pMsg.Head.MsgUid.Unwrap(),
    59  			}
    60  			delItems = append(delItems, delItem)
    61  			if pMsg.Head.ToUin.Unwrap() != c.Uin {
    62  				continue
    63  			}
    64  			if (int64(pairMsg.LastReadTime.Unwrap()) & 4294967295) > int64(pMsg.Head.MsgTime.Unwrap()) {
    65  				continue
    66  			}
    67  			c.commMsgProcessor(pMsg, info)
    68  		}
    69  	}
    70  	if delItems != nil {
    71  		_, _ = c.sendAndWait(c.buildDeleteMessageRequestPacket(delItems))
    72  	}
    73  	if rsp.SyncFlag.Unwrap() != msg.SyncFlag_STOP {
    74  		c.debug("continue sync with flag: %v", rsp.SyncFlag)
    75  		seq, pkt := c.buildGetMessageRequestPacket(rsp.SyncFlag.Unwrap(), time.Now().Unix())
    76  		_, _ = c.sendAndWait(seq, pkt, info)
    77  	}
    78  }
    79  
    80  func (c *QQClient) commMsgProcessor(pMsg *msg.Message, info network.RequestParams) {
    81  	strKey := fmt.Sprintf("%d%d%d%d", pMsg.Head.FromUin.Unwrap(), pMsg.Head.ToUin.Unwrap(), pMsg.Head.MsgSeq.Unwrap(), pMsg.Head.MsgUid.Unwrap())
    82  	if _, ok := c.msgSvcCache.GetAndUpdate(strKey, time.Hour); ok {
    83  		c.debug("c2c msg %v already exists in cache. skip.", pMsg.Head.MsgUid.Unwrap())
    84  		return
    85  	}
    86  	c.msgSvcCache.Add(strKey, unit{}, time.Hour)
    87  	if c.lastC2CMsgTime > int64(pMsg.Head.MsgTime.Unwrap()) && (c.lastC2CMsgTime-int64(pMsg.Head.MsgTime.Unwrap())) > 60*10 {
    88  		c.debug("c2c msg filtered by time. lastMsgTime: %v  msgTime: %v", c.lastC2CMsgTime, pMsg.Head.MsgTime.Unwrap())
    89  		return
    90  	}
    91  	c.lastC2CMsgTime = int64(pMsg.Head.MsgTime.Unwrap())
    92  	if info.Bool("init") {
    93  		return
    94  	}
    95  	if decoder, _ := peekC2CDecoder(pMsg.Head.MsgType.Unwrap()); decoder != nil {
    96  		decoder(c, pMsg, info)
    97  	} else {
    98  		c.debug("unknown msg type on c2c processor: %v - %v", pMsg.Head.MsgType.Unwrap(), pMsg.Head.C2CCmd.Unwrap())
    99  	}
   100  }
   101  
   102  func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
   103  	switch pMsg.Head.C2CCmd.Unwrap() {
   104  	case 11, 175: // friend msg
   105  		if pMsg.Head.FromUin.Unwrap() == c.Uin {
   106  			for {
   107  				frdSeq := c.friendSeq.Load()
   108  				if frdSeq < pMsg.Head.MsgSeq.Unwrap() {
   109  					if c.friendSeq.CompareAndSwap(frdSeq, pMsg.Head.MsgSeq.Unwrap()) {
   110  						break
   111  					}
   112  				} else {
   113  					break
   114  				}
   115  			}
   116  		}
   117  		if pMsg.Body.RichText == nil || pMsg.Body.RichText.Elems == nil {
   118  			return
   119  		}
   120  
   121  		// handle fragmented message
   122  		if pMsg.Content != nil && pMsg.Content.PkgNum.Unwrap() > 1 {
   123  			seq := pMsg.Content.DivSeq.Unwrap()
   124  			builder := c.messageBuilder(seq)
   125  			builder.append(pMsg)
   126  			if builder.len() < pMsg.Content.PkgNum.Unwrap() {
   127  				// continue to receive other fragments
   128  				return
   129  			}
   130  			c.msgBuilders.Delete(seq)
   131  			pMsg = builder.build()
   132  		}
   133  
   134  		if pMsg.Head.FromUin.Unwrap() == c.Uin {
   135  			c.SelfPrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg))
   136  			return
   137  		}
   138  		c.PrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg))
   139  	default:
   140  		c.debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.C2CCmd.Unwrap())
   141  	}
   142  }
   143  
   144  func privatePttDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
   145  	if pMsg.Body == nil || pMsg.Body.RichText == nil || pMsg.Body.RichText.Ptt == nil {
   146  		return
   147  	}
   148  	// if len(pMsg.Body.RichText.Ptt.Reserve) != 0 {
   149  	// 	m := binary.NewReader(pMsg.Body.RichText.Ptt.Reserve[1:]).ReadTlvMap(1)
   150  	// 	T3 -> timestamp T8 -> voiceType T9 -> voiceLength T10 -> PbReserveStruct
   151  	//}
   152  	c.PrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg))
   153  }
   154  
   155  func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
   156  	if pMsg.Head.C2CTmpMsgHead == nil || pMsg.Body == nil {
   157  		return
   158  	}
   159  	if (pMsg.Head.MsgType.Unwrap() == 529 && pMsg.Head.C2CCmd.Unwrap() == 6) || pMsg.Body.RichText != nil {
   160  		genTempSessionInfo := func() *TempSessionInfo {
   161  			if pMsg.Head.C2CTmpMsgHead.ServiceType.Unwrap() == 0 {
   162  				group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GroupCode.Unwrap())
   163  				if group == nil {
   164  					return nil
   165  				}
   166  				return &TempSessionInfo{
   167  					Source:    GroupSource,
   168  					GroupCode: group.Code,
   169  					Sender:    pMsg.Head.FromUin.Unwrap(),
   170  					client:    c,
   171  				}
   172  			}
   173  			info := &TempSessionInfo{
   174  				Source: 0,
   175  				Sender: pMsg.Head.FromUin.Unwrap(),
   176  				sig:    pMsg.Head.C2CTmpMsgHead.Sig,
   177  				client: c,
   178  			}
   179  
   180  			switch pMsg.Head.C2CTmpMsgHead.ServiceType.Unwrap() {
   181  			case 1:
   182  				info.Source = MultiChatSource
   183  			case 130:
   184  				info.Source = AddressBookSource
   185  			case 132:
   186  				info.Source = HotChatSource
   187  			case 134:
   188  				info.Source = SystemMessageSource
   189  			case 201:
   190  				info.Source = ConsultingSource
   191  			default:
   192  				return nil
   193  			}
   194  			return info
   195  		}
   196  		session := genTempSessionInfo()
   197  		if session == nil {
   198  			return
   199  		}
   200  		/*
   201  			group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GroupCode.Unwrap())
   202  			if group == nil {
   203  				return
   204  			}
   205  		*/
   206  		if pMsg.Head.FromUin.Unwrap() == c.Uin {
   207  			return
   208  		}
   209  		c.TempMessageEvent.dispatch(c, &TempMessageEvent{
   210  			Message: c.parseTempMessage(pMsg),
   211  			Session: session,
   212  		})
   213  	}
   214  }
   215  
   216  func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
   217  	groupJoinLock.Lock()
   218  	defer groupJoinLock.Unlock()
   219  	group := c.FindGroupByUin(pMsg.Head.FromUin.Unwrap())
   220  	if pMsg.Head.AuthUin.Unwrap() == c.Uin {
   221  		if group == nil && c.ReloadGroupList() == nil {
   222  			c.GroupJoinEvent.dispatch(c, c.FindGroupByUin(pMsg.Head.FromUin.Unwrap()))
   223  		}
   224  	} else {
   225  		if group != nil && group.FindMember(pMsg.Head.AuthUin.Unwrap()) == nil {
   226  			mem, err := c.GetMemberInfo(group.Code, pMsg.Head.AuthUin.Unwrap())
   227  			if err != nil {
   228  				c.debug("failed to fetch new member info: %v", err)
   229  				return
   230  			}
   231  			group.Update(func(info *GroupInfo) {
   232  				info.Members = append(info.Members, mem)
   233  				info.sort()
   234  			})
   235  			c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{
   236  				Group:  group,
   237  				Member: mem,
   238  			})
   239  		}
   240  	}
   241  }
   242  
   243  func systemMessageDecoder(c *QQClient, _ *msg.Message, _ network.RequestParams) {
   244  	_, pkt := c.buildSystemMsgNewFriendPacket()
   245  	_ = c.sendPacket(pkt)
   246  }
   247  
   248  func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message, info network.RequestParams) {
   249  	if !info.Bool("used_reg_proxy") && pMsg.Head.MsgType.Unwrap() != 85 && pMsg.Head.MsgType.Unwrap() != 36 {
   250  		c.exceptAndDispatchGroupSysMsg()
   251  	}
   252  	if len(pMsg.Body.MsgContent) == 0 {
   253  		return
   254  	}
   255  	reader := binary.NewReader(pMsg.Body.MsgContent)
   256  	groupCode := uint32(reader.ReadInt32())
   257  	if info := c.FindGroup(int64(groupCode)); info != nil && pMsg.Head.GroupName.Unwrap() != "" && info.Name != pMsg.Head.GroupName.Unwrap() {
   258  		c.debug("group %v name updated. %v -> %v", groupCode, info.Name, pMsg.Head.GroupName.Unwrap())
   259  		info.Name = pMsg.Head.GroupName.Unwrap()
   260  	}
   261  }
   262  
   263  func msgType0x211Decoder(c *QQClient, pMsg *msg.Message, info network.RequestParams) {
   264  	if pMsg.Head.C2CCmd.Unwrap() == 6 || pMsg.Head.C2CTmpMsgHead != nil {
   265  		tempSessionDecoder(c, pMsg, info)
   266  	}
   267  	sub4 := msg.SubMsgType0X4Body{}
   268  	if err := proto.Unmarshal(pMsg.Body.MsgContent, &sub4); err != nil {
   269  		err = errors.Wrap(err, "unmarshal sub msg 0x4 error")
   270  		c.error("unmarshal sub msg 0x4 error: %v", err)
   271  		return
   272  	}
   273  	if sub4.NotOnlineFile != nil && sub4.NotOnlineFile.Subcmd.Unwrap() == 1 { // subcmd: 1 -> sendPacket, 2-> recv
   274  		rsp, err := c.sendAndWait(c.buildOfflineFileDownloadRequestPacket(sub4.NotOnlineFile.FileUuid)) // offline_file.go
   275  		if err != nil {
   276  			return
   277  		}
   278  		c.OfflineFileEvent.dispatch(c, &OfflineFileEvent{
   279  			FileName:    string(sub4.NotOnlineFile.FileName),
   280  			FileSize:    sub4.NotOnlineFile.FileSize.Unwrap(),
   281  			Sender:      pMsg.Head.FromUin.Unwrap(),
   282  			DownloadUrl: rsp.(string),
   283  		})
   284  	}
   285  }