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) 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/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/lib/helper.ts b/src/extensions/arcade/lib/helper.ts index 14241044..a38ed360 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({ @@ -48,8 +55,9 @@ export const surfaceEvidence = async (messageTs: string, slackId: string) => { return; } - 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: { diff --git a/src/extensions/arcade/slack/index.ts b/src/extensions/arcade/slack/index.ts index 5debcbee..ec384595 100644 --- a/src/extensions/arcade/slack/index.ts +++ b/src/extensions/arcade/slack/index.ts @@ -9,80 +9,81 @@ 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 { - 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)); -} 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 }) => { - await ack(); - const scrapbook = await prisma.scrapbook.findUniqueOrThrow({ where: { internalId: view.private_metadata, @@ -112,20 +113,20 @@ 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 { - await AirtableAPI.Session.update(session.metadata?.airtable?.id!, { - "Scrapbook": [scrapbook.data.record], - }); - } + + 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 { + await AirtableAPI.Session.update(session.metadata?.airtable?.id!, { + "Scrapbook": [scrapbook.data.record], + }); + // } session.metadata.banked = true; @@ -280,11 +281,11 @@ 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; } - + const subCommand = command.text.split(" ")[0]; const subArgs = command.text.split(" ").slice(1); @@ -307,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') { @@ -321,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 fafbf65d..b55d0733 100644 --- a/src/extensions/arcade/slack/shop.ts +++ b/src/extensions/arcade/slack/shop.ts @@ -1,91 +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 { 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() - }); - - 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, - view: Loading.error(t('error.first_time', {})) +Slack.command(Commands.SHOP, async ({ command, ack, client }) => { + try { + const result = await Slack.views.open({ + trigger_id: command.trigger_id, + view: Loading.loading() }); - return; - } + const viewId = result!.view!.id; - const blocks = []; + const airtableUser = await AirtableAPI.User.lookupBySlack(command.user_id); - const remaining = Math.floor(airtableUser.fields["Balance (Minutes)"] / 60); - const pending = Math.floor(airtableUser.fields["Minutes (Pending Approval)"] / 60); + if (!airtableUser) { + await Slack.views.update({ + view_id: viewId, + view: Loading.error(t('error.first_time', {})) + }); - blocks.push({ - "type": "section", - "text": { - "type": "mrkdwn", - "text": `Available to spend: ${remaining} :tw_admission_tickets: _(Pending Approval: ${pending})_` + return; } - }); - 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", - }); - - 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 + 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) + + 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 96227597..8f09c128 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,4 +146,66 @@ 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, spent: number, banked: number, id: string): View { + const blocks = []; + + blocks.push({ + "type": "section", + "text": { + "type": "mrkdwn", + "text": `Available to spend: ${remaining} :tw_admission_tickets: _(Pending Approval: ${pending})_` + } + }); + + if (spent !== 0) { + blocks.push({ + "type": "section", + "text": { + "type": "mrkdwn", + "text": `Total banked hours: ${banked} :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/${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", + }); + + + 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/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/arcade/watchers/airtable.ts b/src/extensions/arcade/watchers/airtable.ts index 756631ca..0a58c333 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 { @@ -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, @@ -148,7 +151,8 @@ express.post('/airtable/session/update', async (req, res) => { res.sendStatus(200); } catch (error) { - emitter.emit('error', {error}); + handleError(error); + res.sendStatus(500); } }); diff --git a/src/extensions/arcade/watchers/hackhour.ts b/src/extensions/arcade/watchers/hackhour.ts index f59474ad..06f87273 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); } }; @@ -300,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, @@ -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..f72cbe9b 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,11 +11,10 @@ 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 }) => { +Slack.action(Actions.CANCEL, async ({ body }) => { try { - await ack(); - const thread_ts = (body as any).message.thread_ts; await Slack.views.open({ @@ -24,14 +22,12 @@ Slack.action(Actions.CANCEL, async ({ ack, body }) => { view: await Cancel.cancel(thread_ts) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error); } }); -Slack.view(Callbacks.CANCEL, async ({ ack, body, view }) => { +Slack.view(Callbacks.CANCEL, async ({ respond, body, view }) => { try { - await ack(); - const slackId = body.user.id; const messageTs = view.private_metadata; @@ -59,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; } @@ -72,7 +73,7 @@ Slack.view(Callbacks.CANCEL, async ({ ack, body, view }) => { } })); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -105,6 +106,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..fe6aeae6 100644 --- a/src/extensions/slack/functions/extend.ts +++ b/src/extensions/slack/functions/extend.ts @@ -1,18 +1,22 @@ 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"; +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 }) => { @@ -66,6 +70,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..b372197e 100644 --- a/src/extensions/slack/functions/goals.ts +++ b/src/extensions/slack/functions/goals.ts @@ -4,14 +4,12 @@ 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 { - await ack(); - const slackId: string = body.user.id; const trigger_id: string = (body as any).trigger_id @@ -61,14 +59,12 @@ Slack.action(Actions.OPEN_GOAL, async ({ ack, body, client }) => { view: await Goals.main(session.id) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); 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 @@ -138,18 +134,16 @@ Slack.action(Actions.SELECT_GOAL, async ({ ack, body, client }) => { view: await Goals.main(session.id) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); Slack.view(Callbacks.MAIN_GOAL, async ({ ack }) => { - await ack(); + // await 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; @@ -158,14 +152,12 @@ Slack.action(Actions.CREATE_GOAL, async ({ ack, body, client }) => { view: await Goals.create(sessionId) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); Slack.view(Callbacks.CREATE_GOAL, async ({ ack, body, view, client }) => { try { - await ack(); - const slackId = body.user.id; const sessionId = body.view.private_metadata; @@ -259,13 +251,12 @@ Slack.view(Callbacks.CREATE_GOAL, async ({ ack, body, view, client }) => { view: await Goals.main(sessionId) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); 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; @@ -300,14 +291,12 @@ Slack.action(Actions.DELETE_GOAL, async ({ ack, body, client }) => { view: await Goals.delete(sessionId) }); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); Slack.view(Callbacks.DELETE_GOAL, async ({ ack, body, view, client }) => { try { - await ack(); - const sessionId = body.view.private_metadata; // Mark the goal as complete @@ -396,6 +385,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..5c9fa070 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,14 +49,12 @@ Slack.action(Actions.PAUSE, async ({ ack, body }) => { await Session.pause(session); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); Slack.action(Actions.RESUME, async ({ ack, body }) => { try { - await ack(); - const slackId = body.user.id; const session = await prisma.session.findFirst({ @@ -84,7 +82,7 @@ Slack.action(Actions.RESUME, async ({ ack, body }) => { await Session.pause(session); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -126,7 +124,7 @@ Slack.command(Commands.PAUSE, async ({ ack, body }) => { informUser(slackId, toggleMessage, body.channel_id); } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }); @@ -167,6 +165,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/functions/stats.ts b/src/extensions/slack/functions/stats.ts index 088f4a69..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; @@ -92,6 +91,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/extensions/slack/index.ts b/src/extensions/slack/index.ts index c008e3b5..82ba8f82 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 @@ -36,26 +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) { - await informUser(slackId, t('error.already_hacking', {}), command.channel_id); - - return; - } - let slackUser = await prisma.slackUser.upsert( { where: { @@ -88,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 @@ -205,7 +210,7 @@ const hack = async ({ command }: CommandHandler) => { ts: assertVal(topLevel!.ts) }); } catch (error) { - emitter.emit('error', { error }); + handleError(error) } }; @@ -214,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 }); @@ -278,7 +282,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; } @@ -432,7 +455,6 @@ emitter.on('sessionUpdate', async (update: { await updateTopLevel(session); } } catch (error) { - emitter.emit('error', { error }); console.error(error); } }); @@ -714,7 +736,7 @@ emitter.on('error', async (errorRef) => { ] }); } catch (error) { - emitter.emit('error', { error }); + handleError(error) } }); @@ -729,6 +751,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/extensions/slack/lib/open-modal.ts b/src/extensions/slack/lib/open-modal.ts new file mode 100644 index 00000000..fbdd4ae1 --- /dev/null +++ b/src/extensions/slack/lib/open-modal.ts @@ -0,0 +1,30 @@ +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, view}: {triggerId: string, view: View}) { + try { + 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, 3 * 1000)); // raccoons take their time to wake up! + + await Promise.all([loadingDelay, minDelay]); + + if (!viewID) { + throw new Error("View ID not found"); + } + + Slack.views.update({ + view_id: viewID, + view: Loading.loading() + }) + } catch (error) { + handleError(error) + } +} \ No newline at end of file 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) { diff --git a/src/lib/airtable.ts b/src/lib/airtable.ts index 4aaec0a6..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, @@ -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`); diff --git a/src/lib/bolt.ts b/src/lib/bolt.ts index 36dc2d71..f023d898 100644 --- a/src/lib/bolt.ts +++ b/src/lib/bolt.ts @@ -1,40 +1,61 @@ -import bolt, { SlackViewAction, SlackViewMiddlewareArgs } from '@slack/bolt'; +import bolt, { SlackViewAction, SlackViewMiddlewareArgs } from '@slack/bolt'; + import bodyParser from 'body-parser'; import { AllMiddlewareArgs, Middleware, SlackAction, SlackActionMiddlewareArgs, SlackCommandMiddlewareArgs } from "@slack/bolt"; 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, endpoints: '/slack/events', processBeforeResponse: true, + + unhandledRequestHandler(args) { + console.log(`[${new Date().toISOString()}] [Slack] Unhandled request detected`) + + 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, appToken: Environment.SLACK_APP_TOKEN, clientId: Environment.CLIENT_ID, - clientSecret: Environment.CLIENT_SECRET, + clientSecret: Environment.CLIENT_SECRET, receiver: expressReceiver, }); -express.use(bodyParser.json()); +// Time response +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`) +}); 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) } }); @@ -67,9 +88,9 @@ export const Slack = { const { command: event, ack, respond } = payload; - console.log(`[${now.toISOString()}] <@${event.user_id}> ran \`${command} ${event.text}\``) - - await ack(); + console.log(`[${now.toISOString()}] [Slack.command] <@${event.user_id}> ran \`${command} ${event.text}\``) + + // ack(); if (Environment.MAINTAINANCE_MODE) { const user = await app.client.users.info({ @@ -81,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({ @@ -115,19 +136,19 @@ export const Slack = { }) await commandHandler(payload); verb = "succeeded" - } catch(error) { + } catch (error) { verb = "failed" - emitter.emit('error', {error}) + 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`) }) }, @@ -163,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", @@ -183,26 +204,26 @@ export const Slack = { }) verb = "succeeded" - listeners.forEach((listener) => { + listeners.forEach((listener) => { try { - listener(payload) + listener(payload) } catch (error) { verb = "failed" - emitter.emit('error', {error}); + handleError(error) } }); - } catch(error) { + } catch (error) { verb = "failed" - emitter.emit('error', {error}); - /* await app.client.chat.postEphemeral({ - channel: action.channel.id, - user: action.user.id, - text: `An error occurred while processing your action!` - });*/ + handleError(error) + /* 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`) }) }, @@ -213,7 +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(); try { await app.client.chat.postMessage({ @@ -230,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", @@ -255,12 +278,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, @@ -270,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`) }) }, @@ -317,12 +340,12 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }, async postEphemeral(options: Parameters[0]) { - try { + try { // await app.client.chat.postMessage({ // ...options, // channel: Environment.INTERNAL_CHANNEL @@ -367,9 +390,9 @@ export const Slack = { return result; } catch (error: any) { - emitter.emit('error', {error}); + 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({ @@ -430,7 +453,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } }, @@ -448,9 +471,9 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } - } + } }, views: { @@ -460,15 +483,15 @@ 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) { - emitter.emit('error', {error}); + handleError(error) } }, @@ -478,15 +501,15 @@ 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) { - emitter.emit('error', {error}); + handleError(error) } } }, @@ -506,7 +529,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } } }, @@ -526,7 +549,7 @@ export const Slack = { return result; } catch (error) { - emitter.emit('error', {error}); + handleError(error) } } }, @@ -537,7 +560,7 @@ export const Slack = { channel: Environment.MAIN_CHANNEL }); - return; + return; } }, 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 diff --git a/src/lib/templates.yaml b/src/lib/templates.yaml index c4cea7c0..96c26104 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`! @@ -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