SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run

This commit is contained in:
TanTan committed 2026-11-13 09:02:00 +08:00
commit fa9c73363d
1367 files changed
+355735

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -0,0 +1,77 @@
// Recommend-contact ARK card protobufs, RE'd from QQNT wrapper.linux.node.
//
// Buddy: OidbSvcTrpcTcp.0x12b6_0 (getBuddyRecommendContactArkJson) —
// request writes {1:uin, 2:phone, 3:jump_url}; response reads {1:ark}.
// Group: OidbSvcTrpcTcp.0x8b7_5 (getGroupRecommendContactArkJson) — encoder
// `group_get_ark_json_worker.cc::EncodeRequest` writes {1:reqType=1,
// 2:groupCode,5:flag=1}; response (group_info_mgr.cc `[gp_get_ark_json]`)
// reads {1:bussness_error_code, 5:ark_json}.
// Both return a server-built ark JSON string (the share card payload).
import type { pb, pb_optional, pb_repeated, uint_32 } from '@snowluma/proton';
export interface OidbBuddyRecommendArkReq {
uin?: pb<1, uint_32>;
phoneNumber?: pb<2, string>;
jumpUrl?: pb<3, string>;
}
export interface OidbBuddyRecommendArkResp {
ark?: pb<1, string>;
}
export interface OidbGroupRecommendArkReq {
reqType?: pb<1, uint_32>;
groupCode?: pb<2, uint_32>;
flag?: pb<5, uint_32>;
}
export interface OidbGroupRecommendArkResp {
errCode?: pb<1, uint_32>;
arkJson?: pb<5, string>;
}
// 0xdc2_34 — send a custom 图文 (URL-share) ark card to a C2C peer or group.
// Field layout RE'd from QQ Android 9.3.25 captures. Default SSO: OidbSvcTrpcTcp.0xdc2_34.
// appId = 100446242 is fixed for this ark type.
// targetId (UIN or group_id) appears at AppInfo[11] AND Meta[2].
// Meta.peerType: 0 = C2C, 1 = group.
// peerType, field3, and previewUrl are pb_optional — proton preserves 0/empty on wire.
export interface Oidb0xdc2CardField5 {
field1?: pb<1, uint_32>;
}
export interface Oidb0xdc2CardContent {
flag?: pb<1, uint_32>;
title?: pb<10, string>;
desc?: pb<11, string>;
summary?: pb<12, string>;
jumpUrl?: pb<13, string>;
previewUrl?: pb_optional<14, string>;
}
export interface Oidb0xdc2AppInfo {
appId?: pb<1, uint_32>;
field2?: pb<2, uint_32>;
field3?: pb_optional<3, uint_32>;
field5?: pb<5, Oidb0xdc2CardField5>;
targetId?: pb<11, uint_32>;
content?: pb<12, Oidb0xdc2CardContent>;
}
export interface Oidb0xdc2Meta {
peerType?: pb_optional<1, uint_32>;
targetId?: pb<2, uint_32>;
}
export interface Oidb0xdc2_34Req {
appInfo?: pb<1, Oidb0xdc2AppInfo>;
meta?: pb<2, Oidb0xdc2Meta>;
}
export interface Oidb0xdc2CardResult {
errorCode?: pb<4, uint_32>;
errorMessage?: pb<5, string>;
}
export interface Oidb0xdc2_34Resp {
results?: pb_repeated<1, Oidb0xdc2CardResult>;
}
@@ -0,0 +1,50 @@
// Filtered friend requests: listing, approval, and rejection have distinct
// request contracts. List responses can carry an account number or a UID.
import type { pb, pb_optional, pb_repeated, uint_32, uint_64 } from '@snowluma/proton';
export interface OidbDoubtGetReqInner {
num?: pb<1, uint_32>;
uk?: pb<2, string>;
}
export interface OidbDoubtGetReq {
field1?: pb<1, uint_32>;
inner?: pb<2, OidbDoubtGetReqInner>;
}
export interface OidbDoubtItem {
uid?: pb<1, string>;
nick?: pb<2, string>;
age?: pb<3, uint_32>;
sex?: pb<4, uint_32>;
msg?: pb<5, string>;
source?: pb<6, string>;
reason?: pb<7, string>;
reqTime?: pb<8, uint_64>;
groupCode?: pb<9, uint_64>;
commFriendNum?: pb<10, uint_32>;
}
export interface OidbDoubtGetRespBody {
list?: pb_repeated<1, OidbDoubtItem>;
reason?: pb<2, string>;
}
export interface OidbDoubtGetResp {
status?: pb<1, uint_32>;
body?: pb<2, OidbDoubtGetRespBody>;
}
export interface OidbDoubtApprovalReq {
selfUid?: pb<1, string>;
targetUid?: pb<2, string>;
field3?: pb_optional<3, uint_32>;
field4?: pb_optional<4, string>;
}
// Reject an existing filtered friend request.
export interface OidbDoubtDelReqInner {
uid?: pb<1, string>;
}
export interface OidbDoubtDelReq {
field1?: pb<1, uint_32>;
inner?: pb<3, OidbDoubtDelReqInner>;
}
@@ -0,0 +1,551 @@
// OIDB 0x93cf-0x9427, 0x12a9 — QQ 闪传(FlashTransfer / fileset)协议类型。
// 响应成功靠 OIDB envelope 的 errorCode(f3=0),业务体无 retCode。
import type { pb, pb_optional, pb_repeated, uint_32, uint_64, bytes } from '@snowluma/proton';
// ─────────────── 共用:文件条目(0x93d2/0x93d3 响应里的每个文件/fileset) ───────────────
/** f9.f2 — 下载信息(f1=类型,f2=下载URL,URL 里已含 rkey 或 fldc)。 */
export interface FlashFileDownloadInfo {
field1?: pb<1, uint_32>;
downloadUrl?: pb<2, string>;
}
/** f9 — fileId + 下载信息。 */
export interface FlashFileIdWrap {
fileId?: pb<1, string>;
download?: pb<2, FlashFileDownloadInfo>;
}
/** f8 — 上传/分享 URL(qfile.qq.com/q/<code>)。 */
export interface FlashFileUploadUrl {
uploadUrl?: pb<1, string>;
}
/** 文件条目。0x93d3 响应 f1(单条);0x93d2 响应 f1(repeated,多个 fileset)。 */
export interface FlashFileEntry {
filesetUuid?: pb<1, string>;
fileName?: pb<2, string>;
origName?: pb<3, string>;
fileType?: pb<4, uint_32>;
fileSize?: pb<5, uint_64>;
uploadUrlWrap?: pb<8, FlashFileUploadUrl>;
fileIdWrap?: pb<9, FlashFileIdWrap>;
}
// ─────────────── 0x93d3 — 拉取文件集详情(点分享显示链接时触发) ───────────────
export interface FlashGetDetailReq {
filesetUuid?: pb<1, string>;
field2?: pb<2, uint_32>; // 7
}
export interface FlashGetDetailResp {
entries?: pb_repeated<1, FlashFileEntry>;
}
// ─────────────── 0x93d2 — 查询 fileset 列表 ───────────────
export interface FlashListFilesetsReq {
field1?: pb<1, uint_32>; // 3
field2?: pb<2, string>; // ""
field3?: pb<3, uint_32>; // 10
}
export interface FlashListFilesetsResp {
entries?: pb_repeated<1, FlashFileEntry>;
}
// ─────────────── 0x93d4 — 拉取下载URL(完整参数 + fldc) ───────────────
export interface FlashGetDownloadUrlReqInner {
field1?: pb<1, string>;
field2?: pb<2, uint_32>;
field3?: pb<3, uint_32>;
field4?: pb<4, string>;
field5?: pb<5, FlashGetDownloadUrlReqInner5>;
field6?: pb<6, FlashGetDownloadUrlReqInner6>;
}
export interface FlashGetDownloadUrlReqInner5 {
field1?: pb<1, uint_32>;
}
export interface FlashGetDownloadUrlReqInner6 {
field1?: pb<1, uint_32>;
field2?: pb<2, uint_32>;
}
export interface FlashGetDownloadUrlReq {
filesetUuid?: pb<1, string>;
inner?: pb<2, FlashGetDownloadUrlReqInner>;
field3?: pb<3, uint_32>; // 7
field4?: pb<4, uint_32>; // 1
}
/** 0x93d4 响应 f1.f3.f13.f2 — downloadUrl 包装(结构不同于 0x93d3 的 FlashFileDownloadInfo)。 */
export interface FlashDownloadUrlWrap {
field1?: pb<1, uint_32>; // 2
downloadUrl?: pb<2, string>;
}
/** 0x93d4 响应 f1.f3.f13 — fileId + downloadUrl + sha1/md5/宽高。 */
export interface FlashDownloadUrlInfo {
fileId?: pb<1, string>;
download?: pb<2, FlashDownloadUrlWrap>;
sha1?: pb<3, string>;
field4?: pb<4, uint_32>;
md5?: pb<5, string>;
width?: pb<6, uint_32>;
height?: pb<7, uint_32>;
}
/** 0x93d4 响应 f1.f3.f14 — 主文件 fileId(无 downloadUrl,下载 URL 需 0x12a9 sub=200)。 */
export interface FlashDownloadMainFile {
fileId?: pb<1, string>;
field3?: pb<3, uint_32>; // 2
}
/** 0x93d4 响应 f1.f3 — 文件信息(字段号不同于 0x93d3 的 FlashFileEntry)。 */
export interface FlashDownloadFileInfo {
filesetUuid?: pb<1, string>;
fileUuid?: pb<2, string>;
field5?: pb<5, uint_32>;
field6?: pb<6, uint_32>;
field7?: pb<7, uint_32>;
fileName?: pb<8, string>;
origName?: pb<9, string>;
fileSize?: pb<11, uint_64>;
downloadInfo?: pb<13, FlashDownloadUrlInfo>;
mainFile?: pb<14, FlashDownloadMainFile>;
}
/** 0x93d4 响应 f1 — entry wrapper。多文件 fileset 时 f3 含每个文件的 fileInfo。 */
export interface FlashDownloadEntry {
field2?: pb<2, uint_32>; // 1
/** repeated:多文件 fileset 时每个文件一个 fileInfo(f6=文件序号,f14=主文件 fileId)。 */
fileInfo?: pb_repeated<3, FlashDownloadFileInfo>;
field4?: pb<4, string>; // JSON:{"offset":N,"cursor_time":"..."}
field5?: pb<5, uint_32>; // 1
}
/** 0x93d4 响应 — downloadUrl 在 f1.f3.f13.f2.f2(深层嵌套,非 0x93d3 的 f9.fileIdWrap)。 */
export interface FlashGetDownloadUrlResp {
entry?: pb<1, FlashDownloadEntry>;
}
// ─────────────── 0x9407 — 删除闪传文件(额外,OneBot 标准未定义) ───────────────
export interface FlashDeleteFileReq {
filesetUuid?: pb<1, string>;
field2?: pb<2, string>; // ""
field3?: pb<3, uint_32>; // 7
}
export interface FlashDeleteFileResp {}
// ─────────────── 0x9427 — 重命名闪传文件(额外,OneBot 标准未定义) ───────────────
export interface FlashRenameFileReqName {
newName?: pb<1, string>;
displayName?: pb<2, string>;
}
export interface FlashRenameFileReqFlag {
field1?: pb<1, uint_32>;
}
export interface FlashRenameFileReq {
filesetUuid?: pb<1, string>;
name?: pb<2, FlashRenameFileReqName>;
flag?: pb<3, FlashRenameFileReqFlag>;
}
export interface FlashRenameFileResp {}
// ─────────────── 0x93cf — 申请创建 fileSet(上传起点) ───────────────
/** 空占位 message(Uploader.f4 / CommitInfo.f4 / f24 为空 message)。 */
export interface FlashEmpty {}
export interface FlashUploader {
uin?: pb<1, string>;
nickname?: pb<2, string>;
uid?: pb<3, string>;
field4?: pb<4, FlashEmpty>;
}
/** 0x93cf 请求 f2 — 文件信息。 */
export interface FlashUploadFileInfo {
fileName?: pb<2, string>;
origName?: pb<3, string>;
fileType?: pb<4, uint_32>;
fileSize?: pb<5, uint_64>;
uploader?: pb<10, FlashUploader>;
field16?: pb<16, uint_32>;
field20?: pb<20, uint_32>;
field21?: pb<21, uint_32>;
}
export interface FlashApplyFilesetReq {
field1?: pb<1, uint_32>; // 1
fileInfo?: pb<2, FlashUploadFileInfo>;
/** 文件类型码:rar=2, png/mp4=7(按扩展名映射,FlashTransferApi.fileTypeCode)。 */
typeCode?: pb<3, uint_32>;
field12?: pb<12, uint_32>; // 1
}
export interface FlashApplyFilesetResp {
filesetUuid?: pb<1, string>;
/** 上传会话 key(与 f1 同值)。 */
uploadKey?: pb<2, string>;
/** 上传 URL https://qfile.qq.com/q/<code>。 */
uploadUrl?: pb<3, string>;
expire?: pb<4, uint_64>;
ttl?: pb<5, uint_32>;
}
// ─────────────── 0x93d0 — 单文件上传完成上报(commit) ───────────────
export interface FlashCommitFileInfo {
filesetUuid?: pb<1, string>;
fileUuid?: pb<2, string>;
/** pb_optional 强制 0 值上 wire(服务端多文件严格校验字段存在,省略 0 会导致文件不计入)。 */
field3?: pb_optional<3, uint_32>;
field4?: pb<4, FlashEmpty>;
field5?: pb_optional<5, uint_32>;
/** 文件序号:fileset 内从 1 递增(1,2,3...),多文件时每条各不相同。
* 必须与 0x12a9 sub=100/103 filesetWrap.f4 一致,否则服务端不把文件计入 fileset。 */
field6?: pb_optional<6, uint_32>;
/** 格式码:rar=4, mp4=2, png=26(按扩展名映射)。 */
formatCode?: pb_optional<7, uint_32>;
fileName?: pb<8, string>;
origName?: pb<9, string>;
field10?: pb_optional<10, uint_32>;
fileSize?: pb<11, uint_64>;
field12?: pb_optional<12, uint_32>;
field24?: pb<24, FlashEmpty>;
}
export interface FlashCommitFileReq {
field1?: pb<1, uint_32>; // 1
filesetUuid?: pb<2, string>;
uploadKey?: pb<3, string>; // 同 filesetUuid
/** repeated:一个 0x93d0 请求同时携带 fileset 内所有文件条目(多文件机制)。
* 每条 field6=文件序号(1,2,3...)。单文件时只有一个条目。 */
commitInfo?: pb_repeated<4, FlashCommitFileInfo>;
field5?: pb<5, uint_32>; // 1
field6?: pb<6, uint_32>; // 1
}
export interface FlashCommitFileResp {
field1?: pb<1, uint_32>;
filesetUuid?: pb<2, string>;
uploadKey?: pb<3, string>;
}
// ─────────────── 0x93db — fileSet 完成 ───────────────
export interface FlashCompleteFilesetReq {
filesetUuid?: pb<1, string>;
field2?: pb<2, string>; // ""
}
export interface FlashCompleteFilesetResp {}
// ─────────────── 0x93d1 — 设置 fileSet 状态 ───────────────
export interface FlashSetStatusReq {
filesetUuid?: pb<1, string>;
status?: pb<2, uint_32>; // 6
}
export interface FlashSetStatusResp {}
// ─────────────── sliceupload(大文件分片上传) ───────────────
/** sliceupload f107.f6 — 累积 SHA1 state list。每片对应从文件开头到该片末尾的
* SHA1 中间 state(不 finalize,小端 20B),最后一片是标准整文件 SHA1。
* 服务端按此校验分片完整性,传独立分片 SHA1 会被拒(报 "file range data sha1 not match")。 */
export interface FlashSha1StateV {
state?: pb_repeated<1, bytes>;
}
/** sliceupload body f107 — 切片 payload。 */
export interface FlashSlicePayload {
field1?: pb<1, FlashEmpty>;
rkey?: pb<2, string>; // 上传凭证(来自 sub=100 响应)
start?: pb_optional<3, uint_32>; // 本片字节 offset
end?: pb_optional<4, uint_32>; // start + chunkLen - 1
sha1?: pb<5, bytes>; // 当前片 SHA1
sha1StateV?: pb<6, FlashSha1StateV>;
chunk?: pb<7, bytes>; // 本片文件字节
}
/** sliceupload HTTP body — POST multimedia.qfile.qq.com/sliceupload。 */
export interface FlashSliceUploadBody {
field1?: pb_optional<1, uint_32>; // 0
appid?: pb<2, uint_32>; // 14901(与 fileId 的 appid 不同)
field3?: pb_optional<3, uint_32>; // 2
payload?: pb<107, FlashSlicePayload>;
}
/** sliceupload HTTP 响应 — f5=status,"success" 表示该片已落盘。 */
export interface FlashSliceUploadResp {
status?: pb<5, string>;
}
/** sub=103 wrapper.f2 fileId — 客户端构造的 protobuf,base64url 编码。
* appid 用 14902:下载 URL(0x93d4 返回)的 appid 也是 14902,两者必须一致,
* 否则下载报 "appid is not match"。sliceupload body 的 appid 则用 14901。 */
export interface FlashFileId {
sha1?: pb<2, bytes>; // 整文件 SHA1
fileSize?: pb<3, uint_32>;
appid?: pb<4, uint_32>; // 14902
timestamp?: pb<5, uint_64>; // 微秒时间戳
env?: pb<6, string>; // "prod"
ttl?: pb<10, uint_32>; // 1209600(14 天)
sessionId?: pb<11, bytes>; // 16B,随机生成
field15?: pb<15, bytes>; // 3B
region?: pb<16, string>; // "gz"
}
// ─────────────── 0x12a9 sub=100/103 — prepare/apply-upload ───────────────
// sub=100 与 sub=103 的 payload 字段号不同:sub=100 在 f2,sub=103 在 f12。
// sub=100 返回 sliceupload rkey;sub=103 注册 fileId,响应无 rkey。
// 下面的类型描述 sub=103 的 f12 结构(sub=100 的 f2 结构对应 FlashPrepareUploadPayload)。
/** FileInfo.f5 — f4 是 varint,与 FlashApplyPayloadField3.f4 的 message 类型不同。
* pb_optional 保证 0 值显式上 wire,服务端要求字段存在。 */
export interface FlashApplyFileInfo5 {
field1?: pb_optional<1, uint_32>; // sub=100=1 / sub=103=0
field2?: pb_optional<2, uint_32>;
field3?: pb_optional<3, uint_32>;
field4?: pb_optional<4, uint_32>;
}
/** f12.f3 — f4 是空 message,与 FileInfo5.f4 的 varint 类型不同。 */
export interface FlashApplyPayloadField3 {
field1?: pb_optional<1, uint_32>;
field2?: pb_optional<2, uint_32>;
field3?: pb_optional<3, uint_32>;
field4?: pb<4, FlashEmpty>;
}
/** f12.f10 — fileset 包装(filesetUuid + fileUuid + flags)。 */
export interface FlashApplyFilesetWrap {
filesetUuid?: pb_optional<1, string>;
uploadKey?: pb_optional<2, string>; // 同 filesetUuid
fileUuid?: pb_optional<3, string>;
field4?: pb_optional<4, uint_32>; // 1
field5?: pb_optional<5, uint_32>; // 0
field6?: pb_optional<6, uint_32>; // sub=100=0 / sub=103=1
field7?: pb_optional<7, uint_32>; // 2
field8?: pb<8, FlashEmpty>;
field9?: pb_optional<9, uint_32>; // 1
field10?: pb_optional<10, uint_32>;
field11?: pb_optional<11, uint_32>;
field12?: pb_optional<12, uint_32>;
field13?: pb_optional<13, uint_32>;
field14?: pb_optional<14, uint_32>;
}
/** 0x12a9 f12.f1.f1 — FileInfo。 */
export interface FlashApplyFileInfo {
fileSize?: pb_optional<1, uint_32>;
md5?: pb_optional<2, string>; // 32 hex(sub=100 空,sub=103 有)
sha1?: pb_optional<3, string>; // 40 hex
fileName?: pb_optional<4, string>;
field5?: pb<5, FlashApplyFileInfo5>;
field6?: pb_optional<6, uint_32>;
field7?: pb_optional<7, uint_32>;
field8?: pb_optional<8, uint_32>;
field9?: pb_optional<9, uint_32>; // sub=100=0 / sub=103=1
}
/** 0x12a9 f12.f1 — wrapper(FileInfo + fileId + 时间戳/TTL)。 */
export interface FlashApplyUploadWrapper {
fileInfo?: pb<1, FlashApplyFileInfo>;
fileId?: pb_optional<2, string>; // sub=100 不带,sub=103 带
field3?: pb_optional<3, uint_32>; // 1
field4?: pb_optional<4, uint_32>; // 时间戳
field5?: pb_optional<5, uint_32>; // TTL 1209600(14 天)
field6?: pb_optional<6, uint_32>;
}
/** 0x12a9 f12 — apply-upload payload。 */
export interface FlashApplyUploadPayload {
wrapper?: pb<1, FlashApplyUploadWrapper>;
flag2?: pb<2, FlashApplyFlag2>; // {f1:2}
field3?: pb<3, FlashApplyPayloadField3>; // {f1:0,f2:0,f3:0,f4:{}}
filesetWrap?: pb<10, FlashApplyFilesetWrap>;
}
export interface FlashApplyFlag2 {
field1?: pb<1, uint_32>;
}
/** 0x12a9 请求 f1 — head(seq + sub + config)。 */
export interface FlashApplyHeadSub {
seq?: pb<1, uint_32>;
sub?: pb<2, uint_32>;
}
export interface FlashApplyHeadConfig {
field101?: pb<101, uint_32>;
field102?: pb<102, uint_32>;
field103?: pb<103, uint_32>; // 文件类型:jpg=24 / png=23 / mp4=22
field200?: pb<200, uint_32>;
}
export interface FlashApplyHead {
sub?: pb<1, FlashApplyHeadSub>;
config?: pb<2, FlashApplyHeadConfig>;
field3?: pb<3, FlashApplyFlag2>; // {f1:1}
}
/** 0x12a9 sub=103 请求(payload @ f12)。 */
export interface FlashApplyUploadReq {
head?: pb<1, FlashApplyHead>;
payload?: pb<12, FlashApplyUploadPayload>;
}
/** 0x12a9 apply-upload 响应 — rkey 在 f2。 */
export interface FlashApplyUploadResp {
head?: pb<1, FlashApplyHeadResp>;
rkeyWrap?: pb<2, FlashRkeyWrap>;
}
export interface FlashApplyHeadResp {
sub?: pb<1, FlashApplyHeadSub>;
msg?: pb<3, string>; // "success"
}
export interface FlashRkeyWrap {
rkey?: pb<1, string>;
}
// ─────────────── 0x12a9 sub=100 — prepare-upload ───────────────
// sub=100 的 payload 在 f2(sub=103 在 f12),filesetWrap 在 f9(sub=103 在 f10),
// wrapper.f2 是 varint 0(sub=103 是 fileId string)。
/** sub=100 payload.f6.f1。 */
export interface FlashPreparePayloadF6F1 {
field1?: pb_optional<1, uint_32>;
field2?: pb<2, FlashEmpty>;
}
/** sub=100 payload.f6.f2。 */
export interface FlashPreparePayloadF6F2 {
field3?: pb<3, FlashEmpty>;
}
/** sub=100 payload.f6.f3。 */
export interface FlashPreparePayloadF6F3 {
field11?: pb<11, FlashEmpty>;
field12?: pb<12, FlashEmpty>;
}
/** sub=100 payload.f6 — 嵌套 message(固定值)。 */
export interface FlashPreparePayloadF6 {
field1?: pb<1, FlashPreparePayloadF6F1>;
field2?: pb<2, FlashPreparePayloadF6F2>;
field3?: pb<3, FlashPreparePayloadF6F3>;
field10?: pb_optional<10, uint_32>;
}
/** sub=100 wrapper — f2 是 varint 0(sub=103 是 fileId)。 */
export interface FlashPrepareWrapper {
fileInfo?: pb<1, FlashApplyFileInfo>;
field2?: pb_optional<2, uint_32>;
}
/** sub=100 payload。 */
export interface FlashPrepareUploadPayload {
wrapper?: pb<1, FlashPrepareWrapper>;
field2?: pb_optional<2, uint_32>; // 1
field3?: pb_optional<3, uint_32>;
field4?: pb_optional<4, uint_32>;
field5?: pb_optional<5, uint_32>;
field6?: pb<6, FlashPreparePayloadF6>;
field7?: pb_optional<7, uint_32>;
field8?: pb_optional<8, uint_32>;
filesetWrap?: pb<9, FlashApplyFilesetWrap>; // f9(sub=103 在 f10);f6=0(sub=103=1)
}
/** sub=100 请求 — payload 在 f2(sub=103 在 f12)。 */
export interface FlashPrepareUploadReq {
head?: pb<1, FlashApplyHead>;
payload?: pb<2, FlashPrepareUploadPayload>;
}
/** sub=100 响应 — f2.f1 是 sliceupload rkey;秒传时 f1 缺失(deserialize 返回 null)。 */
export interface FlashPrepareUploadResp {
head?: pb<1, FlashApplyHeadResp>;
rkeyWrap?: pb<2, FlashRkeyWrap>;
}
// ─────────────── 0x93d7 — 发送闪传文件给用户(send_flash_msg) ───────────────
// 请求 f1={f1:1, f2:{f1:targetUid}}, f2=filesetUuid(私聊);
// f1={f1:2, f3:{f1:groupId}}, f2=filesetUuid(群聊)。响应仅回显目标(无 message_id)。
/** 0x93d7 请求 f1.f2 — 私聊目标 uid 包装。 */
export interface FlashSendTargetUid {
targetUid?: pb<1, string>;
}
/** 0x93d7 请求 f1.f3 — 群聊目标 groupId 包装。 */
export interface FlashSendTargetGroupId {
groupId?: pb<1, uint_32>;
}
/** 0x93d7 请求 f1 — 发送目标。私聊 f1=1+f2(uid),群聊 f1=2+f3(groupId)。 */
export interface FlashSendTarget {
field1?: pb<1, uint_32>;
targetUid?: pb<2, FlashSendTargetUid>;
targetGroup?: pb<3, FlashSendTargetGroupId>;
}
/** 0x93d7 请求。 */
export interface FlashSendReq {
target?: pb<1, FlashSendTarget>;
filesetUuid?: pb<2, string>;
}
/** 0x93d7 响应 f1.f3 — 回显目标。 */
export interface FlashSendRespEcho {
target?: pb<3, FlashSendTarget>;
}
/** 0x93d7 响应 — 仅回显目标,无 message_id(分享 fileset,非传统消息)。 */
export interface FlashSendResp {
echo?: pb<1, FlashSendRespEcho>;
}
// ─────────────── 0x12a9 sub=200 — get-download(主文件下载直链)───────────────
// 请求 payload @ f3(不同于 sub=100 的 f2 / sub=103 的 f12)。响应 f3 含 rkey + 主文件下载 URL。
// 主文件下载 URL(appid=14901)的唯一来源;0x93d3/0x93d4 的 downloadUrl 是缩略图。
/** sub=200 请求 f3.f1.f1 — FileInfo(简化,只 fileName)。 */
export interface FlashGetDownloadFileInfo {
field1?: pb<1, uint_32>; // 0
field4?: pb<4, string>; // fileName
field5?: pb<5, FlashApplyFileInfo5>; // {0,0,0,0}
field6?: pb<6, uint_32>; // 0
field7?: pb<7, uint_32>; // 0
field8?: pb<8, uint_32>; // 0
field9?: pb<9, uint_32>; // 0
}
/** sub=200 请求 f3.f1 — wrapper(FileInfo + 主文件 fileId)。 */
export interface FlashGetDownloadWrapper {
fileInfo?: pb<1, FlashGetDownloadFileInfo>;
fileId?: pb<2, string>; // 主文件 fileId(来自 0x93d4 f14.f1)
field3?: pb<3, uint_32>; // 0
field4?: pb<4, uint_32>; // 0
field5?: pb<5, uint_32>; // 0
field6?: pb<6, uint_32>; // 0
}
/** sub=200 请求 f3.f2.f2 — 固定下载参数(真客户端实测值,未观察到随文件变化)。 */
export interface FlashGetDownloadParam {
field1?: pb<1, uint_32>; // 4294967294
field3?: pb<3, uint_32>; // 4294967295
field5?: pb<5, uint_32>; // 111
field6?: pb<6, FlashGetDownloadParam6>;
}
export interface FlashGetDownloadParam6 {
field1?: pb<1, uint_32>; // 3403722988
}
export interface FlashGetDownloadFlag4 {
field1?: pb<1, uint_32>;
}
/** sub=200 请求 f3.f2.f10 — filesetWrap(filesetUuid + fileUuid)。 */
export interface FlashGetDownloadFilesetWrap {
filesetUuid?: pb<1, string>;
fileUuid?: pb<2, string>;
field3?: pb<3, uint_32>; // 2
field4?: pb<4, string>; // fileUuid
}
/** sub=200 请求 f3.f2。 */
export interface FlashGetDownloadPayload2 {
field2?: pb<2, FlashGetDownloadParam>;
field4?: pb<4, FlashGetDownloadFlag4>;
filesetWrap?: pb<10, FlashGetDownloadFilesetWrap>;
}
/** sub=200 请求 f3 — payload。 */
export interface FlashGetDownloadPayload {
wrapper?: pb<1, FlashGetDownloadWrapper>;
field2?: pb<2, FlashGetDownloadPayload2>;
field3?: pb<3, uint_32>; // 0
}
/** sub=200 请求 — payload @ f3。 */
export interface FlashGetDownloadReq {
head?: pb<1, FlashApplyHead>; // sub:200
payload?: pb<3, FlashGetDownloadPayload>;
}
/** sub=200 响应 — f3 含 rkey + 主文件下载 URL(bytes,deserialize 正则提取 URL)。 */
export interface FlashGetDownloadResp {
head?: pb<1, FlashApplyHeadResp>;
body?: pb<3, bytes>;
}
@@ -0,0 +1,92 @@
import type { pb, pb_optional, pb_repeated, bytes, uint_32, uint_64 } from '@snowluma/proton';
export interface GroupAdminPermissionItem {
item?: pb<1, uint_32>;
value?: pb<2, uint_32>;
extra?: pb<3, bytes>;
}
export interface GroupAdminKickLimit {
maxKickPerDay?: pb<1, uint_32>;
}
export interface SetGroupAdminPermissionsRequest {
groupCode?: pb<1, uint_64>;
memberUin?: pb<2, uint_64>;
permissions?: pb_repeated<3, GroupAdminPermissionItem>;
}
export interface GroupAdminPermissionsFilter {
permissions?: pb<52, bytes>;
}
export interface GetGroupAdminPermissionsRequest {
groupCode?: pb<1, uint_64>;
memberUins?: pb_repeated<2, uint_64>;
filter?: pb<4, GroupAdminPermissionsFilter>;
}
export interface GroupAdminPermissions {
items?: pb_repeated<1, GroupAdminPermissionItem>;
}
export interface GroupAdminPermissionsExt {
permissions?: pb<52, GroupAdminPermissions>;
}
export interface GroupAdminPermissionsMember {
memberUin?: pb<1, uint_64>;
ext?: pb<3, GroupAdminPermissionsExt>;
}
export interface GetGroupAdminPermissionsResponse {
groupCode?: pb<1, uint_64>;
members?: pb_repeated<3, GroupAdminPermissionsMember>;
}
export interface GroupAdminReceiveVerifyOperation {
adminUins?: pb_repeated<1, uint_64>;
authority?: pb<2, uint_32>;
operate?: pb_optional<3, uint_32>;
}
export interface SetGroupAdminReceiveVerifyRequest {
groupCode?: pb<1, uint_64>;
operation?: pb<2, GroupAdminReceiveVerifyOperation>;
}
export interface GroupAdminReceiveVerifyResult {
successfulUins?: pb_repeated<1, uint_64>;
}
export interface SetGroupAdminReceiveVerifyResponse {
groupCode?: pb<1, uint_64>;
result?: pb<2, GroupAdminReceiveVerifyResult>;
}
export interface GroupAdminReceiveVerifyIdentity {
uid?: pb<2, string>;
uin?: pb_optional<4, uint_64>;
}
export interface GroupAdminReceiveVerifyFilter {
memberFlagExt?: pb<104, uint_32>;
}
export interface GetGroupAdminReceiveVerifyRequest {
groupCode?: pb<1, uint_64>;
field2?: pb<2, uint_32>;
field3?: pb_optional<3, uint_32>;
filter?: pb<4, GroupAdminReceiveVerifyFilter>;
members?: pb_repeated<5, GroupAdminReceiveVerifyIdentity>;
}
export interface GroupAdminReceiveVerifyMember {
identity?: pb<1, GroupAdminReceiveVerifyIdentity>;
memberFlagExt?: pb_optional<104, uint_32>;
}
export interface GetGroupAdminReceiveVerifyResponse {
groupCode?: pb<1, uint_64>;
members?: pb_repeated<2, GroupAdminReceiveVerifyMember>;
}
@@ -0,0 +1,352 @@
import type {
pb,
pb_optional,
pb_repeated,
int_32,
uint_32,
uint_64,
bool,
bytes,
} from '@snowluma/proton';
export interface ExtMapEntry {
key?: pb<1, string>;
value?: pb<2, string>;
}
// QunAlbum.trpc.qzone.webapp_qun_media.QunMedia.GetAlbumList
//
// Recovered from QQ NT's AlbumService codec. The service uses the same
// common request/response envelope as the other QunAlbum methods in this
// file; the operation-specific request body only contains the group id and
// the pagination cursor.
export interface GetAlbumListReqData {
groupId?: pb<1, string>;
attachInfo?: pb_optional<2, string>;
}
export interface GetAlbumListRequest {
seq?: pb<1, int_32>;
field2?: pb_optional<2, bytes>;
field3?: pb_optional<3, bytes>;
data?: pb<4, GetAlbumListReqData>;
traceId?: pb<5, string>;
extMap?: pb_repeated<10, ExtMapEntry>;
}
export interface AlbumCreator {
uid?: pb<1, string>;
nick?: pb<2, string>;
isSweet?: pb<5, bool>;
isSpecial?: pb<6, bool>;
isSuperLike?: pb<7, bool>;
customId?: pb<8, string>;
polyId?: pb<9, string>;
portrait?: pb<10, string>;
canFollow?: pb<11, int_32>;
isFollowed?: pb<12, int_32>;
uin?: pb<13, string>;
dittoUin?: pb<14, string>;
}
export interface GroupAlbumInfo {
albumId?: pb<1, string>;
owner?: pb<2, string>;
name?: pb<3, string>;
description?: pb<4, string>;
createTime?: pb<5, uint_64>;
modifyTime?: pb<6, uint_64>;
lastUploadTime?: pb<7, uint_64>;
uploadNumber?: pb<8, uint_64>;
// QQ NT AlbumCodec_DecodeAlbumInfo tag 9 reuses the media-list MediaInfo codec.
cover?: pb<9, MediaInfo>;
creator?: pb<10, AlbumCreator>;
topFlag?: pb<11, uint_64>;
busiType?: pb<12, int_32>;
status?: pb<13, int_32>;
allowShare?: pb<15, bool>;
isSubscribe?: pb<16, bool>;
bitmap?: pb<17, string>;
isShareAlbum?: pb<18, bool>;
qzAlbumType?: pb<20, int_32>;
coverType?: pb<23, int_32>;
defaultDesc?: pb<26, string>;
sortType?: pb<30, int_32>;
}
export interface GetAlbumListRspData {
albumList?: pb_repeated<1, GroupAlbumInfo>;
attachInfo?: pb<2, string>;
hasMore?: pb<3, bool>;
}
export interface GetAlbumListResponse {
seq?: pb<1, int_32>;
result?: pb<2, int_32>;
errorText?: pb<3, string>;
data?: pb<4, GetAlbumListRspData>;
}
export interface ReqInfo {
groupId?: pb<1, string>;
albumId?: pb<2, string>;
field3?: pb<3, int_32>;
field4?: pb<4, string>;
pageInfo?: pb<5, string>;
}
export interface GetMediaListRequest {
field1?: pb<1, int_32>;
field2?: pb<2, bytes>;
field3?: pb<3, bytes>;
reqInfo?: pb<4, ReqInfo>;
traceId?: pb<5, string>;
extMap?: pb_repeated<10, ExtMapEntry>;
}
export interface UrlInfo {
url?: pb<1, string>;
width?: pb<2, uint_32>;
height?: pb<3, uint_32>;
}
export interface PhotoUrl {
spec?: pb<1, uint_32>;
url?: pb<2, UrlInfo>;
}
export interface ImageInfo {
name?: pb<1, string>;
sloc?: pb<2, string>;
lloc?: pb<3, string>;
photoUrls?: pb_repeated<4, PhotoUrl>;
defaultUrl?: pb<5, UrlInfo>;
isGif?: pb<6, bool>;
hasRaw?: pb<7, bool>;
}
export interface VideoInfo {
id?: pb<1, string>;
url?: pb<2, string>;
cover?: pb<3, ImageInfo>;
width?: pb<4, uint_32>;
height?: pb<5, uint_32>;
videoTime?: pb<6, uint_64>;
videoUrl?: pb_repeated<7, PhotoUrl>;
}
export interface MediaInfo {
type?: pb<1, uint_32>;
image?: pb<2, ImageInfo>;
video?: pb<3, VideoInfo>;
uploader?: pb<6, string>;
batchId?: pb<7, uint_64>;
uploadTime?: pb<8, uint_64>;
}
export interface GetMediaListAlbumInfo {
albumId?: pb<1, string>;
owner?: pb<2, string>;
name?: pb<3, string>;
}
export interface GetMediaListRspData {
albumInfo?: pb<1, GetMediaListAlbumInfo>;
mediaList?: pb_repeated<3, MediaInfo>;
prevAttachInfo?: pb<4, string>;
nextAttachInfo?: pb<5, string>;
}
export interface GetMediaListResponse {
field1?: pb<1, int_32>;
field2?: pb<2, bytes>;
field3?: pb<3, bytes>;
data?: pb<4, GetMediaListRspData>;
}
export interface CommentContentItem {
// Text cells on the wire include type 0. A plain pb<> field would omit it.
type?: pb_optional<1, uint_32>;
content?: pb<2, string>;
}
export interface CommentUser {
uin?: pb<13, string>;
uinNum?: pb<14, uint_64>;
}
export interface CommentReqContent {
user?: pb<2, CommentUser>;
// QQ NT FeedWorker writes StComment content cells as a repeated field.
contents?: pb_repeated<3, CommentContentItem>;
clientKey?: pb<7, string>;
}
// QQ NT FeedWorker copies StFeed media cells with the MediaInfo codec.
export interface CommentReqPhotoInfo {
medias?: pb_repeated<1, MediaInfo>;
albumId?: pb<3, string>;
batchId?: pb<5, uint_64>;
}
export interface CommentReqBodyHeader {
// Fallback only. A successful write copies the feed cellCommon bytes,
// which also carry type, the 421 locator, and field 6.
type?: pb<1, uint_32>;
time?: pb<3, uint_64>;
feedId?: pb<4, string>;
cellId?: pb<5, string>;
field6?: pb<6, uint_32>;
}
export interface CommentReqBodyUserWrap {
field1?: pb<1, CommentUser>;
}
export interface CommentReqBody {
// Raw length-delimited cells from GetQunFeedDetail. Re-encoding them
// through the typed schemas drops fields QZone requires.
field1?: pb<1, bytes>;
field2?: pb<2, bytes>;
field5?: pb<5, bytes>;
}
export interface DoQunCommentRequestBody {
groupId?: pb<2, string>;
field3?: pb<3, uint_32>;
reqBody?: pb<4, CommentReqBody>;
field5?: pb<5, CommentReqContent>;
}
export interface DoQunCommentRequest {
field1?: pb<1, int_32>;
field2?: pb_optional<2, bytes>;
field3?: pb_optional<3, bytes>;
body?: pb<4, DoQunCommentRequestBody>;
traceId?: pb<5, string>;
extMap?: pb_repeated<10, ExtMapEntry>;
}
export interface CommentRespUser {
uin?: pb<13, string>;
}
export interface CommentRespContent {
type?: pb<1, uint_32>;
content?: pb<2, string>;
}
export interface CommentRespData {
id?: pb<1, string>;
user?: pb<2, CommentRespUser>;
content?: pb_repeated<3, CommentRespContent>;
time?: pb<4, uint_64>;
clientKey?: pb<7, string>;
}
export interface DoQunCommentResponseComment {
id?: pb<1, string>;
data?: pb<2, CommentRespData>;
content?: pb_repeated<3, CommentRespContent>;
time?: pb<4, uint_64>;
clientKey?: pb<7, string>;
}
export interface DoQunCommentResponse {
field1?: pb<1, int_32>;
result?: pb<2, int_32>;
errorText?: pb<3, string>;
comment?: pb<4, DoQunCommentResponseComment>;
}
export interface DoQunLikeReqLikeInfo {
id?: pb<1, string>;
status?: pb<3, uint_32>;
}
export interface DoQunLikeReqCellCommon {
time?: pb<3, uint_64>;
feedId?: pb<4, string>;
}
export interface DoQunLikeReqCellUser {
uin?: pb<13, string>;
}
export interface DoQunLikeReqCellUserInfo {
user?: pb<1, DoQunLikeReqCellUser>;
}
export interface DoQunLikeReqCellQunInfo {
qunId?: pb<1, string>;
}
export interface DoQunLikeReqCellMedia {
albumId?: pb<3, string>;
batchId?: pb<5, uint_64>;
}
export interface DoQunLikeReqFeedPublish {
cellCommon?: pb<1, DoQunLikeReqCellCommon>;
cellUserInfo?: pb<2, DoQunLikeReqCellUserInfo>;
cellMedia?: pb<5, DoQunLikeReqCellMedia>;
cellQunInfo?: pb<12, DoQunLikeReqCellQunInfo>;
}
export interface DoQunLikeReqBody {
type?: pb<2, uint_32>;
like?: pb<3, DoQunLikeReqLikeInfo>;
publish?: pb<4, DoQunLikeReqFeedPublish>;
clientKey?: pb<5, string>;
}
export interface DoQunLikeRequest {
field1?: pb<1, int_32>;
field2?: pb<2, string>;
field3?: pb<3, string>;
body?: pb<4, DoQunLikeReqBody>;
extMap?: pb_repeated<10, ExtMapEntry>;
}
export interface DoQunLikeRespBody {
like?: pb<2, DoQunLikeReqLikeInfo>;
}
export interface DoQunLikeResponse {
field1?: pb<1, int_32>;
body?: pb<4, DoQunLikeRespBody>;
}
// QunAlbum.trpc.qzone.webapp_qun_feeds.FeedsReader.GetQunFeedDetail
//
// Official album comments look this feed up first (empty feed id + album /
// batch / lloc) and then copy the returned cell into DoQunComment.
export interface GetQunFeedDetailReqData {
groupId?: pb<2, string>;
feedId?: pb<3, string>;
commentCount?: pb<4, int_32>;
attachInfo?: pb<5, string>;
albumId?: pb<6, string>;
batchId?: pb<7, string>;
lloc?: pb<8, string>;
}
export interface GetQunFeedDetailRequest {
seq?: pb<1, int_32>;
field2?: pb_optional<2, bytes>;
field3?: pb_optional<3, bytes>;
data?: pb<4, GetQunFeedDetailReqData>;
traceId?: pb<5, string>;
extMap?: pb_repeated<10, ExtMapEntry>;
}
export interface QunFeedCellCommon {
type?: pb<1, uint_32>;
time?: pb<3, uint_64>;
feedId?: pb<4, string>;
cellId?: pb<5, string>;
field6?: pb<6, uint_32>;
}
export interface QunFeedCellUser {
uin?: pb<13, string>;
}
export interface QunFeedCellUserInfo {
user?: pb<1, QunFeedCellUser>;
}
export interface QunFeed {
cellCommon?: pb<1, QunFeedCellCommon>;
cellUserInfo?: pb<2, QunFeedCellUserInfo>;
// FeedWorker copies this cell into DoQunComment body.4.5.
cellMedia?: pb<5, CommentReqPhotoInfo>;
}
export interface QunClientFeed {
feed?: pb<1, QunFeed>;
}
export interface GetQunFeedDetailRspData {
feed?: pb<2, QunClientFeed>;
}
export interface GetQunFeedDetailResponse {
seq?: pb<1, int_32>;
result?: pb<2, int_32>;
errorText?: pb<3, string>;
data?: pb<4, GetQunFeedDetailRspData>;
}
export interface DeleteMediasReqBody {
groupId?: pb<1, string>;
albumId?: pb<2, string>;
// deleteMedias 4th arg: photo lloc, or a video's cover lloc.
mediaIds?: pb_repeated<3, string>;
// deleteMedias 5th arg: matching batch ids. Android always sends these.
batchIds?: pb_repeated<4, string>;
}
export interface DeleteMediasRequest {
field1?: pb<1, int_32>;
field2?: pb<2, string>;
field3?: pb<3, string>;
body?: pb<4, DeleteMediasReqBody>;
traceId?: pb<5, string>;
extMap?: pb_repeated<10, ExtMapEntry>;
}
export interface DeleteMediasResponse {
field1?: pb<1, int_32>;
field2?: pb<2, int_32>;
field3?: pb<3, string>;
}
@@ -0,0 +1,23 @@
import type { pb, pb_optional, pb_repeated, uint_32 } from '@snowluma/proton';
/** One tag shown under a group-profile category. */
export interface OidbGroupCategoryTag {
id?: pb<1, uint_32>;
name?: pb<2, string>;
type?: pb<3, uint_32>;
}
/** List the tags of one category. Category 0 is a real value and must be sent. */
export interface OidbGetGroupCategoryTags {
groupClass?: pb_optional<1, uint_32>;
}
export interface OidbGetGroupCategoryTagsResp {
tags?: pb_repeated<1, OidbGroupCategoryTag>;
}
/** Add or remove one tag name in a category. */
export interface OidbChangeGroupCategoryTag {
groupClass?: pb_optional<1, uint_32>;
name?: pb<2, string>;
}
@@ -0,0 +1,48 @@
// Group ext-info modify (robot-add option).
// OidbSvcTrpcTcp.0xf00_3 / modifyGroupExtInfoV2.
//
// Wire: ModifyGroupExtInfoReq{ 1:groupCode, 2:GroupExtInfo{ 1:groupCode,
// 2:EXTInfo{ 29:inviteRobotMemberSwitch, 30:inviteRobotMemberExamine } } }
// GroupExtFilter is client-side only: it decides which EXTInfo fields the
// encoder emits. Presence of tag 29/30 IS the write signal, including 0.
// Response: {1:groupCode, 2:result}.
import type { pb, pb_optional, pb_repeated, int_32, uint_32, uint_64 } from '@snowluma/proton';
export interface OidbGroupExtBody {
inviteRobotMemberSwitch?: pb_optional<29, uint_32>;
inviteRobotMemberExamine?: pb_optional<30, uint_32>;
}
export interface OidbGroupExtInfo {
groupCode?: pb<1, uint_32>;
ext?: pb<2, OidbGroupExtBody>;
}
export interface OidbModifyGroupExtReq {
groupCode?: pb<1, uint_32>;
info?: pb<2, OidbGroupExtInfo>;
}
export interface OidbModifyGroupExtResp {
groupCode?: pb<1, uint_32>;
result?: pb<2, int_32>;
}
// OidbSvcTrpcTcp.0xef0_1 / getGroupExt0xEF0Info.
// DoFetchGroupExtList request: {1:groupCodes, 2:GroupExtFilter}.
// fillGroupExt0xEF0Filter writes filter tags 29/30 when those bits are set.
// Response: repeated item{1:groupCode, 2:resultCode, 3:EXTInfo}.
export interface OidbGroupExtFilter {
inviteRobotMemberSwitch?: pb<29, uint_32>;
inviteRobotMemberExamine?: pb<30, uint_32>;
}
export interface OidbGetGroupExtReq {
groupCodes?: pb_repeated<1, uint_64>;
filter?: pb<2, OidbGroupExtFilter>;
}
export interface OidbGetGroupExtItem {
groupCode?: pb<1, uint_64>;
resultCode?: pb<2, uint_32>;
ext?: pb<3, OidbGroupExtBody>;
}
export interface OidbGetGroupExtResp {
items?: pb_repeated<1, OidbGetGroupExtItem>;
}
@@ -0,0 +1,244 @@
import type { pb, pb_repeated, int_32, uint_32, uint_64, bool, bytes } from '@snowluma/proton';
export interface OidbGroupFileListReq {
groupUin?: pb<1, uint_32>;
appId?: pb<2, uint_32>;
targetDirectory?: pb<3, string>;
fileCount?: pb<5, uint_32>;
sortBy?: pb<9, uint_32>;
startIndex?: pb<13, uint_32>;
field17?: pb<17, uint_32>;
field18?: pb<18, uint_32>;
}
export interface OidbGroupFileViewReq {
list?: pb<2, OidbGroupFileListReq>;
}
export interface OidbGroupFileListFolderResp {
folderId?: pb<1, string>;
parentDirectoryId?: pb<2, string>;
folderName?: pb<3, string>;
createTime?: pb<4, uint_32>;
modifiedTime?: pb<5, uint_32>;
creatorUin?: pb<6, uint_32>;
creatorName?: pb<7, string>;
totalFileCount?: pb<8, uint_32>;
modifierUin?: pb<9, uint_64>;
modifierName?: pb<10, string>;
}
export interface OidbGroupFileListFileResp {
fileId?: pb<1, string>;
fileName?: pb<2, string>;
fileSize?: pb<3, uint_64>;
busId?: pb<4, uint_32>;
uploadedTime?: pb<6, uint_32>;
expireTime?: pb<7, uint_32>;
modifiedTime?: pb<8, uint_32>;
downloadedTimes?: pb<9, uint_32>;
uploaderName?: pb<14, string>;
uploaderUin?: pb<15, uint_32>;
parentDirectory?: pb<16, string>;
}
export interface OidbGroupFileListItemResp {
type?: pb<1, uint_32>;
folderInfo?: pb<2, OidbGroupFileListFolderResp>;
fileInfo?: pb<3, OidbGroupFileListFileResp>;
}
export interface OidbGroupFileListResp {
retCode?: pb<1, uint_32>;
retMsg?: pb<2, string>;
clientWording?: pb<3, string>;
isEnd?: pb<4, bool>;
items?: pb_repeated<5, OidbGroupFileListItemResp>;
}
export interface OidbGroupFileViewResp {
list?: pb<2, OidbGroupFileListResp>;
}
export interface OidbGroupFileUploadReq {
groupUin?: pb<1, uint_32>;
appId?: pb<2, uint_32>;
busId?: pb<3, uint_32>;
entrance?: pb<4, uint_32>;
targetDirectory?: pb<5, string>;
fileName?: pb<6, string>;
localDirectory?: pb<7, string>;
fileSize?: pb<8, uint_64>;
fileSha1?: pb<9, bytes>;
fileSha3?: pb<10, bytes>;
fileMd5?: pb<11, bytes>;
field15?: pb<15, bool>;
}
export interface OidbGroupFileDownloadReq {
groupUin?: pb<1, uint_32>;
appId?: pb<2, uint_32>;
busId?: pb<3, uint_32>;
fileId?: pb<4, string>;
}
export interface OidbGroupFileDeleteReq {
groupUin?: pb<1, uint_32>;
busId?: pb<3, uint_32>;
fileId?: pb<5, string>;
}
export interface OidbGroupFileMoveReq {
groupUin?: pb<1, uint_32>;
appId?: pb<2, uint_32>;
busId?: pb<3, uint_32>;
fileId?: pb<4, string>;
parentDirectory?: pb<5, string>;
targetDirectory?: pb<6, string>;
}
export interface OidbGroupFileRenameReq {
groupUin?: pb<1, uint_32>;
busId?: pb<3, uint_32>;
fileId?: pb<4, string>;
parentFolder?: pb<5, string>;
newFileName?: pb<6, string>;
}
export interface OidbGroupFileReq {
file?: pb<1, OidbGroupFileUploadReq>;
download?: pb<3, OidbGroupFileDownloadReq>;
delete?: pb<4, OidbGroupFileDeleteReq>;
rename?: pb<5, OidbGroupFileRenameReq>;
move?: pb<6, OidbGroupFileMoveReq>;
}
export interface OidbGroupFileUploadResp {
retCode?: pb<1, int_32>;
retMsg?: pb<2, string>;
clientWording?: pb<3, string>;
uploadIp?: pb<4, string>;
serverDns?: pb<5, string>;
busId?: pb<6, int_32>;
fileId?: pb<7, string>;
checkKey?: pb<8, bytes>;
fileKey?: pb<9, bytes>;
boolFileExist?: pb<10, bool>;
uploadPort?: pb<14, uint_32>;
}
export interface OidbGroupFileDownloadResp {
retCode?: pb<1, uint_32>;
retMsg?: pb<2, string>;
clientWording?: pb<3, string>;
downloadIp?: pb<4, string>;
downloadDns?: pb<5, string>;
downloadUrl?: pb<6, bytes>;
saveFileName?: pb<11, string>;
}
export interface OidbGroupFileRetResp {
retCode?: pb<1, uint_32>;
retMsg?: pb<2, string>;
clientWording?: pb<3, string>;
}
export interface OidbGroupFileResp {
upload?: pb<1, OidbGroupFileUploadResp>;
download?: pb<3, OidbGroupFileDownloadResp>;
delete?: pb<4, OidbGroupFileRetResp>;
rename?: pb<5, OidbGroupFileRetResp>;
move?: pb<6, OidbGroupFileRetResp>;
}
export interface OidbGroupTransFileReq {
groupUin?: pb<1, uint_64>;
appId?: pb<2, uint_32>;
busId?: pb<3, uint_32>;
fileId?: pb<4, string>;
}
export interface OidbGroupTransFileResp {
retCode?: pb<1, int_32>;
retMsg?: pb<2, string>;
clientWording?: pb<3, string>;
saveBusId?: pb<4, uint_32>;
saveFilePath?: pb<5, string>;
}
export interface OidbGroupSendFileInfo {
busiType?: pb<1, uint_32>;
fileId?: pb<2, string>;
field3?: pb<3, uint_32>;
field4?: pb<4, string>;
field5?: pb<5, bool>;
}
export interface OidbGroupSendFileBody {
groupUin?: pb<1, uint_32>;
type?: pb<2, uint_32>;
info?: pb<3, OidbGroupSendFileInfo>;
}
export interface OidbGroupSendFileReq {
transFile?: pb<1, OidbGroupTransFileReq>;
body?: pb<5, OidbGroupSendFileBody>;
}
export interface OidbGroupSendFileResp {
transFile?: pb<1, OidbGroupTransFileResp>;
}
export interface OidbGroupFileCreateFolderReq {
groupUin?: pb<1, uint_32>;
rootDirectory?: pb<3, string>;
folderName?: pb<4, string>;
}
export interface OidbGroupFileDeleteFolderReq {
groupUin?: pb<1, uint_32>;
folderId?: pb<3, string>;
}
export interface OidbGroupFileRenameFolderReq {
groupUin?: pb<1, uint_32>;
folderId?: pb<3, string>;
newFolderName?: pb<4, string>;
}
export interface OidbGroupFileFolderReq {
create?: pb<1, OidbGroupFileCreateFolderReq>;
delete?: pb<2, OidbGroupFileDeleteFolderReq>;
rename?: pb<3, OidbGroupFileRenameFolderReq>;
}
// The create (0x6D7_0) response also carries the new folder's info at field 4
// — the source of folderId (#195). delete/rename responses leave it empty.
// Tags confirmed against a live create response (bot creating a folder in a
// test group): modifyUin is field 9, NOT 7 as Lagrange's
// OidbSvcTrpcTcp0x6D7_0ResponseFolderInfo has it.
export interface OidbGroupFileFolderInfoResp {
folderId?: pb<1, string>;
folderPath?: pb<2, string>;
folderName?: pb<3, string>;
createTime?: pb<4, uint_32>;
modifyTime?: pb<5, uint_32>;
createUin?: pb<6, uint_32>;
modifyUin?: pb<9, uint_32>;
}
export interface OidbGroupFileFolderRetResp {
retcode?: pb<1, uint_32>;
retMsg?: pb<2, string>;
clientWording?: pb<3, string>;
folderInfo?: pb<4, OidbGroupFileFolderInfoResp>;
}
export interface OidbGroupFileFolderResp {
create?: pb<1, OidbGroupFileFolderRetResp>;
delete?: pb<2, OidbGroupFileFolderRetResp>;
rename?: pb<3, OidbGroupFileFolderRetResp>;
}
export interface OidbGroupFileCountReq {
groupUin?: pb<1, uint_32>;
appId?: pb<2, uint_32>;
busId?: pb<3, uint_32>;
}
// Count response (0x6D8_2): fileCount is field 4 and maxCount (the group's file
// ceiling, e.g. 1500) field 6 — RE'd from a live count query (#196). The old
// pb<1>/pb<2> tags matched nothing, so file_count always decoded to 0.
export interface OidbGroupFileCountResp {
fileCount?: pb<4, uint_32>;
maxCount?: pb<6, uint_32>;
isFull?: pb<7, bool>;
}
// Space request/response (0x6D8_3). Both values are 64-bit — the 10 GiB ceiling
// already exceeds uint32.
export interface OidbGroupFileSpaceReq {
groupUin?: pb<1, uint_32>;
appId?: pb<2, uint_32>;
}
export interface OidbGroupFileSpaceResp {
totalSpace?: pb<4, uint_64>;
usedSpace?: pb<5, uint_64>;
}
// count → subCommand 2, space → subCommand 3 (List is subCommand 1).
export interface OidbGroupFileCountViewReq {
count?: pb<3, OidbGroupFileCountReq>;
space?: pb<4, OidbGroupFileSpaceReq>;
}
export interface OidbGroupFileCountViewResp {
count?: pb<3, OidbGroupFileCountResp>;
space?: pb<4, OidbGroupFileSpaceResp>;
}
@@ -0,0 +1,20 @@
import type { bool, pb, pb_optional, uint_64 } from '@snowluma/proton';
export interface GroupShareLinkRequest {
groupCode?: pb<1, uint_64>;
additionalParam?: pb_optional<3, string>;
needShortUrl?: pb_optional<4, bool>;
}
export interface OidbGroupShareLinkRequest {
type?: pb<1, uint_64>;
group?: pb<12, GroupShareLinkRequest>;
}
export interface GroupShareLinkResponse {
url?: pb<1, string>;
}
export interface OidbGroupShareLinkResponse {
group?: pb<12, GroupShareLinkResponse>;
}
@@ -0,0 +1,5 @@
export type * from './base';
export type * from './group-file';
export type * from './media';
export type * from './group-album';
export type * from './flash-transfer';
@@ -0,0 +1,283 @@
import type { pb, pb_repeated, int_32, uint_32, uint_64, bool, bytes } from '@snowluma/proton';
export interface OidbPrivateFileDownloadReqBody {
receiverUid?: pb<10, string>;
fileUuid?: pb<20, string>;
type?: pb<30, uint_32>;
fileHash?: pb<60, string>;
t2?: pb<601, uint_32>;
}
export interface OidbPrivateFileDownloadReq {
subCommand?: pb<1, uint_32>;
field2?: pb<2, uint_32>;
body?: pb<14, OidbPrivateFileDownloadReqBody>;
field101?: pb<101, uint_32>;
field102?: pb<102, uint_32>;
field200?: pb<200, uint_32>;
field99999?: pb<99999, bytes>;
}
export interface OidbPrivateFileDownloadRespResult {
server?: pb<20, string>;
port?: pb<40, uint_32>;
url?: pb<50, string>;
}
export interface OidbPrivateFileDownloadRespBody {
state?: pb<20, string>;
result?: pb<30, OidbPrivateFileDownloadRespResult>;
}
export interface OidbPrivateFileDownloadResp {
body?: pb<14, OidbPrivateFileDownloadRespBody>;
}
export interface OidbPrivateFileUploadReqBody {
senderUid?: pb<10, string>;
receiverUid?: pb<20, string>;
fileSize?: pb<30, uint_32>;
fileName?: pb<40, string>;
md510MCheckSum?: pb<50, bytes>;
sha1CheckSum?: pb<60, bytes>;
localPath?: pb<70, string>;
md5CheckSum?: pb<110, bytes>;
sha3CheckSum?: pb<120, bytes>;
}
export interface OidbPrivateFileUploadReq {
command?: pb<1, uint_32>;
seq?: pb<2, int_32>;
upload?: pb<19, OidbPrivateFileUploadReqBody>;
businessId?: pb<101, int_32>;
clientType?: pb<102, int_32>;
flagSupportMediaPlatform?: pb<200, int_32>;
}
export interface IPv4 {
outIP?: pb<1, int_32>;
outPort?: pb<2, int_32>;
inIP?: pb<3, int_32>;
inPort?: pb<4, int_32>;
iPType?: pb<5, int_32>;
}
export interface OidbPrivateFileUploadRespBody {
retCode?: pb<10, int_32>;
retMsg?: pb<20, string>;
uploadIp?: pb<60, string>;
uploadDomain?: pb<70, string>;
uploadPort?: pb<80, uint_32>;
uuid?: pb<90, string>;
uploadKey?: pb<100, bytes>;
boolFileExist?: pb<110, bool>;
uploadIpList?: pb_repeated<130, string>;
uploadHttpsPort?: pb<140, int_32>;
uploadHttpsDomain?: pb<150, string>;
uploadDns?: pb<160, string>;
uploadLanip?: pb<170, string>;
fileAddon?: pb<200, string>;
rtpMediaPlatformUploadAddress?: pb_repeated<210, IPv4>;
mediaPlatformUploadKey?: pb<220, bytes>;
}
export interface OidbPrivateFileUploadResp {
upload?: pb<19, OidbPrivateFileUploadRespBody>;
}
// 0xE37_800 — c2c offline-file finalize / download-credential fetch.
// Called AFTER the Highway upload completes (NapCat runs it even when the
// file already exists server-side). The response metadata (`field30`)
// supplies the download-routing fields the receiver needs; those ride
// along in `FileExtra.field6` on the PbSendMsg. Without this step the file
// uploads + PbSendMsg returns ok but the recipient sees "文件传输失败".
// Ported byte-for-byte from NapCat `Oidb.0XE37_800` + `Oidb.0xE37_1200`.
export interface OidbOfflineFileFinalizeReqBody {
senderUid?: pb<10, string>;
receiverUid?: pb<20, string>;
fileUuid?: pb<30, string>;
fileHash?: pb<40, string>;
}
export interface OidbOfflineFileFinalizeReq {
subCommand?: pb<1, uint_32>;
field2?: pb<2, int_32>;
body?: pb<10, OidbOfflineFileFinalizeReqBody>;
field101?: pb<101, int_32>;
field102?: pb<102, int_32>;
field200?: pb<200, int_32>;
}
// Server-issued download routing for the just-uploaded c2c file. Only the
// fields consumed by `FileExtra.field6` are modelled (NapCat names them
// field3/field100/field101/field110/timestamp1; tags verified).
export interface OidbOfflineFileMetadata {
field3?: pb<3, uint_32>;
field100?: pb<100, bytes>;
field101?: pb<101, bytes>;
field110?: pb<110, uint_32>;
timestamp1?: pb<130, uint_32>;
}
export interface OidbOfflineFileFinalizeRespBody {
field10?: pb<10, uint_32>;
metadata?: pb<30, OidbOfflineFileMetadata>;
}
export interface OidbOfflineFileFinalizeResp {
command?: pb<1, uint_32>;
subCommand?: pb<2, uint_32>;
body?: pb<10, OidbOfflineFileFinalizeRespBody>;
field50?: pb<50, uint_32>;
}
export interface NTV2CommonHead {
requestId?: pb<1, uint_32>;
command?: pb<2, uint_32>;
}
export interface NTV2C2CUserInfo {
accountType?: pb<1, uint_32>;
targetUid?: pb<2, string>;
}
export interface NTV2GroupInfo {
groupUin?: pb<1, uint_32>;
}
export interface NTV2SceneInfo {
requestType?: pb<101, uint_32>;
businessType?: pb<102, uint_32>;
sceneType?: pb<200, uint_32>;
c2c?: pb<201, NTV2C2CUserInfo>;
group?: pb<202, NTV2GroupInfo>;
}
export interface NTV2ClientMeta {
agentType?: pb<1, uint_32>;
}
export interface NTV2ReqHead {
common?: pb<1, NTV2CommonHead>;
scene?: pb<2, NTV2SceneInfo>;
client?: pb<3, NTV2ClientMeta>;
}
export interface NTV2DownloadRKeyReq {
types?: pb_repeated<1, uint_32>;
}
export interface NTV2FileType {
type?: pb<1, uint_32>;
picFormat?: pb<2, uint_32>;
videoFormat?: pb<3, uint_32>;
voiceFormat?: pb<4, uint_32>;
}
export interface NTV2FileInfo {
fileSize?: pb<1, uint_32>;
fileHash?: pb<2, string>;
fileSha1?: pb<3, string>;
fileName?: pb<4, string>;
type?: pb<5, NTV2FileType>;
width?: pb<6, uint_32>;
height?: pb<7, uint_32>;
time?: pb<8, uint_32>;
original?: pb<9, uint_32>;
}
export interface NTV2IndexNode {
info?: pb<1, NTV2FileInfo>;
fileUuid?: pb<2, string>;
storeId?: pb<3, uint_32>;
uploadTime?: pb<4, uint_32>;
ttl?: pb<5, uint_32>;
subType?: pb<6, uint_32>;
}
export interface NTV2VideoDownloadExt {
busiType?: pb<1, uint_32>;
sceneType?: pb<2, uint_32>;
subBusiType?: pb<3, uint_32>;
}
export interface NTV2DownloadExt {
video?: pb<2, NTV2VideoDownloadExt>;
}
export interface NTV2DownloadReq {
node?: pb<1, NTV2IndexNode>;
download?: pb<2, NTV2DownloadExt>;
}
export interface NTV2RichMediaReq {
reqHead?: pb<1, NTV2ReqHead>;
download?: pb<3, NTV2DownloadReq>;
downloadRkey?: pb<4, NTV2DownloadRKeyReq>;
}
export interface NTV2RespHead {
common?: pb<1, NTV2CommonHead>;
retCode?: pb<2, uint_32>;
message?: pb<3, string>;
}
export interface NTV2RKeyInfo {
rkey?: pb<1, string>;
rkeyTtlSec?: pb<2, uint_64>;
storeId?: pb<3, uint_32>;
rkeyCreateTime?: pb<4, uint_32>;
type?: pb<5, uint_32>;
}
export interface NTV2DownloadRKeyResp {
rkeys?: pb_repeated<1, NTV2RKeyInfo>;
}
export interface NTV2MediaDownloadInfo {
domain?: pb<1, string>;
urlPath?: pb<2, string>;
httpsPort?: pb<3, uint_32>;
}
export interface NTV2MediaDownloadResp {
rKeyParam?: pb<1, string>;
rKeyTtlSecond?: pb<2, uint_32>;
info?: pb<3, NTV2MediaDownloadInfo>;
rKeyCreateTime?: pb<4, uint_32>;
}
export interface NTV2RichMediaResp {
respHead?: pb<1, NTV2RespHead>;
download?: pb<3, NTV2MediaDownloadResp>;
downloadRkey?: pb<4, NTV2DownloadRKeyResp>;
}
export interface OidbAiVoiceListReq {
groupUin?: pb<1, uint_32>;
chatType?: pb<2, uint_32>;
}
export interface OidbAiVoiceListEntry {
voiceId?: pb<1, string>;
voiceDisplayName?: pb<2, string>;
voiceExampleUrl?: pb<3, string>;
}
export interface OidbAiVoiceListCategory {
category?: pb<1, string>;
voices?: pb_repeated<2, OidbAiVoiceListEntry>;
}
export interface OidbAiVoiceListResp {
content?: pb_repeated<1, OidbAiVoiceListCategory>;
}
export interface OidbAiVoiceSession {
sessionId?: pb<1, uint_32>;
}
export interface OidbAiVoiceReq {
groupUin?: pb<1, uint_32>;
voiceId?: pb<2, string>;
text?: pb<3, string>;
chatType?: pb<4, uint_32>;
session?: pb<5, OidbAiVoiceSession>;
}
export interface OidbAiVoiceFileType {
type?: pb<1, uint_32>;
picFormat?: pb<2, uint_32>;
videoFormat?: pb<3, uint_32>;
voiceFormat?: pb<4, uint_32>;
}
export interface OidbAiVoiceFileInfo {
fileSize?: pb<1, uint_32>;
fileHash?: pb<2, string>;
fileSha1?: pb<3, string>;
fileName?: pb<4, string>;
type?: pb<5, OidbAiVoiceFileType>;
width?: pb<6, uint_32>;
height?: pb<7, uint_32>;
time?: pb<8, uint_32>;
original?: pb<9, uint_32>;
}
export interface OidbAiVoiceIndexNode {
info?: pb<1, OidbAiVoiceFileInfo>;
fileUuid?: pb<2, string>;
storeId?: pb<3, uint_32>;
uploadTime?: pb<4, uint_32>;
ttl?: pb<5, uint_32>;
subType?: pb<6, uint_32>;
}
export interface OidbAiVoiceMsgInfoBody {
index?: pb<1, OidbAiVoiceIndexNode>;
}
export interface OidbAiVoiceMsgInfo {
msgInfoBody?: pb_repeated<1, OidbAiVoiceMsgInfoBody>;
}
export interface OidbAiVoiceResp {
statusCode?: pb<1, uint_32>;
field2?: pb<2, uint_32>;
field3?: pb<3, uint_32>;
msgInfo?: pb<4, OidbAiVoiceMsgInfo>;
}
@@ -0,0 +1,50 @@
// OIDB 0xE07_0 — server-side image OCR. Field tags verified against
// NapCat's Oidb.0xE07 proto AND Lagrange.Core's OidbSvcTrpcTcp.0xE07_0
// (the two agree; NapCat's transformer builds with cmd 0xEB7, which is a
// typo — the proto name + Lagrange + the `OidbSvcTrpcTcp.0xe07_0` wire
// name are authoritative). Request takes an image URL (server fetches it).
import type { pb, pb_repeated, int_32, uint_32, bool } from '@snowluma/proton';
export interface OcrReqBody {
imageUrl?: pb<1, string>;
languageType?: pb<2, uint_32>;
scene?: pb<3, uint_32>;
originMd5?: pb<10, string>;
afterCompressMd5?: pb<11, string>;
afterCompressFileSize?: pb<12, string>;
afterCompressWeight?: pb<13, string>;
afterCompressHeight?: pb<14, string>;
isCut?: pb<15, bool>;
}
export interface ImageOcrReq {
version?: pb<1, uint_32>;
client?: pb<2, uint_32>;
entrance?: pb<3, uint_32>;
ocrReqBody?: pb<10, OcrReqBody>;
}
export interface OcrCoordinate {
x?: pb<1, int_32>;
y?: pb<2, int_32>;
}
export interface OcrPolygon {
coordinates?: pb_repeated<1, OcrCoordinate>;
}
export interface OcrTextDetection {
detectedText?: pb<1, string>;
confidence?: pb<2, uint_32>;
polygon?: pb<3, OcrPolygon>;
advancedInfo?: pb<4, string>;
}
export interface OcrRspBody {
textDetections?: pb_repeated<1, OcrTextDetection>;
language?: pb<2, string>;
requestId?: pb<3, string>;
}
export interface ImageOcrResp {
retCode?: pb<1, int_32>;
errMsg?: pb<2, string>;
wording?: pb<3, string>;
ocrRspBody?: pb<10, OcrRspBody>;
}
@@ -0,0 +1,60 @@
// 0x9154_1 — QQ system face / emoji catalog protobuf shapes.
import type { pb, pb_repeated, int_32, bool } from '@snowluma/proton';
export interface OidbFaceResourceUrl {
baseUrl?: pb<1, string>;
advUrl?: pb<2, string>;
}
export interface OidbFaceEmoji {
qSid?: pb<1, string>;
qDes?: pb<2, string>;
emCode?: pb<3, string>;
qCid?: pb<4, int_32>;
aniStickerType?: pb<5, int_32>;
aniStickerPackId?: pb<6, int_32>;
aniStickerId?: pb<7, int_32>;
url?: pb<8, OidbFaceResourceUrl>;
emojiNameAlias?: pb_repeated<9, string>;
unknown10?: pb<10, int_32>;
aniStickerWidth?: pb<13, int_32>;
aniStickerHeight?: pb<14, int_32>;
}
export interface OidbFaceEmojiList {
emojiPackName?: pb<1, string>;
emojiDetail?: pb_repeated<2, OidbFaceEmoji>;
}
export interface OidbFaceContent {
emojiList?: pb_repeated<1, OidbFaceEmojiList>;
resourceUrl?: pb<2, OidbFaceResourceUrl>;
}
export interface OidbFetchSysFacesExpInfo {
field1?: pb<1, string>;
}
export interface OidbFetchSysFacesReq {
field1?: pb<1, int_32>;
field2?: pb<2, int_32>;
field3?: pb<3, int_32>;
field4?: pb<4, OidbFetchSysFacesExpInfo>;
}
export interface OidbFetchSysFacesResp {
field1?: pb<1, int_32>;
commonFace?: pb<2, OidbFaceContent>;
specialBigFace?: pb<3, OidbFaceContent>;
specialMagicFace?: pb<4, OidbFaceContent>;
}
export interface OidbFetchSysFaceReq {
qSid?: pb<1, string>;
}
export interface OidbFetchSysFaceResp {
emoji?: pb<1, OidbFaceEmoji>;
field2?: pb<2, bool>;
}