From f22d16f07511a28645a291a8004ee0cb07044f6e Mon Sep 17 00:00:00 2001 From: Max Wofford Date: Mon, 17 Jun 2024 21:51:21 -0400 Subject: [PATCH 01/14] Log errors inline --- src/clock.ts | 3 +- src/extensions/arcade/watchers/airtable.ts | 4 +-- src/extensions/arcade/watchers/hackhour.ts | 9 +++--- src/extensions/slack/functions/cancel.ts | 8 ++--- src/extensions/slack/functions/extend.ts | 3 +- src/extensions/slack/functions/goals.ts | 14 ++++----- src/extensions/slack/functions/pause.ts | 10 +++---- src/extensions/slack/index.ts | 8 ++--- src/extensions/slack/lib/emoji.ts | 4 +-- src/extensions/slack/lib/lib.ts | 6 ++-- src/lib/bolt.ts | 35 +++++++++++----------- src/lib/handleError.ts | 5 ++++ 12 files changed, 57 insertions(+), 52 deletions(-) create mode 100644 src/lib/handleError.ts diff --git a/src/clock.ts b/src/clock.ts index 8ef50ed0..72964596 100644 --- a/src/clock.ts +++ b/src/clock.ts @@ -1,6 +1,7 @@ import { prisma } from "./lib/prisma.js"; import { emitter } from "./lib/emitter.js"; import { Constants } from "./lib/constants.js"; +import { handleError } from "./lib/handleError.js"; emitter.on('minute', async () => { try { @@ -109,7 +110,7 @@ emitter.on('minute', async () => { } } } catch (error) { - emitter.emit('error', { error }); + handleError(error) } }); diff --git a/src/extensions/arcade/watchers/airtable.ts b/src/extensions/arcade/watchers/airtable.ts index 756631ca..b51046b5 100644 --- a/src/extensions/arcade/watchers/airtable.ts +++ b/src/extensions/arcade/watchers/airtable.ts @@ -2,9 +2,9 @@ import { app, express, Slack } from "../../../lib/bolt.js"; import { AirtableAPI } from "../../../lib/airtable.js"; import { prisma } from "../../../lib/prisma.js"; import { Environment } from "../../../lib/constants.js"; -import { emitter } from "../../../lib/emitter.js"; import { log } from "../lib/log.js"; import { t } from "../../../lib/templates.js"; +import { handleError } from "../../../lib/handleError.js"; express.post('/airtable/session/update', async (req, res) => { try { @@ -148,7 +148,7 @@ express.post('/airtable/session/update', async (req, res) => { res.sendStatus(200); } catch (error) { - emitter.emit('error', {error}); + handleError(error); } }); diff --git a/src/extensions/arcade/watchers/hackhour.ts b/src/extensions/arcade/watchers/hackhour.ts index f59474ad..84fcd52d 100644 --- a/src/extensions/arcade/watchers/hackhour.ts +++ b/src/extensions/arcade/watchers/hackhour.ts @@ -9,6 +9,7 @@ import { log } from "../lib/log.js"; import { pfps, t, t_format, templates } from "../../../lib/templates.js"; import { fetchEvidence, surfaceEvidence } from "../lib/helper.js"; import { Session as LibSession } from "../../../lib/corelib.js"; +import { handleError } from "../../../lib/handleError.js"; const findOrCreateUser = async (userId: string) => { try { @@ -68,8 +69,8 @@ const findOrCreateUser = async (userId: string) => { } return user; - } catch (error) { - emitter.emit('error', {error}); + } catch (e) { + handleError(e); } }; @@ -172,7 +173,7 @@ const registerSession = async (session: Session) => { }); } } catch (error) { - emitter.emit('error', {error}); + handleError(error); } }; @@ -463,6 +464,6 @@ emitter.on('start', async (session: Session) => { }); } } catch (error) { - emitter.emit('error', {error}); + handleError(error); } }); \ No newline at end of file diff --git a/src/extensions/slack/functions/cancel.ts b/src/extensions/slack/functions/cancel.ts index a2166fb4..62d80f8c 100644 --- a/src/extensions/slack/functions/cancel.ts +++ b/src/extensions/slack/functions/cancel.ts @@ -4,7 +4,6 @@ Cancellation import { app } from "../../../lib/bolt.js"; import { Environment, Actions, Commands, Callbacks } from "../../../lib/constants.js"; import { prisma } from "../../../lib/prisma.js"; -import { emitter } from "../../../lib/emitter.js"; import { fetchSlackId, informUser } from "../lib/lib.js"; import { Cancel } from "../views/cancel.js"; @@ -12,6 +11,7 @@ import { Cancel } from "../views/cancel.js"; import { Session } from "../../../lib/corelib.js"; import { Slack } from "../../../lib/bolt.js"; import { pfps, t } from "../../../lib/templates.js"; +import { handleError } from "../../../lib/handleError.js"; Slack.action(Actions.CANCEL, async ({ ack, body }) => { try { @@ -24,7 +24,7 @@ Slack.action(Actions.CANCEL, async ({ ack, body }) => { view: await Cancel.cancel(thread_ts) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error); } }); @@ -72,7 +72,7 @@ Slack.view(Callbacks.CANCEL, async ({ ack, body, view }) => { } })); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -105,6 +105,6 @@ Slack.command(Commands.CANCEL, async ({ ack, body }) => { await Session.cancel(session); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); \ No newline at end of file diff --git a/src/extensions/slack/functions/extend.ts b/src/extensions/slack/functions/extend.ts index 1d908fea..97e62026 100644 --- a/src/extensions/slack/functions/extend.ts +++ b/src/extensions/slack/functions/extend.ts @@ -1,7 +1,6 @@ import { Slack } from "../../../lib/bolt.js"; import { Environment, Actions, Commands } from "../../../lib/constants.js"; import { prisma } from "../../../lib/prisma.js"; -import { emitter } from "../../../lib/emitter.js"; import { updateController, updateTopLevel, informUser } from "../lib/lib.js"; import { Session } from "../../../lib/corelib.js"; @@ -66,6 +65,6 @@ Slack.command(Commands.EXTEND, async ({ ack, body }) => { await updateController(updatedSession); await updateTopLevel(updatedSession);*/ } catch (error) { - emitter.emit('error', {error}); + handleError(error); } }); \ No newline at end of file diff --git a/src/extensions/slack/functions/goals.ts b/src/extensions/slack/functions/goals.ts index b1a55c8b..5b50c91d 100644 --- a/src/extensions/slack/functions/goals.ts +++ b/src/extensions/slack/functions/goals.ts @@ -4,9 +4,9 @@ import { prisma, uid } from "../../../lib/prisma.js"; import { Goals } from "../views/goals.js"; import { Actions, Callbacks } from "../../../lib/constants.js"; import { informUser, updateController, updateTopLevel } from "../lib/lib.js"; -import { emitter } from "../../../lib/emitter.js"; import { t } from "../../../lib/templates.js"; import { Loading } from "../views/loading.js"; +import { handleError } from "../../../lib/handleError.js"; Slack.action(Actions.OPEN_GOAL, async ({ ack, body, client }) => { try { @@ -61,7 +61,7 @@ Slack.action(Actions.OPEN_GOAL, async ({ ack, body, client }) => { view: await Goals.main(session.id) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -138,7 +138,7 @@ Slack.action(Actions.SELECT_GOAL, async ({ ack, body, client }) => { view: await Goals.main(session.id) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -158,7 +158,7 @@ Slack.action(Actions.CREATE_GOAL, async ({ ack, body, client }) => { view: await Goals.create(sessionId) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -259,7 +259,7 @@ Slack.view(Callbacks.CREATE_GOAL, async ({ ack, body, view, client }) => { view: await Goals.main(sessionId) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -300,7 +300,7 @@ Slack.action(Actions.DELETE_GOAL, async ({ ack, body, client }) => { view: await Goals.delete(sessionId) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -396,6 +396,6 @@ Slack.view(Callbacks.DELETE_GOAL, async ({ ack, body, view, client }) => { view: await Goals.main(sessionId) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); \ No newline at end of file diff --git a/src/extensions/slack/functions/pause.ts b/src/extensions/slack/functions/pause.ts index eea9f414..7dbcbfdb 100644 --- a/src/extensions/slack/functions/pause.ts +++ b/src/extensions/slack/functions/pause.ts @@ -4,11 +4,11 @@ Pause Management import { Slack } from "../../../lib/bolt.js"; import { Environment, Actions, Commands } from "../../../lib/constants.js"; import { prisma } from "../../../lib/prisma.js"; -import { emitter } from "../../../lib/emitter.js"; import { Session } from "../../../lib/corelib.js"; import { fetchSlackId, informUser } from "../lib/lib.js"; import { pfps, t } from "../../../lib/templates.js"; +import { handleError } from "../../../lib/handleError.js"; // TODO: Move to a standard library @@ -49,7 +49,7 @@ Slack.action(Actions.PAUSE, async ({ ack, body }) => { await Session.pause(session); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -84,7 +84,7 @@ Slack.action(Actions.RESUME, async ({ ack, body }) => { await Session.pause(session); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -126,7 +126,7 @@ Slack.command(Commands.PAUSE, async ({ ack, body }) => { informUser(slackId, toggleMessage, body.channel_id); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -167,6 +167,6 @@ Slack.command(Commands.START, async ({ ack, body }) => { minutes: updatedSession.time - updatedSession.elapsed }), body.channel_id); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); \ No newline at end of file diff --git a/src/extensions/slack/index.ts b/src/extensions/slack/index.ts index c008e3b5..141ab301 100644 --- a/src/extensions/slack/index.ts +++ b/src/extensions/slack/index.ts @@ -18,6 +18,7 @@ import { assertVal } from "../../lib/assert.js"; import { Hack } from "./views/hack.js"; import { firstTime } from "../arcade/watchers/hackhour.js"; import { AirtableAPI } from "../../lib/airtable.js"; +import { handleError } from "../../lib/handleError.js"; /* Session Creation @@ -205,7 +206,7 @@ const hack = async ({ command }: CommandHandler) => { ts: assertVal(topLevel!.ts) }); } catch (error) { - emitter.emit('error', { error }); + handleError(error) } }; @@ -432,7 +433,6 @@ emitter.on('sessionUpdate', async (update: { await updateTopLevel(session); } } catch (error) { - emitter.emit('error', { error }); console.error(error); } }); @@ -714,7 +714,7 @@ emitter.on('error', async (errorRef) => { ] }); } catch (error) { - emitter.emit('error', { error }); + handleError(error) } }); @@ -729,6 +729,6 @@ emitter.on('debug', async (message) => { text: `${message}`, }); } catch (error) { - emitter.emit('error', { error }); + handleError(error) } }); \ No newline at end of file diff --git a/src/extensions/slack/lib/emoji.ts b/src/extensions/slack/lib/emoji.ts index 160c4e4f..c0e9f388 100644 --- a/src/extensions/slack/lib/emoji.ts +++ b/src/extensions/slack/lib/emoji.ts @@ -1,5 +1,5 @@ import { Slack } from "../../../lib/bolt.js"; -import { emitter } from "../../../lib/emitter.js"; +import { handleError } from "../../../lib/handleError.js"; const emojis = { yay: "yay", @@ -266,7 +266,7 @@ export async function reactOnContent(data: { } } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); } \ No newline at end of file diff --git a/src/extensions/slack/lib/lib.ts b/src/extensions/slack/lib/lib.ts index 72766821..f298138a 100644 --- a/src/extensions/slack/lib/lib.ts +++ b/src/extensions/slack/lib/lib.ts @@ -7,9 +7,9 @@ import { t } from "../../../lib/templates.js"; import { Controller } from "../views/controller.js"; import { TopLevel } from "../views/topLevel.js"; -import { emitter } from "../../../lib/emitter.js"; import { AllMiddlewareArgs, Middleware, SlackCommandMiddlewareArgs } from "@slack/bolt"; import { StringIndexed } from "@slack/bolt/dist/types/helpers.js"; +import { handleError } from "../../../lib/handleError.js"; export type Session = Prisma.SessionGetPayload<{}>; @@ -88,7 +88,7 @@ export async function informUser(slackId: string, message: string, channel: stri if (response.error !== 'channel_not_found') { // Error not caused by access perms - emitter.emit('error', {error}); + handleError(error) } } } @@ -115,7 +115,7 @@ export async function informUserBlocks(slackId: string, blocks: any[], channel: if (response.error !== 'channel_not_found') { // Error not caused by access perms - emitter.emit('error', {error}); + handleError(error) } } diff --git a/src/lib/bolt.ts b/src/lib/bolt.ts index 36dc2d71..da7f2b22 100644 --- a/src/lib/bolt.ts +++ b/src/lib/bolt.ts @@ -5,10 +5,10 @@ import { AllMiddlewareArgs, Middleware, SlackAction, SlackActionMiddlewareArgs, import { StringIndexed } from "@slack/bolt/dist/types/helpers.js"; import { Commands, Environment } from './constants.js'; -import { emitter } from './emitter.js'; import { assertVal } from './assert.js'; import { t } from './templates.js'; import { AirtableAPI } from './airtable.js'; +import { handleError } from './handleError.js'; const expressReceiver = new bolt.ExpressReceiver({ signingSecret: Environment.SLACK_SIGNING_SECRET, @@ -30,11 +30,10 @@ export const app = new bolt.App({ express.use(bodyParser.json()); app.error(async (error) => { - if (!error.original) { - emitter.emit('error', {error}); + if (error?.original) { + handleError(error.original) } else { - emitter.emit('error', {error}); - emitter.emit('error', {error: error.original}); + handleError(error) } }); @@ -117,7 +116,7 @@ export const Slack = { verb = "succeeded" } catch(error) { verb = "failed" - emitter.emit('error', {error}) + handleError(error) await app.client.chat.postEphemeral({ channel: event.channel_id, @@ -188,12 +187,12 @@ export const Slack = { listener(payload) } catch (error) { verb = "failed" - emitter.emit('error', {error}); + handleError(error) } }); } catch(error) { verb = "failed" - emitter.emit('error', {error}); + handleError(error) /* await app.client.chat.postEphemeral({ channel: action.channel.id, user: action.user.id, @@ -255,12 +254,12 @@ export const Slack = { listener(payload) } catch (error) { verb = "failed" - emitter.emit('error', {error}); + handleError(error) } }); } catch (error) { verb = "failed" - emitter.emit('error', {error}); + handleError(error) await app.client.chat.postEphemeral({ channel: body.user.id, @@ -317,7 +316,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }, @@ -367,7 +366,7 @@ export const Slack = { return result; } catch (error: any) { - emitter.emit('error', {error}); + handleError(error) console.log(`[${new Date().toISOString()}] failed to post message`) @@ -430,7 +429,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }, @@ -448,7 +447,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } } }, @@ -468,7 +467,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }, @@ -486,7 +485,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } } }, @@ -506,7 +505,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } } }, @@ -526,7 +525,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } } }, diff --git a/src/lib/handleError.ts b/src/lib/handleError.ts new file mode 100644 index 00000000..78e803cb --- /dev/null +++ b/src/lib/handleError.ts @@ -0,0 +1,5 @@ + +// ~~~custom error logic~~~ +export function handleError(e) { + console.error(e) +} \ No newline at end of file From 4b0ecce8e0574f6fbe46d9e2e275bf904a054afe Mon Sep 17 00:00:00 2001 From: Max Wofford Date: Mon, 17 Jun 2024 21:51:36 -0400 Subject: [PATCH 02/14] Return error on airtable failure --- src/extensions/arcade/watchers/airtable.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extensions/arcade/watchers/airtable.ts b/src/extensions/arcade/watchers/airtable.ts index b51046b5..44d92dd6 100644 --- a/src/extensions/arcade/watchers/airtable.ts +++ b/src/extensions/arcade/watchers/airtable.ts @@ -149,6 +149,7 @@ express.post('/airtable/session/update', async (req, res) => { res.sendStatus(200); } catch (error) { handleError(error); + res.sendStatus(500); } }); From 81740c7114691c7123979029f9fea6c0f85dd14d Mon Sep 17 00:00:00 2001 From: Max Wofford Date: Mon, 17 Jun 2024 23:32:34 -0400 Subject: [PATCH 03/14] SAVEPOINT --- src/extensions/arcade/slack/index.ts | 3 ++- src/extensions/slack/lib/open-modal.ts | 31 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/extensions/slack/lib/open-modal.ts diff --git a/src/extensions/arcade/slack/index.ts b/src/extensions/arcade/slack/index.ts index 5debcbee..9588dd58 100644 --- a/src/extensions/arcade/slack/index.ts +++ b/src/extensions/arcade/slack/index.ts @@ -9,6 +9,7 @@ import { Hack } from "../../slack/views/hack.js"; import { emitter } from "../../../lib/emitter.js"; import { firstTime } from "../watchers/hackhour.js"; import { Loading } from "../../slack/views/loading.js"; +import { openModal } from "../../slack/lib/open-modal.js"; Slack.action(Actions.CHOOSE_SESSIONS, async ({ ack, body }) => { try { @@ -69,7 +70,7 @@ Slack.action(Actions.CHOOSE_SESSIONS, async ({ ack, body }) => { log(`\`\`\`${JSON.stringify(sessions, null, 2)}\`\`\``) - + await openModal({triggerId}) await Slack.views.update({ view_id: view?.view?.id, view: ChooseSessions.chooseSessionsModal(sessions, scrapbook?.internalId), diff --git a/src/extensions/slack/lib/open-modal.ts b/src/extensions/slack/lib/open-modal.ts new file mode 100644 index 00000000..bfdc01d9 --- /dev/null +++ b/src/extensions/slack/lib/open-modal.ts @@ -0,0 +1,31 @@ +import { Slack } from "../../../lib/bolt.js"; +import { handleError } from "../../../lib/handleError.js"; + +export async function openModal({triggerId, asyncMessageView}) { + try { + let viewID + const loadingDelay = Slack.views.open({ + trigger_id: triggerId, + view: loadingMessageView() + }).then(res => viewID = res?.view?.id); + const minDelay = new Promise((resolve) => setTimeout(resolve, 400)); // raccoons take their time to wake up! + + await Promise.all([loadingDelay, minDelay]); + + if (!viewID) { + throw new Error("View ID not found"); + } + + await Slack.views.update({ + view_id: viewID, + view: asyncMessageView + }) + } catch (error) { + handleError(error) + } +} + +async function loadingMessageView() { + // todo: make a loading message + // preferably from flavor text +} \ No newline at end of file From 99bd675b9a996f61ea71551f30ecd0db688dbfe5 Mon Sep 17 00:00:00 2001 From: techpixel <68567672+techpixel@users.noreply.github.com> Date: Tue, 18 Jun 2024 00:13:05 -0400 Subject: [PATCH 04/14] check for mimetypes --- src/extensions/arcade/lib/helper.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/extensions/arcade/lib/helper.ts b/src/extensions/arcade/lib/helper.ts index 14241044..4fb413c8 100644 --- a/src/extensions/arcade/lib/helper.ts +++ b/src/extensions/arcade/lib/helper.ts @@ -25,6 +25,13 @@ export const fetchEvidence = async (messageTs: string, slackId: string) => { return { activity, evidenced }; } +const acceptedImageTypes = [ + "image/gif", + "image/jpeg", + "image/png", + "image/jpg" +] + // Take any media sent in the thread and attach it to the session export const surfaceEvidence = async (messageTs: string, slackId: string) => { const evidence = await Slack.conversations.replies({ @@ -49,7 +56,10 @@ export const surfaceEvidence = async (messageTs: string, slackId: string) => { } console.log(image.files[0]); - session.metadata.slack.attachment = image.files[0]?.permalink; + + if (acceptedImageTypes.includes(image.files[0]?.mimetype ?? "")) { + session.metadata.slack.attachment = image.files[0]?.permalink; + } const updatedSession = await prisma.session.update({ where: { From 6da0a35c22904c0a5fb110a98a53e8e8a26317d9 Mon Sep 17 00:00:00 2001 From: techpixel <68567672+techpixel@users.noreply.github.com> Date: Tue, 18 Jun 2024 01:32:17 -0400 Subject: [PATCH 05/14] add linking to start sesh --- src/extensions/slack/index.ts | 42 +++++++++++++++++++++++++++++++++-- src/lib/templates.yaml | 2 +- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/extensions/slack/index.ts b/src/extensions/slack/index.ts index 141ab301..21cb16b5 100644 --- a/src/extensions/slack/index.ts +++ b/src/extensions/slack/index.ts @@ -52,7 +52,26 @@ const hack = async ({ command }: CommandHandler) => { }); if (aggregation._count > 0) { - await informUser(slackId, t('error.already_hacking', {}), command.channel_id); + const sesh = await prisma.session.findFirst({ + where: { + user: { + slackUser: { + slackId: slackId + } + }, + completed: false, + cancelled: false + } + }); + + const url = await Slack.chat.getPermalink({ + channel: Environment.MAIN_CHANNEL, + message_ts: sesh!.messageTs + }); + + await informUser(slackId, t('error.already_hacking', { + url: url?.permalink + }), command.channel_id); return; } @@ -279,7 +298,26 @@ Slack.action(Actions.HACK, async ({ ack, body, respond }) => { ); if (slackUser.user.sessions.length > 0) { - await informUser(slackId, t('error.already_hacking', {}), channel); + const sesh = await prisma.session.findFirst({ + where: { + user: { + slackUser: { + slackId: slackId + } + }, + completed: false, + cancelled: false + } + }); + + const url = await Slack.chat.getPermalink({ + channel: Environment.MAIN_CHANNEL, + message_ts: sesh!.messageTs + }); + + await informUser(slackId, t('error.already_hacking', { + url: url?.permalink + }), channel); return; } diff --git a/src/lib/templates.yaml b/src/lib/templates.yaml index c4cea7c0..e05e4047 100644 --- a/src/lib/templates.yaml +++ b/src/lib/templates.yaml @@ -73,7 +73,7 @@ evidence_reminder: error: #context: attempting to start a new session while already in one already_hacking: - - hey hey!! you're already in a arcade session! you can't start another one until you finish the one you're in! + - hey hey!! you're already in a <${url}|arcade session>! you can't start another one until you finish the one you're in! not_hacking: - hmph, whatcha doin? want some garbage? run `/hack`! From ade9f9923e1fe7bd11bd2a88ff47571f8105f16c Mon Sep 17 00:00:00 2001 From: Max Wofford Date: Tue, 18 Jun 2024 10:27:23 -0400 Subject: [PATCH 06/14] Update README.md with uptime badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2329752e..b2572b1a 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ # Hack Hour Mk. 3 +![](https://api.checklyhq.com/v1/badges/checks/271a416b-7810-47b0-b58c-7cd9e9e37f82?style=flat&theme=default&responseTime=false) From a7662927b65902405de15c83eda463af8b1068f5 Mon Sep 17 00:00:00 2001 From: Max Wofford Date: Tue, 18 Jun 2024 10:50:09 -0400 Subject: [PATCH 07/14] Catch error inline --- src/lib/airtable.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/airtable.ts b/src/lib/airtable.ts index 4aaec0a6..b0d69c7a 100644 --- a/src/lib/airtable.ts +++ b/src/lib/airtable.ts @@ -244,7 +244,7 @@ export const AirtableAPI = { const records = await sessions.update([{ "id": id, "fields": session - }]); + }]).catch(error => { console.error(error); return []}); console.log(`[AirtableAPI.Session.update] Took ${Date.now() - now}ms`); From 7662295093254c8a80fe21a88f74c8e6e2004ce0 Mon Sep 17 00:00:00 2001 From: techpixel <68567672+techpixel@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:08:40 -0400 Subject: [PATCH 08/14] add rejected locked --- src/extensions/arcade/slack/index.ts | 20 ++++++++++---------- src/extensions/arcade/watchers/airtable.ts | 7 +++++-- src/lib/airtable.ts | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/extensions/arcade/slack/index.ts b/src/extensions/arcade/slack/index.ts index 9588dd58..893b0882 100644 --- a/src/extensions/arcade/slack/index.ts +++ b/src/extensions/arcade/slack/index.ts @@ -115,19 +115,19 @@ Slack.view(Callbacks.CHOOSE_SESSIONS, async ({ ack, body, view }) => { }); for (const session of selectedSessions) { - if (session.metadata?.airtable?.status === "Approved") { - session.metadata.airtable.status = "Banked"; - - await AirtableAPI.Session.update(session.metadata?.airtable?.id, { - "Scrapbook": [scrapbook.data.record], - "Status": "Banked", - }); - } else { + // if (session.metadata?.airtable?.status === "Approved") { + // session.metadata.airtable.status = "Banked"; + + // await AirtableAPI.Session.update(session.metadata?.airtable?.id, { + // "Scrapbook": [scrapbook.data.record], + // "Status": "Banked", + // }); + // } else { await AirtableAPI.Session.update(session.metadata?.airtable?.id!, { "Scrapbook": [scrapbook.data.record], }); - } - + // } + session.metadata.banked = true; await prisma.session.update({ diff --git a/src/extensions/arcade/watchers/airtable.ts b/src/extensions/arcade/watchers/airtable.ts index 44d92dd6..0a58c333 100644 --- a/src/extensions/arcade/watchers/airtable.ts +++ b/src/extensions/arcade/watchers/airtable.ts @@ -73,7 +73,7 @@ express.post('/airtable/session/update', async (req, res) => { "Status": "Banked" }); - session.metadata.airtable!.status = "Banked"; + // session.metadata.airtable!.status = "Banked"; await prisma.session.update({ where: { @@ -136,7 +136,10 @@ express.post('/airtable/session/update', async (req, res) => { slackId: slackUser.slackId }) }); - } else if (session.metadata.airtable!.status === "Rejected") { + } else if ( + session.metadata.airtable!.status === "Rejected" || + session.metadata.airtable!.status === "Rejected Locked" + ) { await Slack.chat.postMessage({ channel: Environment.MAIN_CHANNEL, thread_ts: session.messageTs, diff --git a/src/lib/airtable.ts b/src/lib/airtable.ts index b0d69c7a..f2960a0b 100644 --- a/src/lib/airtable.ts +++ b/src/lib/airtable.ts @@ -68,7 +68,7 @@ type AirtableSessionWrite = { "User": [AirtableRecordID], "Work": string, "Minutes": number, - "Status": "Approved" | "Unreviewed" | "Rejected" | "Banked" | "Requested Re-review", + "Status": "Approved" | "Unreviewed" | "Rejected" | "Banked" | "Requested Re-review" | "Rejected Locked", "Created At": string, "Evidenced": boolean, "Activity": boolean, From 9b670d2397ecc69fa19b67727e7dced635fdd94a Mon Sep 17 00:00:00 2001 From: techpixel <68567672+techpixel@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:22:38 -0400 Subject: [PATCH 09/14] remove unneccessary aggregate --- src/extensions/slack/index.ts | 63 +++++++++++++---------------------- 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/src/extensions/slack/index.ts b/src/extensions/slack/index.ts index 21cb16b5..c2a4bb3d 100644 --- a/src/extensions/slack/index.ts +++ b/src/extensions/slack/index.ts @@ -37,45 +37,7 @@ const log = async (message: string) => { const hack = async ({ command }: CommandHandler) => { try { const slackId = command.user_id; - - const aggregation = await prisma.session.aggregate({ - where: { - user: { - slackUser: { - slackId: slackId - } - }, - completed: false, - cancelled: false - }, - _count: true - }); - if (aggregation._count > 0) { - const sesh = await prisma.session.findFirst({ - where: { - user: { - slackUser: { - slackId: slackId - } - }, - completed: false, - cancelled: false - } - }); - - const url = await Slack.chat.getPermalink({ - channel: Environment.MAIN_CHANNEL, - message_ts: sesh!.messageTs - }); - - await informUser(slackId, t('error.already_hacking', { - url: url?.permalink - }), command.channel_id); - - return; - } - let slackUser = await prisma.slackUser.upsert( { where: { @@ -108,11 +70,34 @@ const hack = async ({ command }: CommandHandler) => { }, update: {}, include: { - user: true + user: { + include: { + sessions: { + where: { + completed: false, + cancelled: false + } + } + } + } } } ); + if (slackUser.user.sessions.length > 0) { + const url = await Slack.chat.getPermalink({ + channel: Environment.MAIN_CHANNEL, + message_ts: slackUser.user.sessions[0].messageTs + }); + + await informUser(slackId, t('error.already_hacking', { + url: url?.permalink + }), command.channel_id); + + return; + } + + if (slackUser.user.metadata.firstTime && Environment.ARCADE) { // TODO: remove arcade dependency & check if there are entities/subrountines listening to first time users if (await firstTime(slackUser.user)) { // firstTime returns true if the user is existing, meaning I can redirect them through the arcadius flow From 09084593cfee5dcfa4f7a6ec07819b7c4c2f7136 Mon Sep 17 00:00:00 2001 From: techpixel <68567672+techpixel@users.noreply.github.com> Date: Tue, 18 Jun 2024 14:29:43 -0400 Subject: [PATCH 10/14] add response timing to bolt --- src/extensions/slack/lib/open-modal.ts | 8 ++----- src/lib/bolt.ts | 32 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/extensions/slack/lib/open-modal.ts b/src/extensions/slack/lib/open-modal.ts index bfdc01d9..2653991e 100644 --- a/src/extensions/slack/lib/open-modal.ts +++ b/src/extensions/slack/lib/open-modal.ts @@ -1,12 +1,13 @@ import { Slack } from "../../../lib/bolt.js"; import { handleError } from "../../../lib/handleError.js"; +import { Loading } from "../views/loading.js"; export async function openModal({triggerId, asyncMessageView}) { try { let viewID const loadingDelay = Slack.views.open({ trigger_id: triggerId, - view: loadingMessageView() + view: Loading.loading() }).then(res => viewID = res?.view?.id); const minDelay = new Promise((resolve) => setTimeout(resolve, 400)); // raccoons take their time to wake up! @@ -23,9 +24,4 @@ export async function openModal({triggerId, asyncMessageView}) { } catch (error) { handleError(error) } -} - -async function loadingMessageView() { - // todo: make a loading message - // preferably from flavor text } \ No newline at end of file diff --git a/src/lib/bolt.ts b/src/lib/bolt.ts index da7f2b22..49f87591 100644 --- a/src/lib/bolt.ts +++ b/src/lib/bolt.ts @@ -1,4 +1,5 @@ import bolt, { SlackViewAction, SlackViewMiddlewareArgs } from '@slack/bolt'; + import bodyParser from 'body-parser'; import { AllMiddlewareArgs, Middleware, SlackAction, SlackActionMiddlewareArgs, SlackCommandMiddlewareArgs } from "@slack/bolt"; @@ -14,6 +15,12 @@ const expressReceiver = new bolt.ExpressReceiver({ signingSecret: Environment.SLACK_SIGNING_SECRET, endpoints: '/slack/events', processBeforeResponse: true, + + unhandledRequestHandler(args) { + const diff = Date.now() - (args.request as any).context.start; + + console.log(`[WARN] [${new Date().toISOString()}] Took ${diff}ms to respond\nUnhandled request: ${JSON.stringify(args.request)}`) + } }); export const express = expressReceiver.app; @@ -27,6 +34,31 @@ export const app = new bolt.App({ receiver: expressReceiver, }); +declare global { + namespace Express { + interface Request { + context: { start: number } + } + } +} + + // Time response +express.use((req, res, next) => { + const start = Date.now(); + + // Add context to the response + req.context = { + start, + } + + res.on('finish', () => { + const duration = Date.now() - start; + console.log(`[${new Date().toISOString()}] Request to ${req.path} took ${duration}ms`); + }); + + next(); +}); + express.use(bodyParser.json()); app.error(async (error) => { From 38c59151147d2662dbbb23229c7e815dc20f9495 Mon Sep 17 00:00:00 2001 From: techpixel <68567672+techpixel@users.noreply.github.com> Date: Tue, 18 Jun 2024 14:49:20 -0400 Subject: [PATCH 11/14] create shop view --- src/extensions/arcade/slack/index.ts | 20 +++--- src/extensions/arcade/slack/shop.ts | 81 ++++-------------------- src/extensions/arcade/slack/view.ts | 67 +++++++++++++++++++- src/extensions/slack/lib/open-modal.ts | 11 ++-- src/extensions/slack/views/controller.ts | 4 +- 5 files changed, 101 insertions(+), 82 deletions(-) diff --git a/src/extensions/arcade/slack/index.ts b/src/extensions/arcade/slack/index.ts index 893b0882..882d55e0 100644 --- a/src/extensions/arcade/slack/index.ts +++ b/src/extensions/arcade/slack/index.ts @@ -17,10 +17,10 @@ Slack.action(Actions.CHOOSE_SESSIONS, async ({ ack, body }) => { if (body.type !== "block_actions") return; - const view = await Slack.views.open({ - trigger_id: body.trigger_id, - view: Loading.loading() - }); + // const view = await Slack.views.open({ + // trigger_id: body.trigger_id, + // view: Loading.loading() + // }); const flowTs = body.message!.ts; @@ -70,11 +70,15 @@ Slack.action(Actions.CHOOSE_SESSIONS, async ({ ack, body }) => { log(`\`\`\`${JSON.stringify(sessions, null, 2)}\`\`\``) - await openModal({triggerId}) - await Slack.views.update({ - view_id: view?.view?.id, + // await Slack.views.update({ + // view_id: view?.view?.id, + // view: ChooseSessions.chooseSessionsModal(sessions, scrapbook?.internalId), + // }).catch((err) => console.log(err)); + + await openModal({ + triggerId: body.trigger_id, view: ChooseSessions.chooseSessionsModal(sessions, scrapbook?.internalId), - }).catch((err) => console.log(err)); + }); } catch (error) { console.log(error); emitter.emit("error", error); diff --git a/src/extensions/arcade/slack/shop.ts b/src/extensions/arcade/slack/shop.ts index fafbf65d..17946ff0 100644 --- a/src/extensions/arcade/slack/shop.ts +++ b/src/extensions/arcade/slack/shop.ts @@ -4,88 +4,35 @@ import { t } from "../../../lib/templates.js"; import { informUser } from "../../slack/lib/lib.js"; import { Loading } from "../../slack/views/loading.js"; import { AirtableAPI } from "../../../lib/airtable.js"; +import { openModal } from "../../slack/lib/open-modal.js"; +import { Shop } from "./view.js"; -app.command(Commands.SHOP, async ({ command, ack }) => { - await ack(); - - const view = await Slack.views.open({ - trigger_id: command.trigger_id, - view: Loading.loading() - }); +Slack.command(Commands.SHOP, async ({ command, ack }) => { + // const view = await Slack.views.open({ + // trigger_id: command.trigger_id, + // view: Loading.loading() + // }); const airtableUser = await AirtableAPI.User.lookupBySlack(command.user_id); if (!airtableUser) { // await ack(); // informUser(command.user_id, t('error.first_time', {}), command.channel_id); - await Slack.views.update({ - view_id: view?.view?.id, + await openModal({ + triggerId: command.trigger_id, view: Loading.error(t('error.first_time', {})) }); return; } - const blocks = []; - const remaining = Math.floor(airtableUser.fields["Balance (Minutes)"] / 60); const pending = Math.floor(airtableUser.fields["Minutes (Pending Approval)"] / 60); - blocks.push({ - "type": "section", - "text": { - "type": "mrkdwn", - "text": `Available to spend: ${remaining} :tw_admission_tickets: _(Pending Approval: ${pending})_` - } - }); - - if (Math.floor(airtableUser.fields["Spent (Incl. Pending)"] / 60) !== 0) { - blocks.push({ - "type": "section", - "text": { - "type": "mrkdwn", - "text": `Total banked hours: ${Math.floor(airtableUser.fields["Minutes (Banked)"] / 60)} :tw_admission_tickets: ` - } - }); - } + // await Slack.views.update() - blocks.push({ - "type": "divider" - }, - { - "type": "actions", - "elements": [ - { - "type": "button", - "text": { - "type": "plain_text", - "text": "Open the Shop", - "emoji": true - }, - 'url': `${Environment.SHOP_URL}/arcade/${airtableUser.id}/shop/`, - // 'url': `https://forms.hackclub.com/eligibility?slack_id=${command.user_id}`, - // "url": `${Environment.SHOP_URL}/arcade/${airtableUser.id}/shop/`, - // "action_id": Actions.OPEN_SHOP - } - ], - "block_id": "actions", - }); - - await Slack.views.update({ - view_id: view?.view?.id, - "view": { - "type": "modal", - "title": { - "type": "plain_text", - "text": "The Shop", - "emoji": true - }, - "close": { - "type": "plain_text", - "text": "Close", - "emoji": true - }, - "blocks": blocks - } - }) + await openModal({ + triggerId: command.trigger_id, + view: Shop.shop(remaining, pending) + }); }); \ No newline at end of file diff --git a/src/extensions/arcade/slack/view.ts b/src/extensions/arcade/slack/view.ts index 96227597..13d3ca06 100644 --- a/src/extensions/arcade/slack/view.ts +++ b/src/extensions/arcade/slack/view.ts @@ -146,4 +146,69 @@ export class ChooseSessions { } } -export class Walkthrough {} \ No newline at end of file +export class Walkthrough {} + +export class Shop { + public static shop(remaining: number, pending: number, airtableUser: any) { + const blocks = []; + + blocks.push({ + "type": "section", + "text": { + "type": "mrkdwn", + "text": `Available to spend: ${remaining} :tw_admission_tickets: _(Pending Approval: ${pending})_` + } + }); + + if (Math.floor(airtableUser.fields["Spent (Incl. Pending)"] / 60) !== 0) { + blocks.push({ + "type": "section", + "text": { + "type": "mrkdwn", + "text": `Total banked hours: ${Math.floor(airtableUser.fields["Minutes (Banked)"] / 60)} :tw_admission_tickets: ` + } + }); + } + + blocks.push({ + "type": "divider" + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Open the Shop", + "emoji": true + }, + 'url': `${Environment.SHOP_URL}/arcade/${airtableUser.id}/shop/`, + // 'url': `https://forms.hackclub.com/eligibility?slack_id=${command.user_id}`, + // "url": `${Environment.SHOP_URL}/arcade/${airtableUser.id}/shop/`, + // "action_id": Actions.OPEN_SHOP + } + ], + "block_id": "actions", + }); + + + { + view_id: view?.view?.id, + "view": { + "type": "modal", + "title": { + "type": "plain_text", + "text": "The Shop", + "emoji": true + }, + "close": { + "type": "plain_text", + "text": "Close", + "emoji": true + }, + "blocks": blocks + } + } + } +} \ No newline at end of file diff --git a/src/extensions/slack/lib/open-modal.ts b/src/extensions/slack/lib/open-modal.ts index 2653991e..489f1974 100644 --- a/src/extensions/slack/lib/open-modal.ts +++ b/src/extensions/slack/lib/open-modal.ts @@ -1,15 +1,18 @@ +import { View } from "@slack/bolt"; import { Slack } from "../../../lib/bolt.js"; import { handleError } from "../../../lib/handleError.js"; import { Loading } from "../views/loading.js"; -export async function openModal({triggerId, asyncMessageView}) { +export async function openModal({triggerId, view}: {triggerId: string, view: View}) { try { - let viewID + let viewID; + const loadingDelay = Slack.views.open({ trigger_id: triggerId, view: Loading.loading() }).then(res => viewID = res?.view?.id); - const minDelay = new Promise((resolve) => setTimeout(resolve, 400)); // raccoons take their time to wake up! + + const minDelay = new Promise((resolve) => setTimeout(resolve, 3 * 1000)); // raccoons take their time to wake up! await Promise.all([loadingDelay, minDelay]); @@ -19,7 +22,7 @@ export async function openModal({triggerId, asyncMessageView}) { await Slack.views.update({ view_id: viewID, - view: asyncMessageView + view }) } catch (error) { handleError(error) diff --git a/src/extensions/slack/views/controller.ts b/src/extensions/slack/views/controller.ts index c498d0e5..c9deb187 100644 --- a/src/extensions/slack/views/controller.ts +++ b/src/extensions/slack/views/controller.ts @@ -46,7 +46,7 @@ export class Controller { if (session.metadata.firstTime) { info.text.text = t('firstTime.controller', {}) } else if (session.paused) { - info.text.text = `You have paused your session. You have \`${session.time - session.elapsed}\` minutes remaining. \`${Constants.AUTO_CANCEL - session.elapsedSincePause}\` minutes untill the session is ended early.` + info.text.text = `You have paused your session. You have \`${session.time - session.elapsed}\` minutes remaining. \`${Constants.AUTO_CANCEL - session.elapsedSincePause}\` minutes until the session is ended early.` } else if (session.cancelled) { info.text.text = `You have ended your session early.` } else if (session.completed) { @@ -236,7 +236,7 @@ export class Controller { }; if (session.paused) { - info.text.text = `You have paused your session. You have \`${session.time - session.elapsed}\` minutes remaining. \`${Constants.AUTO_CANCEL - session.elapsedSincePause}\` minutes untill the session is ended early.` + info.text.text = `You have paused your session. You have \`${session.time - session.elapsed}\` minutes remaining. \`${Constants.AUTO_CANCEL - session.elapsedSincePause}\` minutes until the session is ended early.` } else if (session.cancelled) { info.text.text = `You have ended your session early.` } else if (session.completed) { From e73800616c48ed7de88e5adde7654495e6e4e53c Mon Sep 17 00:00:00 2001 From: techpixel <68567672+techpixel@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:06:56 -0400 Subject: [PATCH 12/14] ack all views immediately --- src/extensions/arcade/lib/helper.ts | 2 - src/extensions/arcade/slack/index.ts | 6 +-- src/extensions/arcade/slack/shop.ts | 2 +- src/extensions/arcade/slack/view.ts | 49 ++++++++++------------ src/extensions/arcade/watchers/hackhour.ts | 2 +- src/extensions/slack/functions/cancel.ts | 2 - src/extensions/slack/functions/goals.ts | 6 +-- src/extensions/slack/functions/stats.ts | 4 +- src/lib/bolt.ts | 8 ++-- 9 files changed, 33 insertions(+), 48 deletions(-) diff --git a/src/extensions/arcade/lib/helper.ts b/src/extensions/arcade/lib/helper.ts index 4fb413c8..a38ed360 100644 --- a/src/extensions/arcade/lib/helper.ts +++ b/src/extensions/arcade/lib/helper.ts @@ -55,8 +55,6 @@ export const surfaceEvidence = async (messageTs: string, slackId: string) => { return; } - console.log(image.files[0]); - if (acceptedImageTypes.includes(image.files[0]?.mimetype ?? "")) { session.metadata.slack.attachment = image.files[0]?.permalink; } diff --git a/src/extensions/arcade/slack/index.ts b/src/extensions/arcade/slack/index.ts index 882d55e0..6e500ba9 100644 --- a/src/extensions/arcade/slack/index.ts +++ b/src/extensions/arcade/slack/index.ts @@ -68,13 +68,13 @@ Slack.action(Actions.CHOOSE_SESSIONS, async ({ ack, body }) => { }, }); - log(`\`\`\`${JSON.stringify(sessions, null, 2)}\`\`\``) + // log(`\`\`\`${JSON.stringify(sessions, null, 2)}\`\`\``) // await Slack.views.update({ // view_id: view?.view?.id, // view: ChooseSessions.chooseSessionsModal(sessions, scrapbook?.internalId), // }).catch((err) => console.log(err)); - + await openModal({ triggerId: body.trigger_id, view: ChooseSessions.chooseSessionsModal(sessions, scrapbook?.internalId), @@ -86,8 +86,6 @@ Slack.action(Actions.CHOOSE_SESSIONS, async ({ ack, body }) => { }); Slack.view(Callbacks.CHOOSE_SESSIONS, async ({ ack, body, view }) => { - await ack(); - const scrapbook = await prisma.scrapbook.findUniqueOrThrow({ where: { internalId: view.private_metadata, diff --git a/src/extensions/arcade/slack/shop.ts b/src/extensions/arcade/slack/shop.ts index 17946ff0..e9666d5a 100644 --- a/src/extensions/arcade/slack/shop.ts +++ b/src/extensions/arcade/slack/shop.ts @@ -33,6 +33,6 @@ Slack.command(Commands.SHOP, async ({ command, ack }) => { await openModal({ triggerId: command.trigger_id, - view: Shop.shop(remaining, pending) + view: Shop.shop(remaining, pending, airtableUser) }); }); \ No newline at end of file diff --git a/src/extensions/arcade/slack/view.ts b/src/extensions/arcade/slack/view.ts index 13d3ca06..252c8c8d 100644 --- a/src/extensions/arcade/slack/view.ts +++ b/src/extensions/arcade/slack/view.ts @@ -1,5 +1,5 @@ import { KnownBlock, View } from "@slack/bolt"; -import { Actions, Callbacks } from "../../../lib/constants.js"; +import { Actions, Callbacks, Environment } from "../../../lib/constants.js"; import type { Session } from "@prisma/client"; import { t } from "../../../lib/templates.js"; @@ -110,7 +110,7 @@ export class ChooseSessions { { text: { type: "plain_text", - text: `${session.metadata.work.substring(0,30)} - ${session.createdAt.getMonth()}/${session.createdAt.getDate()}`, + text: `${session.metadata.work.substring(0, 30)} - ${session.createdAt.getMonth()}/${session.createdAt.getDate()}`, emoji: true, }, value: session.id, @@ -146,12 +146,12 @@ export class ChooseSessions { } } -export class Walkthrough {} +export class Walkthrough { } export class Shop { - public static shop(remaining: number, pending: number, airtableUser: any) { + public static shop(remaining: number, pending: number, airtableUser: any): View { const blocks = []; - + blocks.push({ "type": "section", "text": { @@ -159,7 +159,7 @@ export class Shop { "text": `Available to spend: ${remaining} :tw_admission_tickets: _(Pending Approval: ${pending})_` } }); - + if (Math.floor(airtableUser.fields["Spent (Incl. Pending)"] / 60) !== 0) { blocks.push({ "type": "section", @@ -169,10 +169,10 @@ export class Shop { } }); } - + blocks.push({ - "type": "divider" - }, + "type": "divider" + }, { "type": "actions", "elements": [ @@ -191,24 +191,21 @@ export class Shop { ], "block_id": "actions", }); - - { - view_id: view?.view?.id, - "view": { - "type": "modal", - "title": { - "type": "plain_text", - "text": "The Shop", - "emoji": true - }, - "close": { - "type": "plain_text", - "text": "Close", - "emoji": true - }, - "blocks": blocks - } + + return { + "type": "modal", + "title": { + "type": "plain_text", + "text": "The Shop", + "emoji": true + }, + "close": { + "type": "plain_text", + "text": "Close", + "emoji": true + }, + "blocks": blocks } } } \ No newline at end of file diff --git a/src/extensions/arcade/watchers/hackhour.ts b/src/extensions/arcade/watchers/hackhour.ts index 84fcd52d..06f87273 100644 --- a/src/extensions/arcade/watchers/hackhour.ts +++ b/src/extensions/arcade/watchers/hackhour.ts @@ -301,7 +301,7 @@ app.event("message", async ({ event }) => { }); } - if ((airtableSession.fields["Status"] === "Rejected" || airtableSession.fields["Status"] === "Requested Re-review") && activity) { + if ((airtableSession.fields["Status"] === "Rejected" || airtableSession.fields["Status"] === "Requested Re-review") && evidenced) { await AirtableAPI.Session.update(session.metadata.airtable.id, { "Status": "Requested Re-review", "Activity": activity, diff --git a/src/extensions/slack/functions/cancel.ts b/src/extensions/slack/functions/cancel.ts index 62d80f8c..dbdc497a 100644 --- a/src/extensions/slack/functions/cancel.ts +++ b/src/extensions/slack/functions/cancel.ts @@ -30,8 +30,6 @@ Slack.action(Actions.CANCEL, async ({ ack, body }) => { Slack.view(Callbacks.CANCEL, async ({ ack, body, view }) => { try { - await ack(); - const slackId = body.user.id; const messageTs = view.private_metadata; diff --git a/src/extensions/slack/functions/goals.ts b/src/extensions/slack/functions/goals.ts index 5b50c91d..63ae6a7e 100644 --- a/src/extensions/slack/functions/goals.ts +++ b/src/extensions/slack/functions/goals.ts @@ -143,7 +143,7 @@ Slack.action(Actions.SELECT_GOAL, async ({ ack, body, client }) => { }); Slack.view(Callbacks.MAIN_GOAL, async ({ ack }) => { - await ack(); + // await ack(); }); Slack.action(Actions.CREATE_GOAL, async ({ ack, body, client }) => { @@ -164,8 +164,6 @@ Slack.action(Actions.CREATE_GOAL, async ({ ack, body, client }) => { Slack.view(Callbacks.CREATE_GOAL, async ({ ack, body, view, client }) => { try { - await ack(); - const slackId = body.user.id; const sessionId = body.view.private_metadata; @@ -306,8 +304,6 @@ Slack.action(Actions.DELETE_GOAL, async ({ ack, body, client }) => { Slack.view(Callbacks.DELETE_GOAL, async ({ ack, body, view, client }) => { try { - await ack(); - const sessionId = body.view.private_metadata; // Mark the goal as complete diff --git a/src/extensions/slack/functions/stats.ts b/src/extensions/slack/functions/stats.ts index 088f4a69..3ff32ed4 100644 --- a/src/extensions/slack/functions/stats.ts +++ b/src/extensions/slack/functions/stats.ts @@ -92,6 +92,4 @@ Slack.command(Commands.STATS, async ({ ack, body, client }) => { view_id: view?.view?.id, view: await Stats.stats(user.id), }); -}); - -Slack.view(Actions.VIEW_STATS, async ({ ack, body }) => {}); \ No newline at end of file +}); \ No newline at end of file diff --git a/src/lib/bolt.ts b/src/lib/bolt.ts index 49f87591..ab568c98 100644 --- a/src/lib/bolt.ts +++ b/src/lib/bolt.ts @@ -17,8 +17,6 @@ const expressReceiver = new bolt.ExpressReceiver({ processBeforeResponse: true, unhandledRequestHandler(args) { - const diff = Date.now() - (args.request as any).context.start; - console.log(`[WARN] [${new Date().toISOString()}] Took ${diff}ms to respond\nUnhandled request: ${JSON.stringify(args.request)}`) } }); @@ -53,7 +51,7 @@ express.use((req, res, next) => { res.on('finish', () => { const duration = Date.now() - start; - console.log(`[${new Date().toISOString()}] Request to ${req.path} took ${duration}ms`); + console.log(`[${new Date().toISOString()}] [${req.path}] Request to ${req.path} took ${duration}ms`); }); next(); @@ -99,7 +97,7 @@ export const Slack = { const { command: event, ack, respond } = payload; console.log(`[${now.toISOString()}] <@${event.user_id}> ran \`${command} ${event.text}\``) - + await ack(); if (Environment.MAINTAINANCE_MODE) { @@ -246,6 +244,8 @@ export const Slack = { console.log(`[${now.toISOString()}] <@${body.user.id}> ${body.type === "view_submission" ? "submitted" : "closed"} view "${callbackId}"`) + await payload.ack(); + try { await app.client.chat.postMessage({ channel: Environment.INTERNAL_CHANNEL, From 6450e28bc977843317b8d6cbd2d4538886b7b30e Mon Sep 17 00:00:00 2001 From: techpixel <68567672+techpixel@users.noreply.github.com> Date: Tue, 18 Jun 2024 20:07:24 -0400 Subject: [PATCH 13/14] manitej's savepoint --- package-lock.json | 52 ++++++++++++++++++++++++---- src/extensions/arcade/slack/index.ts | 2 +- src/lib/bolt.ts | 44 +++++++++-------------- 3 files changed, 64 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 37f0ac59..143f90ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "get-urls": "^12.1.0", "lowdb": "^7.0.1", "prisma-json-types-generator": "^3.0.4", + "response-time": "^2.3.2", "tsx": "^4.10.5", "ws": "^8.17.0", "yaml": "^2.4.2" @@ -29,6 +30,7 @@ "@types/express": "^4.17.21", "@types/lowdb": "^1.0.15", "@types/node": "^20.12.7", + "@types/response-time": "^2.3.8", "@types/ws": "^8.5.10", "prisma": "^5.12.1", "ts-node": "^10.9.2", @@ -619,9 +621,9 @@ } }, "node_modules/@slack/socket-mode/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "engines": { "node": ">=8.3.0" }, @@ -833,6 +835,16 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, + "node_modules/@types/response-time": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@types/response-time/-/response-time-2.3.8.tgz", + "integrity": "sha512-7qGaNYvdxc0zRab8oHpYx7AW17qj+G0xuag1eCrw3M2VWPJQ/HyKaaghWygiaOUl0y9x7QGQwppDpqLJ5V9pzw==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/node": "*" + } + }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -2517,6 +2529,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", @@ -2735,6 +2755,26 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/response-time": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz", + "integrity": "sha512-MUIDaDQf+CVqflfTdQ5yam+aYCkXj1PY8fjlPDQ6ppxJlmgZb864pHtA750mayywNg8tx4rS7qH9JXd/OF+3gw==", + "dependencies": { + "depd": "~1.1.0", + "on-headers": "~1.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/response-time/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -3357,9 +3397,9 @@ } }, "node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, diff --git a/src/extensions/arcade/slack/index.ts b/src/extensions/arcade/slack/index.ts index 6e500ba9..dc28050b 100644 --- a/src/extensions/arcade/slack/index.ts +++ b/src/extensions/arcade/slack/index.ts @@ -283,7 +283,7 @@ Slack.command(Commands.ADMIN, async ({ command }) => { await Slack.chat.postEphemeral({ user: command.user_id, channel: command.channel_id, - text: "You are not authorized to use this command", + text: "here's a cookie :cookie:" }); return; } diff --git a/src/lib/bolt.ts b/src/lib/bolt.ts index ab568c98..ee4a9313 100644 --- a/src/lib/bolt.ts +++ b/src/lib/bolt.ts @@ -10,6 +10,7 @@ import { assertVal } from './assert.js'; import { t } from './templates.js'; import { AirtableAPI } from './airtable.js'; import { handleError } from './handleError.js'; +import util from 'util'; const expressReceiver = new bolt.ExpressReceiver({ signingSecret: Environment.SLACK_SIGNING_SECRET, @@ -17,11 +18,19 @@ const expressReceiver = new bolt.ExpressReceiver({ processBeforeResponse: true, unhandledRequestHandler(args) { - console.log(`[WARN] [${new Date().toISOString()}] Took ${diff}ms to respond\nUnhandled request: ${JSON.stringify(args.request)}`) + console.log(JSON.stringify(Object.keys(args.request))); + console.log(`[WARN] [${new Date().toISOString()}] Unhandled request detected - did not acknowledge to slack, acking manually`) + + console.log(`Context for failed request: ${util.inspect(args.request)}`) + + args.response.writeHead(200); // (:< + args.response.end(); } }); export const express = expressReceiver.app; + +express.use(bodyParser.json()); export const app = new bolt.App({ token: Environment.SLACK_BOT_TOKEN, @@ -32,33 +41,14 @@ export const app = new bolt.App({ receiver: expressReceiver, }); -declare global { - namespace Express { - interface Request { - context: { start: number } - } - } -} - - // Time response -express.use((req, res, next) => { - const start = Date.now(); +// Time response +app.use(async ({ next, payload }) => { + const now = new Date(); - // Add context to the response - req.context = { - start, - } - - res.on('finish', () => { - const duration = Date.now() - start; - console.log(`[${new Date().toISOString()}] [${req.path}] Request to ${req.path} took ${duration}ms`); - }); - - next(); + await next(); + console.log(`[${now.toISOString()}] Took ${new Date().getTime() - now.getTime()}ms to respond to ${payload.type} event`) }); -express.use(bodyParser.json()); - app.error(async (error) => { if (error?.original) { handleError(error.original) @@ -97,8 +87,8 @@ export const Slack = { const { command: event, ack, respond } = payload; console.log(`[${now.toISOString()}] <@${event.user_id}> ran \`${command} ${event.text}\``) - - await ack(); + + // await ack(); if (Environment.MAINTAINANCE_MODE) { const user = await app.client.users.info({ From cfab932668cb27696bcebe144a5b610c24701a07 Mon Sep 17 00:00:00 2001 From: techpixel <68567672+techpixel@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:05:10 -0400 Subject: [PATCH 14/14] sync dev --- src/extensions/arcade/slack/index.ts | 138 ++++++++++----------- src/extensions/arcade/slack/shop.ts | 59 +++++---- src/extensions/arcade/slack/view.ts | 8 +- src/extensions/arcade/slack/walkthrough.ts | 105 ++++++++-------- src/extensions/slack/functions/cancel.ts | 13 +- src/extensions/slack/functions/extend.ts | 11 +- src/extensions/slack/functions/goals.ts | 7 -- src/extensions/slack/functions/pause.ts | 2 - src/extensions/slack/functions/stats.ts | 15 ++- src/extensions/slack/index.ts | 1 - src/extensions/slack/lib/open-modal.ts | 4 +- src/lib/bolt.ts | 104 ++++++++-------- src/lib/templates.yaml | 3 + 13 files changed, 236 insertions(+), 234 deletions(-) diff --git a/src/extensions/arcade/slack/index.ts b/src/extensions/arcade/slack/index.ts index dc28050b..ec384595 100644 --- a/src/extensions/arcade/slack/index.ts +++ b/src/extensions/arcade/slack/index.ts @@ -13,76 +13,74 @@ import { openModal } from "../../slack/lib/open-modal.js"; Slack.action(Actions.CHOOSE_SESSIONS, async ({ ack, body }) => { try { - await ack(); + if (body.type !== "block_actions") return; - if (body.type !== "block_actions") return; + // const view = await Slack.views.open({ + // trigger_id: body.trigger_id, + // view: Loading.loading() + // }); - // const view = await Slack.views.open({ - // trigger_id: body.trigger_id, - // view: Loading.loading() - // }); + const flowTs = body.message!.ts; - const flowTs = body.message!.ts; + const scrapbook = await prisma.scrapbook.findUniqueOrThrow({ + where: { + flowTs, + } + }); - const scrapbook = await prisma.scrapbook.findUniqueOrThrow({ - where: { - flowTs, - } - }); + // // Get the latest post + // const scrapbooks = await prisma.scrapbook.findMany({ + // where: { + // userId: scrapbook?.userId, + // }, + // select: { + // createdAt: true, + // }, + // orderBy: { + // createdAt: "desc", + // }, + // }); + + const sessions = await prisma.session.findMany({ + where: { + userId: scrapbook?.userId, + // createdAt: { + // // Before today & after the last post + // // Assuming the latest post is the last post (scrapbook) + // gte: scrapbooks.length > 1 ? scrapbooks[1].createdAt : undefined, + // lte: scrapbook?.createdAt, + // }, + metadata: { + path: ["banked"], + equals: false, + }, - // // Get the latest post - // const scrapbooks = await prisma.scrapbook.findMany({ - // where: { - // userId: scrapbook?.userId, - // }, - // select: { - // createdAt: true, - // }, - // orderBy: { - // createdAt: "desc", - // }, - // }); - - const sessions = await prisma.session.findMany({ - where: { - userId: scrapbook?.userId, - // createdAt: { - // // Before today & after the last post - // // Assuming the latest post is the last post (scrapbook) - // gte: scrapbooks.length > 1 ? scrapbooks[1].createdAt : undefined, - // lte: scrapbook?.createdAt, - // }, - metadata: { - path: ["banked"], - equals: false, + OR: [ + { + completed: true + }, + { + cancelled: true + } + ] }, + }); - OR: [ - { - completed: true - }, - { - cancelled: true - } - ] - }, - }); + // log(`\`\`\`${JSON.stringify(sessions, null, 2)}\`\`\``) - // log(`\`\`\`${JSON.stringify(sessions, null, 2)}\`\`\``) + // await Slack.views.update({ + // view_id: view?.view?.id, + // view: ChooseSessions.chooseSessionsModal(sessions, scrapbook?.internalId), + // }).catch((err) => console.log(err)); - // await Slack.views.update({ - // view_id: view?.view?.id, - // view: ChooseSessions.chooseSessionsModal(sessions, scrapbook?.internalId), - // }).catch((err) => console.log(err)); - - await openModal({ - triggerId: body.trigger_id, - view: ChooseSessions.chooseSessionsModal(sessions, scrapbook?.internalId), - }); -} catch (error) { - console.log(error); - emitter.emit("error", error); -} + await openModal({ + triggerId: body.trigger_id, + view: ChooseSessions.chooseSessionsModal(sessions, scrapbook?.internalId), + }); + } catch (error) { + console.log(error); + emitter.emit("error", error); + } }); Slack.view(Callbacks.CHOOSE_SESSIONS, async ({ ack, body, view }) => { @@ -115,8 +113,8 @@ Slack.view(Callbacks.CHOOSE_SESSIONS, async ({ ack, body, view }) => { }, }, }); - - for (const session of selectedSessions) { + + for (const session of selectedSessions) { // if (session.metadata?.airtable?.status === "Approved") { // session.metadata.airtable.status = "Banked"; @@ -125,11 +123,11 @@ Slack.view(Callbacks.CHOOSE_SESSIONS, async ({ ack, body, view }) => { // "Status": "Banked", // }); // } else { - await AirtableAPI.Session.update(session.metadata?.airtable?.id!, { - "Scrapbook": [scrapbook.data.record], - }); + await AirtableAPI.Session.update(session.metadata?.airtable?.id!, { + "Scrapbook": [scrapbook.data.record], + }); // } - + session.metadata.banked = true; await prisma.session.update({ @@ -287,7 +285,7 @@ Slack.command(Commands.ADMIN, async ({ command }) => { }); return; } - + const subCommand = command.text.split(" ")[0]; const subArgs = command.text.split(" ").slice(1); @@ -310,7 +308,7 @@ Slack.command(Commands.ADMIN, async ({ command }) => { channel, thread_ts: ts, text: subArgs.slice(1).join(" "), - username: Constants.USERNAME, + username: Constants.USERNAME, icon_emoji: pfps[pfp as keyof typeof pfps], }); } else if (subCommand === 'pfp') { @@ -324,7 +322,7 @@ Slack.command(Commands.ADMIN, async ({ command }) => { } if (Object.keys(pfps).includes(subArgs[0]) || subArgs[0] === "none") { - pfp = subArgs[0]; + pfp = subArgs[0]; } Slack.chat.postEphemeral({ diff --git a/src/extensions/arcade/slack/shop.ts b/src/extensions/arcade/slack/shop.ts index e9666d5a..b55d0733 100644 --- a/src/extensions/arcade/slack/shop.ts +++ b/src/extensions/arcade/slack/shop.ts @@ -1,38 +1,45 @@ -import { app, Slack } from "../../../lib/bolt.js"; -import { Actions, Commands, Environment } from "../../../lib/constants.js"; +import { Slack } from "../../../lib/bolt.js"; +import { Commands } from "../../../lib/constants.js"; import { t } from "../../../lib/templates.js"; -import { informUser } from "../../slack/lib/lib.js"; import { Loading } from "../../slack/views/loading.js"; import { AirtableAPI } from "../../../lib/airtable.js"; -import { openModal } from "../../slack/lib/open-modal.js"; import { Shop } from "./view.js"; -Slack.command(Commands.SHOP, async ({ command, ack }) => { - // const view = await Slack.views.open({ - // trigger_id: command.trigger_id, - // view: Loading.loading() - // }); +Slack.command(Commands.SHOP, async ({ command, ack, client }) => { + try { + const result = await Slack.views.open({ + trigger_id: command.trigger_id, + view: Loading.loading() + }); - const airtableUser = await AirtableAPI.User.lookupBySlack(command.user_id); + const viewId = result!.view!.id; - if (!airtableUser) { - // await ack(); - // informUser(command.user_id, t('error.first_time', {}), command.channel_id); - await openModal({ - triggerId: command.trigger_id, - view: Loading.error(t('error.first_time', {})) - }); + const airtableUser = await AirtableAPI.User.lookupBySlack(command.user_id); - return; - } + if (!airtableUser) { + await Slack.views.update({ + view_id: viewId, + view: Loading.error(t('error.first_time', {})) + }); - const remaining = Math.floor(airtableUser.fields["Balance (Minutes)"] / 60); - const pending = Math.floor(airtableUser.fields["Minutes (Pending Approval)"] / 60); + return; + } - // await Slack.views.update() + const remaining = Math.floor(airtableUser.fields["Balance (Minutes)"] / 60); + const pending = Math.floor(airtableUser.fields["Minutes (Pending Approval)"] / 60); + const spent = Math.floor(airtableUser.fields["Spent (Incl. Pending)"] / 60); + const banked = Math.floor(airtableUser.fields["Minutes (Banked)"] / 60) - await openModal({ - triggerId: command.trigger_id, - view: Shop.shop(remaining, pending, airtableUser) - }); + try { + await Slack.views.update({ + view_id: viewId, + view: Shop.shop(remaining, pending, spent, banked, airtableUser.id) + }); + } catch (error) { + console.error('Error updating view:', error); + } + + } catch (error) { + console.error('Error opening view:', error); + } }); \ No newline at end of file diff --git a/src/extensions/arcade/slack/view.ts b/src/extensions/arcade/slack/view.ts index 252c8c8d..8f09c128 100644 --- a/src/extensions/arcade/slack/view.ts +++ b/src/extensions/arcade/slack/view.ts @@ -149,7 +149,7 @@ export class ChooseSessions { export class Walkthrough { } export class Shop { - public static shop(remaining: number, pending: number, airtableUser: any): View { + public static shop(remaining: number, pending: number, spent: number, banked: number, id: string): View { const blocks = []; blocks.push({ @@ -160,12 +160,12 @@ export class Shop { } }); - if (Math.floor(airtableUser.fields["Spent (Incl. Pending)"] / 60) !== 0) { + if (spent !== 0) { blocks.push({ "type": "section", "text": { "type": "mrkdwn", - "text": `Total banked hours: ${Math.floor(airtableUser.fields["Minutes (Banked)"] / 60)} :tw_admission_tickets: ` + "text": `Total banked hours: ${banked} :tw_admission_tickets: ` } }); } @@ -183,7 +183,7 @@ export class Shop { "text": "Open the Shop", "emoji": true }, - 'url': `${Environment.SHOP_URL}/arcade/${airtableUser.id}/shop/`, + 'url': `${Environment.SHOP_URL}/arcade/${id}/shop/`, // 'url': `https://forms.hackclub.com/eligibility?slack_id=${command.user_id}`, // "url": `${Environment.SHOP_URL}/arcade/${airtableUser.id}/shop/`, // "action_id": Actions.OPEN_SHOP diff --git a/src/extensions/arcade/slack/walkthrough.ts b/src/extensions/arcade/slack/walkthrough.ts index 179d528b..5057e7e0 100644 --- a/src/extensions/arcade/slack/walkthrough.ts +++ b/src/extensions/arcade/slack/walkthrough.ts @@ -5,9 +5,7 @@ import { updateController } from "../../slack/lib/lib.js"; import { fetchEvidence } from "../lib/helper.js"; import { t } from "../../../lib/templates.js"; -Slack.action(Actions.TUTORIAL_ADVANCE, async ({ ack, body, client }) => { - await ack(); - +Slack.action(Actions.TUTORIAL_ADVANCE, async ({ respond, body, client }) => { const session = await prisma.session.findFirstOrThrow({ where: { messageTs: (body as any).message.thread_ts, @@ -27,12 +25,11 @@ Slack.action(Actions.TUTORIAL_ADVANCE, async ({ ack, body, client }) => { // Make sure the user is the one who started the tutorial if (session.user.slackUser.slackId !== (body as any).user.id) { - await Slack.chat.postEphemeral({ - channel: Environment.MAIN_CHANNEL, + respond({ + response_type: 'ephemeral', text: t('error.not_yours', {}), - thread_ts: session.messageTs, - user: (body as any).user.id - }); + thread_ts: session.messageTs, + }) return; } @@ -93,50 +90,48 @@ Slack.action(Actions.TUTORIAL_ADVANCE, async ({ ack, body, client }) => { await updateController(updatedSession); }); -Slack.action(Actions.TUTORIAL_BACK, async ({ ack, body, client }) => { - await ack(); - - const session = await prisma.session.findFirstOrThrow({//findUnique({ - where: { - messageTs: (body as any).message.thread_ts, - }, - include: { - user: { - include: { - slackUser: true - } - } - } - }); - - if (!session.user.slackUser) { - throw new Error('No slack user found for session user'); - } - - // Make sure the user is the one who started the tutorial - if (session.user.slackUser.slackId !== (body as any).user.id) { - await Slack.chat.postEphemeral({ - channel: Environment.MAIN_CHANNEL, - text: t('error.not_yours', {}), - thread_ts: session.messageTs, - user: (body as any).user.id - }); - - return; - } - - if (session.metadata.firstTime) { - session.metadata.firstTime.step--; - } - - const updatedSession = await prisma.session.update({ - where: { - id: session.id - }, - data: { - metadata: session.metadata - } - }); - - await updateController(updatedSession); -}); \ No newline at end of file +// Slack.action(Actions.TUTORIAL_BACK, async ({ ack, body, client }) => { +// const session = await prisma.session.findFirstOrThrow({//findUnique({ +// where: { +// messageTs: (body as any).message.thread_ts, +// }, +// include: { +// user: { +// include: { +// slackUser: true +// } +// } +// } +// }); + +// if (!session.user.slackUser) { +// throw new Error('No slack user found for session user'); +// } + +// // Make sure the user is the one who started the tutorial +// if (session.user.slackUser.slackId !== (body as any).user.id) { +// await Slack.chat.postEphemeral({ +// channel: Environment.MAIN_CHANNEL, +// text: t('error.not_yours', {}), +// thread_ts: session.messageTs, +// user: (body as any).user.id +// }); + +// return; +// } + +// if (session.metadata.firstTime) { +// session.metadata.firstTime.step--; +// } + +// const updatedSession = await prisma.session.update({ +// where: { +// id: session.id +// }, +// data: { +// metadata: session.metadata +// } +// }); + +// await updateController(updatedSession); +// }); \ No newline at end of file diff --git a/src/extensions/slack/functions/cancel.ts b/src/extensions/slack/functions/cancel.ts index dbdc497a..f72cbe9b 100644 --- a/src/extensions/slack/functions/cancel.ts +++ b/src/extensions/slack/functions/cancel.ts @@ -13,10 +13,8 @@ import { Slack } from "../../../lib/bolt.js"; import { pfps, t } from "../../../lib/templates.js"; import { handleError } from "../../../lib/handleError.js"; -Slack.action(Actions.CANCEL, async ({ ack, body }) => { +Slack.action(Actions.CANCEL, async ({ body }) => { try { - await ack(); - const thread_ts = (body as any).message.thread_ts; await Slack.views.open({ @@ -28,7 +26,7 @@ Slack.action(Actions.CANCEL, async ({ ack, body }) => { } }); -Slack.view(Callbacks.CANCEL, async ({ ack, body, view }) => { +Slack.view(Callbacks.CANCEL, async ({ respond, body, view }) => { try { const slackId = body.user.id; const messageTs = view.private_metadata; @@ -57,7 +55,12 @@ Slack.view(Callbacks.CANCEL, async ({ ack, body, view }) => { console.log(session?.user.slackUser?.slackId); if (!session || slackId !== session.user.slackUser?.slackId) { - informUser(slackId, t('error.not_yours', {}), Environment.MAIN_CHANNEL, messageTs, pfps['threat']); + respond({ + response_type: 'ephemeral', + text: t('error.not_yours', {}), + thread_ts: messageTs, + icon_emoji: pfps['threat'] + }); return; } diff --git a/src/extensions/slack/functions/extend.ts b/src/extensions/slack/functions/extend.ts index 97e62026..fe6aeae6 100644 --- a/src/extensions/slack/functions/extend.ts +++ b/src/extensions/slack/functions/extend.ts @@ -4,14 +4,19 @@ import { prisma } from "../../../lib/prisma.js"; import { updateController, updateTopLevel, informUser } from "../lib/lib.js"; import { Session } from "../../../lib/corelib.js"; +import { handleError } from "../../../lib/handleError.js"; +import { t } from "../../../lib/templates.js"; /* Time Extension */ -Slack.action(Actions.EXTEND, async ({ ack, body }) => { - await ack(); +Slack.action(Actions.EXTEND, async ({ respond }) => { // TODO // informUser(body.user.id, `Use \`${Commands.EXTEND}\` to extend the amount of time you have!`, Environment.MAIN_CHANNEL, (body as any).message.thread_ts); - informUser(body.user.id, `This command is disabled for now!`, Environment.MAIN_CHANNEL, (body as any).message.thread_ts); + // informUser(body.user.id, `This command is disabled for now!`, Environment.MAIN_CHANNEL, (body as any).message.thread_ts); + respond({ + response_type: 'ephemeral', + text: t('error.disabled', {}) + }); }); Slack.command(Commands.EXTEND, async ({ ack, body }) => { diff --git a/src/extensions/slack/functions/goals.ts b/src/extensions/slack/functions/goals.ts index 63ae6a7e..b372197e 100644 --- a/src/extensions/slack/functions/goals.ts +++ b/src/extensions/slack/functions/goals.ts @@ -10,8 +10,6 @@ import { handleError } from "../../../lib/handleError.js"; Slack.action(Actions.OPEN_GOAL, async ({ ack, body, client }) => { try { - await ack(); - const slackId: string = body.user.id; const trigger_id: string = (body as any).trigger_id @@ -67,8 +65,6 @@ Slack.action(Actions.OPEN_GOAL, async ({ ack, body, client }) => { Slack.action(Actions.SELECT_GOAL, async ({ ack, body, client }) => { try { - await ack(); - const goalId = (body as any).actions[0].selected_option.value; const sessionId = (body as any).view.private_metadata @@ -148,8 +144,6 @@ Slack.view(Callbacks.MAIN_GOAL, async ({ ack }) => { Slack.action(Actions.CREATE_GOAL, async ({ ack, body, client }) => { try { - await ack(); - const sessionId = (body as any).view.private_metadata; const trigger_id: string = (body as any).trigger_id; @@ -263,7 +257,6 @@ Slack.view(Callbacks.CREATE_GOAL, async ({ ack, body, view, client }) => { Slack.action(Actions.DELETE_GOAL, async ({ ack, body, client }) => { try { - const sessionId = (body as any).view.private_metadata; const trigger_id: string = (body as any).trigger_id; diff --git a/src/extensions/slack/functions/pause.ts b/src/extensions/slack/functions/pause.ts index 7dbcbfdb..5c9fa070 100644 --- a/src/extensions/slack/functions/pause.ts +++ b/src/extensions/slack/functions/pause.ts @@ -55,8 +55,6 @@ Slack.action(Actions.PAUSE, async ({ ack, body }) => { Slack.action(Actions.RESUME, async ({ ack, body }) => { try { - await ack(); - const slackId = body.user.id; const session = await prisma.session.findFirst({ diff --git a/src/extensions/slack/functions/stats.ts b/src/extensions/slack/functions/stats.ts index 3ff32ed4..04c7e26a 100644 --- a/src/extensions/slack/functions/stats.ts +++ b/src/extensions/slack/functions/stats.ts @@ -7,10 +7,8 @@ import { informUser } from "../lib/lib.js"; import { Loading } from "../views/loading.js"; import { Stats } from "../views/stats.js"; -Slack.action(Actions.VIEW_STATS, async ({ ack, body }) => { +Slack.action(Actions.VIEW_STATS, async ({ respond, body }) => { try { - await ack(); - const view = await Slack.views.open({ trigger_id: (body as any).trigger_id, view: Loading.loading(), @@ -28,10 +26,10 @@ Slack.action(Actions.VIEW_STATS, async ({ ack, body }) => { if (!user) { // informUser(slackId, t('error.not_a_user', {}), Environment.MAIN_CHANNEL, (body as any).message.ts); - await Slack.views.update({ - view_id: view?.view?.id, - view: Loading.error(t('error.not_a_user', {})) - }); + // await Slack.views.update({ + // view_id: view?.view?.id, + // view: Loading.error(t('error.not_a_user', {})) + // }); return; } @@ -41,6 +39,7 @@ Slack.action(Actions.VIEW_STATS, async ({ ack, body }) => { view_id: view?.view?.id, view: Loading.error(t('error.first_time', {})) }); + return; } @@ -53,7 +52,7 @@ Slack.action(Actions.VIEW_STATS, async ({ ack, body }) => { } }); -Slack.command(Commands.STATS, async ({ ack, body, client }) => { +Slack.command(Commands.STATS, async ({ body, client }) => { const slackId = body.user_id; const channelId = body.channel_id; const triggerId = body.trigger_id; diff --git a/src/extensions/slack/index.ts b/src/extensions/slack/index.ts index c2a4bb3d..82ba8f82 100644 --- a/src/extensions/slack/index.ts +++ b/src/extensions/slack/index.ts @@ -219,7 +219,6 @@ Slack.command(Commands.HOUR, hack); Slack.command(Commands.ARCADE, hack); Slack.action(Actions.HACK, async ({ ack, body, respond }) => { - await ack(); await respond({ delete_original: true }); diff --git a/src/extensions/slack/lib/open-modal.ts b/src/extensions/slack/lib/open-modal.ts index 489f1974..fbdd4ae1 100644 --- a/src/extensions/slack/lib/open-modal.ts +++ b/src/extensions/slack/lib/open-modal.ts @@ -20,9 +20,9 @@ export async function openModal({triggerId, view}: {triggerId: string, view: Vie throw new Error("View ID not found"); } - await Slack.views.update({ + Slack.views.update({ view_id: viewID, - view + view: Loading.loading() }) } catch (error) { handleError(error) diff --git a/src/lib/bolt.ts b/src/lib/bolt.ts index ee4a9313..f023d898 100644 --- a/src/lib/bolt.ts +++ b/src/lib/bolt.ts @@ -1,4 +1,4 @@ -import bolt, { SlackViewAction, SlackViewMiddlewareArgs } from '@slack/bolt'; +import bolt, { SlackViewAction, SlackViewMiddlewareArgs } from '@slack/bolt'; import bodyParser from 'body-parser'; @@ -8,20 +8,15 @@ import { StringIndexed } from "@slack/bolt/dist/types/helpers.js"; import { Commands, Environment } from './constants.js'; import { assertVal } from './assert.js'; import { t } from './templates.js'; -import { AirtableAPI } from './airtable.js'; import { handleError } from './handleError.js'; -import util from 'util'; const expressReceiver = new bolt.ExpressReceiver({ signingSecret: Environment.SLACK_SIGNING_SECRET, endpoints: '/slack/events', processBeforeResponse: true, - - unhandledRequestHandler(args) { - console.log(JSON.stringify(Object.keys(args.request))); - console.log(`[WARN] [${new Date().toISOString()}] Unhandled request detected - did not acknowledge to slack, acking manually`) - console.log(`Context for failed request: ${util.inspect(args.request)}`) + unhandledRequestHandler(args) { + console.log(`[${new Date().toISOString()}] [Slack] Unhandled request detected`) args.response.writeHead(200); // (:< args.response.end(); @@ -29,23 +24,30 @@ const expressReceiver = new bolt.ExpressReceiver({ }); export const express = expressReceiver.app; - + express.use(bodyParser.json()); export const app = new bolt.App({ token: Environment.SLACK_BOT_TOKEN, appToken: Environment.SLACK_APP_TOKEN, clientId: Environment.CLIENT_ID, - clientSecret: Environment.CLIENT_SECRET, + clientSecret: Environment.CLIENT_SECRET, receiver: expressReceiver, }); // Time response -app.use(async ({ next, payload }) => { +app.use(async ({ next, payload, ack }) => { const now = new Date(); + if (ack) { + ack().then(() => { + console.log(`[${now.toISOString()}] Acknowledged ${payload.type} event after ${new Date().getTime() - now.getTime()}ms`) + }); + } + await next(); + console.log(`[${now.toISOString()}] Took ${new Date().getTime() - now.getTime()}ms to respond to ${payload.type} event`) }); @@ -86,9 +88,9 @@ export const Slack = { const { command: event, ack, respond } = payload; - console.log(`[${now.toISOString()}] <@${event.user_id}> ran \`${command} ${event.text}\``) + console.log(`[${now.toISOString()}] [Slack.command] <@${event.user_id}> ran \`${command} ${event.text}\``) - // await ack(); + // ack(); if (Environment.MAINTAINANCE_MODE) { const user = await app.client.users.info({ @@ -100,15 +102,15 @@ export const Slack = { } } - if (recordCommands.includes(command)) { - const airtableUser = await AirtableAPI.User.lookupBySlack(event.user_id); + // if (recordCommands.includes(command)) { + // const airtableUser = await AirtableAPI.User.lookupBySlack(event.user_id); - if (airtableUser) { - await AirtableAPI.User.update(airtableUser.id, { - [command]: true - }); - } - } + // if (airtableUser) { + // await AirtableAPI.User.update(airtableUser.id, { + // [command]: true + // }); + // } + // } try { await app.client.chat.postMessage({ @@ -134,19 +136,19 @@ export const Slack = { }) await commandHandler(payload); verb = "succeeded" - } catch(error) { + } catch (error) { verb = "failed" handleError(error) await app.client.chat.postEphemeral({ channel: event.channel_id, - user: event.user_id, + user: event.user_id, text: `An error occurred while processing your command!` }) } const duration = new Date().getTime() - now.getTime(); - console.log(`[${now.toISOString()}] ${verb} after ${duration}ms`) + console.log(`[${now.toISOString()}] [Slack.command] running command ${verb} after ${duration}ms`) }) }, @@ -182,12 +184,12 @@ export const Slack = { type: "context", elements: [ { - type: "mrkdwn", - text: `${actionId} - ran in <#${body.channel?.id}>\n${new Date().toString()}`, + type: "mrkdwn", + text: `${actionId} - ran in <#${body.channel?.id}>\n${new Date().toString()}`, }, ], }, - ] + ] // blocks: [ // { // type: "context", @@ -202,26 +204,26 @@ export const Slack = { }) verb = "succeeded" - listeners.forEach((listener) => { + listeners.forEach((listener) => { try { - listener(payload) + listener(payload) } catch (error) { verb = "failed" handleError(error) } }); - } catch(error) { + } catch (error) { verb = "failed" handleError(error) - /* await app.client.chat.postEphemeral({ - channel: action.channel.id, - user: action.user.id, - text: `An error occurred while processing your action!` - });*/ + /* await app.client.chat.postEphemeral({ + channel: action.channel.id, + user: action.user.id, + text: `An error occurred while processing your action!` + });*/ } const duration = new Date().getTime() - now.getTime(); - console.log(`[${now.toISOString()}] ${verb} after ${duration}ms`) + console.log(`[${now.toISOString()}] responding to action ${verb} after ${duration}ms`) }) }, @@ -232,9 +234,9 @@ export const Slack = { const { body, view } = payload; - console.log(`[${now.toISOString()}] <@${body.user.id}> ${body.type === "view_submission" ? "submitted" : "closed"} view "${callbackId}"`) + console.log(`[${now.toISOString()}] [Slack.view] <@${body.user.id}> ${body.type === "view_submission" ? "submitted" : "closed"} view "${callbackId}"`) - await payload.ack(); + // await payload.ack(); try { await app.client.chat.postMessage({ @@ -251,12 +253,12 @@ export const Slack = { type: "context", elements: [ { - type: "mrkdwn", - text: `${callbackId}\n${new Date().toString()} `, + type: "mrkdwn", + text: `${callbackId}\n${new Date().toString()} `, }, ], }, - ] + ] // blocks: [ // { // type: "context", @@ -278,7 +280,7 @@ export const Slack = { verb = "failed" handleError(error) } - }); + }); } catch (error) { verb = "failed" handleError(error) @@ -291,7 +293,7 @@ export const Slack = { } const duration = new Date().getTime() - now.getTime(); - console.log(`[${now.toISOString()}] ${verb} after ${duration}ms`) + console.log(`[${now.toISOString()}] [Slack.view] response to view event ${verb} after ${duration}ms`) }) }, @@ -343,7 +345,7 @@ export const Slack = { }, async postEphemeral(options: Parameters[0]) { - try { + try { // await app.client.chat.postMessage({ // ...options, // channel: Environment.INTERNAL_CHANNEL @@ -390,7 +392,7 @@ export const Slack = { } catch (error: any) { handleError(error) - console.log(`[${new Date().toISOString()}] failed to post message`) + console.log(`[${new Date().toISOString()}] failed to post message`) if (options) { await app.client.chat.postMessage({ @@ -471,7 +473,7 @@ export const Slack = { } catch (error) { handleError(error) } - } + } }, views: { @@ -481,11 +483,11 @@ export const Slack = { if (!options) { throw new Error('No options provided!') } - console.log(`[${now.toISOString()}] opening view"`) + console.log(`[${now.toISOString()}] [views.open] opening view`) const result = await app.client.views.open(options); - console.log(`[${now.toISOString()}] succeeded after ${new Date().getTime() - now.getTime()}ms`) + console.log(`[${now.toISOString()}] [views.open] succeeded after ${new Date().getTime() - now.getTime()}ms`) return result; } catch (error) { @@ -499,11 +501,11 @@ export const Slack = { if (!options) { throw new Error('No options provided!') } - console.log(`[${now.toISOString()}] updating view`) + console.log(`[${now.toISOString()}] [views.update] updating view`) const result = await app.client.views.update(options); - console.log(`[${now.toISOString()}] succeeded after ${new Date().getTime() - now.getTime()}ms`) + console.log(`[${now.toISOString()}] [views.update] succeeded after ${new Date().getTime() - now.getTime()}ms`) return result; } catch (error) { @@ -558,7 +560,7 @@ export const Slack = { channel: Environment.MAIN_CHANNEL }); - return; + return; } }, diff --git a/src/lib/templates.yaml b/src/lib/templates.yaml index e05e4047..96c26104 100644 --- a/src/lib/templates.yaml +++ b/src/lib/templates.yaml @@ -100,6 +100,9 @@ error: first_time: - hey hey! you need to finish your first arcade session before you can do that! + disabled: + - you hear some scurrying in an adjacent alley. seems like this feature is disabled right now. + action: paused: - i paused the clock for ya. you still got `${minutes}` ${minutes_units} left