121 lines
3.6 KiB
TypeScript
121 lines
3.6 KiB
TypeScript
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
import type { DiscordActionConfig } from "../../config/config.js";
|
|
import {
|
|
banMemberDiscord,
|
|
hasAnyGuildPermissionDiscord,
|
|
kickMemberDiscord,
|
|
timeoutMemberDiscord,
|
|
} from "../../discord/send.js";
|
|
import { type ActionGate, jsonResult, readStringParam } from "./common.js";
|
|
import {
|
|
isDiscordModerationAction,
|
|
readDiscordModerationCommand,
|
|
requiredGuildPermissionForModerationAction,
|
|
} from "./discord-actions-moderation-shared.js";
|
|
|
|
async function verifySenderModerationPermission(params: {
|
|
guildId: string;
|
|
senderUserId?: string;
|
|
requiredPermission: bigint;
|
|
accountId?: string;
|
|
}) {
|
|
// CLI/manual flows may not have sender context; enforce only when present.
|
|
if (!params.senderUserId) {
|
|
return;
|
|
}
|
|
const hasPermission = await hasAnyGuildPermissionDiscord(
|
|
params.guildId,
|
|
params.senderUserId,
|
|
[params.requiredPermission],
|
|
params.accountId ? { accountId: params.accountId } : undefined,
|
|
);
|
|
if (!hasPermission) {
|
|
throw new Error("Sender does not have required permissions for this moderation action.");
|
|
}
|
|
}
|
|
|
|
export async function handleDiscordModerationAction(
|
|
action: string,
|
|
params: Record<string, unknown>,
|
|
isActionEnabled: ActionGate<DiscordActionConfig>,
|
|
): Promise<AgentToolResult<unknown>> {
|
|
if (!isDiscordModerationAction(action)) {
|
|
throw new Error(`Unknown action: ${action}`);
|
|
}
|
|
if (!isActionEnabled("moderation", false)) {
|
|
throw new Error("Discord moderation is disabled.");
|
|
}
|
|
const command = readDiscordModerationCommand(action, params);
|
|
const accountId = readStringParam(params, "accountId");
|
|
const senderUserId = readStringParam(params, "senderUserId");
|
|
await verifySenderModerationPermission({
|
|
guildId: command.guildId,
|
|
senderUserId,
|
|
requiredPermission: requiredGuildPermissionForModerationAction(command.action),
|
|
accountId,
|
|
});
|
|
switch (command.action) {
|
|
case "timeout": {
|
|
const member = accountId
|
|
? await timeoutMemberDiscord(
|
|
{
|
|
guildId: command.guildId,
|
|
userId: command.userId,
|
|
durationMinutes: command.durationMinutes,
|
|
until: command.until,
|
|
reason: command.reason,
|
|
},
|
|
{ accountId },
|
|
)
|
|
: await timeoutMemberDiscord({
|
|
guildId: command.guildId,
|
|
userId: command.userId,
|
|
durationMinutes: command.durationMinutes,
|
|
until: command.until,
|
|
reason: command.reason,
|
|
});
|
|
return jsonResult({ ok: true, member });
|
|
}
|
|
case "kick": {
|
|
if (accountId) {
|
|
await kickMemberDiscord(
|
|
{
|
|
guildId: command.guildId,
|
|
userId: command.userId,
|
|
reason: command.reason,
|
|
},
|
|
{ accountId },
|
|
);
|
|
} else {
|
|
await kickMemberDiscord({
|
|
guildId: command.guildId,
|
|
userId: command.userId,
|
|
reason: command.reason,
|
|
});
|
|
}
|
|
return jsonResult({ ok: true });
|
|
}
|
|
case "ban": {
|
|
if (accountId) {
|
|
await banMemberDiscord(
|
|
{
|
|
guildId: command.guildId,
|
|
userId: command.userId,
|
|
reason: command.reason,
|
|
deleteMessageDays: command.deleteMessageDays,
|
|
},
|
|
{ accountId },
|
|
);
|
|
} else {
|
|
await banMemberDiscord({
|
|
guildId: command.guildId,
|
|
userId: command.userId,
|
|
reason: command.reason,
|
|
deleteMessageDays: command.deleteMessageDays,
|
|
});
|
|
}
|
|
return jsonResult({ ok: true });
|
|
}
|
|
}
|
|
}
|