Config: validate Slack interactive reply capability

This commit is contained in:
Vincent Koc 2026-03-12 23:42:16 -04:00
parent b88b875ac0
commit af414e2e73
4 changed files with 47 additions and 2 deletions

View File

@ -2,9 +2,10 @@ import { normalizeChannelId } from "../channels/plugins/index.js";
import { resolveAccountEntry } from "../routing/account-lookup.js";
import { normalizeAccountId } from "../routing/session-key.js";
import type { OpenClawConfig } from "./config.js";
import type { SlackCapabilitiesConfig } from "./types.slack.js";
import type { TelegramCapabilitiesConfig } from "./types.telegram.js";
type CapabilitiesConfig = TelegramCapabilitiesConfig;
type CapabilitiesConfig = TelegramCapabilitiesConfig | SlackCapabilitiesConfig;
const isStringArray = (value: unknown): value is string[] =>
Array.isArray(value) && value.every((entry) => typeof entry === "string");

View File

@ -59,6 +59,14 @@ const TelegramCapabilitiesSchema = z.union([
})
.strict(),
]);
const SlackCapabilitiesSchema = z.union([
z.array(z.string()),
z
.object({
interactiveReplies: z.boolean().optional(),
})
.strict(),
]);
export const TelegramTopicSchema = z
.object({
@ -831,7 +839,7 @@ export const SlackAccountSchema = z
mode: z.enum(["socket", "http"]).optional(),
signingSecret: SecretInputSchema.optional().register(sensitive),
webhookPath: z.string().optional(),
capabilities: z.array(z.string()).optional(),
capabilities: SlackCapabilitiesSchema.optional(),
markdown: MarkdownConfigSchema,
enabled: z.boolean().optional(),
commands: ProviderCommandsSchema,

View File

@ -8,6 +8,7 @@ export {
resolveSlackAccount,
resolveSlackReplyToMode,
} from "../slack/accounts.js";
export { isSlackInteractiveRepliesEnabled } from "../slack/interactive-replies.js";
export { inspectSlackAccount } from "../slack/account-inspect.js";
export {
projectCredentialSnapshotFields,

View File

@ -0,0 +1,35 @@
import type { OpenClawConfig } from "../config/config.js";
import { listSlackAccountIds, resolveSlackAccount } from "./accounts.js";
function resolveInteractiveRepliesFromCapabilities(capabilities: unknown): boolean {
if (!capabilities) {
return false;
}
if (Array.isArray(capabilities)) {
return capabilities.some(
(entry) => String(entry).trim().toLowerCase() === "interactivereplies",
);
}
if (typeof capabilities === "object") {
return (capabilities as { interactiveReplies?: unknown }).interactiveReplies === true;
}
return false;
}
export function isSlackInteractiveRepliesEnabled(params: {
cfg: OpenClawConfig;
accountId?: string | null;
}): boolean {
if (params.accountId) {
const account = resolveSlackAccount({ cfg: params.cfg, accountId: params.accountId });
return resolveInteractiveRepliesFromCapabilities(account.config.capabilities);
}
const accountIds = listSlackAccountIds(params.cfg);
if (accountIds.length === 0) {
return resolveInteractiveRepliesFromCapabilities(params.cfg.channels?.slack?.capabilities);
}
return accountIds.some((accountId) => {
const account = resolveSlackAccount({ cfg: params.cfg, accountId });
return resolveInteractiveRepliesFromCapabilities(account.config.capabilities);
});
}