mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:40:44 +00:00
fix(slack): send block-only slash replies
This commit is contained in:
@@ -32,6 +32,7 @@ Docs: https://docs.openclaw.ai
|
||||
- WhatsApp: stage `qrcode` through root mirrored runtime dependencies so packaged QR pairing can render from staged plugin-runtime-deps installs. Fixes #75394. Thanks @FelipeX2001.
|
||||
- Discord/voice: apply per-channel Discord `systemPrompt` overrides to voice transcript turns by forwarding the trusted channel prompt through the voice agent run. Fixes #47095. Thanks @qearlyao.
|
||||
- Discord/native commands: send component-only interaction replies from slash command and status handlers instead of treating renderable Discord components as an empty response. Thanks @vincentkoc.
|
||||
- Slack/slash commands: send block-only slash command replies instead of dropping Slack block payloads with no plain-text fallback. Thanks @vincentkoc.
|
||||
- Gateway/agent: reject strict `openclaw agent --deliver` requests with missing delivery targets before starting the agent run, so users do not wait for a completed turn that cannot send anywhere. Thanks @vincentkoc.
|
||||
- Setup/import: honor non-interactive `--import-from` onboarding flags by running the migration import path instead of silently completing normal setup without importing anything. Thanks @vincentkoc.
|
||||
- Discord/voice: run voice-channel turns under a voice-output policy that hides the agent `tts` tool and asks for spoken reply text, so `/vc join` sessions synthesize and play agent replies instead of ending with `NO_REPLY`. Fixes #61536. Thanks @aounakram.
|
||||
|
||||
@@ -297,4 +297,31 @@ describe("deliverSlackSlashReplies chunking", () => {
|
||||
response_type: "ephemeral",
|
||||
});
|
||||
});
|
||||
|
||||
it("sends block-only slash replies instead of dropping them", async () => {
|
||||
const respond = vi.fn(async () => undefined);
|
||||
const blocks = [{ type: "divider" }];
|
||||
|
||||
await deliverSlackSlashReplies({
|
||||
replies: [
|
||||
{
|
||||
channelData: {
|
||||
slack: {
|
||||
blocks,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
respond,
|
||||
ephemeral: false,
|
||||
textLimit: 8000,
|
||||
});
|
||||
|
||||
expect(respond).toHaveBeenCalledTimes(1);
|
||||
expect(respond).toHaveBeenCalledWith({
|
||||
text: "",
|
||||
blocks,
|
||||
response_type: "in_channel",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -116,6 +116,7 @@ export async function deliverReplies(params: {
|
||||
|
||||
export type SlackRespondFn = (payload: {
|
||||
text: string;
|
||||
blocks?: ReturnType<typeof readSlackReplyBlocks>;
|
||||
response_type?: "ephemeral" | "in_channel";
|
||||
}) => Promise<unknown>;
|
||||
|
||||
@@ -202,14 +203,19 @@ export async function deliverSlackSlashReplies(params: {
|
||||
tableMode?: MarkdownTableMode;
|
||||
chunkMode?: ChunkMode;
|
||||
}) {
|
||||
const messages: string[] = [];
|
||||
const messages: Array<{ text: string; blocks?: ReturnType<typeof readSlackReplyBlocks> }> = [];
|
||||
const chunkLimit = Math.min(params.textLimit, SLACK_TEXT_LIMIT);
|
||||
for (const payload of params.replies) {
|
||||
const reply = resolveSendableOutboundReplyParts(payload);
|
||||
const slackBlocks = readSlackReplyBlocks(payload);
|
||||
const text =
|
||||
reply.hasText && !isSilentReplyText(reply.trimmedText, SILENT_REPLY_TOKEN)
|
||||
? reply.trimmedText
|
||||
: undefined;
|
||||
if (slackBlocks?.length && !reply.hasMedia) {
|
||||
messages.push({ text: text ?? "", blocks: slackBlocks });
|
||||
continue;
|
||||
}
|
||||
const combined = [text ?? "", ...reply.mediaUrls].filter(Boolean).join("\n");
|
||||
if (!combined) {
|
||||
continue;
|
||||
@@ -226,7 +232,7 @@ export async function deliverSlackSlashReplies(params: {
|
||||
chunks.push(combined);
|
||||
}
|
||||
for (const chunk of chunks) {
|
||||
messages.push(chunk);
|
||||
messages.push({ text: chunk });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +242,7 @@ export async function deliverSlackSlashReplies(params: {
|
||||
|
||||
// Slack slash command responses can be multi-part by sending follow-ups via response_url.
|
||||
const responseType = params.ephemeral ? "ephemeral" : "in_channel";
|
||||
for (const text of messages) {
|
||||
await params.respond({ text, response_type: responseType });
|
||||
for (const message of messages) {
|
||||
await params.respond({ ...message, response_type: responseType });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user