From 804d5b678d2e280c8a6d3d821c17e5d35aa9c9aa Mon Sep 17 00:00:00 2001 From: Martin Dimitrov Date: Tue, 10 Dec 2024 19:04:15 -0800 Subject: [PATCH] change to typescript for client --- bun.lockb | Bin 406944 -> 408256 bytes package.json | 3 +- packages/doorman-client/.gitignore | 3 + packages/doorman-client/.twilioserverlessrc | 1 + packages/doorman-client/package.json | 10 +- .../functions/buzzer-activated.ts} | 110 +++++------------- packages/doorman-client/src/index.ts | 9 ++ .../src/types/BuzzerDialEvent.ts | 6 + .../doorman-client/src/types/DoorConfig.ts | 6 + .../src/types/DoorStatusResponse.ts | 9 ++ .../doorman-client/src/types/TwilioContext.ts | 5 + .../doorman-client/src/utils/DoormanUtils.ts | 21 ++++ .../doorman-client/src/utils/LambdaUtils.ts | 23 ++++ .../doorman-client/src/utils/TwimlUtils.ts | 29 +++++ 14 files changed, 152 insertions(+), 83 deletions(-) rename packages/doorman-client/{functions/buzzer-activated.js => src/functions/buzzer-activated.ts} (52%) create mode 100644 packages/doorman-client/src/index.ts create mode 100644 packages/doorman-client/src/types/BuzzerDialEvent.ts create mode 100644 packages/doorman-client/src/types/DoorConfig.ts create mode 100644 packages/doorman-client/src/types/DoorStatusResponse.ts create mode 100644 packages/doorman-client/src/types/TwilioContext.ts create mode 100644 packages/doorman-client/src/utils/DoormanUtils.ts create mode 100644 packages/doorman-client/src/utils/LambdaUtils.ts create mode 100644 packages/doorman-client/src/utils/TwimlUtils.ts diff --git a/bun.lockb b/bun.lockb index 82cfba7dd68cfbca0f86e6eaeb5a77a2d8c6d85f..d08672be1188b66728c6f8ffea108a7e1f791804 100755 GIT binary patch delta 18186 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 { - let req; - - if (url.startsWith("https://")) { - req = https.request(url); - } else { - req = http.request(url); - } - req.end(null, null, () => { - resolve(req); - }); - }); -} - -async function getConfig(context, buzzer) { - return await fetch(context.DOORMAN_URL + `/api/door/info?buzzer=${buzzer}`) - .then(res => res.json()) - .catch(err => { - return undefined; - }); -} - -async function notifyDiscord(context, msg, u, optionalJsonStr) { - return lambdaFastHttp(context.DOORMAN_URL + - `/api/door/notify?discordUser=${encodeURIComponent(JSON.stringify(u))}&msg=${encodeURIComponent(JSON.stringify(msg))}&json=${encodeURIComponent(JSON.stringify(optionalJsonStr))}`, - ).catch(err => console.log(err)) -} - -async function notifyAllDiscord(context, config, msg, optionalJsonStr) { - return notifyDiscord(context, config.discordUsers.map(() => msg), config.discordUsers, config.discordUsers.map(() => optionalJsonStr || "")); -} - -function doorOpenTwiml(twiml, config) { - twiml.play('https://buzzer-2439-prod.twil.io/buzzing_up_boosted.mp3'); - twiml.play({ digits: config.pressKey }); // configured in doorman what button to click and passed into this function - twiml.pause({ length: 1 }); - twiml.hangup(); -} - -function dialFallbackTwiml(twiml, config) { - let dial = twiml.dial({ - timeLimit: 20, - timeout: 20 - }); - - config.fallbackNumbers.forEach(number => { - dial.number(number); - }); -} - -exports.handler = async function(context, event, callback) { +export const handler: ServerlessFunctionSignature = async function(context, event, callback) { let invokeId = `[${randomUUID()}]`; - let config = event.config; + let configString = event.config; + let config: DoorConfig | undefined; console.log(invokeId, "starting execution"); // get by api or parse it out from query - if (!config) { + if (!configString) { config = await getConfig(context, event.From); } else { try { - config = JSON.parse(config); + config = JSON.parse(configString); } catch(e) { config = await getConfig(context, event.From); } @@ -93,21 +46,22 @@ exports.handler = async function(context, event, callback) { msg + u + ')' ); - await notifyDiscord(context, msgs, config.discordUsers, config.discordUsers.map(() => undefined)); + await notifyDiscord(context, msgs, config.discordUsers, config.discordUsers.map(() => "")); let discordLock = false; - let intervals = []; - let timeouts = []; + let intervals: Timer[] = []; + let timeouts: Timer[] = []; - const unlockPromise = new Promise((resolve, reject) => { + const unlockPromise = new Promise((resolve, reject) => { intervals.push(setInterval(() => { fetch(context.DOORMAN_URL + `/api/door/status?door=${config.door}`) .then(res => res.json()) - .then(async body => { - if (body?.status === "OPEN") { + .then(async (rawBody) => { + let body = rawBody as DoorStatusResponse; + if (body?.status === DoorStatus.OPEN) { clearInterval(intervals[0]); - const twiml = new Twilio.twiml.VoiceResponse(); - doorOpenTwiml(twiml, config); + const twiml = doorOpenTwiml(config); + if (!discordLock) { discordLock = true; console.log( @@ -125,10 +79,9 @@ exports.handler = async function(context, event, callback) { }, 750)); }); - const gracefulFallbackPromise = new Promise((resolve, reject) => { + const gracefulFallbackPromise = new Promise((resolve, reject) => { timeouts.push(setTimeout(async () => { - const twiml = new Twilio.twiml.VoiceResponse(); - dialFallbackTwiml(twiml, config); + const twiml = dialFallbackTwiml(config); if (!discordLock) { discordLock = true; @@ -150,10 +103,9 @@ exports.handler = async function(context, event, callback) { }, 8000)); }); - const ungracefulFallbackPromise = new Promise((resolve, reject) => { + const ungracefulFallbackPromise = new Promise((resolve, reject) => { timeouts.push(setTimeout(async () => { - const twiml = new Twilio.twiml.VoiceResponse(); - dialFallbackTwiml(twiml, config); + const twiml = dialFallbackTwiml(config); console.error( invokeId, "UngracefulFallbackPromise: Cutting it too close to timeout! Skipping notifying users and calling fallback" ); diff --git a/packages/doorman-client/src/index.ts b/packages/doorman-client/src/index.ts new file mode 100644 index 0000000..92549b2 --- /dev/null +++ b/packages/doorman-client/src/index.ts @@ -0,0 +1,9 @@ +console.log("Building functions..."); +await Bun.build({ + entrypoints: ['./src/functions/buzzer-activated.ts'], + outdir: './build/functions', + packages: 'external', + naming: '[dir]/[name].[ext]' , + target: 'node', + format: 'cjs', +}); diff --git a/packages/doorman-client/src/types/BuzzerDialEvent.ts b/packages/doorman-client/src/types/BuzzerDialEvent.ts new file mode 100644 index 0000000..ef33289 --- /dev/null +++ b/packages/doorman-client/src/types/BuzzerDialEvent.ts @@ -0,0 +1,6 @@ +import { ServerlessEventObject } from "@twilio-labs/serverless-runtime-types/types"; + +export interface BuzzerDialEvent extends ServerlessEventObject { + From: string; + config?: string; // DoorConfig serialized as string +} \ No newline at end of file diff --git a/packages/doorman-client/src/types/DoorConfig.ts b/packages/doorman-client/src/types/DoorConfig.ts new file mode 100644 index 0000000..5a51b7a --- /dev/null +++ b/packages/doorman-client/src/types/DoorConfig.ts @@ -0,0 +1,6 @@ +export interface DoorConfig { + door: string; + pressKey: string; + fallbackNumbers: string[]; + discordUsers: string[]; +} diff --git a/packages/doorman-client/src/types/DoorStatusResponse.ts b/packages/doorman-client/src/types/DoorStatusResponse.ts new file mode 100644 index 0000000..5ab8c5c --- /dev/null +++ b/packages/doorman-client/src/types/DoorStatusResponse.ts @@ -0,0 +1,9 @@ +export enum DoorStatus { + OPEN = "OPEN", + CLOSED = "CLOSED", +} + +export interface DoorStatusResponse { + status: DoorStatus, + fingerprint: any; +} diff --git a/packages/doorman-client/src/types/TwilioContext.ts b/packages/doorman-client/src/types/TwilioContext.ts new file mode 100644 index 0000000..674ded7 --- /dev/null +++ b/packages/doorman-client/src/types/TwilioContext.ts @@ -0,0 +1,5 @@ +import { EnvironmentVariables } from "@twilio-labs/serverless-runtime-types/types"; + +export interface TwilioContext extends EnvironmentVariables { + DOORMAN_URL: string; +} diff --git a/packages/doorman-client/src/utils/DoormanUtils.ts b/packages/doorman-client/src/utils/DoormanUtils.ts new file mode 100644 index 0000000..9b6aa42 --- /dev/null +++ b/packages/doorman-client/src/utils/DoormanUtils.ts @@ -0,0 +1,21 @@ +import { TwilioContext } from "../types/TwilioContext"; +import { DoorConfig } from "../types/DoorConfig"; +import { lambdaFastHttp } from "./LambdaUtils"; + +export async function getConfig(context: TwilioContext, buzzer: string): Promise { + return await fetch(context.DOORMAN_URL + `/api/door/info?buzzer=${buzzer}`) + .then(res => res.json()) + .catch(err => { + return undefined; + }); +} + +export function notifyDiscord(context: TwilioContext, msg: string[], u: string[], optionalJsonStr: string[]){ + return lambdaFastHttp(context.DOORMAN_URL + + `/api/door/notify?discordUser=${encodeURIComponent(JSON.stringify(u))}&msg=${encodeURIComponent(JSON.stringify(msg))}&json=${encodeURIComponent(JSON.stringify(optionalJsonStr))}`, + ).catch(err => console.log(err)) +} + +export async function notifyAllDiscord(context: TwilioContext, config: DoorConfig, msg: string, optionalJsonStr: string = "") { + return notifyDiscord(context, config.discordUsers.map(() => msg), config.discordUsers, config.discordUsers.map(() => optionalJsonStr)); +} \ No newline at end of file diff --git a/packages/doorman-client/src/utils/LambdaUtils.ts b/packages/doorman-client/src/utils/LambdaUtils.ts new file mode 100644 index 0000000..9712336 --- /dev/null +++ b/packages/doorman-client/src/utils/LambdaUtils.ts @@ -0,0 +1,23 @@ +import https from "https"; +import http from "http"; + +/** + * Helper function to do an HTTP request and just await transmission, but not await a response. + * ref: https://www.sensedeep.com/blog/posts/stories/lambda-fast-http.html + * @param url - the URL to do HTTP request to + * @returns promise signalling HTTP request has been transmitted + */ +export async function lambdaFastHttp(url: string): Promise { + return new Promise((resolve, reject) => { + let req; + + if (url.startsWith("https://")) { + req = https.request(url); + } else { + req = http.request(url); + } + req.end(() => { + resolve(); + }); + }); +} \ No newline at end of file diff --git a/packages/doorman-client/src/utils/TwimlUtils.ts b/packages/doorman-client/src/utils/TwimlUtils.ts new file mode 100644 index 0000000..9bfdc95 --- /dev/null +++ b/packages/doorman-client/src/utils/TwimlUtils.ts @@ -0,0 +1,29 @@ +import VoiceResponse from 'twilio/lib/twiml/VoiceResponse'; +import { DoorConfig } from '../types/DoorConfig'; + +export function doorOpenTwiml(config: DoorConfig): VoiceResponse { + const twiml = new Twilio.twiml.VoiceResponse(); + + twiml.play('https://buzzer-2439-prod.twil.io/buzzing_up_boosted.mp3'); + twiml.play({ digits: config.pressKey }); // configured in doorman what button to click and passed into this function + twiml.pause({ length: 1 }); + twiml.hangup(); + // @ts-ignore + return twiml; +} + +export function dialFallbackTwiml(config: DoorConfig): VoiceResponse { + const twiml = new Twilio.twiml.VoiceResponse(); + + let dial = twiml.dial({ + timeLimit: 20, + timeout: 20 + }); + + config.fallbackNumbers.forEach(number => { + dial.number(number); + }); + + // @ts-ignore + return twiml; +}