2026-03-16 21:16:32 -07:00

300 lines
9.1 KiB
TypeScript

import type { AgentToolResult } from "@mariozechner/pi-agent-core";
import {
readNumberParam,
readStringArrayParam,
readStringParam,
} from "openclaw/plugin-sdk/agent-runtime";
import { readDiscordParentIdParam } from "openclaw/plugin-sdk/agent-runtime";
import { handleDiscordAction } from "openclaw/plugin-sdk/agent-runtime";
import { readBooleanParam } from "openclaw/plugin-sdk/boolean-param";
import { resolveReactionMessageId } from "openclaw/plugin-sdk/channel-runtime";
import type { ChannelMessageActionContext } from "openclaw/plugin-sdk/channel-runtime";
import { normalizeInteractiveReply } from "openclaw/plugin-sdk/channel-runtime";
import { buildDiscordInteractiveComponents } from "../shared-interactive.js";
import { resolveDiscordChannelId } from "../targets.js";
import { tryHandleDiscordMessageActionGuildAdmin } from "./handle-action.guild-admin.js";
const providerId = "discord";
export async function handleDiscordMessageAction(
ctx: Pick<
ChannelMessageActionContext,
| "action"
| "params"
| "cfg"
| "accountId"
| "requesterSenderId"
| "toolContext"
| "mediaLocalRoots"
>,
): Promise<AgentToolResult<unknown>> {
const { action, params, cfg } = ctx;
const accountId = ctx.accountId ?? readStringParam(params, "accountId");
const actionOptions = {
mediaLocalRoots: ctx.mediaLocalRoots,
} as const;
const resolveChannelId = () =>
resolveDiscordChannelId(
readStringParam(params, "channelId") ?? readStringParam(params, "to", { required: true }),
);
if (action === "send") {
const to = readStringParam(params, "to", { required: true });
const asVoice = readBooleanParam(params, "asVoice") === true;
const rawComponents =
params.components ??
buildDiscordInteractiveComponents(normalizeInteractiveReply(params.interactive));
const hasComponents =
Boolean(rawComponents) &&
(typeof rawComponents === "function" || typeof rawComponents === "object");
const components = hasComponents ? rawComponents : undefined;
const content = readStringParam(params, "message", {
required: !asVoice && !hasComponents,
allowEmpty: true,
});
// Support media, path, and filePath for media URL
const mediaUrl =
readStringParam(params, "media", { trim: false }) ??
readStringParam(params, "path", { trim: false }) ??
readStringParam(params, "filePath", { trim: false });
const filename = readStringParam(params, "filename");
const replyTo = readStringParam(params, "replyTo");
const rawEmbeds = params.embeds;
const embeds = Array.isArray(rawEmbeds) ? rawEmbeds : undefined;
const silent = readBooleanParam(params, "silent") === true;
const sessionKey = readStringParam(params, "__sessionKey");
const agentId = readStringParam(params, "__agentId");
return await handleDiscordAction(
{
action: "sendMessage",
accountId: accountId ?? undefined,
to,
content,
mediaUrl: mediaUrl ?? undefined,
filename: filename ?? undefined,
replyTo: replyTo ?? undefined,
components,
embeds,
asVoice,
silent,
__sessionKey: sessionKey ?? undefined,
__agentId: agentId ?? undefined,
},
cfg,
actionOptions,
);
}
if (action === "poll") {
const to = readStringParam(params, "to", { required: true });
const question = readStringParam(params, "pollQuestion", {
required: true,
});
const answers = readStringArrayParam(params, "pollOption", { required: true });
const allowMultiselect = readBooleanParam(params, "pollMulti");
const durationHours = readNumberParam(params, "pollDurationHours", {
integer: true,
strict: true,
});
return await handleDiscordAction(
{
action: "poll",
accountId: accountId ?? undefined,
to,
question,
answers,
allowMultiselect,
durationHours: durationHours ?? undefined,
content: readStringParam(params, "message"),
},
cfg,
actionOptions,
);
}
if (action === "react") {
const messageIdRaw = resolveReactionMessageId({ args: params, toolContext: ctx.toolContext });
const messageId = messageIdRaw != null ? String(messageIdRaw).trim() : "";
if (!messageId) {
throw new Error(
"messageId required. Provide messageId explicitly or react to the current inbound message.",
);
}
const emoji = readStringParam(params, "emoji", { allowEmpty: true });
const remove = readBooleanParam(params, "remove");
return await handleDiscordAction(
{
action: "react",
accountId: accountId ?? undefined,
channelId: resolveChannelId(),
messageId,
emoji,
remove,
},
cfg,
actionOptions,
);
}
if (action === "reactions") {
const messageId = readStringParam(params, "messageId", { required: true });
const limit = readNumberParam(params, "limit", { integer: true });
return await handleDiscordAction(
{
action: "reactions",
accountId: accountId ?? undefined,
channelId: resolveChannelId(),
messageId,
limit,
},
cfg,
actionOptions,
);
}
if (action === "read") {
const limit = readNumberParam(params, "limit", { integer: true });
return await handleDiscordAction(
{
action: "readMessages",
accountId: accountId ?? undefined,
channelId: resolveChannelId(),
limit,
before: readStringParam(params, "before"),
after: readStringParam(params, "after"),
around: readStringParam(params, "around"),
},
cfg,
actionOptions,
);
}
if (action === "edit") {
const messageId = readStringParam(params, "messageId", { required: true });
const content = readStringParam(params, "message", { required: true });
return await handleDiscordAction(
{
action: "editMessage",
accountId: accountId ?? undefined,
channelId: resolveChannelId(),
messageId,
content,
},
cfg,
actionOptions,
);
}
if (action === "delete") {
const messageId = readStringParam(params, "messageId", { required: true });
return await handleDiscordAction(
{
action: "deleteMessage",
accountId: accountId ?? undefined,
channelId: resolveChannelId(),
messageId,
},
cfg,
actionOptions,
);
}
if (action === "pin" || action === "unpin" || action === "list-pins") {
const messageId =
action === "list-pins" ? undefined : readStringParam(params, "messageId", { required: true });
return await handleDiscordAction(
{
action: action === "pin" ? "pinMessage" : action === "unpin" ? "unpinMessage" : "listPins",
accountId: accountId ?? undefined,
channelId: resolveChannelId(),
messageId,
},
cfg,
actionOptions,
);
}
if (action === "permissions") {
return await handleDiscordAction(
{
action: "permissions",
accountId: accountId ?? undefined,
channelId: resolveChannelId(),
},
cfg,
actionOptions,
);
}
if (action === "thread-create") {
const name = readStringParam(params, "threadName", { required: true });
const messageId = readStringParam(params, "messageId");
const content = readStringParam(params, "message");
const autoArchiveMinutes = readNumberParam(params, "autoArchiveMin", {
integer: true,
});
const appliedTags = readStringArrayParam(params, "appliedTags");
return await handleDiscordAction(
{
action: "threadCreate",
accountId: accountId ?? undefined,
channelId: resolveChannelId(),
name,
messageId,
content,
autoArchiveMinutes,
appliedTags: appliedTags ?? undefined,
},
cfg,
actionOptions,
);
}
if (action === "sticker") {
const stickerIds =
readStringArrayParam(params, "stickerId", {
required: true,
label: "sticker-id",
}) ?? [];
return await handleDiscordAction(
{
action: "sticker",
accountId: accountId ?? undefined,
to: readStringParam(params, "to", { required: true }),
stickerIds,
content: readStringParam(params, "message"),
},
cfg,
actionOptions,
);
}
if (action === "set-presence") {
return await handleDiscordAction(
{
action: "setPresence",
accountId: accountId ?? undefined,
status: readStringParam(params, "status"),
activityType: readStringParam(params, "activityType"),
activityName: readStringParam(params, "activityName"),
activityUrl: readStringParam(params, "activityUrl"),
activityState: readStringParam(params, "activityState"),
},
cfg,
actionOptions,
);
}
const adminResult = await tryHandleDiscordMessageActionGuildAdmin({
ctx,
resolveChannelId,
readParentIdParam: readDiscordParentIdParam,
});
if (adminResult !== undefined) {
return adminResult;
}
throw new Error(`Action ${String(action)} is not supported for provider ${providerId}.`);
}