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

+93
View File
@@ -0,0 +1,93 @@
{
"license": "SEE LICENSE IN LICENSE",
"name": "@snowluma/proto-defs",
"version": "2.4.2",
"private": true,
"type": "module",
"description": "SnowLuma QQ/Lagrange wire-protobuf type definitions. Pure TypeScript interfaces with proton's `pb<N, T>` marker types — `@snowluma/proton`'s vite plugin compiles them into zero-runtime encoder/decoder functions at the call sites in `@snowluma/core` (and any downstream consumer). Exposed as per-file subpaths (`@snowluma/proto-defs/oidb`, `@snowluma/proto-defs/message`, …) so the import-site shape mirrors the legacy `bridge/proto/proton/*` layout and avoids name collisions across files (notably `highway.ts` and `oidb-actions/media.ts` share NTV2-prefixed names).",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
},
"./action": {
"types": "./src/action.ts",
"default": "./src/action.ts"
},
"./collection": {
"types": "./src/collection.ts",
"default": "./src/collection.ts"
},
"./qzone": {
"types": "./src/qzone.ts",
"default": "./src/qzone.ts"
},
"./dataline": {
"types": "./src/dataline.ts",
"default": "./src/dataline.ts"
},
"./element": {
"types": "./src/element.ts",
"default": "./src/element.ts"
},
"./highway": {
"types": "./src/highway.ts",
"default": "./src/highway.ts"
},
"./longmsg": {
"types": "./src/longmsg.ts",
"default": "./src/longmsg.ts"
},
"./message": {
"types": "./src/message.ts",
"default": "./src/message.ts"
},
"./get-group-msg": {
"types": "./src/get-group-msg.ts",
"default": "./src/get-group-msg.ts"
},
"./get-c2c-msg": {
"types": "./src/get-c2c-msg.ts",
"default": "./src/get-c2c-msg.ts"
},
"./qidian-corp": {
"types": "./src/qidian-corp.ts",
"default": "./src/qidian-corp.ts"
},
"./notify": {
"types": "./src/notify.ts",
"default": "./src/notify.ts"
},
"./ptt-trans": {
"types": "./src/ptt-trans.ts",
"default": "./src/ptt-trans.ts"
},
"./oidb": {
"types": "./src/oidb.ts",
"default": "./src/oidb.ts"
},
"./oidb-action": {
"types": "./src/oidb-action.ts",
"default": "./src/oidb-action.ts"
},
"./oidb-actions/*": {
"types": "./src/oidb-actions/*.ts",
"default": "./src/oidb-actions/*.ts"
}
},
"scripts": {
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@snowluma/proton": "workspace:*"
},
"devDependencies": {
"@types/node": "catalog:",
"typescript": "catalog:"
},
"engines": {
"node": "^24.22.1 || >=26.12.1"
}
}
+136
View File
@@ -0,0 +1,136 @@
import type { pb, pb_repeated, int_32, uint_32, uint_64, bytes } from '@snowluma/proton';
import type { Elem } from './element';
// Routing
export interface RoutingC2C {
uin?: pb<1, uint_32>;
uid?: pb<2, string>;
}
export interface RoutingGroup {
groupCode?: pb<1, uint_64>;
}
// 服务器限制 C2C 文件消息必须使用 Trans0x211 路由头(RoutingHead 字段 15)
// 若误用常规 RoutingC2C 会被服务器拒绝。
// 仅在包含 FileEntity 时触发,且 CcCmd 固定为 4。
export interface RoutingTrans0x211 {
toUin?: pb<1, uint_64>;
ccCmd?: pb<2, uint_32>;
uid?: pb<8, string>;
}
// Group temp-session routing head — RoutingHead field 3. Inner shape
// { 群号 (source group), 对端 uid }.
export interface RoutingGrpTmp {
groupUin?: pb<3, uint_64>;
toUid?: pb<4, string>;
}
export interface RoutingHead {
c2c?: pb<1, RoutingC2C>;
grp?: pb<2, RoutingGroup>;
grpTmp?: pb<3, RoutingGrpTmp>;
trans0x211?: pb<15, RoutingTrans0x211>;
}
// Content Head for send
export interface SendContentHead {
type?: pb<1, uint_32>;
subType?: pb<2, uint_32>;
c2cCmd?: pb<3, uint_32>;
}
// Message Control
export interface MessageControl {
msgFlag?: pb<1, int_32>;
}
// RichText for outgoing message elements.
export interface SendRichText {
elems?: pb_repeated<2, Elem>;
}
// MessageBody (for send)
export interface SendMessageBody<TRichText = SendRichText> {
richText?: pb<1, TRichText>;
// C2C 离线文件元数据(NotOnlineFile)必须序列化后塞入此字段
// 服务器不会读取 richText.notOnlineFile。
msgContent?: pb<2, bytes>;
}
// SendMessageRequest
export interface SendMessageRequest<TRichText = SendRichText> {
routingHead?: pb<1, RoutingHead>;
contentHead?: pb<2, SendContentHead>;
messageBody?: pb<3, SendMessageBody<TRichText>>;
clientSequence?: pb<4, uint_32>;
random?: pb<5, uint_32>;
syncCookie?: pb<6, bytes>;
via?: pb<8, uint_32>;
dataStatist?: pb<9, uint_32>;
ctrl?: pb<12, MessageControl>;
multiSendSeq?: pb<14, uint_32>;
}
// SendMessageResponse
export interface SendMessageResponse {
result?: pb<1, int_32>;
errMsg?: pb<2, string>;
timestamp1?: pb<3, uint_32>;
reviewResult?: pb<6, int_32>;
serverResult?: pb<7, SendMessageServerResult>;
field10?: pb<10, uint_32>;
groupSequence?: pb<11, uint_32>;
timestamp2?: pb<12, uint_32>;
privateSequence?: pb<14, uint_32>;
}
export interface SendMessageServerResult {
type?: pb<1, int_32>;
code?: pb<2, int_32>;
message?: pb<3, string>;
}
// MentionExtra (for building @mention text element)
export interface MentionExtraSend {
type?: pb<3, int_32>;
uin?: pb<4, uint_32>;
field5?: pb<5, int_32>;
uid?: pb<9, string>;
}
// Markdown commonElem (svc=45). Official DecodeMarkdownElement reads
// content / process_msg / summary / extType / mdExtInfo.
// extType=1 dispatches to DecodeMdExtInfoFileTransfer.
/** extInfo.thumbnail — cover download info, not raw image bytes. */
export interface MarkdownFlashTransferThumbUrl {
field1?: pb<1, uint_32>;
downloadUrl?: pb<2, string>;
}
export interface MarkdownFlashTransferThumb {
fileId?: pb<1, string>;
download?: pb<2, MarkdownFlashTransferThumbUrl>;
sha1?: pb<3, string>;
field4?: pb<4, uint_32>;
}
export interface MarkdownFlashTransferExt {
filesetId?: pb<1, string>;
name?: pb<2, string>;
fileSize?: pb<3, uint_32>;
thumbnail?: pb<4, MarkdownFlashTransferThumb>;
expireTime?: pb<6, uint_32>;
}
export interface MarkdownData {
content?: pb<1, string>;
field2?: pb<2, string>;
field3?: pb<3, bytes>;
processMsg?: pb<4, string>;
summary?: pb<5, string>;
extType?: pb<6, uint_32>;
extInfo?: pb<7, MarkdownFlashTransferExt>;
}
+200
View File
@@ -0,0 +1,200 @@
import type {
bool,
bytes,
double,
int_32,
pb,
pb_repeated,
uint_32,
uint_64,
} from '@snowluma/proton';
/** Weiyun collector common request header. */
export interface CollectionRequestHead {
uin?: pb<1, uint_64>;
sequence?: pb<2, uint_32>;
commandType?: pb<3, uint_32>;
operationId?: pb<4, uint_32>;
clientVersion?: pb<5, uint_64>;
platform?: pb<6, uint_32>;
ticketType?: pb<7, uint_32>;
reserved?: pb<10, uint_32>;
ticket?: pb<11, string>;
field14?: pb<14, uint_32>;
field15?: pb<15, uint_32>;
}
/** Weiyun collector common response header. */
export interface CollectionResponseHead {
retCode?: pb<101, int_32>;
retMsg?: pb<102, string>;
promptMsg?: pb<103, string>;
}
export interface GetCollectionListRequest {
field1?: pb<1, uint_32>;
field2?: pb<2, uint_32>;
field3?: pb<3, uint_32>;
timeStamp?: pb<4, uint_64>;
orderType?: pb<5, uint_32>;
groupId?: pb<6, uint_64>;
count?: pb<7, uint_32>;
searchDown?: pb<8, uint_32>;
field9?: pb<9, uint_32>;
}
export interface CollectionRequestOperation {
getCollectionList?: pb<20000, GetCollectionListRequest>;
}
export interface CollectionRequestBody {
operation?: pb<1, CollectionRequestOperation>;
}
export interface CollectionAuthor {
type?: pb<1, uint_32>;
numId?: pb<2, uint_64>;
strId?: pb<3, string>;
groupId?: pb<4, uint_64>;
groupName?: pb<5, string>;
uid?: pb<6, string>;
}
export interface CollectionPictureInfo {
url?: pb<1, string>;
field2?: pb<2, bytes>;
field3?: pb<3, bytes>;
field4?: pb<4, string>;
field5?: pb<5, string>;
field6?: pb<6, uint_32>;
field7?: pb<7, uint_32>;
field8?: pb<8, uint_32>;
field9?: pb<9, uint_32>;
author?: pb<10, CollectionAuthor>;
field11?: pb<11, string>;
}
export interface CollectionFileInfo {
field1?: pb<1, uint_32>;
field2?: pb<2, uint_64>;
field3?: pb<3, uint_32>;
field4?: pb<4, string>;
field5?: pb<5, string>;
field6?: pb<6, uint_64>;
field7?: pb<7, bytes>;
field8?: pb<8, bytes>;
field9?: pb<9, uint_32>;
field10?: pb<10, string>;
}
export interface CollectionTextSummary {
text?: pb<1, string>;
truncated?: pb<2, bool>;
}
export interface CollectionRichMediaSummary {
title?: pb<1, string>;
subTitle?: pb<2, string>;
brief?: pb<3, string>;
picList?: pb_repeated<4, CollectionPictureInfo>;
contentType?: pb<5, uint_32>;
originalUri?: pb<6, string>;
publisher?: pb<7, string>;
richMediaVersion?: pb<8, uint_32>;
}
export interface CollectionGallerySummary {
picList?: pb_repeated<1, CollectionPictureInfo>;
field2?: pb<2, string>;
}
export interface CollectionAudioSummary {
field1?: pb<1, uint_32>;
field2?: pb<2, string>;
field3?: pb<3, string>;
}
export interface CollectionVideoSummary {
field1?: pb<1, string>;
field2?: pb<2, uint_32>;
field3?: pb<3, uint_32>;
field10?: pb<10, uint_32>;
field20?: pb<20, uint_32>;
picture?: pb<21, CollectionPictureInfo>;
field30?: pb<30, uint_32>;
file?: pb<31, CollectionFileInfo>;
}
export interface CollectionFileSummary {
first?: pb<1, CollectionFileInfo>;
second?: pb<2, CollectionFileInfo>;
}
export interface CollectionLocationSummary {
name?: pb<1, string>;
latitude?: pb<2, double>;
longitude?: pb<3, double>;
altitude?: pb<4, double>;
field5?: pb<5, string>;
field6?: pb<6, string>;
}
export interface CollectionLinkSummary {
url?: pb<1, string>;
title?: pb<2, string>;
publisher?: pb<3, string>;
brief?: pb<4, string>;
picList?: pb_repeated<5, CollectionPictureInfo>;
contentType?: pb<6, uint_32>;
field7?: pb<7, string>;
}
export interface CollectionSummary {
textSummary?: pb<1, CollectionTextSummary>;
linkSummary?: pb<2, CollectionLinkSummary>;
gallerySummary?: pb<3, CollectionGallerySummary>;
audioSummary?: pb<4, CollectionAudioSummary>;
videoSummary?: pb<5, CollectionVideoSummary>;
fileSummary?: pb<6, CollectionFileSummary>;
locationSummary?: pb<7, CollectionLocationSummary>;
richMediaSummary?: pb<8, CollectionRichMediaSummary>;
}
export interface CollectionItem {
cid?: pb<1, string>;
type?: pb<2, uint_32>;
status?: pb<3, uint_32>;
author?: pb<4, CollectionAuthor>;
bid?: pb<5, uint_32>;
field6?: pb<6, uint_32>;
field7?: pb<7, string>;
category?: pb<8, uint_32>;
createTime?: pb<9, uint_64>;
collectTime?: pb<10, uint_64>;
modifyTime?: pb<11, uint_64>;
sequence?: pb<12, uint_64>;
field13?: pb<13, string>;
field14?: pb_repeated<14, string>;
summary?: pb<15, CollectionSummary>;
field16?: pb<16, bool>;
field17?: pb<17, uint_64>;
shareUrl?: pb<18, string>;
field19?: pb<19, uint_32>;
customGroupId?: pb<20, uint_32>;
securityBeat?: pb<21, bool>;
field506?: pb<506, string>;
}
export interface GetCollectionListResponse {
items?: pb_repeated<1, CollectionItem>;
totalCount?: pb<2, uint_32>;
reachedBottom?: pb<3, uint_32>;
}
export interface CollectionResponseOperation {
getCollectionList?: pb<20000, GetCollectionListResponse>;
}
export interface CollectionResponseBody {
operation?: pb<2, CollectionResponseOperation>;
}
+50
View File
@@ -0,0 +1,50 @@
import type { pb, pb_repeated, uint_32, uint_64, bytes } from '@snowluma/proton';
/** Header identifying the source/destination device on a my-device chat. */
export interface DatalineMsgHeader {
srcAppId?: pb<1, uint_32>;
srcInstId?: pb<2, uint_32>;
dstAppId?: pb<3, uint_32>;
dstInstId?: pb<4, uint_32>;
dstUin?: pb<5, uint_64>;
srcUin?: pb<6, uint_64>;
srcUinType?: pb<7, uint_32>;
dstUinType?: pb<8, uint_32>;
srcTerType?: pb<9, uint_32>;
dstTerType?: pb<10, uint_32>;
}
/** File notify carried on a my-device transfer. */
export interface DatalineFtnNotify {
sessionId?: pb<1, uint_64>;
fileName?: pb<2, string>;
fileIndex?: pb<3, string>;
fileMd5?: pb<4, bytes>;
fileKey?: pb<5, string>;
fileLen?: pb<6, uint_64>;
}
/** Generic my-device command; text lives in `buf`. */
export interface DatalineGenericSubCmd {
sessionId?: pb<1, uint_64>;
size?: pb<2, uint_32>;
index?: pb<3, uint_32>;
type?: pb<4, uint_32>;
buf?: pb<5, bytes>;
}
export interface DatalineTextItem {
type?: pb<1, uint_32>;
text?: pb<2, bytes>;
}
export interface DatalineTextMsg {
items?: pb_repeated<1, DatalineTextItem>;
}
export interface DatalineMsgBody {
subCmd?: pb<1, uint_32>;
header?: pb<2, DatalineMsgHeader>;
ftn?: pb_repeated<3, DatalineFtnNotify>;
generic?: pb<6, DatalineGenericSubCmd>;
}
+492
View File
@@ -0,0 +1,492 @@
import type { pb, pb_repeated, int_32, uint_32, uint_64, bool, bytes } from '@snowluma/proton';
// Nested / helper schemas
export interface CustomFacePbReserve {
subType?: pb<1, int_32>;
summary?: pb<9, string>;
}
export interface NotOnlineImagePbReserve2 {
field1?: pb<1, int_32>;
field2?: pb<2, string>;
field3?: pb<3, int_32>;
field4?: pb<4, int_32>;
field5?: pb<5, int_32>;
field7?: pb<7, string>;
}
export interface NotOnlineImagePbReserve {
subType?: pb<1, int_32>;
field3?: pb<3, int_32>;
field4?: pb<4, int_32>;
summary?: pb<8, string>;
field10?: pb<10, int_32>;
field20?: pb<20, NotOnlineImagePbReserve2>;
url?: pb<30, string>;
md5Str?: pb<31, string>;
}
// Core element schemas
export interface TextElem {
str?: pb<1, string>;
link?: pb<2, string>;
attr6Buf?: pb<3, bytes>;
attr7Buf?: pb<4, bytes>;
buf?: pb<11, bytes>;
pbReserve?: pb<12, bytes>;
}
export interface FaceElem {
index?: pb<1, int_32>;
oldData?: pb<2, bytes>;
buf?: pb<11, bytes>;
}
export interface OnlineImage {
guid?: pb<1, bytes>;
filePath?: pb<2, bytes>;
oldVerSendFile?: pb<3, bytes>;
}
export interface NotOnlineImage {
filePath?: pb<1, string>;
fileLen?: pb<2, uint_32>;
downloadPath?: pb<3, string>;
oldVerSendFile?: pb<4, bytes>;
imgType?: pb<5, int_32>;
previewsImage?: pb<6, bytes>;
picMd5?: pb<7, bytes>;
picHeight?: pb<8, uint_32>;
picWidth?: pb<9, uint_32>;
resId?: pb<10, string>;
flag?: pb<11, bytes>;
thumbUrl?: pb<12, string>;
original?: pb<13, int_32>;
bigUrl?: pb<14, string>;
origUrl?: pb<15, string>;
bizType?: pb<16, int_32>;
result?: pb<17, int_32>;
index?: pb<18, int_32>;
opFaceBuf?: pb<19, bytes>;
oldPicMd5?: pb<20, bool>;
thumbWidth?: pb<21, int_32>;
thumbHeight?: pb<22, int_32>;
fileId?: pb<23, int_32>;
showLen?: pb<24, uint_32>;
downloadLen?: pb<25, uint_32>;
x400Url?: pb<26, string>;
x400Width?: pb<27, int_32>;
x400Height?: pb<28, int_32>;
pbRes?: pb<29, NotOnlineImagePbReserve>;
}
export interface TransElem {
elemType?: pb<1, int_32>;
elemValue?: pb<2, bytes>;
}
// MarketFace.pbReserve(13) inner message. NapCat/Lagrange set field8=1 on
// send (marks the sticker as a "magic"/animated face). We encode it via
// protobuf_encode<MarketFacePbReserve> into the bytes field — same pattern as
// the mention pbReserve in element-builder.makeMentionElem.
export interface MarketFacePbReserve {
field8?: pb<8, uint_32>;
}
export interface MarketFace {
faceName?: pb<1, string>;
itemType?: pb<2, uint_32>;
faceInfo?: pb<3, uint_32>;
faceId?: pb<4, bytes>;
tabId?: pb<5, uint_32>;
subType?: pb<6, uint_32>;
// `key` is a short ASCII token (e.g. "0"); Lagrange.Core and NapCat both
// type it as a string. Kept as string so it round-trips verbatim through
// the receive→`emoji_id` marker→re-send path without a bytes/utf8 hop.
key?: pb<7, string>;
param?: pb<8, bytes>;
mediaType?: pb<9, uint_32>;
imageWidth?: pb<10, uint_32>;
imageHeight?: pb<11, uint_32>;
mobileParam?: pb<12, bytes>;
pbReserve?: pb<13, bytes>;
}
export interface CustomFace {
guid?: pb<1, bytes>;
filePath?: pb<2, string>;
shortcut?: pb<3, string>;
buffer?: pb<4, bytes>;
flag?: pb<5, bytes>;
oldData?: pb<6, bytes>;
fileId?: pb<7, uint_32>;
serverIp?: pb<8, int_32>;
serverPort?: pb<9, int_32>;
fileType?: pb<10, int_32>;
signature?: pb<11, bytes>;
useful?: pb<12, int_32>;
md5?: pb<13, bytes>;
thumbUrl?: pb<14, string>;
bigUrl?: pb<15, string>;
origUrl?: pb<16, string>;
bizType?: pb<17, int_32>;
repeatIndex?: pb<18, int_32>;
repeatImage?: pb<19, int_32>;
imageType?: pb<20, int_32>;
index?: pb<21, int_32>;
width?: pb<22, int_32>;
height?: pb<23, int_32>;
source?: pb<24, int_32>;
size?: pb<25, uint_32>;
origin?: pb<26, int_32>;
thumbWidth?: pb<27, int_32>;
thumbHeight?: pb<28, int_32>;
showLen?: pb<29, int_32>;
downloadLen?: pb<30, int_32>;
x400Url?: pb<31, string>;
x400Width?: pb<32, int_32>;
x400Height?: pb<33, int_32>;
pbRes?: pb<34, CustomFacePbReserve>;
}
export interface RichMsg {
template1?: pb<1, bytes>;
serviceId?: pb<2, int_32>;
msgResId?: pb<3, bytes>;
rand?: pb<4, int_32>;
seq?: pb<5, uint_32>;
}
export interface GroupFileElem {
filename?: pb<1, string>;
fileSize?: pb<2, uint_64>;
fileId?: pb<3, string>;
batchId?: pb<4, string>;
fileKey?: pb<5, string>;
mark?: pb<6, bytes>;
sequence?: pb<7, uint_64>;
batchItemId?: pb<8, bytes>;
feedMsgTime?: pb<9, int_32>;
pbReserve?: pb<10, bytes>;
}
export interface ExtraInfo {
nick?: pb<1, bytes>;
groupCard?: pb<2, bytes>;
level?: pb<3, int_32>;
flags?: pb<4, int_32>;
groupMask?: pb<5, int_32>;
msgTailId?: pb<6, int_32>;
senderTitle?: pb<7, bytes>;
apnsTips?: pb<8, bytes>;
uin?: pb<9, uint_64>;
msgStateFlag?: pb<10, int_32>;
apnsSoundType?: pb<11, int_32>;
newGroupFlag?: pb<12, int_32>;
}
export interface VideoFile {
fileUuid?: pb<1, string>;
fileMd5?: pb<2, bytes>;
fileName?: pb<3, string>;
fileFormat?: pb<4, int_32>;
fileTime?: pb<5, int_32>;
fileSize?: pb<6, int_32>;
thumbWidth?: pb<7, int_32>;
thumbHeight?: pb<8, int_32>;
thumbFileMd5?: pb<9, bytes>;
source?: pb<10, bytes>;
thumbFileSize?: pb<11, int_32>;
busiType?: pb<12, int_32>;
fromChatType?: pb<13, int_32>;
toChatType?: pb<14, int_32>;
supportProgressive?: pb<15, bool>;
fileWidth?: pb<16, int_32>;
fileHeight?: pb<17, int_32>;
subBusiType?: pb<18, int_32>;
videoAttr?: pb<19, int_32>;
pbReserve?: pb<24, bytes>;
}
export interface SrcMsg {
origSeqs?: pb_repeated<1, uint_32>;
senderUin?: pb<2, uint_64>;
time?: pb<3, int_32>;
flag?: pb<4, int_32>;
elemsRaw?: pb_repeated<5, bytes>;
type?: pb<6, int_32>;
richMsg?: pb<7, bytes>;
pbReserve?: pb<8, bytes>;
sourceMsg?: pb<9, bytes>;
toUin?: pb<10, uint_64>;
troopName?: pb<11, bytes>;
}
// Decoded content of SrcMsg.pbReserve(8). `friendSequence` is a friendship-side
// counter observed in QQNT, not the conversation-wide C2C NT message sequence;
// private reply resolution therefore uses SrcMsg.origSeqs plus direction/time.
export interface SrcMsgPbReserve {
receiverUid?: pb<7, string>;
friendSequence?: pb<8, uint_32>;
}
export interface LightAppElem {
data?: pb<1, bytes>;
msgResid?: pb<2, bytes>;
}
export interface CommonElem {
serviceType?: pb<1, int_32>;
pbElem?: pb<2, bytes>;
businessType?: pb<3, uint_32>;
}
/** CommonElem services 46/50/51, business type 1. */
export interface InlineKeyboardPermission {
type?: pb<1, uint_32>;
specifyRoleIds?: pb_repeated<2, string>;
specifyUserIds?: pb_repeated<3, string>;
}
export interface InlineKeyboardAction {
type?: pb<1, uint_32>;
permission?: pb<2, InlineKeyboardPermission>;
clickLimit?: pb<3, uint_32>;
unsupportedTips?: pb<4, string>;
data?: pb<5, string>;
atBotShowChannelList?: pb<6, bool>;
reply?: pb<7, bool>;
enter?: pb<8, bool>;
anchor?: pb<9, uint_32>;
}
export interface InlineKeyboardRenderData {
label?: pb<1, string>;
visitedLabel?: pb<2, string>;
style?: pb<3, uint_32>;
}
export interface InlineKeyboardButton {
id?: pb<1, string>;
renderData?: pb<2, InlineKeyboardRenderData>;
action?: pb<3, InlineKeyboardAction>;
}
export interface InlineKeyboardRow {
buttons?: pb_repeated<1, InlineKeyboardButton>;
}
export interface InlineKeyboardData {
rows?: pb_repeated<1, InlineKeyboardRow>;
botAppid?: pb<2, uint_64>;
}
export interface InlineKeyboardExtra {
keyboard?: pb<1, InlineKeyboardData>;
}
/** Payload carried by CommonElem serviceType=2 for private window shakes. */
export interface PokeExtra {
type?: pb<1, uint_32>;
}
export interface GeneralFlags {
bubbleDiyTextId?: pb<1, int_32>;
groupFlagNew?: pb<2, int_32>;
uin?: pb<3, uint_64>;
longTextFlag?: pb<6, int_32>;
longTextResId?: pb<7, string>;
}
// Elem (union of all element types)
export interface Elem {
text?: pb<1, TextElem>;
face?: pb<2, FaceElem>;
onlineImage?: pb<3, OnlineImage>;
notOnlineImage?: pb<4, NotOnlineImage>;
transElem?: pb<5, TransElem>;
marketFace?: pb<6, MarketFace>;
customFace?: pb<8, CustomFace>;
richMsg?: pb<12, RichMsg>;
groupFile?: pb<13, GroupFileElem>;
extraInfo?: pb<16, ExtraInfo>;
videoFile?: pb<19, VideoFile>;
generalFlags?: pb<37, GeneralFlags>;
srcMsg?: pb<45, SrcMsg>;
lightApp?: pb<51, LightAppElem>;
commonElem?: pb<53, CommonElem>;
}
// Extra decode types (for CommonElem.pbElem sub-messages)
export interface MentionExtra {
type?: pb<3, int_32>;
uin?: pb<4, uint_32>;
field5?: pb<5, int_32>;
uid?: pb<9, string>;
}
export interface QFaceExtra {
packId?: pb<1, string>;
stickerId?: pb<2, string>;
qsid?: pb<3, int_32>;
sourceType?: pb<4, int_32>;
stickerType?: pb<5, int_32>;
resultId?: pb<6, string>;
text?: pb<7, string>;
randomType?: pb<9, int_32>;
}
export interface QSmallFaceExtra {
faceId?: pb<1, uint_32>;
preview?: pb<2, string>;
preview2?: pb<3, string>;
}
// NTQQ MsgInfo types (CommonElem service_type 48)
export interface FileType {
type?: pb<1, uint_32>;
picFormat?: pb<2, uint_32>;
videoFormat?: pb<3, uint_32>;
voiceFormat?: pb<4, uint_32>;
}
export interface FileInfo {
fileSize?: pb<1, uint_32>;
fileHash?: pb<2, string>;
fileSha1?: pb<3, string>;
fileName?: pb<4, string>;
type?: pb<5, FileType>;
width?: pb<6, uint_32>;
height?: pb<7, uint_32>;
time?: pb<8, uint_32>;
original?: pb<9, uint_32>;
}
export interface IndexNode {
info?: pb<1, FileInfo>;
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 PicUrlExtInfo {
originalParameter?: pb<1, string>;
bigParameter?: pb<2, string>;
thumbParameter?: pb<3, string>;
}
export interface PictureInfo {
urlPath?: pb<1, string>;
ext?: pb<2, PicUrlExtInfo>;
domain?: pb<3, string>;
}
export interface PicExtData {
subType?: pb<1, uint_32>;
textSummary?: pb<9, string>;
}
export interface PicExtBizInfo {
bizType?: pb<1, uint_32>;
textSummary?: pb<2, string>;
bytesPbReserveC2c?: pb<11, bytes>;
extData?: pb<12, PicExtData>;
fromScene?: pb<1001, uint_32>;
toScene?: pb<1002, uint_32>;
oldFileId?: pb<1003, uint_32>;
}
export interface VideoExtBizInfo {
fromScene?: pb<1, uint_32>;
toScene?: pb<2, uint_32>;
bytesPbReserve?: pb<3, bytes>;
}
/** Encoded payload of `PttExtBizInfo.waveform`. Count must equal amplitudes. */
export interface PttWaveform {
size?: pb<1, uint_32>;
amplitudes?: pb<2, bytes>;
}
export interface PttExtBizInfo {
srcUin?: pb<1, uint_64>;
pttScene?: pb<2, uint_32>;
pttType?: pb<3, uint_32>;
changeVoice?: pb<4, uint_32>;
waveform?: pb<5, bytes>;
autoConvertText?: pb<6, uint_32>;
bytesReserve?: pb<11, bytes>;
bytesPbReserve?: pb<12, bytes>;
bytesGeneralFlags?: pb<13, bytes>;
}
export interface ExtBizInfo {
pic?: pb<1, PicExtBizInfo>;
video?: pb<2, VideoExtBizInfo>;
ptt?: pb<3, PttExtBizInfo>;
busiType?: pb<10, uint_32>;
}
export interface C2cSource {
friendUid?: pb<2, string>;
}
export interface TroopSource {
groupUin?: pb<1, uint_32>;
}
export interface HashSum {
bytesPbReserveC2c?: pb<201, C2cSource>;
troopSource?: pb<202, TroopSource>;
}
export interface MsgInfoBody {
index?: pb<1, IndexNode>;
picture?: pb<2, PictureInfo>;
fileExist?: pb<5, bool>;
hashSum?: pb<6, HashSum>;
}
export interface MsgInfo {
msgInfoBody?: pb_repeated<1, MsgInfoBody>;
extBizInfo?: pb<2, ExtBizInfo>;
}
// 群文件附加信息 (TransElem type=24)
// 旧版 Schema 字段 5-7 错位曾导致服务器返回 result=79 并拒收消息。
// 字段 5 为 uint32 占位符。
export interface GroupFileInfo {
busId?: pb<1, uint_32>;
fileId?: pb<2, string>;
fileSize?: pb<3, uint_64>;
fileName?: pb<4, string>;
field5?: pb<5, uint_32>; // 关键:必须是 uint32 占位符,错位会导致 SHA 字节流污染此字段
fileSha?: pb<6, bytes>;
extInfoString?: pb<7, string>;
fileMd5?: pb<8, bytes>;
}
export interface GroupFileExtraInner {
info?: pb<2, GroupFileInfo>;
}
// 新版服务器强校验外层结构。缺少 field1 (Magic Tag)、fileName 或 display 槽位,消息将被直接拒绝。
export interface GroupFileExtra {
field1?: pb<1, uint_32>; // NapCat 硬编码固定为 6 (Magic Op Tag)
fileName?: pb<2, string>; // 镜像同步内部的 info.fileName
display?: pb<3, string>; // UI 预览槽位,可留空但必须声明
inner?: pb<7, GroupFileExtraInner>;
}
// 消息回执保留信息
export interface Preserve {
messageId?: pb<3, uint_64>;
senderUid?: pb<6, string>;
receiverUid?: pb<7, string>;
clientSequence?: pb<8, uint_32>;
}
+43
View File
@@ -0,0 +1,43 @@
import type { pb, pb_repeated, uint_32, int_32, bool } from '@snowluma/proton';
import type { PushMsgBody } from './message';
// trpc.msg.register_proxy.RegisterProxy.SsoGetC2cMsg — fetch private (c2c)
// message history from the server by conversation-wide NT sequence range.
// Reference: Lagrange.Core
// Internal/Service/Message/GetC2cMessageService.cs + Packets/Message/Action/
// SsoGetC2cMsg(.Response).cs. The peer is the friend's UID (not uin); the
// response carries the same `PushMsgBody` shape as a live message.
export interface SsoGetC2cMsg {
friendUid?: pb<2, string>;
startSequence?: pb<3, uint_32>;
endSequence?: pb<4, uint_32>;
}
export interface SsoGetC2cMsgResponse {
result?: pb<1, int_32>;
errorMessage?: pb<2, string>;
friendUid?: pb<4, string>;
messages?: pb_repeated<7, PushMsgBody>;
}
// trpc.msg.register_proxy.RegisterProxy.SsoGetRoamMsg — fetch private
// history before a timestamp cursor. This is QQ's latest-page C2C history
// endpoint; unlike SsoGetC2cMsg it does not require a locally known sequence.
export interface SsoGetRoamMsg {
friendUid?: pb<1, string>;
time?: pb<2, uint_32>;
random?: pb<3, uint_32>;
count?: pb<4, uint_32>;
direction?: pb<5, bool>;
}
export interface SsoGetRoamMsgResponse {
result?: pb<1, uint_32>;
errorMessage?: pb<2, string>;
friendUid?: pb<3, string>;
isComplete?: pb<4, bool>;
timestamp?: pb<5, uint_32>;
random?: pb<6, uint_32>;
messages?: pb_repeated<7, PushMsgBody>;
}
+30
View File
@@ -0,0 +1,30 @@
import type { pb, pb_repeated, uint_32, bool } from '@snowluma/proton';
import type { PushMsgBody } from './message';
// trpc.msg.register_proxy.RegisterProxy.SsoGetGroupMsg — fetch group message
// history from the server by sequence range. Reference: Lagrange.Core
// Internal/Service/Message/GetGroupMessageService.cs + Packets/Message/Action/
// SsoGetGroupMsg(.Response).cs. The response carries the same `PushMsgBody`
// shape as a live OlPush message, so the existing msg-push decoders parse it.
export interface SsoGetGroupMsgInfo {
groupUin?: pb<1, uint_32>;
startSequence?: pb<2, uint_32>;
endSequence?: pb<3, uint_32>;
}
export interface SsoGetGroupMsg {
info?: pb<1, SsoGetGroupMsgInfo>;
direction?: pb<2, bool>; // true (Lagrange hard-codes this)
}
export interface SsoGetGroupMsgResponseBody {
groupUin?: pb<3, uint_32>;
startSequence?: pb<4, uint_32>;
endSequence?: pb<5, uint_32>;
messages?: pb_repeated<6, PushMsgBody>;
}
export interface SsoGetGroupMsgResponse {
body?: pb<3, SsoGetGroupMsgResponseBody>;
}
+338
View File
@@ -0,0 +1,338 @@
import type {
pb,
pb_repeated,
int_32,
uint_32,
uint_64,
fixed_32,
bool,
bytes,
} from '@snowluma/proton';
import type {
IndexNode,
PictureInfo,
HashSum,
PicExtBizInfo,
VideoExtBizInfo,
PttExtBizInfo,
} from './element';
// DataHighwayHead
export interface DataHighwayHead {
version?: pb<1, uint_32>;
uin?: pb<2, string>;
command?: pb<3, string>;
seq?: pb<4, uint_32>;
retryTimes?: pb<5, uint_32>;
appId?: pb<6, uint_32>;
dataFlag?: pb<7, uint_32>;
commandId?: pb<8, uint_32>;
}
export interface SegHead {
serviceId?: pb<1, uint_32>;
filesize?: pb<2, uint_64>;
dataOffset?: pb<3, uint_64>;
dataLength?: pb<4, uint_32>;
retCode?: pb<5, uint_32>;
serviceTicket?: pb<6, bytes>;
flag?: pb<7, uint_32>;
md5?: pb<8, bytes>;
fileMd5?: pb<9, bytes>;
cacheAddr?: pb<10, uint_32>;
cachePort?: pb<13, uint_32>;
}
export interface LoginSigHead {
loginSigType?: pb<1, uint_32>;
appId?: pb<3, uint_32>;
}
export interface ReqDataHighwayHead {
msgBaseHead?: pb<1, DataHighwayHead>;
msgSegHead?: pb<2, SegHead>;
bytesReqExtendInfo?: pb<3, bytes>;
timestamp?: pb<4, uint_64>;
msgLoginSigHead?: pb<5, LoginSigHead>;
}
export interface RespDataHighwayHead {
msgBaseHead?: pb<1, DataHighwayHead>;
msgSegHead?: pb<2, SegHead>;
errorCode?: pb<3, uint_32>;
}
// Highway extend (NTV2RichMediaHighwayExt)
export interface HighwayDomain {
isEnable?: pb<1, bool>;
ip?: pb<2, string>;
}
export interface HighwayIPv4 {
domain?: pb<1, HighwayDomain>;
port?: pb<2, uint_32>;
}
export interface HighwayNetwork {
ipv4s?: pb_repeated<1, HighwayIPv4>;
}
export interface HighwayHash {
fileSha1?: pb_repeated<1, bytes>;
}
export interface HighwayMsgInfoBody {
index?: pb<1, IndexNode>;
picture?: pb<2, PictureInfo>;
fileExist?: pb<5, bool>;
hashSum?: pb<6, HashSum>;
}
export interface NTV2RichMediaHighwayExt {
fileUuid?: pb<1, string>;
uKey?: pb<2, string>;
network?: pb<5, HighwayNetwork>;
msgInfoBody?: pb_repeated<6, HighwayMsgInfoBody>;
blockSize?: pb<10, uint_32>;
hash?: pb<11, HighwayHash>;
}
// File upload extend (group/private file highway)
export interface FileUploadUrl {
unknown?: pb<1, int_32>;
host?: pb<2, string>;
}
export interface FileUploadHost {
url?: pb<1, FileUploadUrl>;
port?: pb<2, uint_32>;
}
export interface FileUploadHostConfig {
hosts?: pb_repeated<200, FileUploadHost>;
}
export interface FileUploadNameInfo {
fileName?: pb<100, string>;
}
export interface FileUploadClientInfo {
clientType?: pb<100, int_32>;
appId?: pb<200, string>;
terminalType?: pb<300, int_32>;
clientVer?: pb<400, string>;
unknown?: pb<600, int_32>;
}
export interface FileUploadFileEntry {
fileSize?: pb<100, uint_64>;
md5?: pb<200, bytes>;
checkKey?: pb<300, bytes>;
md5S2?: pb<400, bytes>;
fileId?: pb<600, string>;
uploadKey?: pb<700, bytes>;
}
export interface FileUploadBusiInfo {
busId?: pb<1, int_32>;
senderUin?: pb<100, uint_64>;
receiverUin?: pb<200, uint_64>;
groupCode?: pb<400, uint_64>;
}
export interface FileUploadEntry {
busiBuff?: pb<100, FileUploadBusiInfo>;
fileEntry?: pb<200, FileUploadFileEntry>;
clientInfo?: pb<300, FileUploadClientInfo>;
fileNameInfo?: pb<400, FileUploadNameInfo>;
host?: pb<500, FileUploadHostConfig>;
}
export interface FileUploadExt {
unknown1?: pb<1, int_32>;
unknown2?: pb<2, int_32>;
unknown3?: pb<3, int_32>;
entry?: pb<100, FileUploadEntry>;
unknown200?: pb<200, int_32>;
}
// Encodable MsgInfo (for embedding in message element)
export interface EncodableMediaExtBizInfo {
pic?: pb<1, PicExtBizInfo>;
video?: pb<2, VideoExtBizInfo>;
ptt?: pb<3, PttExtBizInfo>;
busiType?: pb<10, uint_32>;
}
export interface EncodableMediaMsgInfo {
msgInfoBody?: pb_repeated<1, HighwayMsgInfoBody>;
extBizInfo?: pb<2, EncodableMediaExtBizInfo>;
}
// HttpConn.0x6ff_501 (highway session)
export interface HttpConn {
field1?: pb<1, int_32>;
field2?: pb<2, int_32>;
field3?: pb<3, int_32>;
field4?: pb<4, int_32>;
field6?: pb<6, int_32>;
serviceTypes?: pb_repeated<7, uint_32>;
field9?: pb<9, int_32>;
field10?: pb<10, int_32>;
field11?: pb<11, int_32>;
ver?: pb<15, string>;
}
export interface HttpConn0x6FF501Request {
httpConn?: pb<0x501, HttpConn>;
}
export interface ServerAddr {
type?: pb<1, uint_32>;
ip?: pb<2, fixed_32>;
port?: pb<3, uint_32>;
area?: pb<4, uint_32>;
}
export interface ServerInfo {
serviceType?: pb<1, uint_32>;
serverAddrs?: pb_repeated<2, ServerAddr>;
}
export interface HttpConnResponse {
sigSession?: pb<1, bytes>;
sessionKey?: pb<2, bytes>;
serverInfos?: pb_repeated<3, ServerInfo>;
}
export interface HttpConn0x6FF501Response {
httpConn?: pb<0x501, HttpConnResponse>;
}
// NTV2 Upload Request (for OIDB 0x11C4/0x11C5)
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 NTV2UploadInfo {
fileInfo?: pb<1, NTV2FileInfo>;
subFileType?: pb<2, uint_32>;
}
// NTV2 上传请求/响应扩展信息(与消息解码结构共用,参考 bridge/proto/highway.ts)
// 必须包含 video.fromScene 和 toScene 字段,否则私聊发送视频时会被服务器返回 result=79 拒绝。
export interface NTV2ExtBizInfo {
pic?: pb<1, PicExtBizInfo>;
video?: pb<2, VideoExtBizInfo>;
ptt?: pb<3, PttExtBizInfo>;
busiType?: pb<10, uint_32>;
}
export interface NTV2UploadReq {
uploadInfo?: pb_repeated<1, NTV2UploadInfo>;
tryFastUploadCompleted?: pb<2, bool>;
srvSendMsg?: pb<3, bool>;
clientRandomId?: pb<4, uint_64>;
compatQmsgSceneType?: pb<5, uint_32>;
extBizInfo?: pb<6, NTV2ExtBizInfo>;
clientSeq?: pb<7, uint_32>;
noNeedCompatMsg?: pb<8, bool>;
}
export interface NTV2C2CUserInfo {
accountType?: pb<1, uint_32>;
targetUid?: pb<2, string>;
routingHead?: pb<3, bytes>;
}
export interface NTV2GroupInfo {
groupUin?: pb<1, uint_32>;
}
export interface NTV2UploadSceneInfo {
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 NTV2CommonHead {
requestId?: pb<1, uint_32>;
command?: pb<2, uint_32>;
}
export interface NTV2ClientMeta {
agentType?: pb<1, uint_32>;
}
export interface NTV2UploadReqHead {
common?: pb<1, NTV2CommonHead>;
scene?: pb<2, NTV2UploadSceneInfo>;
client?: pb<3, NTV2ClientMeta>;
}
export interface NTV2UploadRichMediaReq {
reqHead?: pb<1, NTV2UploadReqHead>;
upload?: pb<2, NTV2UploadReq>;
}
// NTV2 Upload Response
export interface NTV2IPv4 {
outIp?: pb<1, uint_32>;
outPort?: pb<2, uint_32>;
}
export interface NTV2SubFileInfoResp {
subType?: pb<1, uint_32>;
uKey?: pb<2, string>;
uKeyTtl?: pb<3, uint_32>;
ipv4s?: pb_repeated<4, NTV2IPv4>;
}
export interface NTV2UploadRespMsgInfo {
msgInfoBody?: pb_repeated<1, HighwayMsgInfoBody>;
extBizInfo?: pb<2, NTV2ExtBizInfo>;
}
export interface NTV2UploadRespBody {
uKey?: pb<1, string>;
uKeyTtl?: pb<2, uint_32>;
ipv4s?: pb_repeated<3, NTV2IPv4>;
msgSeq?: pb<5, uint_64>;
msgInfo?: pb<6, NTV2UploadRespMsgInfo>;
compatQMsg?: pb<8, bytes>;
subFileInfos?: pb_repeated<10, NTV2SubFileInfoResp>;
}
export interface NTV2UploadRespHead {
common?: pb<1, NTV2CommonHead>;
retCode?: pb<2, uint_32>;
message?: pb<3, string>;
}
export interface NTV2UploadRichMediaResp {
respHead?: pb<1, NTV2UploadRespHead>;
upload?: pb<2, NTV2UploadRespBody>;
}
+38
View File
@@ -0,0 +1,38 @@
// SnowLuma wire-protobuf type definitions.
//
// This barrel exists so `@snowluma/proto-defs` can be resolved by a
// plain `import {} from '@snowluma/proto-defs'` (consumers that don't
// know which sub-namespace they want, or just want to see what's
// available). The package itself is otherwise consumed via the
// per-file subpath exports declared in package.json — that's the
// recommended import shape because it mirrors the legacy
// `bridge/proto/proton/<sub>` paths 1:1 and avoids name collisions.
//
// ─── Collision note ───
//
// `highway.ts` and `oidb-actions/media.ts` each define their own
// `NTV2*` types (NTV2CommonHead, NTV2ClientMeta, NTV2C2CUserInfo,
// NTV2FileInfo, NTV2FileType, NTV2GroupInfo). The two definitions
// are NOT compatible — they describe different wire shapes for
// different protocol surfaces and happen to share the same name in
// the upstream Lagrange schema. `export *` silently drops one side
// when names collide, so a consumer that grabs an `NTV2CommonHead`
// off the barrel might end up with the wrong one. Prefer the subpath
// imports below for any NTV2-named type.
export type * from './action.js';
export type * from './collection.js';
export type * from './qzone.js';
export type * from './element.js';
// NOTE: `highway.ts` intentionally NOT re-exported here — see note above.
export type * from './longmsg.js';
export type * from './message.js';
export type * from './get-group-msg.js';
export type * from './get-c2c-msg.js';
export type * from './notify.js';
export type * from './oidb.js';
export type * from './oidb-action.js';
export type * from './oidb-actions/base.js';
export type * from './oidb-actions/group-album.js';
export type * from './oidb-actions/group-file.js';
// NOTE: `oidb-actions/media.ts` intentionally NOT re-exported — see note above.
+69
View File
@@ -0,0 +1,69 @@
import type { pb, pb_repeated, uint_32, bool, bytes } from '@snowluma/proton';
import type { PushMsgBody } from './message';
export interface LongMsgUid {
uid?: pb<2, string>;
}
export interface LongMsgSettings {
field1?: pb<1, uint_32>;
field2?: pb<2, uint_32>;
field3?: pb<3, uint_32>;
field4?: pb<4, uint_32>;
}
export interface SendLongMsgInfo {
type?: pb<1, uint_32>;
uid?: pb<2, LongMsgUid>;
groupUin?: pb<3, uint_32>;
payload?: pb<4, bytes>;
}
export interface SendLongMsgReq {
info?: pb<2, SendLongMsgInfo>;
settings?: pb<15, LongMsgSettings>;
}
export interface SendLongMsgRespResult {
resId?: pb<3, string>;
}
export interface SendLongMsgResp {
result?: pb<2, SendLongMsgRespResult>;
settings?: pb<15, LongMsgSettings>;
}
export interface RecvLongMsgInfo {
uid?: pb<1, LongMsgUid>;
resId?: pb<2, string>;
acquire?: pb<3, bool>;
}
export interface RecvLongMsgReq {
info?: pb<1, RecvLongMsgInfo>;
settings?: pb<15, LongMsgSettings>;
}
export interface RecvLongMsgRespResult {
resId?: pb<3, string>;
payload?: pb<4, bytes>;
}
export interface RecvLongMsgResp {
result?: pb<1, RecvLongMsgRespResult>;
settings?: pb<15, LongMsgSettings>;
}
// Schemas that reach into PushMsgBody
export interface LongMsgContent {
msgBody?: pb_repeated<1, PushMsgBody>;
}
export interface LongMsgAction {
actionCommand?: pb<1, string>;
actionData?: pb<2, LongMsgContent>;
}
export interface LongMsgResult {
action?: pb_repeated<2, LongMsgAction>;
}
+170
View File
@@ -0,0 +1,170 @@
import type { pb, pb_repeated, int_32, uint_32, uint_64, bytes } from '@snowluma/proton';
import type { Elem } from './element';
export interface GroupCardPart {
control?: pb<1, bytes>;
text?: pb<2, bytes>;
}
export interface RichGroupCard {
parts?: pb_repeated<1, GroupCardPart>;
}
/** Join-origin metadata used by QQ's member-add notification handler. */
export interface GroupJoinOrigin {
/** 1 names a QR-code sharer rather than an inviter. */
type?: pb<1, uint_32>;
uid?: pb<3, bytes>;
}
export interface GroupJoinExtra {
origin?: pb<1, GroupJoinOrigin>;
}
export interface GroupMessageKey {
joinExtra?: pb<4, GroupJoinExtra>;
}
// ResponseHead.Grp
export interface ResponseGrp {
groupUin?: pb<1, uint_32>;
/** Sender display name as populated by the group-history fetch (SsoGetGroupMsg).
* Empty on live OlPush pushes and merged-forward nodes — those use `memberCard`
* (field 4) instead. */
memberName?: pb<2, string>;
/** Sender display name (group card if set, else nickname) as populated on live
* group pushes AND merged-forward nodes. Verified on-target (#201): a live
* message shows "星屿"/"墨梓柒111" (the card) here while field 2 is empty, and
* forward nodes carry per-node sender names here. Lagrange labels this
* `MemberName`; we keep `memberName` for the field-2 group-history slot. */
memberCard?: pb<4, bytes>;
/** Type 2 carries the literal sender nickname. Other values may carry a RichGroupCard. */
memberCardType?: pb<5, uint_32>;
/** The real group name (Lagrange: GroupName). "测试123" on-target. */
groupName?: pb<7, string>;
messageKey?: pb<8, GroupMessageKey>;
}
export interface ResponseForward {
tempGroupUin?: pb<5, uint_32>;
friendName?: pb<6, string>;
}
// ResponseHead
export interface ResponseHead {
fromUin?: pb<1, uint_32>;
fromUid?: pb<2, string>;
type?: pb<3, uint_32>;
sigMap?: pb<4, uint_32>;
toUin?: pb<5, uint_32>;
toUid?: pb<6, string>;
forward?: pb<7, ResponseForward>;
grp?: pb<8, ResponseGrp>;
}
// ContentHead
export interface ContentHead {
msgType?: pb<1, uint_32>;
subType?: pb<2, uint_32>;
// C2C command / sub-message-type. QQ NT (msg_header_codec_helper.cc::
// DecodeRoutingHead) reads this as `c2c_cmd` and uses it to route C2C-family
// pushes (msgType 141/166/167) as system/control signals via OnRecvSysMsg
// rather than chat bubbles. A fixed set of values is excluded from the chat
// list — see `isC2cControlPush` in msg-push/blank-filter.ts.
c2cCmd?: pb<3, uint_32>;
msgId?: pb<4, uint_32>;
sequence?: pb<5, uint_32>;
timestamp?: pb<6, uint_32>;
field7?: pb<7, uint_64>;
/** QQ sets a nonzero value for automatic replies. */
autoReply?: pb<10, uint_32>;
/** Conversation-wide C2C sequence. Unlike field 5, this advances across
* messages sent by either participant and is the sequence accepted by
* SsoGetC2cMsg. It is absent on group messages. */
ntMsgSeq?: pb<11, uint_32>;
newId?: pb<12, uint_64>;
}
// Ptt (voice)
export interface Ptt {
fileType?: pb<1, uint_32>;
fileId?: pb<2, uint_64>;
fileUuid?: pb<3, bytes>;
fileMd5?: pb<4, bytes>;
fileName?: pb<5, string>;
fileSize?: pb<6, uint_32>;
groupFileKey?: pb<10, string>;
fileKey?: pb<14, bytes>;
time?: pb<19, uint_32>;
format?: pb<29, uint_32>;
}
// C2C 离线文件结构
// 1. 发送端必填:字段 9/50/55(subcmd=1, dangerEvel=0, expireTime=当前时间+7天)。
// 2. 接收端只读:核心标识槽位(Uuid/Md5/Name/Size/Hash)。
export interface NotOnlineFile {
fileType?: pb<1, uint_32>;
fileUuid?: pb<3, string>;
fileMd5?: pb<4, bytes>;
fileName?: pb<5, string>;
fileSize?: pb<6, uint_64>;
subcmd?: pb<9, uint_32>; // 发送必填:固定为 1
downloadFlag?: pb<12, uint_32>; // 2 = 对方已下载的回执,不是一条新文件消息
dangerEvel?: pb<50, uint_32>; // 发送必填:固定为 0
expireTime?: pb<55, uint_32>; // 发送必填:过期时间戳(now + 7 days)
pbReserve?: pb<56, bytes>;
fileHash?: pb<57, string>;
}
// RichText
export interface RichText {
elems?: pb_repeated<2, Elem>;
notOnlineFile?: pb<3, NotOnlineFile>;
ptt?: pb<4, Ptt>;
}
// MessageBody
export interface MessageBody {
richText?: pb<1, RichText>;
msgContent?: pb<2, bytes>;
}
// C2C 文件附加信息
// 统一使用与线上数据对齐的 NotOnlineFile 结构,修复收发双向的解析问题。
//
// `field6` 携带服务器签发的下载路由(来自 0xE37_800 finalize 响应的
// metadata)。发送 c2c 文件时缺了它,接收方点开会"文件传输失败"——
// `file` 只是元数据,真正的下载凭证在这里。字段 tag 对照 NapCat
// `message/component.ts: PrivateFileExtra / PrivateFileExtraField2`。
export interface PrivateFileExtraField2 {
field1?: pb<1, uint_32>; // ← finalize metadata.field110
fileUuid?: pb<4, string>;
fileName?: pb<5, string>;
field6?: pb<6, uint_32>; // ← finalize metadata.field3
field7?: pb<7, bytes>; // ← finalize metadata.field101
field8?: pb<8, bytes>; // ← finalize metadata.field100
timestamp1?: pb<9, uint_32>; // ← finalize metadata.timestamp1
fileHash?: pb<14, string>;
selfUid?: pb<15, string>;
destUid?: pb<16, string>;
}
export interface PrivateFileExtra {
field2?: pb<2, PrivateFileExtraField2>;
}
export interface FileExtra {
file?: pb<1, NotOnlineFile>;
field6?: pb<6, PrivateFileExtra>;
}
// PushMsgBody
export interface PushMsgBody {
responseHead?: pb<1, ResponseHead>;
contentHead?: pb<2, ContentHead>;
body?: pb<3, MessageBody>;
}
// PushMsg (top-level)
export interface PushMsg {
message?: pb<1, PushMsgBody>;
status?: pb<3, int_32>;
}
+384
View File
@@ -0,0 +1,384 @@
import type { pb, pb_repeated, int_32, uint_32, uint_64, bool, bytes } from '@snowluma/proton';
export interface OperatorField1 {
uid?: pb<1, string>;
field2?: pb<2, uint_32>;
field3?: pb<3, bytes>;
field4?: pb<4, uint_32>;
field5?: pb<5, bytes>;
}
export interface OperatorInfo {
operatorField?: pb<1, OperatorField1>;
}
export interface FriendRemarkChangedTarget {
uin?: pb<3, uint_64>;
uid?: pb<7, string>;
}
export interface FriendRemarkChangedValue {
target?: pb<1, FriendRemarkChangedTarget>;
remark?: pb<2, string>;
}
/** Event0x210 / subType 364 — server-authoritative friend remark update. */
export interface FriendRemarkChangedNotify {
change?: pb<1, FriendRemarkChangedValue>;
updateTime?: pb<3, uint_32>;
nickname?: pb<4, string>;
changeType?: pb<5, uint_32>;
}
export interface GroupChange {
groupUin?: pb<1, uint_32>;
flag?: pb<2, uint_32>;
memberUid?: pb<3, string>;
/** Shared member-change operation code; used by both increase and decrease pushes. */
decreaseType?: pb<4, uint_32>;
operatorBytes?: pb<5, bytes>;
increaseType?: pb<6, uint_32>;
field7?: pb<7, bytes>;
}
export interface GroupAdminExtra {
adminUid?: pb<1, string>;
isPromote?: pb<2, bool>;
}
export interface GroupAdminBody {
extraDisable?: pb<1, GroupAdminExtra>;
extraEnable?: pb<2, GroupAdminExtra>;
}
export interface GroupAdmin {
groupUin?: pb<1, uint_32>;
flag?: pb<2, uint_32>;
isPromote?: pb<3, bool>;
body?: pb<4, GroupAdminBody>;
}
export interface InvitationInner {
groupUin?: pb<1, uint_32>;
field2?: pb<2, uint_32>;
field3?: pb<3, uint_32>;
field4?: pb<4, uint_32>;
targetUid?: pb<5, string>;
invitorUid?: pb<6, string>;
field7?: pb<7, uint_32>;
field9?: pb<9, uint_32>;
field10?: pb<10, bytes>;
field11?: pb<11, uint_32>;
field12?: pb<12, string>;
}
export interface InvitationInfo {
inner?: pb<1, InvitationInner>;
}
export interface GroupInvitation {
cmd?: pb<1, int_32>;
info?: pb<2, InvitationInfo>;
}
export interface GroupInvite {
groupUin?: pb<1, uint_32>;
field2?: pb<2, uint_32>;
field3?: pb<3, uint_32>;
field4?: pb<4, uint_32>;
invitorUid?: pb<5, string>;
hashes?: pb<6, bytes>;
}
export interface GroupJoin {
groupUin?: pb<1, uint_32>;
field2?: pb<2, uint_32>;
targetUid?: pb<3, string>;
field4?: pb<4, uint_32>;
field6?: pb<6, uint_32>;
field7?: pb<7, string>;
field8?: pb<8, uint_32>;
field9?: pb<9, bytes>;
}
export interface FriendRequestInfo {
targetUid?: pb<1, string>;
sourceUid?: pb<2, string>;
newSource?: pb<5, string>;
message?: pb<10, string>;
source?: pb<11, string>;
}
export interface FriendRequest {
info?: pb<1, FriendRequestInfo>;
}
/**
* Online-device snapshot carried by Event 0x210 / subType 349.
*
* Field numbers were recovered from QQ NT's OnRecvOnLineDev decoder. The
* outer four fields describe the current client, while field 5 contains the
* online-device entries exposed by getOnLineDev's kernel cache.
*/
export interface OnlineDeviceNotifyItem {
appId?: pb<1, uint_32>;
instanceId?: pb<2, uint_32>;
/** Packed terminal value; QQ normalizes it to the low byte before use. */
clientType?: pb<3, uint_32>;
platform?: pb<4, uint_32>;
deviceName?: pb<5, string>;
}
export interface OnlineDeviceNotify {
appId?: pb<1, uint_32>;
instanceId?: pb<2, uint_32>;
clientType?: pb<3, uint_32>;
platform?: pb<4, uint_32>;
devices?: pb_repeated<5, OnlineDeviceNotifyItem>;
}
export interface FriendRecallTipInfo {
tip?: pb<2, string>;
}
export interface FriendRecallInfo {
fromUid?: pb<1, string>;
toUid?: pb<2, string>;
clientSequence?: pb<3, uint_32>;
newId?: pb<4, uint_64>;
time?: pb<5, uint_32>;
random?: pb<6, uint_32>;
pkgNum?: pb<7, uint_32>;
pkgIndex?: pb<8, uint_32>;
divSeq?: pb<9, uint_32>;
tipInfo?: pb<13, FriendRecallTipInfo>;
}
export interface FriendRecall {
info?: pb<1, FriendRecallInfo>;
instId?: pb<2, uint_32>;
appId?: pb<3, uint_32>;
longMessageFlag?: pb<4, uint_32>;
reserved?: pb<5, bytes>;
}
// 新好友通知 (0x210 子类型 179 / 226)
// 触发场景:双向同意好友申请(179:对方同意 Bot;226:Bot 同意对方)。
export interface NewFriendInfo {
uid?: pb<1, string>;
field2?: pb<2, uint_32>;
time?: pb<3, uint_32>; // Unix 时间戳 (Wire 层为 fixed32)
message?: pb<4, string>;
nickName?: pb<5, string>;
field6?: pb<6, uint_32>;
field7?: pb<7, uint_32>;
toUid?: pb<9, string>;
}
export interface NewFriend {
field1?: pb<1, uint_32>;
info?: pb<2, NewFriendInfo>;
}
// Bot 自身进群通知 (PkgType 85)
// 触发场景:Bot 成功加入群聊(管理员通过申请 或 接受邀请进群)。
export interface SelfJoinInGroup {
groupUin?: pb<1, uint_64>;
field2?: pb<2, uint_32>;
operatorUid?: pb<3, string>; // 操作人 UID (如批准申请的管理员)
field4?: pb<4, uint_32>;
field6?: pb<6, uint_32>;
field7?: pb<7, string>;
}
export interface GroupMuteState {
targetUid?: pb<1, string>;
duration?: pb<2, uint_32>;
}
export interface GroupMuteData {
timestamp?: pb<1, uint_32>;
type?: pb<2, uint_32>;
state?: pb<3, GroupMuteState>;
}
export interface GroupMute {
groupUin?: pb<1, uint_32>;
subType?: pb<2, uint_32>;
field3?: pb<3, uint_32>;
operatorUid?: pb<4, string>;
data?: pb<5, GroupMuteData>;
}
export interface RecallMessage {
sequence?: pb<1, uint_32>;
time?: pb<2, uint_32>;
random?: pb<3, uint_32>;
type?: pb<4, uint_32>;
flag?: pb<5, uint_32>;
authorUid?: pb<6, string>;
}
export interface GroupRecallTipInfo {
tip?: pb<2, string>;
}
export interface GroupRecall {
operatorUid?: pb<1, string>;
recallMessages?: pb_repeated<3, RecallMessage>;
userDef?: pb<5, bytes>;
groupType?: pb<6, int_32>;
opType?: pb<7, int_32>;
tipInfo?: pb<9, GroupRecallTipInfo>;
}
export interface TemplParam {
name?: pb<1, string>;
value?: pb<2, string>;
}
export interface GeneralGrayTipInfo {
busiType?: pb<1, uint_64>;
busiId?: pb<2, uint_64>;
ctrlFlag?: pb<3, uint_32>;
c2cType?: pb<4, uint_32>;
serviceType?: pb<5, uint_32>;
templId?: pb<6, uint_64>;
msgTemplParam?: pb_repeated<7, TemplParam>;
content?: pb<8, string>;
}
export interface EssenceMessage {
groupUin?: pb<1, uint_32>;
msgSequence?: pb<2, uint_32>;
random?: pb<3, uint_32>;
setFlag?: pb<4, uint_32>;
memberUin?: pb<5, uint_32>;
operatorUin?: pb<6, uint_32>;
timestamp?: pb<7, uint_32>;
msgSequence2?: pb<8, uint_32>;
operatorNickname?: pb<9, string>;
memberNickname?: pb<10, string>;
setFlag2?: pb<11, uint_32>;
}
export interface NotifyMessageBody {
type?: pb<1, uint_32>;
groupUin?: pb<4, uint_32>;
eventParam?: pb<5, bytes>;
recall?: pb<11, GroupRecall>;
field13?: pb<13, uint_32>;
operatorUid?: pb<21, string>;
generalGrayTip?: pb<26, GeneralGrayTipInfo>;
essenceMessage?: pb<33, EssenceMessage>;
msgSequence?: pb<37, uint_32>;
field39?: pb<39, uint_32>;
}
// Event0x2DC subType=16: GroupMsgEmojiLike
export interface GroupReactionDataInnerDataTarget {
seq?: pb<1, uint_64>;
}
export interface GroupReactionDataContent {
code?: pb<1, string>;
count?: pb<3, uint_32>;
operatorUid?: pb<4, string>;
type?: pb<5, uint_32>;
}
export interface GroupReactionDataInnerData {
groupReactionTarget?: pb<2, GroupReactionDataInnerDataTarget>;
groupReactionDataContent?: pb<3, GroupReactionDataContent>;
}
export interface GroupReactionDataInner {
data?: pb<1, GroupReactionDataInnerData>;
}
export interface GroupReactionData {
data?: pb<1, GroupReactionDataInner>;
}
export interface GroupReactNotify {
groupUin?: pb<4, uint_64>;
field13?: pb<13, uint_32>;
groupReactionData?: pb<44, GroupReactionData>;
}
// Forced-offline ("被迫下线") push — SSO cmd
// `trpc.qq_new_tech.status_svc.StatusService.KickNT`. QQ NT's unified offline
// notification (kick / login-elsewhere / risk-control-triggered offline all
// arrive here; the reason is in the strings). Field layout RE'd from
// wrapper.linux.node (log fmt "tips_title:{} tips_content:{}") + Lagrange's
// ServiceKickNTResponse: f4 = tips_title (short title), f3 = tips_content (desc).
export interface KickNTResponse {
uin?: pb<1, uint_32>;
tips?: pb<3, string>; // tips_content — the longer description
title?: pb<4, string>; // tips_title — the short title
}
// Group-name change (Event 0x2DC subType 16, NotifyMessageBody.field13 == 12).
// Rides in `NotifyMessageBody.eventParam` (field 5). Field layout matches
// Lagrange's `GroupNameChange` (RE'd from wrapper.linux.node): only the new
// name at field 2. The operator uid is `NotifyMessageBody.operatorUid` (f21).
export interface GroupNameChange {
name?: pb<2, string>;
}
// Group special-title granted (Event 0x2DC subType 16, NotifyMessageBody.field13
// == 6). Rides in `NotifyMessageBody.eventParam` (field 5). Field layout captured
// on-wire: f2 = the gray-tip template text ("恭喜<{…}>获得群主授予的<{…"text":TITLE…}>头衔"),
// f5 = the member uin who received the title. The title text itself is embedded in
// the last `<{…}>` rich token of f2 (the kernel parses that template into a clean
// string; on the raw wire we parse it ourselves).
export interface GroupSpecialTitleChange {
tipText?: pb<2, string>;
memberUin?: pb<5, uint_32>;
}
// Profile-like ("名片赞") notify — Event 0x210 subType 39, whose body.msgContent
// decodes as this ProfileLikeTip. subType 39 is multiplexed; only inner
// msgType==0 && subType==203 is a like. Field numbers CONFIRMED byte-exact against
// real on-wire captures (detail.txt = "赞了我的资料卡N次", f3=liker uin, f5=nick);
// the like count is parsed from `detail.txt`.
export interface ProfileLikeDetail {
txt?: pb<1, string>;
uin?: pb<3, uint_64>;
nickname?: pb<5, string>;
}
export interface ProfileLikeMsg {
times?: pb<1, int_32>;
time?: pb<2, int_32>;
detail?: pb<3, ProfileLikeDetail>;
}
export interface ProfileLikeSubTip {
msg?: pb<14, ProfileLikeMsg>;
}
export interface ProfileLikeTip {
msgType?: pb<1, int_32>;
subType?: pb<2, int_32>;
content?: pb<203, ProfileLikeSubTip>;
}
// C2C input-status notify — the "对方正在输入…" push. Delivered as a system
// message (msgType 0x210 / subMsgType 0x115) whose `MsgBody.msgContent` carries
// this body. Field layout RE'd from `wrapper.linux.node`
// `aio_input_state_worker.cc::ProcessInputStateNotifySysMsg` (reads f1=fromUid,
// f2=toUid, f3=notifyItem; the item's f4 is the event type).
export interface InputStatusNotifyItem {
field2?: pb<2, uint_32>;
field3?: pb<3, uint_64>;
eventType?: pb<4, uint_32>; // 1 = 正在输入 (typing), 3 = 正在讲话 (recording voice)
field5?: pb<5, uint_32>;
statusText?: pb<6, string>;
}
export interface InputStatusNotify {
fromUid?: pb<1, string>;
toUid?: pb<2, string>;
notifyItem?: pb<3, InputStatusNotifyItem>;
}
+1
View File
@@ -0,0 +1 @@
export type * from './oidb-actions';
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>;
}
+269
View File
@@ -0,0 +1,269 @@
import type { pb, pb_repeated, int_32, uint_32, uint_64, bytes, bool } from '@snowluma/proton';
export interface OidbProperty {
key?: pb<1, string>;
value?: pb<2, bytes>;
}
export interface OidbBase<TBody> {
command?: pb<1, uint_32>;
subCommand?: pb<2, uint_32>;
errorCode?: pb<3, uint_32>;
body?: pb<4, TBody>;
errorMsg?: pb<5, string>;
properties?: pb_repeated<11, OidbProperty>;
reserved?: pb<12, int_32>;
}
export interface OidbBaseMeta {
command?: pb<1, uint_32>;
subCommand?: pb<2, uint_32>;
errorCode?: pb<3, uint_32>;
errorMsg?: pb<5, string>;
reserved?: pb<12, int_32>;
}
/** Placeholder body for OIDB cmds whose successful response carries no
* payload (only the envelope's errorCode matters). Used as the
* TResp parameter in OidbCallSpec for fire-and-forget cmds — proton
* needs a real message interface here, not `void` or `never`. */
export interface OidbEmpty {}
// Oidb.0xFD4_1 Friend list
export interface OidbFriendProperty {
code?: pb<1, uint_32>;
value?: pb<2, bytes>;
}
export interface OidbFriendNumberProperty {
key?: pb<1, uint_32>;
value?: pb<2, int_32>;
}
export interface OidbFriendLayer1 {
numberProperties?: pb_repeated<1, OidbFriendNumberProperty>;
properties?: pb_repeated<2, OidbFriendProperty>;
}
export interface OidbFriendAdditional {
type?: pb<1, uint_32>;
layer1?: pb<2, OidbFriendLayer1>;
}
export interface OidbFriend {
uid?: pb<1, string>;
customGroup?: pb<2, uint_32>;
uin?: pb<3, uint_32>;
additional?: pb_repeated<10001, OidbFriendAdditional>;
}
export interface OidbFriendCategory {
categoryId?: pb<1, uint_32>;
categoryName?: pb<2, string>;
memberCount?: pb<3, uint_32>;
sortId?: pb<4, uint_32>;
}
export interface OidbSvcTrpcTcp0xFD4_1Response {
cookie?: pb<2, bytes>;
isEnd?: pb<3, bool>;
timestamp?: pb<6, uint_64>;
selfUin?: pb<7, uint_64>;
smallSeq?: pb<8, uint_32>;
friends?: pb_repeated<101, OidbFriend>;
categories?: pb_repeated<102, OidbFriendCategory>;
}
// Oidb.0xFE5_2 Group list
export interface OidbSvcTrpcTcp0xFE5_2Member {
uid?: pb<2, string>;
}
export interface OidbSvcTrpcTcp0xFE5_2GroupInfo {
groupOwner?: pb<1, OidbSvcTrpcTcp0xFE5_2Member>;
createdTime?: pb<2, uint_32>;
memberMax?: pb<3, uint_32>;
memberCount?: pb<4, uint_32>;
groupName?: pb<5, string>;
description?: pb<18, string>;
question?: pb<19, string>;
announcement?: pb<30, string>;
// wrapper.node 3.2.32 group_codec.cc DecodeGroupInfo: tag 10 → internal
// 60027 (JS groupShutupExpireTime). Tag 31 is 60259, not the mute expire.
// 0 = off, 0xFFFFFFFF = permanent, otherwise unix-seconds expire.
shutUpAllTimestamp?: pb<10, uint_32>;
}
export interface OidbSvcTrpcTcp0xFE5_2CustomInfo {
remark?: pb<3, string>;
}
export interface OidbSvcTrpcTcp0xFE5_2Group {
groupUin?: pb<3, uint_32>;
info?: pb<4, OidbSvcTrpcTcp0xFE5_2GroupInfo>;
customInfo?: pb<5, OidbSvcTrpcTcp0xFE5_2CustomInfo>;
}
export interface OidbSvcTrpcTcp0xFE5_2Response {
groups?: pb_repeated<2, OidbSvcTrpcTcp0xFE5_2Group>;
}
// Oidb.0x88D_0 — single group detail by uin. `results` tags mirror the request
// flags. Cross-checked against
// dev/Lagrange.Core/.../Response/OidbSvcTrpcTcp0x88D_0Response.cs.
export interface OidbSvcTrpcTcp0x88D_0Results {
ownerUid?: pb<1, string>;
createTime?: pb<2, uint_64>;
maxMemberCount?: pb<5, uint_64>;
memberCount?: pb<6, uint_64>;
// DecodeSingleGroupDetailInfoByBaseFilter: tag 7 → 60205 addOption.
// SET uses a different message domain (0x89A settings tag 16).
addType?: pb<7, uint_32>;
level?: pb<10, uint_64>;
name?: pb<15, string>;
noticePreview?: pb<16, string>;
uin?: pb<21, uint_64>;
lastSequence?: pb<22, uint_64>;
lastMessageTime?: pb<23, uint_64>;
question?: pb<24, string>;
answer?: pb<25, string>;
maxAdminCount?: pb<29, uint_64>;
// wrapper.node 3.2.32 group_info_fetch_codec.cc
// DecodeSingleGroupDetailInfoByBaseFilter: tag 45 → internal 60027.
// Tag 59 is 60259, not the mute expire. Request mask tag is also 45.
shutUpAllTimestamp?: pb<45, uint_32>;
/** Complete app privilege bitfield (requested with detail flag tag 56). */
privilegeFlag?: pb<56, uint_32>;
/** New-member history-visible switch (0/1; omitted means hidden). */
groupFlagExt4?: pb<101, uint_32>;
// DecodeSingleGroupDetailInfoByBaseFilter: tag 82 → 60282, tag 83 → 60283.
// SET counterparts are 0x89A settings 35/36. 0 = that search mode is open.
noFingerOpen?: pb<82, uint_32>;
noCodeFingerOpen?: pb<83, uint_32>;
}
export interface OidbSvcTrpcTcp0x88D_0ResponseGroupInfo {
uin?: pb<1, uint_64>;
results?: pb<3, OidbSvcTrpcTcp0x88D_0Results>;
}
export interface OidbSvcTrpcTcp0x88D_0Response {
groupInfo?: pb<1, OidbSvcTrpcTcp0x88D_0ResponseGroupInfo>;
}
// Oidb.0xFE7_3 Group member list
export interface OidbSvcTrpcTcp0xFE7_3Uin {
uid?: pb<2, string>;
uin?: pb<4, uint_32>;
}
export interface OidbSvcTrpcTcp0xFE7_3Card {
/** Plain UTF-8 or a serialized RichGroupCard. */
memberCard?: pb<2, bytes>;
}
export interface OidbSvcTrpcTcp0xFE7_3Level {
infos?: pb_repeated<1, uint_32>;
level?: pb<2, uint_32>;
}
export interface OidbSvcTrpcTcp0xFE7_3Member {
uin?: pb<1, OidbSvcTrpcTcp0xFE7_3Uin>;
memberName?: pb<10, string>;
specialTitle?: pb<17, string>;
memberCard?: pb<11, OidbSvcTrpcTcp0xFE7_3Card>;
level?: pb<12, OidbSvcTrpcTcp0xFE7_3Level>;
joinTimestamp?: pb<100, uint_32>;
lastMsgTimestamp?: pb<101, uint_32>;
shutUpTimestamp?: pb<102, uint_32>;
permission?: pb<107, uint_32>;
}
export interface OidbSvcTrpcTcp0xFE7_3Response {
groupUin?: pb<1, uint_32>;
members?: pb_repeated<2, OidbSvcTrpcTcp0xFE7_3Member>;
field3?: pb<3, uint_32>;
memberChangeSeq?: pb<5, uint_32>;
memberCardChangeSeq?: pb<6, uint_32>;
token?: pb<15, string>;
}
// OIDB 0x496_0 — dynamic robot UIN ranges. QQ's native
// NodeIKernelRobotService uses this config when deciding whether a group
// member is a robot; the ranges are server-versioned and must not be
// hard-coded client-side.
export interface OidbRobotUinRange {
minUin?: pb<1, uint_64>;
maxUin?: pb<2, uint_64>;
}
export interface OidbRobotUinRangeConfig {
version?: pb<1, uint_32>;
ranges?: pb_repeated<2, OidbRobotUinRange>;
}
export interface OidbRobotUinRangeResponse {
robotConfig?: pb<5, OidbRobotUinRangeConfig>;
}
// OIDB.0x10C0 Group Request — UID-form response (envelope reserved=0).
export interface OidbSvcTrpcTcp0x10C0ResponseUser {
uid?: pb<1, string>;
name?: pb<2, string>;
}
export interface OidbSvcTrpcTcp0x10C0ResponseGroup {
groupUin?: pb<1, uint_32>;
groupName?: pb<2, string>;
}
export interface OidbSvcTrpcTcp0x10C0ResponseRequest {
sequence?: pb<1, uint_64>;
eventType?: pb<2, uint_32>;
state?: pb<3, uint_32>;
group?: pb<4, OidbSvcTrpcTcp0x10C0ResponseGroup>;
target?: pb<5, OidbSvcTrpcTcp0x10C0ResponseUser>;
invitor?: pb<6, OidbSvcTrpcTcp0x10C0ResponseUser>;
operatorUser?: pb<7, OidbSvcTrpcTcp0x10C0ResponseUser>;
field9?: pb<9, string>;
comment?: pb<10, string>;
operateTransInfo?: pb<14, bytes>;
}
export interface OidbSvcTrpcTcp0x10C0Response {
requests?: pb_repeated<1, OidbSvcTrpcTcp0x10C0ResponseRequest>;
field2?: pb<2, uint_64>;
newLatestSeq?: pb<3, uint_64>;
field4?: pb<4, uint_32>;
field5?: pb<5, uint_64>;
field6?: pb<6, uint_32>;
}
// The native UIN-form request (envelope reserved=1) uses the same response
// tags, but user field 1 changes wire type from string to uint32. Keep a
// separate schema: decoding both forms through one interface would silently
// discard every numeric account identifier as a mismatched wire type.
export interface OidbSvcTrpcTcp0x10C0ResponseUserByUin {
uin?: pb<1, uint_32>;
name?: pb<2, string>;
}
export interface OidbSvcTrpcTcp0x10C0ResponseRequestByUin {
sequence?: pb<1, uint_64>;
eventType?: pb<2, uint_32>;
state?: pb<3, uint_32>;
group?: pb<4, OidbSvcTrpcTcp0x10C0ResponseGroup>;
target?: pb<5, OidbSvcTrpcTcp0x10C0ResponseUserByUin>;
invitor?: pb<6, OidbSvcTrpcTcp0x10C0ResponseUserByUin>;
operatorUser?: pb<7, OidbSvcTrpcTcp0x10C0ResponseUserByUin>;
field9?: pb<9, string>;
comment?: pb<10, string>;
operateTransInfo?: pb<14, bytes>;
}
export interface OidbSvcTrpcTcp0x10C0ResponseByUin {
requests?: pb_repeated<1, OidbSvcTrpcTcp0x10C0ResponseRequestByUin>;
field2?: pb<2, uint_64>;
newLatestSeq?: pb<3, uint_64>;
field4?: pb<4, uint_32>;
field5?: pb<5, uint_64>;
field6?: pb<6, uint_32>;
}
+99
View File
@@ -0,0 +1,99 @@
import type { pb, uint_32, uint_64 } from '@snowluma/proton';
/**
* Voice-to-text (语音转文字) request/response protobufs for the
* `pttTrans.TransC2CPttReq` / `pttTrans.TransGroupPttReq` SSO commands.
*
* Reverse-engineered from QQ-NT `wrapper.node` (macOS 9.9.x) —
* `im_core/msg/ptt/ptt_trans_worker.cc` (`EncodeC2CPtt` / `EncodeTroopPtt`
* build the request, `OnTranslateResp` decodes the response). The wire shape
* here is RE-derived and NOT yet verified against a live server: the numeric
* field tags are read straight from the encoder, but the length-delimited
* fields (uuid / md5) and the response `text` tag should be confirmed with a
* single live capture before relying on them.
*
* Flow: the response can carry completed text inline. Otherwise, the caller
* waits for an asynchronous result correlated with the original message ID.
*/
/** Source ptt-element field (NT internal tag) noted in the comment after each. */
export interface C2CPttTransItem {
msgId?: pb<1, uint_64>;
senderUin?: pb<2, uint_64>;
receiverUin?: pb<3, uint_64>;
uuid?: pb<4, string>; // element 45503 (c2c ptt uuid)
duration?: pb<5, uint_32>; // element 45906 (seconds)
size?: pb<6, uint_32>; // element 45405 (bytes)
format?: pb<7, uint_32>; // element 45907 (voice codec)
eventType?: pb<8, uint_32>; // element 45921
// QQ NT (EncodeC2CPtt) hex-encodes the md5 to a 32-char lowercase string
// before sending it (verified in wrapper.linux.node: field 9 = sub_7A02D80
// = bytes→hex). Sending the raw 16 bytes makes the server reject with
// errCode -1079. So this is a hex STRING, not bytes.
md5?: pb<9, string>; // element 45406 (32-char lowercase hex)
}
export interface GroupPttTransItem {
msgId?: pb<1, uint_64>;
senderUin?: pb<2, uint_64>;
groupUin?: pb<3, uint_64>;
fileId?: pb<4, uint_32>; // element 45903 (group ptt numeric file id)
md5?: pb<5, string>; // element 45406 (32-char lowercase hex, see C2C note)
duration?: pb<6, uint_32>; // element 45906
size?: pb<7, uint_32>; // element 45405
format?: pb<8, uint_32>; // element 45907
uuid?: pb<9, string>; // element 45503
eventType?: pb<10, uint_32>; // element 45921
ext?: pb<11, uint_32>; // element 49202 (optional)
}
/**
* Outer request. `type` selects the scene (1 = group, 2 = c2c) and only the
* matching item is populated — group at field 2, c2c at field 3.
*/
export interface PttTransReq {
type?: pb<1, uint_32>;
groupItem?: pb<2, GroupPttTransItem>;
c2cItem?: pb<3, C2CPttTransItem>;
}
/** Per-scene result. `errCode` 0 = ok; `text` carries the recognised speech. */
export interface C2CPttTransResult {
errCode?: pb<2, uint_32>;
text?: pb<8, string>; // VERIFY LIVE: text tag from OnTranslateResp
}
export interface GroupPttTransResult {
errCode?: pb<2, uint_32>;
text?: pb<9, string>; // VERIFY LIVE: text tag from OnTranslateResp
}
/**
* Outer response. `type` echoes the scene; result is at field 2 (group) /
* field 3 (c2c). An empty `text` with `errCode == 0` means "transcribing,
* await the result push".
*/
export interface PttTransResp {
type?: pb<1, uint_32>;
groupResult?: pb<2, GroupPttTransResult>;
c2cResult?: pb<3, C2CPttTransResult>;
}
/**
* Async result push (Event 0x210 subType 61), sent after the request once the
* server finishes transcribing. **Live-verified** against a real push: the
* outer carries a single result item at field 2; the item echoes the request
* `msgId` at field 1 (the correlation key) and the recognised text at field 8.
*/
export interface PttTransPushItem {
msgId?: pb<1, uint_64>; // echoes the request msgId — correlation key
text?: pb<8, string>; // recognised text
senderUin?: pb<9, uint_64>;
receiverUin?: pb<10, uint_64>;
uuid?: pb<13, string>;
}
export interface PttTransPush {
field1?: pb<1, uint_32>;
item?: pb<2, PttTransPushItem>;
}
+18
View File
@@ -0,0 +1,18 @@
import type { pb, uint_32 } from '@snowluma/proton';
// trpc.basic.corp.Datacard.SsoCorpInfo — 企点企业资料卡信息。
// 请求:field1 = 目标账号 UIN(varint)。
// 响应字段为企点企业资料卡:名称 / 简介 / 官网 / 企业签名 / 地址 / 电话 / 邮箱。
export interface QidianCorpInfoRequest {
uin?: pb<1, uint_32>;
}
export interface QidianCorpInfoResponse {
corpName?: pb<2, string>;
intro?: pb<3, string>;
website?: pb<4, string>;
slogan?: pb<5, string>;
address?: pb<6, string>;
phone?: pb<7, string>;
email?: pb<8, string>;
}
+13
View File
@@ -0,0 +1,13 @@
import type { int_64, pb, pb_optional } from '@snowluma/proton';
/** Personal-space emotion identity carried in GetFeedDetail's base64 ugc_id. */
export interface QzoneEmotionUgcId {
appId: pb<1, int_64>;
uin: pb<2, int_64>;
publishTime: pb<3, int_64>;
// These empty fields have explicit presence in the upstream identifier.
field4: pb_optional<4, string>;
field5: pb_optional<5, string>;
field6: pb<6, string>;
tid: pb<8, string>;
}
+13
View File
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"types": ["node"],
"noEmit": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}