"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.decryptWecomMedia = decryptWecomMedia;exports.decryptWecomMediaWithHttp = decryptWecomMediaWithHttp;exports.decryptWecomMediaWithMeta = decryptWecomMediaWithMeta;var _nodeCrypto = _interopRequireDefault(require("node:crypto")); var _crypto = require("./crypto.js"); var _http = require("./http.js");function _interopRequireDefault(e) {return e && e.__esModule ? e : { default: e };} function normalizeMime(contentType) { const raw = String(contentType ?? "").trim(); if (!raw) return undefined; return raw.split(";")[0]?.trim().toLowerCase() || undefined; } function extractFilenameFromContentDisposition(disposition) { const raw = String(disposition ?? "").trim(); if (!raw) return undefined; const star = raw.match(/filename\*\s*=\s*([^;]+)/i); if (star?.[1]) { const v = star[1].trim().replace(/^UTF-8''/i, "").replace(/^"(.*)"$/, "$1"); try { const decoded = decodeURIComponent(v); if (decoded.trim()) return decoded.trim(); } catch {/* ignore */} if (v.trim()) return v.trim(); } const plain = raw.match(/filename\s*=\s*([^;]+)/i); if (plain?.[1]) { const v = plain[1].trim().replace(/^"(.*)"$/, "$1").trim(); if (v) return v; } return undefined; } /** * **decryptWecomMedia (解密企业微信媒体文件)** * * 简易封装:直接传入 URL 和 AES Key 下载并解密。 * 企业微信媒体文件使用与消息体相同的 AES-256-CBC 加密,IV 为 AES Key 前16字节。 * 解密后需移除 PKCS#7 填充。 */ async function decryptWecomMedia(url, encodingAESKey, maxBytes) { return decryptWecomMediaWithHttp(url, encodingAESKey, { maxBytes }); } /** * **decryptWecomMediaWithHttp (解密企业微信媒体 - 高级)** * * 支持传递 HTTP 选项(如 Proxy、Timeout)。 * 流程: * 1. 下载加密内容。 * 2. 准备 AES Key 和 IV。 * 3. AES-CBC 解密。 * 4. PKCS#7 去除填充。 */ async function decryptWecomMediaWithHttp( url, encodingAESKey, params) { const decrypted = await decryptWecomMediaWithMeta(url, encodingAESKey, params); return decrypted.buffer; } /** * **decryptWecomMediaWithMeta (解密企业微信媒体并返回源信息)** * * 在返回解密结果的同时,保留下载响应中的元信息(content-type / filename / final url), * 供上层更准确地推断文件后缀和 MIME。 */ async function decryptWecomMediaWithMeta( url, encodingAESKey, params) { // 1. Download encrypted content const res = await (0, _http.wecomFetch)(url, undefined, { ...params?.http, timeoutMs: params?.http?.timeoutMs ?? 15_000 }); if (!res.ok) { throw new Error(`failed to download media: ${res.status}`); } const sourceContentType = normalizeMime(res.headers.get("content-type")); const sourceFilename = extractFilenameFromContentDisposition(res.headers.get("content-disposition")); const sourceUrl = res.url || url; const encryptedData = await (0, _http.readResponseBodyAsBuffer)(res, params?.maxBytes); // 2. Prepare Key and IV const aesKey = (0, _crypto.decodeEncodingAESKey)(encodingAESKey); const iv = aesKey.subarray(0, 16); // 3. Decrypt const decipher = _nodeCrypto.default.createDecipheriv("aes-256-cbc", aesKey, iv); decipher.setAutoPadding(false); // We handle padding manually const decryptedPadded = Buffer.concat([ decipher.update(encryptedData), decipher.final()] ); // 4. Unpad // Note: Unlike msg bodies, usually removing PKCS#7 padding is enough for media files. // The Python SDK logic: pad_len = decrypted_data[-1]; decrypted_data = decrypted_data[:-pad_len] // Our pkcs7Unpad function does exactly this + validation. return { buffer: (0, _crypto.pkcs7Unpad)(decryptedPadded, _crypto.WECOM_PKCS7_BLOCK_SIZE), sourceContentType, sourceFilename, sourceUrl }; } /* v9-e3991a1d738b6b81 */