Modularize tool list and handlers
Modularize tool list and handlers for better maintainability - Move tool list to separate toolList.ts file - Split handlers into functional modules (channel, reactions, webhooks) - Optimize import structure and switch statement in index.ts - Maintain same functionality with improved code organization
This commit is contained in:
parent
646b55b234
commit
56faf1ad85
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "mcp-discord",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mcp-discord",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"discord.js": "^14.18.0",
|
||||
|
|
1316
src/index.ts
1316
src/index.ts
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,111 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const DiscordLoginSchema = z.object({
|
||||
random_string: z.string().optional()
|
||||
});
|
||||
|
||||
export const SendMessageSchema = z.object({
|
||||
channelId: z.string(),
|
||||
message: z.string()
|
||||
});
|
||||
|
||||
export const GetForumChannelsSchema = z.object({
|
||||
guildId: z.string()
|
||||
});
|
||||
|
||||
export const CreateForumPostSchema = z.object({
|
||||
forumChannelId: z.string(),
|
||||
title: z.string(),
|
||||
content: z.string(),
|
||||
tags: z.array(z.string()).optional()
|
||||
});
|
||||
|
||||
export const GetForumPostSchema = z.object({
|
||||
threadId: z.string()
|
||||
});
|
||||
|
||||
export const ReplyToForumSchema = z.object({
|
||||
threadId: z.string(),
|
||||
message: z.string()
|
||||
});
|
||||
|
||||
export const CreateTextChannelSchema = z.object({
|
||||
guildId: z.string(),
|
||||
channelName: z.string(),
|
||||
topic: z.string().optional()
|
||||
});
|
||||
|
||||
export const DeleteChannelSchema = z.object({
|
||||
channelId: z.string(),
|
||||
reason: z.string().optional()
|
||||
});
|
||||
|
||||
export const ReadMessagesSchema = z.object({
|
||||
channelId: z.string(),
|
||||
limit: z.number().min(1).max(100).optional().default(50)
|
||||
});
|
||||
|
||||
export const GetServerInfoSchema = z.object({
|
||||
guildId: z.string()
|
||||
});
|
||||
|
||||
export const AddReactionSchema = z.object({
|
||||
channelId: z.string(),
|
||||
messageId: z.string(),
|
||||
emoji: z.string()
|
||||
});
|
||||
|
||||
export const AddMultipleReactionsSchema = z.object({
|
||||
channelId: z.string(),
|
||||
messageId: z.string(),
|
||||
emojis: z.array(z.string())
|
||||
});
|
||||
|
||||
export const RemoveReactionSchema = z.object({
|
||||
channelId: z.string(),
|
||||
messageId: z.string(),
|
||||
emoji: z.string(),
|
||||
userId: z.string().optional()
|
||||
});
|
||||
|
||||
export const DeleteForumPostSchema = z.object({
|
||||
threadId: z.string(),
|
||||
reason: z.string().optional()
|
||||
});
|
||||
|
||||
export const DeleteMessageSchema = z.object({
|
||||
channelId: z.string(),
|
||||
messageId: z.string(),
|
||||
reason: z.string().optional()
|
||||
});
|
||||
|
||||
export const CreateWebhookSchema = z.object({
|
||||
channelId: z.string(),
|
||||
name: z.string(),
|
||||
avatar: z.string().optional(),
|
||||
reason: z.string().optional()
|
||||
});
|
||||
|
||||
export const SendWebhookMessageSchema = z.object({
|
||||
webhookId: z.string(),
|
||||
webhookToken: z.string(),
|
||||
content: z.string(),
|
||||
username: z.string().optional(),
|
||||
avatarURL: z.string().optional(),
|
||||
threadId: z.string().optional()
|
||||
});
|
||||
|
||||
export const EditWebhookSchema = z.object({
|
||||
webhookId: z.string(),
|
||||
webhookToken: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
avatar: z.string().optional(),
|
||||
channelId: z.string().optional(),
|
||||
reason: z.string().optional()
|
||||
});
|
||||
|
||||
export const DeleteWebhookSchema = z.object({
|
||||
webhookId: z.string(),
|
||||
webhookToken: z.string().optional(),
|
||||
reason: z.string().optional()
|
||||
});
|
|
@ -0,0 +1,256 @@
|
|||
export const toolList = [
|
||||
{
|
||||
name: "discord_login",
|
||||
description: "Logs in to Discord using the configured token",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
random_string: { type: "string" }
|
||||
},
|
||||
required: []
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_send",
|
||||
description: "Sends a message to a specified Discord text channel",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
channelId: { type: "string" },
|
||||
message: { type: "string" }
|
||||
},
|
||||
required: ["channelId", "message"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_get_forum_channels",
|
||||
description: "Lists all forum channels in a specified Discord server (guild)",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
guildId: { type: "string" }
|
||||
},
|
||||
required: ["guildId"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_create_forum_post",
|
||||
description: "Creates a new post in a Discord forum channel with optional tags",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
forumChannelId: { type: "string" },
|
||||
title: { type: "string" },
|
||||
content: { type: "string" },
|
||||
tags: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
}
|
||||
},
|
||||
required: ["forumChannelId", "title", "content"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_get_forum_post",
|
||||
description: "Retrieves details about a forum post including its messages",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
threadId: { type: "string" }
|
||||
},
|
||||
required: ["threadId"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_reply_to_forum",
|
||||
description: "Adds a reply to an existing forum post or thread",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
threadId: { type: "string" },
|
||||
message: { type: "string" }
|
||||
},
|
||||
required: ["threadId", "message"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_create_text_channel",
|
||||
description: "Creates a new text channel in a Discord server with an optional topic",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
guildId: { type: "string" },
|
||||
channelName: { type: "string" },
|
||||
topic: { type: "string" }
|
||||
},
|
||||
required: ["guildId", "channelName"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_delete_channel",
|
||||
description: "Deletes a Discord channel with an optional reason",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
channelId: { type: "string" },
|
||||
reason: { type: "string" }
|
||||
},
|
||||
required: ["channelId"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_read_messages",
|
||||
description: "Retrieves messages from a Discord text channel with a configurable limit",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
channelId: { type: "string" },
|
||||
limit: {
|
||||
type: "number",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 50
|
||||
}
|
||||
},
|
||||
required: ["channelId"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_get_server_info",
|
||||
description: "Retrieves detailed information about a Discord server including channels and member count",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
guildId: { type: "string" }
|
||||
},
|
||||
required: ["guildId"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_add_reaction",
|
||||
description: "Adds an emoji reaction to a specific Discord message",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
channelId: { type: "string" },
|
||||
messageId: { type: "string" },
|
||||
emoji: { type: "string" }
|
||||
},
|
||||
required: ["channelId", "messageId", "emoji"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_add_multiple_reactions",
|
||||
description: "Adds multiple emoji reactions to a Discord message at once",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
channelId: { type: "string" },
|
||||
messageId: { type: "string" },
|
||||
emojis: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
}
|
||||
},
|
||||
required: ["channelId", "messageId", "emojis"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_remove_reaction",
|
||||
description: "Removes a specific emoji reaction from a Discord message",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
channelId: { type: "string" },
|
||||
messageId: { type: "string" },
|
||||
emoji: { type: "string" },
|
||||
userId: { type: "string" }
|
||||
},
|
||||
required: ["channelId", "messageId", "emoji"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_delete_forum_post",
|
||||
description: "Deletes a forum post or thread with an optional reason",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
threadId: { type: "string" },
|
||||
reason: { type: "string" }
|
||||
},
|
||||
required: ["threadId"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_delete_message",
|
||||
description: "Deletes a specific message from a Discord text channel",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
channelId: { type: "string" },
|
||||
messageId: { type: "string" },
|
||||
reason: { type: "string" }
|
||||
},
|
||||
required: ["channelId", "messageId"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_create_webhook",
|
||||
description: "Creates a new webhook for a Discord channel",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
channelId: { type: "string" },
|
||||
name: { type: "string" },
|
||||
avatar: { type: "string" },
|
||||
reason: { type: "string" }
|
||||
},
|
||||
required: ["channelId", "name"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_send_webhook_message",
|
||||
description: "Sends a message to a Discord channel using a webhook",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
webhookId: { type: "string" },
|
||||
webhookToken: { type: "string" },
|
||||
content: { type: "string" },
|
||||
username: { type: "string" },
|
||||
avatarURL: { type: "string" },
|
||||
threadId: { type: "string" }
|
||||
},
|
||||
required: ["webhookId", "webhookToken", "content"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_edit_webhook",
|
||||
description: "Edits an existing webhook for a Discord channel",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
webhookId: { type: "string" },
|
||||
webhookToken: { type: "string" },
|
||||
name: { type: "string" },
|
||||
avatar: { type: "string" },
|
||||
channelId: { type: "string" },
|
||||
reason: { type: "string" }
|
||||
},
|
||||
required: ["webhookId"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_delete_webhook",
|
||||
description: "Deletes an existing webhook for a Discord channel",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
webhookId: { type: "string" },
|
||||
webhookToken: { type: "string" },
|
||||
reason: { type: "string" }
|
||||
},
|
||||
required: ["webhookId"]
|
||||
}
|
||||
}
|
||||
];
|
|
@ -0,0 +1,241 @@
|
|||
import { z } from "zod";
|
||||
import { ChannelType } from "discord.js";
|
||||
import { ToolContext, ToolResponse } from "./types.js";
|
||||
import {
|
||||
CreateTextChannelSchema,
|
||||
DeleteChannelSchema,
|
||||
ReadMessagesSchema,
|
||||
GetServerInfoSchema
|
||||
} from "../schemas.js";
|
||||
|
||||
// Text channel creation handler
|
||||
export async function createTextChannelHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { guildId, channelName, topic } = CreateTextChannelSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const guild = await context.client.guilds.fetch(guildId);
|
||||
if (!guild) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find guild with ID: ${guildId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Create the text channel
|
||||
const channel = await guild.channels.create({
|
||||
name: channelName,
|
||||
type: ChannelType.GuildText,
|
||||
topic: topic
|
||||
});
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully created text channel "${channelName}" with ID: ${channel.id}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to create text channel: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Channel deletion handler
|
||||
export async function deleteChannelHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { channelId, reason } = DeleteChannelSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const channel = await context.client.channels.fetch(channelId);
|
||||
if (!channel) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find channel with ID: ${channelId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Check if channel can be deleted (has delete method)
|
||||
if (!('delete' in channel)) {
|
||||
return {
|
||||
content: [{ type: "text", text: `This channel type does not support deletion or the bot lacks permissions` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Delete the channel
|
||||
await channel.delete(reason || "Channel deleted via API");
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully deleted channel with ID: ${channelId}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to delete channel: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Message reading handler
|
||||
export async function readMessagesHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { channelId, limit } = ReadMessagesSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const channel = await context.client.channels.fetch(channelId);
|
||||
if (!channel) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find channel with ID: ${channelId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Check if channel has messages (text channel, thread, etc.)
|
||||
if (!channel.isTextBased() || !('messages' in channel)) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Channel type does not support reading messages` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Fetch messages
|
||||
const messages = await channel.messages.fetch({ limit });
|
||||
|
||||
if (messages.size === 0) {
|
||||
return {
|
||||
content: [{ type: "text", text: `No messages found in channel` }]
|
||||
};
|
||||
}
|
||||
|
||||
// Format messages
|
||||
const formattedMessages = messages.map(msg => ({
|
||||
id: msg.id,
|
||||
content: msg.content,
|
||||
author: {
|
||||
id: msg.author.id,
|
||||
username: msg.author.username,
|
||||
bot: msg.author.bot
|
||||
},
|
||||
timestamp: msg.createdAt,
|
||||
attachments: msg.attachments.size,
|
||||
embeds: msg.embeds.length,
|
||||
replyTo: msg.reference ? msg.reference.messageId : null
|
||||
})).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
channelId,
|
||||
messageCount: formattedMessages.length,
|
||||
messages: formattedMessages
|
||||
}, null, 2)
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to read messages: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Server information handler
|
||||
export async function getServerInfoHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { guildId } = GetServerInfoSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const guild = await context.client.guilds.fetch(guildId);
|
||||
if (!guild) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find guild with ID: ${guildId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Fetch additional guild data
|
||||
await guild.fetch();
|
||||
|
||||
// Fetch channel information
|
||||
const channels = await guild.channels.fetch();
|
||||
|
||||
// Categorize channels by type
|
||||
const channelsByType = {
|
||||
text: channels.filter(c => c?.type === ChannelType.GuildText).size,
|
||||
voice: channels.filter(c => c?.type === ChannelType.GuildVoice).size,
|
||||
category: channels.filter(c => c?.type === ChannelType.GuildCategory).size,
|
||||
forum: channels.filter(c => c?.type === ChannelType.GuildForum).size,
|
||||
announcement: channels.filter(c => c?.type === ChannelType.GuildAnnouncement).size,
|
||||
stage: channels.filter(c => c?.type === ChannelType.GuildStageVoice).size,
|
||||
total: channels.size
|
||||
};
|
||||
|
||||
// Fetch member count
|
||||
const approximateMemberCount = guild.approximateMemberCount || "unknown";
|
||||
|
||||
// Format guild information
|
||||
const guildInfo = {
|
||||
id: guild.id,
|
||||
name: guild.name,
|
||||
description: guild.description,
|
||||
icon: guild.iconURL(),
|
||||
owner: guild.ownerId,
|
||||
createdAt: guild.createdAt,
|
||||
memberCount: approximateMemberCount,
|
||||
channels: channelsByType,
|
||||
features: guild.features,
|
||||
premium: {
|
||||
tier: guild.premiumTier,
|
||||
subscriptions: guild.premiumSubscriptionCount
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(guildInfo, null, 2) }]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to fetch server info: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
import { ChannelType, ForumChannel } from 'discord.js';
|
||||
import { GetForumChannelsSchema, CreateForumPostSchema, GetForumPostSchema, ReplyToForumSchema, DeleteForumPostSchema } from '../schemas.js';
|
||||
import { ToolHandler } from './types.js';
|
||||
|
||||
export const getForumChannelsHandler: ToolHandler = async (args, { client }) => {
|
||||
const { guildId } = GetForumChannelsSchema.parse(args);
|
||||
|
||||
try {
|
||||
if (!client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const guild = await client.guilds.fetch(guildId);
|
||||
if (!guild) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find guild with ID: ${guildId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Fetch all channels from the guild
|
||||
const channels = await guild.channels.fetch();
|
||||
|
||||
// Filter to get only forum channels
|
||||
const forumChannels = channels.filter(channel => channel?.type === ChannelType.GuildForum);
|
||||
|
||||
if (forumChannels.size === 0) {
|
||||
return {
|
||||
content: [{ type: "text", text: `No forum channels found in guild: ${guild.name}` }]
|
||||
};
|
||||
}
|
||||
|
||||
// Format forum channels information
|
||||
const forumInfo = forumChannels.map(channel => ({
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
topic: channel.topic || "No topic set"
|
||||
}));
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(forumInfo, null, 2) }]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to fetch forum channels: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const createForumPostHandler: ToolHandler = async (args, { client }) => {
|
||||
const { forumChannelId, title, content, tags } = CreateForumPostSchema.parse(args);
|
||||
|
||||
try {
|
||||
if (!client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const channel = await client.channels.fetch(forumChannelId);
|
||||
if (!channel || channel.type !== ChannelType.GuildForum) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Channel ID ${forumChannelId} is not a forum channel.` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const forumChannel = channel as ForumChannel;
|
||||
|
||||
// Get available tags in the forum
|
||||
const availableTags = forumChannel.availableTags;
|
||||
let selectedTagIds: string[] = [];
|
||||
|
||||
// If tags are provided, find their IDs
|
||||
if (tags && tags.length > 0) {
|
||||
selectedTagIds = availableTags
|
||||
.filter(tag => tags.includes(tag.name))
|
||||
.map(tag => tag.id);
|
||||
}
|
||||
|
||||
// Create the forum post
|
||||
const thread = await forumChannel.threads.create({
|
||||
name: title,
|
||||
message: {
|
||||
content: content
|
||||
},
|
||||
appliedTags: selectedTagIds.length > 0 ? selectedTagIds : undefined
|
||||
});
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully created forum post "${title}" with ID: ${thread.id}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to create forum post: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const getForumPostHandler: ToolHandler = async (args, { client }) => {
|
||||
const { threadId } = GetForumPostSchema.parse(args);
|
||||
|
||||
try {
|
||||
if (!client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const thread = await client.channels.fetch(threadId);
|
||||
if (!thread || !(thread.isThread())) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find thread with ID: ${threadId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Get messages from the thread
|
||||
const messages = await thread.messages.fetch({ limit: 10 });
|
||||
|
||||
const threadDetails = {
|
||||
id: thread.id,
|
||||
name: thread.name,
|
||||
parentId: thread.parentId,
|
||||
messageCount: messages.size,
|
||||
createdAt: thread.createdAt,
|
||||
messages: messages.map(msg => ({
|
||||
id: msg.id,
|
||||
content: msg.content,
|
||||
author: msg.author.tag,
|
||||
createdAt: msg.createdAt
|
||||
}))
|
||||
};
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(threadDetails, null, 2) }]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to fetch forum post: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const replyToForumHandler: ToolHandler = async (args, { client }) => {
|
||||
const { threadId, message } = ReplyToForumSchema.parse(args);
|
||||
|
||||
try {
|
||||
if (!client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const thread = await client.channels.fetch(threadId);
|
||||
if (!thread || !(thread.isThread())) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find thread with ID: ${threadId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
if (!('send' in thread)) {
|
||||
return {
|
||||
content: [{ type: "text", text: `This thread does not support sending messages` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Send the reply
|
||||
const sentMessage = await thread.send(message);
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully replied to forum post. Message ID: ${sentMessage.id}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to reply to forum post: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteForumPostHandler: ToolHandler = async (args, { client }) => {
|
||||
const { threadId, reason } = DeleteForumPostSchema.parse(args);
|
||||
|
||||
try {
|
||||
if (!client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const thread = await client.channels.fetch(threadId);
|
||||
if (!thread || !thread.isThread()) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find forum post/thread with ID: ${threadId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Delete the forum post/thread
|
||||
await thread.delete(reason || "Forum post deleted via API");
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully deleted forum post/thread with ID: ${threadId}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to delete forum post: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
import { DiscordLoginSchema } from '../schemas.js';
|
||||
import { ToolHandler } from './types.js';
|
||||
|
||||
export const loginHandler: ToolHandler = async (args, { client }) => {
|
||||
DiscordLoginSchema.parse(args);
|
||||
|
||||
try {
|
||||
// Check if client is already logged in
|
||||
if (client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Already logged in as: ${client.user?.tag}` }]
|
||||
};
|
||||
}
|
||||
|
||||
// loginHandler doesn't directly handle token, it needs to be set before invocation
|
||||
if (!client.token) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord token not configured. Cannot log in." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
await client.login(client.token);
|
||||
return {
|
||||
content: [{ type: "text", text: `Successfully logged in to Discord: ${client.user?.tag}` }]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Login failed: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
};
|
|
@ -0,0 +1,224 @@
|
|||
import { z } from "zod";
|
||||
import { ToolContext, ToolResponse } from "./types.js";
|
||||
import {
|
||||
AddReactionSchema,
|
||||
AddMultipleReactionsSchema,
|
||||
RemoveReactionSchema,
|
||||
DeleteMessageSchema
|
||||
} from "../schemas.js";
|
||||
|
||||
// Add reaction handler
|
||||
export async function addReactionHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { channelId, messageId, emoji } = AddReactionSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const channel = await context.client.channels.fetch(channelId);
|
||||
if (!channel || !channel.isTextBased() || !('messages' in channel)) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find text channel with ID: ${channelId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const message = await channel.messages.fetch(messageId);
|
||||
if (!message) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find message with ID: ${messageId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Add the reaction
|
||||
await message.react(emoji);
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully added reaction ${emoji} to message ID: ${messageId}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to add reaction: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Add multiple reactions handler
|
||||
export async function addMultipleReactionsHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { channelId, messageId, emojis } = AddMultipleReactionsSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const channel = await context.client.channels.fetch(channelId);
|
||||
if (!channel || !channel.isTextBased() || !('messages' in channel)) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find text channel with ID: ${channelId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const message = await channel.messages.fetch(messageId);
|
||||
if (!message) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find message with ID: ${messageId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Add each reaction sequentially
|
||||
for (const emoji of emojis) {
|
||||
await message.react(emoji);
|
||||
// Small delay to prevent rate limiting
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully added ${emojis.length} reactions to message ID: ${messageId}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to add reactions: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Remove reaction handler
|
||||
export async function removeReactionHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { channelId, messageId, emoji, userId } = RemoveReactionSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const channel = await context.client.channels.fetch(channelId);
|
||||
if (!channel || !channel.isTextBased() || !('messages' in channel)) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find text channel with ID: ${channelId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const message = await channel.messages.fetch(messageId);
|
||||
if (!message) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find message with ID: ${messageId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Get the reactions
|
||||
const reactions = message.reactions.cache;
|
||||
|
||||
// Find the specific reaction
|
||||
const reaction = reactions.find(r => r.emoji.toString() === emoji || r.emoji.name === emoji);
|
||||
|
||||
if (!reaction) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Reaction ${emoji} not found on message ID: ${messageId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
// Remove a specific user's reaction
|
||||
await reaction.users.remove(userId);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully removed reaction ${emoji} from user ID: ${userId} on message ID: ${messageId}`
|
||||
}]
|
||||
};
|
||||
} else {
|
||||
// Remove bot's reaction
|
||||
await reaction.users.remove(context.client.user.id);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully removed bot's reaction ${emoji} from message ID: ${messageId}`
|
||||
}]
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to remove reaction: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Delete message handler
|
||||
export async function deleteMessageHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { channelId, messageId, reason } = DeleteMessageSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const channel = await context.client.channels.fetch(channelId);
|
||||
if (!channel || !channel.isTextBased() || !('messages' in channel)) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find text channel with ID: ${channelId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Fetch the message
|
||||
const message = await channel.messages.fetch(messageId);
|
||||
if (!message) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find message with ID: ${messageId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Delete the message
|
||||
await message.delete();
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully deleted message with ID: ${messageId} from channel: ${channelId}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to delete message: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import { SendMessageSchema } from '../schemas.js';
|
||||
import { ToolHandler } from './types.js';
|
||||
|
||||
export const sendMessageHandler: ToolHandler = async (args, { client }) => {
|
||||
const { channelId, message } = SendMessageSchema.parse(args);
|
||||
|
||||
try {
|
||||
if (!client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const channel = await client.channels.fetch(channelId);
|
||||
if (!channel || !channel.isTextBased()) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find text channel ID: ${channelId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Ensure channel is text-based and can send messages
|
||||
if ('send' in channel) {
|
||||
await channel.send(message);
|
||||
return {
|
||||
content: [{ type: "text", text: `Message successfully sent to channel ID: ${channelId}` }]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
content: [{ type: "text", text: `This channel type does not support sending messages` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Send message failed: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
import { Client } from "discord.js";
|
||||
import { z } from "zod";
|
||||
import { ToolResponse, ToolContext, ToolHandler } from "./types.js";
|
||||
import { loginHandler } from './login.js';
|
||||
import { sendMessageHandler } from './send-message.js';
|
||||
import {
|
||||
getForumChannelsHandler,
|
||||
createForumPostHandler,
|
||||
getForumPostHandler,
|
||||
replyToForumHandler,
|
||||
deleteForumPostHandler
|
||||
} from './forum.js';
|
||||
import {
|
||||
createTextChannelHandler,
|
||||
deleteChannelHandler,
|
||||
readMessagesHandler,
|
||||
getServerInfoHandler
|
||||
} from './channel.js';
|
||||
import {
|
||||
addReactionHandler,
|
||||
addMultipleReactionsHandler,
|
||||
removeReactionHandler,
|
||||
deleteMessageHandler
|
||||
} from './reactions.js';
|
||||
import {
|
||||
createWebhookHandler,
|
||||
sendWebhookMessageHandler,
|
||||
editWebhookHandler,
|
||||
deleteWebhookHandler
|
||||
} from './webhooks.js';
|
||||
|
||||
// Export tool handlers
|
||||
export {
|
||||
loginHandler,
|
||||
sendMessageHandler,
|
||||
getForumChannelsHandler,
|
||||
createForumPostHandler,
|
||||
getForumPostHandler,
|
||||
replyToForumHandler,
|
||||
deleteForumPostHandler,
|
||||
createTextChannelHandler,
|
||||
deleteChannelHandler,
|
||||
readMessagesHandler,
|
||||
getServerInfoHandler,
|
||||
addReactionHandler,
|
||||
addMultipleReactionsHandler,
|
||||
removeReactionHandler,
|
||||
deleteMessageHandler,
|
||||
createWebhookHandler,
|
||||
sendWebhookMessageHandler,
|
||||
editWebhookHandler,
|
||||
deleteWebhookHandler
|
||||
};
|
||||
|
||||
// Export common types
|
||||
export { ToolResponse, ToolContext, ToolHandler };
|
||||
|
||||
// Create tool context
|
||||
export function createToolContext(client: Client): ToolContext {
|
||||
return { client };
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { Client } from "discord.js";
|
||||
|
||||
export interface ToolResponse {
|
||||
content: { type: string; text: string }[];
|
||||
isError?: boolean;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ToolContext {
|
||||
client: Client;
|
||||
}
|
||||
|
||||
export type ToolHandler<T = any> = (args: T, context: ToolContext) => Promise<ToolResponse>;
|
|
@ -0,0 +1,186 @@
|
|||
import { z } from "zod";
|
||||
import { ToolContext, ToolResponse } from "./types.js";
|
||||
import {
|
||||
CreateWebhookSchema,
|
||||
SendWebhookMessageSchema,
|
||||
EditWebhookSchema,
|
||||
DeleteWebhookSchema
|
||||
} from "../schemas.js";
|
||||
|
||||
// Create webhook handler
|
||||
export async function createWebhookHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { channelId, name, avatar, reason } = CreateWebhookSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const channel = await context.client.channels.fetch(channelId);
|
||||
if (!channel || !channel.isTextBased()) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find text channel with ID: ${channelId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Check if the channel supports webhooks
|
||||
if (!('createWebhook' in channel)) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Channel type does not support webhooks: ${channelId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Create the webhook
|
||||
const webhook = await channel.createWebhook({
|
||||
name: name,
|
||||
avatar: avatar,
|
||||
reason: reason
|
||||
});
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully created webhook with ID: ${webhook.id} and token: ${webhook.token}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to create webhook: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Send webhook message handler
|
||||
export async function sendWebhookMessageHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { webhookId, webhookToken, content, username, avatarURL, threadId } = SendWebhookMessageSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const webhook = await context.client.fetchWebhook(webhookId, webhookToken);
|
||||
if (!webhook) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find webhook with ID: ${webhookId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Send the message
|
||||
await webhook.send({
|
||||
content: content,
|
||||
username: username,
|
||||
avatarURL: avatarURL,
|
||||
threadId: threadId
|
||||
});
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully sent webhook message to webhook ID: ${webhookId}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to send webhook message: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Edit webhook handler
|
||||
export async function editWebhookHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { webhookId, webhookToken, name, avatar, channelId, reason } = EditWebhookSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const webhook = await context.client.fetchWebhook(webhookId, webhookToken);
|
||||
if (!webhook) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find webhook with ID: ${webhookId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Edit the webhook
|
||||
await webhook.edit({
|
||||
name: name,
|
||||
avatar: avatar,
|
||||
channel: channelId,
|
||||
reason: reason
|
||||
});
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully edited webhook with ID: ${webhook.id}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to edit webhook: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Delete webhook handler
|
||||
export async function deleteWebhookHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { webhookId, webhookToken, reason } = DeleteWebhookSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const webhook = await context.client.fetchWebhook(webhookId, webhookToken);
|
||||
if (!webhook) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Cannot find webhook with ID: ${webhookId}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
// Delete the webhook
|
||||
await webhook.delete(reason || "Webhook deleted via API");
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Successfully deleted webhook with ID: ${webhook.id}`
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Failed to delete webhook: ${error}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue