fix(whatsapp): infer same-chat action targets

This commit is contained in:
Marcus Castro
2026-06-21 21:48:34 -03:00
parent a94d544861
commit 05fd3b12d0
2 changed files with 83 additions and 4 deletions

View File

@@ -150,6 +150,43 @@ describe("whatsapp react action messageId resolution", () => {
});
});
it("uses toolContext current chat for same-chat upload-file", async () => {
const mediaReadFile = vi.fn(async () => Buffer.from("media"));
await handleWhatsAppMessageAction({
action: "upload-file",
params: {
filePath: "/tmp/pic.png",
caption: "picture caption",
},
cfg: baseCfg,
accountId: "default",
mediaLocalRoots: ["/tmp"],
mediaReadFile,
toolContext: {
currentChannelId: "whatsapp:+1555",
currentChannelProvider: "whatsapp",
currentMessageId: "ctx-msg-42",
},
});
expect(hoisted.resolveAuthorizedWhatsAppOutboundTarget).toHaveBeenCalledWith({
cfg: baseCfg,
chatJid: "+1555",
accountId: "default",
actionLabel: "upload-file",
});
expect(hoisted.sendMessageWhatsApp).toHaveBeenCalledWith(
"+1555",
"picture caption",
expect.objectContaining({
accountId: "default",
mediaReadFile,
mediaUrl: "/tmp/pic.png",
}),
);
});
it("does not send upload-file when target authorization fails", async () => {
hoisted.resolveAuthorizedWhatsAppOutboundTarget.mockImplementationOnce(() => {
throw new Error("WhatsApp upload-file blocked");
@@ -286,6 +323,33 @@ describe("whatsapp react action messageId resolution", () => {
);
});
it("falls back to toolContext current chat for same-chat reactions", async () => {
await handleWhatsAppMessageAction({
action: "react",
params: { emoji: "❤️" },
cfg: baseCfg,
accountId: "default",
toolContext: {
currentChannelId: "whatsapp:+1555",
currentChannelProvider: "whatsapp",
currentMessageId: "ctx-msg-42",
},
});
expect(hoisted.handleWhatsAppAction).toHaveBeenCalledWith(
{
action: "react",
chatJid: "+1555",
messageId: "ctx-msg-42",
emoji: "❤️",
remove: undefined,
participant: undefined,
accountId: "default",
fromMe: undefined,
},
baseCfg,
);
});
it("converts numeric toolContext messageId to string", async () => {
await handleWhatsAppMessageAction({
action: "react",

View File

@@ -76,6 +76,21 @@ function hasUploadFileBufferPayload(args: Record<string, unknown>): boolean {
return readStringParam(args, "buffer", { trim: false }) !== undefined;
}
function readWhatsAppActionChatJid(params: WhatsAppMessageActionParams): string | undefined {
const explicit =
readStringParam(params.params, "chatJid") ?? readStringParam(params.params, "to");
if (explicit) {
return explicit;
}
if (
params.toolContext?.currentChannelProvider !== WHATSAPP_CHANNEL ||
!params.toolContext.currentChannelId
) {
return undefined;
}
return normalizeWhatsAppTarget(params.toolContext.currentChannelId) ?? undefined;
}
function extractBase64Payload(encoded: string): string {
const match = /^data:[^;]+;base64,(.*)$/i.exec(encoded.trim());
return match ? match[1] : encoded;
@@ -134,7 +149,8 @@ async function handleWhatsAppUploadFileAction(params: WhatsAppMessageActionParam
"WhatsApp upload-file requires media, mediaUrl, filePath, path, fileUrl, or buffer.",
);
}
const to = readStringParam(params.params, "to", { required: true });
const to =
readWhatsAppActionChatJid(params) ?? readStringParam(params.params, "to", { required: true });
const resolved = resolveAuthorizedWhatsAppOutboundTarget({
cfg: params.cfg,
chatJid: to,
@@ -188,8 +204,7 @@ export async function handleWhatsAppMessageAction(params: WhatsAppMessageActionP
throw new Error(`Action ${params.action} is not supported for provider ${WHATSAPP_CHANNEL}.`);
}
const isWhatsAppSource = params.toolContext?.currentChannelProvider === WHATSAPP_CHANNEL;
const explicitTarget =
readStringParam(params.params, "chatJid") ?? readStringParam(params.params, "to");
const explicitTarget = readWhatsAppActionChatJid(params);
const normalizedTarget = explicitTarget ? normalizeWhatsAppTarget(explicitTarget) : null;
const normalizedCurrent =
isWhatsAppSource && params.toolContext?.currentChannelId
@@ -232,7 +247,7 @@ export async function handleWhatsAppMessageAction(params: WhatsAppMessageActionP
{
action: "react",
chatJid:
readStringParam(params.params, "chatJid") ??
readWhatsAppActionChatJid(params) ??
readStringParam(params.params, "to", { required: true }),
messageId,
emoji,