SnowCat 雪猫底层全源码:NapCat 4.18.28 底座 + SnowLuma 能力移植 + V8 通道适配 9.9.37

26 包 monorepo(TS 全源码):
- snowcat-core: NT 内核 API 封装 + listener(含 9.9.37 六回调适配)+ Packet 发包
- snowcat-onebot: OneBot v11 全动作 + MCP Streamable HTTP 适配器 + 稳定线
- snowcat-webui-frontend/backend: 雪猫控制台(React+HeroUI)
- snowcat-shell: 单文件 bundle 出口(vite inlineDynamicImports)
- snowcat-native: napi2native / MoeHoo / ffmpeg / pty 运行时
- 56 项 SnowLuma 独门动作移植(qzone 全家桶/群相册/装扮/闪照/收藏)
- 工具件: 解包器/启动器/便携制作器/doctor

GitHub 关联已全量指向 DandanLLab/SnowCatQQ
This commit is contained in:
天天的鸟蛋蛋 committed 2026-11-12 06:27:36 +08:00
commit b9655a5ab1
1048 files changed
+130851

No files matched your search

@@ -0,0 +1,29 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
class DownloadGroupFile extends PacketTransformer<typeof proto.OidbSvcTrpcTcp0x6D6Response> {
build (groupUin: number, fileUUID: string): OidbPacket {
const body = new NapProtoMsg(proto.OidbSvcTrpcTcp0x6D6).encode({
download: {
groupUin,
appId: 7,
busId: 102,
fileId: fileUUID,
},
});
return OidbBase.build(0x6D6, 2, body, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
const res = new NapProtoMsg(proto.OidbSvcTrpcTcp0x6D6Response).decode(oidbBody);
if (res.download.retCode !== 0) {
throw new Error(`sendGroupFileDownloadReq error: ${res.download.clientWording} (code=${res.download.retCode})`);
}
return res;
}
}
export default new DownloadGroupFile();
@@ -0,0 +1,46 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoEncodeStructType, NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import { IndexNode } from '@/snowcat-core/packet/transformer/proto';
class DownloadGroupImage extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (group_uin: number, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 1,
command: 200,
},
scene: {
requestType: 2,
businessType: 1,
sceneType: 2,
group: {
groupUin: group_uin,
},
},
client: {
agentType: 2,
},
},
download: {
node,
download: {
video: {
busiType: 0,
sceneType: 0,
},
},
},
});
return OidbBase.build(0x11C4, 200, body, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new DownloadGroupImage();
@@ -0,0 +1,45 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoEncodeStructType, NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
class DownloadGroupPtt extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (groupUin: number, node: NapProtoEncodeStructType<typeof proto.IndexNode>): OidbPacket {
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 4,
command: 200,
},
scene: {
requestType: 1,
businessType: 3,
sceneType: 2,
group: {
groupUin,
},
},
client: {
agentType: 2,
},
},
download: {
node,
download: {
video: {
busiType: 0,
sceneType: 0,
},
},
},
});
return OidbBase.build(0x126E, 200, body, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new DownloadGroupPtt();
@@ -0,0 +1,46 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoEncodeStructType, NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import { IndexNode } from '@/snowcat-core/packet/transformer/proto';
class DownloadGroupVideo extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 1,
command: 200,
},
scene: {
requestType: 2,
businessType: 2,
sceneType: 2,
group: {
groupUin,
},
},
client: {
agentType: 2,
},
},
download: {
node,
download: {
video: {
busiType: 0,
sceneType: 0,
},
},
},
});
return OidbBase.build(0x11EA, 200, body, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new DownloadGroupVideo();
@@ -0,0 +1,47 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoEncodeStructType, NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import { IndexNode } from '@/snowcat-core/packet/transformer/proto';
class DownloadImage extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 1,
command: 200,
},
scene: {
requestType: 2,
businessType: 1,
sceneType: 1,
c2C: {
accountType: 2,
targetUid: selfUid,
},
},
client: {
agentType: 2,
},
},
download: {
node,
download: {
video: {
busiType: 0,
sceneType: 0,
},
},
},
});
return OidbBase.build(0x11C5, 200, body, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new DownloadImage();
@@ -0,0 +1,31 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
class DownloadOfflineFile extends PacketTransformer<typeof proto.OidbSvcTrpcTcp0XE37Response> {
build (fileUUID: string, fileHash: string, senderUid: string, receiverUid: string): OidbPacket {
const body = new NapProtoMsg(proto.OidbSvcTrpcTcp0XE37_800).encode({
subCommand: 800,
field2: 0,
body: {
senderUid,
receiverUid,
fileUuid: fileUUID,
fileHash,
},
field101: 3,
field102: 1,
field200: 1,
});
return OidbBase.build(0xE37, 800, body, false, false);
}
// TODO:check
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.OidbSvcTrpcTcp0XE37Response).decode(oidbBody);
}
}
export default new DownloadOfflineFile();
@@ -0,0 +1,32 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
class DownloadPrivateFile extends PacketTransformer<typeof proto.OidbSvcTrpcTcp0XE37_1200Response> {
build (selfUid: string, fileUUID: string, fileHash: string): OidbPacket {
const body = new NapProtoMsg(proto.OidbSvcTrpcTcp0XE37_1200).encode({
subCommand: 1200,
field2: 1,
body: {
receiverUid: selfUid,
fileUuid: fileUUID,
type: 2,
fileHash,
t2: 0,
},
field101: 3,
field102: 103,
field200: 1,
field99999: Buffer.from([0xc0, 0x85, 0x2c, 0x01]),
});
return OidbBase.build(0xE37, 1200, body, false, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.OidbSvcTrpcTcp0XE37_1200Response).decode(oidbBody);
}
}
export default new DownloadPrivateFile();
@@ -0,0 +1,47 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoEncodeStructType, NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import { IndexNode } from '@/snowcat-core/packet/transformer/proto';
class DownloadPtt extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 1,
command: 200,
},
scene: {
requestType: 1,
businessType: 3,
sceneType: 1,
c2C: {
accountType: 2,
targetUid: selfUid,
},
},
client: {
agentType: 2,
},
},
download: {
node,
download: {
video: {
busiType: 0,
sceneType: 0,
},
},
},
});
return OidbBase.build(0x126D, 200, body, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new DownloadPtt();
@@ -0,0 +1,47 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoEncodeStructType, NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import { IndexNode } from '@/snowcat-core/packet/transformer/proto';
class DownloadVideo extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 1,
command: 200,
},
scene: {
requestType: 2,
businessType: 2,
sceneType: 1,
c2C: {
accountType: 2,
targetUid: selfUid,
},
},
client: {
agentType: 2,
},
},
download: {
node,
download: {
video: {
busiType: 0,
sceneType: 0,
},
},
},
});
return OidbBase.build(0x11E9, 200, body, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new DownloadVideo();
@@ -0,0 +1,33 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketBufBuilder, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
class FetchSessionKey extends PacketTransformer<typeof proto.HttpConn0x6ff_501Response> {
build (): OidbPacket {
const req = new NapProtoMsg(proto.HttpConn0x6ff_501).encode({
httpConn: {
field1: 0,
field2: 0,
field3: 16,
field4: 1,
field6: 3,
serviceTypes: [1, 5, 10, 21],
// tgt: "", // TODO: do we really need tgt? seems not
field9: 2,
field10: 9,
field11: 8,
ver: '1.0.1',
},
});
return {
cmd: 'HttpConn.0x6ff_501',
data: PacketBufBuilder(req),
};
}
parse (data: Buffer) {
return new NapProtoMsg(proto.HttpConn0x6ff_501Response).decode(data);
}
}
export default new FetchSessionKey();
@@ -0,0 +1,34 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import { PacketMsgFileElement } from '@/snowcat-core/packet/message/element';
class UploadGroupFile extends PacketTransformer<typeof proto.OidbSvcTrpcTcp0x6D6Response> {
build (groupUin: number, file: PacketMsgFileElement): OidbPacket {
const body = new NapProtoMsg(proto.OidbSvcTrpcTcp0x6D6).encode({
file: {
groupUin,
appId: 4,
busId: 102,
entrance: 6,
targetDirectory: '/', // TODO:
fileName: file.fileName,
localDirectory: `/${file.fileName}`,
fileSize: BigInt(file.fileSize),
fileMd5: file.fileMd5,
fileSha1: file.fileSha1,
fileSha3: Buffer.alloc(0),
field15: true,
},
});
return OidbBase.build(0x6D6, 0, body, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.OidbSvcTrpcTcp0x6D6Response).decode(oidbBody);
}
}
export default new UploadGroupFile();
@@ -0,0 +1,86 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import crypto from 'node:crypto';
import { PacketMsgPicElement } from '@/snowcat-core/packet/message/element';
class UploadGroupImage extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (groupUin: number, img: PacketMsgPicElement): OidbPacket {
const data = new NapProtoMsg(proto.NTV2RichMediaReq).encode(
{
reqHead: {
common: {
requestId: 1,
command: 100,
},
scene: {
requestType: 2,
businessType: 1,
sceneType: 2,
group: {
groupUin,
},
},
client: {
agentType: 2,
},
},
upload: {
uploadInfo: [
{
fileInfo: {
fileSize: +img.size,
fileHash: img.md5,
fileSha1: img.sha1!,
fileName: img.name,
type: {
type: 1,
picFormat: img.picType, // TODO: extend NapCat imgType /cc @MliKiowa
videoFormat: 0,
voiceFormat: 0,
},
width: img.width,
height: img.height,
time: 0,
original: 1,
},
subFileType: 0,
},
],
tryFastUploadCompleted: true,
srvSendMsg: false,
clientRandomId: crypto.randomBytes(8).readBigUInt64BE() & BigInt('0x7FFFFFFFFFFFFFFF'),
compatQMsgSceneType: 2,
extBizInfo: {
pic: {
bizType: img.picSubType,
bytesPbReserveTroop: {
subType: img.picSubType,
},
textSummary: img.summary,
},
video: {
bytesPbReserve: Buffer.alloc(0),
},
ptt: {
bytesPbReserve: Buffer.alloc(0),
bytesReserve: Buffer.alloc(0),
bytesGeneralFlags: Buffer.alloc(0),
},
},
clientSeq: 0,
noNeedCompatMsg: false,
},
}
);
return OidbBase.build(0x11C4, 100, data, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new UploadGroupImage();
@@ -0,0 +1,82 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import crypto from 'node:crypto';
import { PacketMsgPttElement } from '@/snowcat-core/packet/message/element';
class UploadGroupPtt extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (groupUin: number, ptt: PacketMsgPttElement): OidbPacket {
const data = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 1,
command: 100,
},
scene: {
requestType: 2,
businessType: 3,
sceneType: 2,
group: {
groupUin,
},
},
client: {
agentType: 2,
},
},
upload: {
uploadInfo: [
{
fileInfo: {
fileSize: ptt.fileSize,
fileHash: ptt.fileMd5,
fileSha1: ptt.fileSha1,
fileName: `${ptt.fileMd5}.amr`,
type: {
type: 3,
picFormat: 0,
videoFormat: 0,
voiceFormat: 1,
},
height: 0,
width: 0,
time: ptt.fileDuration,
original: 0,
},
subFileType: 0,
},
],
tryFastUploadCompleted: true,
srvSendMsg: false,
clientRandomId: crypto.randomBytes(8).readBigUInt64BE() & BigInt('0x7FFFFFFFFFFFFFFF'),
compatQMsgSceneType: 2,
extBizInfo: {
pic: {
textSummary: 'Nya~',
},
video: {
bytesPbReserve: Buffer.alloc(0),
},
ptt: {
bytesPbReserve: Buffer.alloc(0),
bytesReserve: Buffer.from([0x08, 0x00, 0x38, 0x00]),
bytesGeneralFlags: Buffer.from([0x9a, 0x01, 0x07, 0xaa, 0x03, 0x04, 0x08, 0x08, 0x12, 0x00]),
},
},
clientSeq: 0,
noNeedCompatMsg: false,
},
});
return OidbBase.build(0x126E, 100, data, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
const instance = new UploadGroupPtt();
export default instance;
@@ -0,0 +1,100 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import crypto from 'node:crypto';
import { PacketMsgVideoElement } from '@/snowcat-core/packet/message/element';
class UploadGroupVideo extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (groupUin: number, video: PacketMsgVideoElement): OidbPacket {
if (!video.fileSize || !video.thumbSize) throw new Error('video.fileSize or video.thumbSize is empty');
const data = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 3,
command: 100,
},
scene: {
requestType: 2,
businessType: 2,
sceneType: 2,
group: {
groupUin,
},
},
client: {
agentType: 2,
},
},
upload: {
uploadInfo: [
{
fileInfo: {
fileSize: +video.fileSize,
fileHash: video.fileMd5,
fileSha1: video.fileSha1,
fileName: video.fileName || 'video.mp4',
type: {
type: 2,
picFormat: 0,
videoFormat: video.fileFormat,
voiceFormat: 0,
},
height: video.fileHeight ?? 0,
width: video.fileWidth ?? 0,
time: video.fileTime,
original: 0,
},
subFileType: 0,
}, {
fileInfo: {
fileSize: +video.thumbSize,
fileHash: video.thumbMd5,
fileSha1: video.thumbSha1,
fileName: `${video.thumbMd5 || 'video-thumb'}.jpg`,
type: {
type: 1,
picFormat: 0,
videoFormat: 0,
voiceFormat: 0,
},
height: video.thumbHeight,
width: video.thumbWidth,
time: 0,
original: 0,
},
subFileType: 100,
},
],
tryFastUploadCompleted: true,
srvSendMsg: false,
clientRandomId: crypto.randomBytes(8).readBigUInt64BE() & BigInt('0x7FFFFFFFFFFFFFFF'),
compatQMsgSceneType: 2,
extBizInfo: {
pic: {
bizType: 0,
textSummary: 'Nya~',
},
video: {
bytesPbReserve: Buffer.from([0x80, 0x01, 0x00]),
},
ptt: {
bytesPbReserve: Buffer.alloc(0),
bytesReserve: Buffer.alloc(0),
bytesGeneralFlags: Buffer.alloc(0),
},
},
clientSeq: 0,
noNeedCompatMsg: false,
},
});
return OidbBase.build(0x11EA, 100, data, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new UploadGroupVideo();
@@ -0,0 +1,37 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import { PacketMsgFileElement } from '@/snowcat-core/packet/message/element';
import { computeMd5AndLengthWithLimit } from '@/snowcat-core/packet/utils/crypto/hash';
class UploadPrivateFile extends PacketTransformer<typeof proto.OidbSvcTrpcTcp0XE37Response> {
async build (selfUid: string, peerUid: string, file: PacketMsgFileElement): Promise<OidbPacket> {
const body = new NapProtoMsg(proto.OidbSvcTrpcTcp0XE37_1700).encode({
command: 1700,
seq: 0,
upload: {
senderUid: selfUid,
receiverUid: peerUid,
fileSize: file.fileSize,
fileName: file.fileName,
md510MCheckSum: await computeMd5AndLengthWithLimit(file.filePath, 10 * 1024 * 1024),
sha1CheckSum: file.fileSha1,
localPath: '/',
md5CheckSum: file.fileMd5,
sha3CheckSum: Buffer.alloc(0),
},
businessId: 3,
clientType: 1,
flagSupportMediaPlatform: 1,
});
return OidbBase.build(0xE37, 1700, body, false, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.OidbSvcTrpcTcp0XE37Response).decode(oidbBody);
}
}
export default new UploadPrivateFile();
@@ -0,0 +1,86 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import crypto from 'node:crypto';
import { PacketMsgPicElement } from '@/snowcat-core/packet/message/element';
class UploadPrivateImage extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (peerUin: string, img: PacketMsgPicElement): OidbPacket {
const data = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 1,
command: 100,
},
scene: {
requestType: 2,
businessType: 1,
sceneType: 1,
c2C: {
accountType: 2,
targetUid: peerUin,
},
},
client: {
agentType: 2,
},
},
upload: {
uploadInfo: [
{
fileInfo: {
fileSize: +img.size,
fileHash: img.md5,
fileSha1: img.sha1!,
fileName: img.name,
type: {
type: 1,
picFormat: img.picType, // TODO: extend NapCat imgType /cc @MliKiowa
videoFormat: 0,
voiceFormat: 0,
},
width: img.width,
height: img.height,
time: 0,
original: 1,
},
subFileType: 0,
},
],
tryFastUploadCompleted: true,
srvSendMsg: false,
clientRandomId: crypto.randomBytes(8).readBigUInt64BE() & BigInt('0x7FFFFFFFFFFFFFFF'),
compatQMsgSceneType: 1,
extBizInfo: {
pic: {
bizType: img.picSubType,
bytesPbReserveC2C: {
subType: img.picSubType,
},
textSummary: img.summary,
},
video: {
bytesPbReserve: Buffer.alloc(0),
},
ptt: {
bytesPbReserve: Buffer.alloc(0),
bytesReserve: Buffer.alloc(0),
bytesGeneralFlags: Buffer.alloc(0),
},
},
clientSeq: 0,
noNeedCompatMsg: false,
},
}
);
return OidbBase.build(0x11C5, 100, data, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new UploadPrivateImage();
@@ -0,0 +1,77 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import crypto from 'node:crypto';
import { PacketMsgPttElement } from '@/snowcat-core/packet/message/element';
class UploadPrivatePtt extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (peerUin: string, ptt: PacketMsgPttElement): OidbPacket {
const data = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 4,
command: 100,
},
scene: {
requestType: 2,
businessType: 3,
sceneType: 1,
c2C: {
accountType: 2,
targetUid: peerUin,
},
},
client: {
agentType: 2,
},
},
upload: {
uploadInfo: [
{
fileInfo: {
fileSize: ptt.fileSize,
fileHash: ptt.fileMd5,
fileSha1: ptt.fileSha1,
fileName: `${ptt.fileMd5}.amr`,
type: {
type: 3,
picFormat: 0,
videoFormat: 0,
voiceFormat: 1,
},
height: 0,
width: 0,
time: ptt.fileDuration,
original: 0,
},
subFileType: 0,
},
],
tryFastUploadCompleted: true,
srvSendMsg: false,
clientRandomId: crypto.randomBytes(8).readBigUInt64BE() & BigInt('0x7FFFFFFFFFFFFFFF'),
compatQMsgSceneType: 1,
extBizInfo: {
pic: {
textSummary: 'Nya~',
},
ptt: {
bytesReserve: Buffer.from([0x08, 0x00, 0x38, 0x00]),
bytesGeneralFlags: Buffer.from([0x9a, 0x01, 0x0b, 0xaa, 0x03, 0x08, 0x08, 0x04, 0x12, 0x04, 0x00, 0x00, 0x00, 0x00]),
},
},
clientSeq: 0,
noNeedCompatMsg: false,
},
});
return OidbBase.build(0x126D, 100, data, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new UploadPrivatePtt();
@@ -0,0 +1,101 @@
import * as proto from '@/snowcat-core/packet/transformer/proto';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbPacket, PacketTransformer } from '@/snowcat-core/packet/transformer/base';
import OidbBase from '@/snowcat-core/packet/transformer/oidb/oidbBase';
import crypto from 'node:crypto';
import { PacketMsgVideoElement } from '@/snowcat-core/packet/message/element';
class UploadPrivateVideo extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
build (peerUin: string, video: PacketMsgVideoElement): OidbPacket {
if (!video.fileSize || !video.thumbSize) throw new Error('video.fileSize or video.thumbSize is empty');
const data = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 3,
command: 100,
},
scene: {
requestType: 2,
businessType: 2,
sceneType: 1,
c2C: {
accountType: 2,
targetUid: peerUin,
},
},
client: {
agentType: 2,
},
},
upload: {
uploadInfo: [
{
fileInfo: {
fileSize: +video.fileSize,
fileHash: video.fileMd5,
fileSha1: video.fileSha1,
fileName: video.fileName || 'video.mp4',
type: {
type: 2,
picFormat: 0,
videoFormat: video.fileFormat,
voiceFormat: 0,
},
height: video.fileHeight ?? 0,
width: video.fileWidth ?? 0,
time: video.fileTime,
original: 0,
},
subFileType: 0,
}, {
fileInfo: {
fileSize: +video.thumbSize,
fileHash: video.thumbMd5,
fileSha1: video.thumbSha1,
fileName: `${video.thumbMd5 || 'video-thumb'}.jpg`,
type: {
type: 1,
picFormat: 0,
videoFormat: 0,
voiceFormat: 0,
},
height: video.thumbHeight,
width: video.thumbWidth,
time: 0,
original: 0,
},
subFileType: 100,
},
],
tryFastUploadCompleted: true,
srvSendMsg: false,
clientRandomId: crypto.randomBytes(8).readBigUInt64BE() & BigInt('0x7FFFFFFFFFFFFFFF'),
compatQMsgSceneType: 2,
extBizInfo: {
pic: {
bizType: 0,
textSummary: 'Nya~',
},
video: {
bytesPbReserve: Buffer.from([0x80, 0x01, 0x00]),
},
ptt: {
bytesPbReserve: Buffer.alloc(0),
bytesReserve: Buffer.alloc(0),
bytesGeneralFlags: Buffer.alloc(0),
},
},
clientSeq: 0,
noNeedCompatMsg: false,
},
});
return OidbBase.build(0x11E9, 100, data, true, false);
}
parse (data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}
export default new UploadPrivateVideo();
@@ -0,0 +1,18 @@
export { default as DownloadGroupFile } from './DownloadGroupFile';
export { default as DownloadGroupPtt } from './DownloadGroupPtt';
export { default as DownloadOfflineFile } from './DownloadOfflineFile';
export { default as DownloadPrivateFile } from './DownloadPrivateFile';
export { default as FetchSessionKey } from './FetchSessionKey';
export { default as UploadGroupFile } from './UploadGroupFile';
export { default as UploadGroupImage } from './UploadGroupImage';
export { default as UploadGroupPtt } from './UploadGroupPtt';
export { default as UploadGroupVideo } from './UploadGroupVideo';
export { default as UploadPrivateFile } from './UploadPrivateFile';
export { default as UploadPrivateImage } from './UploadPrivateImage';
export { default as UploadPrivatePtt } from './UploadPrivatePtt';
export { default as UploadPrivateVideo } from './UploadPrivateVideo';
export { default as DownloadImage } from './DownloadImage';
export { default as DownloadGroupImage } from './DownloadGroupImage';
export { default as DownloadVideo } from './DownloadVideo';
export { default as DownloadGroupVideo } from './DownloadGroupVideo';
export { default as DownloadPtt } from './DownloadPtt';