Repository navigation
24 add error notifications via discord on task failures #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
krzys13
wants to merge
3
commits into
main
Choose a base branch
from
24-add-error-notifications-via-discord-on-task-failures
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
krzys13 marked this conversation as resolved.
Resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import { DateTime } from "luxon"; | ||
|
|
||
| import DiscordWebhookManager from "./discord_webhook_manager.js"; | ||
|
|
||
| const ONE_HOUR_SECONDS = 3600; | ||
| export const commandDiscordManager = new DiscordWebhookManager( | ||
| DateTime.fromSeconds(ONE_HOUR_SECONDS), | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import type { DateTime } from "luxon"; | ||
|
|
||
| import DiscordWebhookSender from "./discord_webhook_sender.js"; | ||
| import type { DiscordWebhookRequestInit } from "./discord_webhook_sender.js"; | ||
|
|
||
| /** Notification type determining the emoji and label in the message title. */ | ||
| export enum MessageType { | ||
| INFO, | ||
| WARNING, | ||
| ERROR, | ||
| } | ||
| export interface MessagePayload { | ||
| messageType: MessageType; | ||
| title: string; | ||
| description: string; | ||
| timestamp: DateTime; | ||
| } | ||
| const MILISECONDS_IN_MINUTE = 60000; | ||
|
|
||
| /** Returns the title prefix, such as "❌ [ERROR]", for the notification type. */ | ||
| function messageTypeToString(messageType: MessageType): string { | ||
| switch (messageType) { | ||
| case MessageType.INFO: | ||
| return "ℹ️ [INFO]"; | ||
| case MessageType.WARNING: | ||
| return "⚠️ [WARNING]"; | ||
| case MessageType.ERROR: | ||
| return "❌ [ERROR]"; | ||
| } | ||
| } | ||
|
|
||
| export default class DiscordWebhookManager { | ||
| private sender = DiscordWebhookSender.getInstance(); | ||
|
|
||
| private discordWebhookMessageBuffor: MessagePayload[]; | ||
| private repetitionTimeLimit: DateTime; | ||
|
|
||
| public constructor(repetitionLimit: DateTime) { | ||
| this.discordWebhookMessageBuffor = []; | ||
| this.repetitionTimeLimit = repetitionLimit; | ||
| } | ||
|
|
||
| private hasBeenSend(payload: MessagePayload, timesSpan: DateTime): boolean { | ||
| const { timestamp: payloadTimestamp, ...payloadContent } = payload; | ||
|
|
||
| const hasFlag = this.discordWebhookMessageBuffor.some((value) => { | ||
| const { timestamp: valueTimestamp, ...valueContent } = value; | ||
| const minutesTimeSpan = Math.floor( | ||
| timesSpan.toMillis() / MILISECONDS_IN_MINUTE, | ||
| ); | ||
| const minutesdTimeDiff = Math.floor( | ||
| payloadTimestamp.diff(valueTimestamp).toMillis() / | ||
| MILISECONDS_IN_MINUTE, | ||
| ); | ||
|
|
||
| return ( | ||
| JSON.stringify(payloadContent) === JSON.stringify(valueContent) && | ||
| minutesdTimeDiff < minutesTimeSpan | ||
| ); | ||
| }); | ||
| return hasFlag; | ||
| } | ||
|
|
||
| public sendMessage(payload: MessagePayload) { | ||
| const requestInit: DiscordWebhookRequestInit = { | ||
| embeds: [ | ||
| { | ||
| title: `${messageTypeToString(payload.messageType)}: ${payload.title}`, | ||
| description: payload.description, | ||
| timestamp: payload.timestamp.toISO() as unknown as string, | ||
| }, | ||
| ], | ||
| }; | ||
| if (!this.hasBeenSend(payload, this.repetitionTimeLimit)) { | ||
| const res = this.sender.sendWebhook(requestInit); | ||
| this.discordWebhookMessageBuffor.push(payload); | ||
|
|
||
| return res; | ||
| } else { | ||
| throw new Error( | ||
| "Message to Discord was not send due to repetitionTimeLimit. Look for repetitionTimeLimit to change antyspam blocker", | ||
| ); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| import env from "#start/env"; | ||
|
|
||
| export interface DiscordWebhookRequestInit { | ||
| embeds: DiscordWebhookPayload[]; | ||
| } | ||
| // Can be extended by fields supported by Discord Api. | ||
| interface DiscordWebhookPayload { | ||
| title: string; | ||
| description?: string; | ||
| /** Event time as an ISO 8601 string, e.g. "2026-11-06T12:00:00Z". */ | ||
| timestamp?: string; | ||
| } | ||
|
|
||
| /** | ||
| * Sends Discord notifications with one embed per message. | ||
| * | ||
| * Requires the `DISORD_WEBHOOK_URL` environment variable with the webhook URL. | ||
| * The URL is read on the first send and reused for subsequent calls. | ||
| * Methods return the HTTP response; callers should check `response.ok`. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import DiscordWebhookSender, { MessageType } from "./discord_webhook_sender.js"; | ||
| * import { DateTime } from "luxon"; | ||
| * | ||
| * const response = await DiscordWebhookSender.sendMessage( | ||
| * MessageType.INFO, | ||
| * "Parking synchronization", | ||
| * "Parking data has been updated.", | ||
| * DateTime.now(), | ||
| * ); | ||
| * | ||
| * if (!response.ok) { | ||
| * throw new Error(`Failed to send notification: ${response.status}`); | ||
| * } | ||
| * ``` | ||
| */ | ||
| export default class DiscordWebhookSender { | ||
| private static instance: DiscordWebhookSender | undefined; | ||
|
|
||
| private static url: string; | ||
|
|
||
| private constructor() { | ||
| DiscordWebhookSender.url = env.get( | ||
| "DISORD_WEBHOOK_URL", | ||
| ) as unknown as string; | ||
| } | ||
| public static getInstance(): DiscordWebhookSender { | ||
| DiscordWebhookSender.instance ??= new DiscordWebhookSender(); | ||
| return DiscordWebhookSender.instance; | ||
| } | ||
|
|
||
| /** | ||
| * Sends a POST request with an embed containing the title, body, and event time. | ||
| * Adds an emoji and an INFO, WARNING, or ERROR label to the title. | ||
| * Accepts the event time as an ISO 8601 string. | ||
| * | ||
| * @param messageType - Notification type determining the title prefix. | ||
| * @param title - Notification title without the type prefix. | ||
| * @param description - Notification body. | ||
| * @param timestamp - Event time as an ISO 8601 string, e.g. "2026-11-06T12:00:00Z". | ||
| * @returns The webhook's HTTP response. Status codes 4xx and 5xx do not throw; | ||
| * callers should check `response.ok` or `response.status`. | ||
| * @throws If the fetch request fails, for example due to a network error | ||
| * or an invalid webhook URL. | ||
| */ | ||
|
|
||
| public async sendWebhook( | ||
| requestInit: DiscordWebhookRequestInit, | ||
| ): Promise<Response> { | ||
| DiscordWebhookSender.getInstance(); | ||
|
|
||
| const res = await fetch(DiscordWebhookSender.url, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify(requestInit), | ||
| }); | ||
| return res; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.