diff --git a/docs/reference/notification.md b/docs/reference/notification.md index afa4e48990..2a509db1bd 100644 --- a/docs/reference/notification.md +++ b/docs/reference/notification.md @@ -209,3 +209,16 @@ Instructions on how to set up tokens can be found at [d-fischer.github.io/twitch | `TWITCH_ACCESS_TOKEN` | Twitch access token | | `TWITCH_REFRESH_TOKEN` | Twitch refresh token | | `TWITCH_CHANNEL` | Twitch channel | + +## StreamLabs + +Instructions on how to set up tokens can be found at [dev.streamlabs.com](https://dev.streamlabs.com/docs/register-your-application). +You don't need to submit your application for review, just whitelist yourself! + +| Environment variable | Description | +|:---:|---| +| `STREAMLABS_ACCESS_TOKEN` | StreamLabs access token | +| `STREAMLABS_TYPE` | StreamLabs alert type | +| `STREAMLABS_IMAGE`| Custom image to display. Leave it blank for default | +| `STREAMLABS_SOUND` | Custom image to play. Leave it blank for default | +| `STREAMLABS_DURATION` | StreamLabs alert duration (in milliseconds) | diff --git a/dotenv-example b/dotenv-example index 0c60ade9c0..326cf0717b 100644 --- a/dotenv-example +++ b/dotenv-example @@ -116,4 +116,9 @@ TWITTER_ACCESS_TOKEN_SECRET= TWITTER_CONSUMER_KEY= TWITTER_CONSUMER_SECRET= TWITTER_TWEET_TAGS= +STREAMLABS_ACCESS_TOKEN= +STREAMLABS_TYPE= +STREAMLABS_IMAGE= +STREAMLABS_SOUND= +STREAMLABS_DURATION= WEB_PORT= diff --git a/package-lock.json b/package-lock.json index d7d4996bdc..f7187d25b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3072,16 +3072,6 @@ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -4927,6 +4917,18 @@ "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } } }, "request-promise-core": { @@ -7806,6 +7808,16 @@ "uuid": "^3.3.2" }, "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", diff --git a/src/config.ts b/src/config.ts index a721f0db96..65b1236240 100644 --- a/src/config.ts +++ b/src/config.ts @@ -332,6 +332,13 @@ const notifications = { consumerSecret: envOrString(process.env.TWITTER_CONSUMER_SECRET), tweetTags: envOrString(process.env.TWITTER_TWEET_TAGS), }, + streamlabs: { + accessToken: envOrString(process.env.STREAMLABS_ACCESS_TOKEN), + type: envOrString(process.env.STREAMLABS_TYPE), + imageHref: envOrString(process.env.STREAMLABS_IMAGE), + soundHref: envOrString(process.env.STREAMLABS_SOUND), + duration: envOrNumber(process.env.STREAMLABS_DURATION), + }, }; const nvidia = { diff --git a/src/notification/notification.ts b/src/notification/notification.ts index 1e8550a538..ae68d6a945 100644 --- a/src/notification/notification.ts +++ b/src/notification/notification.ts @@ -16,6 +16,7 @@ import {sendTwilioMessage} from './twilio'; import {sendTwitchMessage} from './twitch'; import {updateRedis} from './redis'; import {activateSmartthingsSwitch} from './smartthings'; +import {sendStreamLabsAlert} from './streamlabs'; export function sendNotification(link: Link, store: Store) { // Priority @@ -37,4 +38,5 @@ export function sendNotification(link: Link, store: Store) { sendTwilioMessage(link, store); sendTwitchMessage(link, store); updateRedis(link, store); + sendStreamLabsAlert(link, store); } diff --git a/src/notification/streamlabs.ts b/src/notification/streamlabs.ts new file mode 100644 index 0000000000..3ebe2b8ff0 --- /dev/null +++ b/src/notification/streamlabs.ts @@ -0,0 +1,42 @@ +import {Link, Store} from '../store/model'; +import {Print, logger} from '../logger'; +import {config} from '../config'; +import {URLSearchParams} from 'url'; +import fetch from 'node-fetch'; + +const {streamlabs} = config.notifications; +let requestParams: URLSearchParams; + +if (streamlabs.accessToken && streamlabs.type) { + requestParams = new URLSearchParams(); + requestParams.append('access_token', streamlabs.accessToken); + requestParams.append('type', streamlabs.type); + requestParams.append('image_href', streamlabs.imageHref); + requestParams.append('sound_href', streamlabs.soundHref); + requestParams.append('duration', streamlabs.duration.toString()); +} + +export function sendStreamLabsAlert(link: Link, store: Store) { + if (requestParams) { + logger.debug('↗ sending StreamLabs alert'); + + (async () => { + const message = `${Print.inStock(link, store)}`; + requestParams.set('message', message); + + try { + const response = await fetch('https://streamlabs.com/api/v1.0/alerts', { + method: 'POST', + body: requestParams, + }); + + const json = await response.json(); + if (!json.success) throw Error(JSON.stringify(json)); + + logger.info('✔ StreamLabs alert sent'); + } catch (error: unknown) { + logger.error("✖ couldn't send StreamLabs alert", error); + } + })(); + } +}