---
changelog.d/808.feature | 1 +
.../jsTransformationFunctions/alertmanager.js | 68 +++++++++++++++++++
2 files changed, 69 insertions(+)
create mode 100644 changelog.d/808.feature
create mode 100644 contrib/jsTransformationFunctions/alertmanager.js
diff --git a/changelog.d/808.feature b/changelog.d/808.feature
new file mode 100644
index 00000000..8ab64a6f
--- /dev/null
+++ b/changelog.d/808.feature
@@ -0,0 +1 @@
+Add generic webhook transformation JS snippet for Prometheus Alertmanager.
diff --git a/contrib/jsTransformationFunctions/alertmanager.js b/contrib/jsTransformationFunctions/alertmanager.js
new file mode 100644
index 00000000..11ec18aa
--- /dev/null
+++ b/contrib/jsTransformationFunctions/alertmanager.js
@@ -0,0 +1,68 @@
+/**
+ * This is a transformation function for Prometheus Alertmanager webhooks.
+ * https://prometheus.io/docs/alerting/latest/configuration/#webhook_config
+ *
+ * Creates a formatted `m.text` message with plaintext fallback, containing:
+ * - alert status and severity
+ * - alert name and description
+ * - URL to the entity that caused the alert
+ * The formatted message also contains a clickable link that silences the alert.
+ */
+
+/**
+ * @param status resolved or firing
+ * @param severity from the labels of the alert
+ * @returns colored text rendering of the status and severity
+ */
+function statusBadge(status, severity) {
+ let statusColor;
+ if (status === "resolved") {
+ return `[RESOLVED]`;
+ }
+
+ switch(severity) {
+ case 'resolved':
+ case 'critical':
+ return `[FIRING - CRITICAL]`;
+ case 'warning':
+ return `[FIRING - WARNING]`;
+ default:
+ return `[${status.toUpperCase()}]`;
+ }
+}
+
+/**
+ * @param alert object from the webhook payload
+ * @param externalURL from the webhook payload
+ * @returns a formatted link that will silence the alert when clicked
+ */
+function silenceLink(alert, externalURL) {
+ filters = []
+ for (const [label, val] of Object.entries(alert.labels)) {
+ filters.push(encodeURIComponent(`${label}="${val}"`));
+ }
+ return `silence`;
+}
+
+if (!data.alerts) {
+ result = {
+ version: 'v2',
+ empty: true,
+ };
+ return;
+}
+
+const plainErrors = [];
+const htmlErrors = [];
+const { externalURL, alerts } = data;
+
+for (const alert of data.alerts) {
+ plainErrors.push(`**[${alert.status.toUpperCase()} - ${alert.labels.severity}]** - ${alert.labels.alertname}: ${alert.annotations.description} [source](${alert.generatorURL})`);
+ htmlErrors.push(`${statusBadge(alert.status, alert.labels.severity)}
${alert.labels.alertname}: ${alert.annotations.description.replaceAll("\n","
")}
source | ${silenceLink(alert, externalURL)}
`)
+ result = {
+ version: 'v2',
+ plain: plainErrors.join(`\n\n`),
+ html: htmlErrors.join(`
`),
+ msgtype: 'm.text'
+ };
+}