diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index b41ac6ab05d..ba4ce75f01c 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -1,12 +1,12 @@ import { applyAccountNameToChannelSection, buildChannelConfigSchema, - createActionGate, DEFAULT_ACCOUNT_ID, deleteAccountFromConfigSection, + extractSlackToolSend, formatPairingApproveHint, getChatChannelMeta, - listEnabledSlackAccounts, + listSlackMessageActions, listSlackAccountIds, listSlackDirectoryGroupsFromConfig, listSlackDirectoryPeersFromConfig, @@ -26,7 +26,6 @@ import { setAccountEnabledInConfigSection, slackOnboardingAdapter, SlackConfigSchema, - type ChannelMessageActionName, type ChannelPlugin, type ResolvedSlackAccount, } from "openclaw/plugin-sdk"; @@ -233,63 +232,8 @@ export const slackPlugin: ChannelPlugin = { }, }, actions: { - listActions: ({ cfg }) => { - const accounts = listEnabledSlackAccounts(cfg).filter( - (account) => account.botTokenSource !== "none", - ); - if (accounts.length === 0) { - return []; - } - const isActionEnabled = (key: string, defaultValue = true) => { - for (const account of accounts) { - const gate = createActionGate( - (account.actions ?? cfg.channels?.slack?.actions) as Record< - string, - boolean | undefined - >, - ); - if (gate(key, defaultValue)) { - return true; - } - } - return false; - }; - - const actions = new Set(["send"]); - if (isActionEnabled("reactions")) { - actions.add("react"); - actions.add("reactions"); - } - if (isActionEnabled("messages")) { - actions.add("read"); - actions.add("edit"); - actions.add("delete"); - } - if (isActionEnabled("pins")) { - actions.add("pin"); - actions.add("unpin"); - actions.add("list-pins"); - } - if (isActionEnabled("memberInfo")) { - actions.add("member-info"); - } - if (isActionEnabled("emojiList")) { - actions.add("emoji-list"); - } - return Array.from(actions); - }, - extractToolSend: ({ args }) => { - const action = typeof args.action === "string" ? args.action.trim() : ""; - if (action !== "sendMessage") { - return null; - } - const to = typeof args.to === "string" ? args.to : undefined; - if (!to) { - return null; - } - const accountId = typeof args.accountId === "string" ? args.accountId.trim() : undefined; - return { to, accountId }; - }, + listActions: ({ cfg }) => listSlackMessageActions(cfg), + extractToolSend: ({ args }) => extractSlackToolSend(args), handleAction: async ({ action, params, cfg, accountId, toolContext }) => { const resolveChannelId = () => readStringParam(params, "channelId") ?? readStringParam(params, "to", { required: true }); diff --git a/src/channels/plugins/slack.actions.ts b/src/channels/plugins/slack.actions.ts index 81eaa92b7cc..0570444372f 100644 --- a/src/channels/plugins/slack.actions.ts +++ b/src/channels/plugins/slack.actions.ts @@ -1,73 +1,13 @@ -import type { - ChannelMessageActionAdapter, - ChannelMessageActionContext, - ChannelMessageActionName, - ChannelToolSend, -} from "./types.js"; -import { createActionGate, readNumberParam, readStringParam } from "../../agents/tools/common.js"; +import type { ChannelMessageActionAdapter, ChannelMessageActionContext } from "./types.js"; +import { readNumberParam, readStringParam } from "../../agents/tools/common.js"; import { handleSlackAction, type SlackActionContext } from "../../agents/tools/slack-actions.js"; -import { listEnabledSlackAccounts } from "../../slack/accounts.js"; +import { extractSlackToolSend, listSlackMessageActions } from "../../slack/message-actions.js"; import { resolveSlackChannelId } from "../../slack/targets.js"; export function createSlackActions(providerId: string): ChannelMessageActionAdapter { return { - listActions: ({ cfg }) => { - const accounts = listEnabledSlackAccounts(cfg).filter( - (account) => account.botTokenSource !== "none", - ); - if (accounts.length === 0) { - return []; - } - const isActionEnabled = (key: string, defaultValue = true) => { - for (const account of accounts) { - const gate = createActionGate( - (account.actions ?? cfg.channels?.slack?.actions) as Record< - string, - boolean | undefined - >, - ); - if (gate(key, defaultValue)) { - return true; - } - } - return false; - }; - - const actions = new Set(["send"]); - if (isActionEnabled("reactions")) { - actions.add("react"); - actions.add("reactions"); - } - if (isActionEnabled("messages")) { - actions.add("read"); - actions.add("edit"); - actions.add("delete"); - } - if (isActionEnabled("pins")) { - actions.add("pin"); - actions.add("unpin"); - actions.add("list-pins"); - } - if (isActionEnabled("memberInfo")) { - actions.add("member-info"); - } - if (isActionEnabled("emojiList")) { - actions.add("emoji-list"); - } - return Array.from(actions); - }, - extractToolSend: ({ args }): ChannelToolSend | null => { - const action = typeof args.action === "string" ? args.action.trim() : ""; - if (action !== "sendMessage") { - return null; - } - const to = typeof args.to === "string" ? args.to : undefined; - if (!to) { - return null; - } - const accountId = typeof args.accountId === "string" ? args.accountId.trim() : undefined; - return { to, accountId }; - }, + listActions: ({ cfg }) => listSlackMessageActions(cfg), + extractToolSend: ({ args }) => extractSlackToolSend(args), handleAction: async (ctx: ChannelMessageActionContext) => { const { action, params, cfg } = ctx; const accountId = ctx.accountId ?? undefined; diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts index eb2d6ae4085..8e1b409f870 100644 --- a/src/plugin-sdk/index.ts +++ b/src/plugin-sdk/index.ts @@ -323,6 +323,7 @@ export { resolveSlackReplyToMode, type ResolvedSlackAccount, } from "../slack/accounts.js"; +export { extractSlackToolSend, listSlackMessageActions } from "../slack/message-actions.js"; export { slackOnboardingAdapter } from "../channels/plugins/onboarding/slack.js"; export { looksLikeSlackTargetId, diff --git a/src/slack/message-actions.ts b/src/slack/message-actions.ts new file mode 100644 index 00000000000..bf7d6f202c9 --- /dev/null +++ b/src/slack/message-actions.ts @@ -0,0 +1,61 @@ +import type { ChannelMessageActionName, ChannelToolSend } from "../channels/plugins/types.js"; +import type { OpenClawConfig } from "../config/config.js"; +import { createActionGate } from "../agents/tools/common.js"; +import { listEnabledSlackAccounts } from "./accounts.js"; + +export function listSlackMessageActions(cfg: OpenClawConfig): ChannelMessageActionName[] { + const accounts = listEnabledSlackAccounts(cfg).filter( + (account) => account.botTokenSource !== "none", + ); + if (accounts.length === 0) { + return []; + } + + const isActionEnabled = (key: string, defaultValue = true) => { + for (const account of accounts) { + const gate = createActionGate( + (account.actions ?? cfg.channels?.slack?.actions) as Record, + ); + if (gate(key, defaultValue)) { + return true; + } + } + return false; + }; + + const actions = new Set(["send"]); + if (isActionEnabled("reactions")) { + actions.add("react"); + actions.add("reactions"); + } + if (isActionEnabled("messages")) { + actions.add("read"); + actions.add("edit"); + actions.add("delete"); + } + if (isActionEnabled("pins")) { + actions.add("pin"); + actions.add("unpin"); + actions.add("list-pins"); + } + if (isActionEnabled("memberInfo")) { + actions.add("member-info"); + } + if (isActionEnabled("emojiList")) { + actions.add("emoji-list"); + } + return Array.from(actions); +} + +export function extractSlackToolSend(args: Record): ChannelToolSend | null { + const action = typeof args.action === "string" ? args.action.trim() : ""; + if (action !== "sendMessage") { + return null; + } + const to = typeof args.to === "string" ? args.to : undefined; + if (!to) { + return null; + } + const accountId = typeof args.accountId === "string" ? args.accountId.trim() : undefined; + return { to, accountId }; +}