mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-29 16:54:30 +00:00
fix: emit message_sent hook for all successful outbound paths (#15104)
This commit is contained in:
@@ -14,6 +14,12 @@ import {
|
||||
const mocks = vi.hoisted(() => ({
|
||||
appendAssistantMessageToSessionTranscript: vi.fn(async () => ({ ok: true, sessionFile: "x" })),
|
||||
}));
|
||||
const hookMocks = vi.hoisted(() => ({
|
||||
runner: {
|
||||
hasHooks: vi.fn(() => false),
|
||||
runMessageSent: vi.fn(async () => {}),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("../../config/sessions.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../config/sessions.js")>(
|
||||
@@ -24,12 +30,19 @@ vi.mock("../../config/sessions.js", async () => {
|
||||
appendAssistantMessageToSessionTranscript: mocks.appendAssistantMessageToSessionTranscript,
|
||||
};
|
||||
});
|
||||
vi.mock("../../plugins/hook-runner-global.js", () => ({
|
||||
getGlobalHookRunner: () => hookMocks.runner,
|
||||
}));
|
||||
|
||||
const { deliverOutboundPayloads, normalizeOutboundPayloads } = await import("./deliver.js");
|
||||
|
||||
describe("deliverOutboundPayloads", () => {
|
||||
beforeEach(() => {
|
||||
setActivePluginRegistry(defaultRegistry);
|
||||
hookMocks.runner.hasHooks.mockReset();
|
||||
hookMocks.runner.hasHooks.mockReturnValue(false);
|
||||
hookMocks.runner.runMessageSent.mockReset();
|
||||
hookMocks.runner.runMessageSent.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -422,6 +435,86 @@ describe("deliverOutboundPayloads", () => {
|
||||
expect.objectContaining({ text: "report.pdf" }),
|
||||
);
|
||||
});
|
||||
|
||||
it("emits message_sent success for text-only deliveries", async () => {
|
||||
hookMocks.runner.hasHooks.mockImplementation((name: string) => name === "message_sent");
|
||||
const sendWhatsApp = vi.fn().mockResolvedValue({ messageId: "w1", toJid: "jid" });
|
||||
|
||||
await deliverOutboundPayloads({
|
||||
cfg: {},
|
||||
channel: "whatsapp",
|
||||
to: "+1555",
|
||||
payloads: [{ text: "hello" }],
|
||||
deps: { sendWhatsApp },
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(hookMocks.runner.runMessageSent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ to: "+1555", content: "hello", success: true }),
|
||||
expect.objectContaining({ channelId: "whatsapp" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("emits message_sent success for sendPayload deliveries", async () => {
|
||||
hookMocks.runner.hasHooks.mockImplementation((name: string) => name === "message_sent");
|
||||
const sendPayload = vi.fn().mockResolvedValue({ channel: "matrix", messageId: "mx-1" });
|
||||
const sendText = vi.fn();
|
||||
const sendMedia = vi.fn();
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{
|
||||
pluginId: "matrix",
|
||||
source: "test",
|
||||
plugin: createOutboundTestPlugin({
|
||||
id: "matrix",
|
||||
outbound: { deliveryMode: "direct", sendPayload, sendText, sendMedia },
|
||||
}),
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
await deliverOutboundPayloads({
|
||||
cfg: {},
|
||||
channel: "matrix",
|
||||
to: "!room:1",
|
||||
payloads: [{ text: "payload text", channelData: { mode: "custom" } }],
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(hookMocks.runner.runMessageSent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ to: "!room:1", content: "payload text", success: true }),
|
||||
expect.objectContaining({ channelId: "matrix" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("emits message_sent failure when delivery errors", async () => {
|
||||
hookMocks.runner.hasHooks.mockImplementation((name: string) => name === "message_sent");
|
||||
const sendWhatsApp = vi.fn().mockRejectedValue(new Error("downstream failed"));
|
||||
|
||||
await expect(
|
||||
deliverOutboundPayloads({
|
||||
cfg: {},
|
||||
channel: "whatsapp",
|
||||
to: "+1555",
|
||||
payloads: [{ text: "hi" }],
|
||||
deps: { sendWhatsApp },
|
||||
}),
|
||||
).rejects.toThrow("downstream failed");
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(hookMocks.runner.runMessageSent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
to: "+1555",
|
||||
content: "hi",
|
||||
success: false,
|
||||
error: "downstream failed",
|
||||
}),
|
||||
expect.objectContaining({ channelId: "whatsapp" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const emptyRegistry = createTestRegistry([]);
|
||||
|
||||
@@ -345,6 +345,25 @@ export async function deliverOutboundPayloads(params: {
|
||||
mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []),
|
||||
channelData: payload.channelData,
|
||||
};
|
||||
const emitMessageSent = (success: boolean, error?: string) => {
|
||||
if (!hookRunner?.hasHooks("message_sent")) {
|
||||
return;
|
||||
}
|
||||
void hookRunner
|
||||
.runMessageSent(
|
||||
{
|
||||
to,
|
||||
content: payloadSummary.text,
|
||||
success,
|
||||
...(error ? { error } : {}),
|
||||
},
|
||||
{
|
||||
channelId: channel,
|
||||
accountId: accountId ?? undefined,
|
||||
},
|
||||
)
|
||||
.catch(() => {});
|
||||
};
|
||||
try {
|
||||
throwIfAborted(abortSignal);
|
||||
|
||||
@@ -378,6 +397,7 @@ export async function deliverOutboundPayloads(params: {
|
||||
params.onPayload?.(payloadSummary);
|
||||
if (handler.sendPayload && effectivePayload.channelData) {
|
||||
results.push(await handler.sendPayload(effectivePayload));
|
||||
emitMessageSent(true);
|
||||
continue;
|
||||
}
|
||||
if (payloadSummary.mediaUrls.length === 0) {
|
||||
@@ -386,6 +406,7 @@ export async function deliverOutboundPayloads(params: {
|
||||
} else {
|
||||
await sendTextChunks(payloadSummary.text);
|
||||
}
|
||||
emitMessageSent(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -400,40 +421,9 @@ export async function deliverOutboundPayloads(params: {
|
||||
results.push(await handler.sendMedia(caption, url));
|
||||
}
|
||||
}
|
||||
// Run message_sent plugin hook (fire-and-forget) on success
|
||||
if (hookRunner?.hasHooks("message_sent")) {
|
||||
void hookRunner
|
||||
.runMessageSent(
|
||||
{
|
||||
to,
|
||||
content: payloadSummary.text,
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
channelId: channel,
|
||||
accountId: accountId ?? undefined,
|
||||
},
|
||||
)
|
||||
.catch(() => {});
|
||||
}
|
||||
emitMessageSent(true);
|
||||
} catch (err) {
|
||||
// Run message_sent plugin hook on failure (fire-and-forget)
|
||||
if (hookRunner?.hasHooks("message_sent")) {
|
||||
void hookRunner
|
||||
.runMessageSent(
|
||||
{
|
||||
to,
|
||||
content: payloadSummary.text,
|
||||
success: false,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
},
|
||||
{
|
||||
channelId: channel,
|
||||
accountId: accountId ?? undefined,
|
||||
},
|
||||
)
|
||||
.catch(() => {});
|
||||
}
|
||||
emitMessageSent(false, err instanceof Error ? err.message : String(err));
|
||||
if (!params.bestEffort) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user