feat(hooks): connect message_sending hook to outbound message pipeline
- Add getGlobalHookRunner import to message handlers - Call runMessageSending before onBlockReply in handleMessageEnd - Support content modification and cancellation via hook result - Fallback to original message on hook error Closes #3945
This commit is contained in:
parent
5f4715acfc
commit
90226c05e0
@ -18,6 +18,7 @@ import {
|
|||||||
promoteThinkingTagsToBlocks,
|
promoteThinkingTagsToBlocks,
|
||||||
} from "./pi-embedded-utils.js";
|
} from "./pi-embedded-utils.js";
|
||||||
import { createInlineCodeState } from "../markdown/code-spans.js";
|
import { createInlineCodeState } from "../markdown/code-spans.js";
|
||||||
|
import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
|
||||||
|
|
||||||
export function handleMessageStart(
|
export function handleMessageStart(
|
||||||
ctx: EmbeddedPiSubscribeContext,
|
ctx: EmbeddedPiSubscribeContext,
|
||||||
@ -236,16 +237,72 @@ export function handleMessageEnd(
|
|||||||
replyToTag,
|
replyToTag,
|
||||||
replyToCurrent,
|
replyToCurrent,
|
||||||
} = splitResult;
|
} = splitResult;
|
||||||
// Emit if there's content OR audioAsVoice flag (to propagate the flag).
|
|
||||||
if (cleanedText || (mediaUrls && mediaUrls.length > 0) || audioAsVoice) {
|
// Run message_sending hook before emitting
|
||||||
void onBlockReply({
|
const hookRunner = getGlobalHookRunner();
|
||||||
text: cleanedText,
|
const sessionKey = (ctx.params as { sessionKey?: string }).sessionKey;
|
||||||
mediaUrls: mediaUrls?.length ? mediaUrls : undefined,
|
const targetTo = sessionKey ?? "unknown";
|
||||||
audioAsVoice,
|
|
||||||
replyToId,
|
if (hookRunner?.hasHooks("message_sending") && cleanedText) {
|
||||||
replyToTag,
|
// Async hook execution with modification/cancellation support
|
||||||
replyToCurrent,
|
void hookRunner
|
||||||
});
|
.runMessageSending(
|
||||||
|
{
|
||||||
|
to: targetTo,
|
||||||
|
content: cleanedText,
|
||||||
|
metadata: { mediaUrls, sessionKey },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channelId: (ctx.params as { channel?: string }).channel ?? "unknown",
|
||||||
|
accountId: (ctx.params as { accountId?: string }).accountId,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then((hookResult) => {
|
||||||
|
const finalText = hookResult?.content ?? cleanedText;
|
||||||
|
const shouldCancel = hookResult?.cancel ?? false;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!shouldCancel &&
|
||||||
|
(finalText || (mediaUrls && mediaUrls.length > 0) || audioAsVoice)
|
||||||
|
) {
|
||||||
|
void onBlockReply({
|
||||||
|
text: finalText,
|
||||||
|
mediaUrls: mediaUrls?.length ? mediaUrls : undefined,
|
||||||
|
audioAsVoice,
|
||||||
|
replyToId,
|
||||||
|
replyToTag,
|
||||||
|
replyToCurrent,
|
||||||
|
});
|
||||||
|
} else if (shouldCancel) {
|
||||||
|
ctx.log.debug("Message cancelled by message_sending hook");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
ctx.log.warn(`message_sending hook failed: ${err}`);
|
||||||
|
// Fallback: send original message on hook error
|
||||||
|
if (cleanedText || (mediaUrls && mediaUrls.length > 0) || audioAsVoice) {
|
||||||
|
void onBlockReply({
|
||||||
|
text: cleanedText,
|
||||||
|
mediaUrls: mediaUrls?.length ? mediaUrls : undefined,
|
||||||
|
audioAsVoice,
|
||||||
|
replyToId,
|
||||||
|
replyToTag,
|
||||||
|
replyToCurrent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No hooks registered, emit directly
|
||||||
|
if (cleanedText || (mediaUrls && mediaUrls.length > 0) || audioAsVoice) {
|
||||||
|
void onBlockReply({
|
||||||
|
text: cleanedText,
|
||||||
|
mediaUrls: mediaUrls?.length ? mediaUrls : undefined,
|
||||||
|
audioAsVoice,
|
||||||
|
replyToId,
|
||||||
|
replyToTag,
|
||||||
|
replyToCurrent,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user