mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-15 21:50:45 +08:00
feat: support inbound files across all channels
This commit is contained in:
parent
29bcb67a64
commit
0a26d7df92
45 changed files with 2651 additions and 343 deletions
|
|
@ -196,6 +196,47 @@ export function extractWeixinImages(message, { fetchImpl = fetch } = {}) {
|
|||
return images;
|
||||
}
|
||||
|
||||
async function fetchWeixinFileCiphertext(url, { fetchImpl, signal }) {
|
||||
const response = await fetchImpl(new URL(url), {
|
||||
method: 'GET',
|
||||
signal,
|
||||
redirect: 'manual',
|
||||
});
|
||||
if (!response?.ok) {
|
||||
await response?.body?.cancel?.().catch?.(() => undefined);
|
||||
throw new WeixinApiError(
|
||||
'file-download-failed',
|
||||
`微信文件下载失败(HTTP ${response?.status ?? 'unknown'})。`,
|
||||
{ status: response?.status },
|
||||
);
|
||||
}
|
||||
return Buffer.from(await response.arrayBuffer());
|
||||
}
|
||||
|
||||
/** Convert native iLink file items into lazily downloaded, decrypted file references. */
|
||||
export function extractWeixinFiles(message, { fetchImpl = fetch } = {}) {
|
||||
if (typeof fetchImpl !== 'function') throw new TypeError('fetchImpl must be a function');
|
||||
const files = [];
|
||||
for (const item of message?.item_list ?? []) {
|
||||
const fileItem = item?.file_item;
|
||||
if (!fileItem || typeof fileItem !== 'object') continue;
|
||||
const declaredSize = Number(fileItem.len);
|
||||
files.push({
|
||||
name: nonEmptyString(fileItem.file_name) ?? (files.length === 0 ? 'file' : `file-${files.length + 1}`),
|
||||
...(Number.isFinite(declaredSize) && declaredSize >= 0 ? { size: declaredSize } : {}),
|
||||
load: async ({ signal } = {}) => {
|
||||
signal?.throwIfAborted();
|
||||
const key = parseWeixinImageAesKey(fileItem);
|
||||
const url = weixinImageDownloadUrl(fileItem.media);
|
||||
const ciphertext = await fetchWeixinFileCiphertext(url, { fetchImpl, signal });
|
||||
signal?.throwIfAborted();
|
||||
return decryptWeixinImage(ciphertext, key);
|
||||
},
|
||||
});
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function isWeixinHost(hostname) {
|
||||
const normalized = hostname.toLowerCase().replace(/\.$/, '');
|
||||
return normalized === 'weixin.qq.com' || normalized.endsWith('.weixin.qq.com');
|
||||
|
|
@ -421,6 +462,10 @@ export function createWeixinApi({ fetchImpl = fetch } = {}) {
|
|||
return extractWeixinImages(message, { fetchImpl });
|
||||
},
|
||||
|
||||
inboundFiles(message) {
|
||||
return extractWeixinFiles(message, { fetchImpl });
|
||||
},
|
||||
|
||||
async beginLogin({ localTokens = [], botType = DEFAULT_BOT_TYPE, signal } = {}) {
|
||||
const tokens = [...new Set(localTokens.map(nonEmptyString).filter(Boolean))].slice(-10);
|
||||
const response = await requestJson(fetchImpl, {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue