diff --git a/src/classes/SlackLogger.ts b/src/classes/SlackLogger.ts index 7118418..ffe78eb 100644 --- a/src/classes/SlackLogger.ts +++ b/src/classes/SlackLogger.ts @@ -31,34 +31,51 @@ export class SlackLogger { return SlackLogger.instance; } - private async log(level: LogLevel, message: string, logToConsole = true): Promise { + private async log( + level: LogLevel, + message: string, + codeBlockContent: string = "", + logToConsole = true, + ): Promise { const formattedMessage = this.formatMessage(level, message); if (logToConsole) { if (level === LogLevel.ERROR) { console.error(formattedMessage); + console.error(codeBlockContent); } else { console.log(formattedMessage); + console.log(codeBlockContent); } } - await postMessage(this.slackClient, loggingChannel, formattedMessage); + let formattedSlackMessage = formattedMessage; + + if (codeBlockContent) { + formattedSlackMessage += "\n" + this.formatcodeBlockContent(codeBlockContent); + } + + await postMessage(this.slackClient, loggingChannel, formattedSlackMessage); } - async info(message: string, logToConsole = true): Promise { - await this.log(LogLevel.INFO, message, logToConsole); + async info(message: string, codeBlockContent: string = "", logToConsole = true): Promise { + await this.log(LogLevel.INFO, message, codeBlockContent, logToConsole); } - async warning(message: string, logToConsole = true): Promise { - await this.log(LogLevel.WARNING, message, logToConsole); + async warning(message: string, codeBlockContent: string = "", logToConsole = true): Promise { + await this.log(LogLevel.WARNING, message, codeBlockContent, logToConsole); } - async error(message: string, logToConsole = true): Promise { - await this.log(LogLevel.ERROR, message, logToConsole); + async error(message: string, codeBlockContent: string = "", logToConsole = true): Promise { + await this.log(LogLevel.ERROR, message, codeBlockContent, logToConsole); } private formatMessage(level: LogLevel, message: string): string { const timeStamp = new Date().toISOString(); return `[${timeStamp}] [${level}] ${message}`; } + + private formatcodeBlockContent(codeBlockContent: string): string { + return `\`\`\`${codeBlockContent}\`\`\``; + } } diff --git a/src/listeners/commands/helpCommand.ts b/src/listeners/commands/helpCommand.ts index 62ed9e3..6dbd852 100644 --- a/src/listeners/commands/helpCommand.ts +++ b/src/listeners/commands/helpCommand.ts @@ -1,6 +1,7 @@ -import { slackBotToken } from "../../utils/env"; import { Middleware, SlackCommandMiddlewareArgs } from "@slack/bolt"; import { logCommandUsed } from "../../utils/logging"; +import { postEphemeralMessage } from "../../utils/slack"; +import { SlackLogger } from "../../classes/SlackLogger"; const message = `For more information, check out .`; @@ -8,10 +9,14 @@ export const helpCommandHandler: Middleware = async await ack(); await logCommandUsed(command); - await client.chat.postEphemeral({ - token: slackBotToken, - channel: command.channel_id, - user: command.user_id, - text: message, - }); + try { + await postEphemeralMessage(client, command.channel_id, command.user_id, message); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + throw new Error("This is a test error"); + } catch (error) { + SlackLogger.getInstance().error( + `Failed to post ephemeral message to user \`${command.user_id}\` with error:`, + error, + ); + } }; diff --git a/src/utils/channels.ts b/src/utils/channels.ts index 4844507..d48e797 100644 --- a/src/utils/channels.ts +++ b/src/utils/channels.ts @@ -36,13 +36,15 @@ export function filterSlackChannelsFromNames(names: string[], channels: SlackCha const channel = channels.find((channel) => channel.name === name); // If a channel with a given name is not found, it logs an error and continues to the next name. if (channel == undefined) { - SlackLogger.getInstance().warning(`could not find channel with name ${name} when filtering channels`); + SlackLogger.getInstance().warning(`could not find channel with name \`${name}\` when filtering channels`); continue; } // If a channel has an undefined name or id, it logs an error and continues to the next channel. if (channel.name == undefined || channel.id == undefined) { - SlackLogger.getInstance().error(`channel with name ${name} has undefined name or id. This should not happen.`); + SlackLogger.getInstance().error( + `channel with name \`${name}\` has undefined name or id. This should not happen.`, + ); continue; } @@ -79,7 +81,9 @@ export async function getAllSlackChannels(client: WebClient): Promise { await SlackLogger.getInstance().info( - `${command.user_name} used the ${command.command} command in ${command.channel_name}`, + `\`${command.user_name}\` used the \`${command.command}\` command in \`${command.channel_name}\``, ); } diff --git a/src/utils/slack.ts b/src/utils/slack.ts index 4c87f68..90aed2b 100644 --- a/src/utils/slack.ts +++ b/src/utils/slack.ts @@ -45,12 +45,48 @@ export async function postMessage( ...options, }); } catch (error) { - throw `Failed to post message to channel ${channel.name} with error ${error}`; + SlackLogger.getInstance().error(`Failed to post message to channel \`${channel.name}\` with error:`, String(error)); + throw error; } return res; } +/** + * Posts an ephemeral message to a Slack channel + * @param client Slack Web API client + * @param channel The Slack channel to post the message to + * @param user The Slack user to post the message to + * @param text The text of the message to post + * @param options Optional arguments for the message as per https://api.slack.com/methods/chat.postEphemeral#args + * @returns A promise that resolves to the response from the Slack API + */ +export async function postEphemeralMessage( + client: WebClient, + channel: string, + user: string, + text: string, + options?: ChatPostMessageOptionalArgs, +): Promise { + let res: ChatPostMessageResponse | undefined = undefined; + try { + res = await client.chat.postEphemeral({ + channel: channel, + user: user, + text: text, + ...options, + }); + + return res; + } catch (error) { + SlackLogger.getInstance().error( + `Failed to post ephemeral message to channel \`${channel}\` with error:`, + String(error), + ); + throw error; + } +} + // https://api.slack.com/methods/emoji.list /** * Retrieves all custom emoji from a Slack workspace. @@ -65,11 +101,7 @@ export async function getAllEmoji(client: WebClient): Promise { } return Object.keys(result.emoji); } catch (error) { - SlackLogger.getInstance().error(`Failed to get emojis for workspace: -\`\`\` -${error} -\`\`\` - `); + SlackLogger.getInstance().error(`Failed to get emojis for workspace:`, String(error)); throw error; } } @@ -88,24 +120,29 @@ export async function getAllSlackUsers( let cursor: string | undefined = undefined; while (true) { - const response = await client.users.list({ - limit: 900, - cursor: cursor, - }); - - if (response.members) { - response.members.forEach((user) => { - if (user.deleted == includeDeactivatedMembers) { - const userType: UserType = determineUserType(user); - const newGuest = new SlackUser(user.real_name as string, user.id as string, userType); - usersList.push(newGuest); - } + try { + const response = await client.users.list({ + limit: 900, + cursor: cursor, }); - } - cursor = response.response_metadata?.next_cursor; - if (!cursor) { - break; + if (response.members) { + response.members.forEach((user) => { + if (user.deleted == includeDeactivatedMembers) { + const userType: UserType = determineUserType(user); + const newGuest = new SlackUser(user.real_name as string, user.id as string, userType); + usersList.push(newGuest); + } + }); + } + + cursor = response.response_metadata?.next_cursor; + if (!cursor) { + break; + } + } catch (error) { + SlackLogger.getInstance().error(`Failed to get users from workspace:`, String(error)); + throw error; } } return usersList; @@ -140,12 +177,8 @@ export async function getChannelMembers(client: WebClient, channelId: string): P } return members.length > 0 ? members : undefined; } catch (error) { - SlackLogger.getInstance().error(`Failed to get members for channel with id ${channelId}: -\`\`\` -${error} -\`\`\` - `); - return undefined; + SlackLogger.getInstance().error(`Failed to get members for channel with id \`${channelId}\`:`, String(error)); + throw error; } } @@ -167,11 +200,19 @@ export function addReactionToMessage( // Convert timestamp to string if it's a number const timestampStr = typeof timestamp === "number" ? timestamp.toString() : timestamp; - return client.reactions.add({ - channel, - name: emoji, - timestamp: timestampStr, - }); + try { + return client.reactions.add({ + channel, + name: emoji, + timestamp: timestampStr, + }); + } catch (error) { + SlackLogger.getInstance().error( + `Failed to add reaction \`${emoji}\` to message \`${timestampStr}\` in \`${channel}\`:`, + String(error), + ); + throw error; + } } /** @@ -193,13 +234,11 @@ export async function getMessagePermalink( message_ts: timestamp, }); } catch (error) { - SlackLogger.getInstance() - .error(`Error fetching message permalink for message with timestamp ${timestamp} in ${channel}: -\`\`\` -${error} -\`\`\` - `); - return; + SlackLogger.getInstance().error( + `Error fetching message permalink for message with timestamp \`${timestamp}\` in \`${channel}\`:`, + String(error), + ); + throw error; } if (res?.ok) {