1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-07-08 07:08:32 +00:00

9 Commits

Author SHA1 Message Date
367f1d52e9 edit note 2024-09-05 15:10:04 +08:00
18c895cf21 support parse 0x126d_200 rsp 2024-09-05 14:58:52 +08:00
f51b043e9e support RecordDownloadReqPacket 2024-09-05 12:19:08 +08:00
26a9f6d604 support NTV2RichMediaReq 2024-09-05 11:19:37 +08:00
47b45f8b7b add image file name 2024-09-05 10:13:02 +08:00
1faca7cf73 fix: wrong imgurl 2024-09-05 10:07:27 +08:00
9ee41fc5f9 update 2024-09-04 21:05:51 +08:00
cd753c1953 update 2024-09-04 20:55:17 +08:00
f4a63a83cd fix: panic occurred when receive voice record in group message 2024-09-03 15:11:22 +08:00
10 changed files with 342 additions and 52 deletions

View File

@ -195,16 +195,16 @@ func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage
Sender: sender,
Self: c.Uin,
Elements: func() []message.IMessageElement {
if msg.Body.RichText.Ptt != nil {
return []message.IMessageElement{
&message.VoiceElement{
Name: msg.Body.RichText.Ptt.FileName.Unwrap(),
Md5: msg.Body.RichText.Ptt.FileMd5,
Size: msg.Body.RichText.Ptt.FileSize.Unwrap(),
Url: string(msg.Body.RichText.Ptt.DownPara),
},
}
}
// if msg.Body.RichText.Ptt != nil {
// return []message.IMessageElement{
// &message.VoiceElement{
// Name: msg.Body.RichText.Ptt.FileName.Unwrap(),
// Md5: msg.Body.RichText.Ptt.FileMd5,
// Size: msg.Body.RichText.Ptt.FileSize.Unwrap(),
// Url: string(msg.Body.RichText.Ptt.DownPara),
// },
// }
// }
return message.ParseMessageElems(msg.Body.RichText.Elems)
}(),
}

View File

@ -59,6 +59,7 @@ OidbSvcTrpcTcp.0x101e_2
OidbSvcTrpcTcp.0x1100_1
OidbSvcTrpcTcp.0x1105_1
OidbSvcTrpcTcp.0x1107_1
OidbSvcTrpcTcp.0x126d_200
OidbSvcTrpcTcp.0x55f_0
OidbSvcTrpcTcp.0x6d9_4
OidbSvcTrpcTcp.0xf55_1

View File

@ -881,6 +881,12 @@ type MsgElemInfoServtype37 struct {
Randomtype proto.Option[uint32] `protobuf:"varint,9,opt"`
}
type PbMultiMediaElement struct {
Elem1 *PbMultiMediaElement_Elem1 `protobuf:"bytes,1,opt"`
Elem2 *PbMultiMediaElement_Elem2 `protobuf:"bytes,2,opt"`
_ [0]func()
}
type ElemFlags2_Inst struct {
AppId proto.Option[uint32] `protobuf:"varint,1,opt"`
InstId proto.Option[uint32] `protobuf:"varint,2,opt"`
@ -893,30 +899,47 @@ type NotOnlineImage_PbReserve struct {
_ [0]func()
}
type PbMultiMediaElement struct {
Elem1 *struct {
Meta *struct {
Data *struct {
FileLen proto.Option[int32] `protobuf:"varint,1,opt"`
PicMd5 []byte `protobuf:"bytes,2,opt"`
} `protobuf:"bytes,1,opt"`
FilePath proto.Option[string] `protobuf:"bytes,2,opt"`
} `protobuf:"bytes,1,opt"`
Data *struct {
ImgURL proto.Option[string] `protobuf:"bytes,1,opt"`
Domain proto.Option[string] `protobuf:"bytes,3,opt"`
} `protobuf:"bytes,2,opt"`
} `protobuf:"bytes,1,opt"`
Elem2 *struct {
Data *struct {
Friend *struct {
RKey proto.Option[string] `protobuf:"bytes,30,opt"`
} `protobuf:"bytes,11,opt"`
Group *struct {
RKey proto.Option[string] `protobuf:"bytes,30,opt"`
} `protobuf:"bytes,12,opt"`
} `protobuf:"bytes,1,opt"`
} `protobuf:"bytes,2,opt"`
type PbMultiMediaElement_Elem1 struct {
Meta *PbMultiMediaElement_Elem1_Meta `protobuf:"bytes,1,opt"`
Data *PbMultiMediaElement_Elem1_Data `protobuf:"bytes,2,opt"`
_ [0]func()
}
type PbMultiMediaElement_Elem2 struct {
Data *PbMultiMediaElement_Elem2_Data `protobuf:"bytes,1,opt"`
_ [0]func()
}
type PbMultiMediaElement_Elem1_Meta struct {
Data *PbMultiMediaElement_Elem1_Meta_Data `protobuf:"bytes,1,opt"`
FilePath proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
}
type PbMultiMediaElement_Elem1_Data struct {
ImgURL proto.Option[string] `protobuf:"bytes,1,opt"`
Domain proto.Option[string] `protobuf:"bytes,3,opt"`
_ [0]func()
}
type PbMultiMediaElement_Elem1_Meta_Data struct {
FileLen proto.Option[int32] `protobuf:"varint,1,opt"`
FileMd5 []byte `protobuf:"bytes,2,opt"`
FileName proto.Option[string] `protobuf:"bytes,4,opt"`
}
type PbMultiMediaElement_Elem2_Data struct {
Friend *PbMultiMediaElement_Elem2_Data_Friend `protobuf:"bytes,11,opt"`
Group *PbMultiMediaElement_Elem2_Data_Group `protobuf:"bytes,12,opt"`
_ [0]func()
}
type PbMultiMediaElement_Elem2_Data_Friend struct {
RKey proto.Option[string] `protobuf:"bytes,30,opt"`
_ [0]func()
}
type PbMultiMediaElement_Elem2_Data_Group struct {
RKey proto.Option[string] `protobuf:"bytes,30,opt"`
_ [0]func()
}

View File

@ -883,7 +883,8 @@ message PbMultiMediaElement {
message Meta {
message Data {
optional int32 FileLen = 1;
optional bytes PicMd5 = 2;
optional bytes FileMd5 = 2;
optional string FileName = 4;
}
optional Data data = 1;
optional string FilePath = 2;
@ -891,7 +892,7 @@ message PbMultiMediaElement {
optional Meta meta = 1;
message Data {
optional string ImgURL = 2;
optional string ImgURL = 1;
optional string Domain = 3;
}
optional Data data = 2;

View File

@ -0,0 +1,94 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/richmedia/ntv2.proto
package richmedia
type NTV2RichMediaReq struct {
ReqHead *MultiMediaReqHead `protobuf:"bytes,1,opt"`
Download *DownloadReq `protobuf:"bytes,3,opt"`
_ [0]func()
}
type MultiMediaReqHead struct {
Common *CommonHead `protobuf:"bytes,1,opt"`
Scene *SceneInfo `protobuf:"bytes,2,opt"`
Client *ClientMeta `protobuf:"bytes,3,opt"`
_ [0]func()
}
type CommonHead struct {
RequestId uint32 `protobuf:"varint,1,opt"`
Command uint32 `protobuf:"varint,2,opt"`
_ [0]func()
}
type SceneInfo struct {
RequestType uint32 `protobuf:"varint,101,opt"`
BusinessType uint32 `protobuf:"varint,102,opt"`
SceneType uint32 `protobuf:"varint,200,opt"`
C2C *C2CUserInfo `protobuf:"bytes,201,opt"`
Group *NTGroupInfo `protobuf:"bytes,202,opt"`
_ [0]func()
}
type ClientMeta struct {
AgentType uint32 `protobuf:"varint,1,opt"`
_ [0]func()
}
type C2CUserInfo struct {
AccountType uint32 `protobuf:"varint,1,opt"`
TargetUid string `protobuf:"bytes,2,opt"`
_ [0]func()
}
type NTGroupInfo struct {
GroupUin uint32 `protobuf:"varint,1,opt"`
_ [0]func()
}
type DownloadReq struct {
Node *IndexNode `protobuf:"bytes,1,opt"`
_ [0]func()
}
type IndexNode struct {
Info *FileInfo `protobuf:"bytes,1,opt"`
FileUuid string `protobuf:"bytes,2,opt"`
StoreId uint32 `protobuf:"varint,3,opt"`
_ [0]func()
}
type FileInfo struct {
Type *FileType `protobuf:"bytes,5,opt"`
Time uint32 `protobuf:"varint,8,opt"`
_ [0]func()
}
type FileType struct {
Type uint32 `protobuf:"varint,1,opt"`
VoiceFormat uint32 `protobuf:"varint,4,opt"`
_ [0]func()
}
type NTV2RichMediaRsp struct {
MediaResp *MediaResp `protobuf:"bytes,4,opt"`
_ [0]func()
}
type MediaResp struct {
DownloadResp *DownloadResp `protobuf:"bytes,3,opt"`
_ [0]func()
}
type DownloadResp struct {
Rkey string `protobuf:"bytes,1,opt"`
Info *DownloadInfo `protobuf:"bytes,3,opt"`
_ [0]func()
}
type DownloadInfo struct {
Domain string `protobuf:"bytes,1,opt"`
UrlPath string `protobuf:"bytes,2,opt"`
_ [0]func()
}

View File

@ -0,0 +1,78 @@
syntax = "proto3";
option go_package = "github.com/Mrs4s/MiraiGo/client/pb/richmedia";
message NTV2RichMediaReq {
MultiMediaReqHead ReqHead = 1;
DownloadReq Download = 3;
}
message MultiMediaReqHead {
CommonHead Common = 1;
SceneInfo Scene = 2;
ClientMeta Client = 3;
}
message CommonHead {
uint32 RequestId = 1;
uint32 Command = 2;
}
message SceneInfo {
uint32 RequestType = 101;
uint32 BusinessType = 102;
uint32 SceneType = 200;
optional C2CUserInfo C2C = 201;
optional NTGroupInfo Group = 202;
}
message ClientMeta {
uint32 AgentType = 1;
}
message C2CUserInfo {
uint32 AccountType = 1;
string TargetUid = 2;
}
message NTGroupInfo {
uint32 GroupUin = 1;
}
message DownloadReq {
IndexNode Node = 1;
}
message IndexNode {
FileInfo Info = 1;
string FileUuid = 2;
uint32 StoreId = 3;
}
message FileInfo {
FileType Type = 5;
uint32 Time = 8;
}
message FileType {
uint32 Type = 1;
uint32 VoiceFormat = 4;
}
message NTV2RichMediaRsp {
MediaResp MediaResp = 4;
}
message MediaResp {
DownloadResp DownloadResp = 3;
}
message DownloadResp {
string Rkey = 1;
DownloadInfo Info = 3;
}
message DownloadInfo {
string Domain = 1;
string UrlPath = 2;
}

74
client/richmedia.go Normal file
View File

@ -0,0 +1,74 @@
package client
import (
"fmt"
"github.com/Mrs4s/MiraiGo/client/pb/richmedia"
"github.com/Mrs4s/MiraiGo/internal/proto"
)
// OidbSvcTrpcTcp.0x126d_200
func (c *QQClient) buildRecordDownloadReqPacket(Uid string, FileId string, groupUin int64, isGroup bool) (uint16, []byte) {
scene := &richmedia.SceneInfo{
RequestType: 2,
BusinessType: 3,
SceneType: 1,
C2C: &richmedia.C2CUserInfo{
AccountType: 2,
TargetUid: Uid,
},
}
if isGroup {
scene.RequestType = 1
scene.SceneType = 2
scene.Group = &richmedia.NTGroupInfo{
GroupUin: uint32(groupUin),
}
}
body := &richmedia.NTV2RichMediaReq{
ReqHead: &richmedia.MultiMediaReqHead{
Common: &richmedia.CommonHead{
RequestId: 3,
Command: 200,
},
Scene: scene,
Client: &richmedia.ClientMeta{
AgentType: 2,
},
},
Download: &richmedia.DownloadReq{
Node: &richmedia.IndexNode{
Info: &richmedia.FileInfo{
Type: &richmedia.FileType{
Type: 3,
VoiceFormat: 1,
},
Time: 1,
},
FileUuid: FileId,
StoreId: 1,
},
},
}
b, err := proto.Marshal(body)
if err != nil {
fmt.Println(err)
}
payload := c.packOIDBPackage(4717, 200, b)
return c.uniPacket("OidbSvcTrpcTcp.0x126d_200", payload)
}
func (c *QQClient) ParseRecordDownloadRspPacket(body []byte) string {
rp := &richmedia.NTV2RichMediaRsp{}
if err := proto.Unmarshal(body, rp); err != nil && rp.MediaResp.DownloadResp.Info != nil {
c.error("parse RecordDownloadRspPacket error: %v", err)
return ""
}
return fmt.Sprintf("https://%s%s%s", rp.MediaResp.DownloadResp.Info.Domain, rp.MediaResp.DownloadResp.Info.UrlPath, rp.MediaResp.DownloadResp.Rkey)
}
// GetRecordDownloadUrl 获取语音文件下载地址
func (c *QQClient) GetRecordDownloadUrl(selfUid string, FileId string, groupUin int64, isGroup bool) string {
body, _ := c.sendAndWaitDynamic(c.buildRecordDownloadReqPacket(selfUid, FileId, groupUin, isGroup))
return c.ParseRecordDownloadRspPacket(body)
}

View File

@ -14,10 +14,12 @@ type TextElement struct {
}
type VoiceElement struct {
Name string
Md5 []byte
Size int32
Url string
Name string
Md5 []byte
Size int32
Url string
FileId string
IsGroup bool
// --- sending ---
Data []byte

View File

@ -19,7 +19,7 @@ type GroupImageElement struct {
Height int32
Md5 []byte
Url string
Name string
// EffectID show pic effect id.
EffectID int32
Flash bool
@ -32,8 +32,8 @@ type FriendImageElement struct {
Width int32
Height int32
Url string
Flash bool
Name string
Flash bool
}
type GuildImageElement struct {

View File

@ -671,36 +671,53 @@ func ParseMessageElems(elems []*msg.Elem) []IMessageElement {
Name: strings.TrimPrefix(string(animatedStickerMsg.Text), "/"),
}
return []IMessageElement{sticker} // sticker 永远为单独消息
case 48:
}
bt := elem.CommonElem.BusinessType.Unwrap()
switch bt {
case 10, 20:
img := &msg.PbMultiMediaElement{}
_ = proto.Unmarshal(elem.CommonElem.PbElem, img)
domain := img.Elem1.Data.Domain.Unwrap()
imgURL := img.Elem1.Data.ImgURL.Unwrap()
if img.Elem2.Data.Friend != nil {
rKey := img.Elem2.Data.Friend.RKey.Unwrap()
url := fmt.Sprintf("https://%s%s%s&spec=0&rf=naio", domain, imgURL, rKey)
url := fmt.Sprintf("https://%s%s%s", domain, imgURL, rKey)
res = append(res, &FriendImageElement{
ImageId: img.Elem1.Meta.FilePath.Unwrap(),
Size: img.Elem1.Meta.Data.FileLen.Unwrap(),
Url: url,
Md5: img.Elem1.Meta.Data.PicMd5,
Md5: img.Elem1.Meta.Data.FileMd5,
Name: img.Elem1.Meta.Data.FileName.Unwrap(),
})
newImg = true
}
if img.Elem2.Data.Group != nil {
rKey := img.Elem2.Data.Group.RKey.Unwrap()
url := fmt.Sprintf("https://%s%s%s&spec=0&rf=naio", domain, imgURL, rKey)
url := fmt.Sprintf("https://%s%s%s", domain, imgURL, rKey)
res = append(res, &GroupImageElement{
ImageId: img.Elem1.Meta.FilePath.Unwrap(),
Size: img.Elem1.Meta.Data.FileLen.Unwrap(),
Url: url,
Md5: img.Elem1.Meta.Data.PicMd5,
Md5: img.Elem1.Meta.Data.FileMd5,
Name: img.Elem1.Meta.Data.FileName.Unwrap(),
})
newImg = true
}
case 12, 22:
audio := &msg.PbMultiMediaElement{}
_ = proto.Unmarshal(elem.CommonElem.PbElem, audio)
ve := &VoiceElement{
Name: audio.Elem1.Meta.Data.FileName.Unwrap(),
Md5: audio.Elem1.Meta.Data.FileMd5,
Size: audio.Elem1.Meta.Data.FileLen.Unwrap(),
FileId: audio.Elem1.Meta.FilePath.Unwrap(),
IsGroup: false,
}
if bt == 22 {
ve.IsGroup = true
}
res = append(res, ve)
}
}
}
return res