300 lines
9.1 KiB
TypeScript
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}.`);
|
|
}
|