|
1 | | -import { handleSlackMessageAction as handleSlackMessageActionImpl } from "openclaw/plugin-sdk/slack"; |
| 1 | +import type { AgentToolResult } from "@mariozechner/pi-agent-core"; |
| 2 | +import { |
| 3 | + normalizeInteractiveReply, |
| 4 | + type ChannelMessageActionContext, |
| 5 | +} from "openclaw/plugin-sdk/channel-runtime"; |
| 6 | +import { readNumberParam, readStringParam } from "openclaw/plugin-sdk/slack-core"; |
| 7 | +import { parseSlackBlocksInput } from "./blocks-input.js"; |
| 8 | +import { buildSlackInteractiveBlocks } from "./blocks-render.js"; |
2 | 9 |
|
3 | | -type HandleSlackMessageAction = typeof import("openclaw/plugin-sdk/slack").handleSlackMessageAction; |
| 10 | +type SlackActionInvoke = ( |
| 11 | + action: Record<string, unknown>, |
| 12 | + cfg: ChannelMessageActionContext["cfg"], |
| 13 | + toolContext?: ChannelMessageActionContext["toolContext"], |
| 14 | +) => Promise<AgentToolResult<unknown>>; |
4 | 15 |
|
5 | | -export async function handleSlackMessageAction( |
6 | | - ...args: Parameters<HandleSlackMessageAction> |
7 | | -): ReturnType<HandleSlackMessageAction> { |
8 | | - return await handleSlackMessageActionImpl(...args); |
| 16 | +function readSlackBlocksParam(actionParams: Record<string, unknown>) { |
| 17 | + return parseSlackBlocksInput(actionParams.blocks) as Record<string, unknown>[] | undefined; |
| 18 | +} |
| 19 | + |
| 20 | +/** Translate generic channel action requests into Slack-specific tool invocations and payload shapes. */ |
| 21 | +export async function handleSlackMessageAction(params: { |
| 22 | + providerId: string; |
| 23 | + ctx: ChannelMessageActionContext; |
| 24 | + invoke: SlackActionInvoke; |
| 25 | + normalizeChannelId?: (channelId: string) => string; |
| 26 | + includeReadThreadId?: boolean; |
| 27 | +}): Promise<AgentToolResult<unknown>> { |
| 28 | + const { providerId, ctx, invoke, normalizeChannelId, includeReadThreadId = false } = params; |
| 29 | + const { action, cfg, params: actionParams } = ctx; |
| 30 | + const accountId = ctx.accountId ?? undefined; |
| 31 | + const resolveChannelId = () => { |
| 32 | + const channelId = |
| 33 | + readStringParam(actionParams, "channelId") ?? |
| 34 | + readStringParam(actionParams, "to", { required: true }); |
| 35 | + return normalizeChannelId ? normalizeChannelId(channelId) : channelId; |
| 36 | + }; |
| 37 | + |
| 38 | + if (action === "send") { |
| 39 | + const to = readStringParam(actionParams, "to", { required: true }); |
| 40 | + const content = readStringParam(actionParams, "message", { |
| 41 | + required: false, |
| 42 | + allowEmpty: true, |
| 43 | + }); |
| 44 | + const mediaUrl = readStringParam(actionParams, "media", { trim: false }); |
| 45 | + const interactive = normalizeInteractiveReply(actionParams.interactive); |
| 46 | + const interactiveBlocks = interactive ? buildSlackInteractiveBlocks(interactive) : undefined; |
| 47 | + const blocks = readSlackBlocksParam(actionParams) ?? interactiveBlocks; |
| 48 | + if (!content && !mediaUrl && !blocks) { |
| 49 | + throw new Error("Slack send requires message, blocks, or media."); |
| 50 | + } |
| 51 | + if (mediaUrl && blocks) { |
| 52 | + throw new Error("Slack send does not support blocks with media."); |
| 53 | + } |
| 54 | + const threadId = readStringParam(actionParams, "threadId"); |
| 55 | + const replyTo = readStringParam(actionParams, "replyTo"); |
| 56 | + return await invoke( |
| 57 | + { |
| 58 | + action: "sendMessage", |
| 59 | + to, |
| 60 | + content: content ?? "", |
| 61 | + mediaUrl: mediaUrl ?? undefined, |
| 62 | + accountId, |
| 63 | + threadTs: threadId ?? replyTo ?? undefined, |
| 64 | + ...(blocks ? { blocks } : {}), |
| 65 | + }, |
| 66 | + cfg, |
| 67 | + ctx.toolContext, |
| 68 | + ); |
| 69 | + } |
| 70 | + |
| 71 | + if (action === "react") { |
| 72 | + const messageId = readStringParam(actionParams, "messageId", { |
| 73 | + required: true, |
| 74 | + }); |
| 75 | + const emoji = readStringParam(actionParams, "emoji", { allowEmpty: true }); |
| 76 | + const remove = typeof actionParams.remove === "boolean" ? actionParams.remove : undefined; |
| 77 | + return await invoke( |
| 78 | + { |
| 79 | + action: "react", |
| 80 | + channelId: resolveChannelId(), |
| 81 | + messageId, |
| 82 | + emoji, |
| 83 | + remove, |
| 84 | + accountId, |
| 85 | + }, |
| 86 | + cfg, |
| 87 | + ); |
| 88 | + } |
| 89 | + |
| 90 | + if (action === "reactions") { |
| 91 | + const messageId = readStringParam(actionParams, "messageId", { |
| 92 | + required: true, |
| 93 | + }); |
| 94 | + const limit = readNumberParam(actionParams, "limit", { integer: true }); |
| 95 | + return await invoke( |
| 96 | + { |
| 97 | + action: "reactions", |
| 98 | + channelId: resolveChannelId(), |
| 99 | + messageId, |
| 100 | + limit, |
| 101 | + accountId, |
| 102 | + }, |
| 103 | + cfg, |
| 104 | + ); |
| 105 | + } |
| 106 | + |
| 107 | + if (action === "read") { |
| 108 | + const limit = readNumberParam(actionParams, "limit", { integer: true }); |
| 109 | + const readAction: Record<string, unknown> = { |
| 110 | + action: "readMessages", |
| 111 | + channelId: resolveChannelId(), |
| 112 | + limit, |
| 113 | + before: readStringParam(actionParams, "before"), |
| 114 | + after: readStringParam(actionParams, "after"), |
| 115 | + accountId, |
| 116 | + }; |
| 117 | + if (includeReadThreadId) { |
| 118 | + readAction.threadId = readStringParam(actionParams, "threadId"); |
| 119 | + } |
| 120 | + return await invoke(readAction, cfg); |
| 121 | + } |
| 122 | + |
| 123 | + if (action === "edit") { |
| 124 | + const messageId = readStringParam(actionParams, "messageId", { |
| 125 | + required: true, |
| 126 | + }); |
| 127 | + const content = readStringParam(actionParams, "message", { allowEmpty: true }); |
| 128 | + const blocks = readSlackBlocksParam(actionParams); |
| 129 | + if (!content && !blocks) { |
| 130 | + throw new Error("Slack edit requires message or blocks."); |
| 131 | + } |
| 132 | + return await invoke( |
| 133 | + { |
| 134 | + action: "editMessage", |
| 135 | + channelId: resolveChannelId(), |
| 136 | + messageId, |
| 137 | + content: content ?? "", |
| 138 | + blocks, |
| 139 | + accountId, |
| 140 | + }, |
| 141 | + cfg, |
| 142 | + ); |
| 143 | + } |
| 144 | + |
| 145 | + if (action === "delete") { |
| 146 | + const messageId = readStringParam(actionParams, "messageId", { |
| 147 | + required: true, |
| 148 | + }); |
| 149 | + return await invoke( |
| 150 | + { |
| 151 | + action: "deleteMessage", |
| 152 | + channelId: resolveChannelId(), |
| 153 | + messageId, |
| 154 | + accountId, |
| 155 | + }, |
| 156 | + cfg, |
| 157 | + ); |
| 158 | + } |
| 159 | + |
| 160 | + if (action === "pin" || action === "unpin" || action === "list-pins") { |
| 161 | + const messageId = |
| 162 | + action === "list-pins" |
| 163 | + ? undefined |
| 164 | + : readStringParam(actionParams, "messageId", { required: true }); |
| 165 | + return await invoke( |
| 166 | + { |
| 167 | + action: action === "pin" ? "pinMessage" : action === "unpin" ? "unpinMessage" : "listPins", |
| 168 | + channelId: resolveChannelId(), |
| 169 | + messageId, |
| 170 | + accountId, |
| 171 | + }, |
| 172 | + cfg, |
| 173 | + ); |
| 174 | + } |
| 175 | + |
| 176 | + if (action === "member-info") { |
| 177 | + const userId = readStringParam(actionParams, "userId", { required: true }); |
| 178 | + return await invoke({ action: "memberInfo", userId, accountId }, cfg); |
| 179 | + } |
| 180 | + |
| 181 | + if (action === "emoji-list") { |
| 182 | + const limit = readNumberParam(actionParams, "limit", { integer: true }); |
| 183 | + return await invoke({ action: "emojiList", limit, accountId }, cfg); |
| 184 | + } |
| 185 | + |
| 186 | + if (action === "download-file") { |
| 187 | + const fileId = readStringParam(actionParams, "fileId", { required: true }); |
| 188 | + const channelId = |
| 189 | + readStringParam(actionParams, "channelId") ?? readStringParam(actionParams, "to"); |
| 190 | + const threadId = |
| 191 | + readStringParam(actionParams, "threadId") ?? readStringParam(actionParams, "replyTo"); |
| 192 | + return await invoke( |
| 193 | + { |
| 194 | + action: "downloadFile", |
| 195 | + fileId, |
| 196 | + channelId: channelId ?? undefined, |
| 197 | + threadId: threadId ?? undefined, |
| 198 | + accountId, |
| 199 | + }, |
| 200 | + cfg, |
| 201 | + ); |
| 202 | + } |
| 203 | + |
| 204 | + throw new Error(`Action ${action} is not supported for provider ${providerId}.`); |
9 | 205 | } |
0 commit comments