From 4ebb66cec581058340a0304ab3a3f20ded280946 Mon Sep 17 00:00:00 2001 From: ChrisChrome Date: Thu, 8 Jan 2026 22:09:00 -0700 Subject: [PATCH] Add sling messaging to buttons --- .gitignore | 2 ++ buttons.json | 34 +++++++++++++++--------- index.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 94 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 0ccb8df..8dbe5bc 100644 --- a/.gitignore +++ b/.gitignore @@ -139,3 +139,5 @@ dist vite.config.js.timestamp-* vite.config.ts.timestamp-* .vite/ + +slingtoken.txt \ No newline at end of file diff --git a/buttons.json b/buttons.json index c1b3b68..116c26a 100644 --- a/buttons.json +++ b/buttons.json @@ -16,7 +16,7 @@ "text": "6PM Announcement", "btnClass": "btn-secondary", "faIcon": "", - "name": "LivePage", + "name": "6PMAnnouncement", "doubleHeight": false, "context": { "context": "custom-emergency.9001.1", @@ -28,7 +28,7 @@ "text": "15 Min Warning", "btnClass": "btn-info", "faIcon": "", - "name": "LivePage", + "name": "CloseWarning", "doubleHeight": false, "context": { "context": "custom-emergency.9001.1", @@ -40,7 +40,7 @@ "text": "Pool Closed", "btnClass": "btn-warning", "faIcon": "", - "name": "LivePage", + "name": "PoolClosed", "doubleHeight": false, "context": { "context": "custom-emergency.9001.1", @@ -52,7 +52,7 @@ "text": "Health Code Violation", "btnClass": "btn-warning", "faIcon": "", - "name": "LivePage", + "name": "HealthCodeViolation", "doubleHeight": false, "context": { "context": "custom-emergency.9001.1", @@ -64,7 +64,7 @@ "text": "Snack Shop 15 Min Warning", "btnClass": "btn-info", "faIcon": "", - "name": "LivePage", + "name": "SnackShopWarning", "doubleHeight": false, "context": { "context": "custom-emergency.9001.1", @@ -77,7 +77,7 @@ "text": "Snack Shop Closed", "btnClass": "btn-warning", "faIcon": "", - "name": "LivePage", + "name": "SnackShopClosed", "doubleHeight": false, "context": { "context": "custom-emergency.9001.1", @@ -98,7 +98,9 @@ "context": "custom-emergency.9002.1", "timeout": 30000, "cid": "Emergency Announcement" - } + }, + "sling_chat_id": "17132563", + "sling_chat_message": "A user has initiated an Emergency Announcement." }, { "text": "Lightning Alert", @@ -116,7 +118,7 @@ { "text": "EAP Stand By", "btnClass": "btn-primary", - "name": "LightningAlert", + "name": "EAPStandBy", "faIcon": "fa-duotone fa-solid fa-pause", "doubleHeight": false, "context": { @@ -124,12 +126,14 @@ "timeout": 30000, "cid": "\"Weather Alert\" <9000>", "dial": "9000" - } + }, + "sling_chat_id": "17132563", + "sling_chat_message": "CUDA ALERT: EAP Activation - Stand By" }, { "text": "EAP Evacuate", "btnClass": "btn-danger", - "name": "LightningAlert", + "name": "EAPEvac", "faIcon": "fa-duotone fa-solid fa-person-to-door", "doubleHeight": false, "context": { @@ -137,12 +141,14 @@ "timeout": 30000, "cid": "\"Weather Alert\" <9000>", "dial": "9000" - } + }, + "sling_chat_id": "17132563", + "sling_chat_message": "CUDA ALERT: EAP Activation - Evacuate" }, { "text": "Fire Evac", "btnClass": "btn-danger", - "name": "LightningAlert", + "name": "FireEvac", "faIcon": "fa-duotone fa-solid fa-fire", "doubleHeight": false, "context": { @@ -150,7 +156,9 @@ "timeout": 30000, "cid": "\"Weather Alert\" <9000>", "dial": "9000" - } + }, + "sling_chat_id": "17132563", + "sling_chat_message": "Fire Evacuation" } ] diff --git a/index.js b/index.js index cf260b5..8b80f3f 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,16 @@ require('dotenv').config(); const phonesCfg = require('./phones.json'); const buttonsCfg = require('./buttons.json'); +const fs = require('fs'); +const path = require('path'); +if (!fs.existsSync(path.join(__dirname, 'slingtoken.txt'))) { + // Create empty file + fs.writeFileSync(path.join(__dirname, 'slingtoken.txt'), '', 'utf8'); + console.warn('slingtoken.txt file created. Please add your Sling token to this file to use sling messaging!'); +} + +var slingToken = fs.readFileSync(path.join(__dirname, 'slingtoken.txt'), 'utf8').trim(); const contexts = {}; // Generate contexts from buttonsCfg @@ -16,24 +25,47 @@ Object.keys(buttonsCfg).forEach(category => { context: button.context.context, timeout: button.context.timeout, cid: button.context.cid, - ...(button.context.dial && { dial: button.context.dial }) + ...(button.context.dial && { dial: button.context.dial }), + ...(button.sling_chat_id && { sling_chat_id: button.sling_chat_id }), + ...(button.sling_chat_message && { sling_chat_message: button.sling_chat_message }) }; } }); }); -console.log('Generated contexts:', contexts); +//console.log('Generated contexts:', contexts); function trigCall(pageType, phone) { // If contexts[pageType] does not exist, return an error if (!contexts[pageType]) { throw new Error(`Invalid page type: ${pageType}`); } - const { context, timeout, cid, dial } = contexts[pageType]; + const { context, timeout, cid, dial, sling_chat_id, sling_chat_message } = contexts[pageType]; const targetNumber = dial || phone; if (!targetNumber) { throw new Error(`Phone number is required for page type: ${pageType}`); } + + // Slink chat notification + if (sling_chat_id && sling_chat_message) { + fetch(`https://api.getsling.com/v1/conversations/${sling_chat_id}/messages`, { + method: 'POST', + headers: { + 'Authorization': `${slingToken}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'content': sling_chat_message + }) + }).then(res => res.json()).then(data => { + if (data && data.success) { + console.log('Sling chat message sent successfully.'); + } else { + console.error('Error sending Sling chat message:', data); + } + }); + } + originateCall(targetNumber, context, 0, timeout, cid).then((output) => { console.log(`Call originated: ${output}`); }).catch((error) => { @@ -68,6 +100,42 @@ function originateCall(number, context, delay, timeout, cid, variables = {}) { } +// Sling chat stuff; Check current token with GET https://api.getsling.com/v1/account/session, refresh with POST https://api.getsling.com/v1/account/session (GET NEW TOKEN FROM RETURN AUTHORIZATION HEADER) +async function slingAuthLoop() { + console.log("Start slingAuthLoop") + if (!slingToken) { + console.warn('No Sling token provided in environment variables.'); + return; + } + setTimeout(slingAuthLoop, 15 * 60 * 1000); // Refresh every 15 minutes + const sessionCheck = await fetch('https://api.getsling.com/v1/account/session', { + method: 'GET', + headers: { + 'Authorization': `${slingToken}` + } + }); + console.log(sessionCheck) + if (sessionCheck && sessionCheck.ok) { + console.log('Sling session is valid. Refreshing token...'); + const sessionRefresh = await fetch('https://api.getsling.com/v1/account/session', { + method: 'POST', + headers: { + 'Authorization': `${slingToken}` + } + }); + const newToken = sessionRefresh.headers.get('Authorization'); + if (newToken) { + slingToken = newToken; + fs.writeFileSync(path.join(__dirname, 'slingtoken.txt'), newToken, 'utf8'); + console.log('Sling token refreshed.'); + } + } else { + console.error('Sling session is invalid. Please get a new token manually!'); + } +} + +slingAuthLoop(); + const app = express(); const HOST = process.env.HOST || 'localhost'; const PORT = process.env.PORT || 3000;