mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 06:27:27 +00:00
fix(whatsapp): allow per-message link preview override\n\nWhatsApp messages default to enabling link previews for URLs. This adds\nsupport for overriding this behavior per-message via the \nparameter (e.g. from tool options), consistent with Telegram.\n\nFix: Updated internal WhatsApp Web API layers to pass option\ndown to Baileys .
This commit is contained in:
56
extensions/whatsapp/src/channel.send-options.test.ts
Normal file
56
extensions/whatsapp/src/channel.send-options.test.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
|
import { whatsappPlugin } from "./channel.js";
|
||||||
|
|
||||||
|
// Mock runtime
|
||||||
|
const mockSendMessageWhatsApp = vi.fn().mockResolvedValue({ messageId: "123", toJid: "123@s.whatsapp.net" });
|
||||||
|
|
||||||
|
vi.mock("./runtime.js", () => ({
|
||||||
|
getWhatsAppRuntime: () => ({
|
||||||
|
channel: {
|
||||||
|
text: { chunkText: (t: string) => [t] },
|
||||||
|
whatsapp: {
|
||||||
|
sendMessageWhatsApp: mockSendMessageWhatsApp,
|
||||||
|
createLoginTool: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
logging: { shouldLogVerbose: () => false },
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("whatsappPlugin.outbound.sendText", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes linkPreview option to sendMessageWhatsApp", async () => {
|
||||||
|
await whatsappPlugin.outbound.sendText({
|
||||||
|
to: "1234567890",
|
||||||
|
text: "http://example.com",
|
||||||
|
// @ts-expect-error - injecting extra param as per runtime behavior
|
||||||
|
linkPreview: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockSendMessageWhatsApp).toHaveBeenCalledWith(
|
||||||
|
"1234567890",
|
||||||
|
"http://example.com",
|
||||||
|
expect.objectContaining({
|
||||||
|
linkPreview: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes linkPreview=undefined when omitted", async () => {
|
||||||
|
await whatsappPlugin.outbound.sendText({
|
||||||
|
to: "1234567890",
|
||||||
|
text: "hello",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockSendMessageWhatsApp).toHaveBeenCalledWith(
|
||||||
|
"1234567890",
|
||||||
|
"hello",
|
||||||
|
expect.objectContaining({
|
||||||
|
linkPreview: undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -290,12 +290,15 @@ export const whatsappPlugin: ChannelPlugin<ResolvedWhatsAppAccount> = {
|
|||||||
pollMaxOptions: 12,
|
pollMaxOptions: 12,
|
||||||
resolveTarget: ({ to, allowFrom, mode }) =>
|
resolveTarget: ({ to, allowFrom, mode }) =>
|
||||||
resolveWhatsAppOutboundTarget({ to, allowFrom, mode }),
|
resolveWhatsAppOutboundTarget({ to, allowFrom, mode }),
|
||||||
sendText: async ({ to, text, accountId, deps, gifPlayback }) => {
|
sendText: async (params) => {
|
||||||
|
const { to, text, accountId, deps, gifPlayback } = params;
|
||||||
|
const linkPreview = (params as { linkPreview?: boolean }).linkPreview;
|
||||||
const send = deps?.sendWhatsApp ?? getWhatsAppRuntime().channel.whatsapp.sendMessageWhatsApp;
|
const send = deps?.sendWhatsApp ?? getWhatsAppRuntime().channel.whatsapp.sendMessageWhatsApp;
|
||||||
const result = await send(to, text, {
|
const result = await send(to, text, {
|
||||||
verbose: false,
|
verbose: false,
|
||||||
accountId: accountId ?? undefined,
|
accountId: accountId ?? undefined,
|
||||||
gifPlayback,
|
gifPlayback,
|
||||||
|
linkPreview,
|
||||||
});
|
});
|
||||||
return { channel: "whatsapp", ...result };
|
return { channel: "whatsapp", ...result };
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export type ActiveWebSendOptions = {
|
|||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
accountId?: string;
|
accountId?: string;
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
|
linkPreview?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ActiveWebListener = {
|
export type ActiveWebListener = {
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ export async function monitorWebInbox(options: {
|
|||||||
|
|
||||||
const sendApi = createWebSendApi({
|
const sendApi = createWebSendApi({
|
||||||
sock: {
|
sock: {
|
||||||
sendMessage: (jid: string, content: AnyMessageContent) => sock.sendMessage(jid, content),
|
sendMessage: (jid, content, options) => sock.sendMessage(jid, content, options),
|
||||||
sendPresenceUpdate: (presence, jid?: string) => sock.sendPresenceUpdate(presence, jid),
|
sendPresenceUpdate: (presence, jid?: string) => sock.sendPresenceUpdate(presence, jid),
|
||||||
},
|
},
|
||||||
defaultAccountId: options.accountId,
|
defaultAccountId: options.accountId,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { AnyMessageContent, WAPresence } from "@whiskeysockets/baileys";
|
import type { AnyMessageContent, MiscMessageGenerationOptions, WAPresence } from "@whiskeysockets/baileys";
|
||||||
import type { ActiveWebSendOptions } from "../active-listener.js";
|
import type { ActiveWebSendOptions } from "../active-listener.js";
|
||||||
import { recordChannelActivity } from "../../infra/channel-activity.js";
|
import { recordChannelActivity } from "../../infra/channel-activity.js";
|
||||||
import { toWhatsappJid } from "../../utils.js";
|
import { toWhatsappJid } from "../../utils.js";
|
||||||
@@ -19,7 +19,11 @@ function resolveOutboundMessageId(result: unknown): string {
|
|||||||
|
|
||||||
export function createWebSendApi(params: {
|
export function createWebSendApi(params: {
|
||||||
sock: {
|
sock: {
|
||||||
sendMessage: (jid: string, content: AnyMessageContent) => Promise<unknown>;
|
sendMessage: (
|
||||||
|
jid: string,
|
||||||
|
content: AnyMessageContent,
|
||||||
|
options?: MiscMessageGenerationOptions,
|
||||||
|
) => Promise<unknown>;
|
||||||
sendPresenceUpdate: (presence: WAPresence, jid?: string) => Promise<unknown>;
|
sendPresenceUpdate: (presence: WAPresence, jid?: string) => Promise<unknown>;
|
||||||
};
|
};
|
||||||
defaultAccountId: string;
|
defaultAccountId: string;
|
||||||
@@ -63,7 +67,10 @@ export function createWebSendApi(params: {
|
|||||||
} else {
|
} else {
|
||||||
payload = { text };
|
payload = { text };
|
||||||
}
|
}
|
||||||
const result = await params.sock.sendMessage(jid, payload);
|
const miscOptions: MiscMessageGenerationOptions = {
|
||||||
|
linkPreview: sendOptions?.linkPreview === false ? null : undefined,
|
||||||
|
};
|
||||||
|
const result = await params.sock.sendMessage(jid, payload, miscOptions);
|
||||||
const accountId = sendOptions?.accountId ?? params.defaultAccountId;
|
const accountId = sendOptions?.accountId ?? params.defaultAccountId;
|
||||||
recordWhatsAppOutbound(accountId);
|
recordWhatsAppOutbound(accountId);
|
||||||
const messageId = resolveOutboundMessageId(result);
|
const messageId = resolveOutboundMessageId(result);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export async function sendMessageWhatsApp(
|
|||||||
mediaLocalRoots?: readonly string[];
|
mediaLocalRoots?: readonly string[];
|
||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
accountId?: string;
|
accountId?: string;
|
||||||
|
linkPreview?: boolean;
|
||||||
},
|
},
|
||||||
): Promise<{ messageId: string; toJid: string }> {
|
): Promise<{ messageId: string; toJid: string }> {
|
||||||
let text = body;
|
let text = body;
|
||||||
@@ -75,10 +76,16 @@ export async function sendMessageWhatsApp(
|
|||||||
const hasExplicitAccountId = Boolean(options.accountId?.trim());
|
const hasExplicitAccountId = Boolean(options.accountId?.trim());
|
||||||
const accountId = hasExplicitAccountId ? resolvedAccountId : undefined;
|
const accountId = hasExplicitAccountId ? resolvedAccountId : undefined;
|
||||||
const sendOptions: ActiveWebSendOptions | undefined =
|
const sendOptions: ActiveWebSendOptions | undefined =
|
||||||
options.gifPlayback || accountId || documentFileName
|
options.gifPlayback ||
|
||||||
|
options.accountId ||
|
||||||
|
options.linkPreview !== undefined ||
|
||||||
|
documentFileName
|
||||||
? {
|
? {
|
||||||
...(options.gifPlayback ? { gifPlayback: true } : {}),
|
...(options.gifPlayback ? { gifPlayback: true } : {}),
|
||||||
...(documentFileName ? { fileName: documentFileName } : {}),
|
...(documentFileName ? { fileName: documentFileName } : {}),
|
||||||
|
...(options.linkPreview !== undefined
|
||||||
|
? { linkPreview: options.linkPreview }
|
||||||
|
: {}),
|
||||||
accountId,
|
accountId,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user