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 }