From d01fa9bcfcec87c14adcf82f5097a83129967ae9 Mon Sep 17 00:00:00 2001
From: Ponkhy <github@myon.lu>
Date: Tue, 7 Sep 2021 10:25:25 +0200
Subject: [PATCH 1/5] Auto login after setup

---
 src/pages/Setup.vue | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/pages/Setup.vue b/src/pages/Setup.vue
index dbff86837..9cb7c5ecb 100644
--- a/src/pages/Setup.vue
+++ b/src/pages/Setup.vue
@@ -85,7 +85,12 @@ export default {
                 this.$root.toastRes(res)
 
                 if (res.ok) {
-                    this.$router.push("/")
+                    this.processing = true;
+
+                    this.$root.login(this.username, this.password, (res) => {
+                        this.processing = false;
+                        this.$router.push("/")
+                    })
                 }
             })
         },

From da74391c3e6c315d2f317f7f34224a50a4431520 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Tue, 7 Sep 2021 22:42:46 +0800
Subject: [PATCH 2/5] convert notifications into modules

---
 .eslintrc.js                                  |   3 +-
 server/notification-providers/apprise.js      |  26 +
 server/notification-providers/discord.js      | 105 +++
 server/notification-providers/gotify.js       |  28 +
 server/notification-providers/line.js         |  60 ++
 server/notification-providers/lunasea.js      |  48 ++
 server/notification-providers/mattermost.js   | 123 +++
 .../notification-provider.js                  |  36 +
 server/notification-providers/octopush.js     |  40 +
 server/notification-providers/pushbullet.js   |  50 ++
 server/notification-providers/pushover.js     |  49 ++
 server/notification-providers/pushy.js        |  30 +
 server/notification-providers/rocket-chat.js  |  46 ++
 server/notification-providers/signal.js       |  27 +
 server/notification-providers/slack.js        |  70 ++
 server/notification-providers/smtp.js         |  43 ++
 server/notification-providers/telegram.js     |  27 +
 server/notification-providers/webhook.js      |  44 ++
 server/notification.js                        | 714 ++----------------
 server/server.js                              |   3 +
 20 files changed, 918 insertions(+), 654 deletions(-)
 create mode 100644 server/notification-providers/apprise.js
 create mode 100644 server/notification-providers/discord.js
 create mode 100644 server/notification-providers/gotify.js
 create mode 100644 server/notification-providers/line.js
 create mode 100644 server/notification-providers/lunasea.js
 create mode 100644 server/notification-providers/mattermost.js
 create mode 100644 server/notification-providers/notification-provider.js
 create mode 100644 server/notification-providers/octopush.js
 create mode 100644 server/notification-providers/pushbullet.js
 create mode 100644 server/notification-providers/pushover.js
 create mode 100644 server/notification-providers/pushy.js
 create mode 100644 server/notification-providers/rocket-chat.js
 create mode 100644 server/notification-providers/signal.js
 create mode 100644 server/notification-providers/slack.js
 create mode 100644 server/notification-providers/smtp.js
 create mode 100644 server/notification-providers/telegram.js
 create mode 100644 server/notification-providers/webhook.js

diff --git a/.eslintrc.js b/.eslintrc.js
index fe63d4a4e..6704a85b2 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -17,7 +17,8 @@ module.exports = {
     },
     rules: {
         "camelcase": ["warn", {
-            "properties": "never"
+            "properties": "never",
+            "ignoreImports": true
         }],
         // override/add rules settings here, such as:
         // 'vue/no-unused-vars': 'error'
diff --git a/server/notification-providers/apprise.js b/server/notification-providers/apprise.js
new file mode 100644
index 000000000..fdcd8d61b
--- /dev/null
+++ b/server/notification-providers/apprise.js
@@ -0,0 +1,26 @@
+const NotificationProvider = require("./notification-provider");
+const child_process = require("child_process");
+
+class Apprise extends NotificationProvider {
+
+    name = "apprise";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        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";
+            }
+
+            throw new Error(output)
+        } else {
+            return "No output from apprise";
+        }
+    }
+}
+
+module.exports = Apprise;
diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
new file mode 100644
index 000000000..d6ee0afe1
--- /dev/null
+++ b/server/notification-providers/discord.js
@@ -0,0 +1,105 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+const { DOWN, UP } = require("../../src/util");
+
+class Discord extends NotificationProvider {
+
+    name = "discord";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+
+        try {
+            const discordDisplayName = notification.discordUsername || "Uptime Kuma";
+
+            // If heartbeatJSON is null, assume we're testing.
+            if (heartbeatJSON == null) {
+                let discordtestdata = {
+                    username: discordDisplayName,
+                    content: msg,
+                }
+                await axios.post(notification.discordWebhookUrl, discordtestdata)
+                return okMsg;
+            }
+
+            let url;
+
+            if (monitorJSON["type"] === "port") {
+                url = monitorJSON["hostname"];
+                if (monitorJSON["port"]) {
+                    url += ":" + monitorJSON["port"];
+                }
+
+            } else {
+                url = monitorJSON["url"];
+            }
+
+            // If heartbeatJSON is not null, we go into the normal alerting loop.
+            if (heartbeatJSON["status"] == DOWN) {
+                let discorddowndata = {
+                    username: discordDisplayName,
+                    embeds: [{
+                        title: "❌ Your service " + monitorJSON["name"] + " went down. ❌",
+                        color: 16711680,
+                        timestamp: heartbeatJSON["time"],
+                        fields: [
+                            {
+                                name: "Service Name",
+                                value: monitorJSON["name"],
+                            },
+                            {
+                                name: "Service URL",
+                                value: url,
+                            },
+                            {
+                                name: "Time (UTC)",
+                                value: heartbeatJSON["time"],
+                            },
+                            {
+                                name: "Error",
+                                value: heartbeatJSON["msg"],
+                            },
+                        ],
+                    }],
+                }
+                await axios.post(notification.discordWebhookUrl, discorddowndata)
+                return okMsg;
+
+            } else if (heartbeatJSON["status"] == UP) {
+                let discordupdata = {
+                    username: discordDisplayName,
+                    embeds: [{
+                        title: "✅ Your service " + monitorJSON["name"] + " is up! ✅",
+                        color: 65280,
+                        timestamp: heartbeatJSON["time"],
+                        fields: [
+                            {
+                                name: "Service Name",
+                                value: monitorJSON["name"],
+                            },
+                            {
+                                name: "Service URL",
+                                value: url.startsWith("http") ? "[Visit Service](" + url + ")" : url,
+                            },
+                            {
+                                name: "Time (UTC)",
+                                value: heartbeatJSON["time"],
+                            },
+                            {
+                                name: "Ping",
+                                value: heartbeatJSON["ping"] + "ms",
+                            },
+                        ],
+                    }],
+                }
+                await axios.post(notification.discordWebhookUrl, discordupdata)
+                return okMsg;
+            }
+        } catch (error) {
+            this.throwGeneralAxiosError(error)
+        }
+    }
+
+}
+
+module.exports = Discord;
diff --git a/server/notification-providers/gotify.js b/server/notification-providers/gotify.js
new file mode 100644
index 000000000..9d2d55aa5
--- /dev/null
+++ b/server/notification-providers/gotify.js
@@ -0,0 +1,28 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+class Gotify extends NotificationProvider {
+
+    name = "gotify";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+        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) {
+            this.throwGeneralAxiosError(error);
+        }
+    }
+}
+
+module.exports = Gotify;
diff --git a/server/notification-providers/line.js b/server/notification-providers/line.js
new file mode 100644
index 000000000..830969034
--- /dev/null
+++ b/server/notification-providers/line.js
@@ -0,0 +1,60 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+const { DOWN, UP } = require("../../src/util");
+
+class Line extends NotificationProvider {
+
+    name = "line";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+        try {
+            let lineAPIUrl = "https://api.line.me/v2/bot/message/push";
+            let config = {
+                headers: {
+                    "Content-Type": "application/json",
+                    "Authorization": "Bearer " + notification.lineChannelAccessToken
+                }
+            };
+            if (heartbeatJSON == null) {
+                let testMessage = {
+                    "to": notification.lineUserID,
+                    "messages": [
+                        {
+                            "type": "text",
+                            "text": "Test Successful!"
+                        }
+                    ]
+                }
+                await axios.post(lineAPIUrl, testMessage, config)
+            } else if (heartbeatJSON["status"] == DOWN) {
+                let downMessage = {
+                    "to": notification.lineUserID,
+                    "messages": [
+                        {
+                            "type": "text",
+                            "text": "UptimeKuma Alert: [🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
+                        }
+                    ]
+                }
+                await axios.post(lineAPIUrl, downMessage, config)
+            } else if (heartbeatJSON["status"] == UP) {
+                let upMessage = {
+                    "to": notification.lineUserID,
+                    "messages": [
+                        {
+                            "type": "text",
+                            "text": "UptimeKuma Alert: [✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
+                        }
+                    ]
+                }
+                await axios.post(lineAPIUrl, upMessage, config)
+            }
+            return okMsg;
+        } catch (error) {
+            this.throwGeneralAxiosError(error)
+        }
+    }
+}
+
+module.exports = Line;
diff --git a/server/notification-providers/lunasea.js b/server/notification-providers/lunasea.js
new file mode 100644
index 000000000..fb6cd2368
--- /dev/null
+++ b/server/notification-providers/lunasea.js
@@ -0,0 +1,48 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+const { DOWN, UP } = require("../../src/util");
+
+class LunaSea extends NotificationProvider {
+
+    name = "lunasea";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+        let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice
+
+        try {
+            if (heartbeatJSON == null) {
+                let testdata = {
+                    "title": "Uptime Kuma Alert",
+                    "body": "Testing Successful.",
+                }
+                await axios.post(lunaseadevice, testdata)
+                return okMsg;
+            }
+
+            if (heartbeatJSON["status"] == DOWN) {
+                let downdata = {
+                    "title": "UptimeKuma Alert: " + monitorJSON["name"],
+                    "body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
+                }
+                await axios.post(lunaseadevice, downdata)
+                return okMsg;
+            }
+
+            if (heartbeatJSON["status"] == UP) {
+                let updata = {
+                    "title": "UptimeKuma Alert: " + monitorJSON["name"],
+                    "body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
+                }
+                await axios.post(lunaseadevice, updata)
+                return okMsg;
+            }
+
+        } catch (error) {
+            this.throwGeneralAxiosError(error)
+        }
+
+    }
+}
+
+module.exports = LunaSea;
diff --git a/server/notification-providers/mattermost.js b/server/notification-providers/mattermost.js
new file mode 100644
index 000000000..97779435a
--- /dev/null
+++ b/server/notification-providers/mattermost.js
@@ -0,0 +1,123 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+const { DOWN, UP } = require("../../src/util");
+
+class Mattermost extends NotificationProvider {
+
+    name = "mattermost";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+        try {
+            const mattermostUserName = notification.mattermostusername || "Uptime Kuma";
+            // If heartbeatJSON is null, assume we're testing.
+            if (heartbeatJSON == null) {
+                let mattermostTestData = {
+                    username: mattermostUserName,
+                    text: msg,
+                }
+                await axios.post(notification.mattermostWebhookUrl, mattermostTestData)
+                return okMsg;
+            }
+
+            const mattermostChannel = notification.mattermostchannel;
+            const mattermostIconEmoji = notification.mattermosticonemo;
+            const mattermostIconUrl = notification.mattermosticonurl;
+
+            if (heartbeatJSON["status"] == DOWN) {
+                let mattermostdowndata = {
+                    username: mattermostUserName,
+                    text: "Uptime Kuma Alert",
+                    channel: mattermostChannel,
+                    icon_emoji: mattermostIconEmoji,
+                    icon_url: mattermostIconUrl,
+                    attachments: [
+                        {
+                            fallback:
+                                "Your " +
+                                monitorJSON["name"] +
+                                " service went down.",
+                            color: "#FF0000",
+                            title:
+                                "❌ " +
+                                monitorJSON["name"] +
+                                " service went down. ❌",
+                            title_link: monitorJSON["url"],
+                            fields: [
+                                {
+                                    short: true,
+                                    title: "Service Name",
+                                    value: monitorJSON["name"],
+                                },
+                                {
+                                    short: true,
+                                    title: "Time (UTC)",
+                                    value: heartbeatJSON["time"],
+                                },
+                                {
+                                    short: false,
+                                    title: "Error",
+                                    value: heartbeatJSON["msg"],
+                                },
+                            ],
+                        },
+                    ],
+                };
+                await axios.post(
+                    notification.mattermostWebhookUrl,
+                    mattermostdowndata
+                );
+                return okMsg;
+            } else if (heartbeatJSON["status"] == UP) {
+                let mattermostupdata = {
+                    username: mattermostUserName,
+                    text: "Uptime Kuma Alert",
+                    channel: mattermostChannel,
+                    icon_emoji: mattermostIconEmoji,
+                    icon_url: mattermostIconUrl,
+                    attachments: [
+                        {
+                            fallback:
+                                "Your " +
+                                monitorJSON["name"] +
+                                " service went up!",
+                            color: "#32CD32",
+                            title:
+                                "✅ " +
+                                monitorJSON["name"] +
+                                " service went up! ✅",
+                            title_link: monitorJSON["url"],
+                            fields: [
+                                {
+                                    short: true,
+                                    title: "Service Name",
+                                    value: monitorJSON["name"],
+                                },
+                                {
+                                    short: true,
+                                    title: "Time (UTC)",
+                                    value: heartbeatJSON["time"],
+                                },
+                                {
+                                    short: false,
+                                    title: "Ping",
+                                    value: heartbeatJSON["ping"] + "ms",
+                                },
+                            ],
+                        },
+                    ],
+                };
+                await axios.post(
+                    notification.mattermostWebhookUrl,
+                    mattermostupdata
+                );
+                return okMsg;
+            }
+        } catch (error) {
+            this.throwGeneralAxiosError(error);
+        }
+
+    }
+}
+
+module.exports = Mattermost;
diff --git a/server/notification-providers/notification-provider.js b/server/notification-providers/notification-provider.js
new file mode 100644
index 000000000..61c6242d7
--- /dev/null
+++ b/server/notification-providers/notification-provider.js
@@ -0,0 +1,36 @@
+class NotificationProvider {
+
+    /**
+     * Notification Provider Name
+     * @type string
+     */
+    name = undefined;
+
+    /**
+     * @param notification : BeanModel
+     * @param msg : string General Message
+     * @param monitorJSON : object Monitor details (For Up/Down only)
+     * @param heartbeatJSON : object Heartbeat details (For Up/Down only)
+     * @returns {Promise<string>} Return Successful Message
+     * Throw Error with fail msg
+     */
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        throw new Error("Have to override Notification.send(...)");
+    }
+
+    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 = NotificationProvider;
diff --git a/server/notification-providers/octopush.js b/server/notification-providers/octopush.js
new file mode 100644
index 000000000..40273f9b8
--- /dev/null
+++ b/server/notification-providers/octopush.js
@@ -0,0 +1,40 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+class Octopush extends NotificationProvider {
+
+    name = "octopush";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+
+        try {
+            let config = {
+                headers: {
+                    "api-key": notification.octopushAPIKey,
+                    "api-login": notification.octopushLogin,
+                    "cache-control": "no-cache"
+                }
+            };
+            let data = {
+                "recipients": [
+                    {
+                        "phone_number": notification.octopushPhoneNumber
+                    }
+                ],
+                //octopush not supporting non ascii char
+                "text": msg.replace(/[^\x00-\x7F]/g, ""),
+                "type": notification.octopushSMSType,
+                "purpose": "alert",
+                "sender": notification.octopushSenderName
+            };
+
+            await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config)
+            return okMsg;
+        } catch (error) {
+            this.throwGeneralAxiosError(error);
+        }
+    }
+}
+
+module.exports = Octopush;
diff --git a/server/notification-providers/pushbullet.js b/server/notification-providers/pushbullet.js
new file mode 100644
index 000000000..0ed6f0fdf
--- /dev/null
+++ b/server/notification-providers/pushbullet.js
@@ -0,0 +1,50 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+const { DOWN, UP } = require("../../src/util");
+
+class Pushbullet extends NotificationProvider {
+
+    name = "pushbullet";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+
+        try {
+            let pushbulletUrl = "https://api.pushbullet.com/v2/pushes";
+            let config = {
+                headers: {
+                    "Access-Token": notification.pushbulletAccessToken,
+                    "Content-Type": "application/json"
+                }
+            };
+            if (heartbeatJSON == null) {
+                let testdata = {
+                    "type": "note",
+                    "title": "Uptime Kuma Alert",
+                    "body": "Testing Successful.",
+                }
+                await axios.post(pushbulletUrl, testdata, config)
+            } else if (heartbeatJSON["status"] == DOWN) {
+                let downdata = {
+                    "type": "note",
+                    "title": "UptimeKuma Alert: " + monitorJSON["name"],
+                    "body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
+                }
+                await axios.post(pushbulletUrl, downdata, config)
+            } else if (heartbeatJSON["status"] == UP) {
+                let updata = {
+                    "type": "note",
+                    "title": "UptimeKuma Alert: " + monitorJSON["name"],
+                    "body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
+                }
+                await axios.post(pushbulletUrl, updata, config)
+            }
+            return okMsg;
+        } catch (error) {
+            this.throwGeneralAxiosError(error)
+        }
+    }
+}
+
+module.exports = Pushbullet;
diff --git a/server/notification-providers/pushover.js b/server/notification-providers/pushover.js
new file mode 100644
index 000000000..2133ca1cf
--- /dev/null
+++ b/server/notification-providers/pushover.js
@@ -0,0 +1,49 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+class Pushover extends NotificationProvider {
+
+    name = "pushover";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+        let pushoverlink = "https://api.pushover.net/1/messages.json"
+
+        try {
+            if (heartbeatJSON == null) {
+                let data = {
+                    "message": "<b>Uptime Kuma Pushover testing successful.</b>",
+                    "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": "<b>Uptime Kuma Alert</b>\n\n<b>Message</b>:" + msg + "\n<b>Time (UTC)</b>:" + 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) {
+            this.throwGeneralAxiosError(error)
+        }
+
+    }
+}
+
+module.exports = Pushover;
diff --git a/server/notification-providers/pushy.js b/server/notification-providers/pushy.js
new file mode 100644
index 000000000..431cf8c37
--- /dev/null
+++ b/server/notification-providers/pushy.js
@@ -0,0 +1,30 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+class Pushy extends NotificationProvider {
+
+    name = "pushy";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+
+        try {
+            await axios.post(`https://api.pushy.me/push?api_key=${notification.pushyAPIKey}`, {
+                "to": notification.pushyToken,
+                "data": {
+                    "message": "Uptime-Kuma"
+                },
+                "notification": {
+                    "body": msg,
+                    "badge": 1,
+                    "sound": "ping.aiff"
+                }
+            })
+            return okMsg;
+        } catch (error) {
+            this.throwGeneralAxiosError(error)
+        }
+    }
+}
+
+module.exports = Pushy;
diff --git a/server/notification-providers/rocket-chat.js b/server/notification-providers/rocket-chat.js
new file mode 100644
index 000000000..149189650
--- /dev/null
+++ b/server/notification-providers/rocket-chat.js
@@ -0,0 +1,46 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+class RocketChat extends NotificationProvider {
+
+    name = "rocket.chat";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+        try {
+            if (heartbeatJSON == null) {
+                let data = {
+                    "text": "Uptime Kuma Rocket.chat testing successful.",
+                    "channel": notification.rocketchannel,
+                    "username": notification.rocketusername,
+                    "icon_emoji": notification.rocketiconemo,
+                }
+                await axios.post(notification.rocketwebhookURL, data)
+                return okMsg;
+            }
+
+            const time = heartbeatJSON["time"];
+            let data = {
+                "text": "Uptime Kuma Alert",
+                "channel": notification.rocketchannel,
+                "username": notification.rocketusername,
+                "icon_emoji": notification.rocketiconemo,
+                "attachments": [
+                    {
+                        "title": "Uptime Kuma Alert *Time (UTC)*\n" + time,
+                        "title_link": notification.rocketbutton,
+                        "text": "*Message*\n" + msg,
+                        "color": "#32cd32"
+                    }
+                ]
+            }
+            await axios.post(notification.rocketwebhookURL, data)
+            return okMsg;
+        } catch (error) {
+            this.throwGeneralAxiosError(error)
+        }
+
+    }
+}
+
+module.exports = RocketChat;
diff --git a/server/notification-providers/signal.js b/server/notification-providers/signal.js
new file mode 100644
index 000000000..ba5f87f9c
--- /dev/null
+++ b/server/notification-providers/signal.js
@@ -0,0 +1,27 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+class Signal extends NotificationProvider {
+
+    name = "signal";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+
+        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) {
+            this.throwGeneralAxiosError(error)
+        }
+    }
+}
+
+module.exports = Signal;
diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js
new file mode 100644
index 000000000..661df5a0f
--- /dev/null
+++ b/server/notification-providers/slack.js
@@ -0,0 +1,70 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+class Slack extends NotificationProvider {
+
+    name = "slack";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+        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) {
+            this.throwGeneralAxiosError(error)
+        }
+
+    }
+}
+
+module.exports = Slack;
diff --git a/server/notification-providers/smtp.js b/server/notification-providers/smtp.js
new file mode 100644
index 000000000..4914c0748
--- /dev/null
+++ b/server/notification-providers/smtp.js
@@ -0,0 +1,43 @@
+const nodemailer = require("nodemailer");
+const NotificationProvider = require("./notification-provider");
+
+class SMTP extends NotificationProvider {
+
+    name = "smtp";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+
+        const config = {
+            host: notification.smtpHost,
+            port: notification.smtpPort,
+            secure: notification.smtpSecure,
+        };
+
+        // Should fix the issue in https://github.com/louislam/uptime-kuma/issues/26#issuecomment-896373904
+        if (notification.smtpUsername || notification.smtpPassword) {
+            config.auth = {
+                user: notification.smtpUsername,
+                pass: notification.smtpPassword,
+            };
+        }
+
+        let transporter = nodemailer.createTransport(config);
+
+        let bodyTextContent = msg;
+        if (heartbeatJSON) {
+            bodyTextContent = `${msg}\nTime (UTC): ${heartbeatJSON["time"]}`;
+        }
+
+        // send mail with defined transport object
+        await transporter.sendMail({
+            from: `"Uptime Kuma" <${notification.smtpFrom}>`,
+            to: notification.smtpTo,
+            subject: msg,
+            text: bodyTextContent,
+        });
+
+        return "Sent Successfully.";
+    }
+}
+
+module.exports = SMTP;
diff --git a/server/notification-providers/telegram.js b/server/notification-providers/telegram.js
new file mode 100644
index 000000000..f88dcf5de
--- /dev/null
+++ b/server/notification-providers/telegram.js
@@ -0,0 +1,27 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+
+class Telegram extends NotificationProvider {
+
+    name = "telegram";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+
+        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)
+        }
+    }
+}
+
+module.exports = Telegram;
diff --git a/server/notification-providers/webhook.js b/server/notification-providers/webhook.js
new file mode 100644
index 000000000..197e9f9f3
--- /dev/null
+++ b/server/notification-providers/webhook.js
@@ -0,0 +1,44 @@
+const NotificationProvider = require("./notification-provider");
+const axios = require("axios");
+const FormData = require("form-data");
+
+class Webhook extends NotificationProvider {
+
+    name = "webhook";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        let okMsg = "Sent Successfully. ";
+
+        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) {
+            this.throwGeneralAxiosError(error)
+        }
+
+    }
+
+}
+
+module.exports = Webhook;
diff --git a/server/notification.js b/server/notification.js
index f78401d9b..5d2ffb030 100644
--- a/server/notification.js
+++ b/server/notification.js
@@ -1,602 +1,75 @@
-const axios = require("axios");
 const { R } = require("redbean-node");
-const FormData = require("form-data");
-const nodemailer = require("nodemailer");
-const child_process = require("child_process");
-
-const { UP, DOWN } = require("../src/util");
+const Apprise = require("./notification-providers/apprise");
+const Discord = require("./notification-providers/discord");
+const Gotify = require("./notification-providers/gotify");
+const Line = require("./notification-providers/line");
+const LunaSea = require("./notification-providers/lunasea");
+const Mattermost = require("./notification-providers/mattermost");
+const Octopush = require("./notification-providers/octopush");
+const Pushbullet = require("./notification-providers/pushbullet");
+const Pushover = require("./notification-providers/pushover");
+const Pushy = require("./notification-providers/pushy");
+const RocketChat = require("./notification-providers/rocket-chat");
+const Signal = require("./notification-providers/signal");
+const Slack = require("./notification-providers/slack");
+const SMTP = require("./notification-providers/smtp");
+const Telegram = require("./notification-providers/telegram");
+const Webhook = require("./notification-providers/webhook");
 
 class Notification {
 
+    providerList = {};
+
+    static init() {
+        console.log("Prepare Notification Providers");
+
+        this.providerList = {};
+
+        const list = [
+            new Apprise(),
+            new Discord(),
+            new Gotify(),
+            new Line(),
+            new LunaSea(),
+            new Mattermost(),
+            new Octopush(),
+            new Pushbullet(),
+            new Pushover(),
+            new Pushy(),
+            new RocketChat(),
+            new Signal(),
+            new Slack(),
+            new SMTP(),
+            new Telegram(),
+            new Webhook(),
+        ];
+
+        for (let item of list) {
+            if (! item.name) {
+                throw new Error("Notification provider without name");
+            }
+
+            if (this.providerList[item.name]) {
+                throw new Error("Duplicate notification provider name");
+            }
+            this.providerList[item.name] = item;
+        }
+    }
+
     /**
      *
-     * @param notification
-     * @param msg
-     * @param monitorJSON
-     * @param heartbeatJSON
+     * @param notification : BeanModel
+     * @param msg : string General Message
+     * @param monitorJSON : object Monitor details (For Up/Down only)
+     * @param heartbeatJSON : object Heartbeat details (For Up/Down only)
      * @returns {Promise<string>} 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, heartbeatJSON)
-
-        } else if (notification.type === "discord") {
-            try {
-                const discordDisplayName = notification.discordUsername || "Uptime Kuma";
-
-                // If heartbeatJSON is null, assume we're testing.
-                if (heartbeatJSON == null) {
-                    let discordtestdata = {
-                        username: discordDisplayName,
-                        content: msg,
-                    }
-                    await axios.post(notification.discordWebhookUrl, discordtestdata)
-                    return okMsg;
-                }
-
-                let url;
-
-                if (monitorJSON["type"] === "port") {
-                    url = monitorJSON["hostname"];
-                    if (monitorJSON["port"]) {
-                        url += ":" + monitorJSON["port"];
-                    }
-
-                } else {
-                    url = monitorJSON["url"];
-                }
-
-                // If heartbeatJSON is not null, we go into the normal alerting loop.
-                if (heartbeatJSON["status"] == DOWN) {
-                    let discorddowndata = {
-                        username: discordDisplayName,
-                        embeds: [{
-                            title: "❌ Your service " + monitorJSON["name"] + " went down. ❌",
-                            color: 16711680,
-                            timestamp: heartbeatJSON["time"],
-                            fields: [
-                                {
-                                    name: "Service Name",
-                                    value: monitorJSON["name"],
-                                },
-                                {
-                                    name: "Service URL",
-                                    value: url,
-                                },
-                                {
-                                    name: "Time (UTC)",
-                                    value: heartbeatJSON["time"],
-                                },
-                                {
-                                    name: "Error",
-                                    value: heartbeatJSON["msg"],
-                                },
-                            ],
-                        }],
-                    }
-                    await axios.post(notification.discordWebhookUrl, discorddowndata)
-                    return okMsg;
-
-                } else if (heartbeatJSON["status"] == UP) {
-                    let discordupdata = {
-                        username: discordDisplayName,
-                        embeds: [{
-                            title: "✅ Your service " + monitorJSON["name"] + " is up! ✅",
-                            color: 65280,
-                            timestamp: heartbeatJSON["time"],
-                            fields: [
-                                {
-                                    name: "Service Name",
-                                    value: monitorJSON["name"],
-                                },
-                                {
-                                    name: "Service URL",
-                                    value: url.startsWith("http") ? "[Visit Service](" + url + ")" : url,
-                                },
-                                {
-                                    name: "Time (UTC)",
-                                    value: heartbeatJSON["time"],
-                                },
-                                {
-                                    name: "Ping",
-                                    value: heartbeatJSON["ping"] + "ms",
-                                },
-                            ],
-                        }],
-                    }
-                    await axios.post(notification.discordWebhookUrl, discordupdata)
-                    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 === "pushy") {
-            try {
-                await axios.post(`https://api.pushy.me/push?api_key=${notification.pushyAPIKey}`, {
-                    "to": notification.pushyToken,
-                    "data": {
-                        "message": "Uptime-Kuma"
-                    },
-                    "notification": {
-                        "body": msg,
-                        "badge": 1,
-                        "sound": "ping.aiff"
-                    }
-                })
-                return true;
-            } catch (error) {
-                console.log(error)
-                return false;
-            }
-        } else if (notification.type === "octopush") {
-            try {
-                let config = {
-                    headers: {
-                        "api-key": notification.octopushAPIKey,
-                        "api-login": notification.octopushLogin,
-                        "cache-control": "no-cache"
-                    }
-                };
-                let data = {
-                    "recipients": [
-                        {
-                            "phone_number": notification.octopushPhoneNumber
-                        }
-                    ],
-                    //octopush not supporting non ascii char
-                    "text": msg.replace(/[^\x00-\x7F]/g, ""),
-                    "type": notification.octopushSMSType,
-                    "purpose": "alert",
-                    "sender": notification.octopushSenderName
-                };
-
-                await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config)
-                return true;
-            } catch (error) {
-                console.log(error)
-                return false;
-            }
-        } 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 === "rocket.chat") {
-            try {
-                if (heartbeatJSON == null) {
-                    let data = {
-                        "text": "Uptime Kuma Rocket.chat testing successful.",
-                        "channel": notification.rocketchannel,
-                        "username": notification.rocketusername,
-                        "icon_emoji": notification.rocketiconemo,
-                    }
-                    await axios.post(notification.rocketwebhookURL, data)
-                    return okMsg;
-                }
-
-                const time = heartbeatJSON["time"];
-                let data = {
-                    "text": "Uptime Kuma Alert",
-                    "channel": notification.rocketchannel,
-                    "username": notification.rocketusername,
-                    "icon_emoji": notification.rocketiconemo,
-                    "attachments": [
-                        {
-                            "title": "Uptime Kuma Alert *Time (UTC)*\n" + time,
-                            "title_link": notification.rocketbutton,
-                            "text": "*Message*\n" + msg,
-                            "color": "#32cd32"
-                        }
-                    ]
-                }
-                await axios.post(notification.rocketwebhookURL, data)
-                return okMsg;
-            } catch (error) {
-                throwGeneralAxiosError(error)
-            }
-
-        } else if (notification.type === "mattermost") {
-            try {
-                const mattermostUserName = notification.mattermostusername || "Uptime Kuma";
-                // If heartbeatJSON is null, assume we're testing.
-                if (heartbeatJSON == null) {
-                    let mattermostTestData = {
-                        username: mattermostUserName,
-                        text: msg,
-                    }
-                    await axios.post(notification.mattermostWebhookUrl, mattermostTestData)
-                    return okMsg;
-                }
-
-                const mattermostChannel = notification.mattermostchannel;
-                const mattermostIconEmoji = notification.mattermosticonemo;
-                const mattermostIconUrl = notification.mattermosticonurl;
-
-                if (heartbeatJSON["status"] == DOWN) {
-                    let mattermostdowndata = {
-                        username: mattermostUserName,
-                        text: "Uptime Kuma Alert",
-                        channel: mattermostChannel,
-                        icon_emoji: mattermostIconEmoji,
-                        icon_url: mattermostIconUrl,
-                        attachments: [
-                            {
-                                fallback:
-                                    "Your " +
-                                    monitorJSON["name"] +
-                                    " service went down.",
-                                color: "#FF0000",
-                                title:
-                                    "❌ " +
-                                    monitorJSON["name"] +
-                                    " service went down. ❌",
-                                title_link: monitorJSON["url"],
-                                fields: [
-                                    {
-                                        short: true,
-                                        title: "Service Name",
-                                        value: monitorJSON["name"],
-                                    },
-                                    {
-                                        short: true,
-                                        title: "Time (UTC)",
-                                        value: heartbeatJSON["time"],
-                                    },
-                                    {
-                                        short: false,
-                                        title: "Error",
-                                        value: heartbeatJSON["msg"],
-                                    },
-                                ],
-                            },
-                        ],
-                    };
-                    await axios.post(
-                        notification.mattermostWebhookUrl,
-                        mattermostdowndata
-                    );
-                    return okMsg;
-                } else if (heartbeatJSON["status"] == UP) {
-                    let mattermostupdata = {
-                        username: mattermostUserName,
-                        text: "Uptime Kuma Alert",
-                        channel: mattermostChannel,
-                        icon_emoji: mattermostIconEmoji,
-                        icon_url: mattermostIconUrl,
-                        attachments: [
-                            {
-                                fallback:
-                                    "Your " +
-                                    monitorJSON["name"] +
-                                    " service went up!",
-                                color: "#32CD32",
-                                title:
-                                    "✅ " +
-                                    monitorJSON["name"] +
-                                    " service went up! ✅",
-                                title_link: monitorJSON["url"],
-                                fields: [
-                                    {
-                                        short: true,
-                                        title: "Service Name",
-                                        value: monitorJSON["name"],
-                                    },
-                                    {
-                                        short: true,
-                                        title: "Time (UTC)",
-                                        value: heartbeatJSON["time"],
-                                    },
-                                    {
-                                        short: false,
-                                        title: "Ping",
-                                        value: heartbeatJSON["ping"] + "ms",
-                                    },
-                                ],
-                            },
-                        ],
-                    };
-                    await axios.post(
-                        notification.mattermostWebhookUrl,
-                        mattermostupdata
-                    );
-                    return okMsg;
-                }
-            } catch (error) {
-                throwGeneralAxiosError(error);
-            }
-
-        } else if (notification.type === "pushover") {
-            let pushoverlink = "https://api.pushover.net/1/messages.json"
-            try {
-                if (heartbeatJSON == null) {
-                    let data = {
-                        "message": "<b>Uptime Kuma Pushover testing successful.</b>",
-                        "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": "<b>Uptime Kuma Alert</b>\n\n<b>Message</b>:" + msg + "\n<b>Time (UTC)</b>:" + 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 if (notification.type === "lunasea") {
-            let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice
-
-            try {
-                if (heartbeatJSON == null) {
-                    let testdata = {
-                        "title": "Uptime Kuma Alert",
-                        "body": "Testing Successful.",
-                    }
-                    await axios.post(lunaseadevice, testdata)
-                    return okMsg;
-                }
-
-                if (heartbeatJSON["status"] == DOWN) {
-                    let downdata = {
-                        "title": "UptimeKuma Alert: " + monitorJSON["name"],
-                        "body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
-                    }
-                    await axios.post(lunaseadevice, downdata)
-                    return okMsg;
-                }
-
-                if (heartbeatJSON["status"] == UP) {
-                    let updata = {
-                        "title": "UptimeKuma Alert: " + monitorJSON["name"],
-                        "body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
-                    }
-                    await axios.post(lunaseadevice, updata)
-                    return okMsg;
-                }
-
-            } catch (error) {
-                throwGeneralAxiosError(error)
-            }
-
-        } else if (notification.type === "pushbullet") {
-            try {
-                let pushbulletUrl = "https://api.pushbullet.com/v2/pushes";
-                let config = {
-                    headers: {
-                        "Access-Token": notification.pushbulletAccessToken,
-                        "Content-Type": "application/json"
-                    }
-                };
-                if (heartbeatJSON == null) {
-                    let testdata = {
-                        "type": "note",
-                        "title": "Uptime Kuma Alert",
-                        "body": "Testing Successful.",
-                    }
-                    await axios.post(pushbulletUrl, testdata, config)
-                } else if (heartbeatJSON["status"] == DOWN) {
-                    let downdata = {
-                        "type": "note",
-                        "title": "UptimeKuma Alert: " + monitorJSON["name"],
-                        "body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
-                    }
-                    await axios.post(pushbulletUrl, downdata, config)
-                } else if (heartbeatJSON["status"] == UP) {
-                    let updata = {
-                        "type": "note",
-                        "title": "UptimeKuma Alert: " + monitorJSON["name"],
-                        "body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
-                    }
-                    await axios.post(pushbulletUrl, updata, config)
-                }
-                return okMsg;
-            } catch (error) {
-                throwGeneralAxiosError(error)
-            }
-        } else if (notification.type === "line") {
-            try {
-                let lineAPIUrl = "https://api.line.me/v2/bot/message/push";
-                let config = {
-                    headers: {
-                        "Content-Type": "application/json",
-                        "Authorization": "Bearer " + notification.lineChannelAccessToken
-                    }
-                };
-                if (heartbeatJSON == null) {
-                    let testMessage = {
-                        "to": notification.lineUserID,
-                        "messages": [
-                            {
-                                "type": "text",
-                                "text": "Test Successful!"
-                            }
-                        ]
-                    }
-                    await axios.post(lineAPIUrl, testMessage, config)
-                } else if (heartbeatJSON["status"] == DOWN) {
-                    let downMessage = {
-                        "to": notification.lineUserID,
-                        "messages": [
-                            {
-                                "type": "text",
-                                "text": "UptimeKuma Alert: [🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
-                            }
-                        ]
-                    }
-                    await axios.post(lineAPIUrl, downMessage, config)
-                } else if (heartbeatJSON["status"] == UP) {
-                    let upMessage = {
-                        "to": notification.lineUserID,
-                        "messages": [
-                            {
-                                "type": "text",
-                                "text": "UptimeKuma Alert: [✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
-                            }
-                        ]
-                    }
-                    await axios.post(lineAPIUrl, upMessage, config)
-                }
-                return okMsg;
-            } catch (error) {
-                throwGeneralAxiosError(error)
-            }
+        if (this.providerList[notification.type]) {
+            return this.providerList[notification.type].send(notification, msg, monitorJSON, heartbeatJSON);
         } else {
-            throw new Error("Notification type is not supported")
+            throw new Error("Notification type is not supported");
         }
     }
 
@@ -636,57 +109,6 @@ class Notification {
         await R.trash(bean)
     }
 
-    static async smtp(notification, msg, heartbeatJSON = null) {
-
-        const config = {
-            host: notification.smtpHost,
-            port: notification.smtpPort,
-            secure: notification.smtpSecure,
-        };
-
-        // Should fix the issue in https://github.com/louislam/uptime-kuma/issues/26#issuecomment-896373904
-        if (notification.smtpUsername || notification.smtpPassword) {
-            config.auth = {
-                user: notification.smtpUsername,
-                pass: notification.smtpPassword,
-            };
-        }
-
-        let transporter = nodemailer.createTransport(config);
-
-        let bodyTextContent = msg;
-        if(heartbeatJSON) {
-            bodyTextContent = `${msg}\nTime (UTC): ${heartbeatJSON["time"]}`;
-        }
-
-        // send mail with defined transport object
-        await transporter.sendMail({
-            from: `"Uptime Kuma" <${notification.smtpFrom}>`,
-            to: notification.smtpTo,
-            subject: msg,
-            text: bodyTextContent,
-        });
-
-        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";
-            }
-
-            throw new Error(output)
-        } else {
-            return ""
-        }
-    }
-
     static checkApprise() {
         let commandExistsSync = require("command-exists").sync;
         let exists = commandExistsSync("apprise");
@@ -695,20 +117,6 @@ class Notification {
 
 }
 
-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,
 }
diff --git a/server/server.js b/server/server.js
index 7d9295354..29e0857a0 100644
--- a/server/server.js
+++ b/server/server.js
@@ -27,8 +27,11 @@ debug("Importing Monitor");
 const Monitor = require("./model/monitor");
 debug("Importing Settings");
 const { getSettings, setSettings, setting, initJWTSecret } = require("./util-server");
+
 debug("Importing Notification");
 const { Notification } = require("./notification");
+Notification.init();
+
 debug("Importing Database");
 const Database = require("./database");
 

From d164b6ccce012180a9dcc493c6eb0ead8157e1f4 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Tue, 7 Sep 2021 23:06:49 +0800
Subject: [PATCH 3/5] prevent Chrome ask for saving password for notification
 settings (change to one-time-code to solve it)

---
 src/components/HiddenInput.vue        | 38 +++++----------------------
 src/components/NotificationDialog.vue | 20 +++++++-------
 2 files changed, 17 insertions(+), 41 deletions(-)

diff --git a/src/components/HiddenInput.vue b/src/components/HiddenInput.vue
index 7ec9f2e49..2b588af19 100644
--- a/src/components/HiddenInput.vue
+++ b/src/components/HiddenInput.vue
@@ -1,11 +1,7 @@
 <template>
     <div class="input-group mb-3">
-        <!--
-        Hack - Disable Chrome save password
-        readonly + onfocus
-        https://stackoverflow.com/questions/41217019/how-to-prevent-a-browser-from-storing-passwords
-       -->
         <input
+            ref="input"
             v-model="model"
             :type="visibility"
             class="form-control"
@@ -13,8 +9,7 @@
             :maxlength="maxlength"
             :autocomplete="autocomplete"
             :required="required"
-            :readonly="isReadOnly"
-            @focus="removeReadOnly"
+            :readonly="readonly"
         >
 
         <a v-if="visibility == 'password'" class="btn btn-outline-primary" @click="showInput()">
@@ -42,20 +37,20 @@ export default {
             default: 255
         },
         autocomplete: {
-            type: Boolean,
+            type: String,
+            default: undefined,
         },
         required: {
             type: Boolean
         },
         readonly: {
-            type: Boolean,
-            default: false,
+            type: String,
+            default: undefined,
         },
     },
     data() {
         return {
             visibility: "password",
-            readOnlyValue: false,
         }
     },
     computed: {
@@ -66,22 +61,10 @@ export default {
             set(value) {
                 this.$emit("update:modelValue", value)
             }
-        },
-        isReadOnly() {
-            // Actually readonly from prop
-            if (this.readonly) {
-                return true;
-            }
-
-            // Hack - Disable Chrome save password
-            return this.readOnlyValue;
         }
     },
     created() {
-        // Hack - Disable Chrome save password
-        if (this.autocomplete) {
-            this.readOnlyValue = "readonly";
-        }
+
     },
     methods: {
         showInput() {
@@ -90,13 +73,6 @@ export default {
         hideInput() {
             this.visibility = "password";
         },
-
-        // Hack - Disable Chrome save password
-        removeReadOnly() {
-            if (this.autocomplete) {
-                this.readOnlyValue = false;
-            }
-        }
     }
 }
 </script>
diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue
index 080fb859e..bc7d4c054 100644
--- a/src/components/NotificationDialog.vue
+++ b/src/components/NotificationDialog.vue
@@ -40,7 +40,7 @@
                         <template v-if="notification.type === 'telegram'">
                             <div class="mb-3">
                                 <label for="telegram-bot-token" class="form-label">Bot Token</label>
-                                <HiddenInput id="telegram-bot-token" v-model="notification.telegramBotToken" :required="true" :readonly="true"></HiddenInput>
+                                <HiddenInput id="telegram-bot-token" v-model="notification.telegramBotToken" :required="true" autocomplete="one-time-code"></HiddenInput>
                                 <div class="form-text">
                                     You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.
                                 </div>
@@ -130,7 +130,7 @@
 
                             <div class="mb-3">
                                 <label for="password" class="form-label">Password</label>
-                                <HiddenInput id="password" v-model="notification.smtpPassword" :required="true" autocomplete="false"></HiddenInput>
+                                <HiddenInput id="password" v-model="notification.smtpPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
                             </div>
 
                             <div class="mb-3">
@@ -195,7 +195,7 @@
                         <template v-if="notification.type === 'gotify'">
                             <div class="mb-3">
                                 <label for="gotify-application-token" class="form-label">Application Token</label>
-                                <HiddenInput id="gotify-application-token" v-model="notification.gotifyapplicationToken" :required="true"></HiddenInput>
+                                <HiddenInput id="gotify-application-token" v-model="notification.gotifyapplicationToken" :required="true" autocomplete="one-time-code"></HiddenInput>
                             </div>
                             <div class="mb-3">
                                 <label for="gotify-server-url" class="form-label">Server URL</label>
@@ -306,13 +306,13 @@
                         <template v-if="notification.type === 'pushy'">
                             <div class="mb-3">
                                 <label for="pushy-app-token" class="form-label">API_KEY</label>
-                                <HiddenInput id="pushy-app-token" v-model="notification.pushyAPIKey" :required="true"></HiddenInput>
+                                <HiddenInput id="pushy-app-token" v-model="notification.pushyAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
                             </div>
 
                             <div class="mb-3">
                                 <label for="pushy-user-key" class="form-label">USER_TOKEN</label>
                                 <div class="input-group mb-3">
-                                    <HiddenInput id="pushy-user-key" v-model="notification.pushyToken" :required="true"></HiddenInput>
+                                    <HiddenInput id="pushy-user-key" v-model="notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput>
                                 </div>
                             </div>
                             <p style="margin-top: 8px;">
@@ -323,7 +323,7 @@
                         <template v-if="notification.type === 'octopush'">
                             <div class="mb-3">
                                 <label for="octopush-key" class="form-label">API KEY</label>
-                                <HiddenInput id="octopush-key" v-model="notification.octopushAPIKey" :required="true"></HiddenInput>
+                                <HiddenInput id="octopush-key" v-model="notification.octopushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
                                 <label for="octopush-login" class="form-label">API LOGIN</label>
                                 <input id="octopush-login" v-model="notification.octopushLogin" type="text" class="form-control" required>
                             </div>
@@ -354,9 +354,9 @@
                         <template v-if="notification.type === 'pushover'">
                             <div class="mb-3">
                                 <label for="pushover-user" class="form-label">User Key<span style="color: red;"><sup>*</sup></span></label>
-                                <HiddenInput id="pushover-user" v-model="notification.pushoveruserkey" :required="true"></HiddenInput>
+                                <HiddenInput id="pushover-user" v-model="notification.pushoveruserkey" :required="true" autocomplete="one-time-code"></HiddenInput>
                                 <label for="pushover-app-token" class="form-label">Application Token<span style="color: red;"><sup>*</sup></span></label>
-                                <HiddenInput id="pushover-app-token" v-model="notification.pushoverapptoken" :required="true"></HiddenInput>
+                                <HiddenInput id="pushover-app-token" v-model="notification.pushoverapptoken" :required="true" autocomplete="one-time-code"></HiddenInput>
                                 <label for="pushover-device" class="form-label">Device</label>
                                 <input id="pushover-device" v-model="notification.pushoverdevice" type="text" class="form-control">
                                 <label for="pushover-device" class="form-label">Message Title</label>
@@ -442,7 +442,7 @@
                         <template v-if="notification.type === 'pushbullet'">
                             <div class="mb-3">
                                 <label for="pushbullet-access-token" class="form-label">Access Token</label>
-                                <HiddenInput id="pushbullet-access-token" v-model="notification.pushbulletAccessToken" :required="true"></HiddenInput>
+                                <HiddenInput id="pushbullet-access-token" v-model="notification.pushbulletAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput>
                             </div>
 
                             <p style="margin-top: 8px;">
@@ -453,7 +453,7 @@
                         <template v-if="notification.type === 'line'">
                             <div class="mb-3">
                                 <label for="line-channel-access-token" class="form-label">Channel access token</label>
-                                <HiddenInput id="line-channel-access-token" v-model="notification.lineChannelAccessToken" :required="true"></HiddenInput>
+                                <HiddenInput id="line-channel-access-token" v-model="notification.lineChannelAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput>
                             </div>
                             <div class="form-text">
                                 Line Developers Console - <b>Basic Settings</b>

From 4a106431f39328b13deec16ded67118c519426aa Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Tue, 7 Sep 2021 23:32:05 +0800
Subject: [PATCH 4/5] convert Telegram into a vue components

---
 src/components/NotificationDialog.vue     | 84 +++----------------
 src/components/notifications/Telegram.vue | 98 +++++++++++++++++++++++
 src/languages/da-DK.js                    |  3 +-
 src/languages/de-DE.js                    |  1 +
 src/languages/en.js                       |  3 +-
 src/languages/es-ES.js                    |  3 +-
 src/languages/fr-FR.js                    |  3 +-
 src/languages/ja.js                       |  3 +-
 src/languages/ko-KR.js                    |  3 +-
 src/languages/nl-NL.js                    |  3 +-
 src/languages/pl.js                       |  9 ++-
 src/languages/ru-RU.js                    |  3 +-
 src/languages/sr-latn.js                  |  3 +-
 src/languages/sr.js                       |  3 +-
 src/languages/sv-SE.js                    |  3 +-
 src/languages/zh-CN.js                    |  3 +-
 src/languages/zh-HK.js                    |  3 +-
 17 files changed, 142 insertions(+), 89 deletions(-)
 create mode 100644 src/components/notifications/Telegram.vue

diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue
index bc7d4c054..8a05d66ef 100644
--- a/src/components/NotificationDialog.vue
+++ b/src/components/NotificationDialog.vue
@@ -37,44 +37,9 @@
                             <input id="name" v-model="notification.name" type="text" class="form-control" required>
                         </div>
 
-                        <template v-if="notification.type === 'telegram'">
-                            <div class="mb-3">
-                                <label for="telegram-bot-token" class="form-label">Bot Token</label>
-                                <HiddenInput id="telegram-bot-token" v-model="notification.telegramBotToken" :required="true" autocomplete="one-time-code"></HiddenInput>
-                                <div class="form-text">
-                                    You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.
-                                </div>
-                            </div>
+                        <Telegram></Telegram>
 
-                            <div class="mb-3">
-                                <label for="telegram-chat-id" class="form-label">Chat ID</label>
-
-                                <div class="input-group mb-3">
-                                    <input id="telegram-chat-id" v-model="notification.telegramChatID" type="text" class="form-control" required>
-                                    <button v-if="notification.telegramBotToken" class="btn btn-outline-secondary" type="button" @click="autoGetTelegramChatID">
-                                        Auto Get
-                                    </button>
-                                </div>
-
-                                <div class="form-text">
-                                    Support Direct Chat / Group / Channel's Chat ID
-
-                                    <p style="margin-top: 8px;">
-                                        You can get your chat id by sending message to the bot and go to this url to view the chat_id:
-                                    </p>
-
-                                    <p style="margin-top: 8px;">
-                                        <template v-if="notification.telegramBotToken">
-                                            <a :href="telegramGetUpdatesURL" target="_blank" style="word-break: break-word;">{{ telegramGetUpdatesURL }}</a>
-                                        </template>
-
-                                        <template v-else>
-                                            {{ telegramGetUpdatesURL }}
-                                        </template>
-                                    </p>
-                                </div>
-                            </div>
-                        </template>
+                        <!-- TODO: Convert all into vue components, but not an easy task.  -->
 
                         <template v-if="notification.type === 'webhook'">
                             <div class="mb-3">
@@ -469,6 +434,8 @@
                                 First access the <a href="https://developers.line.biz/console/" target="_blank">Line Developers Console</a>, create a provider and channel (Messaging API), then you can get the channel access token and user id from the above mentioned menu items.
                             </div>
                         </template>
+
+                        <!-- DEPRECATED! Please create vue component in "./src/components/notifications/{notification name}.vue" -->
                     </div>
                     <div class="modal-footer">
                         <button v-if="id" type="button" class="btn btn-danger" :disabled="processing" @click="deleteConfirm">
@@ -495,15 +462,18 @@
 import { Modal } from "bootstrap"
 import { ucfirst } from "../util.ts"
 import axios from "axios";
-import { useToast } from "vue-toastification"
+
 import Confirm from "./Confirm.vue";
 import HiddenInput from "./HiddenInput.vue";
-const toast = useToast()
+import Telegram from "./notifications/Telegram.vue";
+import { useToast } from "vue-toastification"
+const toast = useToast();
 
 export default {
     components: {
         Confirm,
         HiddenInput,
+        Telegram,
     },
     props: {},
     data() {
@@ -519,17 +489,7 @@ export default {
             appriseInstalled: false,
         }
     },
-    computed: {
-        telegramGetUpdatesURL() {
-            let token = "<YOUR BOT TOKEN HERE>"
 
-            if (this.notification.telegramBotToken) {
-                token = this.notification.telegramBotToken;
-            }
-
-            return `https://api.telegram.org/bot${token}/getUpdates`;
-        },
-    },
     watch: {
         "notification.type"(to, from) {
             let oldName;
@@ -615,32 +575,6 @@ export default {
                 }
             })
         },
-
-        async autoGetTelegramChatID() {
-            try {
-                let res = await axios.get(this.telegramGetUpdatesURL)
-
-                if (res.data.result.length >= 1) {
-                    let update = res.data.result[res.data.result.length - 1]
-
-                    if (update.channel_post) {
-                        this.notification.telegramChatID = update.channel_post.chat.id;
-                    } else if (update.message) {
-                        this.notification.telegramChatID = update.message.chat.id;
-                    } else {
-                        throw new Error("Chat ID is not found, please send a message to this bot first")
-                    }
-
-                } else {
-                    throw new Error("Chat ID is not found, please send a message to this bot first")
-                }
-
-            } catch (error) {
-                toast.error(error.message)
-            }
-
-        },
-
     },
 }
 </script>
diff --git a/src/components/notifications/Telegram.vue b/src/components/notifications/Telegram.vue
new file mode 100644
index 000000000..59d1992d6
--- /dev/null
+++ b/src/components/notifications/Telegram.vue
@@ -0,0 +1,98 @@
+<template>
+    <template v-if="$parent.notification.type === name">
+        <div class="mb-3">
+            <label for="telegram-bot-token" class="form-label">Bot Token</label>
+            <HiddenInput id="telegram-bot-token" v-model="$parent.notification.telegramBotToken" :required="true" autocomplete="one-time-code"></HiddenInput>
+            <div class="form-text">
+                You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.
+            </div>
+        </div>
+
+        <div class="mb-3">
+            <label for="telegram-chat-id" class="form-label">Chat ID</label>
+
+            <div class="input-group mb-3">
+                <input id="telegram-chat-id" v-model="$parent.notification.telegramChatID" type="text" class="form-control" required>
+                <button v-if="$parent.notification.telegramBotToken" class="btn btn-outline-secondary" type="button" @click="autoGetTelegramChatID">
+                    {{ $t("Auto Get") }}
+                </button>
+            </div>
+
+            <div class="form-text">
+                Support Direct Chat / Group / Channel's Chat ID
+
+                <p style="margin-top: 8px;">
+                    You can get your chat id by sending message to the bot and go to this url to view the chat_id:
+                </p>
+
+                <p style="margin-top: 8px;">
+                    <template v-if="$parent.notification.telegramBotToken">
+                        <a :href="telegramGetUpdatesURL" target="_blank" style="word-break: break-word;">{{ telegramGetUpdatesURL }}</a>
+                    </template>
+
+                    <template v-else>
+                        {{ telegramGetUpdatesURL }}
+                    </template>
+                </p>
+            </div>
+        </div>
+    </template>
+</template>
+
+<script>
+import HiddenInput from "../HiddenInput.vue";
+import axios from "axios";
+import { useToast } from "vue-toastification"
+const toast = useToast();
+
+export default {
+    components: {
+        HiddenInput,
+    },
+    data() {
+        return {
+            name: "telegram",
+        }
+    },
+    computed: {
+        telegramGetUpdatesURL() {
+            let token = "<YOUR BOT TOKEN HERE>"
+
+            if (this.$parent.notification.telegramBotToken) {
+                token = this.$parent.notification.telegramBotToken;
+            }
+
+            return `https://api.telegram.org/bot${token}/getUpdates`;
+        },
+    },
+    mounted() {
+        console.log(this.$parent.notification.type);
+    },
+    methods: {
+        async autoGetTelegramChatID() {
+            try {
+                let res = await axios.get(this.telegramGetUpdatesURL)
+
+                if (res.data.result.length >= 1) {
+                    let update = res.data.result[res.data.result.length - 1]
+
+                    if (update.channel_post) {
+                        this.notification.telegramChatID = update.channel_post.chat.id;
+                    } else if (update.message) {
+                        this.notification.telegramChatID = update.message.chat.id;
+                    } else {
+                        throw new Error("Chat ID is not found, please send a message to this bot first")
+                    }
+
+                } else {
+                    throw new Error("Chat ID is not found, please send a message to this bot first")
+                }
+
+            } catch (error) {
+                toast.error(error.message)
+            }
+
+        },
+    }
+}
+</script>
diff --git a/src/languages/da-DK.js b/src/languages/da-DK.js
index 7e2e8a9f1..c8e4e68fd 100644
--- a/src/languages/da-DK.js
+++ b/src/languages/da-DK.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/de-DE.js b/src/languages/de-DE.js
index 9945bdff6..a39d2b110 100644
--- a/src/languages/de-DE.js
+++ b/src/languages/de-DE.js
@@ -116,4 +116,5 @@ export default {
     respTime: "Antw. Zeit (ms)",
     notAvailableShort: "N/A",
     Create: "Erstellen",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/en.js b/src/languages/en.js
index 435160750..cece7e96f 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -115,5 +115,6 @@ export default {
     Create: "Create",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/es-ES.js b/src/languages/es-ES.js
index fadd3fccc..035608483 100644
--- a/src/languages/es-ES.js
+++ b/src/languages/es-ES.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/fr-FR.js b/src/languages/fr-FR.js
index f11781966..dca82ac25 100644
--- a/src/languages/fr-FR.js
+++ b/src/languages/fr-FR.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/ja.js b/src/languages/ja.js
index f7fb55eee..d7b4e6e2a 100644
--- a/src/languages/ja.js
+++ b/src/languages/ja.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/ko-KR.js b/src/languages/ko-KR.js
index 8956de072..d57dcbd7e 100644
--- a/src/languages/ko-KR.js
+++ b/src/languages/ko-KR.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/nl-NL.js b/src/languages/nl-NL.js
index 647d136b2..ef2305ca5 100644
--- a/src/languages/nl-NL.js
+++ b/src/languages/nl-NL.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/pl.js b/src/languages/pl.js
index cc625d6de..74137dbbf 100644
--- a/src/languages/pl.js
+++ b/src/languages/pl.js
@@ -109,5 +109,12 @@ export default {
     "Repeat Password": "Powtórz hasło",
     respTime: "Czas odp. (ms)",
     notAvailableShort: "N/A",
-    Create: "Stwórz"
+    Create: "Stwórz",
+    clearEventsMsg: "Are you sure want to delete all events for this monitor?",
+    clearHeartbeatsMsg: "Are you sure want to delete all heartbeats for this monitor?",
+    confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
+    "Clear Data": "Clear Data",
+    Events: "Events",
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/ru-RU.js b/src/languages/ru-RU.js
index e3835816b..1cea0567f 100644
--- a/src/languages/ru-RU.js
+++ b/src/languages/ru-RU.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/sr-latn.js b/src/languages/sr-latn.js
index 98453decd..46a6efec7 100644
--- a/src/languages/sr-latn.js
+++ b/src/languages/sr-latn.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/sr.js b/src/languages/sr.js
index 779754b96..c46244ada 100644
--- a/src/languages/sr.js
+++ b/src/languages/sr.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/sv-SE.js b/src/languages/sv-SE.js
index c830e8907..a07396678 100644
--- a/src/languages/sv-SE.js
+++ b/src/languages/sv-SE.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js
index 5491ab5be..d837aced5 100644
--- a/src/languages/zh-CN.js
+++ b/src/languages/zh-CN.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
     "Clear Data": "Clear Data",
     Events: "Events",
-    Heartbeats: "Heartbeats"
+    Heartbeats: "Heartbeats",
+    "Auto Get": "Auto Get"
 }
diff --git a/src/languages/zh-HK.js b/src/languages/zh-HK.js
index b76bdfc85..d7aa9de88 100644
--- a/src/languages/zh-HK.js
+++ b/src/languages/zh-HK.js
@@ -115,5 +115,6 @@ export default {
     confirmClearStatisticsMsg: "是否確定刪除所有監測器的脈搏資料?(您的監測器會繼續正常運作)",
     "Clear Data": "清除資料",
     Events: "事件",
-    Heartbeats: "脈搏"
+    Heartbeats: "脈搏",
+    "Auto Get": "自動獲取"
 }

From 5ca0dd628d09616dafed5d0906afaaa1939ba121 Mon Sep 17 00:00:00 2001
From: LouisLam <louislam@users.noreply.github.com>
Date: Tue, 7 Sep 2021 23:36:55 +0800
Subject: [PATCH 5/5] remove debug msg

---
 src/components/HeartbeatBar.vue           | 1 -
 src/components/notifications/Telegram.vue | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue
index 04d046bd5..fb6086d00 100644
--- a/src/components/HeartbeatBar.vue
+++ b/src/components/HeartbeatBar.vue
@@ -137,7 +137,6 @@ export default {
 
         if (! Number.isInteger(actualWidth)) {
             this.beatWidth = Math.round(actualWidth) / window.devicePixelRatio;
-            console.log(this.beatWidth);
         }
 
         if (! Number.isInteger(actualMargin)) {
diff --git a/src/components/notifications/Telegram.vue b/src/components/notifications/Telegram.vue
index 59d1992d6..537b7fd34 100644
--- a/src/components/notifications/Telegram.vue
+++ b/src/components/notifications/Telegram.vue
@@ -66,7 +66,7 @@ export default {
         },
     },
     mounted() {
-        console.log(this.$parent.notification.type);
+
     },
     methods: {
         async autoGetTelegramChatID() {