ntfy
Version:
Send notifications over ntfy.sh.
144 lines (143 loc) • 4.48 kB
JavaScript
import { URL } from 'node:url';
import { promises as fs } from 'node:fs';
import axios, { AxiosHeaders } from 'axios';
const defaultServerURL = 'https://ntfy.sh';
export class NtfyClient {
constructor(config) {
this.config = config;
}
publish(config) {
return publish({
server: defaultServerURL,
...this.config,
...config,
});
}
}
function buildBroadcastActionString(action) {
let str = `${action.type}, ${action.label}`;
if (action.clear) {
str += ', clear=true';
}
if (action.extras && Object.keys(action.extras).length) {
str += `, ${Object.entries(action.extras)
.map(([key, value]) => `extras.${key}=${value}`)
.join(', ')}`;
}
if (action.intent) {
str += `, intent=${action.intent}`;
}
return str;
}
function ConfigHasAttachment(config) {
return !!config.fileAttachment;
}
function ConfigHasMessage(config) {
return !!config.message;
}
function buildHTTPActionString(action) {
let str = `${action.type}, ${action.label}, ${action.url}`;
if (action.method) {
str += `, method=${action.method.toUpperCase()}`;
}
if (action.clear) {
str += ', clear=true';
}
if (action.headers && Object.keys(action.headers).length) {
str += `, ${Object.entries(action.headers)
.map(([key, value]) => `headers.${key}=${value}`)
.join(', ')}`;
}
if (action.body) {
str += `, ${action.body}`;
}
return str;
}
function buildViewActionString(action) {
let str = `${action.type}, ${action.label}, ${action.url}`;
if (action.clear) {
str += ', clear=true';
}
return str;
}
export async function publish(config) {
const axiosConfig = { headers: new AxiosHeaders() };
let postData;
if (config.actions && config.actions.length) {
axiosConfig.headers.set('X-Actions', config.actions
.map(action => {
switch (action.type) {
case 'broadcast': {
return buildBroadcastActionString(action);
}
case 'http': {
return buildHTTPActionString(action);
}
case 'view': {
return buildViewActionString(action);
}
default: {
return '';
}
}
})
.join('; '));
}
if (config.authorization) {
axiosConfig.withCredentials = true;
if (typeof config.authorization === 'string') {
axiosConfig.headers.Authorization = config.authorization;
}
else {
axiosConfig.auth = config.authorization;
}
}
if (config.delay) {
axiosConfig.headers.set('X-Delay', config.delay);
}
if (config.disableCache) {
axiosConfig.headers.set('X-Cache', 'no');
}
if (config.disableFirebase) {
axiosConfig.headers.set('X-Firebase', 'no');
}
if (config.emailAddress) {
axiosConfig.headers.set('X-Email', config.emailAddress);
}
if (ConfigHasMessage(config) && config.fileURL) {
if (typeof config.fileURL === 'string') {
axiosConfig.headers.set('X-Attach', config.fileURL);
}
axiosConfig.headers.set('X-Attach', config.fileURL.url);
axiosConfig.headers.set('X-Filename', config.fileURL.filename);
}
if (ConfigHasAttachment(config)) {
try {
postData = await fs.readFile(config.fileAttachment);
}
catch (error) {
console.error('Error while reading file:', error.message);
}
}
else if (config.message) {
postData = config.message;
}
else {
throw new Error('No message or file attachment specified');
}
if (config.iconURL) {
axiosConfig.headers.set('X-Icon', config.iconURL);
}
if (config.priority) {
axiosConfig.headers.set('X-Priority', config.priority);
}
if (config.tags && config.tags.length) {
axiosConfig.headers.set('X-Tags', typeof config.tags === 'string' ? config.tags : config.tags.join(','));
}
if (config.title) {
axiosConfig.headers.set('X-Title', config.title);
}
const url = new URL(config.topic, config.server || defaultServerURL);
const { data } = await axios.post(url.href, postData, axiosConfig);
return data;
}