Integrations: address review follow-ups

This commit is contained in:
Vincent Koc 2026-03-14 23:16:47 -07:00
parent c13b1b9fde
commit 9514a38b0b
5 changed files with 30 additions and 31 deletions

View File

@ -26,7 +26,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. Thanks @vincentkoc.
- Inbound policy hardening: tighten callback and webhook sender checks across Mattermost and Google Chat, match Nextcloud Talk rooms by stable room token, treat explicit empty allowlists in Twitch and Tlon as deny-all, defer Tlon cross-channel cite expansion until after authorization, and require `operator.admin` for mutating internal `/acp` actions. Thanks @vincentkoc.
- Inbound policy hardening: tighten callback and webhook sender checks across Mattermost and Google Chat, match Nextcloud Talk rooms by stable room token, and treat explicit empty Twitch allowlists as deny-all. Thanks @vincentkoc.
- CI/channel test routing: move the built-in channel suites into `test:channels` and keep them out of `test:extensions`, so extension CI no longer fails after the channel migration while targeted test routing still sends Slack, Signal, and iMessage suites to the right lane. (#46066) Thanks @scoootscooob.
- Node/startup: remove leftover debug `console.log("node host PATH: ...")` that printed the resolved PATH on every `openclaw node run` invocation. (#46411)
- Control UI/dashboard: preserve structured gateway shutdown reasons across restart disconnects so config-triggered restarts no longer fall back to `disconnected (1006): no reason`. (#46532) Thanks @vincentkoc.

View File

@ -741,6 +741,7 @@ describe("createMattermostInteractionHandler", () => {
it("blocks button dispatch when the sender is not allowed for the action", async () => {
const { context, token } = createActionContext();
const dispatchButtonClick = vi.fn();
const handleInteraction = vi.fn();
const handler = createMattermostInteractionHandler({
client: {
request: async (_path: string, init?: { method?: string }) =>
@ -754,6 +755,7 @@ describe("createMattermostInteractionHandler", () => {
ephemeral_text: "blocked",
},
}),
handleInteraction,
dispatchButtonClick,
});
@ -763,6 +765,7 @@ describe("createMattermostInteractionHandler", () => {
expect(res.statusCode).toBe(200);
expect(res.body).toContain("blocked");
expect(handleInteraction).not.toHaveBeenCalled();
expect(dispatchButtonClick).not.toHaveBeenCalled();
});

View File

@ -574,32 +574,6 @@ export function createMattermostInteractionHandler(params: {
`post=${payload.post_id} channel=${payload.channel_id}`,
);
if (params.handleInteraction) {
try {
const response = await params.handleInteraction({
payload,
userName,
actionId,
actionName: clickedButtonName,
originalMessage,
context: contextWithoutToken,
post: originalPost,
});
if (response !== null) {
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(response));
return;
}
} catch (err) {
log?.(`mattermost interaction: custom handler failed: ${String(err)}`);
res.statusCode = 500;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({ error: "Interaction handler failed" }));
return;
}
}
if (params.authorizeButtonClick) {
try {
const authorization = await params.authorizeButtonClick({
@ -627,6 +601,32 @@ export function createMattermostInteractionHandler(params: {
}
}
if (params.handleInteraction) {
try {
const response = await params.handleInteraction({
payload,
userName,
actionId,
actionName: clickedButtonName,
originalMessage,
context: contextWithoutToken,
post: originalPost,
});
if (response !== null) {
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(response));
return;
}
} catch (err) {
log?.(`mattermost interaction: custom handler failed: ${String(err)}`);
res.statusCode = 500;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({ error: "Interaction handler failed" }));
return;
}
}
// Dispatch as system event so the agent can handle it.
// Wrapped in try/catch — the post update below must still run even if
// system event dispatch fails (e.g. missing sessionKey or channel lookup).

View File

@ -114,7 +114,6 @@ export async function handleNextcloudTalkInbound(params: {
const roomMatch = resolveNextcloudTalkRoomMatch({
rooms: account.config.rooms,
roomToken,
roomName,
});
const roomConfig = roomMatch.roomConfig;
if (isGroup && !roomMatch.allowed) {

View File

@ -57,7 +57,6 @@ export type NextcloudTalkRoomMatch = {
export function resolveNextcloudTalkRoomMatch(params: {
rooms?: Record<string, NextcloudTalkRoomConfig>;
roomToken: string;
roomName?: string | null;
}): NextcloudTalkRoomMatch {
const rooms = params.rooms ?? {};
const allowlistConfigured = Object.keys(rooms).length > 0;
@ -96,11 +95,9 @@ export function resolveNextcloudTalkGroupToolPolicy(
if (!roomToken) {
return undefined;
}
const roomName = params.groupChannel?.trim() || undefined;
const match = resolveNextcloudTalkRoomMatch({
rooms: cfg.channels?.["nextcloud-talk"]?.rooms,
roomToken,
roomName,
});
return match.roomConfig?.tools ?? match.wildcardConfig?.tools;
}