Merge branch 'master' into telegram-custom-server

# Conflicts:
#	src/components/notifications/Telegram.vue
This commit is contained in:
Ramin Mazloomi 2025-03-19 04:10:46 +03:30
commit 6dd37e1ab1
19 changed files with 465 additions and 91 deletions

View file

@ -41,7 +41,7 @@
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push", "build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push",
"upload-artifacts": "node extra/release/upload-artifacts.mjs", "upload-artifacts": "node extra/release/upload-artifacts.mjs",
"upload-artifacts-beta": "node extra/release/upload-artifacts-beta.mjs", "upload-artifacts-beta": "node extra/release/upload-artifacts-beta.mjs",
"setup": "git checkout 1.23.16 && npm ci --production && npm run download-dist", "setup": "git checkout 1.23.16 && npm ci --omit dev && npm run download-dist",
"download-dist": "node extra/download-dist.js", "download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js", "mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js", "reset-password": "node extra/reset-password.js",

View file

@ -1434,7 +1434,7 @@ class Monitor extends BeanModel {
for (let notification of notificationList) { for (let notification of notificationList) {
try { try {
log.debug("monitor", "Sending to " + notification.name); log.debug("monitor", "Sending to " + notification.name);
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] ${certType} certificate ${certCN} will be expired in ${daysRemaining} days`); await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] ${certType} certificate ${certCN} will expire in ${daysRemaining} days`);
sent = true; sent = true;
} catch (e) { } catch (e) {
log.error("monitor", "Cannot send cert notification to " + notification.name); log.error("monitor", "Cannot send cert notification to " + notification.name);

View file

@ -1,3 +1,6 @@
const { Liquid } = require("liquidjs");
const { DOWN } = require("../../src/util");
class NotificationProvider { class NotificationProvider {
/** /**
@ -49,6 +52,50 @@ class NotificationProvider {
} }
} }
/**
* Renders a message template with notification context
* @param {string} template the template
* @param {string} msg the message that will be included in the context
* @param {?object} monitorJSON Monitor details (For Up/Down/Cert-Expiry only)
* @param {?object} heartbeatJSON Heartbeat details (For Up/Down only)
* @returns {Promise<string>} rendered template
*/
async renderTemplate(template, msg, monitorJSON, heartbeatJSON) {
const engine = new Liquid();
const parsedTpl = engine.parse(template);
// Let's start with dummy values to simplify code
let monitorName = "Monitor Name not available";
let monitorHostnameOrURL = "testing.hostname";
if (monitorJSON !== null) {
monitorName = monitorJSON["name"];
monitorHostnameOrURL = this.extractAddress(monitorJSON);
}
let serviceStatus = "⚠️ Test";
if (heartbeatJSON !== null) {
serviceStatus = (heartbeatJSON["status"] === DOWN) ? "🔴 Down" : "✅ Up";
}
const context = {
// for v1 compatibility, to be removed in v3
"STATUS": serviceStatus,
"NAME": monitorName,
"HOSTNAME_OR_URL": monitorHostnameOrURL,
// variables which are officially supported
"status": serviceStatus,
"name": monitorName,
"hostnameOrURL": monitorHostnameOrURL,
monitorJSON,
heartbeatJSON,
msg,
};
return engine.render(parsedTpl, context);
}
/** /**
* Throws an error * Throws an error
* @param {any} error The error to throw * @param {any} error The error to throw

View file

@ -1,7 +1,5 @@
const nodemailer = require("nodemailer"); const nodemailer = require("nodemailer");
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const { DOWN } = require("../../src/util");
const { Liquid } = require("liquidjs");
class SMTP extends NotificationProvider { class SMTP extends NotificationProvider {
name = "smtp"; name = "smtp";
@ -53,15 +51,11 @@ class SMTP extends NotificationProvider {
const customSubject = notification.customSubject?.trim() || ""; const customSubject = notification.customSubject?.trim() || "";
const customBody = notification.customBody?.trim() || ""; const customBody = notification.customBody?.trim() || "";
const context = this.generateContext(msg, monitorJSON, heartbeatJSON);
const engine = new Liquid();
if (customSubject !== "") { if (customSubject !== "") {
const tpl = engine.parse(customSubject); subject = await this.renderTemplate(customSubject, msg, monitorJSON, heartbeatJSON);
subject = await engine.render(tpl, context);
} }
if (customBody !== "") { if (customBody !== "") {
const tpl = engine.parse(customBody); body = await this.renderTemplate(customBody, msg, monitorJSON, heartbeatJSON);
body = await engine.render(tpl, context);
} }
} }
@ -78,43 +72,6 @@ class SMTP extends NotificationProvider {
return okMsg; return okMsg;
} }
/**
* Generate context for LiquidJS
* @param {string} msg the message that will be included in the context
* @param {?object} monitorJSON Monitor details (For Up/Down/Cert-Expiry only)
* @param {?object} heartbeatJSON Heartbeat details (For Up/Down only)
* @returns {{STATUS: string, status: string, HOSTNAME_OR_URL: string, hostnameOrUrl: string, NAME: string, name: string, monitorJSON: ?object, heartbeatJSON: ?object, msg: string}} the context
*/
generateContext(msg, monitorJSON, heartbeatJSON) {
// Let's start with dummy values to simplify code
let monitorName = "Monitor Name not available";
let monitorHostnameOrURL = "testing.hostname";
if (monitorJSON !== null) {
monitorName = monitorJSON["name"];
monitorHostnameOrURL = this.extractAddress(monitorJSON);
}
let serviceStatus = "⚠️ Test";
if (heartbeatJSON !== null) {
serviceStatus = (heartbeatJSON["status"] === DOWN) ? "🔴 Down" : "✅ Up";
}
return {
// for v1 compatibility, to be removed in v3
"STATUS": serviceStatus,
"NAME": monitorName,
"HOSTNAME_OR_URL": monitorHostnameOrURL,
// variables which are officially supported
"status": serviceStatus,
"name": monitorName,
"hostnameOrURL": monitorHostnameOrURL,
monitorJSON,
heartbeatJSON,
msg,
};
}
} }
module.exports = SMTP; module.exports = SMTP;

View file

@ -22,6 +22,14 @@ class Telegram extends NotificationProvider {
params.message_thread_id = notification.telegramMessageThreadID; params.message_thread_id = notification.telegramMessageThreadID;
} }
if (notification.telegramUseTemplate) {
params.text = await this.renderTemplate(notification.telegramTemplate, msg, monitorJSON, heartbeatJSON);
if (notification.telegramTemplateParseMode !== "plain") {
params.parse_mode = notification.telegramTemplateParseMode;
}
}
await axios.get(`${url}/bot${notification.telegramBotToken}/sendMessage`, { await axios.get(`${url}/bot${notification.telegramBotToken}/sendMessage`, {
params: params, params: params,
}); });

View file

@ -0,0 +1,40 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class WAHA extends NotificationProvider {
name = "waha";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
try {
const config = {
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"X-Api-Key": notification.wahaApiKey,
}
};
let data = {
"session": notification.wahaSession,
"chatId": notification.wahaChatId,
"text": msg,
};
let url = notification.wahaApiUrl.replace(/([^/])\/+$/, "$1") + "/api/sendText";
await axios.post(url, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = WAHA;

View file

@ -1,7 +1,6 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const FormData = require("form-data"); const FormData = require("form-data");
const { Liquid } = require("liquidjs");
class Webhook extends NotificationProvider { class Webhook extends NotificationProvider {
name = "webhook"; name = "webhook";
@ -28,17 +27,7 @@ class Webhook extends NotificationProvider {
config.headers = formData.getHeaders(); config.headers = formData.getHeaders();
data = formData; data = formData;
} else if (notification.webhookContentType === "custom") { } else if (notification.webhookContentType === "custom") {
// Initialize LiquidJS and parse the custom Body Template data = await this.renderTemplate(notification.webhookCustomBody, msg, monitorJSON, heartbeatJSON);
const engine = new Liquid();
const tpl = engine.parse(notification.webhookCustomBody);
// Insert templated values into Body
data = await engine.render(tpl,
{
msg,
heartbeatJSON,
monitorJSON
});
} }
if (notification.webhookAdditionalHeaders) { if (notification.webhookAdditionalHeaders) {

View file

@ -0,0 +1,57 @@
const NotificationProvider = require("./notification-provider");
const { DOWN, UP } = require("../../src/util");
const { default: axios } = require("axios");
class YZJ extends NotificationProvider {
name = "YZJ";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
if (heartbeatJSON !== null) {
msg = `${this.statusToString(heartbeatJSON["status"])} ${monitorJSON["name"]} \n> ${heartbeatJSON["msg"]}\n> Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`;
}
const config = {
headers: {
"Content-Type": "application/json",
},
};
const params = {
content: msg
};
// yzjtype=0 => general robot
const url = `${notification.yzjWebHookUrl}?yzjtype=0&yzjtoken=${notification.yzjToken}`;
const result = await axios.post(url, params, config);
if (!result.data?.success) {
throw new Error(result.data?.errmsg ?? "yzj's server did not respond with the expected result");
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
/**
* Convert status constant to string
* @param {string} status The status constant
* @returns {string} status
*/
statusToString(status) {
switch (status) {
case DOWN:
return "❌";
case UP:
return "✅";
default:
return status;
}
}
}
module.exports = YZJ;

View file

@ -64,11 +64,13 @@ const ServerChan = require("./notification-providers/serverchan");
const ZohoCliq = require("./notification-providers/zoho-cliq"); const ZohoCliq = require("./notification-providers/zoho-cliq");
const SevenIO = require("./notification-providers/sevenio"); const SevenIO = require("./notification-providers/sevenio");
const Whapi = require("./notification-providers/whapi"); const Whapi = require("./notification-providers/whapi");
const WAHA = require("./notification-providers/waha");
const GtxMessaging = require("./notification-providers/gtx-messaging"); const GtxMessaging = require("./notification-providers/gtx-messaging");
const Cellsynt = require("./notification-providers/cellsynt"); const Cellsynt = require("./notification-providers/cellsynt");
const Onesender = require("./notification-providers/onesender"); const Onesender = require("./notification-providers/onesender");
const Wpush = require("./notification-providers/wpush"); const Wpush = require("./notification-providers/wpush");
const SendGrid = require("./notification-providers/send-grid"); const SendGrid = require("./notification-providers/send-grid");
const YZJ = require("./notification-providers/yzj");
class Notification { class Notification {
@ -151,10 +153,12 @@ class Notification {
new ZohoCliq(), new ZohoCliq(),
new SevenIO(), new SevenIO(),
new Whapi(), new Whapi(),
new WAHA(),
new GtxMessaging(), new GtxMessaging(),
new Cellsynt(), new Cellsynt(),
new Wpush(), new Wpush(),
new SendGrid() new SendGrid(),
new YZJ()
]; ];
for (let item of list) { for (let item of list) {
if (! item.name) { if (! item.name) {

View file

@ -163,6 +163,7 @@ export default {
"ZohoCliq": "ZohoCliq", "ZohoCliq": "ZohoCliq",
"SevenIO": "SevenIO", "SevenIO": "SevenIO",
"whapi": "WhatsApp (Whapi)", "whapi": "WhatsApp (Whapi)",
"waha": "WhatsApp (WAHA)",
"gtxmessaging": "GtxMessaging", "gtxmessaging": "GtxMessaging",
"Cellsynt": "Cellsynt", "Cellsynt": "Cellsynt",
"SendGrid": "SendGrid" "SendGrid": "SendGrid"
@ -183,6 +184,7 @@ export default {
"ServerChan": "ServerChan (Server酱)", "ServerChan": "ServerChan (Server酱)",
"smsc": "SMSC", "smsc": "SMSC",
"WPush": "WPush(wpush.cn)", "WPush": "WPush(wpush.cn)",
"YZJ": "YZJ (云之家自定义机器人)"
}; };
// Sort by notification name // Sort by notification name

View file

@ -0,0 +1,75 @@
<template>
<div class="form-text mb-2">
<i18n-t tag="div" keypath="liquidIntroduction">
<a href="https://liquidjs.com/" target="_blank">{{ $t("documentation") }}</a>
</i18n-t>
<code v-pre>{{ msg }}</code>: {{ $t("templateMsg") }}<br />
<code v-pre>{{ name }}</code>: {{ $t("templateServiceName") }}<br />
<code v-pre>{{ status }}</code>: {{ $t("templateStatus") }}<br />
<code v-pre>{{ hostnameOrURL }}</code>: {{ $t("templateHostnameOrURL") }}<br />
<code v-pre>{{ heartbeatJSON }}</code>: {{ $t("templateHeartbeatJSON") }} <b>({{ $t("templateLimitedToUpDownNotifications") }})</b><br />
<code v-pre>{{ monitorJSON }}</code>: {{ $t("templateMonitorJSON") }} <b>({{ $t("templateLimitedToUpDownCertNotifications") }})</b><br />
</div>
<input
:id="id"
ref="templatedInput"
v-model="model"
type="text"
class="form-control"
:placeholder="placeholder"
:required="required"
autocomplete="false"
>
</template>
<script>
export default {
props: {
/**
* The value of the templated input.
*/
modelValue: {
type: String,
default: ""
},
/**
* id for the templated input.
*/
id: {
type: String,
required: true,
},
/**
* Whether the templated input is required.
* @example true
*/
required: {
type: Boolean,
required: true,
},
/**
* Placeholder text for the templated input.
*/
placeholder: {
type: String,
default: ""
},
},
emits: [ "update:modelValue" ],
computed: {
/**
* Send value update to parent on change.
*/
model: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value);
}
}
},
};
</script>

View file

@ -0,0 +1,80 @@
<template>
<div class="form-text mb-2">
<i18n-t tag="div" keypath="liquidIntroduction">
<a href="https://liquidjs.com/" target="_blank">{{ $t("documentation") }}</a>
</i18n-t>
<code v-pre>{{ msg }}</code>: {{ $t("templateMsg") }}<br />
<code v-pre>{{ name }}</code>: {{ $t("templateServiceName") }}<br />
<code v-pre>{{ status }}</code>: {{ $t("templateStatus") }}<br />
<code v-pre>{{ hostnameOrURL }}</code>: {{ $t("templateHostnameOrURL") }}<br />
<code v-pre>{{ heartbeatJSON }}</code>: {{ $t("templateHeartbeatJSON") }} <b>({{ $t("templateLimitedToUpDownNotifications") }})</b><br />
<code v-pre>{{ monitorJSON }}</code>: {{ $t("templateMonitorJSON") }} <b>({{ $t("templateLimitedToUpDownCertNotifications") }})</b><br />
</div>
<textarea
:id="id"
ref="templatedTextarea"
v-model="model"
class="form-control"
:placeholder="placeholder"
:required="required"
autocomplete="false"
></textarea>
</template>
<script>
export default {
props: {
/**
* The value of the templated textarea.
*/
modelValue: {
type: String,
default: ""
},
/**
* id for the templated textarea.
*/
id: {
type: String,
required: true,
},
/**
* Whether the templated textarea is required.
* @example true
*/
required: {
type: Boolean,
required: true,
},
/**
* Placeholder text for the templated textarea.
*/
placeholder: {
type: String,
default: ""
},
},
emits: [ "update:modelValue" ],
computed: {
/**
* Send value update to parent on change.
*/
model: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value);
}
}
},
};
</script>
<style lang="scss" scoped>
textarea {
min-height: 150px;
}
</style>

View file

@ -67,25 +67,15 @@
<input id="to-bcc" v-model="$parent.notification.smtpBCC" type="text" class="form-control" autocomplete="false" :required="!hasRecipient"> <input id="to-bcc" v-model="$parent.notification.smtpBCC" type="text" class="form-control" autocomplete="false" :required="!hasRecipient">
</div> </div>
<p class="form-text">
<i18n-t tag="div" keypath="smtpLiquidIntroduction" class="form-text mb-3">
<a href="https://liquidjs.com/" target="_blank">{{ $t("documentation") }}</a>
</i18n-t>
<code v-pre>{{name}}</code>: {{ $t("emailTemplateServiceName") }}<br />
<code v-pre>{{msg}}</code>: {{ $t("emailTemplateMsg") }}<br />
<code v-pre>{{status}}</code>: {{ $t("emailTemplateStatus") }}<br />
<code v-pre>{{heartbeatJSON}}</code>: {{ $t("emailTemplateHeartbeatJSON") }}<b>{{ $t("emailTemplateLimitedToUpDownNotification") }}</b><br />
<code v-pre>{{monitorJSON}}</code>: {{ $t("emailTemplateMonitorJSON") }} <b>{{ $t("emailTemplateLimitedToUpDownNotification") }}</b><br />
<code v-pre>{{hostnameOrURL}}</code>: {{ $t("emailTemplateHostnameOrURL") }}<br />
</p>
<div class="mb-3"> <div class="mb-3">
<label for="subject-email" class="form-label">{{ $t("emailCustomSubject") }}</label> <label for="subject-email" class="form-label">{{ $t("emailCustomSubject") }}</label>
<input id="subject-email" v-model="$parent.notification.customSubject" type="text" class="form-control" autocomplete="false" placeholder=""> <TemplatedInput id="subject-email" v-model="$parent.notification.customSubject" :required="false" placeholder=""></TemplatedInput>
<div class="form-text">{{ $t("leave blank for default subject") }}</div> <div class="form-text">{{ $t("leave blank for default subject") }}</div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="body-email" class="form-label">{{ $t("emailCustomBody") }}</label> <label for="body-email" class="form-label">{{ $t("emailCustomBody") }}</label>
<textarea id="body-email" v-model="$parent.notification.customBody" type="text" class="form-control" autocomplete="false" placeholder=""></textarea> <TemplatedTextarea id="body-email" v-model="$parent.notification.customBody" :required="false" placeholder=""></TemplatedTextarea>
<div class="form-text">{{ $t("leave blank for default body") }}</div> <div class="form-text">{{ $t("leave blank for default body") }}</div>
</div> </div>
@ -124,11 +114,15 @@
<script> <script>
import HiddenInput from "../HiddenInput.vue"; import HiddenInput from "../HiddenInput.vue";
import TemplatedInput from "../TemplatedInput.vue";
import TemplatedTextarea from "../TemplatedTextarea.vue";
import ToggleSection from "../ToggleSection.vue"; import ToggleSection from "../ToggleSection.vue";
export default { export default {
components: { components: {
HiddenInput, HiddenInput,
TemplatedInput,
TemplatedTextarea,
ToggleSection, ToggleSection,
}, },
computed: { computed: {

View file

@ -36,7 +36,42 @@
<label for="server_url" class="form-label">{{ $t("telegramServerUrl") }}</label> <label for="server_url" class="form-label">{{ $t("telegramServerUrl") }}</label>
<input id="server_url" v-model="$parent.notification.telegramServerUrl" type="text" class="form-control"> <input id="server_url" v-model="$parent.notification.telegramServerUrl" type="text" class="form-control">
<p class="form-text">{{ $t("telegramServerUrlDescription") }}</p> <p class="form-text">{{ $t("telegramServerUrlDescription") }}</p>
</div>
<div class="mb-3">
<div class="form-check form-switch">
<input v-model="$parent.notification.telegramUseTemplate" class="form-check-input" type="checkbox">
<label class="form-check-label">{{ $t("telegramUseTemplate") }}</label>
</div>
<div class="form-text">
{{ $t("telegramUseTemplateDescription") }}
</div>
</div>
<template v-if="$parent.notification.telegramUseTemplate">
<div class="mb-3">
<label class="form-label" for="message_parse_mode">{{ $t("Message Format") }}</label>
<select
id="message_parse_mode"
v-model="$parent.notification.telegramTemplateParseMode"
class="form-select"
required
>
<option value="plain">{{ $t("Plain Text") }}</option>
<option value="HTML">HTML</option>
<option value="MarkdownV2">MarkdownV2</option>
</select>
<i18n-t tag="p" keypath="telegramTemplateFormatDescription" class="form-text">
<a href="https://core.telegram.org/bots/api#formatting-options" target="_blank">{{ $t("documentation") }}</a>
</i18n-t>
<label class="form-label" for="message_template">{{ $t('Message Template') }}</label>
<TemplatedTextarea id="message_template" v-model="$parent.notification.telegramTemplate" :required="true" :placeholder="telegramTemplatedTextareaPlaceholder"></TemplatedTextarea>
</div>
</template>
<div class="mb-3">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input v-model="$parent.notification.telegramSendSilently" class="form-check-input" type="checkbox"> <input v-model="$parent.notification.telegramSendSilently" class="form-check-input" type="checkbox">
<label class="form-check-label">{{ $t("telegramSendSilently") }}</label> <label class="form-check-label">{{ $t("telegramSendSilently") }}</label>
@ -61,15 +96,28 @@
<script> <script>
import HiddenInput from "../HiddenInput.vue"; import HiddenInput from "../HiddenInput.vue";
import TemplatedTextarea from "../TemplatedTextarea.vue";
import axios from "axios"; import axios from "axios";
export default { export default {
components: { components: {
HiddenInput, HiddenInput,
TemplatedTextarea,
}, },
mounted() { mounted() {
this.$parent.notification.telegramServerUrl ||= "https://api.telegram.org"; this.$parent.notification.telegramServerUrl ||= "https://api.telegram.org";
}, },
computed: {
telegramTemplatedTextareaPlaceholder() {
return this.$t("Example:", [
`
Uptime Kuma Alert{% if monitorJSON %} - {{ monitorJSON['name'] }}{% endif %}
{{ msg }}
`,
]);
}
},
methods: { methods: {
/** /**
* Get the URL for telegram updates * Get the URL for telegram updates
@ -123,3 +171,9 @@ export default {
} }
}; };
</script> </script>
<style lang="scss" scoped>
textarea {
min-height: 150px;
}
</style>

View file

@ -0,0 +1,38 @@
<template>
<div class="mb-3">
<label for="waha-api-url" class="form-label">{{ $t("API URL") }}</label>
<input id="waha-api-url" v-model="$parent.notification.wahaApiUrl" placeholder="http://localhost:3000/" type="url" class="form-control" required>
<div class="form-text">{{ $t("wayToGetWahaApiUrl") }}</div>
</div>
<div class="mb-3">
<label for="waha-api-key" class="form-label">{{ $t("API Key") }}</label>
<HiddenInput id="waha-api-key" v-model="$parent.notification.wahaApiKey" :required="false" autocomplete="new-password"></HiddenInput>
<div class="form-text">{{ $t("wayToGetWahaApiKey") }}</div>
</div>
<div class="mb-3">
<label for="waha-session" class="form-label">{{ $t("wahaSession") }}</label>
<input id="waha-session" v-model="$parent.notification.wahaSession" type="text" placeholder="default" class="form-control" required>
<div class="form-text">{{ $t("wayToGetWahaSession") }}</div>
</div>
<div class="mb-3">
<label for="waha-chat-id" class="form-label">{{ $t("wahaChatId") }}</label>
<input id="waha-chat-id" v-model="$parent.notification.wahaChatId" type="text" pattern="^[\d-]{10,31}$" class="form-control" required>
<div class="form-text">{{ $t("wayToWriteWahaChatId", ["00117612345678", "00117612345678@c.us", "123456789012345678@g.us"]) }}</div>
</div>
<i18n-t tag="div" keypath="More info on:" class="mb-3 form-text">
<a href="https://waha.devlike.pro/" target="_blank">https://waha.devlike.pro/</a>
</i18n-t>
</template>
<script>
import HiddenInput from "../HiddenInput.vue";
export default {
components: {
HiddenInput,
}
};
</script>

View file

@ -32,20 +32,7 @@
</template> </template>
</i18n-t> </i18n-t>
<template v-else-if="$parent.notification.webhookContentType == 'custom'"> <template v-else-if="$parent.notification.webhookContentType == 'custom'">
<i18n-t tag="div" keypath="liquidIntroduction" class="form-text"> <TemplatedTextarea id="customBody" v-model="$parent.notification.webhookCustomBody" :required="true" :placeholder="customBodyPlaceholder"></TemplatedTextarea>
<a href="https://liquidjs.com/" target="_blank">{{ $t("documentation") }}</a>
</i18n-t>
<code v-pre>{{msg}}</code>: {{ $t("templateMsg") }}<br />
<code v-pre>{{heartbeatJSON}}</code>: {{ $t("templateHeartbeatJSON") }} <b>({{ $t("templateLimitedToUpDownNotifications") }})</b><br />
<code v-pre>{{monitorJSON}}</code>: {{ $t("templateMonitorJSON") }} <b>({{ $t("templateLimitedToUpDownCertNotifications") }})</b><br />
<textarea
id="customBody"
v-model="$parent.notification.webhookCustomBody"
class="form-control"
:placeholder="customBodyPlaceholder"
required
></textarea>
</template> </template>
</div> </div>
@ -67,7 +54,12 @@
</template> </template>
<script> <script>
import TemplatedTextarea from "../TemplatedTextarea.vue";
export default { export default {
components: {
TemplatedTextarea,
},
data() { data() {
return { return {
showAdditionalHeadersField: this.$parent.notification.webhookAdditionalHeaders != null, showAdditionalHeadersField: this.$parent.notification.webhookAdditionalHeaders != null,

View file

@ -0,0 +1,19 @@
<template>
<div class="mb-3">
<label for="yzjWebHookUrl" class="form-label">{{ $t("YZJ Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="yzjWebHookUrl" v-model="$parent.notification.yzjWebHookUrl" type="url" class="form-control" required />
<i18n-t class="form-text" keypath="wayToGetTeamsURL">
<a href="https://www.yunzhijia.com/opendocs/docs.html#/tutorial/index/robot" target="_blank">{{ $t("here") }}</a>
</i18n-t>
</div>
<div class="mb-3">
<label for="yzjToken" class="form-label">{{ $t("YZJ Robot Token") }}<span style="color: red;"><sup>*</sup></span></label>
<HiddenInput id="yzjToken" v-model="$parent.notification.yzjToken" :required="true" autocomplete="new-password"></HiddenInput>
<i18n-t class="form-text" keypath="wayToGetLineNotifyToken">
<a href="https://www.yunzhijia.com/opendocs/docs.html#/server-api/im/index?id=%e6%8e%a5%e5%8f%a3%e5%9c%b0%e5%9d%80%e5%92%8c%e6%8e%88%e6%9d%83%e7%a0%81" target="_blank">{{ $t("here") }}</a>
</i18n-t>
</div>
</template>
<script setup lang="ts">
import HiddenInput from "../HiddenInput.vue";
</script>

View file

@ -63,10 +63,12 @@ import ZohoCliq from "./ZohoCliq.vue";
import Splunk from "./Splunk.vue"; import Splunk from "./Splunk.vue";
import SevenIO from "./SevenIO.vue"; import SevenIO from "./SevenIO.vue";
import Whapi from "./Whapi.vue"; import Whapi from "./Whapi.vue";
import WAHA from "./WAHA.vue";
import Cellsynt from "./Cellsynt.vue"; import Cellsynt from "./Cellsynt.vue";
import WPush from "./WPush.vue"; import WPush from "./WPush.vue";
import SIGNL4 from "./SIGNL4.vue"; import SIGNL4 from "./SIGNL4.vue";
import SendGrid from "./SendGrid.vue"; import SendGrid from "./SendGrid.vue";
import YZJ from "./YZJ.vue";
/** /**
* Manage all notification form. * Manage all notification form.
@ -138,10 +140,12 @@ const NotificationFormList = {
"ZohoCliq": ZohoCliq, "ZohoCliq": ZohoCliq,
"SevenIO": SevenIO, "SevenIO": SevenIO,
"whapi": Whapi, "whapi": Whapi,
"waha": WAHA,
"gtxmessaging": GtxMessaging, "gtxmessaging": GtxMessaging,
"Cellsynt": Cellsynt, "Cellsynt": Cellsynt,
"WPush": WPush, "WPush": WPush,
"SendGrid": SendGrid, "SendGrid": SendGrid,
"YZJ": YZJ,
}; };
export default NotificationFormList; export default NotificationFormList;

View file

@ -231,6 +231,9 @@
"templateMonitorJSON": "object describing the monitor", "templateMonitorJSON": "object describing the monitor",
"templateLimitedToUpDownCertNotifications": "only available for UP/DOWN/Certificate expiry notifications", "templateLimitedToUpDownCertNotifications": "only available for UP/DOWN/Certificate expiry notifications",
"templateLimitedToUpDownNotifications": "only available for UP/DOWN notifications", "templateLimitedToUpDownNotifications": "only available for UP/DOWN notifications",
"templateServiceName": "service name",
"templateHostnameOrURL": "hostname or URL",
"templateStatus": "status",
"webhookAdditionalHeadersTitle": "Additional Headers", "webhookAdditionalHeadersTitle": "Additional Headers",
"webhookAdditionalHeadersDesc": "Sets additional headers sent with the webhook. Each header should be defined as a JSON key/value.", "webhookAdditionalHeadersDesc": "Sets additional headers sent with the webhook. Each header should be defined as a JSON key/value.",
"webhookBodyPresetOption": "Preset - {0}", "webhookBodyPresetOption": "Preset - {0}",
@ -421,6 +424,9 @@
"telegramSendSilentlyDescription": "Sends the message silently. Users will receive a notification with no sound.", "telegramSendSilentlyDescription": "Sends the message silently. Users will receive a notification with no sound.",
"telegramProtectContent": "Protect Forwarding/Saving", "telegramProtectContent": "Protect Forwarding/Saving",
"telegramProtectContentDescription": "If enabled, the bot messages in Telegram will be protected from forwarding and saving.", "telegramProtectContentDescription": "If enabled, the bot messages in Telegram will be protected from forwarding and saving.",
"telegramUseTemplate": "Use custom message template",
"telegramUseTemplateDescription": "If enabled, the message will be sent using a custom template.",
"telegramTemplateFormatDescription": "Telegram allows using different markup languages for messages, see Telegram {0} for specifc details.",
"supportTelegramChatID": "Support Direct Chat / Group / Channel's Chat ID", "supportTelegramChatID": "Support Direct Chat / Group / Channel's Chat ID",
"wayToGetTelegramChatID": "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:", "wayToGetTelegramChatID": "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:",
"telegramServerUrl": "(Optional) Server Url", "telegramServerUrl": "(Optional) Server Url",
@ -521,9 +527,6 @@
"leave blank for default subject": "leave blank for default subject", "leave blank for default subject": "leave blank for default subject",
"emailCustomBody": "Custom Body", "emailCustomBody": "Custom Body",
"leave blank for default body": "leave blank for default body", "leave blank for default body": "leave blank for default body",
"emailTemplateServiceName": "Service Name",
"emailTemplateHostnameOrURL": "Hostname or URL",
"emailTemplateStatus": "Status",
"emailTemplateMonitorJSON": "object describing the monitor", "emailTemplateMonitorJSON": "object describing the monitor",
"emailTemplateHeartbeatJSON": "object describing the heartbeat", "emailTemplateHeartbeatJSON": "object describing the heartbeat",
"emailTemplateMsg": "message of the notification", "emailTemplateMsg": "message of the notification",
@ -1053,5 +1056,16 @@
"RabbitMQ Password": "RabbitMQ Password", "RabbitMQ Password": "RabbitMQ Password",
"rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.", "rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.",
"SendGrid API Key": "SendGrid API Key", "SendGrid API Key": "SendGrid API Key",
"Separate multiple email addresses with commas": "Separate multiple email addresses with commas" "Separate multiple email addresses with commas": "Separate multiple email addresses with commas",
"wahaSession": "Session",
"wahaChatId": "Chat ID (Phone Number / Contact ID / Group ID)",
"wayToGetWahaApiUrl": "Your WAHA Instance URL.",
"wayToGetWahaApiKey": "API Key is WHATSAPP_API_KEY environment variable value you used to run WAHA.",
"wayToGetWahaSession": "From this session WAHA sends notifications to Chat ID. You can find it in WAHA Dashboard.",
"wayToWriteWahaChatId": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}). Notifications are sent to this Chat ID from WAHA Session.",
"YZJ Webhook URL": "YZJ Webhook URL",
"YZJ Robot Token": "YZJ Robot token",
"Plain Text": "Plain Text",
"Message Template": "Message Template",
"Template Format": "Template Format"
} }