Skip to main content
Glama
channel.ts12.3 kB
import { ChannelType } from 'discord.js'; import { handleDiscordError } from '../error-handler.js'; import { CreateCategorySchema, CreateTextChannelSchema, DeleteCategorySchema, DeleteChannelSchema, EditCategorySchema, GetServerInfoSchema, ReadMessagesSchema, } from '../schemas.js'; import type { ToolContext, ToolResponse } from './types.js'; // Category creation handler export async function createCategoryHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { guildId, name, position, reason } = CreateCategorySchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: 'text', text: 'Discord client not logged in.' }], 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, }; } const options = { name, type: ChannelType.GuildCategory } as const; if (typeof position === 'number') { (options as { position?: number }).position = position; } if (reason) { (options as { reason?: string }).reason = reason; } const category = await guild.channels.create(options); return { content: [ { type: 'text', text: `Successfully created category "${name}" with ID: ${category.id}`, }, ], }; } catch (error) { return handleDiscordError(error); } } // Category edit handler export async function editCategoryHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { categoryId, name, position, reason } = EditCategorySchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: 'text', text: 'Discord client not logged in.' }], isError: true, }; } const category = await context.client.channels.fetch(categoryId); if (!category || category.type !== ChannelType.GuildCategory) { return { content: [ { type: 'text', text: `Cannot find category with ID: ${categoryId}` }, ], isError: true, }; } const update: { name?: string; position?: number; reason?: string; } = {}; if (name !== undefined) { update.name = name; } if (typeof position === 'number') { update.position = position; } if (reason !== undefined) { update.reason = reason; } await category.edit(update); return { content: [ { type: 'text', text: `Successfully edited category with ID: ${categoryId}`, }, ], }; } catch (error) { return handleDiscordError(error); } } // Category deletion handler export async function deleteCategoryHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { categoryId, reason } = DeleteCategorySchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: 'text', text: 'Discord client not logged in.' }], isError: true, }; } const category = await context.client.channels.fetch(categoryId); if (!category || category.type !== ChannelType.GuildCategory) { return { content: [ { type: 'text', text: `Cannot find category with ID: ${categoryId}` }, ], isError: true, }; } await category.delete(reason || 'Category deleted via API'); return { content: [ { type: 'text', text: `Successfully deleted category with ID: ${categoryId}`, }, ], }; } catch (error) { return handleDiscordError(error); } } // Text channel creation handler export async function createTextChannelHandler( args: unknown, context: ToolContext ): Promise<ToolResponse> { const { guildId, channelName, topic, reason } = CreateTextChannelSchema.parse(args); try { if (!context.client.isReady()) { return { content: [{ type: 'text', text: 'Discord client not logged in.' }], 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 channelOptions: { name: string; type: ChannelType.GuildText; topic?: string; reason?: string; } = { name: channelName, type: ChannelType.GuildText, }; if (topic !== undefined) { channelOptions.topic = topic; } if (reason !== undefined) { channelOptions.reason = reason; } const channel = await guild.channels.create(channelOptions); return { content: [ { type: 'text', text: `Successfully created text channel "${channelName}" with ID: ${channel.id}`, }, ], }; } catch (error) { return handleDiscordError(error); } } // 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.' }], 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 handleDiscordError(error); } } // 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.' }], 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 handleDiscordError(error); } } // 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.' }], 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 server 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, }; // Get detailed information for all channels const channelDetails = channels .map((channel) => { if (!channel) { return null; } return { id: channel.id, name: channel.name, type: ChannelType[channel.type] || channel.type, categoryId: channel.parentId, position: channel.position, // Only add topic for text channels topic: 'topic' in channel ? channel.topic : null, }; }) .filter((c) => c !== null); // Filter out null values // Group channels by type const groupedChannels = { text: channelDetails.filter( (c) => c.type === ChannelType[ChannelType.GuildText] || c.type === ChannelType.GuildText ), voice: channelDetails.filter( (c) => c.type === ChannelType[ChannelType.GuildVoice] || c.type === ChannelType.GuildVoice ), category: channelDetails.filter( (c) => c.type === ChannelType[ChannelType.GuildCategory] || c.type === ChannelType.GuildCategory ), forum: channelDetails.filter( (c) => c.type === ChannelType[ChannelType.GuildForum] || c.type === ChannelType.GuildForum ), announcement: channelDetails.filter( (c) => c.type === ChannelType[ChannelType.GuildAnnouncement] || c.type === ChannelType.GuildAnnouncement ), stage: channelDetails.filter( (c) => c.type === ChannelType[ChannelType.GuildStageVoice] || c.type === ChannelType.GuildStageVoice ), all: channelDetails, }; // Get 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: { count: channelsByType, details: groupedChannels, }, features: guild.features, premium: { tier: guild.premiumTier, subscriptions: guild.premiumSubscriptionCount, }, }; return { content: [{ type: 'text', text: JSON.stringify(guildInfo, null, 2) }], }; } catch (error) { return handleDiscordError(error); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/IQAIcom/mcp-discord'

If you have feedback or need assistance with the MCP directory API, please join our Discord server