fix(config): allow discord action flags in schema
Ensure discord action flags survive config validation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
Peter Steinberger
parent
ff645524d8
commit
72f28be648
@@ -227,6 +227,8 @@ Outbound Discord API calls retry on rate limits (429) using Discord `retry_after
|
|||||||
actions: {
|
actions: {
|
||||||
reactions: true,
|
reactions: true,
|
||||||
stickers: true,
|
stickers: true,
|
||||||
|
emojiUploads: true,
|
||||||
|
stickerUploads: true,
|
||||||
polls: true,
|
polls: true,
|
||||||
permissions: true,
|
permissions: true,
|
||||||
messages: true,
|
messages: true,
|
||||||
@@ -237,6 +239,7 @@ Outbound Discord API calls retry on rate limits (429) using Discord `retry_after
|
|||||||
roleInfo: true,
|
roleInfo: true,
|
||||||
roles: false,
|
roles: false,
|
||||||
channelInfo: true,
|
channelInfo: true,
|
||||||
|
channels: true,
|
||||||
voiceStatus: true,
|
voiceStatus: true,
|
||||||
events: true,
|
events: true,
|
||||||
moderation: false
|
moderation: false
|
||||||
@@ -304,8 +307,9 @@ ack reaction after the bot replies.
|
|||||||
- `retry`: retry policy for outbound Discord API calls (attempts, minDelayMs, maxDelayMs, jitter).
|
- `retry`: retry policy for outbound Discord API calls (attempts, minDelayMs, maxDelayMs, jitter).
|
||||||
- `actions`: per-action tool gates; omit to allow all (set `false` to disable).
|
- `actions`: per-action tool gates; omit to allow all (set `false` to disable).
|
||||||
- `reactions` (covers react + read reactions)
|
- `reactions` (covers react + read reactions)
|
||||||
- `stickers`, `polls`, `permissions`, `messages`, `threads`, `pins`, `search`
|
- `stickers`, `emojiUploads`, `stickerUploads`, `polls`, `permissions`, `messages`, `threads`, `pins`, `search`
|
||||||
- `memberInfo`, `roleInfo`, `channelInfo`, `voiceStatus`, `events`
|
- `memberInfo`, `roleInfo`, `channelInfo`, `voiceStatus`, `events`
|
||||||
|
- `channels` (create/edit/delete channels + categories + permissions)
|
||||||
- `roles` (role add/remove, default `false`)
|
- `roles` (role add/remove, default `false`)
|
||||||
- `moderation` (timeout/kick/ban, default `false`)
|
- `moderation` (timeout/kick/ban, default `false`)
|
||||||
|
|
||||||
@@ -321,6 +325,8 @@ Reaction notifications use `guilds.<id>.reactionNotifications`:
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| reactions | enabled | React + list reactions + emojiList |
|
| reactions | enabled | React + list reactions + emojiList |
|
||||||
| stickers | enabled | Send stickers |
|
| stickers | enabled | Send stickers |
|
||||||
|
| emojiUploads | enabled | Upload emojis |
|
||||||
|
| stickerUploads | enabled | Upload stickers |
|
||||||
| polls | enabled | Create polls |
|
| polls | enabled | Create polls |
|
||||||
| permissions | enabled | Channel permission snapshot |
|
| permissions | enabled | Channel permission snapshot |
|
||||||
| messages | enabled | Read/send/edit/delete |
|
| messages | enabled | Read/send/edit/delete |
|
||||||
@@ -330,6 +336,7 @@ Reaction notifications use `guilds.<id>.reactionNotifications`:
|
|||||||
| memberInfo | enabled | Member info |
|
| memberInfo | enabled | Member info |
|
||||||
| roleInfo | enabled | Role list |
|
| roleInfo | enabled | Role list |
|
||||||
| channelInfo | enabled | Channel info + list |
|
| channelInfo | enabled | Channel info + list |
|
||||||
|
| channels | enabled | Channel/category management |
|
||||||
| voiceStatus | enabled | Voice state lookup |
|
| voiceStatus | enabled | Voice state lookup |
|
||||||
| events | enabled | List/create scheduled events |
|
| events | enabled | List/create scheduled events |
|
||||||
| roles | disabled | Role add/remove |
|
| roles | disabled | Role add/remove |
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ export async function handleDiscordGuildAction(
|
|||||||
return jsonResult({ ok: true, event });
|
return jsonResult({ ok: true, event });
|
||||||
}
|
}
|
||||||
case "channelCreate": {
|
case "channelCreate": {
|
||||||
if (!isActionEnabled("channels", false)) {
|
if (!isActionEnabled("channels")) {
|
||||||
throw new Error("Discord channel management is disabled.");
|
throw new Error("Discord channel management is disabled.");
|
||||||
}
|
}
|
||||||
const guildId = readStringParam(params, "guildId", { required: true });
|
const guildId = readStringParam(params, "guildId", { required: true });
|
||||||
@@ -241,7 +241,7 @@ export async function handleDiscordGuildAction(
|
|||||||
return jsonResult({ ok: true, channel });
|
return jsonResult({ ok: true, channel });
|
||||||
}
|
}
|
||||||
case "channelEdit": {
|
case "channelEdit": {
|
||||||
if (!isActionEnabled("channels", false)) {
|
if (!isActionEnabled("channels")) {
|
||||||
throw new Error("Discord channel management is disabled.");
|
throw new Error("Discord channel management is disabled.");
|
||||||
}
|
}
|
||||||
const channelId = readStringParam(params, "channelId", {
|
const channelId = readStringParam(params, "channelId", {
|
||||||
@@ -267,7 +267,7 @@ export async function handleDiscordGuildAction(
|
|||||||
return jsonResult({ ok: true, channel });
|
return jsonResult({ ok: true, channel });
|
||||||
}
|
}
|
||||||
case "channelDelete": {
|
case "channelDelete": {
|
||||||
if (!isActionEnabled("channels", false)) {
|
if (!isActionEnabled("channels")) {
|
||||||
throw new Error("Discord channel management is disabled.");
|
throw new Error("Discord channel management is disabled.");
|
||||||
}
|
}
|
||||||
const channelId = readStringParam(params, "channelId", {
|
const channelId = readStringParam(params, "channelId", {
|
||||||
@@ -277,7 +277,7 @@ export async function handleDiscordGuildAction(
|
|||||||
return jsonResult(result);
|
return jsonResult(result);
|
||||||
}
|
}
|
||||||
case "channelMove": {
|
case "channelMove": {
|
||||||
if (!isActionEnabled("channels", false)) {
|
if (!isActionEnabled("channels")) {
|
||||||
throw new Error("Discord channel management is disabled.");
|
throw new Error("Discord channel management is disabled.");
|
||||||
}
|
}
|
||||||
const guildId = readStringParam(params, "guildId", { required: true });
|
const guildId = readStringParam(params, "guildId", { required: true });
|
||||||
@@ -295,7 +295,7 @@ export async function handleDiscordGuildAction(
|
|||||||
return jsonResult({ ok: true });
|
return jsonResult({ ok: true });
|
||||||
}
|
}
|
||||||
case "categoryCreate": {
|
case "categoryCreate": {
|
||||||
if (!isActionEnabled("channels", false)) {
|
if (!isActionEnabled("channels")) {
|
||||||
throw new Error("Discord channel management is disabled.");
|
throw new Error("Discord channel management is disabled.");
|
||||||
}
|
}
|
||||||
const guildId = readStringParam(params, "guildId", { required: true });
|
const guildId = readStringParam(params, "guildId", { required: true });
|
||||||
@@ -310,7 +310,7 @@ export async function handleDiscordGuildAction(
|
|||||||
return jsonResult({ ok: true, category: channel });
|
return jsonResult({ ok: true, category: channel });
|
||||||
}
|
}
|
||||||
case "categoryEdit": {
|
case "categoryEdit": {
|
||||||
if (!isActionEnabled("channels", false)) {
|
if (!isActionEnabled("channels")) {
|
||||||
throw new Error("Discord channel management is disabled.");
|
throw new Error("Discord channel management is disabled.");
|
||||||
}
|
}
|
||||||
const categoryId = readStringParam(params, "categoryId", {
|
const categoryId = readStringParam(params, "categoryId", {
|
||||||
@@ -326,7 +326,7 @@ export async function handleDiscordGuildAction(
|
|||||||
return jsonResult({ ok: true, category: channel });
|
return jsonResult({ ok: true, category: channel });
|
||||||
}
|
}
|
||||||
case "categoryDelete": {
|
case "categoryDelete": {
|
||||||
if (!isActionEnabled("channels", false)) {
|
if (!isActionEnabled("channels")) {
|
||||||
throw new Error("Discord channel management is disabled.");
|
throw new Error("Discord channel management is disabled.");
|
||||||
}
|
}
|
||||||
const categoryId = readStringParam(params, "categoryId", {
|
const categoryId = readStringParam(params, "categoryId", {
|
||||||
@@ -336,7 +336,7 @@ export async function handleDiscordGuildAction(
|
|||||||
return jsonResult(result);
|
return jsonResult(result);
|
||||||
}
|
}
|
||||||
case "channelPermissionSet": {
|
case "channelPermissionSet": {
|
||||||
if (!isActionEnabled("channels", false)) {
|
if (!isActionEnabled("channels")) {
|
||||||
throw new Error("Discord channel management is disabled.");
|
throw new Error("Discord channel management is disabled.");
|
||||||
}
|
}
|
||||||
const channelId = readStringParam(params, "channelId", {
|
const channelId = readStringParam(params, "channelId", {
|
||||||
@@ -359,7 +359,7 @@ export async function handleDiscordGuildAction(
|
|||||||
return jsonResult({ ok: true });
|
return jsonResult({ ok: true });
|
||||||
}
|
}
|
||||||
case "channelPermissionRemove": {
|
case "channelPermissionRemove": {
|
||||||
if (!isActionEnabled("channels", false)) {
|
if (!isActionEnabled("channels")) {
|
||||||
throw new Error("Discord channel management is disabled.");
|
throw new Error("Discord channel management is disabled.");
|
||||||
}
|
}
|
||||||
const channelId = readStringParam(params, "channelId", {
|
const channelId = readStringParam(params, "channelId", {
|
||||||
|
|||||||
24
src/channels/plugins/actions/discord.test.ts
Normal file
24
src/channels/plugins/actions/discord.test.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import type { ClawdbotConfig } from "../../../config/config.js";
|
||||||
|
import { discordMessageActions } from "./discord.js";
|
||||||
|
|
||||||
|
describe("discord message actions", () => {
|
||||||
|
it("lists channel and upload actions by default", () => {
|
||||||
|
const cfg = { channels: { discord: { token: "d0" } } } as ClawdbotConfig;
|
||||||
|
const actions = discordMessageActions.listActions?.({ cfg }) ?? [];
|
||||||
|
|
||||||
|
expect(actions).toContain("emoji-upload");
|
||||||
|
expect(actions).toContain("sticker-upload");
|
||||||
|
expect(actions).toContain("channel-create");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("respects disabled channel actions", () => {
|
||||||
|
const cfg = {
|
||||||
|
channels: { discord: { token: "d0", actions: { channels: false } } },
|
||||||
|
} as ClawdbotConfig;
|
||||||
|
const actions = discordMessageActions.listActions?.({ cfg }) ?? [];
|
||||||
|
|
||||||
|
expect(actions).not.toContain("channel-create");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -47,7 +47,7 @@ export const discordMessageActions: ChannelMessageActionAdapter = {
|
|||||||
actions.add("channel-info");
|
actions.add("channel-info");
|
||||||
actions.add("channel-list");
|
actions.add("channel-list");
|
||||||
}
|
}
|
||||||
if (gate("channels", false)) {
|
if (gate("channels")) {
|
||||||
actions.add("channel-create");
|
actions.add("channel-create");
|
||||||
actions.add("channel-edit");
|
actions.add("channel-edit");
|
||||||
actions.add("channel-delete");
|
actions.add("channel-delete");
|
||||||
|
|||||||
@@ -28,13 +28,18 @@ describe("config discord", () => {
|
|||||||
dm: {
|
dm: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
allowFrom: ["steipete"],
|
allowFrom: ["steipete"],
|
||||||
groupEnabled: true,
|
groupEnabled: true,
|
||||||
groupChannels: ["clawd-dm"],
|
groupChannels: ["clawd-dm"],
|
||||||
},
|
},
|
||||||
guilds: {
|
actions: {
|
||||||
"123": {
|
emojiUploads: true,
|
||||||
slug: "friends-of-clawd",
|
stickerUploads: false,
|
||||||
requireMention: false,
|
channels: true,
|
||||||
|
},
|
||||||
|
guilds: {
|
||||||
|
"123": {
|
||||||
|
slug: "friends-of-clawd",
|
||||||
|
requireMention: false,
|
||||||
users: ["steipete"],
|
users: ["steipete"],
|
||||||
channels: {
|
channels: {
|
||||||
general: { allow: true },
|
general: { allow: true },
|
||||||
@@ -57,6 +62,9 @@ describe("config discord", () => {
|
|||||||
expect(cfg.channels?.discord?.enabled).toBe(true);
|
expect(cfg.channels?.discord?.enabled).toBe(true);
|
||||||
expect(cfg.channels?.discord?.dm?.groupEnabled).toBe(true);
|
expect(cfg.channels?.discord?.dm?.groupEnabled).toBe(true);
|
||||||
expect(cfg.channels?.discord?.dm?.groupChannels).toEqual(["clawd-dm"]);
|
expect(cfg.channels?.discord?.dm?.groupChannels).toEqual(["clawd-dm"]);
|
||||||
|
expect(cfg.channels?.discord?.actions?.emojiUploads).toBe(true);
|
||||||
|
expect(cfg.channels?.discord?.actions?.stickerUploads).toBe(false);
|
||||||
|
expect(cfg.channels?.discord?.actions?.channels).toBe(true);
|
||||||
expect(cfg.channels?.discord?.guilds?.["123"]?.slug).toBe("friends-of-clawd");
|
expect(cfg.channels?.discord?.guilds?.["123"]?.slug).toBe("friends-of-clawd");
|
||||||
expect(cfg.channels?.discord?.guilds?.["123"]?.channels?.general?.allow).toBe(true);
|
expect(cfg.channels?.discord?.guilds?.["123"]?.channels?.general?.allow).toBe(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ export const DiscordAccountSchema = z.object({
|
|||||||
.object({
|
.object({
|
||||||
reactions: z.boolean().optional(),
|
reactions: z.boolean().optional(),
|
||||||
stickers: z.boolean().optional(),
|
stickers: z.boolean().optional(),
|
||||||
|
emojiUploads: z.boolean().optional(),
|
||||||
|
stickerUploads: z.boolean().optional(),
|
||||||
polls: z.boolean().optional(),
|
polls: z.boolean().optional(),
|
||||||
permissions: z.boolean().optional(),
|
permissions: z.boolean().optional(),
|
||||||
messages: z.boolean().optional(),
|
messages: z.boolean().optional(),
|
||||||
@@ -167,6 +169,7 @@ export const DiscordAccountSchema = z.object({
|
|||||||
voiceStatus: z.boolean().optional(),
|
voiceStatus: z.boolean().optional(),
|
||||||
events: z.boolean().optional(),
|
events: z.boolean().optional(),
|
||||||
moderation: z.boolean().optional(),
|
moderation: z.boolean().optional(),
|
||||||
|
channels: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
replyToMode: ReplyToModeSchema.optional(),
|
replyToMode: ReplyToModeSchema.optional(),
|
||||||
|
|||||||
Reference in New Issue
Block a user