Skip to main content
Glama
slackClient.ts4.55 kB
import { WebClient } from "@slack/web-api"; export interface SlackMessageOptions { token: string; channel: string; text: string; } /** * Post message to Slack using Web API */ export async function postToSlack(options: SlackMessageOptions): Promise<void> { const { token, channel, text } = options; const client = new WebClient(token); try { // Resolve channel name to ID if needed let channelId = channel; // If channel doesn't start with 'C' (Slack channel ID format), try to resolve it if (!channel.startsWith("C") && !channel.startsWith("G")) { try { const result = await client.conversations.list({ types: "public_channel,private_channel", }); const foundChannel = result.channels?.find( (c: { name?: string; id?: string }) => c.name === channel || c.id === channel ); if (foundChannel?.id) { channelId = foundChannel.id; } } catch (err) { // If we can't list channels, try using the channel name directly // Slack API might accept channel names } } // Post message to Slack // When using a user token (xoxp-), messages automatically post as that user // When using a bot token (xoxb-), messages post as the bot const response = await client.chat.postMessage({ channel: channelId, text: text, parse: "full", // Enable Markdown parsing }); if (!response.ok) { throw new Error(`Slack API error: ${response.error || "Unknown error"}`); } } catch (error) { if (error instanceof Error) { throw new Error(`Failed to post to Slack: ${error.message}`); } throw error; } } const isLikelySlackApiToken = (token: string) => /^xox[a-zA-Z]-/.test(token); /** * Validate Slack token */ export async function validateSlackToken(token: string): Promise<{ valid: boolean; team?: string; user?: string; error?: string; }> { if (!isLikelySlackApiToken(token)) { return { valid: false, error: "Token format looks wrong (expected Slack token like xoxb-... for bot or xoxp-... for user). Webhook URLs will not work for API calls." }; } const client = new WebClient(token); try { const result = await client.auth.test(); if (!result.ok) { return { valid: false, error: result.error || "Token validation failed", }; } const response: { valid: boolean; team?: string; user?: string; error?: string; } = { valid: true, }; if (result.team) { response.team = result.team; } if (result.user) { response.user = result.user; } return response; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : "Unknown error", }; } } /** * List available Slack channels */ export async function listSlackChannels(token: string): Promise<Array<{ id: string; name: string; is_private: boolean; }>> { if (!isLikelySlackApiToken(token)) { const authError: any = new Error("invalid_auth"); authError.isAuthError = true; authError.slackErrorCode = "invalid_token_format"; throw authError; } const client = new WebClient(token); try { const result = await client.conversations.list({ types: "public_channel,private_channel", exclude_archived: true, }); if (!result.ok || !result.channels) { return []; } return result.channels .filter((channel) => channel.id && channel.name) .map((channel) => ({ id: channel.id!, name: channel.name!, is_private: channel.is_private ?? false, })); } catch (error: any) { // Slack WebClient errors have a 'data' property with error details if (error?.data?.error) { const errorCode = error.data.error; if (errorCode === 'invalid_auth' || errorCode === 'not_authed') { const authError: any = new Error("invalid_auth"); authError.isAuthError = true; authError.slackErrorCode = errorCode; throw authError; } } // Check error message for auth errors const errorMessage = error?.message || String(error); if (errorMessage.includes("invalid_auth") || errorMessage.includes("not_authed")) { const authError: any = new Error("invalid_auth"); authError.isAuthError = true; throw authError; } // Re-throw original error to preserve error chain throw 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/SackeyDavid/cursor-eod-mcp'

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