Skip to content

Commit d1e8bcb

Browse files
Adding configurable frequency
1 parent 613ace5 commit d1e8bcb

5 files changed

Lines changed: 334 additions & 22 deletions

File tree

src/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { registerCoffeeChatCommands } from "./coffeeChats/coffeeChatCommands";
1212
import { registerCoffeeChatActions } from "./coffeeChats/coffeeChatActions";
1313

1414
export const SEMESTER = "sp24";
15+
export const DEFAULT_PAIRING_FREQUENCY_DAYS = 7;
1516

1617
const initializeFormServices = async () => {
1718
await sendFormReminders();

src/coffeeChats/coffeeChatCommands.ts

Lines changed: 160 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ import {
44
processCoffeeChatChannel,
55
registerCoffeeChatChannel,
66
getCoffeeChatsOptInStatus,
7+
startCoffeeChats,
8+
pauseCoffeeChats,
79
} from "./coffeeChatService";
810
import {
911
CoffeeChatConfigModel,
1012
CoffeeChatPairingModel,
1113
CoffeeChatUserPreferenceModel,
1214
} from "./coffeeChatModels";
15+
import { DEFAULT_PAIRING_FREQUENCY_DAYS } from "../app";
1316

1417
/**
1518
* Checks if a user is a workspace admin or owner
1619
*/
17-
const isUserAdmin = async (
18-
slackbot: App,
19-
userId: string,
20-
): Promise<boolean> => {
20+
const isUserAdmin = async (slackbot: App, userId: string): Promise<boolean> => {
2121
try {
2222
const userInfo = await slackbot.client.users.info({ user: userId });
2323
return (
@@ -53,10 +53,38 @@ export function registerCoffeeChatCommands(slackbot: App) {
5353

5454
const channelName = channelInfo.channel?.name || channelId;
5555

56-
await registerCoffeeChatChannel(channelId, channelName);
56+
// Parse frequency from command text (optional)
57+
let pairingFrequencyDays = DEFAULT_PAIRING_FREQUENCY_DAYS;
58+
const text = command.text.trim();
59+
if (text) {
60+
const parsed = parseInt(text, 10);
61+
if (!isNaN(parsed) && parsed > 0 && parsed <= 365) {
62+
pairingFrequencyDays = parsed;
63+
} else {
64+
await say({
65+
text: `❌ Invalid frequency. Please provide a number between 1 and 365 days.`,
66+
});
67+
return;
68+
}
69+
}
70+
71+
await registerCoffeeChatChannel(
72+
channelId,
73+
channelName,
74+
pairingFrequencyDays,
75+
);
76+
77+
const frequencyText =
78+
pairingFrequencyDays === 7
79+
? "weekly"
80+
: pairingFrequencyDays === 14
81+
? "biweekly"
82+
: pairingFrequencyDays === 30
83+
? "monthly"
84+
: `every ${pairingFrequencyDays} days`;
5785

5886
await say(
59-
`✅ This channel has been registered for biweekly coffee chat pairings! Members will be paired every two weeks.`,
87+
`✅ This channel has been registered for ${frequencyText} coffee chat pairings! Use \`/start-coffee-chats\` to begin the pairing cycle.`,
6088
);
6189
} catch (error) {
6290
await say(`❌ Error registering channel: ${error}`);
@@ -70,9 +98,7 @@ export function registerCoffeeChatCommands(slackbot: App) {
7098
// Check if user is admin
7199
const isAdmin = await isUserAdmin(slackbot, command.user_id);
72100
if (!isAdmin) {
73-
await say(
74-
`❌ Only workspace admins can manually trigger coffee chats.`,
75-
);
101+
await say(`❌ Only workspace admins can manually trigger coffee chats.`);
76102
return;
77103
}
78104

@@ -122,6 +148,131 @@ export function registerCoffeeChatCommands(slackbot: App) {
122148
}
123149
});
124150

151+
// Command to start coffee chats (begin the pairing cycle)
152+
slackbot.command("/start-coffee-chats", async ({ command, ack, say }) => {
153+
await ack();
154+
155+
// Check if user is admin
156+
const isAdmin = await isUserAdmin(slackbot, command.user_id);
157+
if (!isAdmin) {
158+
await say(`❌ Only workspace admins can start coffee chats.`);
159+
return;
160+
}
161+
162+
try {
163+
const channelId = command.channel_id;
164+
const config = await CoffeeChatConfigModel.findOne({ channelId });
165+
166+
if (!config) {
167+
await say({
168+
text: `❌ This channel is not registered for coffee chats. Use \`/register-coffee-chats\` first.`,
169+
});
170+
return;
171+
}
172+
173+
if (config.isStarted) {
174+
await say({
175+
text: `❌ Coffee chats are already running in this channel. Use \`/pause-coffee-chats\` to pause them.`,
176+
});
177+
return;
178+
}
179+
180+
// Start the coffee chats and create first pairing
181+
await startCoffeeChats(channelId);
182+
await processCoffeeChatChannel(config);
183+
184+
const nextPairingDate = moment()
185+
.tz("America/New_York")
186+
.add(config.pairingFrequencyDays, "days");
187+
188+
const frequencyText =
189+
config.pairingFrequencyDays === 7
190+
? "weekly"
191+
: config.pairingFrequencyDays === 14
192+
? "biweekly"
193+
: config.pairingFrequencyDays === 30
194+
? "monthly"
195+
: `every ${config.pairingFrequencyDays} days`;
196+
197+
await say({
198+
text: "Coffee chats have been started!",
199+
blocks: [
200+
{
201+
type: "section",
202+
text: {
203+
type: "mrkdwn",
204+
text: `✅ Coffee chats have been started (${frequencyText})! The first pairings have been created.`,
205+
},
206+
},
207+
{
208+
type: "section",
209+
text: {
210+
type: "mrkdwn",
211+
text: `📅 Next automatic pairing will be on ${nextPairingDate.format("dddd (MMM Do)")} at ${nextPairingDate.format("h:mm A z")}`,
212+
},
213+
},
214+
],
215+
});
216+
} catch (error) {
217+
await say(`❌ Error starting coffee chats: ${error}`);
218+
}
219+
});
220+
221+
// Command to pause coffee chats (stop automatic scheduling)
222+
slackbot.command("/pause-coffee-chats", async ({ command, ack, say }) => {
223+
await ack();
224+
225+
// Check if user is admin
226+
const isAdmin = await isUserAdmin(slackbot, command.user_id);
227+
if (!isAdmin) {
228+
await say(`❌ Only workspace admins can pause coffee chats.`);
229+
return;
230+
}
231+
232+
try {
233+
const channelId = command.channel_id;
234+
const config = await CoffeeChatConfigModel.findOne({ channelId });
235+
236+
if (!config) {
237+
await say({
238+
text: `❌ This channel is not registered for coffee chats.`,
239+
});
240+
return;
241+
}
242+
243+
if (!config.isStarted) {
244+
await say({
245+
text: `❌ Coffee chats are not currently running. Use \`/start-coffee-chats\` to begin.`,
246+
});
247+
return;
248+
}
249+
250+
await pauseCoffeeChats(channelId);
251+
252+
await say({
253+
text: "Coffee chats have been paused.",
254+
blocks: [
255+
{
256+
type: "section",
257+
text: {
258+
type: "mrkdwn",
259+
text: `⏸️ Coffee chats have been paused. No new automatic pairings will be created.`,
260+
},
261+
},
262+
{
263+
type: "section",
264+
text: {
265+
type: "mrkdwn",
266+
text: `Use \`/start-coffee-chats\` to resume automatic pairings.`,
267+
},
268+
},
269+
],
270+
});
271+
} catch (error) {
272+
await say(`❌ Error pausing coffee chats: ${error}`);
273+
}
274+
});
275+
125276
// Command to check coffee chat opt-in status
126277
slackbot.command("/coffee-chat-status", async ({ command, ack, say }) => {
127278
await ack();

src/coffeeChats/coffeeChatModels.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getModelForClass, prop, index } from "@typegoose/typegoose";
2+
import { DEFAULT_PAIRING_FREQUENCY_DAYS } from "../app";
23

34
class CoffeeChatPairing {
45
@prop({ required: true })
@@ -39,8 +40,17 @@ class CoffeeChatConfig {
3940
@prop({ default: true })
4041
isActive!: boolean;
4142

43+
@prop({ default: false })
44+
isStarted!: boolean;
45+
46+
@prop({ default: DEFAULT_PAIRING_FREQUENCY_DAYS })
47+
pairingFrequencyDays!: number;
48+
4249
@prop()
4350
lastPairingDate?: Date;
51+
52+
@prop()
53+
nextPairingDate?: Date;
4454
}
4555

4656
@index({ userId: 1, channelId: 1 }, { unique: true })

0 commit comments

Comments
 (0)