Files
NapCatQQ/packages/napcat-core/data/webapi.ts
T
TanTan f954252a34
Build NapCat Artifacts / Type Check & Test (push) Waiting to run
Release Tag / validate-version (push) Waiting to run
NapCatQQ source snapshot
2026-11-13 09:01:47 +08:00

387 lines
8.8 KiB
TypeScript

export interface ControlReq {
appid?: string;
asy_upload?: number;
biz_req?: BizReq;
check_type?: number;
checksum?: string;
cmd?: string;
env?: Env;
file_len?: number;
model?: number;
session?: string;
token?: Token;
uin?: string;
}
export interface BizReq {
iAlbumTypeID: number;
iBatchID: number;
iBitmap: number;
iDistinctUse: number;
iNeedFeeds?: number;
iPicHight: number;
iPicWidth: number;
iUploadTime: number;
iUploadType: number;
iUpPicType: number;
iWaterType: number;
mapExt?: MapExt;
mutliPicInfo?: MutliPicInfo;
sAlbumID: string;
sAlbumName: string;
sPicDesc: string;
sPicPath: string;
sPicTitle: string;
stExtendInfo?: StExtendInfo;
// 视频主体上传专用
extend_info?: VideoExtendInfo;
iFlag?: number;
iIsFormatF20?: number;
iIsNew?: number;
iIsOriginalVideo?: number;
iPlayTime?: number;
sCoverUrl?: string;
sDesc?: string;
sTitle?: string;
// 视频封面二段上传专用
stExternalMapExt?: StExternalMapExt;
sExif_CameraMaker?: string;
sExif_CameraModel?: string;
sExif_Latitude?: string;
sExif_LatitudeRef?: string;
sExif_Longitude?: string;
sExif_LongitudeRef?: string;
sExif_Time?: string;
}
// 多图同批上传时描述「本批共几张 / 当前第几张」,相册里据此合并成同一条
export interface MutliPicInfo {
iBatUploadNum: string;
iCurUpload: string;
iSuccNum: number;
iFailNum: number;
}
// 视频主体上传用(视频不走 stExtendInfo.mapParams,而是这段扩展信息)
export interface VideoExtendInfo {
video_type: string;
domainid: string;
photo_num: string;
video_num: string;
batch_num: string;
qun_id: string;
}
// 视频封面二段上传用:声明这是客户端上传的封面,且与图片混合在同一条动态里
export interface StExternalMapExt {
is_client_upload_cover: string;
is_pic_video_mix_feeds: string;
}
export interface MapExt {
appid: string;
userid: string;
}
export interface StExtendInfo {
mapParams: MapParams;
}
export interface MapParams {
batch_num: string;
photo_num: string;
video_num: string;
// 视频封面二段上传时用它把封面挂到已上传的视频上
vid?: string;
}
export interface Env {
deviceInfo: string;
refer: string;
}
export interface Token {
appid: number;
data: string;
type: number;
}
// 一次上传的批次信息:同批的多张图必须共用 batchId,相册里才会显示成同一条。
export interface QunAlbumBatch {
batchId: number,
count: string,
index: string
}
let lastQunAlbumBatchId = 0;
// 批次号取秒级时间戳。同一秒里发起的两次上传要错开,不然会被相册并成同一条
export function nextQunAlbumBatchId () {
lastQunAlbumBatchId = Math.max(Math.floor(Date.now() / 1000), lastQunAlbumBatchId + 1);
return lastQunAlbumBatchId;
}
// control_req 的外层(鉴权、来源、会话),图片、视频、封面都一样
function buildQunAlbumControlReq (
{ uin, pskey, appid, checksum, check_type, file_len, refer = 'qzone', cmd }: {
uin: string,
pskey: string,
appid: string,
checksum: string,
check_type: number,
file_len: number,
refer?: string,
cmd?: string
},
biz_req: BizReq
): ControlReq {
const req: ControlReq = {
uin,
token: { type: 4, data: pskey, appid: 5 },
appid,
checksum,
check_type,
file_len,
env: { refer, deviceInfo: 'h5' },
model: 0,
biz_req,
session: '',
asy_upload: 0,
};
if (cmd) req.cmd = cmd;
return req;
}
// biz_req 里三种上传都要带的基础字段
function buildQunAlbumBizBase (
{ iUploadType, sAlbumName = '', sAlbumID = '', iBatchID = 0, sPicTitle = '' }: {
iUploadType: number,
sAlbumName?: string,
sAlbumID?: string,
iBatchID?: number,
sPicTitle?: string
}
): BizReq {
return {
sPicTitle,
sPicDesc: '',
sAlbumName,
sAlbumID,
iAlbumTypeID: 0,
iBitmap: 0,
iUploadType,
iUpPicType: 0,
iBatchID,
sPicPath: '',
iPicWidth: 0,
iPicHight: 0,
iWaterType: 0,
iDistinctUse: 0,
iUploadTime: Math.floor(Date.now() / 1000),
};
}
// 进相册的那一步(图片、视频封面)要带的群与批次信息
function buildQunAlbumFeedFields (group_id: string, mapParams: MapParams, batchIndex: string) {
return {
iNeedFeeds: 1,
mapExt: { appid: 'qun', userid: group_id },
stExtendInfo: { mapParams },
mutliPicInfo: {
iBatUploadNum: mapParams.batch_num,
iCurUpload: batchIndex,
iSuccNum: 0,
iFailNum: 0,
},
};
}
export function qunAlbumControl ({
uin,
group_id,
pskey,
pic_md5,
img_size,
img_name,
sAlbumName,
sAlbumID,
photo_num = '1',
video_num = '0',
batch_num = '1',
batchId = nextQunAlbumBatchId(),
batchIndex = '0',
}: {
uin: string,
group_id: string,
pskey: string,
pic_md5: string,
img_size: number,
img_name: string,
sAlbumName: string,
sAlbumID: string,
photo_num?: string,
video_num?: string,
batch_num?: string,
batchId?: number,
batchIndex?: string
}
): {
control_req: ControlReq[]
} {
return {
control_req: [buildQunAlbumControlReq(
{ uin, pskey, appid: 'qun', checksum: pic_md5, check_type: 0, file_len: img_size, cmd: 'FileUpload' },
{
...buildQunAlbumBizBase({ iUploadType: 0, sAlbumName, sAlbumID, iBatchID: batchId, sPicTitle: img_name }),
...buildQunAlbumFeedFields(group_id, { photo_num, video_num, batch_num }, batchIndex),
}
)],
};
}
export function createStreamUpload (
{
uin,
session,
offset,
seq,
end,
slice_size,
data,
}: { uin: string, session: string, offset: number, seq: number, end: number, slice_size: number, data: string }
) {
return {
uin,
appid: 'qun',
session,
offset, // 分片起始位置
data, // base64编码数据
checksum: '',
check_type: 0,
retry: 0, // 重试次数
seq, // 分片序号
end, // 分片结束位置 文件总大小
cmd: 'FileUpload',
slice_size, // 分片大小16KB 16384
biz_req: {
iUploadType: 3,
},
};
}
/**
* 视频主体上传的控制体。
*
* 视频这一步不进相册(相册留空,iBatchID 为 0),只是先把视频传成素材;
* 分片响应里拿到 sVid 后,再由封面上传(qunAlbumVideoCoverControl)把视频挂进相册。
*/
export function qunAlbumVideoControl ({
uin,
group_id,
pskey,
video_sha1,
video_size,
play_time = 0,
photo_num = '0',
video_num = '1',
batch_num = '1',
}: {
uin: string,
group_id: string,
pskey: string,
video_sha1: string,
video_size: number,
play_time?: number,
photo_num?: string,
video_num?: string,
batch_num?: string
}): {
control_req: ControlReq[]
} {
return {
control_req: [buildQunAlbumControlReq(
{ uin, pskey, appid: 'video_qun', checksum: video_sha1, check_type: 1, file_len: video_size, cmd: 'FileUploadVideo' },
{
...buildQunAlbumBizBase({ iUploadType: 3 }),
sTitle: '',
sDesc: '',
sCoverUrl: '',
iFlag: 0,
iPlayTime: play_time,
iIsNew: 111,
iIsOriginalVideo: 0,
iIsFormatF20: 0,
extend_info: {
video_type: '3',
domainid: '5',
photo_num,
video_num,
batch_num,
qun_id: group_id,
},
}
)],
};
}
/**
* 视频封面上传的控制体。
*
* 按图片上传,但 iUploadType 为 2,并用 mapParams.vid 指向已上传的视频。
* 相册和批次号只在这一步出现,视频是靠封面进的相册。
*/
export function qunAlbumVideoCoverControl ({
uin,
group_id,
pskey,
sAlbumName,
sAlbumID,
cover_md5,
cover_size,
vid,
batchId = nextQunAlbumBatchId(),
batchIndex = '0',
photo_num = '0',
video_num = '1',
batch_num = '1',
}: {
uin: string,
group_id: string,
pskey: string,
sAlbumName: string,
sAlbumID: string,
cover_md5: string,
cover_size: number,
vid: string,
batchId?: number,
batchIndex?: string,
photo_num?: string,
video_num?: string,
batch_num?: string
}): {
control_req: ControlReq[]
} {
return {
control_req: [buildQunAlbumControlReq(
{ uin, pskey, appid: 'qun', checksum: cover_md5, check_type: 0, file_len: cover_size, refer: 'huodong' },
{
...buildQunAlbumBizBase({ iUploadType: 2, sAlbumName, sAlbumID, iBatchID: batchId }),
...buildQunAlbumFeedFields(group_id, { vid, photo_num, video_num, batch_num }, batchIndex),
// 标明封面由客户端上传,且允许和图片出现在同一条动态里
stExternalMapExt: {
is_client_upload_cover: '1',
is_pic_video_mix_feeds: '1',
},
sExif_CameraMaker: '',
sExif_CameraModel: '',
sExif_Time: '',
sExif_LatitudeRef: '',
sExif_Latitude: '',
sExif_LongitudeRef: '',
sExif_Longitude: '',
}
)],
};
}