Доработать вложенные файлы и архив проекта

This commit is contained in:
AidarKC
2026-09-11 14:33:21 +03:00
parent f9ae811702
commit ef068eca81
22 changed files with 3865 additions and 216 deletions
+60 -7
View File
@@ -9,6 +9,7 @@ function defaultParsed(rawText = '') {
replyRef: null,
callSummary: null,
fileAttachment: null,
fileAttachments: [],
};
}
@@ -97,17 +98,46 @@ function decodeFileField(value = '') {
}
function normalizeFileAttachment(fields = {}) {
const version = Number(fields.v || 0);
const id = String(fields.id || '').trim();
const url = decodeFileField(fields.url || '').trim();
const keyB64Url = String(fields.key || '').trim();
const ivB64Url = String(fields.iv || '').trim();
const name = decodeFileField(fields.name || '').trim() || 'file';
const mime = decodeFileField(fields.mime || '').trim() || 'application/octet-stream';
const size = Number(fields.size || 0);
const encryptedSize = Number(fields.encsize || 0);
if (!id || !url || !keyB64Url || !ivB64Url || !Number.isFinite(size) || size < 0) return null;
if (!id || !url || !keyB64Url || !Number.isFinite(size) || size < 0) return null;
if (version >= 2) {
const ivPrefixB64Url = String(fields.ivp || '').trim();
if (!ivPrefixB64Url) return null;
const chunkSize = Number(fields.chunk || 0);
const chunkCount = Number(fields.chunks || 0);
const kind = String(fields.kind || 'file').trim().toLowerCase() === 'voice' ? 'voice' : 'file';
const durationMs = Math.max(0, Math.floor(Number(fields.dur || 0)));
return {
version,
id,
url,
keyB64Url,
ivPrefixB64Url,
name,
mime,
size,
encryptedSize: Number.isFinite(encryptedSize) && encryptedSize > 0 ? encryptedSize : 0,
chunkSize: Number.isFinite(chunkSize) && chunkSize > 0 ? chunkSize : 0,
chunkCount: Number.isFinite(chunkCount) && chunkCount >= 0 ? Math.floor(chunkCount) : 0,
torrentV2InfoHashB64Url: String(fields.th || '').trim(),
torrentV2PiecesRootB64Url: String(fields.pr || '').trim(),
kind,
durationMs,
};
}
const ivB64Url = String(fields.iv || '').trim();
if (!ivB64Url) return null;
return {
version: Number(fields.v || 0),
version: version || 1,
id,
url,
keyB64Url,
@@ -116,20 +146,37 @@ function normalizeFileAttachment(fields = {}) {
mime,
size,
encryptedSize: Number.isFinite(encryptedSize) && encryptedSize > 0 ? encryptedSize : 0,
kind: 'file',
durationMs: 0,
};
}
export function buildDmFileTechBlock(attachment = {}) {
const version = Math.max(1, Math.floor(Number(attachment?.version || 1)));
const id = String(attachment?.id || '').trim();
const url = String(attachment?.url || '').trim();
const keyB64Url = String(attachment?.keyB64Url || '').trim();
const ivB64Url = String(attachment?.ivB64Url || '').trim();
const size = Math.max(0, Math.floor(Number(attachment?.size || 0)));
const encryptedSize = Math.max(0, Math.floor(Number(attachment?.encryptedSize || 0)));
if (!id || !url || !keyB64Url || !ivB64Url) throw new Error('Не хватает данных для технического блока файла');
if (!id || !url || !keyB64Url) throw new Error('Не хватает данных для технического блока файла');
const name = encodeURIComponent(String(attachment?.name || 'file'));
const mime = encodeURIComponent(String(attachment?.mime || 'application/octet-stream'));
const encodedUrl = encodeURIComponent(url);
if (version >= 2) {
const ivPrefix = String(attachment?.ivPrefixB64Url || '').trim();
if (!ivPrefix) throw new Error('Не хватает IV prefix для chunked-файла');
const chunkSize = Math.max(0, Math.floor(Number(attachment?.chunkSize || 0)));
const chunkCount = Math.max(0, Math.floor(Number(attachment?.chunkCount || 0)));
const torrentHash = String(attachment?.torrentV2InfoHashB64Url || '').trim();
const piecesRoot = String(attachment?.torrentV2PiecesRootB64Url || '').trim();
const kind = String(attachment?.kind || 'file') === 'voice' ? 'voice' : 'file';
const durationMs = Math.max(0, Math.floor(Number(attachment?.durationMs || 0)));
return `<S:file;v=2;id=${id};url=${encodedUrl};key=${keyB64Url};ivp=${ivPrefix};name=${name};mime=${mime};size=${size};encsize=${encryptedSize};chunk=${chunkSize};chunks=${chunkCount};th=${torrentHash};pr=${piecesRoot};kind=${kind};dur=${durationMs}>`;
}
const ivB64Url = String(attachment?.ivB64Url || '').trim();
if (!ivB64Url) throw new Error('Не хватает IV для файла v1');
return `<S:file;v=1;id=${id};url=${encodedUrl};key=${keyB64Url};iv=${ivB64Url};name=${name};mime=${mime};size=${size};encsize=${encryptedSize}>`;
}
@@ -142,6 +189,7 @@ export function parseDmTechBlocks(rawText = '') {
let replyRef = null;
let callSummary = null;
let fileAttachment = null;
const fileAttachments = [];
while (hasTechPrefix(text, cursor)) {
const end = text.indexOf('>', cursor);
@@ -189,8 +237,12 @@ export function parseDmTechBlocks(rawText = '') {
reason: String(fields.reason || '').trim().toLowerCase(),
};
}
} else if (kind === 'file' && !fileAttachment) {
fileAttachment = normalizeFileAttachment(fields);
} else if (kind === 'file') {
const attachment = normalizeFileAttachment(fields);
if (attachment) {
fileAttachments.push(attachment);
if (!fileAttachment) fileAttachment = attachment;
}
}
cursor = end + 1;
@@ -207,5 +259,6 @@ export function parseDmTechBlocks(rawText = '') {
replyRef,
callSummary,
fileAttachment,
fileAttachments,
};
}