discord notifications
All checks were successful
Build and push Doorman UI / API / docker (push) Successful in 1m27s
All checks were successful
Build and push Doorman UI / API / docker (push) Successful in 1m27s
This commit is contained in:
parent
f4ac30260c
commit
4cad1fbe2b
@ -25,6 +25,7 @@ jobs:
|
||||
AUTH_TOKEN: ${{ secrets.TWILIO_AUTH_TOKEN }}
|
||||
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }}
|
||||
|
||||
- name: Deploy Doorman Buzzer Client
|
||||
run: bun run deploy-buzzer-client
|
||||
|
||||
@ -4,4 +4,7 @@ AUTH_TOKEN=
|
||||
|
||||
# aws stuff
|
||||
AWS_ACCESS_KEY=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
|
||||
# discord notifs
|
||||
DISCORD_BOT_TOKEN=
|
||||
@ -49,6 +49,7 @@ exports.handler = async function(context, event, callback) {
|
||||
door,
|
||||
fallbackNumbers: config.Item.fallbackNumbers.SS,
|
||||
pressKey: config.Item.pressKey.S,
|
||||
discordUsers: config.Item?.discordUsers?.SS || [],
|
||||
});
|
||||
} else {
|
||||
await client.send(ddb.getLockStatusCommand(door))
|
||||
|
||||
15
packages/doorman-api/functions/api/door/notify.js
Normal file
15
packages/doorman-api/functions/api/door/notify.js
Normal file
@ -0,0 +1,15 @@
|
||||
exports.handler = async function(context, event, callback) {
|
||||
const response = new Twilio.Response();
|
||||
|
||||
const discordPath = Runtime.getFunctions()['common/discord'].path;
|
||||
const discord = require(discordPath);
|
||||
|
||||
// user must be in "Doorman" server
|
||||
await discord.sendMessageToUser(context,
|
||||
event.discordUser,
|
||||
event.msg,
|
||||
)
|
||||
.catch((e) => console.error(e));
|
||||
|
||||
return callback(null, response);
|
||||
};
|
||||
39
packages/doorman-api/functions/common/discord.private.js
Normal file
39
packages/doorman-api/functions/common/discord.private.js
Normal file
@ -0,0 +1,39 @@
|
||||
// reusing from refbounty:
|
||||
// https://gitea.chromart.cc/martin/refbounty/src/commit/1f40d7870a530fe7e7acbc3b901024092c9fb90a/packages/server/src/connectors/DiscordConnector.ts
|
||||
// https://gitea.chromart.cc/martin/refbounty/src/commit/1f40d7870a530fe7e7acbc3b901024092c9fb90a/packages/server/src/utils/DiscordUtils.ts
|
||||
|
||||
const { Client, GatewayIntentBits } = require("discord.js");
|
||||
|
||||
let conn;
|
||||
|
||||
exports.getDiscordClient = async (context) => {
|
||||
if (!conn) {
|
||||
const client = new Client({
|
||||
intents: [GatewayIntentBits.DirectMessages],
|
||||
rest: {
|
||||
timeout: 60_000, // 1 minute
|
||||
}
|
||||
});
|
||||
|
||||
await client.login(context.DISCORD_BOT_TOKEN);
|
||||
conn = client;
|
||||
}
|
||||
|
||||
return conn;
|
||||
};
|
||||
|
||||
|
||||
exports.sendMessageToUser = async (
|
||||
context,
|
||||
userId,
|
||||
msg,
|
||||
) => {
|
||||
try {
|
||||
const client = await exports.getDiscordClient(context);
|
||||
const user = await client.users.fetch(userId);
|
||||
return user.send(msg);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.log(`Failed to send msg to ${userId}`);
|
||||
}
|
||||
}
|
||||
@ -4,12 +4,13 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "twilio-run",
|
||||
"start": "twilio-run --live --port 8080",
|
||||
"deploy": "twilio-run deploy --load-system-env --env .env.example --service-name doorman --environment=prod --override-existing-project"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-dynamodb": "^3.609.0",
|
||||
"@twilio/runtime-handler": "1.3.0",
|
||||
"discord.js": "^14.16.3",
|
||||
"twilio": "^3.56"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
DOORMAN_URL=https://doorman.chromart.cc
|
||||
NTFY_DOMAIN=ntfy.chromart.cc
|
||||
|
||||
# twilio auth
|
||||
ACCOUNT_SID=
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
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}`)
|
||||
@ -24,7 +23,7 @@ exports.handler = async function(context, event, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
const promise = new Promise(() => null, () => null);
|
||||
let configQuery = `config=${encodeURIComponent(JSON.stringify(config))}`;
|
||||
|
||||
// poll Doorman, to see if we should unlock
|
||||
const interval = setInterval(() => {
|
||||
@ -33,19 +32,16 @@ exports.handler = async function(context, event, callback) {
|
||||
// handle the case where doorman is explictly rejecting the buzzer
|
||||
if (res.status === 410) {
|
||||
clearInterval(interval);
|
||||
twiml.redirect('/text-me?method=doorman-time-lock');
|
||||
twiml.redirect(`/text-me?method=doorman-time-lock&${configQuery}`);
|
||||
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}`);
|
||||
twiml.redirect(`/door-open?fingerprint=${encodeURIComponent(JSON.stringify(body))}&${configQuery}`);
|
||||
callback(null, twiml);
|
||||
promise.resolve();
|
||||
}
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
@ -53,11 +49,9 @@ exports.handler = async function(context, event, callback) {
|
||||
|
||||
// redirect to call after 6s
|
||||
setTimeout(() => {
|
||||
twiml.redirect(`/call-residents?numbers=${encodeURIComponent(config.fallbackNumbers)}`);
|
||||
twiml.redirect(`/call-residents?${configQuery}`);
|
||||
callback(null, twiml);
|
||||
promise.resolve();
|
||||
}, 6000);
|
||||
|
||||
await promise;
|
||||
return callback(null, twiml);
|
||||
// return callback(null, twiml);
|
||||
};
|
||||
@ -4,13 +4,18 @@
|
||||
exports.handler = function(context, event, callback) {
|
||||
let twiml = new Twilio.twiml.VoiceResponse();
|
||||
|
||||
// numbers are passed in
|
||||
let numbers = event.numbers.split(',');
|
||||
let config = JSON.parse(event.config);
|
||||
console.log(config);
|
||||
let configQuery = `config=${encodeURIComponent(JSON.stringify(config))}`;
|
||||
|
||||
// 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});
|
||||
let dial = twiml.dial({
|
||||
action: `/text-me?Method=call&${configQuery}`,
|
||||
timeLimit: 20,
|
||||
timeout: 20
|
||||
});
|
||||
|
||||
numbers.forEach(number => {
|
||||
config.fallbackNumbers.forEach(number => {
|
||||
dial.number(number);
|
||||
});
|
||||
|
||||
|
||||
@ -4,10 +4,13 @@
|
||||
exports.handler = function(context, event, callback) {
|
||||
let twiml = new Twilio.twiml.VoiceResponse();
|
||||
|
||||
let passAlong = `fingerprint=${encodeURIComponent(event.fingerprint)}`;
|
||||
let config = JSON.parse(event.config);
|
||||
let configQuery = `config=${encodeURIComponent(JSON.stringify(config))}`;
|
||||
|
||||
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
|
||||
let passAlong = `fingerprint=${encodeURIComponent(event.fingerprint)}&${configQuery}`;
|
||||
|
||||
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.redirect(`/text-me?Method=doorman&${passAlong}`);
|
||||
|
||||
|
||||
@ -4,27 +4,31 @@
|
||||
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
exports.handler = function(context, event, callback) {
|
||||
exports.handler = async function(context, event, callback) {
|
||||
let twiml = new Twilio.twiml.VoiceResponse();
|
||||
|
||||
// should be a list of strings representing user ids in discord
|
||||
let discordUsers = JSON.parse(event.config).discordUsers || [];
|
||||
|
||||
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)}`;
|
||||
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));
|
||||
let promises = discordUsers.map((u) =>
|
||||
fetch(context.DOORMAN_URL + `/api/door/notify?discordUser=${u}&msg=${bodyText}`)
|
||||
.catch(err => console.log(err))
|
||||
);
|
||||
|
||||
await Promise.all(promises)
|
||||
.catch (e => console.log(e));
|
||||
|
||||
return callback(null, twiml);
|
||||
};
|
||||
@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "twilio-run --live",
|
||||
"start": "twilio-run --live --port 4500",
|
||||
"deploy": "twilio-run deploy --load-system-env --env .env.example --service-name buzzer --environment=prod --override-existing-project"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user