diff --git a/docs/reference/notification.md b/docs/reference/notification.md index 88b4c4e0b0..1cf43649c6 100644 --- a/docs/reference/notification.md +++ b/docs/reference/notification.md @@ -262,3 +262,16 @@ Copy the API key generated with the service activation. | `FREEMOBILE_API_KEY` | API key generated with your notification option activation | Note: here you do not need to give neither your password nor phone number. + +## NTFY.sh + +You can send notifications using NTFY.sh, which supports various features like priority, tags, and action buttons. +Use the free service at [ntfy.sh](https://ntfy.sh) or host your own instance. + +| Environment variable | Description | +|:---:|---| +| `NTFY_URL` | ntfy server URL, e.g. `https://ntfy.sh` | +| `NTFY_TOPIC` | Topic to publish alerts to | +| `NTFY_PRIORITY` | Message priority, e.g. max/high/default/low/min, I recommend to use the numbers instead of the string values for the priority. https://docs.ntfy.sh/publish/?h=priority#message-priority | +| `NTFY_TITLE` | Title of the message | +| `NTFY_ACCESS_TOKEN` | Access token for authentication. https://docs.ntfy.sh/config/#access-tokens | diff --git a/dotenv-example b/dotenv-example index 12bb3f8b4e..376ffb1733 100644 --- a/dotenv-example +++ b/dotenv-example @@ -163,4 +163,9 @@ STREAMLABS_SOUND= STREAMLABS_DURATION= FREEMOBILE_ID= FREEMOBILE_API_KEY= +NTFY_TOPIC= +NTFY_PRIORITY= +NTFY_TITLE= +NTFY_ACCESS_TOKEN= +NTFY_URL= WEB_PORT= diff --git a/src/config.ts b/src/config.ts index adc106b71d..7709426bcc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -379,6 +379,13 @@ const notifications = { id: envOrString(process.env.FREEMOBILE_ID), apiKey: envOrString(process.env.FREEMOBILE_API_KEY), }, + ntfy: { + url: envOrString(process.env.NTFY_URL, 'https://ntfy.sh'), + topic: envOrString(process.env.NTFY_TOPIC), + priority: envOrString(process.env.NTFY_PRIORITY), + title: envOrString(process.env.NTFY_TITLE), + accessToken: envOrString(process.env.NTFY_ACCESS_TOKEN), + }, }; const nvidia = { diff --git a/src/messaging/notification.ts b/src/messaging/notification.ts index ac4de7a355..521cf34c22 100644 --- a/src/messaging/notification.ts +++ b/src/messaging/notification.ts @@ -20,10 +20,12 @@ import {updateRedis} from './redis'; import {sendStreamLabsAlert} from './streamlabs'; import {sendFreeMobileAlert} from './freemobile'; import {DMPayload} from '.'; +import {sendNtfyAlert} from './ntfy'; export function sendNotification(link: Link, store: Store) { // Priority playSound(); + sendNtfyAlert(link, store); sendDiscordMessage(link, store); sendDesktopNotification(link, store); sendEmail(link, store); diff --git a/src/messaging/ntfy.ts b/src/messaging/ntfy.ts new file mode 100644 index 0000000000..f10f572741 --- /dev/null +++ b/src/messaging/ntfy.ts @@ -0,0 +1,55 @@ +import {Link, Store} from '../store/model'; +import {Print, logger} from '../logger'; +import {config} from '../config'; +import fetch from 'node-fetch'; + +const {ntfy} = config.notifications; + +export function sendNtfyAlert(link: Link, store: Store) { + if (ntfy.topic) { + logger.debug('↗ sending ntfy alert'); + + (async () => { + const message = `${Print.inStock(link, store)}`; + const headers: Record<string, string> = {}; + + if (ntfy.priority) headers['Priority'] = ntfy.priority; + headers[ + 'Tags' + ] = `${store.name},${link.model},${link.series},${link.brand}`; + if (ntfy.title) headers['Title'] = ntfy.title; + if (ntfy.accessToken) + headers['Authorization'] = `Bearer ${ntfy.accessToken}`; + + const body = { + topic: ntfy.topic, + message, + actions: [ + { + action: 'view', + label: 'Add to cart', + url: link.cartUrl ?? link.url, + }, + ], + }; + + try { + const response = await fetch(ntfy.url, { + method: 'POST', + body: JSON.stringify(body), + headers: { + ...headers, + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) + throw new Error(`Failed to send ntfy alert: ${response.statusText}`); + + logger.info('✔ ntfy alert sent'); + } catch (error: unknown) { + logger.error("✖ couldn't send ntfy alert", error); + } + })(); + } +}