@microfox/slack
Version:
This package provides a lightweight, proxy interface to the official Slack Web API, offering a curated set of the most commonly used functions for building Slack integrations. It is designed to be simple, efficient, and easy to integrate into your project
1 lines • 34.8 kB
Source Map (JSON)
{"version":3,"sources":["../src/MicrofoxSlackClient.ts"],"sourcesContent":["import {\n WebClient,\n WebClientOptions,\n ChatPostMessageResponse,\n ConversationsCreateResponse,\n ConversationsInviteResponse,\n ConversationsKickResponse,\n ReactionsAddResponse,\n RemindersAddResponse,\n ChatPostMessageArguments,\n ConversationsListResponse,\n ConversationsInfoResponse,\n ConversationsJoinResponse,\n UsersListResponse,\n ConversationsHistoryResponse,\n UsersLookupByEmailResponse,\n UsersInfoResponse,\n FilesInfoResponse,\n FilesCompleteUploadExternalResponse,\n UsersConversationsResponse,\n} from '@slack/web-api';\nimport { Buffer } from 'buffer';\nimport dotenv from 'dotenv';\n\ndotenv.config();\n\nexport class MicrofoxSlackClient {\n private web: WebClient;\n\n constructor(token: string, options?: WebClientOptions) {\n this.web = new WebClient(token, options);\n }\n\n /**\n * Lists channels in a workspace.\n * @param cursor A cursor to the next page of results.\n * @param limit The maximum number of channels to return.\n * @param types Channel types to include. Defaults to all types (public, private, im).\n */\n async getChannels({\n cursor,\n limit = 50,\n types = ['public', 'private'],\n }: {\n cursor?: string;\n limit?: number;\n types?: ('public' | 'private' | 'im')[];\n }): Promise<{\n channels: ConversationsListResponse['channels'];\n nextCursor: string | undefined;\n }> {\n // Convert types to Slack API format\n const typeMapping = {\n 'public': 'public_channel',\n 'private': 'private_channel',\n 'im': 'im'\n };\n const slackTypes = types.map(type => typeMapping[type]).join(',');\n\n let channels: ConversationsListResponse['channels'] = [];\n let nextCursor: string | undefined = cursor;\n let hasMore = true;\n while (hasMore) {\n const result = await this.web.conversations.list({\n types: slackTypes,\n limit,\n cursor: nextCursor,\n });\n channels = [...channels, ...(result.channels || [])];\n nextCursor = result.response_metadata?.next_cursor;\n hasMore = !!nextCursor && channels.length < limit;\n }\n return {\n channels,\n nextCursor,\n };\n }\n\n /**\n * Lists channel IDs and names in a workspace.\n * @param cursor A cursor to the next page of results.\n * @param limit The maximum number of channels to return.\n * @param types Channel types to include. Defaults to all types (public, private, im).\n */\n async getChannelsIds({\n cursor,\n limit,\n types = ['public', 'private', 'im'],\n }: {\n cursor?: string;\n limit?: number;\n types?: ('public' | 'private' | 'im')[];\n }): Promise<{\n channels: {\n id: string;\n name: string;\n }[];\n nextCursor: string | undefined;\n }> {\n const response = await this.getChannels({ cursor, limit, types });\n return {\n channels: response.channels?.map((channel) => ({\n id: channel.id || '',\n name: channel.name || '',\n })) || [],\n nextCursor: response.nextCursor,\n }\n }\n\n /**\n * Fetches information about a conversation.\n * @param channelId Conversation ID to fetch information for.\n */\n async getChannelConversationInfo({ channelId }: { channelId: string }): Promise<ConversationsInfoResponse['channel']> {\n const result = await this.web.conversations.info({\n channel: channelId,\n });\n return result.channel;\n }\n\n /**\n * Lists all users in a workspace.\n * @param cursor A cursor to the next page of results.\n * @param limit The maximum number of users to return.\n * @param includeBots Whether to include bots.\n */\n async getActiveUsers({\n cursor,\n limit,\n includeBots = false,\n }: {\n cursor?: string;\n limit?: number;\n includeBots?: boolean;\n }): Promise<{\n users: UsersListResponse['members'];\n nextCursor: string | undefined;\n }> {\n const result = await this.web.users.list({\n limit: limit || 100,\n ...(cursor ? { cursor } : {}),\n });\n return {\n users: result.members?.filter((member) => !member.deleted)?.filter((member) => includeBots ? true : !member.is_bot) || [],\n nextCursor: result.response_metadata?.next_cursor,\n }\n }\n\n /**\n * Lists all users in a workspace.\n * @param cursor A cursor to the next page of results.\n * @param limit The maximum number of users to return.\n * @param includeBots Whether to include bots.\n */\n async getActiveUsersIds({\n cursor,\n limit,\n includeBots = false,\n }: {\n includeBots?: boolean;\n cursor?: string;\n limit?: number;\n }): Promise<{\n users: {\n id: string;\n name: string;\n real_name: string;\n display_name: string;\n title: string;\n email: string;\n }[];\n nextCursor: string | undefined;\n }> {\n const response = await this.getActiveUsers({ includeBots, cursor, limit });\n return {\n users: response.users?.map((user) => ({\n id: user.id || '',\n name: user.name || '',\n email: user.profile?.email || '',\n real_name: user.profile?.real_name || '',\n display_name: user.profile?.display_name || '',\n title: user.profile?.title || '',\n })) || [],\n nextCursor: response.nextCursor,\n }\n }\n\n /**\n * Lists all users in a channel.\n * @param channelId Channel ID to get members of.\n */\n async getChannelMembers({ channelId, includeBots = false, limit, nextCursor }: { channelId: string, includeBots?: boolean, limit?: number, nextCursor?: string }): Promise<{\n members: {\n id: string;\n name: string;\n is_bot: boolean;\n real_name: string;\n display_name: string;\n title: string;\n email: string;\n is_deleted: boolean;\n }[];\n nextCursor: string | undefined;\n }> {\n const result = await this.web.conversations.members({\n channel: channelId,\n limit,\n cursor: nextCursor,\n });\n let members: {\n id: string;\n name: string;\n is_bot: boolean;\n real_name: string;\n display_name: string;\n title: string;\n email: string;\n is_deleted: boolean;\n }[] = [];\n if (result?.members) {\n members = await Promise.all(result?.members?.map(async (member) => {\n const user = await this.getUserInfo({ userId: member });\n return {\n id: member || '',\n name: user?.name || '',\n is_bot: user?.is_bot || false,\n real_name: user?.profile?.real_name || '',\n display_name: user?.profile?.display_name || '',\n title: user?.profile?.title || '',\n email: user?.profile?.email || '',\n is_deleted: user?.deleted || false,\n };\n }));\n }\n return {\n members: members?.filter((member) => includeBots ? true : !member.is_bot) || [],\n nextCursor: result.response_metadata?.next_cursor,\n }\n }\n\n /**\n * Finds a user by their email address.\n * @param email The email address of the user to find.\n */\n async getUserByEmail({ email }: { email: string }): Promise<UsersLookupByEmailResponse['user']> {\n const result = await this.web.users.lookupByEmail({ email });\n return result.user;\n }\n\n /**\n * Finds users by their email addresses.\n * @param emails The email addresses of the users to find.\n */\n async getUsersByEmails({ emails }: { emails: string[] }): Promise<UsersLookupByEmailResponse['user'][]> {\n const result = await Promise.all(emails.map(async (email) => {\n const user = await this.getUserByEmail({ email });\n return user;\n }));\n return result;\n }\n\n /**\n * Sends a direct message to a user.\n * @param userId The ID of the user to message.\n * @param text The text of the message to send.\n * @param username Optional custom username for the message.\n * @param icon_url Optional URL to an image to use as the icon for the message.\n */\n async messageUser({\n userId,\n text,\n username,\n icon_url,\n }: {\n userId: string;\n text: string;\n username?: string;\n icon_url?: string;\n }): Promise<ChatPostMessageResponse> {\n const im = await this.web.conversations.open({ users: userId });\n if (im.ok && im.channel?.id) {\n return this.messageChannel({\n channelId: im.channel.id,\n text,\n username,\n icon_url,\n });\n }\n throw new Error(`Could not open DM with user ${userId}`);\n }\n\n /**\n * Sends a message to a channel.\n * @param channelId The ID of the channel to message.\n * @param text The text of the message to send.\n * @param username Optional custom username for the message.\n * @param icon_url Optional URL to an image to use as the icon for the message.\n */\n async messageChannel({\n channelId,\n text,\n username,\n icon_url,\n }: {\n channelId: string;\n text: string;\n username?: string;\n icon_url?: string;\n }): Promise<ChatPostMessageResponse> {\n const payload: ChatPostMessageArguments = {\n channel: channelId,\n text: text,\n };\n\n const finalUsername = username ?? process.env.SLACK_AUTHOR_NAME;\n if (finalUsername) {\n payload.username = finalUsername;\n }\n\n const finalIconUrl = icon_url ?? process.env.SLACK_ICON_URL;\n if (finalIconUrl) {\n payload.icon_url = finalIconUrl;\n }\n\n return this.web.chat.postMessage(payload);\n }\n\n /**\n * Sends a direct message to multiple users with optional templating.\n * @param userIds The IDs of the users to message.\n * @param text The text of the message to send. Can include template variables for personalization.\n * @param username Optional custom username for the message.\n * @param icon_url Optional URL to an image to use as the icon for the message.\n * \n * Available template variables:\n * - {mention} - Mentions the user (@username)\n * - {user_name} - User's name (fallback: username -> real_name -> display_name)\n * - {user_email} - User's email address\n * - {user_title} - User's job title\n * - {user_phone} - User's phone number\n * - {user_status} - User's status text\n * - {user_avatar} - User's profile image URL\n * - {first_name} - User's first name\n * - {last_name} - User's last name\n * \n * @example\n * ```typescript\n * // Simple message\n * await client.messageUsers({\n * userIds: ['U1234567890', 'U0987654321'],\n * text: \"Hello everyone!\"\n * });\n * \n * // Templated message\n * await client.messageUsers({\n * userIds: ['U1234567890', 'U0987654321'],\n * text: \"Hi {mention}! Your title is {user_title} and email is {user_email}.\"\n * });\n * ```\n */\n async messageUsers({\n userIds,\n text,\n username,\n icon_url,\n }: {\n userIds: string[];\n text: string;\n username?: string;\n icon_url?: string;\n }): Promise<ChatPostMessageResponse[]> {\n const results: ChatPostMessageResponse[] = [];\n\n // Check if text contains template variables\n const hasTemplateVars = /{(mention|user_name|user_email|user_title|user_phone|user_status|user_avatar|first_name|last_name)}/.test(text);\n\n if (!hasTemplateVars) {\n // No template variables, send same message to all users\n const responses = await Promise.all(\n userIds.map((userId) =>\n this.messageUser({ userId, text, username, icon_url })\n )\n );\n return responses;\n }\n\n // Process each user ID for templated messages\n for (const userId of userIds) {\n try {\n // Get detailed user information\n const user = await this.getUserInfo({ userId });\n \n if (!user) {\n // Skip user if not found, but continue with others\n continue;\n }\n\n // Replace template variables with actual user data using fallbacks\n let personalizedMessage = text\n // {mention} -> <@U1234567>\n .replace(/{mention}/g, `<@${user.id}>`)\n // {user_name} -> best available name\n .replace(/{user_name}/g, user.name || user.real_name || user.profile?.display_name || user.profile?.real_name || 'User')\n // {user_email} -> email\n .replace(/{user_email}/g, user.profile?.email || '')\n // {user_title} -> job title\n .replace(/{user_title}/g, user.profile?.title || '')\n // {user_phone} -> phone number\n .replace(/{user_phone}/g, user.profile?.phone || '')\n // {user_status} -> status text\n .replace(/{user_status}/g, user.profile?.status_text || '')\n // {user_avatar} -> profile image (prefer 72px size)\n .replace(/{user_avatar}/g, user.profile?.image_72 || user.profile?.image_192 || user.profile?.image_original || '')\n // {first_name} -> first name\n .replace(/{first_name}/g, user.profile?.first_name || user.profile?.real_name?.split(' ')[0] || user.name || 'User')\n // {last_name} -> last name\n .replace(/{last_name}/g, user.profile?.last_name || (user.profile?.real_name?.split(' ').slice(1).join(' ')) || '');\n\n // Send the personalized message\n const result = await this.messageUser({\n userId: user.id!,\n text: personalizedMessage,\n username,\n icon_url,\n });\n\n results.push(result);\n } catch (error) {\n // Skip failed users but continue with others\n console.error(`Failed to send message to user ${userId}:`, error);\n }\n }\n\n return results;\n }\n\n /**\n * Sends a message to multiple channels.\n * @param channelIds The IDs of the channels to message.\n * @param text The text of the message to send.\n * @param username Optional custom username for the message.\n * @param icon_url Optional URL to an image to use as the icon for the message.\n */\n async messageChannels({\n channelIds,\n text,\n username,\n icon_url,\n }: {\n channelIds: string[];\n text: string;\n username?: string;\n icon_url?: string;\n }): Promise<ChatPostMessageResponse[]> {\n const results = await Promise.all(\n channelIds.map((channelId) =>\n this.messageChannel({ channelId, text, username, icon_url })\n )\n );\n return results;\n }\n \n /**\n * Sets a reminder for a user.\n * @param userId The ID of the user to set a reminder for.\n * @param text The text of the reminder.\n * @param time A string describing when the reminder should fire (e.g., \"in 5 minutes\" or a Unix timestamp).\n */\n async setReminder({\n userId,\n text,\n time\n }: {\n userId: string;\n text: string;\n time: string;\n }): Promise<RemindersAddResponse> {\n return this.web.reminders.add({\n user: userId,\n text: text,\n time: time,\n });\n }\n\n /**\n * Creates a new channel.\n * @param name The name of the channel to create.\n * @param isPrivate Whether the channel should be private. Defaults to false.\n * @param join Whether to join the channel after creation. Defaults to true.\n * @param userIds Optional array of user IDs to add to the channel after creation.\n */\n async createChannel({\n name,\n isPrivate = false,\n userIds,\n }: {\n name: string;\n isPrivate?: boolean;\n join?: boolean;\n userIds?: string[];\n }): Promise<ConversationsCreateResponse['channel']> {\n const result = await this.web.conversations.create({\n name: name,\n is_private: isPrivate,\n });\n \n if (result?.channel?.id) {\n // Add users to the channel if provided\n if (userIds && userIds.length > 0) {\n await this.addUsersToChannel({ \n channelId: result.channel.id, \n userIds \n });\n }\n }\n \n return result.channel;\n }\n\n /**\n * Adds a reaction to a message.\n * @param channelId The ID of the channel where the message is.\n * @param timestamp The timestamp of the message to react to.\n * @param reaction The name of the emoji to use for the reaction.\n */\n async reactMessage({\n channelId,\n timestamp,\n reaction\n }: {\n channelId: string;\n timestamp: string;\n reaction: string;\n }): Promise<ReactionsAddResponse> {\n return this.web.reactions.add({\n channel: channelId,\n timestamp: timestamp,\n name: reaction,\n });\n }\n\n /**\n * Gets information about a user.\n * @param userId The ID of the user to get information for.\n */\n async getUserInfo({ userId }: { userId: string }): Promise<UsersInfoResponse['user']> {\n const result = await this.web.users.info({\n user: userId,\n });\n return result.user;\n }\n\n /**\n * Fetches a list of conversations a user is a member of.\n * @param userId The ID of the user to fetch conversations for.\n * @param cursor A cursor to the next page of results.\n * @param limit The maximum number of conversations to return.\n * @param types An array of conversation types to include.\n * @param excludeArchived Whether to exclude archived conversations.\n */\n async getUserChannels({\n userId,\n cursor,\n limit,\n types = ['public_channel', 'private_channel'],\n excludeArchived,\n }: {\n userId: string;\n cursor?: string;\n limit?: number;\n types?: ('public_channel' | 'private_channel' | 'mpim' | 'im')[];\n excludeArchived?: boolean;\n }): Promise<{\n channels: UsersConversationsResponse['channels'];\n nextCursor: string | undefined;\n }> {\n const result = await this.web.users.conversations({\n user: userId,\n cursor,\n limit,\n types: types.join(','),\n exclude_archived: excludeArchived,\n });\n\n return {\n channels: result.channels || [],\n nextCursor: result.response_metadata?.next_cursor,\n };\n }\n\n /**\n * Replies to a message in a thread.\n * @param channelId The ID of the channel where the message is.\n * @param thread_ts The timestamp of the message to reply to, establishing the thread.\n * @param text The text of the reply.\n * @param username Optional custom username for the message.\n * @param icon_url Optional URL to an image to use as the icon for the message.\n */\n async replyMessage({\n channelId,\n thread_ts,\n text,\n username,\n icon_url,\n }: {\n channelId: string;\n thread_ts: string;\n text: string;\n username?: string;\n icon_url?: string;\n }): Promise<ChatPostMessageResponse> {\n const payload: ChatPostMessageArguments = {\n channel: channelId,\n thread_ts: thread_ts,\n text: text,\n };\n\n const finalUsername = username ?? process.env.SLACK_AUTHOR_NAME;\n if (finalUsername) {\n payload.username = finalUsername;\n }\n\n const finalIconUrl = icon_url ?? process.env.SLACK_ICON_URL;\n if (finalIconUrl) {\n payload.icon_url = finalIconUrl;\n }\n\n return this.web.chat.postMessage(payload);\n }\n\n /**\n * Joins a channel.\n * @param channelId The ID of the channel to join.\n */\n async joinChannel({\n channelId\n }: {\n channelId: string;\n }): Promise<ConversationsJoinResponse> {\n return this.web.conversations.join({\n channel: channelId,\n });\n }\n\n\n\n /**\n * Adds multiple users to a channel.\n * @param channelId The ID of the channel to add users to.\n * @param userIds Array of user IDs to add to the channel.\n */\n async addUsersToChannel({\n channelId,\n userIds\n }: {\n channelId: string;\n userIds: string[];\n }): Promise<ConversationsInviteResponse[]> {\n const results: ConversationsInviteResponse[] = [];\n \n // Slack API supports adding multiple users in one call with comma-separated list\n // but to handle errors better, we'll add them in batches or individually if needed\n try {\n // Try to add all users at once (more efficient)\n const result = await this.web.conversations.invite({\n channel: channelId,\n users: userIds.join(','),\n });\n results.push(result);\n } catch (error) {\n // If bulk invite fails, try adding users individually\n console.warn('Bulk user invite failed, trying individual invites:', error);\n for (const userId of userIds) {\n try {\n const result = await this.web.conversations.invite({\n channel: channelId,\n users: userId,\n });\n results.push(result);\n } catch (individualError) {\n console.error(`Failed to add user ${userId} to channel ${channelId}:`, individualError);\n // Continue with other users even if one fails\n }\n }\n }\n \n return results;\n }\n\n /**\n * Removes a user from a channel.\n * @param channelId The ID of the channel to remove the user from.\n * @param userId The ID of the user to remove.\n */\n async removeUserFromChannel({\n channelId,\n userId\n }: {\n channelId: string;\n userId: string;\n }): Promise<ConversationsKickResponse> {\n return this.web.conversations.kick({\n channel: channelId,\n user: userId,\n });\n }\n\n /**\n * Gets information about a file.\n * @param fileId The ID of the file to get information for.\n */\n async getFileInfo({ fileId }: { fileId: string }): Promise<FilesInfoResponse['file']> {\n const result = await this.web.files.info({\n file: fileId,\n });\n return result.file;\n }\n\n /**\n * Fetches a conversation's history of messages and events.\n * @param channelId Conversation ID to fetch history for.\n * @param limit The maximum number of items to return.\n * @param latest End of the time range of messages to include in results.\n * @param oldest Start of the time range of messages to include in results.\n * @param inclusive Include messages with latest or oldest timestamps in results.\n * @param cursor Paginate through collections of data by setting the cursor parameter to a next_cursor attribute returned by a previous request's response_metadata.\n */\n async getConversationHistory({\n channelId,\n limit,\n latest,\n oldest,\n inclusive,\n cursor,\n }: {\n channelId: string;\n limit?: number;\n latest?: string;\n oldest?: string;\n inclusive?: boolean;\n cursor?: string;\n }): Promise<{\n messages: ConversationsHistoryResponse['messages'];\n nextCursor: string | undefined;\n }> {\n const result = await this.web.conversations.history({\n channel: channelId,\n limit,\n latest,\n oldest,\n inclusive,\n cursor,\n });\n return {\n messages: result.messages || [],\n nextCursor: result.response_metadata?.next_cursor,\n }\n }\n\n /**\n * Uploads a file to a channel using an external URL.\n * @param filename The name of the file.\n * @param file A Buffer containing the file content.\n * @param channelId The ID of the channel to upload the file to. Can be a comma-separated list of strings.\n * @param alt_text Description of image for screen-reader.\n * @param snippet_type Syntax type of the snippet being uploaded.\n * @param initialComment The message text introducing the file in specified channels.\n * @param title An optional title for the file.\n */\n async uploadFile({\n filename,\n file,\n channelId,\n alt_text,\n snippet_type,\n initialComment,\n title,\n }: {\n filename: string;\n file: Buffer;\n channelId?: string;\n alt_text?: string;\n snippet_type?: string;\n initialComment?: string;\n title?: string;\n }): Promise<FilesCompleteUploadExternalResponse['files']> {\n // Step 1: Get an upload URL\n const uploadURLResponse = await this.web.files.getUploadURLExternal({\n filename,\n length: file.length,\n alt_text,\n snippet_type,\n });\n\n if (!uploadURLResponse.ok || !uploadURLResponse.upload_url || !uploadURLResponse.file_id) {\n throw new Error(`Failed to get upload URL: ${uploadURLResponse.error}`);\n }\n\n const { upload_url, file_id } = uploadURLResponse;\n\n // Step 2: Upload the file to the URL\n const uploadResponse = await fetch(upload_url, {\n method: 'POST',\n body: file,\n });\n\n if (!uploadResponse.ok) {\n throw new Error(`File upload failed with status: ${uploadResponse.statusText}`);\n }\n\n // Step 3: Complete the upload\n const completeUploadResponse = await this.web.files.completeUploadExternal({\n files: [{ id: file_id, title: title || filename }],\n channel_id: channelId,\n initial_comment: initialComment,\n });\n\n if (!completeUploadResponse.ok) {\n throw new Error(`Failed to complete upload: ${completeUploadResponse.error}`);\n }\n\n return completeUploadResponse.files;\n }\n} \n"],"mappings":"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","names":["_a","_b"]}