Skip to content

Commit f02406c

Browse files
nxmxbbdvincentkoc
authored andcommitted
fix(discord): keep audio voice replies threaded
1 parent 770b19f commit f02406c

2 files changed

Lines changed: 36 additions & 7 deletions

File tree

extensions/discord/src/outbound-adapter.test.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,22 +345,47 @@ describe("discordOutbound", () => {
345345
2,
346346
);
347347
expect(messageOptions.accountId).toBe("default");
348-
expect(messageOptions.replyTo).toBeUndefined();
348+
expect(messageOptions.replyTo).toBe("reply-1");
349349

350350
const mediaCall = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 1);
351351
expect(mediaCall[0]).toBe("channel:123456");
352352
expect(mediaCall[1]).toBe("");
353353
const mediaOptions = mockObjectArg(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 1, 2);
354354
expect(mediaOptions.accountId).toBe("default");
355355
expect(mediaOptions.mediaUrl).toBe("https://example.com/extra.png");
356-
expect(mediaOptions.replyTo).toBeUndefined();
356+
expect(mediaOptions.replyTo).toBe("reply-1");
357357
expect(result).toEqual({
358358
channel: "discord",
359359
messageId: "msg-1",
360360
channelId: "ch-1",
361361
});
362362
});
363363

364+
it("keeps captured replyTo on audioAsVoice sends when replyToMode is batched", async () => {
365+
await discordOutbound.sendPayload?.({
366+
cfg: {},
367+
to: "channel:123456",
368+
text: "",
369+
payload: {
370+
text: "voice note",
371+
mediaUrls: ["https://example.com/voice.ogg", "https://example.com/extra.png"],
372+
audioAsVoice: true,
373+
},
374+
accountId: "default",
375+
replyToId: "reply-1",
376+
replyToMode: "batched",
377+
});
378+
379+
expect(
380+
mockObjectArg(hoisted.sendVoiceMessageDiscordMock, "sendVoiceMessageDiscord", 0, 2).replyTo,
381+
).toBe("reply-1");
382+
expect(
383+
hoisted.sendMessageDiscordMock.mock.calls.map(
384+
(call) => (call[2] as { replyTo?: unknown } | undefined)?.replyTo,
385+
),
386+
).toEqual(["reply-1", "reply-1"]);
387+
});
388+
364389
it("keeps replyToId on every internal audioAsVoice send when replyToMode is all", async () => {
365390
await discordOutbound.sendPayload?.({
366391
cfg: {},

extensions/discord/src/outbound-payload.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,23 @@ export async function sendDiscordOutboundPayload(params: {
8484
const sendContext = await createDiscordPayloadSendContext(ctx);
8585

8686
if (payload.audioAsVoice && mediaUrls.length > 0) {
87+
// audioAsVoice emits one logical Discord reply across voice/text/media sends.
88+
// Capture before helper calls consume implicit single-use reply targets.
89+
const voiceReplyTo = sendContext.resolveReplyTo();
8790
let lastResult = await sendContext.withRetry(
8891
async () =>
89-
await sendContext.sendVoice(
90-
sendContext.target,
91-
mediaUrls[0],
92-
resolveDiscordDeliveryOptions(ctx, sendContext),
93-
),
92+
await sendContext.sendVoice(sendContext.target, mediaUrls[0], {
93+
...resolveDiscordDeliveryOptions(ctx, sendContext),
94+
replyTo: voiceReplyTo,
95+
}),
9496
);
9597
if (payload.text?.trim()) {
9698
lastResult = await sendContext.withRetry(
9799
async () =>
98100
await sendContext.send(sendContext.target, payload.text, {
99101
verbose: false,
100102
...resolveDiscordFormattedDeliveryOptions(ctx, sendContext),
103+
replyTo: voiceReplyTo,
101104
}),
102105
);
103106
}
@@ -107,6 +110,7 @@ export async function sendDiscordOutboundPayload(params: {
107110
await sendContext.send(sendContext.target, "", {
108111
verbose: false,
109112
...resolveDiscordMediaDeliveryOptions(ctx, sendContext, mediaUrl),
113+
replyTo: voiceReplyTo,
110114
}),
111115
);
112116
}

0 commit comments

Comments
 (0)