mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 04:13:17 +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
|
|
@ -27,6 +27,7 @@ import {
|
|||
import { WhatsappController } from '../../../src/channels/whatsapp/whatsapp-controller.mjs';
|
||||
import {
|
||||
WhatsappRuntime,
|
||||
createWhatsappMediaDownloader,
|
||||
normalizeWhatsappMessage,
|
||||
whatsappInboundAllowed,
|
||||
} from '../../../src/channels/whatsapp/whatsapp-runtime.mjs';
|
||||
|
|
@ -255,15 +256,21 @@ test('WhatsApp Web session accepts recent append events without replaying stale
|
|||
const root = await mkdtemp(join(tmpdir(), 'dsh-im-whatsapp-append-'));
|
||||
const events = new EventEmitter();
|
||||
const received = [];
|
||||
const contexts = [];
|
||||
const socket = {
|
||||
ev: events,
|
||||
updateMediaMessage: async () => {},
|
||||
end: async () => {},
|
||||
logout: async () => {},
|
||||
};
|
||||
const session = await createWhatsappWebSession({
|
||||
authDir: root,
|
||||
onQr: () => {},
|
||||
onMessage: async (message) => { received.push(message.key.id); },
|
||||
makeSocket: () => ({
|
||||
ev: events,
|
||||
end: async () => {},
|
||||
logout: async () => {},
|
||||
}),
|
||||
onMessage: async (message, context) => {
|
||||
received.push(message.key.id);
|
||||
contexts.push(context);
|
||||
},
|
||||
makeSocket: () => socket,
|
||||
loadAuthState: async () => ({
|
||||
state: {
|
||||
creds: {},
|
||||
|
|
@ -286,9 +293,33 @@ test('WhatsApp Web session accepts recent append events without replaying stale
|
|||
});
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
assert.deepEqual(received, ['recent', 'notify']);
|
||||
assert.equal(contexts[0].socket, socket);
|
||||
assert.equal(contexts[1].socket, socket);
|
||||
await session.close();
|
||||
});
|
||||
|
||||
test('WhatsApp media downloader supplies Baileys reupload context', async () => {
|
||||
const reuploaded = [];
|
||||
const socket = {
|
||||
updateMediaMessage: async (message) => {
|
||||
reuploaded.push(message);
|
||||
return { ...message, reuploaded: true };
|
||||
},
|
||||
};
|
||||
let receivedContext;
|
||||
const download = createWhatsappMediaDownloader({
|
||||
socket,
|
||||
logger: { info() {} },
|
||||
download: async (_message, _type, _options, context) => {
|
||||
receivedContext = context;
|
||||
return context.reuploadRequest({ key: { id: 'expired-media' } });
|
||||
},
|
||||
});
|
||||
assert.equal((await download({}, 'stream', {})).reuploaded, true);
|
||||
assert.equal(typeof receivedContext.logger.info, 'function');
|
||||
assert.deepEqual(reuploaded, [{ key: { id: 'expired-media' } }]);
|
||||
});
|
||||
|
||||
test('WhatsApp normalizes direct and explicitly mentioned group messages', () => {
|
||||
const direct = normalizeWhatsappMessage({
|
||||
key: { remoteJid: '16505550999@s.whatsapp.net', id: 'direct-1', fromMe: false },
|
||||
|
|
@ -371,7 +402,7 @@ test('WhatsApp access modes allow self-chat, selected contacts, or the existing
|
|||
}), true);
|
||||
});
|
||||
|
||||
test('WhatsApp exposes image media through a bounded Baileys download stream', async () => {
|
||||
test('WhatsApp keeps native and document images as images and exposes ordinary documents as files', async () => {
|
||||
const png = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
||||
const calls = [];
|
||||
const controller = new AbortController();
|
||||
|
|
@ -441,9 +472,52 @@ test('WhatsApp exposes image media through a bounded Baileys download stream', a
|
|||
url: 'https://mmg.whatsapp.net/document',
|
||||
},
|
||||
},
|
||||
}, ACCOUNT_JID);
|
||||
}, ACCOUNT_JID, {
|
||||
download: async (raw, type, options) => {
|
||||
calls.push({ raw, type, options });
|
||||
return {
|
||||
async *[Symbol.asyncIterator]() { yield Buffer.from('document'); },
|
||||
};
|
||||
},
|
||||
});
|
||||
assert.equal(document.images[0].name, 'diagram.webp');
|
||||
assert.equal(document.images[0].mediaType, 'image/webp');
|
||||
assert.equal(document.images[0].size, 2_000);
|
||||
assert.deepEqual(document.files, []);
|
||||
const documentController = new AbortController();
|
||||
assert.equal((await document.images[0].load({
|
||||
signal: documentController.signal,
|
||||
maxBytes: 5_000,
|
||||
})).toString(), 'document');
|
||||
assert.equal(calls[1].type, 'stream');
|
||||
assert.equal(calls[1].options.options.signal instanceof AbortSignal, true);
|
||||
|
||||
const ordinaryDocument = normalizeWhatsappMessage({
|
||||
key: { remoteJid: '16505550999@s.whatsapp.net', id: 'document-pdf-1', fromMe: false },
|
||||
message: {
|
||||
documentMessage: {
|
||||
mimetype: 'application/pdf', fileName: 'report.pdf', fileLength: 4_000,
|
||||
url: 'https://mmg.whatsapp.net/document-pdf',
|
||||
},
|
||||
},
|
||||
}, ACCOUNT_JID, {
|
||||
download: async (raw, type, options) => {
|
||||
calls.push({ raw, type, options });
|
||||
return {
|
||||
async *[Symbol.asyncIterator]() { yield Buffer.from('pdf'); },
|
||||
};
|
||||
},
|
||||
});
|
||||
assert.deepEqual(ordinaryDocument.images, []);
|
||||
assert.equal(ordinaryDocument.files[0].name, 'report.pdf');
|
||||
assert.equal(ordinaryDocument.files[0].mediaType, 'application/pdf');
|
||||
assert.equal(ordinaryDocument.files[0].size, 4_000);
|
||||
const loadedDocument = await ordinaryDocument.files[0].load({ signal: documentController.signal });
|
||||
const documentChunks = [];
|
||||
for await (const chunk of loadedDocument.stream) documentChunks.push(Buffer.from(chunk));
|
||||
assert.equal(Buffer.concat(documentChunks).toString(), 'pdf');
|
||||
assert.equal(calls[2].type, 'stream');
|
||||
assert.equal(calls[2].options.options.signal, documentController.signal);
|
||||
|
||||
for (const [index, wrapper] of [
|
||||
'viewOnceMessage',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue