···10101111An [Alerter](https://komo.do/docs/resources#alerter) that pushes to [ntfy](https://ntfy.sh/)
12121313-[See README](/notifiers/ntfy/README.md)1313+[See README](/notifiers/ntfy/README.md)
1414+1515+## Discord Webhook Alerter
1616+1717+An [Alerter](https://komo.do/docs/resources#alerter) that pushes to a [Discord Webhook](https://discordjs.guide/popular-topics/webhooks.html#what-is-a-webhook)
1818+1919+[See README](/notifiers/discord/README.md)
···11+FROM denoland/deno:2.0.4
22+33+# The port that your application listens to.
44+EXPOSE 7000
55+66+WORKDIR /app
77+88+# Prefer not to run as root.
99+USER deno
1010+1111+# These steps will be re-run upon each file change in your working directory:
1212+COPY common/ common/
1313+COPY notifiers/common/ notifiers/common/
1414+COPY notifiers/discord/ notifiers/discord/
1515+1616+CMD ["run", "--allow-net", "--allow-env", "notifiers/discord/main.ts"]
+13
notifiers/discord/README.md
···11+A [Komodo](https://komo.do/) [Alerter](https://komo.do/docs/resources#alerter) client for [Discord Webhooks](https://discordjs.guide/popular-topics/webhooks.html#what-is-a-webhook)
22+33+# Usage
44+55+See [Komodohub deploy instructions](https://github.com/FoxxMD/deploy-discord-alerter)
66+77+# Building
88+99+Run from the repository top-level folder:
1010+1111+```shell
1212+docker build -t komodo-discord-alerter -f notifiers/discord/Dockerfile .
1313+```
+3
notifiers/discord/main.ts
···11+import {program} from './program.ts';
22+33+program();
+102
notifiers/discord/program.ts
···11+import { Types } from "npm:komodo_client";
22+import { EmbedBuilder, WebhookClient } from 'npm:discord.js';
33+import { CommonAlert, parseAlert } from "../common/alertParser.ts";
44+import { valToBoolean } from "../../common/utils.ts";
55+import path from 'node:path';
66+77+const program = () => {
88+99+const DISCORD_WEBHOOK: string = Deno.env.get("DISCORD_WEBHOOK") as string;
1010+if (DISCORD_WEBHOOK === undefined || DISCORD_WEBHOOK.trim() === "") {
1111+ console.error("DISCORD_WEBHOOK not defined in ENV");
1212+ Deno.exit(1);
1313+}
1414+1515+console.log(`Webhook : ${DISCORD_WEBHOOK}`);
1616+1717+const hookParts = path.parse(DISCORD_WEBHOOK);
1818+const token = hookParts.name;
1919+const id = hookParts.dir.split('/').pop();
2020+2121+if(id === undefined) {
2222+ console.error("Could not find ID in DISCORD_WEBHOOK. Is the webhook URL properly formed? https://discordjs.guide/popular-topics/webhooks.html#creating-webhooks-through-server-settings");
2323+ Deno.exit(1);
2424+}
2525+console.info(`Webhook ID : ${id}`);
2626+if(token === undefined) {
2727+ console.error("Could not find token in DISCORD_WEBHOOK. Is the webhook URL properly formed? https://discordjs.guide/popular-topics/webhooks.html#creating-webhooks-through-server-settings");
2828+ Deno.exit(1);
2929+}
3030+console.info(`Webhook Token: ${token}`);
3131+3232+let levelInTitle: boolean | undefined;
3333+try {
3434+ levelInTitle = valToBoolean(Deno.env.get('LEVEL_IN_TITLE'));
3535+} catch (e) {
3636+ console.warn('Could not parse LEVEL_IN_TITLE to a truthy value, will use notifier default', {cause: e});
3737+}
3838+3939+4040+const pushAlert = async (data: CommonAlert, level: Types.SeverityLevel): Promise<any> => {
4141+ const embed = new EmbedBuilder();
4242+ switch(level) {
4343+ case Types.SeverityLevel.Ok:
4444+ embed.setColor('#58b9ff');
4545+ break;
4646+ case Types.SeverityLevel.Warning:
4747+ embed.setColor('#fa8020');
4848+ break;
4949+ case Types.SeverityLevel.Critical:
5050+ embed.setColor('#fa2020');
5151+ break;
5252+ }
5353+5454+ embed.setTitle(data.title);
5555+ if(data.subtitle !== undefined) {
5656+ embed.setDescription(data.subtitle);
5757+ }
5858+ if(data.message !== undefined) {
5959+ embed.addFields({name: 'Message', value: data.message});
6060+ }
6161+6262+ const client = new WebhookClient({id, token});
6363+ try {
6464+ const msg = await client.send({
6565+ embeds: [embed]
6666+ });
6767+ } catch (e) {
6868+ throw new Error('Failed to send Discord Webhook', {cause: e})
6969+ }
7070+}
7171+7272+const server = Deno.serve({ port: 7000 }, async (req) => {
7373+ const alert: Types.Alert = await req.json();
7474+ console.log(`Recieved data from ${req.headers.get("host")}...`);
7575+7676+ let data: CommonAlert;
7777+7878+ try {
7979+ data = parseAlert(alert, {levelInTitle, markdown: true});
8080+ } catch (e) {
8181+ console.debug('Komodo Alert Payload:', alert);
8282+ console.error(e);
8383+ return new Response();
8484+ }
8585+8686+ try {
8787+ await pushAlert(data, alert.level)
8888+ } catch (e) {
8989+ console.debug('Komodo Alert Payload:', alert);
9090+ console.error(
9191+ new Error("Failed to push Alert to Gotify", { cause: e }),
9292+ );
9393+ }
9494+9595+ return new Response();
9696+});
9797+9898+return server;
9999+100100+}
101101+102102+export {program};