fix: add gif playback for WhatsApp sends

This commit is contained in:
Peter Steinberger
2026-01-03 23:56:36 +00:00
parent e1dd764504
commit e17c038d18
13 changed files with 122 additions and 4 deletions

View File

@@ -1,9 +1,14 @@
export type ActiveWebSendOptions = {
gifPlayback?: boolean;
};
export type ActiveWebListener = {
sendMessage: (
to: string,
text: string,
mediaBuffer?: Buffer,
mediaType?: string,
options?: ActiveWebSendOptions,
) => Promise<{ messageId: string }>;
sendComposingTo: (to: string) => Promise<void>;
close?: () => Promise<void>;

View File

@@ -22,6 +22,7 @@ import {
normalizeE164,
toWhatsappJid,
} from "../utils.js";
import type { ActiveWebSendOptions } from "./active-listener.js";
import {
createWaSocket,
getStatusCode,
@@ -380,6 +381,7 @@ export async function monitorWebInbox(options: {
text: string,
mediaBuffer?: Buffer,
mediaType?: string,
options?: ActiveWebSendOptions,
): Promise<{ messageId: string }> => {
const jid = toWhatsappJid(to);
let payload: AnyMessageContent;
@@ -397,10 +399,12 @@ export async function monitorWebInbox(options: {
mimetype: mediaType,
};
} else if (mediaType.startsWith("video/")) {
const gifPlayback = options?.gifPlayback;
payload = {
video: mediaBuffer,
caption: text || undefined,
mimetype: mediaType,
...(gifPlayback ? { gifPlayback: true } : {}),
};
} else {
payload = {

View File

@@ -282,6 +282,26 @@ describe("web monitor inbox", () => {
await listener.close();
});
it("sets gifPlayback on outbound video payloads when requested", async () => {
const onMessage = vi.fn();
const listener = await monitorWebInbox({ verbose: false, onMessage });
const sock = await createWaSocket();
const buf = Buffer.from("gifvid");
await listener.sendMessage("+1555", "gif", buf, "video/mp4", {
gifPlayback: true,
});
expect(sock.sendMessage).toHaveBeenCalledWith("1555@s.whatsapp.net", {
video: buf,
caption: "gif",
mimetype: "video/mp4",
gifPlayback: true,
});
await listener.close();
});
it("resolves onClose when the socket closes", async () => {
const listener = await monitorWebInbox({
verbose: false,

View File

@@ -78,6 +78,27 @@ describe("web outbound", () => {
);
});
it("marks gif playback for video when requested", async () => {
const buf = Buffer.from("gifvid");
loadWebMediaMock.mockResolvedValueOnce({
buffer: buf,
contentType: "video/mp4",
kind: "video",
});
await sendMessageWhatsApp("+1555", "gif", {
verbose: false,
mediaUrl: "/tmp/anim.mp4",
gifPlayback: true,
});
expect(sendMessage).toHaveBeenLastCalledWith(
"+1555",
"gif",
buf,
"video/mp4",
{ gifPlayback: true },
);
});
it("maps image with caption", async () => {
const buf = Buffer.from("img");
loadWebMediaMock.mockResolvedValueOnce({

View File

@@ -2,7 +2,10 @@ import { randomUUID } from "node:crypto";
import { createSubsystemLogger, getChildLogger } from "../logging.js";
import { toWhatsappJid } from "../utils.js";
import { getActiveWebListener } from "./active-listener.js";
import {
type ActiveWebSendOptions,
getActiveWebListener,
} from "./active-listener.js";
import { loadWebMedia } from "./media.js";
const outboundLog = createSubsystemLogger("gateway/providers/whatsapp").child(
@@ -12,7 +15,7 @@ const outboundLog = createSubsystemLogger("gateway/providers/whatsapp").child(
export async function sendMessageWhatsApp(
to: string,
body: string,
options: { verbose: boolean; mediaUrl?: string },
options: { verbose: boolean; mediaUrl?: string; gifPlayback?: boolean },
): Promise<{ messageId: string; toJid: string }> {
let text = body;
const correlationId = randomUUID();
@@ -60,7 +63,18 @@ export async function sendMessageWhatsApp(
);
if (!active) throw new Error("Active web listener missing");
await active.sendComposingTo(to);
const result = await active.sendMessage(to, text, mediaBuffer, mediaType);
const sendOptions: ActiveWebSendOptions | undefined = options.gifPlayback
? { gifPlayback: true }
: undefined;
const result = sendOptions
? await active.sendMessage(
to,
text,
mediaBuffer,
mediaType,
sendOptions,
)
: await active.sendMessage(to, text, mediaBuffer, mediaType);
const messageId =
(result as { messageId?: string })?.messageId ?? "unknown";
const durationMs = Date.now() - startedAt;