Skip to content

Commit

Permalink
Implement meeting reminder command
Browse files Browse the repository at this point in the history
  • Loading branch information
QuantumManiac committed Feb 7, 2024
1 parent 5fcd7f6 commit fef8000
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const app = new App({
});

// Register listeners
registerListeners(app);
registerListeners(app, auth);

// Schedule tasks
scheduleTasks(app.client, auth);
Expand Down
6 changes: 5 additions & 1 deletion src/listeners/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// commands/index.ts
import { App } from "@slack/bolt";
import { OAuth2Client } from "google-auth-library";

import { helpCommandHandler } from "./helpCommand";
import { meetingReminderCommandHandler } from "./meetingReminderCommand";

const register = (app: App): void => {
const register = (app: App, googleAuth: OAuth2Client): void => {
app.command("/help", helpCommandHandler);
app.command("/meeting_reminder", (payload) => meetingReminderCommandHandler(payload, googleAuth));
// Other command registrations would go here
};

Expand Down
46 changes: 46 additions & 0 deletions src/listeners/commands/meetingReminderCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { AllMiddlewareArgs, SlackCommandMiddlewareArgs } from "@slack/bolt";
import { OAuth2Client } from "google-auth-library";

import { filterEventsForChannels, getEvents, parseEvents } from "../../utils/googleCalendar";
import { getAllSlackChannels } from "../../utils/channels";
import { logCommandUsed } from "../../utils/logging";
import { postEphemeralMessage } from "../../utils/slack";
import { EventReminderType, remindUpcomingEvent } from "../../utils/eventReminders";
import { SlackLogger } from "../../classes/SlackLogger";

export async function meetingReminderCommandHandler(
{ command, ack, client }: SlackCommandMiddlewareArgs & AllMiddlewareArgs,
googleAuth: OAuth2Client,
): Promise<void> {
ack();
logCommandUsed(command);

try {
const slackChannels = await getAllSlackChannels(client);
const fetchedEvents = await getEvents(googleAuth);
const events = parseEvents(fetchedEvents, slackChannels);
const eventsInChannel = filterEventsForChannels(events, [command.channel_name]);

if (eventsInChannel.length === 0) {
await postEphemeralMessage(client, command.channel_id, command.user_id, "No upcoming events in this channel.");
} else {
const soonestEvent = eventsInChannel.sort((a, b) => a.start.getTime() - b.start.getTime())[0];

const commandText = command.text.trim().toLowerCase();

await remindUpcomingEvent(
soonestEvent,
client,
commandText == "ping" ? EventReminderType.MANUAL_PING : EventReminderType.MANUAL,
);
await postEphemeralMessage(
client,
command.channel_id,
command.user_id,
"Manual reminder sent for next event in channel.",
);
}
} catch (error) {
SlackLogger.getInstance().error("Failed to send manual meeting reminder", error);
}
}
6 changes: 4 additions & 2 deletions src/listeners/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { App } from "@slack/bolt";
import { OAuth2Client } from "google-auth-library";

import actions from "./actions";
import commands from "./commands";
import events from "./events";
import messages from "./messages";
import shortcuts from "./shortcuts";
import views from "./views";

const registerListeners = (app: App): void => {
const registerListeners = (app: App, googleClient: OAuth2Client): void => {
actions.register(app);
commands.register(app);
commands.register(app, googleClient);
events.register(app);
messages.register(app);
shortcuts.register(app);
Expand Down
28 changes: 19 additions & 9 deletions src/utils/eventReminders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const TIME_CHECK_INTERVAL = 1000 * 60 * 5; // 5 minutes in milliseconds
* The types of event reminders that can be sent
*/
export enum EventReminderType {
MANUAL = 1,
MANUAL_PING = 2,
FIVE_MINUTES = 1000 * 60 * 5, // 5 minutes in milliseconds
SIX_HOURS = 1000 * 60 * 60 * 6, // 6 hours in milliseconds
}
Expand Down Expand Up @@ -59,12 +61,14 @@ export function getEventReminderType(event: CalendarEvent): EventReminderType |
* Posts a reminder for the given event to the channel it is associated with
* @param event The event to post a reminder for
* @param client Slack Web API client
* @param reminderType The type of reminder to post
* @param defaultSlackChannels The default Slack channels to post reminders to. If not provided, the default channels will be fetched from the Slack API
* @param allSlackUsersInWorkspace All Slack users in the workspace. If not provided, the users will be fetched from the Slack API
*/
export async function remindUpcomingEvent(
event: CalendarEvent,
client: WebClient,
reminderType: EventReminderType | null,
defaultSlackChannels?: SlackChannel[],
allSlackUsersInWorkspace?: SlackUser[],
): Promise<void> {
Expand All @@ -73,9 +77,7 @@ export async function remindUpcomingEvent(
return;
}

const reminderType = getEventReminderType(event);

if (!reminderType) {
if (reminderType == null) {
return;
}

Expand Down Expand Up @@ -104,10 +106,15 @@ export async function remindUpcomingEvent(
allSlackUsersInWorkspace,
);

const reminderTypeString = reminderType === EventReminderType.FIVE_MINUTES ? "5 minute" : "6 hour";
const reminderTypeStrings = {
[EventReminderType.MANUAL]: "manually triggered",
[EventReminderType.MANUAL_PING]: "manually triggered (with ping)",
[EventReminderType.FIVE_MINUTES]: "5 minute",
[EventReminderType.SIX_HOURS]: "6 hour",
};

SlackLogger.getInstance().info(
`Sent ${reminderTypeString} reminder for event \`${
`Sent ${reminderTypeStrings[reminderType]} reminder for event \`${
event.title
}\` at \`${event.start.toISOString()}\` to channel \`${
event.minervaEventMetadata.channel.name
Expand Down Expand Up @@ -206,7 +213,8 @@ export async function remindUpcomingEvents(client: WebClient, events: CalendarEv
const allSlackUsersInWorkspace = await getAllSlackUsers(client);

events.forEach((event) => {
remindUpcomingEvent(event, client, defaultSlackChannels, allSlackUsersInWorkspace);
const reminderType = getEventReminderType(event);
remindUpcomingEvent(event, client, reminderType, defaultSlackChannels, allSlackUsersInWorkspace);
});
}

Expand All @@ -217,9 +225,11 @@ export async function remindUpcomingEvents(client: WebClient, events: CalendarEv
* @returns The generated reminder text
*/
export function generateEventReminderChannelText(event: CalendarEvent, reminderType: EventReminderType): string {
let message = `${reminderType == EventReminderType.FIVE_MINUTES ? "<!channel>\n" : ""}Reminder: *${
event.title
}* is occurring`;
let message = `${
reminderType == EventReminderType.FIVE_MINUTES || reminderType == EventReminderType.MANUAL_PING
? "<!channel>\n"
: ""
}Reminder: *${event.title}* is occurring`;

if (reminderType === EventReminderType.FIVE_MINUTES) {
const timeUntilEvent = event.start.getTime() - new Date().getTime();
Expand Down

0 comments on commit fef8000

Please sign in to comment.