a fun bot for the hc slack
1import { slackApp } from "../../../index";
2import TakesConfig from "../../../libs/config";
3import { db } from "../../../libs/db";
4import { takes as takesTable } from "../../../libs/schema";
5import { eq } from "drizzle-orm";
6import {
7 calculateElapsedTime,
8 getPausedDuration,
9 getRemainingTime,
10} from "../../../libs/time-periods";
11import { prettyPrintTime } from "../../../libs/time";
12
13// Check for paused sessions that have exceeded the max pause duration
14export async function expirePausedSessions() {
15 const now = new Date();
16 const pausedTakes = await db
17 .select()
18 .from(takesTable)
19 .where(eq(takesTable.status, "paused"));
20
21 for (const take of pausedTakes) {
22 const pausedDuration = getPausedDuration(take.periods) / 60000; // Convert to minutes
23
24 // Send warning notification when getting close to expiration
25 if (
26 pausedDuration >
27 TakesConfig.MAX_PAUSE_DURATION -
28 TakesConfig.NOTIFICATIONS.PAUSE_EXPIRATION_WARNING &&
29 !take.notifiedPauseExpiration
30 ) {
31 // Update notification flag
32 await db
33 .update(takesTable)
34 .set({
35 notifiedPauseExpiration: true,
36 })
37 .where(eq(takesTable.id, take.id));
38
39 // Send warning message
40 try {
41 const timeRemaining = Math.round(
42 TakesConfig.MAX_PAUSE_DURATION - pausedDuration,
43 );
44 await slackApp.client.chat.postMessage({
45 channel: take.userId,
46 text: `⚠️ Reminder: Your paused takes session will automatically complete in about ${timeRemaining} minutes if not resumed.`,
47 });
48 } catch (error) {
49 console.error(
50 "Failed to send pause expiration warning:",
51 error,
52 );
53 }
54 }
55
56 // Calculate elapsed time
57 const elapsedTime = calculateElapsedTime(JSON.parse(take.periods));
58
59 // Auto-expire paused sessions that exceed the max pause duration
60 if (pausedDuration > TakesConfig.MAX_PAUSE_DURATION) {
61 let ts: string | undefined;
62 // Notify user that their session was auto-completed
63 try {
64 const res = await slackApp.client.chat.postMessage({
65 channel: take.userId,
66 text: `⏰ Your paused takes session has been automatically completed because it was paused for more than ${TakesConfig.MAX_PAUSE_DURATION} minutes.\n\nPlease upload your takes video in this thread within the next 24 hours!`,
67 blocks: [
68 {
69 type: "section",
70 text: {
71 type: "mrkdwn",
72 text: `⏰ Your paused takes session has been automatically completed because it was paused for more than ${TakesConfig.MAX_PAUSE_DURATION} minutes.\n\nPlease upload your takes video in this thread within the next 24 hours!`,
73 },
74 },
75 {
76 type: "divider",
77 },
78 {
79 type: "context",
80 elements: [
81 {
82 type: "mrkdwn",
83 text: `\`${prettyPrintTime(elapsedTime)}\`${take.description ? ` working on: *${take.description}*` : ""}`,
84 },
85 ],
86 },
87 ],
88 });
89 ts = res.ts;
90 } catch (error) {
91 console.error(
92 "Failed to notify user of auto-completed session:",
93 error,
94 );
95 }
96
97 await db
98 .update(takesTable)
99 .set({
100 status: "waitingUpload",
101 completedAt: now,
102 elapsedTimeMs: elapsedTime,
103 ts,
104 notes: take.notes
105 ? `${take.notes} (Automatically completed due to pause timeout)`
106 : "Automatically completed due to pause timeout",
107 })
108 .where(eq(takesTable.id, take.id));
109 }
110 }
111}
112
113// Check for active sessions that are almost done
114export async function checkActiveSessions() {
115 const now = new Date();
116 const activeTakes = await db
117 .select()
118 .from(takesTable)
119 .where(eq(takesTable.status, "active"));
120
121 for (const take of activeTakes) {
122 const endTime = getRemainingTime(take.targetDurationMs, take.periods);
123
124 const remainingMinutes = endTime.remaining / 60000;
125
126 if (
127 remainingMinutes <= TakesConfig.NOTIFICATIONS.LOW_TIME_WARNING &&
128 remainingMinutes > 0 &&
129 !take.notifiedLowTime
130 ) {
131 await db
132 .update(takesTable)
133 .set({ notifiedLowTime: true })
134 .where(eq(takesTable.id, take.id));
135
136 try {
137 await slackApp.client.chat.postMessage({
138 channel: take.userId,
139 text: `⏱️ Your takes session has less than ${TakesConfig.NOTIFICATIONS.LOW_TIME_WARNING} minutes remaining.`,
140 });
141 } catch (error) {
142 console.error("Failed to send low time warning:", error);
143 }
144 }
145
146 const elapsedTime = calculateElapsedTime(JSON.parse(take.periods));
147
148 if (endTime.remaining <= 0) {
149 let ts: string | undefined;
150 try {
151 const res = await slackApp.client.chat.postMessage({
152 channel: take.userId,
153 text: "⏰ Your takes session has automatically completed because the time is up. Please upload your takes video in this thread within the next 24 hours!",
154 blocks: [
155 {
156 type: "section",
157 text: {
158 type: "mrkdwn",
159 text: "⏰ Your takes session has automatically completed because the time is up. Please upload your takes video in this thread within the next 24 hours!",
160 },
161 },
162 {
163 type: "divider",
164 },
165 {
166 type: "context",
167 elements: [
168 {
169 type: "mrkdwn",
170 text: `\`${prettyPrintTime(elapsedTime)}\`${take.description ? ` working on: *${take.description}*` : ""}`,
171 },
172 ],
173 },
174 ],
175 });
176
177 ts = res.ts;
178 } catch (error) {
179 console.error(
180 "Failed to notify user of completed session:",
181 error,
182 );
183 }
184
185 await db
186 .update(takesTable)
187 .set({
188 status: "waitingUpload",
189 completedAt: now,
190 elapsedTimeMs: elapsedTime,
191 ts,
192 notes: take.notes
193 ? `${take.notes} (Automatically completed - time expired)`
194 : "Automatically completed - time expired",
195 })
196 .where(eq(takesTable.id, take.id));
197 }
198 }
199}