openclaw-vainplex/src/agents/tools/slack-actions.ts
2026-01-09 08:30:24 +01:00

212 lines
7 KiB
TypeScript

import type { AgentToolResult } from "@mariozechner/pi-agent-core";
import type { ClawdbotConfig } from "../../config/config.js";
import { resolveSlackAccount } from "../../slack/accounts.js";
import {
deleteSlackMessage,
editSlackMessage,
getSlackMemberInfo,
listSlackEmojis,
listSlackPins,
listSlackReactions,
pinSlackMessage,
reactSlackMessage,
readSlackMessages,
removeOwnSlackReactions,
removeSlackReaction,
sendSlackMessage,
unpinSlackMessage,
} from "../../slack/actions.js";
import {
createActionGate,
jsonResult,
readReactionParams,
readStringParam,
} from "./common.js";
const messagingActions = new Set([
"sendMessage",
"editMessage",
"deleteMessage",
"readMessages",
]);
const reactionsActions = new Set(["react", "reactions"]);
const pinActions = new Set(["pinMessage", "unpinMessage", "listPins"]);
export async function handleSlackAction(
params: Record<string, unknown>,
cfg: ClawdbotConfig,
): Promise<AgentToolResult<unknown>> {
const action = readStringParam(params, "action", { required: true });
const accountId = readStringParam(params, "accountId");
const accountOpts = accountId ? { accountId } : undefined;
const account = resolveSlackAccount({ cfg, accountId });
const actionConfig = account.actions ?? cfg.slack?.actions;
const isActionEnabled = createActionGate(actionConfig);
if (reactionsActions.has(action)) {
if (!isActionEnabled("reactions")) {
throw new Error("Slack reactions are disabled.");
}
const channelId = readStringParam(params, "channelId", { required: true });
const messageId = readStringParam(params, "messageId", { required: true });
if (action === "react") {
const { emoji, remove, isEmpty } = readReactionParams(params, {
removeErrorMessage: "Emoji is required to remove a Slack reaction.",
});
if (remove) {
if (accountOpts) {
await removeSlackReaction(channelId, messageId, emoji, accountOpts);
} else {
await removeSlackReaction(channelId, messageId, emoji);
}
return jsonResult({ ok: true, removed: emoji });
}
if (isEmpty) {
const removed = accountOpts
? await removeOwnSlackReactions(channelId, messageId, accountOpts)
: await removeOwnSlackReactions(channelId, messageId);
return jsonResult({ ok: true, removed });
}
if (accountOpts) {
await reactSlackMessage(channelId, messageId, emoji, accountOpts);
} else {
await reactSlackMessage(channelId, messageId, emoji);
}
return jsonResult({ ok: true, added: emoji });
}
const reactions = accountOpts
? await listSlackReactions(channelId, messageId, accountOpts)
: await listSlackReactions(channelId, messageId);
return jsonResult({ ok: true, reactions });
}
if (messagingActions.has(action)) {
if (!isActionEnabled("messages")) {
throw new Error("Slack messages are disabled.");
}
switch (action) {
case "sendMessage": {
const to = readStringParam(params, "to", { required: true });
const content = readStringParam(params, "content", { required: true });
const mediaUrl = readStringParam(params, "mediaUrl");
const threadTs = readStringParam(params, "threadTs");
const result = await sendSlackMessage(to, content, {
accountId: accountId ?? undefined,
mediaUrl: mediaUrl ?? undefined,
threadTs: threadTs ?? undefined,
});
return jsonResult({ ok: true, result });
}
case "editMessage": {
const channelId = readStringParam(params, "channelId", {
required: true,
});
const messageId = readStringParam(params, "messageId", {
required: true,
});
const content = readStringParam(params, "content", {
required: true,
});
if (accountOpts) {
await editSlackMessage(channelId, messageId, content, accountOpts);
} else {
await editSlackMessage(channelId, messageId, content);
}
return jsonResult({ ok: true });
}
case "deleteMessage": {
const channelId = readStringParam(params, "channelId", {
required: true,
});
const messageId = readStringParam(params, "messageId", {
required: true,
});
if (accountOpts) {
await deleteSlackMessage(channelId, messageId, accountOpts);
} else {
await deleteSlackMessage(channelId, messageId);
}
return jsonResult({ ok: true });
}
case "readMessages": {
const channelId = readStringParam(params, "channelId", {
required: true,
});
const limitRaw = params.limit;
const limit =
typeof limitRaw === "number" && Number.isFinite(limitRaw)
? limitRaw
: undefined;
const before = readStringParam(params, "before");
const after = readStringParam(params, "after");
const result = await readSlackMessages(channelId, {
accountId: accountId ?? undefined,
limit,
before: before ?? undefined,
after: after ?? undefined,
});
return jsonResult({ ok: true, ...result });
}
default:
break;
}
}
if (pinActions.has(action)) {
if (!isActionEnabled("pins")) {
throw new Error("Slack pins are disabled.");
}
const channelId = readStringParam(params, "channelId", { required: true });
if (action === "pinMessage") {
const messageId = readStringParam(params, "messageId", {
required: true,
});
if (accountOpts) {
await pinSlackMessage(channelId, messageId, accountOpts);
} else {
await pinSlackMessage(channelId, messageId);
}
return jsonResult({ ok: true });
}
if (action === "unpinMessage") {
const messageId = readStringParam(params, "messageId", {
required: true,
});
if (accountOpts) {
await unpinSlackMessage(channelId, messageId, accountOpts);
} else {
await unpinSlackMessage(channelId, messageId);
}
return jsonResult({ ok: true });
}
const pins = accountOpts
? await listSlackPins(channelId, accountOpts)
: await listSlackPins(channelId);
return jsonResult({ ok: true, pins });
}
if (action === "memberInfo") {
if (!isActionEnabled("memberInfo")) {
throw new Error("Slack member info is disabled.");
}
const userId = readStringParam(params, "userId", { required: true });
const info = accountOpts
? await getSlackMemberInfo(userId, accountOpts)
: await getSlackMemberInfo(userId);
return jsonResult({ ok: true, info });
}
if (action === "emojiList") {
if (!isActionEnabled("emojiList")) {
throw new Error("Slack emoji list is disabled.");
}
const emojis = accountOpts
? await listSlackEmojis(accountOpts)
: await listSlackEmojis();
return jsonResult({ ok: true, emojis });
}
throw new Error(`Unknown action: ${action}`);
}