const axios = require("axios"); const {R} = require("redbean-node"); const FormData = require('form-data'); const nodemailer = require("nodemailer"); const child_process = require("child_process"); class Notification { /** * * @param notification * @param msg * @param monitorJSON * @param heartbeatJSON * @returns {Promise} Successful msg * Throw Error with fail msg */ static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully. "; if (notification.type === "telegram") { try { await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, { params: { chat_id: notification.telegramChatID, text: msg, } }) return okMsg; } catch (error) { let msg = (error.response.data.description) ? error.response.data.description : "Error without description" throw new Error(msg) } } else if (notification.type === "gotify") { try { if (notification.gotifyserverurl && notification.gotifyserverurl.endsWith("/")) { notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1); } await axios.post(`${notification.gotifyserverurl}/message?token=${notification.gotifyapplicationToken}`, { "message": msg, "priority": notification.gotifyPriority || 8, "title": "Uptime-Kuma" }) return okMsg; } catch (error) { throwGeneralAxiosError(error) } } else if (notification.type === "webhook") { try { let data = { heartbeat: heartbeatJSON, monitor: monitorJSON, msg, }; let finalData; let config = {}; if (notification.webhookContentType === "form-data") { finalData = new FormData(); finalData.append('data', JSON.stringify(data)); config = { headers: finalData.getHeaders() } } else { finalData = data; } await axios.post(notification.webhookURL, finalData, config) return okMsg; } catch (error) { throwGeneralAxiosError(error) } } else if (notification.type === "smtp") { return await Notification.smtp(notification, msg) } else if (notification.type === "discord") { try { // If heartbeatJSON is null, assume we're testing. if(heartbeatJSON == null) { let data = { username: 'Uptime-Kuma', content: msg } await axios.post(notification.discordWebhookUrl, data) return okMsg; } // If heartbeatJSON is not null, we go into the normal alerting loop. if(heartbeatJSON['status'] == 0) { var alertColor = "16711680"; } else if(heartbeatJSON['status'] == 1) { var alertColor = "65280"; } let data = { username: 'Uptime-Kuma', embeds: [{ title: "Uptime-Kuma Alert", color: alertColor, fields: [ { name: "Time (UTC)", value: heartbeatJSON["time"] }, { name: "Message", value: msg } ] }] } await axios.post(notification.discordWebhookUrl, data) return okMsg; } catch(error) { throwGeneralAxiosError(error) } } else if (notification.type === "signal") { try { let data = { "message": msg, "number": notification.signalNumber, "recipients": notification.signalRecipients.replace(/\s/g, '').split(",") }; let config = {}; await axios.post(notification.signalURL, data, config) return okMsg; } catch (error) { throwGeneralAxiosError(error) } } else if (notification.type === "slack") { try { if (heartbeatJSON == null) { let data = {'text': "Uptime Kuma Slack testing successful.", 'channel': notification.slackchannel, 'username': notification.slackusername, 'icon_emoji': notification.slackiconemo} await axios.post(notification.slackwebhookURL, data) return okMsg; } const time = heartbeatJSON["time"]; let data = { "text": "Uptime Kuma Alert", "channel":notification.slackchannel, "username": notification.slackusername, "icon_emoji": notification.slackiconemo, "blocks": [{ "type": "header", "text": { "type": "plain_text", "text": "Uptime Kuma Alert" } }, { "type": "section", "fields": [{ "type": "mrkdwn", "text": '*Message*\n'+msg }, { "type": "mrkdwn", "text": "*Time (UTC)*\n"+time } ] }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "Visit Uptime Kuma", }, "value": "Uptime-Kuma", "url": notification.slackbutton || "https://github.com/louislam/uptime-kuma" } ] } ] } await axios.post(notification.slackwebhookURL, data) return okMsg; } catch (error) { throwGeneralAxiosError(error) } } else if (notification.type === "pushover") { var pushoverlink = 'https://api.pushover.net/1/messages.json' try { if (heartbeatJSON == null) { let data = {'message': "Uptime Kuma Pushover testing successful.", 'user': notification.pushoveruserkey, 'token': notification.pushoverapptoken, 'sound':notification.pushoversounds, 'priority': notification.pushoverpriority, 'title':notification.pushovertitle, 'retry': "30", 'expire':"3600", 'html': 1} await axios.post(pushoverlink, data) return okMsg; } let data = { "message": "Uptime Kuma Alert\n\nMessage:"+msg+ '\nTime (UTC):' +heartbeatJSON["time"], "user":notification.pushoveruserkey, "token": notification.pushoverapptoken, "sound": notification.pushoversounds, "priority": notification.pushoverpriority, "title": notification.pushovertitle, "retry": "30", "expire": "3600", "html": 1 } await axios.post(pushoverlink, data) return okMsg; } catch (error) { throwGeneralAxiosError(error) } } else if (notification.type === "apprise") { return Notification.apprise(notification, msg) } else { throw new Error("Notification type is not supported") } } static async save(notification, notificationID, userID) { let bean if (notificationID) { bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ notificationID, userID, ]) if (! bean) { throw new Error("notification not found") } } else { bean = R.dispense("notification") } bean.name = notification.name; bean.user_id = userID; bean.config = JSON.stringify(notification) await R.store(bean) } static async delete(notificationID, userID) { let bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ notificationID, userID, ]) if (! bean) { throw new Error("notification not found") } await R.trash(bean) } static async smtp(notification, msg) { let transporter = nodemailer.createTransport({ host: notification.smtpHost, port: notification.smtpPort, secure: notification.smtpSecure, auth: { user: notification.smtpUsername, pass: notification.smtpPassword, }, }); // send mail with defined transport object await transporter.sendMail({ from: `"Uptime Kuma" <${notification.smtpFrom}>`, to: notification.smtpTo, subject: msg, text: msg, }); return "Sent Successfully."; } static async apprise(notification, msg) { let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL]) let output = (s.stdout) ? s.stdout.toString() : 'ERROR: maybe apprise not found'; if (output) { if (! output.includes("ERROR")) { return "Sent Successfully"; } else { throw new Error(output) } } else { return "" } } static checkApprise() { let commandExistsSync = require('command-exists').sync; let exists = commandExistsSync('apprise'); return exists; } } function throwGeneralAxiosError(error) { let msg = "Error: " + error + " "; if (error.response && error.response.data) { if (typeof error.response.data === "string") { msg += error.response.data; } else { msg += JSON.stringify(error.response.data) } } throw new Error(msg) } module.exports = { Notification, }