a fun bot for the hc slack
at main 3.2 kB view raw
1import { slackClient } from "../index"; 2import Bottleneck from "bottleneck"; 3import Queue from "./queue"; 4import colors from "colors"; 5import * as Sentry from "@sentry/bun"; 6import type { 7 ChatPostMessageRequest, 8 ChatPostMessageResponse, 9} from "slack-edge"; 10 11// Create a rate limiter with Bottleneck 12const limiter = new Bottleneck({ 13 minTime: 1000, // 1 second between each request 14}); 15 16const messageQueue = new Queue(); 17 18async function sendMessage( 19 message: ChatPostMessageRequest, 20): Promise<ChatPostMessageResponse> { 21 try { 22 return await limiter.schedule(() => 23 slackClient.chat.postMessage(message), 24 ); 25 } catch (error) { 26 Sentry.captureException(error, { 27 extra: { channel: message.channel, text: message.text }, 28 tags: { type: "slack_message_error" }, 29 }); 30 console.error("Failed to send Slack message:", error); 31 throw error; 32 } 33} 34 35async function slog( 36 logMessage: string, 37 location?: { 38 thread_ts?: string; 39 channel: string; 40 }, 41): Promise<void> { 42 try { 43 const channel = location?.channel || process.env.SLACK_LOG_CHANNEL; 44 45 if (!channel) { 46 throw new Error("No Slack channel specified for logging"); 47 } 48 49 const message: ChatPostMessageRequest = { 50 channel, 51 thread_ts: location?.thread_ts, 52 text: logMessage.substring(0, 2500), 53 blocks: [ 54 { 55 type: "section", 56 text: { 57 type: "mrkdwn", 58 text: logMessage 59 .split("\n") 60 .map((a) => `> ${a}`) 61 .join("\n"), 62 }, 63 }, 64 { 65 type: "context", 66 elements: [ 67 { 68 type: "mrkdwn", 69 text: `${new Date().toString()}`, 70 }, 71 ], 72 }, 73 ], 74 }; 75 76 messageQueue.enqueue(() => sendMessage(message)); 77 } catch (error) { 78 Sentry.captureException(error, { 79 extra: { logMessage, location, channel: location?.channel }, 80 tags: { type: "slog_error" }, 81 }); 82 console.error("Failed to queue Slack log message:", error); 83 } 84} 85 86type LogType = "info" | "start" | "cron" | "error"; 87 88type LogMetadata = { 89 error?: Error; 90 context?: string; 91 additional?: Record<string, unknown>; 92}; 93 94export async function clog( 95 logMessage: string, 96 type: LogType, 97 metadata?: LogMetadata, 98): Promise<void> { 99 const timestamp = new Date().toISOString(); 100 const formattedMessage = `[${timestamp}] ${logMessage}`; 101 102 switch (type) { 103 case "info": 104 console.log(colors.blue(formattedMessage)); 105 break; 106 case "start": 107 console.log(colors.green(formattedMessage)); 108 break; 109 case "cron": 110 console.log(colors.magenta(`[CRON]: ${formattedMessage}`)); 111 break; 112 case "error": { 113 const errorMessage = colors.red.bold( 114 `Yo <@S0790GPRA48> deres an error \n\n [ERROR]: ${formattedMessage}`, 115 ); 116 console.error(errorMessage); 117 break; 118 } 119 default: 120 console.log(formattedMessage); 121 } 122} 123 124export async function blog( 125 logMessage: string, 126 type: LogType, 127 location?: { 128 thread_ts?: string; 129 channel: string; 130 }, 131 metadata?: LogMetadata, 132): Promise<void> { 133 try { 134 await Promise.all([ 135 slog(logMessage, location), 136 clog(logMessage, type, metadata), 137 ]); 138 } catch (error) { 139 console.error("Failed to log message:", error); 140 Sentry.captureException(error, { 141 extra: { logMessage, type, location, metadata }, 142 tags: { type: "blog_error" }, 143 }); 144 } 145} 146 147export { clog as default, slog };