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

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"math/rand"
     7  	"strconv"
     8  
     9  	"github.com/pkg/errors"
    10  
    11  	"github.com/Mrs4s/MiraiGo/client/internal/network"
    12  	"github.com/Mrs4s/MiraiGo/client/pb/channel"
    13  	"github.com/Mrs4s/MiraiGo/client/pb/cmd0x388"
    14  	"github.com/Mrs4s/MiraiGo/client/pb/msg"
    15  	"github.com/Mrs4s/MiraiGo/internal/proto"
    16  	"github.com/Mrs4s/MiraiGo/message"
    17  )
    18  
    19  func init() {
    20  	decoders["ImgStore.QQMeetPicUp"] = decodeGuildImageStoreResponse
    21  }
    22  
    23  func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *message.SendingMessage) (*message.GuildChannelMessage, error) {
    24  	mr := rand.Uint32() // 客户端似乎是生成的 u32 虽然类型是u64
    25  	for _, elem := range m.Elements {
    26  		if elem.Type() == message.At {
    27  			at := elem.(*message.AtElement)
    28  			if at.SubType == message.AtTypeGroupMember {
    29  				at.SubType = message.AtTypeGuildMember
    30  			}
    31  		}
    32  	}
    33  	req := &channel.DF62ReqBody{Msg: &channel.ChannelMsgContent{
    34  		Head: &channel.ChannelMsgHead{
    35  			RoutingHead: &channel.ChannelRoutingHead{
    36  				GuildId:   proto.Some(guildId),
    37  				ChannelId: proto.Some(channelId),
    38  				FromUin:   proto.Uint64(uint64(s.c.Uin)),
    39  			},
    40  			ContentHead: &channel.ChannelContentHead{
    41  				Type:   proto.Uint64(3840), // const
    42  				Random: proto.Uint64(uint64(mr)),
    43  			},
    44  		},
    45  		Body: &msg.MessageBody{
    46  			RichText: &msg.RichText{
    47  				Elems: message.ToProtoElems(m.Elements, true),
    48  			},
    49  		},
    50  	}}
    51  	payload, _ := proto.Marshal(req)
    52  	seq, packet := s.c.uniPacket("MsgProxy.SendMsg", payload)
    53  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
    54  	if err != nil {
    55  		return nil, errors.Wrap(err, "send packet error")
    56  	}
    57  	body := new(channel.DF62RspBody)
    58  	if err = proto.Unmarshal(rsp, body); err != nil {
    59  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
    60  	}
    61  	if body.Result.Unwrap() != 0 {
    62  		return nil, errors.Errorf("send channel message error: server response %v", body.Result.Unwrap())
    63  	}
    64  	elements := m.Elements
    65  	if body.Body != nil && body.Body.RichText != nil {
    66  		elements = message.ParseMessageElems(body.Body.RichText.Elems)
    67  	}
    68  	return &message.GuildChannelMessage{
    69  		Id:         body.Head.ContentHead.Seq.Unwrap(),
    70  		InternalId: body.Head.ContentHead.Random.Unwrap(),
    71  		GuildId:    guildId,
    72  		ChannelId:  channelId,
    73  		Time:       int64(body.SendTime.Unwrap()),
    74  		Sender: &message.GuildSender{
    75  			TinyId:   body.Head.RoutingHead.FromTinyid.Unwrap(),
    76  			Nickname: s.Nickname,
    77  		},
    78  		Elements: elements,
    79  	}, nil
    80  }
    81  
    82  func (s *GuildService) QueryImage(guildId, channelId uint64, hash []byte, size uint64) (*message.GuildImageElement, error) {
    83  	rsp, err := s.c.sendAndWait(s.c.buildGuildImageStorePacket(guildId, channelId, hash, size))
    84  	if err != nil {
    85  		return nil, errors.Wrap(err, "send packet error")
    86  	}
    87  	body := rsp.(*imageUploadResponse)
    88  	if body.IsExists {
    89  		return &message.GuildImageElement{
    90  			FileId:        body.FileId,
    91  			FilePath:      fmt.Sprintf("%x.jpg", hash),
    92  			Size:          int32(size),
    93  			DownloadIndex: body.DownloadIndex,
    94  			Width:         body.Width,
    95  			Height:        body.Height,
    96  			Md5:           hash,
    97  		}, nil
    98  	}
    99  	return nil, errors.New("image is not exists")
   100  }
   101  
   102  // Deprecated: use QQClient.UploadImage instead
   103  func (s *GuildService) UploadGuildImage(guildId, channelId uint64, img io.ReadSeeker) (*message.GuildImageElement, error) {
   104  	source := message.Source{
   105  		SourceType:  message.SourceGuildChannel,
   106  		PrimaryID:   int64(guildId),
   107  		SecondaryID: int64(channelId),
   108  	}
   109  	image, err := s.c.uploadGroupOrGuildImage(source, img)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	return image.(*message.GuildImageElement), nil
   114  }
   115  
   116  func (s *GuildService) PullGuildChannelMessage(guildId, channelId, beginSeq, endSeq uint64) (r []*message.GuildChannelMessage, e error) {
   117  	contents, err := s.pullChannelMessages(guildId, channelId, beginSeq, endSeq, 0, false)
   118  	if err != nil {
   119  		return nil, errors.Wrap(err, "pull channel message error")
   120  	}
   121  	for _, c := range contents {
   122  		if cm := s.parseGuildChannelMessage(c); cm != nil {
   123  			cm.Reactions = decodeGuildMessageEmojiReactions(c)
   124  			r = append(r, cm)
   125  		}
   126  	}
   127  	if len(r) == 0 {
   128  		return nil, errors.New("message not found")
   129  	}
   130  	return
   131  }
   132  
   133  func (s *GuildService) pullChannelMessages(guildId, channelId, beginSeq, endSeq, eventVersion uint64, direct bool) ([]*channel.ChannelMsgContent, error) {
   134  	param := &channel.ChannelParam{
   135  		GuildId:   proto.Some(guildId),
   136  		ChannelId: proto.Some(channelId),
   137  		BeginSeq:  proto.Some(beginSeq),
   138  		EndSeq:    proto.Some(endSeq),
   139  	}
   140  	if eventVersion != 0 {
   141  		param.Version = []uint64{eventVersion}
   142  	}
   143  
   144  	withVersionFlag := uint32(0)
   145  	if eventVersion != 0 {
   146  		withVersionFlag = 1
   147  	}
   148  	directFlag := uint32(0)
   149  	if direct {
   150  		directFlag = 1
   151  	}
   152  	payload, _ := proto.Marshal(&channel.ChannelMsgReq{
   153  		ChannelParam:      param,
   154  		WithVersionFlag:   proto.Some(withVersionFlag),
   155  		DirectMessageFlag: proto.Some(directFlag),
   156  	})
   157  	seq, packet := s.c.uniPacket("trpc.group_pro.synclogic.SyncLogic.GetChannelMsg", payload)
   158  	rsp, err := s.c.sendAndWaitDynamic(seq, packet)
   159  	if err != nil {
   160  		return nil, errors.Wrap(err, "send packet error")
   161  	}
   162  	msgRsp := new(channel.ChannelMsgRsp)
   163  	if err = proto.Unmarshal(rsp, msgRsp); err != nil {
   164  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   165  	}
   166  	return msgRsp.ChannelMsg.Msgs, nil
   167  }
   168  
   169  func (c *QQClient) buildGuildImageStorePacket(guildId, channelId uint64, hash []byte, size uint64) (uint16, []byte) {
   170  	payload, _ := proto.Marshal(&cmd0x388.D388ReqBody{
   171  		NetType: proto.Uint32(3),
   172  		Subcmd:  proto.Uint32(1),
   173  		TryupImgReq: []*cmd0x388.TryUpImgReq{
   174  			{
   175  				GroupCode:       proto.Some(channelId),
   176  				SrcUin:          proto.Uint64(uint64(c.Uin)),
   177  				FileId:          proto.Uint64(0),
   178  				FileMd5:         hash,
   179  				FileSize:        proto.Some(size),
   180  				FileName:        []byte(fmt.Sprintf("%x.jpg", hash)),
   181  				SrcTerm:         proto.Uint32(5),
   182  				PlatformType:    proto.Uint32(9),
   183  				BuType:          proto.Uint32(211),
   184  				PicType:         proto.Uint32(1000),
   185  				BuildVer:        []byte("8.8.38.2266"),
   186  				AppPicType:      proto.Uint32(1052),
   187  				SrvUpload:       proto.Uint32(0),
   188  				QqmeetGuildId:   proto.Some(guildId),
   189  				QqmeetChannelId: proto.Some(channelId),
   190  			},
   191  		},
   192  		CommandId: proto.Uint32(83),
   193  	})
   194  	return c.uniPacket("ImgStore.QQMeetPicUp", payload)
   195  }
   196  
   197  func decodeGuildMessageEmojiReactions(content *channel.ChannelMsgContent) (r []*message.GuildMessageEmojiReaction) {
   198  	r = []*message.GuildMessageEmojiReaction{}
   199  	var common *msg.CommonElem
   200  	for _, elem := range content.Body.RichText.Elems {
   201  		if elem.CommonElem != nil && elem.CommonElem.ServiceType.Unwrap() == 38 {
   202  			common = elem.CommonElem
   203  			break
   204  		}
   205  	}
   206  	if common == nil {
   207  		return
   208  	}
   209  	serv38 := new(msg.MsgElemInfoServtype38)
   210  	_ = proto.Unmarshal(common.PbElem, serv38)
   211  	if len(serv38.ReactData) > 0 {
   212  		cnt := new(channel.MsgCnt)
   213  		_ = proto.Unmarshal(serv38.ReactData, cnt)
   214  		if len(cnt.EmojiReaction) == 0 {
   215  			return
   216  		}
   217  		for _, e := range cnt.EmojiReaction {
   218  			reaction := &message.GuildMessageEmojiReaction{
   219  				EmojiId:   e.EmojiId.Unwrap(),
   220  				EmojiType: e.EmojiType.Unwrap(),
   221  				Count:     int32(e.Cnt.Unwrap()),
   222  				Clicked:   e.IsClicked.Unwrap(),
   223  			}
   224  			if index, err := strconv.ParseInt(e.EmojiId.Unwrap(), 10, 32); err == nil {
   225  				reaction.Face = message.NewFace(int32(index))
   226  			}
   227  			r = append(r, reaction)
   228  		}
   229  	}
   230  	return
   231  }
   232  
   233  func decodeGuildImageStoreResponse(_ *QQClient, pkt *network.Packet) (any, error) {
   234  	body := new(cmd0x388.D388RspBody)
   235  	if err := proto.Unmarshal(pkt.Payload, body); err != nil {
   236  		return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
   237  	}
   238  	if len(body.TryupImgRsp) == 0 {
   239  		return nil, errors.New("response is empty")
   240  	}
   241  	rsp := body.TryupImgRsp[0]
   242  	if rsp.Result.Unwrap() != 0 {
   243  		return &imageUploadResponse{
   244  			ResultCode: int32(rsp.Result.Unwrap()),
   245  			Message:    string(rsp.FailMsg),
   246  		}, nil
   247  	}
   248  	if rsp.FileExit.Unwrap() {
   249  		resp := &imageUploadResponse{
   250  			IsExists:      true,
   251  			FileId:        int64(rsp.Fileid.Unwrap()),
   252  			DownloadIndex: string(rsp.DownloadIndex),
   253  		}
   254  		if rsp.ImgInfo != nil {
   255  			resp.Width = int32(rsp.ImgInfo.FileWidth.Unwrap())
   256  			resp.Height = int32(rsp.ImgInfo.FileHeight.Unwrap())
   257  		}
   258  		return resp, nil
   259  	}
   260  	return &imageUploadResponse{
   261  		FileId:        int64(rsp.Fileid.Unwrap()),
   262  		UploadKey:     rsp.UpUkey,
   263  		UploadIp:      rsp.UpIp,
   264  		UploadPort:    rsp.UpPort,
   265  		DownloadIndex: string(rsp.DownloadIndex),
   266  	}, nil
   267  }
   268  
   269  func (s *GuildService) parseGuildChannelMessage(msg *channel.ChannelMsgContent) *message.GuildChannelMessage {
   270  	guild := s.FindGuild(msg.Head.RoutingHead.GuildId.Unwrap())
   271  	if guild == nil {
   272  		return nil // todo: sync guild info
   273  	}
   274  	if msg.Body == nil || msg.Body.RichText == nil {
   275  		return nil
   276  	}
   277  	// mem := guild.FindMember(msg.Head.RoutingHead.FromTinyid.Unwrap())
   278  	memberName := msg.ExtInfo.MemberName
   279  	if memberName == nil {
   280  		memberName = msg.ExtInfo.FromNick
   281  	}
   282  	return &message.GuildChannelMessage{
   283  		Id:         msg.Head.ContentHead.Seq.Unwrap(),
   284  		InternalId: msg.Head.ContentHead.Random.Unwrap(),
   285  		GuildId:    msg.Head.RoutingHead.GuildId.Unwrap(),
   286  		ChannelId:  msg.Head.RoutingHead.ChannelId.Unwrap(),
   287  		Time:       int64(msg.Head.ContentHead.Time.Unwrap()),
   288  		Sender: &message.GuildSender{
   289  			TinyId:   msg.Head.RoutingHead.FromTinyid.Unwrap(),
   290  			Nickname: string(memberName),
   291  		},
   292  		Elements: message.ParseMessageElems(msg.Body.RichText.Elems),
   293  	}
   294  }