refactor(slack): share plugin base config
This commit is contained in:
parent
f90d432de3
commit
75b8117f83
@ -1,61 +1,19 @@
|
||||
import {
|
||||
buildChannelConfigSchema,
|
||||
getChatChannelMeta,
|
||||
SlackConfigSchema,
|
||||
type ChannelPlugin,
|
||||
} from "../../../src/plugin-sdk-internal/slack.js";
|
||||
import { type ChannelPlugin } from "openclaw/plugin-sdk/slack";
|
||||
import { type ResolvedSlackAccount } from "./accounts.js";
|
||||
import { isSlackInteractiveRepliesEnabled } from "./interactive-replies.js";
|
||||
import {
|
||||
isSlackPluginAccountConfigured,
|
||||
slackConfigAccessors,
|
||||
slackConfigBase,
|
||||
slackSetupWizard,
|
||||
} from "./plugin-shared.js";
|
||||
import { slackSetupAdapter } from "./setup-core.js";
|
||||
import { createSlackSetupWizardProxy, slackSetupAdapter } from "./setup-core.js";
|
||||
import { createSlackPluginBase } from "./shared.js";
|
||||
|
||||
async function loadSlackChannelRuntime() {
|
||||
return await import("./channel.runtime.js");
|
||||
}
|
||||
|
||||
const slackSetupWizard = createSlackSetupWizardProxy(async () => ({
|
||||
slackSetupWizard: (await loadSlackChannelRuntime()).slackSetupWizard,
|
||||
}));
|
||||
|
||||
export const slackSetupPlugin: ChannelPlugin<ResolvedSlackAccount> = {
|
||||
id: "slack",
|
||||
meta: {
|
||||
...getChatChannelMeta("slack"),
|
||||
preferSessionLookupForAnnounceTarget: true,
|
||||
},
|
||||
setupWizard: slackSetupWizard,
|
||||
capabilities: {
|
||||
chatTypes: ["direct", "channel", "thread"],
|
||||
reactions: true,
|
||||
threads: true,
|
||||
media: true,
|
||||
nativeCommands: true,
|
||||
},
|
||||
agentPrompt: {
|
||||
messageToolHints: ({ cfg, accountId }) =>
|
||||
isSlackInteractiveRepliesEnabled({ cfg, accountId })
|
||||
? [
|
||||
"- Slack interactive replies: use `[[slack_buttons: Label:value, Other:other]]` to add action buttons that route clicks back as Slack interaction system events.",
|
||||
"- Slack selects: use `[[slack_select: Placeholder | Label:value, Other:other]]` to add a static select menu that routes the chosen value back as a Slack interaction system event.",
|
||||
]
|
||||
: [
|
||||
"- Slack interactive replies are disabled. If needed, ask to set `channels.slack.capabilities.interactiveReplies=true` (or the same under `channels.slack.accounts.<account>.capabilities`).",
|
||||
],
|
||||
},
|
||||
streaming: {
|
||||
blockStreamingCoalesceDefaults: { minChars: 1500, idleMs: 1000 },
|
||||
},
|
||||
reload: { configPrefixes: ["channels.slack"] },
|
||||
configSchema: buildChannelConfigSchema(SlackConfigSchema),
|
||||
config: {
|
||||
...slackConfigBase,
|
||||
isConfigured: (account) => isSlackPluginAccountConfigured(account),
|
||||
describeAccount: (account) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: isSlackPluginAccountConfigured(account),
|
||||
botTokenSource: account.botTokenSource,
|
||||
appTokenSource: account.appTokenSource,
|
||||
}),
|
||||
...slackConfigAccessors,
|
||||
},
|
||||
setup: slackSetupAdapter,
|
||||
...createSlackPluginBase({
|
||||
setupWizard: slackSetupWizard,
|
||||
setup: slackSetupAdapter,
|
||||
}),
|
||||
};
|
||||
|
||||
@ -1,20 +1,17 @@
|
||||
import { resolveOutboundSendDep } from "../../../src/infra/outbound/send-deps.js";
|
||||
import {
|
||||
buildAccountScopedAllowlistConfigEditor,
|
||||
buildAccountScopedDmSecurityPolicy,
|
||||
collectOpenGroupPolicyConfiguredRouteWarnings,
|
||||
collectOpenProviderGroupPolicyWarnings,
|
||||
} from "../../../src/plugin-sdk-internal/channel-config.js";
|
||||
collectOpenGroupPolicyConfiguredRouteWarnings,
|
||||
} from "openclaw/plugin-sdk/compat";
|
||||
import {
|
||||
buildAgentSessionKey,
|
||||
resolveThreadSessionKeys,
|
||||
type RoutePeer,
|
||||
} from "../../../src/plugin-sdk-internal/core.js";
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import {
|
||||
buildComputedAccountStatusSnapshot,
|
||||
buildChannelConfigSchema,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
getChatChannelMeta,
|
||||
listSlackDirectoryGroupsFromConfig,
|
||||
listSlackDirectoryPeersFromConfig,
|
||||
looksLikeSlackTargetId,
|
||||
@ -24,10 +21,10 @@ import {
|
||||
resolveConfiguredFromRequiredCredentialStatuses,
|
||||
resolveSlackGroupRequireMention,
|
||||
resolveSlackGroupToolPolicy,
|
||||
SlackConfigSchema,
|
||||
type ChannelPlugin,
|
||||
type OpenClawConfig,
|
||||
} from "../../../src/plugin-sdk-internal/slack.js";
|
||||
} from "openclaw/plugin-sdk/slack";
|
||||
import { resolveOutboundSendDep } from "../../../src/infra/outbound/send-deps.js";
|
||||
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
|
||||
import {
|
||||
listEnabledSlackAccounts,
|
||||
@ -41,23 +38,25 @@ import { isSlackInteractiveRepliesEnabled } from "./interactive-replies.js";
|
||||
import { handleSlackMessageAction } from "./message-action-dispatch.js";
|
||||
import { extractSlackToolSend, listSlackMessageActions } from "./message-actions.js";
|
||||
import { normalizeAllowListLower } from "./monitor/allow-list.js";
|
||||
import {
|
||||
isSlackPluginAccountConfigured,
|
||||
slackConfigAccessors,
|
||||
slackConfigBase,
|
||||
slackSetupWizard,
|
||||
} from "./plugin-shared.js";
|
||||
import type { SlackProbe } from "./probe.js";
|
||||
import { resolveSlackUserAllowlist } from "./resolve-users.js";
|
||||
import { getSlackRuntime } from "./runtime.js";
|
||||
import { fetchSlackScopes } from "./scopes.js";
|
||||
import { slackSetupAdapter } from "./setup-core.js";
|
||||
import { createSlackSetupWizardProxy, slackSetupAdapter } from "./setup-core.js";
|
||||
import {
|
||||
createSlackPluginBase,
|
||||
isSlackPluginAccountConfigured,
|
||||
slackConfigAccessors,
|
||||
} from "./shared.js";
|
||||
import { parseSlackTarget } from "./targets.js";
|
||||
import { buildSlackThreadingToolContext } from "./threading-tool-context.js";
|
||||
|
||||
const meta = getChatChannelMeta("slack");
|
||||
const SLACK_CHANNEL_TYPE_CACHE = new Map<string, "channel" | "group" | "dm" | "unknown">();
|
||||
|
||||
async function loadSlackChannelRuntime() {
|
||||
return await import("./channel.runtime.js");
|
||||
}
|
||||
|
||||
// Select the appropriate Slack token for read/write operations.
|
||||
function getTokenForOperation(
|
||||
account: ResolvedSlackAccount,
|
||||
@ -329,13 +328,15 @@ async function resolveSlackAllowlistNames(params: {
|
||||
return await resolveSlackUserAllowlist({ token, entries: params.entries });
|
||||
}
|
||||
|
||||
const slackSetupWizard = createSlackSetupWizardProxy(async () => ({
|
||||
slackSetupWizard: (await loadSlackChannelRuntime()).slackSetupWizard,
|
||||
}));
|
||||
|
||||
export const slackPlugin: ChannelPlugin<ResolvedSlackAccount> = {
|
||||
id: "slack",
|
||||
meta: {
|
||||
...meta,
|
||||
preferSessionLookupForAnnounceTarget: true,
|
||||
},
|
||||
setupWizard: slackSetupWizard,
|
||||
...createSlackPluginBase({
|
||||
setupWizard: slackSetupWizard,
|
||||
setup: slackSetupAdapter,
|
||||
}),
|
||||
pairing: {
|
||||
idLabel: "slackUserId",
|
||||
normalizeAllowEntry: (entry) => entry.replace(/^(slack|user):/i, ""),
|
||||
@ -364,42 +365,6 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount> = {
|
||||
}
|
||||
},
|
||||
},
|
||||
capabilities: {
|
||||
chatTypes: ["direct", "channel", "thread"],
|
||||
reactions: true,
|
||||
threads: true,
|
||||
media: true,
|
||||
nativeCommands: true,
|
||||
},
|
||||
agentPrompt: {
|
||||
messageToolHints: ({ cfg, accountId }) =>
|
||||
isSlackInteractiveRepliesEnabled({ cfg, accountId })
|
||||
? [
|
||||
"- Slack interactive replies: use `[[slack_buttons: Label:value, Other:other]]` to add action buttons that route clicks back as Slack interaction system events.",
|
||||
"- Slack selects: use `[[slack_select: Placeholder | Label:value, Other:other]]` to add a static select menu that routes the chosen value back as a Slack interaction system event.",
|
||||
]
|
||||
: [
|
||||
"- Slack interactive replies are disabled. If needed, ask to set `channels.slack.capabilities.interactiveReplies=true` (or the same under `channels.slack.accounts.<account>.capabilities`).",
|
||||
],
|
||||
},
|
||||
streaming: {
|
||||
blockStreamingCoalesceDefaults: { minChars: 1500, idleMs: 1000 },
|
||||
},
|
||||
reload: { configPrefixes: ["channels.slack"] },
|
||||
configSchema: buildChannelConfigSchema(SlackConfigSchema),
|
||||
config: {
|
||||
...slackConfigBase,
|
||||
isConfigured: (account) => isSlackPluginAccountConfigured(account),
|
||||
describeAccount: (account) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: isSlackPluginAccountConfigured(account),
|
||||
botTokenSource: account.botTokenSource,
|
||||
appTokenSource: account.appTokenSource,
|
||||
}),
|
||||
...slackConfigAccessors,
|
||||
},
|
||||
allowlist: {
|
||||
supportsScope: ({ scope }) => scope === "dm",
|
||||
readConfig: ({ cfg, accountId }) =>
|
||||
@ -569,14 +534,13 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount> = {
|
||||
extractToolSend: ({ args }) => extractSlackToolSend(args),
|
||||
handleAction: async (ctx) =>
|
||||
await handleSlackMessageAction({
|
||||
providerId: meta.id,
|
||||
providerId: "slack",
|
||||
ctx,
|
||||
includeReadThreadId: true,
|
||||
invoke: async (action, cfg, toolContext) =>
|
||||
await getSlackRuntime().channel.slack.handleSlackAction(action, cfg, toolContext),
|
||||
}),
|
||||
},
|
||||
setup: slackSetupAdapter,
|
||||
outbound: {
|
||||
deliveryMode: "direct",
|
||||
chunker: null,
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
|
||||
import {
|
||||
buildChannelConfigSchema,
|
||||
getChatChannelMeta,
|
||||
SlackConfigSchema,
|
||||
type ChannelPlugin,
|
||||
} from "openclaw/plugin-sdk/slack";
|
||||
import { patchChannelConfigForAccount } from "../../../src/channels/plugins/setup-wizard-helpers.js";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import { hasConfiguredSecretInput } from "../../../src/config/types.secrets.js";
|
||||
import { formatAllowFromLowercase } from "../../../src/plugin-sdk/allow-from.js";
|
||||
import {
|
||||
createScopedAccountConfigAccessors,
|
||||
createScopedChannelConfigBase,
|
||||
} from "openclaw/plugin-sdk/channel-config-helpers";
|
||||
import {
|
||||
formatDocsLink,
|
||||
hasConfiguredSecretInput,
|
||||
patchChannelConfigForAccount,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
} from "../../../src/plugin-sdk/channel-config-helpers.js";
|
||||
import { formatDocsLink } from "../../../src/terminal/links.js";
|
||||
import { inspectSlackAccount } from "./account-inspect.js";
|
||||
import {
|
||||
listSlackAccountIds,
|
||||
@ -16,6 +20,7 @@ import {
|
||||
resolveSlackAccount,
|
||||
type ResolvedSlackAccount,
|
||||
} from "./accounts.js";
|
||||
import { isSlackInteractiveRepliesEnabled } from "./interactive-replies.js";
|
||||
|
||||
export const SLACK_CHANNEL = "slack" as const;
|
||||
|
||||
@ -152,3 +157,66 @@ export const slackConfigBase = createScopedChannelConfigBase({
|
||||
defaultAccountId: resolveDefaultSlackAccountId,
|
||||
clearBaseFields: ["botToken", "appToken", "name"],
|
||||
});
|
||||
|
||||
export function createSlackPluginBase(params: {
|
||||
setupWizard: NonNullable<ChannelPlugin<ResolvedSlackAccount>["setupWizard"]>;
|
||||
setup: NonNullable<ChannelPlugin<ResolvedSlackAccount>["setup"]>;
|
||||
}): Pick<
|
||||
ChannelPlugin<ResolvedSlackAccount>,
|
||||
| "id"
|
||||
| "meta"
|
||||
| "setupWizard"
|
||||
| "capabilities"
|
||||
| "agentPrompt"
|
||||
| "streaming"
|
||||
| "reload"
|
||||
| "configSchema"
|
||||
| "config"
|
||||
| "setup"
|
||||
> {
|
||||
return {
|
||||
id: SLACK_CHANNEL,
|
||||
meta: {
|
||||
...getChatChannelMeta(SLACK_CHANNEL),
|
||||
preferSessionLookupForAnnounceTarget: true,
|
||||
},
|
||||
setupWizard: params.setupWizard,
|
||||
capabilities: {
|
||||
chatTypes: ["direct", "channel", "thread"],
|
||||
reactions: true,
|
||||
threads: true,
|
||||
media: true,
|
||||
nativeCommands: true,
|
||||
},
|
||||
agentPrompt: {
|
||||
messageToolHints: ({ cfg, accountId }) =>
|
||||
isSlackInteractiveRepliesEnabled({ cfg, accountId })
|
||||
? [
|
||||
"- Slack interactive replies: use `[[slack_buttons: Label:value, Other:other]]` to add action buttons that route clicks back as Slack interaction system events.",
|
||||
"- Slack selects: use `[[slack_select: Placeholder | Label:value, Other:other]]` to add a static select menu that routes the chosen value back as a Slack interaction system event.",
|
||||
]
|
||||
: [
|
||||
"- Slack interactive replies are disabled. If needed, ask to set `channels.slack.capabilities.interactiveReplies=true` (or the same under `channels.slack.accounts.<account>.capabilities`).",
|
||||
],
|
||||
},
|
||||
streaming: {
|
||||
blockStreamingCoalesceDefaults: { minChars: 1500, idleMs: 1000 },
|
||||
},
|
||||
reload: { configPrefixes: ["channels.slack"] },
|
||||
configSchema: buildChannelConfigSchema(SlackConfigSchema),
|
||||
config: {
|
||||
...slackConfigBase,
|
||||
isConfigured: (account) => isSlackPluginAccountConfigured(account),
|
||||
describeAccount: (account) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: isSlackPluginAccountConfigured(account),
|
||||
botTokenSource: account.botTokenSource,
|
||||
appTokenSource: account.appTokenSource,
|
||||
}),
|
||||
...slackConfigAccessors,
|
||||
},
|
||||
setup: params.setup,
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user