Merge pull request #870 from JDIVE/fix/discord-actions-schema
fix(config): allow discord action flags in schema
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
- Telegram: add bidirectional reaction support with configurable notifications and agent guidance. (#964) — thanks @bohdanpodvirnyi.
|
||||
- Telegram: skip `message_thread_id=1` for General topic sends while keeping typing indicators. (#848) — thanks @azade-c.
|
||||
- Discord: allow allowlisted guilds without channel lists to receive messages when `groupPolicy="allowlist"`. — thanks @thewilloftheshadow.
|
||||
- Discord: allow emoji/sticker uploads + channel actions in config defaults. (#870) — thanks @JDIVE.
|
||||
- Fix: sanitize user-facing error text + strip `<final>` tags across reply pipelines. (#975) — thanks @ThomsenDrake.
|
||||
- Fix: normalize pairing CLI aliases, allow extension channels, and harden Zalo webhook payload parsing. (#991) — thanks @longmaba.
|
||||
|
||||
|
||||
@@ -227,6 +227,8 @@ Outbound Discord API calls retry on rate limits (429) using Discord `retry_after
|
||||
actions: {
|
||||
reactions: true,
|
||||
stickers: true,
|
||||
emojiUploads: true,
|
||||
stickerUploads: true,
|
||||
polls: true,
|
||||
permissions: true,
|
||||
messages: true,
|
||||
@@ -237,6 +239,7 @@ Outbound Discord API calls retry on rate limits (429) using Discord `retry_after
|
||||
roleInfo: true,
|
||||
roles: false,
|
||||
channelInfo: true,
|
||||
channels: true,
|
||||
voiceStatus: true,
|
||||
events: true,
|
||||
moderation: false
|
||||
@@ -304,8 +307,9 @@ ack reaction after the bot replies.
|
||||
- `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).
|
||||
- `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`
|
||||
- `channels` (create/edit/delete channels + categories + permissions)
|
||||
- `roles` (role add/remove, default `false`)
|
||||
- `moderation` (timeout/kick/ban, default `false`)
|
||||
|
||||
@@ -321,6 +325,8 @@ Reaction notifications use `guilds.<id>.reactionNotifications`:
|
||||
| --- | --- | --- |
|
||||
| reactions | enabled | React + list reactions + emojiList |
|
||||
| stickers | enabled | Send stickers |
|
||||
| emojiUploads | enabled | Upload emojis |
|
||||
| stickerUploads | enabled | Upload stickers |
|
||||
| polls | enabled | Create polls |
|
||||
| permissions | enabled | Channel permission snapshot |
|
||||
| messages | enabled | Read/send/edit/delete |
|
||||
@@ -330,6 +336,7 @@ Reaction notifications use `guilds.<id>.reactionNotifications`:
|
||||
| memberInfo | enabled | Member info |
|
||||
| roleInfo | enabled | Role list |
|
||||
| channelInfo | enabled | Channel info + list |
|
||||
| channels | enabled | Channel/category management |
|
||||
| voiceStatus | enabled | Voice state lookup |
|
||||
| events | enabled | List/create scheduled events |
|
||||
| roles | disabled | Role add/remove |
|
||||
|
||||
@@ -219,7 +219,7 @@ export async function handleDiscordGuildAction(
|
||||
return jsonResult({ ok: true, event });
|
||||
}
|
||||
case "channelCreate": {
|
||||
if (!isActionEnabled("channels", false)) {
|
||||
if (!isActionEnabled("channels")) {
|
||||
throw new Error("Discord channel management is disabled.");
|
||||
}
|
||||
const guildId = readStringParam(params, "guildId", { required: true });
|
||||
@@ -241,7 +241,7 @@ export async function handleDiscordGuildAction(
|
||||
return jsonResult({ ok: true, channel });
|
||||
}
|
||||
case "channelEdit": {
|
||||
if (!isActionEnabled("channels", false)) {
|
||||
if (!isActionEnabled("channels")) {
|
||||
throw new Error("Discord channel management is disabled.");
|
||||
}
|
||||
const channelId = readStringParam(params, "channelId", {
|
||||
@@ -267,7 +267,7 @@ export async function handleDiscordGuildAction(
|
||||
return jsonResult({ ok: true, channel });
|
||||
}
|
||||
case "channelDelete": {
|
||||
if (!isActionEnabled("channels", false)) {
|
||||
if (!isActionEnabled("channels")) {
|
||||
throw new Error("Discord channel management is disabled.");
|
||||
}
|
||||
const channelId = readStringParam(params, "channelId", {
|
||||
@@ -277,7 +277,7 @@ export async function handleDiscordGuildAction(
|
||||
return jsonResult(result);
|
||||
}
|
||||
case "channelMove": {
|
||||
if (!isActionEnabled("channels", false)) {
|
||||
if (!isActionEnabled("channels")) {
|
||||
throw new Error("Discord channel management is disabled.");
|
||||
}
|
||||
const guildId = readStringParam(params, "guildId", { required: true });
|
||||
@@ -295,7 +295,7 @@ export async function handleDiscordGuildAction(
|
||||
return jsonResult({ ok: true });
|
||||
}
|
||||
case "categoryCreate": {
|
||||
if (!isActionEnabled("channels", false)) {
|
||||
if (!isActionEnabled("channels")) {
|
||||
throw new Error("Discord channel management is disabled.");
|
||||
}
|
||||
const guildId = readStringParam(params, "guildId", { required: true });
|
||||
@@ -310,7 +310,7 @@ export async function handleDiscordGuildAction(
|
||||
return jsonResult({ ok: true, category: channel });
|
||||
}
|
||||
case "categoryEdit": {
|
||||
if (!isActionEnabled("channels", false)) {
|
||||
if (!isActionEnabled("channels")) {
|
||||
throw new Error("Discord channel management is disabled.");
|
||||
}
|
||||
const categoryId = readStringParam(params, "categoryId", {
|
||||
@@ -326,7 +326,7 @@ export async function handleDiscordGuildAction(
|
||||
return jsonResult({ ok: true, category: channel });
|
||||
}
|
||||
case "categoryDelete": {
|
||||
if (!isActionEnabled("channels", false)) {
|
||||
if (!isActionEnabled("channels")) {
|
||||
throw new Error("Discord channel management is disabled.");
|
||||
}
|
||||
const categoryId = readStringParam(params, "categoryId", {
|
||||
@@ -336,7 +336,7 @@ export async function handleDiscordGuildAction(
|
||||
return jsonResult(result);
|
||||
}
|
||||
case "channelPermissionSet": {
|
||||
if (!isActionEnabled("channels", false)) {
|
||||
if (!isActionEnabled("channels")) {
|
||||
throw new Error("Discord channel management is disabled.");
|
||||
}
|
||||
const channelId = readStringParam(params, "channelId", {
|
||||
@@ -359,7 +359,7 @@ export async function handleDiscordGuildAction(
|
||||
return jsonResult({ ok: true });
|
||||
}
|
||||
case "channelPermissionRemove": {
|
||||
if (!isActionEnabled("channels", false)) {
|
||||
if (!isActionEnabled("channels")) {
|
||||
throw new Error("Discord channel management is disabled.");
|
||||
}
|
||||
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-list");
|
||||
}
|
||||
if (gate("channels", false)) {
|
||||
if (gate("channels")) {
|
||||
actions.add("channel-create");
|
||||
actions.add("channel-edit");
|
||||
actions.add("channel-delete");
|
||||
|
||||
@@ -28,13 +28,18 @@ describe("config discord", () => {
|
||||
dm: {
|
||||
enabled: true,
|
||||
allowFrom: ["steipete"],
|
||||
groupEnabled: true,
|
||||
groupChannels: ["clawd-dm"],
|
||||
},
|
||||
guilds: {
|
||||
"123": {
|
||||
slug: "friends-of-clawd",
|
||||
requireMention: false,
|
||||
groupEnabled: true,
|
||||
groupChannels: ["clawd-dm"],
|
||||
},
|
||||
actions: {
|
||||
emojiUploads: true,
|
||||
stickerUploads: false,
|
||||
channels: true,
|
||||
},
|
||||
guilds: {
|
||||
"123": {
|
||||
slug: "friends-of-clawd",
|
||||
requireMention: false,
|
||||
users: ["steipete"],
|
||||
channels: {
|
||||
general: { allow: true },
|
||||
@@ -57,6 +62,9 @@ describe("config discord", () => {
|
||||
expect(cfg.channels?.discord?.enabled).toBe(true);
|
||||
expect(cfg.channels?.discord?.dm?.groupEnabled).toBe(true);
|
||||
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"]?.channels?.general?.allow).toBe(true);
|
||||
});
|
||||
|
||||
@@ -154,6 +154,8 @@ export const DiscordAccountSchema = z.object({
|
||||
.object({
|
||||
reactions: z.boolean().optional(),
|
||||
stickers: z.boolean().optional(),
|
||||
emojiUploads: z.boolean().optional(),
|
||||
stickerUploads: z.boolean().optional(),
|
||||
polls: z.boolean().optional(),
|
||||
permissions: z.boolean().optional(),
|
||||
messages: z.boolean().optional(),
|
||||
@@ -167,6 +169,7 @@ export const DiscordAccountSchema = z.object({
|
||||
voiceStatus: z.boolean().optional(),
|
||||
events: z.boolean().optional(),
|
||||
moderation: z.boolean().optional(),
|
||||
channels: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
replyToMode: ReplyToModeSchema.optional(),
|
||||
|
||||
Reference in New Issue
Block a user