fix(whatsapp): support pluginHooks.messageReceived in channel/account config schema (#86426)

Merged via squash.

Prepared head SHA: 27003a8d5a
Co-authored-by: bladin <1740879+bladin@users.noreply.github.com>
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Reviewed-by: @mcaxtr
This commit is contained in:
bladin
2026-05-28 10:31:47 +08:00
committed by GitHub
parent 2229122077
commit e0d003b372
4 changed files with 100 additions and 2 deletions

View File

@@ -80,7 +80,7 @@ const WHATSAPP_MESSAGE_RECEIVED_HOOK_LIMITS = {
type WhatsAppMessageReceivedHookConfig = {
pluginHooks?: {
messageReceived?: unknown;
messageReceived?: boolean;
};
accounts?: Record<string, unknown>;
};
@@ -90,7 +90,10 @@ function readWhatsAppMessageReceivedHookOptIn(value: unknown): boolean | undefin
return undefined;
}
const pluginHooks = (value as WhatsAppMessageReceivedHookConfig).pluginHooks;
return pluginHooks?.messageReceived === true ? true : undefined;
if (pluginHooks?.messageReceived === undefined) {
return undefined;
}
return pluginHooks.messageReceived;
}
function shouldEmitWhatsAppMessageReceivedHooks(params: {
@@ -104,6 +107,7 @@ function shouldEmitWhatsAppMessageReceivedHooks(params: {
params.accountId && channelConfig?.accounts
? channelConfig.accounts[params.accountId]
: undefined;
return (
readWhatsAppMessageReceivedHookOptIn(accountConfig) ??
readWhatsAppMessageReceivedHookOptIn(channelConfig) ??

View File

@@ -134,6 +134,11 @@ export type WhatsAppConfig = WhatsAppConfigCore &
defaultAccount?: string;
/** Per-action tool gating (default: true for all). */
actions?: WhatsAppActionConfig;
/** Plugin hook opt-in configuration for privacy-sensitive inbound events. */
pluginHooks?: {
/** Enable message_received hooks to broadcast inbound WhatsApp messages to plugins. */
messageReceived?: boolean;
};
};
export type WhatsAppAccountConfig = WhatsAppConfigCore &
@@ -144,4 +149,9 @@ export type WhatsAppAccountConfig = WhatsAppConfigCore &
enabled?: boolean;
/** Override auth directory (Baileys multi-file auth state). */
authDir?: string;
/** Plugin hook opt-in configuration for privacy-sensitive inbound events. */
pluginHooks?: {
/** Enable message_received hooks to broadcast inbound WhatsApp messages to plugins. */
messageReceived?: boolean;
};
};

View File

@@ -136,4 +136,80 @@ describe("WhatsApp prompt config Zod validation", () => {
undefined,
);
});
it("accepts channel-level pluginHooks.messageReceived", () => {
const config = {
pluginHooks: {
messageReceived: true,
},
};
const result = WhatsAppConfigSchema.safeParse(config);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.pluginHooks?.messageReceived).toBe(true);
}
});
it("accepts account-level pluginHooks.messageReceived", () => {
const config = {
accounts: {
work: {
pluginHooks: {
messageReceived: true,
},
},
},
};
const result = WhatsAppConfigSchema.safeParse(config);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.accounts?.work?.pluginHooks?.messageReceived).toBe(true);
}
});
it("rejects extra properties in pluginHooks", () => {
const config = {
pluginHooks: {
messageReceived: true,
otherProp: "invalid",
},
};
const result = WhatsAppConfigSchema.safeParse(config);
expect(result.success).toBe(false);
});
it("accepts channel-level pluginHooks.messageReceived: false", () => {
const config = {
pluginHooks: {
messageReceived: false,
},
};
const result = WhatsAppConfigSchema.safeParse(config);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.pluginHooks?.messageReceived).toBe(false);
}
});
it("accepts account-level pluginHooks.messageReceived: false", () => {
const config = {
accounts: {
work: {
pluginHooks: {
messageReceived: false,
},
},
},
};
const result = WhatsAppConfigSchema.safeParse(config);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.accounts?.work?.pluginHooks?.messageReceived).toBe(false);
}
});
});

View File

@@ -48,6 +48,13 @@ const WhatsAppAckReactionSchema = z
.strict()
.optional();
const WhatsAppPluginHooksSchema = z
.object({
messageReceived: z.boolean().optional(),
})
.strict()
.optional();
function stripDeprecatedWhatsAppNoopKeys(value: unknown): unknown {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return value;
@@ -97,6 +104,7 @@ function buildWhatsAppCommonShape(params: { useDefaults: boolean }) {
replyToMode: ReplyToModeSchema.optional(),
heartbeat: ChannelHeartbeatVisibilitySchema,
healthMonitor: ChannelHealthMonitorSchema,
pluginHooks: WhatsAppPluginHooksSchema,
};
}