a fun bot for the hc slack
1import { slackApp } from "../../../index";
2import { db } from "../../../libs/db";
3import { blog } from "../../../libs/Logger";
4import { takes as takesTable } from "../../../libs/schema";
5import handleHelp from "../handlers/help";
6import { handleHistory } from "../handlers/history";
7import handlePause from "../handlers/pause";
8import handleResume from "../handlers/resume";
9import handleStart from "../handlers/start";
10import handleStatus from "../handlers/status";
11import handleStop from "../handlers/stop";
12import { getActiveTake } from "../services/database";
13import upload from "../services/upload";
14import type { MessageResponse } from "../types";
15import { getDescriptionBlocks, getEditDescriptionBlocks } from "../ui/blocks";
16import * as Sentry from "@sentry/bun";
17
18export default function setupActions() {
19 // Handle button actions
20 slackApp.action(/^takes_(\w+)$/, async ({ payload, context }) => {
21 try {
22 const userId = payload.user.id;
23 const channelId = context.channelId || "";
24 const actionId = payload.actions[0]?.action_id as string;
25 const command = actionId.replace("takes_", "");
26 const descriptionInput =
27 payload.state.values.note_block?.note_input;
28
29 let response: MessageResponse | undefined;
30
31 const activeTake = await getActiveTake(userId);
32
33 // Route to the appropriate handler function
34 switch (command) {
35 case "start": {
36 if (activeTake.length > 0) {
37 if (context.respond) {
38 response = await handleStatus(userId);
39 }
40 } else {
41 if (!descriptionInput?.value?.trim()) {
42 response = getDescriptionBlocks(
43 "Please enter a note for your session.",
44 );
45 } else {
46 response = await handleStart(
47 userId,
48 channelId,
49 descriptionInput?.value?.trim(),
50 );
51 }
52 }
53 break;
54 }
55 case "pause":
56 response = await handlePause(userId);
57 break;
58 case "resume":
59 response = await handleResume(userId);
60 break;
61 case "stop":
62 response = await handleStop(userId);
63 break;
64 case "edit": {
65 if (!activeTake.length && context.respond) {
66 await context.respond({
67 text: "You don't have an active takes session to edit!",
68 response_type: "ephemeral",
69 });
70 return;
71 }
72
73 if (!descriptionInput) {
74 response = getEditDescriptionBlocks(
75 activeTake[0]?.description || "",
76 );
77 } else if (descriptionInput.value?.trim()) {
78 const takeToUpdate = activeTake[0];
79 if (!takeToUpdate) return;
80
81 // Update the note for the active session
82 await db.update(takesTable).set({
83 description: descriptionInput.value.trim(),
84 });
85
86 response = await handleStatus(userId);
87 } else {
88 response = getEditDescriptionBlocks(
89 "",
90 "Please enter a note for your session.",
91 );
92 }
93 break;
94 }
95
96 case "status":
97 response = await handleStatus(userId);
98 break;
99 case "history":
100 response = await handleHistory(userId);
101 break;
102 default:
103 response = await handleHelp();
104 break;
105 }
106
107 // Send the response
108 if (response && context.respond) {
109 await context.respond(response);
110 }
111 } catch (error) {
112 if (error instanceof Error)
113 blog(
114 `Error in \`${payload.actions[0]?.action_id}\` action: ${error.message}`,
115 "error",
116 );
117
118 // Capture the error in Sentry
119 Sentry.captureException(error, {
120 extra: {
121 actionId: payload.actions[0]?.action_id,
122 userId: payload.user.id,
123 channelId: context.channelId,
124 },
125 });
126
127 // Respond with error message to user
128 if (context.respond) {
129 await context.respond({
130 text: "An error occurred while processing your request. Please stand by while we try to put out the fire.",
131 response_type: "ephemeral",
132 });
133 }
134 }
135 });
136
137 // setup the upload actions
138 try {
139 upload();
140 } catch (error) {
141 Sentry.captureException(error, {
142 extra: {
143 context: "upload setup",
144 },
145 });
146 }
147}