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

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"math"
     8  	"math/rand"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/pkg/errors"
    14  
    15  	"github.com/Mrs4s/MiraiGo/client/internal/network"
    16  	"github.com/Mrs4s/MiraiGo/client/pb/cmd0x388"
    17  	"github.com/Mrs4s/MiraiGo/client/pb/longmsg"
    18  	"github.com/Mrs4s/MiraiGo/client/pb/msg"
    19  	"github.com/Mrs4s/MiraiGo/client/pb/multimsg"
    20  	"github.com/Mrs4s/MiraiGo/client/pb/oidb"
    21  	"github.com/Mrs4s/MiraiGo/internal/proto"
    22  	"github.com/Mrs4s/MiraiGo/message"
    23  	"github.com/Mrs4s/MiraiGo/utils"
    24  )
    25  
    26  func init() {
    27  	decoders["OnlinePush.PbPushGroupMsg"] = decodeGroupMessagePacket
    28  	decoders["MessageSvc.PbSendMsg"] = decodeMsgSendResponse
    29  	decoders["MessageSvc.PbGetGroupMsg"] = decodeGetGroupMsgResponse
    30  	decoders["OidbSvc.0x8a7_0"] = decodeAtAllRemainResponse
    31  	decoders["OidbSvc.0xeac_1"] = decodeEssenceMsgResponse
    32  	decoders["OidbSvc.0xeac_2"] = decodeEssenceMsgResponse
    33  }
    34  
    35  // SendGroupMessage 发送群消息
    36  func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage) *message.GroupMessage {
    37  	imgCount := 0
    38  	for _, e := range m.Elements {
    39  		switch e.Type() {
    40  		case message.Image:
    41  			imgCount++
    42  		}
    43  	}
    44  	msgLen := message.EstimateLength(m.Elements)
    45  	if msgLen > message.MaxMessageSize || imgCount > 50 {
    46  		return nil
    47  	}
    48  	return c.sendGroupMessage(groupCode, false, m)
    49  }
    50  
    51  // SendGroupForwardMessage 发送群合并转发消息
    52  func (c *QQClient) SendGroupForwardMessage(groupCode int64, m *message.ForwardElement) *message.GroupMessage {
    53  	return c.sendGroupMessage(groupCode, true,
    54  		&message.SendingMessage{Elements: []message.IMessageElement{m}},
    55  	)
    56  }
    57  
    58  // GetGroupMessages 从服务器获取历史信息
    59  func (c *QQClient) GetGroupMessages(groupCode, beginSeq, endSeq int64) ([]*message.GroupMessage, error) {
    60  	seq, pkt := c.buildGetGroupMsgRequest(groupCode, beginSeq, endSeq)
    61  	i, err := c.sendAndWait(seq, pkt, network.RequestParams{"raw": false})
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	return i.([]*message.GroupMessage), nil
    66  }
    67  
    68  func (c *QQClient) GetAtAllRemain(groupCode int64) (*AtAllRemainInfo, error) {
    69  	i, err := c.sendAndWait(c.buildAtAllRemainRequestPacket(groupCode))
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	return i.(*AtAllRemainInfo), nil
    74  }
    75  
    76  func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.SendingMessage) *message.GroupMessage {
    77  	eid := utils.RandomString(6)
    78  	mr := int32(rand.Uint32())
    79  	ch := make(chan int32, 1)
    80  	c.onGroupMessageReceipt(eid, func(c *QQClient, e *groupMessageReceiptEvent) {
    81  		if e.Rand == mr {
    82  			ch <- e.Seq
    83  		}
    84  	})
    85  	defer c.onGroupMessageReceipt(eid)
    86  	imgCount := 0
    87  	serviceFlag := true
    88  	for _, e := range m.Elements {
    89  		switch e.Type() {
    90  		case message.Image:
    91  			imgCount++
    92  		case message.Forward:
    93  			forward = true
    94  			fallthrough
    95  		case message.Reply, message.Voice, message.Service:
    96  			serviceFlag = false
    97  		}
    98  	}
    99  	if !forward && serviceFlag && c.UseFragmentMessage && (imgCount > 1 || message.EstimateLength(m.Elements) > 100) {
   100  		div := int32(rand.Uint32())
   101  		fragmented := m.ToFragmented()
   102  		for i, elems := range fragmented {
   103  			_, pkt := c.buildGroupSendingPacket(groupCode, mr, int32(len(fragmented)), int32(i), div, forward, elems)
   104  			_ = c.sendPacket(pkt)
   105  		}
   106  	} else {
   107  		_, pkt := c.buildGroupSendingPacket(groupCode, mr, 1, 0, 0, forward, m.Elements)
   108  		_ = c.sendPacket(pkt)
   109  	}
   110  	var mid int32
   111  	ret := &message.GroupMessage{
   112  		Id:         -1,
   113  		InternalId: mr,
   114  		GroupCode:  groupCode,
   115  		Sender: &message.Sender{
   116  			Uin:      c.Uin,
   117  			Nickname: c.Nickname,
   118  			IsFriend: true,
   119  		},
   120  		Time:     int32(time.Now().Unix()),
   121  		Elements: m.Elements,
   122  	}
   123  	select {
   124  	case mid = <-ch:
   125  		ret.Id = mid
   126  		return ret
   127  	case <-time.After(time.Second * 5):
   128  		if g, err := c.GetGroupInfo(groupCode); err == nil {
   129  			if history, err := c.GetGroupMessages(groupCode, g.LastMsgSeq-10, g.LastMsgSeq+1); err == nil {
   130  				for _, m := range history {
   131  					if m.InternalId == mr {
   132  						return m
   133  					}
   134  				}
   135  			}
   136  		}
   137  		return ret
   138  	}
   139  }
   140  
   141  func (c *QQClient) multiMsgApplyUp(groupCode int64, data []byte, hash []byte, buType int32) (*multimsg.MultiMsgApplyUpRsp, []byte, error) {
   142  	i, err := c.sendAndWait(c.buildMultiApplyUpPacket(data, hash, buType, utils.ToGroupUin(groupCode)))
   143  	if err != nil {
   144  		return nil, nil, err
   145  	}
   146  	rsp := i.(*multimsg.MultiMsgApplyUpRsp)
   147  	body, _ := proto.Marshal(&longmsg.LongReqBody{
   148  		Subcmd:       1,
   149  		TermType:     5,
   150  		PlatformType: 9,
   151  		MsgUpReq: []*longmsg.LongMsgUpReq{
   152  			{
   153  				MsgType:    3,
   154  				DstUin:     utils.ToGroupUin(groupCode),
   155  				MsgContent: data,
   156  				StoreType:  2,
   157  				MsgUkey:    rsp.MsgUkey,
   158  			},
   159  		},
   160  	})
   161  	return rsp, body, nil
   162  }
   163  
   164  // MessageSvc.PbSendMsg
   165  func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex, pkgDiv int32, forward bool, m []message.IMessageElement) (uint16, []byte) {
   166  	var ptt *message.GroupVoiceElement
   167  	if len(m) > 0 {
   168  		if p, ok := m[0].(*message.GroupVoiceElement); ok {
   169  			ptt = p
   170  			m = []message.IMessageElement{}
   171  		}
   172  	}
   173  	req := &msg.SendMessageRequest{
   174  		RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: proto.Some(groupCode)}},
   175  		ContentHead: &msg.ContentHead{PkgNum: proto.Some(pkgNum), PkgIndex: proto.Some(pkgIndex), DivSeq: proto.Some(pkgDiv)},
   176  		MsgBody: &msg.MessageBody{
   177  			RichText: &msg.RichText{
   178  				Elems: message.ToProtoElems(m, true),
   179  				Ptt: func() *msg.Ptt {
   180  					if ptt != nil {
   181  						return ptt.Ptt
   182  					}
   183  					return nil
   184  				}(),
   185  			},
   186  		},
   187  		MsgSeq:     proto.Int32(c.nextGroupSeq()),
   188  		MsgRand:    proto.Some(r),
   189  		SyncCookie: EmptyBytes,
   190  		MsgVia:     proto.Int32(1),
   191  		MsgCtrl: func() *msg.MsgCtrl {
   192  			if forward {
   193  				return &msg.MsgCtrl{MsgFlag: proto.Int32(4)}
   194  			}
   195  			return nil
   196  		}(),
   197  	}
   198  	payload, _ := proto.Marshal(req)
   199  	return c.uniPacket("MessageSvc.PbSendMsg", payload)
   200  }
   201  
   202  func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) (uint16, []byte) {
   203  	req := &msg.GetGroupMsgReq{
   204  		GroupCode:   proto.Uint64(uint64(groupCode)),
   205  		BeginSeq:    proto.Uint64(uint64(beginSeq)),
   206  		EndSeq:      proto.Uint64(uint64(endSeq)),
   207  		PublicGroup: proto.Bool(false),
   208  	}
   209  	payload, _ := proto.Marshal(req)
   210  	return c.uniPacket("MessageSvc.PbGetGroupMsg", payload)
   211  }
   212  
   213  func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byte) {
   214  	payload := c.packOIDBPackageProto(2215, 0, &oidb.D8A7ReqBody{
   215  		SubCmd:                    proto.Uint32(1),
   216  		LimitIntervalTypeForUin:   proto.Uint32(2),
   217  		LimitIntervalTypeForGroup: proto.Uint32(1),
   218  		Uin:                       proto.Uint64(uint64(c.Uin)),
   219  		GroupCode:                 proto.Uint64(uint64(groupCode)),
   220  	})
   221  	return c.uniPacket("OidbSvc.0x8a7_0", payload)
   222  }
   223  
   224  // OnlinePush.PbPushGroupMsg
   225  func decodeGroupMessagePacket(c *QQClient, packet *network.Packet) (any, error) {
   226  	pkt := msg.PushMessagePacket{}
   227  	err := proto.Unmarshal(packet.Payload, &pkt)
   228  	if err != nil {
   229  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   230  	}
   231  	if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
   232  		c.dispatchGroupMessageReceiptEvent(&groupMessageReceiptEvent{
   233  			Rand: pkt.Message.Body.RichText.Attr.Random.Unwrap(),
   234  			Seq:  pkt.Message.Head.MsgSeq.Unwrap(),
   235  			Msg:  c.parseGroupMessage(pkt.Message),
   236  		})
   237  	}
   238  	if pkt.Message.Content != nil && pkt.Message.Content.PkgNum.Unwrap() > 1 {
   239  		seq := pkt.Message.Content.DivSeq.Unwrap()
   240  		builder := c.messageBuilder(pkt.Message.Content.DivSeq.Unwrap())
   241  		builder.append(pkt.Message)
   242  		if builder.len() >= pkt.Message.Content.PkgNum.Unwrap() {
   243  			c.msgBuilders.Delete(seq)
   244  			if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
   245  				c.SelfGroupMessageEvent.dispatch(c, c.parseGroupMessage(builder.build()))
   246  			} else {
   247  				c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(builder.build()))
   248  			}
   249  		}
   250  		return nil, nil
   251  	}
   252  	if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
   253  		c.SelfGroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message))
   254  	} else {
   255  		c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message))
   256  	}
   257  	return nil, nil
   258  }
   259  
   260  func decodeMsgSendResponse(c *QQClient, pkt *network.Packet) (any, error) {
   261  	rsp := msg.SendMessageResponse{}
   262  	if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
   263  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   264  	}
   265  	switch rsp.Result.Unwrap() {
   266  	case 0: // OK.
   267  	case 46:
   268  		c.error("sendPacket msg error: 需要使用安全设备验证")
   269  	case 55:
   270  		c.error("sendPacket msg error: %v Bot has been blocked ta.'s content", rsp.Result.Unwrap())
   271  	default:
   272  		c.error("sendPacket msg error: %v %v", rsp.Result.Unwrap(), rsp.ErrMsg.Unwrap())
   273  	}
   274  	return nil, nil
   275  }
   276  
   277  func decodeGetGroupMsgResponse(c *QQClient, pkt *network.Packet) (any, error) {
   278  	rsp := msg.GetGroupMsgResp{}
   279  	if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
   280  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   281  	}
   282  	if rsp.Result.Unwrap() != 0 {
   283  		c.error("get msg error: %v %v", rsp.Result.Unwrap(), rsp.Errmsg.Unwrap())
   284  		return nil, errors.Errorf("get msg error: %v msg: %v", rsp.Result.Unwrap(), rsp.Errmsg.Unwrap())
   285  	}
   286  	var ret []*message.GroupMessage
   287  	for _, m := range rsp.Msg {
   288  		if m.Head.FromUin.IsNone() {
   289  			continue
   290  		}
   291  		if m.Content != nil && m.Content.PkgNum.Unwrap() > 1 && !pkt.Params.Bool("raw") {
   292  			if m.Content.PkgIndex.Unwrap() == 0 {
   293  				c.debug("build fragmented message from history")
   294  				i := m.Head.MsgSeq.Unwrap() - m.Content.PkgNum.Unwrap()
   295  				builder := &messageBuilder{}
   296  				for {
   297  					end := int32(math.Min(float64(i+19), float64(m.Head.MsgSeq.Unwrap()+m.Content.PkgNum.Unwrap())))
   298  					seq, pkt := c.buildGetGroupMsgRequest(m.Head.GroupInfo.GroupCode.Unwrap(), int64(i), int64(end))
   299  					data, err := c.sendAndWait(seq, pkt, network.RequestParams{"raw": true})
   300  					if err != nil {
   301  						return nil, errors.Wrap(err, "build fragmented message error")
   302  					}
   303  					for _, fm := range data.([]*message.GroupMessage) {
   304  						if fm.OriginalObject.Content != nil && fm.OriginalObject.Content.DivSeq.Unwrap() == m.Content.DivSeq.Unwrap() {
   305  							builder.append(fm.OriginalObject)
   306  						}
   307  					}
   308  					if end >= m.Head.MsgSeq.Unwrap()+m.Content.PkgNum.Unwrap() {
   309  						break
   310  					}
   311  					i = end
   312  				}
   313  				if elem := c.parseGroupMessage(builder.build()); elem != nil {
   314  					ret = append(ret, elem)
   315  				}
   316  			}
   317  			continue
   318  		}
   319  		if elem := c.parseGroupMessage(m); elem != nil {
   320  			ret = append(ret, elem)
   321  		}
   322  	}
   323  	return ret, nil
   324  }
   325  
   326  func decodeAtAllRemainResponse(_ *QQClient, pkt *network.Packet) (any, error) {
   327  	rsp := oidb.D8A7RspBody{}
   328  	err := unpackOIDBPackage(pkt.Payload, &rsp)
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  	return &AtAllRemainInfo{
   333  		CanAtAll:                 rsp.CanAtAll.Unwrap(),
   334  		RemainAtAllCountForGroup: rsp.RemainAtAllCountForGroup.Unwrap(),
   335  		RemainAtAllCountForUin:   rsp.RemainAtAllCountForUin.Unwrap(),
   336  	}, nil
   337  }
   338  
   339  func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
   340  	group := c.FindGroup(m.Head.GroupInfo.GroupCode.Unwrap())
   341  	if group == nil {
   342  		c.debug("sync group %v.", m.Head.GroupInfo.GroupCode.Unwrap())
   343  		info, err := c.GetGroupInfo(m.Head.GroupInfo.GroupCode.Unwrap())
   344  		if err != nil {
   345  			c.error("failed to sync group %v : %+v", m.Head.GroupInfo.GroupCode.Unwrap(), err)
   346  			return nil
   347  		}
   348  		group = info
   349  		c.GroupList = append(c.GroupList, info)
   350  	}
   351  	if len(group.Members) == 0 {
   352  		mem, err := c.GetGroupMembers(group)
   353  		if err != nil {
   354  			c.error("failed to sync group %v members : %+v", m.Head.GroupInfo.GroupCode, err)
   355  			return nil
   356  		}
   357  		group.Members = mem
   358  	}
   359  	var anonInfo *msg.AnonymousGroupMessage
   360  	for _, e := range m.Body.RichText.Elems {
   361  		if e.AnonGroupMsg != nil {
   362  			anonInfo = e.AnonGroupMsg
   363  		}
   364  	}
   365  	var sender *message.Sender
   366  	if anonInfo != nil {
   367  		sender = &message.Sender{
   368  			Uin:      80000000,
   369  			Nickname: string(anonInfo.AnonNick),
   370  			AnonymousInfo: &message.AnonymousInfo{
   371  				AnonymousId:   base64.StdEncoding.EncodeToString(anonInfo.AnonId),
   372  				AnonymousNick: string(anonInfo.AnonNick),
   373  			},
   374  			IsFriend: false,
   375  		}
   376  	} else {
   377  		mem := group.FindMember(m.Head.FromUin.Unwrap())
   378  		if mem == nil {
   379  			group.Update(func(_ *GroupInfo) {
   380  				if mem = group.FindMemberWithoutLock(m.Head.FromUin.Unwrap()); mem != nil {
   381  					return
   382  				}
   383  				info, _ := c.GetMemberInfo(group.Code, m.Head.FromUin.Unwrap())
   384  				if info == nil {
   385  					return
   386  				}
   387  				mem = info
   388  				group.Members = append(group.Members, mem)
   389  				group.sort()
   390  				go c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{
   391  					Group:  group,
   392  					Member: info,
   393  				})
   394  			})
   395  			if mem == nil {
   396  				return nil
   397  			}
   398  		}
   399  		sender = &message.Sender{
   400  			Uin:      mem.Uin,
   401  			Nickname: mem.Nickname,
   402  			CardName: mem.CardName,
   403  			IsFriend: c.FindFriend(mem.Uin) != nil,
   404  		}
   405  	}
   406  	var g *message.GroupMessage
   407  	g = &message.GroupMessage{
   408  		Id:             m.Head.MsgSeq.Unwrap(),
   409  		GroupCode:      group.Code,
   410  		GroupName:      string(m.Head.GroupInfo.GroupName),
   411  		Sender:         sender,
   412  		Time:           m.Head.MsgTime.Unwrap(),
   413  		Elements:       message.ParseMessageElems(m.Body.RichText.Elems),
   414  		OriginalObject: m,
   415  	}
   416  	var extInfo *msg.ExtraInfo
   417  	// pre parse
   418  	for _, elem := range m.Body.RichText.Elems {
   419  		// is rich long msg
   420  		if elem.GeneralFlags != nil && elem.GeneralFlags.LongTextResid.Unwrap() != "" && len(g.Elements) == 1 {
   421  			if f := c.GetForwardMessage(elem.GeneralFlags.LongTextResid.Unwrap()); f != nil && len(f.Nodes) == 1 {
   422  				g = &message.GroupMessage{
   423  					Id:             m.Head.MsgSeq.Unwrap(),
   424  					GroupCode:      group.Code,
   425  					GroupName:      string(m.Head.GroupInfo.GroupName),
   426  					Sender:         sender,
   427  					Time:           m.Head.MsgTime.Unwrap(),
   428  					Elements:       f.Nodes[0].Message,
   429  					OriginalObject: m,
   430  				}
   431  			}
   432  		}
   433  		if elem.ExtraInfo != nil {
   434  			extInfo = elem.ExtraInfo
   435  		}
   436  	}
   437  	if !sender.IsAnonymous() {
   438  		mem := group.FindMember(m.Head.FromUin.Unwrap())
   439  		groupCard := m.Head.GroupInfo.GroupCard.Unwrap()
   440  		if extInfo != nil && len(extInfo.GroupCard) > 0 && extInfo.GroupCard[0] == 0x0A {
   441  			buf := oidb.D8FCCommCardNameBuf{}
   442  			if err := proto.Unmarshal(extInfo.GroupCard, &buf); err == nil && len(buf.RichCardName) > 0 {
   443  				var gcard strings.Builder
   444  				for _, e := range buf.RichCardName {
   445  					gcard.Write(e.Text)
   446  				}
   447  				groupCard = gcard.String()
   448  			}
   449  		}
   450  		if m.Head.GroupInfo != nil && groupCard != "" && mem.CardName != groupCard {
   451  			old := mem.CardName
   452  			if mem.Nickname == groupCard {
   453  				mem.CardName = ""
   454  			} else {
   455  				mem.CardName = groupCard
   456  			}
   457  			if old != mem.CardName {
   458  				c.MemberCardUpdatedEvent.dispatch(c, &MemberCardUpdatedEvent{
   459  					Group:   group,
   460  					OldCard: old,
   461  					Member:  mem,
   462  				})
   463  			}
   464  		}
   465  	}
   466  	if m.Body.RichText.Ptt != nil {
   467  		var url string
   468  		if len(m.Body.RichText.Ptt.DownPara) == 0 {
   469  			req := &cmd0x388.D388ReqBody{
   470  				NetType: proto.Uint32(3),
   471  				Subcmd:  proto.Uint32(4),
   472  				GetpttUrlReq: []*cmd0x388.GetPttUrlReq{
   473  					{
   474  						GroupCode:       proto.Uint64(uint64(m.Head.GroupInfo.GroupCode.Unwrap())),
   475  						DstUin:          proto.Uint64(uint64(m.Head.ToUin.Unwrap())),
   476  						Fileid:          proto.Uint64(uint64(m.Body.RichText.Ptt.FileId.Unwrap())),
   477  						FileMd5:         m.Body.RichText.Ptt.FileMd5,
   478  						ReqTerm:         proto.Uint32(5),
   479  						ReqPlatformType: proto.Uint32(9),
   480  						InnerIp:         proto.Uint32(0),
   481  						BuType:          proto.Uint32(3),
   482  						FileId:          proto.Uint64(0),
   483  						FileKey:         m.Body.RichText.Ptt.FileKey,
   484  						ReqTransferType: proto.Uint32(2),
   485  						IsAuto:          proto.Uint32(1),
   486  					},
   487  				},
   488  			}
   489  			payload, _ := proto.Marshal(req)
   490  			rsp_raw, _ := c.sendAndWaitDynamic(c.uniPacket("PttStore.GroupPttDown", payload))
   491  			rsp := new(cmd0x388.D388RspBody)
   492  			proto.Unmarshal(rsp_raw, rsp)
   493  			resp := rsp.GetpttUrlRsp[0]
   494  			url = "http://" + string(resp.DownDomain) + string(resp.DownPara)
   495  		} else {
   496  			url = "http://grouptalk.c2c.qq.com" + string(m.Body.RichText.Ptt.DownPara)
   497  		}
   498  
   499  		g.Elements = []message.IMessageElement{
   500  			&message.VoiceElement{
   501  				Name: m.Body.RichText.Ptt.FileName.Unwrap(),
   502  				Md5:  m.Body.RichText.Ptt.FileMd5,
   503  				Size: m.Body.RichText.Ptt.FileSize.Unwrap(),
   504  				Url:  url,
   505  			},
   506  		}
   507  	}
   508  	if m.Body.RichText.Attr != nil {
   509  		g.InternalId = m.Body.RichText.Attr.Random.Unwrap()
   510  	}
   511  	return g
   512  }
   513  
   514  // SetEssenceMessage 设为群精华消息
   515  func (c *QQClient) SetEssenceMessage(groupCode int64, msgID, msgInternalId int32) error {
   516  	r, err := c.sendAndWait(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 1))
   517  	if err != nil {
   518  		return errors.Wrap(err, "set essence msg network")
   519  	}
   520  	rsp := r.(*oidb.EACRspBody)
   521  	if rsp.ErrorCode.Unwrap() != 0 {
   522  		return errors.New(rsp.Wording.Unwrap())
   523  	}
   524  	return nil
   525  }
   526  
   527  // DeleteEssenceMessage 移出群精华消息
   528  func (c *QQClient) DeleteEssenceMessage(groupCode int64, msgID, msgInternalId int32) error {
   529  	r, err := c.sendAndWait(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 2))
   530  	if err != nil {
   531  		return errors.Wrap(err, "set essence msg networ")
   532  	}
   533  	rsp := r.(*oidb.EACRspBody)
   534  	if rsp.ErrorCode.Unwrap() != 0 {
   535  		return errors.New(rsp.Wording.Unwrap())
   536  	}
   537  	return nil
   538  }
   539  
   540  func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand, opType uint32) (uint16, []byte) {
   541  	commandName := "OidbSvc.0xeac_" + strconv.FormatInt(int64(opType), 10)
   542  	payload := c.packOIDBPackageProto(3756, int32(opType), &oidb.EACReqBody{ // serviceType 2 取消
   543  		GroupCode: proto.Uint64(uint64(groupCode)),
   544  		Seq:       proto.Uint32(msgSeq),
   545  		Random:    proto.Uint32(msgRand),
   546  	})
   547  	return c.uniPacket(commandName, payload)
   548  }
   549  
   550  // OidbSvc.0xeac_1/2
   551  func decodeEssenceMsgResponse(_ *QQClient, pkt *network.Packet) (any, error) {
   552  	rsp := &oidb.EACRspBody{}
   553  	err := unpackOIDBPackage(pkt.Payload, &rsp)
   554  	if err != nil {
   555  		return nil, err
   556  	}
   557  	return rsp, nil
   558  }
   559  
   560  // GetGroupEssenceMsgList 获取群精华消息列表
   561  func (c *QQClient) GetGroupEssenceMsgList(groupCode int64) ([]GroupDigest, error) {
   562  	essenceURL := "https://qun.qq.com/essence/index?gc=" + strconv.FormatInt(groupCode, 10) + "&_wv=3&_wwv=128&_wvx=2&_wvxBclr=f5f6fa"
   563  	rsp, err := utils.HttpGetBytes(essenceURL, c.getCookiesWithDomain("qun.qq.com"))
   564  	if err != nil {
   565  		return nil, errors.Wrap(err, "get essence msg network error")
   566  	}
   567  	rsp = rsp[bytes.Index(rsp, []byte("window.__INITIAL_STATE__={"))+25:]
   568  	rsp = rsp[:bytes.Index(rsp, []byte("</script>"))]
   569  	data := &struct {
   570  		List []GroupDigest `json:"msgList"`
   571  	}{}
   572  	err = json.Unmarshal(rsp, data)
   573  	if err != nil {
   574  		return nil, errors.Wrap(err, "failed to unmarshal json")
   575  	}
   576  	return data.List, nil
   577  }