migrate buzzer client
All checks were successful
Build and push Doorman UI / API / docker (push) Successful in 49s
All checks were successful
Build and push Doorman UI / API / docker (push) Successful in 49s
This commit is contained in:
parent
92e34d89c0
commit
e37df33c08
@ -11,9 +11,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "packages/serverless"
|
"path": "packages/serverless"
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../../sideprojects/2023/smart-door-buzzer-twilio-functions"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|||||||
@ -8,7 +8,8 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare-client-serverless": "bun --filter 'doorman-client' build && rm -rf packages/serverless/assets/* && mkdir -p packages/serverless/assets/assets && cp -fr packages/client/dist/* packages/serverless/assets/ && cp -f packages/serverless/assets/index.html packages/serverless/assets/assets/index.html",
|
"prepare-client-serverless": "bun --filter 'doorman-client' build && rm -rf packages/serverless/assets/* && mkdir -p packages/serverless/assets/assets && cp -fr packages/client/dist/* packages/serverless/assets/ && cp -f packages/serverless/assets/index.html packages/serverless/assets/assets/index.html",
|
||||||
"deploy-serverless": "bun run prepare-client-serverless && bun --filter 'serverless' deploy"
|
"deploy-serverless": "bun run prepare-client-serverless && bun --filter 'serverless' deploy",
|
||||||
|
"deploy-buzzer-client": "bun --filter 'buzzer-client' deploy"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
|
|||||||
6
packages/buzzer-client/.env.example
Normal file
6
packages/buzzer-client/.env.example
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
DOORMAN_URL=https://doorman.chromart.cc
|
||||||
|
NTFY_DOMAIN=ntfy.chromart.cc
|
||||||
|
|
||||||
|
# twilio auth
|
||||||
|
ACCOUNT_SID=
|
||||||
|
AUTH_TOKEN=
|
||||||
133
packages/buzzer-client/.gitignore
vendored
Normal file
133
packages/buzzer-client/.gitignore
vendored
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
# Twilio Serverless
|
||||||
|
.twiliodeployinfo
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
1
packages/buzzer-client/.nvmrc
Normal file
1
packages/buzzer-client/.nvmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
18
|
||||||
42
packages/buzzer-client/.twilioserverlessrc
Normal file
42
packages/buzzer-client/.twilioserverlessrc
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"commands": {},
|
||||||
|
"environments": {},
|
||||||
|
"projects": {},
|
||||||
|
// "assets": true /* Upload assets. Can be turned off with --no-assets */,
|
||||||
|
// "assetsFolder": null /* Specific folder name to be used for static assets */,
|
||||||
|
// "buildSid": null /* An existing Build SID to deploy to the new environment */,
|
||||||
|
// "createEnvironment": false /* Creates environment if it couldn't find it. */,
|
||||||
|
// "cwd": null /* Sets the directory of your existing Serverless project. Defaults to current directory */,
|
||||||
|
// "detailedLogs": false /* Toggles detailed request logging by showing request body and query params */,
|
||||||
|
// "edge": null /* Twilio API Region */,
|
||||||
|
// "env": null /* Path to .env file for environment variables that should be installed */,
|
||||||
|
// "environment": "dev" /* The environment name (domain suffix) you want to use for your deployment. Alternatively you can specify an environment SID starting with ZE. */,
|
||||||
|
// "extendedOutput": false /* Show an extended set of properties on the output */,
|
||||||
|
// "force": false /* Will run deployment in force mode. Can be dangerous. */,
|
||||||
|
// "forkProcess": true /* Disable forking function processes to emulate production environment */,
|
||||||
|
// "functionSid": null /* Specific Function SID to retrieve logs for */,
|
||||||
|
// "functions": true /* Upload functions. Can be turned off with --no-functions */,
|
||||||
|
// "functionsFolder": null /* Specific folder name to be used for static functions */,
|
||||||
|
// "inspect": null /* Enables Node.js debugging protocol */,
|
||||||
|
// "inspectBrk": null /* Enables Node.js debugging protocol, stops execution until debugger is attached */,
|
||||||
|
// "legacyMode": false /* Enables legacy mode, it will prefix your asset paths with /assets */,
|
||||||
|
// "live": true /* Always serve from the current functions (no caching) */,
|
||||||
|
// "loadLocalEnv": false /* Includes the local environment variables */,
|
||||||
|
// "loadSystemEnv": false /* Uses system environment variables as fallback for variables specified in your .env file. Needs to be used with --env explicitly specified. */,
|
||||||
|
// "logCacheSize": null /* Tailing the log endpoint will cache previously seen entries to avoid duplicates. The cache is topped at a maximum of 1000 by default. This option can change that. */,
|
||||||
|
// "logLevel": "info" /* Level of logging messages. */,
|
||||||
|
// "logs": true /* Toggles request logging */,
|
||||||
|
// "ngrok": null /* Uses ngrok to create a public url. Pass a string to set the subdomain (requires a paid-for ngrok account). */,
|
||||||
|
// "outputFormat": "" /* Output the results in a different format */,
|
||||||
|
// "overrideExistingProject": false /* Deploys Serverless project to existing service if a naming conflict has been found. */,
|
||||||
|
// "port": "3000" /* Override default port of 3000 */,
|
||||||
|
// "production": false /* Promote build to the production environment (no domain suffix). Overrides environment flag */,
|
||||||
|
// "properties": null /* Specify the output properties you want to see. Works best on single types */,
|
||||||
|
// "region": null /* Twilio API Region */,
|
||||||
|
"runtime": "node18" /* The version of Node.js to deploy the build to. (node18) */,
|
||||||
|
// "serviceName": null /* Overrides the name of the Serverless project. Default: the name field in your package.json */,
|
||||||
|
// "serviceSid": null /* SID of the Twilio Serverless Service to deploy to */,
|
||||||
|
// "sourceEnvironment": null /* SID or suffix of an existing environment you want to deploy from. */,
|
||||||
|
// "tail": false /* Continuously stream the logs */,
|
||||||
|
// "template": null /* undefined */,
|
||||||
|
}
|
||||||
3
packages/buzzer-client/.vscode/settings.json
vendored
Normal file
3
packages/buzzer-client/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"dotenv.enableAutocloaking": false
|
||||||
|
}
|
||||||
29
packages/buzzer-client/LICENSE
Normal file
29
packages/buzzer-client/LICENSE
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2018, John Lian
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
51
packages/buzzer-client/README.md
Normal file
51
packages/buzzer-client/README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Make your apartment buzzer smart with Twilio Functions
|
||||||
|
|
||||||
|
I wanted to make my apartment buzzer acessible without a physical key, so I created these Twilio Functions to make my buzzer smarter. Features include:
|
||||||
|
|
||||||
|
* Voice password (make your friends shout stupid things in public to get into your building!)
|
||||||
|
* PIN password, a classic
|
||||||
|
* No obvious weirdness or extra waiting for first-time guests or delivery people
|
||||||
|
* Notify multiple people until someone picks up the phone - as long as one of the roommates pick up, you won't miss your package
|
||||||
|
|
||||||
|
I kind of went overboard I think, given my original goal. But this was actually *really* easy to develop and set up. And also really cheap.
|
||||||
|
|
||||||
|
## How to set this up
|
||||||
|
|
||||||
|
1. You could `git clone https://github.com/jlian/smart-door-buzzer-twilio-functions.git`, but it's not critical.
|
||||||
|
2. Get a Twilio account and valid Twilio number.
|
||||||
|
3. Go to https://www.twilio.com/console/runtime/functions/manage and hit **+**.
|
||||||
|
4. Add each of the 4 `.js` files into its own function with names that you'd remember.
|
||||||
|
5. Go to https://www.twilio.com/console/runtime/functions/configure and configure the environment variables:
|
||||||
|
* `TWILIO_PHONE` with the Twilio number you bought
|
||||||
|
* `PASSPHRASE` for voice password
|
||||||
|
* `PASSCODE` for PIN
|
||||||
|
* `JOHN_PHONE` and others for your cellphone number
|
||||||
|
5. Go to https://www.twilio.com/console/phone-numbers/incoming and select the phone number you bought earlier.
|
||||||
|
6. Scroll to where it says **A call comes in**, select **Function**, and then the function that corresponds to `buzzer-activated.js`.
|
||||||
|
7. Contact your HOA to make the Twilio number your buzzer number - this might be the hardest step.
|
||||||
|
|
||||||
|
## How this works
|
||||||
|
|
||||||
|
[Twilio Functions](https://www.twilio.com/functions) is pretty sweet. It's completely serverless so you don't need a VM or computer to keep running an app. It's perfect for something small scale like your apartment buzzer. The flow of this program goes like this:
|
||||||
|
|
||||||
|
1. A call comes to the Twilio phone number, `buzzer-activated.js` runs.
|
||||||
|
1. The [Gather](https://www.twilio.com/docs/api/twiml/gather) verb is used to get either a voice password or a 4-digit PIN.
|
||||||
|
1. If correct, `door-open.js` dials a `9` to the buzzer (my building's buzzer code, yours may be different), which opens the door.
|
||||||
|
1. If incorrect, `call-residents.js` calls all the residents until someone picks up and manually dial `9` to open the door.
|
||||||
|
1. When everything is finished, `text-me.js` texts a specified number with info on what happened.
|
||||||
|
|
||||||
|
## How much this costs
|
||||||
|
|
||||||
|
According to Twilio docs, collecting speech is charged at $.02 per 15 seconds. A Twilio number costs $1/month. Looking at my own billing dashboard, it never exceeds $2/month - pretty reasonable.
|
||||||
|
|
||||||
|
## Twilio tooling
|
||||||
|
Using the twilio serverless toolbox you can locally run the functions and test.
|
||||||
|
|
||||||
|
```
|
||||||
|
bun run start
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy to your account with
|
||||||
|
```
|
||||||
|
bun run deploy
|
||||||
|
```
|
||||||
Binary file not shown.
Binary file not shown.
BIN
packages/buzzer-client/assets/buzzing_up_boosted.protected.mp3
Normal file
BIN
packages/buzzer-client/assets/buzzing_up_boosted.protected.mp3
Normal file
Binary file not shown.
BIN
packages/buzzer-client/bun.lockb
Executable file
BIN
packages/buzzer-client/bun.lockb
Executable file
Binary file not shown.
63
packages/buzzer-client/functions/buzzer-activated.js
Normal file
63
packages/buzzer-client/functions/buzzer-activated.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Simple call box routine
|
||||||
|
*
|
||||||
|
* This function is meant for the apartment building callbox
|
||||||
|
* It gives the user a couple of seconds to produce the password
|
||||||
|
* Then dials all the residents to grant manual entry
|
||||||
|
*/
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
|
exports.handler = async function(context, event, callback) {
|
||||||
|
|
||||||
|
let twiml = new Twilio.twiml.VoiceResponse();
|
||||||
|
|
||||||
|
let config = await fetch(context.DOORMAN_URL + `/api/door/info?buzzer=${event.From}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.catch(err => {
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
// reject the call if this is not configured
|
||||||
|
if (!config || !config.door) {
|
||||||
|
twiml.reject();
|
||||||
|
callback(null, twiml);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const promise = new Promise(() => null, () => null);
|
||||||
|
|
||||||
|
// poll Doorman, to see if we should unlock
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
fetch(context.DOORMAN_URL + `/api/door/status?door=${config.door}`)
|
||||||
|
.then(async res => {
|
||||||
|
// handle the case where doorman is explictly rejecting the buzzer
|
||||||
|
if (res.status === 410) {
|
||||||
|
clearInterval(interval);
|
||||||
|
twiml.redirect('/text-me?method=doorman-time-lock');
|
||||||
|
callback(null, twiml);
|
||||||
|
promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
// we got the successful unlock
|
||||||
|
else if (res.status === 200) {
|
||||||
|
clearInterval(interval);
|
||||||
|
|
||||||
|
const body = await res.json();
|
||||||
|
twiml.redirect(`/door-open?fingerprint=${encodeURIComponent(JSON.stringify(body))}&pressKey=${config.pressKey}`);
|
||||||
|
callback(null, twiml);
|
||||||
|
promise.resolve();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// redirect to call after 6s
|
||||||
|
setTimeout(() => {
|
||||||
|
twiml.redirect(`/call-residents?numbers=${encodeURIComponent(config.fallbackNumbers)}`);
|
||||||
|
callback(null, twiml);
|
||||||
|
promise.resolve();
|
||||||
|
}, 6000);
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
return callback(null, twiml);
|
||||||
|
};
|
||||||
18
packages/buzzer-client/functions/call-residents.js
Normal file
18
packages/buzzer-client/functions/call-residents.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Fallback behavior, if the code is wrong or unspecified, then we should dial the fallback numbers
|
||||||
|
*/
|
||||||
|
exports.handler = function(context, event, callback) {
|
||||||
|
let twiml = new Twilio.twiml.VoiceResponse();
|
||||||
|
|
||||||
|
// numbers are passed in
|
||||||
|
let numbers = event.numbers.split(',');
|
||||||
|
|
||||||
|
// If no valid answer after timeout, dial all residents until someone picks up
|
||||||
|
let dial = twiml.dial({action: '/text-me?Method=call', timeLimit: 20, timeout: 20});
|
||||||
|
|
||||||
|
numbers.forEach(number => {
|
||||||
|
dial.number(number);
|
||||||
|
});
|
||||||
|
|
||||||
|
return callback(null, twiml);
|
||||||
|
}
|
||||||
15
packages/buzzer-client/functions/door-open.js
Normal file
15
packages/buzzer-client/functions/door-open.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Automatically open the door
|
||||||
|
*/
|
||||||
|
exports.handler = function(context, event, callback) {
|
||||||
|
let twiml = new Twilio.twiml.VoiceResponse();
|
||||||
|
|
||||||
|
let passAlong = `fingerprint=${encodeURIComponent(event.fingerprint)}`;
|
||||||
|
|
||||||
|
twiml.play('https://smart-door-buzzer-3172.twil.io/buzzing_up_boosted.mp3');
|
||||||
|
twiml.play({ digits: event.pressKey }); // configured in doorman what button to click and passed into this function
|
||||||
|
twiml.pause({ length: 1 });
|
||||||
|
twiml.redirect(`/text-me?Method=doorman&${passAlong}`);
|
||||||
|
|
||||||
|
callback(null, twiml);
|
||||||
|
};
|
||||||
30
packages/buzzer-client/functions/text-me.js
Normal file
30
packages/buzzer-client/functions/text-me.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Send a NTFY message with an update of what happened. If the password was used or not...
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
|
exports.handler = function(context, event, callback) {
|
||||||
|
let twiml = new Twilio.twiml.VoiceResponse();
|
||||||
|
|
||||||
|
let bodyText;
|
||||||
|
|
||||||
|
if (event.Method == 'doorman') {
|
||||||
|
bodyText = 'Doorman buzzed someone up!';
|
||||||
|
const fingerprint = JSON.parse(event.fingerprint);
|
||||||
|
bodyText += `\n\n${JSON.stringify(fingerprint, null, 4)}`;
|
||||||
|
} else if (event.Method == 'doorman-time-lock') {
|
||||||
|
bodyText = 'Doorman rejected a buzzer call due to time restriction';
|
||||||
|
} else if (event.Method == 'call') {
|
||||||
|
bodyText = 'Somebody buzzed the door and it dialed through to a phone.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// send webhook to ntfy
|
||||||
|
fetch(`https://${context.NTFY_DOMAIN}/buzzer`, {
|
||||||
|
method: "POST",
|
||||||
|
body: bodyText,
|
||||||
|
})
|
||||||
|
.then(res => callback(null, twiml))
|
||||||
|
// even if we error then we should just end the call normally
|
||||||
|
.catch(err => callback(null, twiml));
|
||||||
|
};
|
||||||
9678
packages/buzzer-client/package-lock.json
generated
Normal file
9678
packages/buzzer-client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
packages/buzzer-client/package.json
Normal file
27
packages/buzzer-client/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "buzzer-client",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "twilio-run --live",
|
||||||
|
"deploy": "twilio-run deploy --load-system-env --env .env.example --service-name buzzer --environment=prod --override-existing-project"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@twilio-labs/serverless-runtime-types": "^3.0.0",
|
||||||
|
"@twilio/runtime-handler": "1.3.0",
|
||||||
|
"node-fetch": "2",
|
||||||
|
"twilio": "^3.56"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"twilio-run": "^3.5.4",
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user