fix: honor accountId in message actions
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { ClawdbotConfig } from "../../../config/config.js";
|
||||
type SendMessageDiscord = typeof import("../../../discord/send.js").sendMessageDiscord;
|
||||
@@ -32,6 +32,32 @@ const loadDiscordMessageActions = async () => {
|
||||
return mod.discordMessageActions;
|
||||
};
|
||||
|
||||
type SendMessageDiscord = typeof import("../../../discord/send.js").sendMessageDiscord;
|
||||
type SendPollDiscord = typeof import("../../../discord/send.js").sendPollDiscord;
|
||||
|
||||
const sendMessageDiscord = vi.fn<Parameters<SendMessageDiscord>, ReturnType<SendMessageDiscord>>(
|
||||
async () => ({ ok: true }) as Awaited<ReturnType<SendMessageDiscord>>,
|
||||
);
|
||||
const sendPollDiscord = vi.fn<Parameters<SendPollDiscord>, ReturnType<SendPollDiscord>>(
|
||||
async () => ({ ok: true }) as Awaited<ReturnType<SendPollDiscord>>,
|
||||
);
|
||||
|
||||
vi.mock("../../../discord/send.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../../discord/send.js")>(
|
||||
"../../../discord/send.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
sendMessageDiscord: (...args: Parameters<SendMessageDiscord>) => sendMessageDiscord(...args),
|
||||
sendPollDiscord: (...args: Parameters<SendPollDiscord>) => sendPollDiscord(...args),
|
||||
};
|
||||
});
|
||||
|
||||
const loadHandleDiscordMessageAction = async () => {
|
||||
const mod = await import("./discord/handle-action.js");
|
||||
return mod.handleDiscordMessageAction;
|
||||
};
|
||||
|
||||
describe("discord message actions", () => {
|
||||
it("lists channel and upload actions by default", async () => {
|
||||
const cfg = { channels: { discord: { token: "d0" } } } as ClawdbotConfig;
|
||||
@@ -53,3 +79,78 @@ describe("discord message actions", () => {
|
||||
expect(actions).not.toContain("channel-create");
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleDiscordMessageAction", () => {
|
||||
it("forwards context accountId for send", async () => {
|
||||
sendMessageDiscord.mockClear();
|
||||
const handleDiscordMessageAction = await loadHandleDiscordMessageAction();
|
||||
|
||||
await handleDiscordMessageAction({
|
||||
action: "send",
|
||||
params: {
|
||||
to: "channel:123",
|
||||
message: "hi",
|
||||
},
|
||||
cfg: {} as ClawdbotConfig,
|
||||
accountId: "ops",
|
||||
});
|
||||
|
||||
expect(sendMessageDiscord).toHaveBeenCalledWith(
|
||||
"channel:123",
|
||||
"hi",
|
||||
expect.objectContaining({
|
||||
accountId: "ops",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to params accountId when context missing", async () => {
|
||||
sendPollDiscord.mockClear();
|
||||
const handleDiscordMessageAction = await loadHandleDiscordMessageAction();
|
||||
|
||||
await handleDiscordMessageAction({
|
||||
action: "poll",
|
||||
params: {
|
||||
to: "channel:123",
|
||||
pollQuestion: "Ready?",
|
||||
pollOption: ["Yes", "No"],
|
||||
accountId: "marve",
|
||||
},
|
||||
cfg: {} as ClawdbotConfig,
|
||||
});
|
||||
|
||||
expect(sendPollDiscord).toHaveBeenCalledWith(
|
||||
"channel:123",
|
||||
expect.objectContaining({
|
||||
question: "Ready?",
|
||||
options: ["Yes", "No"],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
accountId: "marve",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards accountId for thread replies", async () => {
|
||||
sendMessageDiscord.mockClear();
|
||||
const handleDiscordMessageAction = await loadHandleDiscordMessageAction();
|
||||
|
||||
await handleDiscordMessageAction({
|
||||
action: "thread-reply",
|
||||
params: {
|
||||
channelId: "123",
|
||||
message: "hi",
|
||||
},
|
||||
cfg: {} as ClawdbotConfig,
|
||||
accountId: "ops",
|
||||
});
|
||||
|
||||
expect(sendMessageDiscord).toHaveBeenCalledWith(
|
||||
"channel:123",
|
||||
"hi",
|
||||
expect.objectContaining({
|
||||
accountId: "ops",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -80,7 +80,7 @@ export const discordMessageActions: ChannelMessageActionAdapter = {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
handleAction: async ({ action, params, cfg }) => {
|
||||
return await handleDiscordMessageAction({ action, params, cfg });
|
||||
handleAction: async ({ action, params, cfg, accountId }) => {
|
||||
return await handleDiscordMessageAction({ action, params, cfg, accountId });
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import { handleDiscordAction } from "../../../../agents/tools/discord-actions.js";
|
||||
import type { ChannelMessageActionContext } from "../../types.js";
|
||||
|
||||
type Ctx = Pick<ChannelMessageActionContext, "action" | "params" | "cfg">;
|
||||
type Ctx = Pick<ChannelMessageActionContext, "action" | "params" | "cfg" | "accountId">;
|
||||
|
||||
export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
ctx: Ctx;
|
||||
@@ -16,27 +16,37 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
}): Promise<AgentToolResult<unknown> | undefined> {
|
||||
const { ctx, resolveChannelId, readParentIdParam } = params;
|
||||
const { action, params: actionParams, cfg } = ctx;
|
||||
const accountId = ctx.accountId ?? readStringParam(actionParams, "accountId");
|
||||
|
||||
if (action === "member-info") {
|
||||
const userId = readStringParam(actionParams, "userId", { required: true });
|
||||
const guildId = readStringParam(actionParams, "guildId", {
|
||||
required: true,
|
||||
});
|
||||
return await handleDiscordAction({ action: "memberInfo", guildId, userId }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{ action: "memberInfo", accountId: accountId ?? undefined, guildId, userId },
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "role-info") {
|
||||
const guildId = readStringParam(actionParams, "guildId", {
|
||||
required: true,
|
||||
});
|
||||
return await handleDiscordAction({ action: "roleInfo", guildId }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{ action: "roleInfo", accountId: accountId ?? undefined, guildId },
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "emoji-list") {
|
||||
const guildId = readStringParam(actionParams, "guildId", {
|
||||
required: true,
|
||||
});
|
||||
return await handleDiscordAction({ action: "emojiList", guildId }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{ action: "emojiList", accountId: accountId ?? undefined, guildId },
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "emoji-upload") {
|
||||
@@ -50,7 +60,14 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
});
|
||||
const roleIds = readStringArrayParam(actionParams, "roleIds");
|
||||
return await handleDiscordAction(
|
||||
{ action: "emojiUpload", guildId, name, mediaUrl, roleIds },
|
||||
{
|
||||
action: "emojiUpload",
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
name,
|
||||
mediaUrl,
|
||||
roleIds,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
@@ -73,7 +90,15 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
trim: false,
|
||||
});
|
||||
return await handleDiscordAction(
|
||||
{ action: "stickerUpload", guildId, name, description, tags, mediaUrl },
|
||||
{
|
||||
action: "stickerUpload",
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
name,
|
||||
description,
|
||||
tags,
|
||||
mediaUrl,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
@@ -87,6 +112,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: action === "role-add" ? "roleAdd" : "roleRemove",
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
userId,
|
||||
roleId,
|
||||
@@ -99,14 +125,20 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
const channelId = readStringParam(actionParams, "channelId", {
|
||||
required: true,
|
||||
});
|
||||
return await handleDiscordAction({ action: "channelInfo", channelId }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{ action: "channelInfo", accountId: accountId ?? undefined, channelId },
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "channel-list") {
|
||||
const guildId = readStringParam(actionParams, "guildId", {
|
||||
required: true,
|
||||
});
|
||||
return await handleDiscordAction({ action: "channelList", guildId }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{ action: "channelList", accountId: accountId ?? undefined, guildId },
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "channel-create") {
|
||||
@@ -124,6 +156,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "channelCreate",
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
name,
|
||||
type: type ?? undefined,
|
||||
@@ -153,6 +186,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "channelEdit",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId,
|
||||
name: name ?? undefined,
|
||||
topic: topic ?? undefined,
|
||||
@@ -169,7 +203,10 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
const channelId = readStringParam(actionParams, "channelId", {
|
||||
required: true,
|
||||
});
|
||||
return await handleDiscordAction({ action: "channelDelete", channelId }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{ action: "channelDelete", accountId: accountId ?? undefined, channelId },
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "channel-move") {
|
||||
@@ -186,6 +223,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "channelMove",
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
channelId,
|
||||
parentId: parentId === undefined ? undefined : parentId,
|
||||
@@ -206,6 +244,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "categoryCreate",
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
name,
|
||||
position: position ?? undefined,
|
||||
@@ -225,6 +264,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "categoryEdit",
|
||||
accountId: accountId ?? undefined,
|
||||
categoryId,
|
||||
name: name ?? undefined,
|
||||
position: position ?? undefined,
|
||||
@@ -237,7 +277,10 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
const categoryId = readStringParam(actionParams, "categoryId", {
|
||||
required: true,
|
||||
});
|
||||
return await handleDiscordAction({ action: "categoryDelete", categoryId }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{ action: "categoryDelete", accountId: accountId ?? undefined, categoryId },
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "voice-status") {
|
||||
@@ -245,14 +288,20 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
required: true,
|
||||
});
|
||||
const userId = readStringParam(actionParams, "userId", { required: true });
|
||||
return await handleDiscordAction({ action: "voiceStatus", guildId, userId }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{ action: "voiceStatus", accountId: accountId ?? undefined, guildId, userId },
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "event-list") {
|
||||
const guildId = readStringParam(actionParams, "guildId", {
|
||||
required: true,
|
||||
});
|
||||
return await handleDiscordAction({ action: "eventList", guildId }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{ action: "eventList", accountId: accountId ?? undefined, guildId },
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "event-create") {
|
||||
@@ -271,6 +320,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "eventCreate",
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
name,
|
||||
startTime,
|
||||
@@ -301,6 +351,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: discordAction,
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
userId,
|
||||
durationMinutes,
|
||||
@@ -325,6 +376,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "threadList",
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
channelId,
|
||||
includeArchived,
|
||||
@@ -344,6 +396,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "threadReply",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId: resolveChannelId(),
|
||||
content,
|
||||
mediaUrl: mediaUrl ?? undefined,
|
||||
@@ -361,6 +414,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "searchMessages",
|
||||
accountId: accountId ?? undefined,
|
||||
guildId,
|
||||
content: query,
|
||||
channelId: readStringParam(actionParams, "channelId"),
|
||||
|
||||
@@ -18,9 +18,10 @@ function readParentIdParam(params: Record<string, unknown>): string | null | und
|
||||
}
|
||||
|
||||
export async function handleDiscordMessageAction(
|
||||
ctx: Pick<ChannelMessageActionContext, "action" | "params" | "cfg">,
|
||||
ctx: Pick<ChannelMessageActionContext, "action" | "params" | "cfg" | "accountId">,
|
||||
): Promise<AgentToolResult<unknown>> {
|
||||
const { action, params, cfg } = ctx;
|
||||
const accountId = ctx.accountId ?? readStringParam(params, "accountId");
|
||||
|
||||
const resolveChannelId = () =>
|
||||
resolveDiscordChannelId(
|
||||
@@ -39,6 +40,7 @@ export async function handleDiscordMessageAction(
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "sendMessage",
|
||||
accountId: accountId ?? undefined,
|
||||
to,
|
||||
content,
|
||||
mediaUrl: mediaUrl ?? undefined,
|
||||
@@ -62,6 +64,7 @@ export async function handleDiscordMessageAction(
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "poll",
|
||||
accountId: accountId ?? undefined,
|
||||
to,
|
||||
question,
|
||||
answers,
|
||||
@@ -80,6 +83,7 @@ export async function handleDiscordMessageAction(
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "react",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId: resolveChannelId(),
|
||||
messageId,
|
||||
emoji,
|
||||
@@ -93,7 +97,13 @@ export async function handleDiscordMessageAction(
|
||||
const messageId = readStringParam(params, "messageId", { required: true });
|
||||
const limit = readNumberParam(params, "limit", { integer: true });
|
||||
return await handleDiscordAction(
|
||||
{ action: "reactions", channelId: resolveChannelId(), messageId, limit },
|
||||
{
|
||||
action: "reactions",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId: resolveChannelId(),
|
||||
messageId,
|
||||
limit,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
@@ -103,6 +113,7 @@ export async function handleDiscordMessageAction(
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "readMessages",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId: resolveChannelId(),
|
||||
limit,
|
||||
before: readStringParam(params, "before"),
|
||||
@@ -119,6 +130,7 @@ export async function handleDiscordMessageAction(
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "editMessage",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId: resolveChannelId(),
|
||||
messageId,
|
||||
content,
|
||||
@@ -130,7 +142,12 @@ export async function handleDiscordMessageAction(
|
||||
if (action === "delete") {
|
||||
const messageId = readStringParam(params, "messageId", { required: true });
|
||||
return await handleDiscordAction(
|
||||
{ action: "deleteMessage", channelId: resolveChannelId(), messageId },
|
||||
{
|
||||
action: "deleteMessage",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId: resolveChannelId(),
|
||||
messageId,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
@@ -141,6 +158,7 @@ export async function handleDiscordMessageAction(
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: action === "pin" ? "pinMessage" : action === "unpin" ? "unpinMessage" : "listPins",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId: resolveChannelId(),
|
||||
messageId,
|
||||
},
|
||||
@@ -149,7 +167,14 @@ export async function handleDiscordMessageAction(
|
||||
}
|
||||
|
||||
if (action === "permissions") {
|
||||
return await handleDiscordAction({ action: "permissions", channelId: resolveChannelId() }, cfg);
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "permissions",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId: resolveChannelId(),
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "thread-create") {
|
||||
@@ -161,6 +186,7 @@ export async function handleDiscordMessageAction(
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "threadCreate",
|
||||
accountId: accountId ?? undefined,
|
||||
channelId: resolveChannelId(),
|
||||
name,
|
||||
messageId,
|
||||
@@ -179,6 +205,7 @@ export async function handleDiscordMessageAction(
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: "sticker",
|
||||
accountId: accountId ?? undefined,
|
||||
to: readStringParam(params, "to", { required: true }),
|
||||
stickerIds,
|
||||
content: readStringParam(params, "message"),
|
||||
|
||||
Reference in New Issue
Block a user