From 08a0add8ca0b44f8701dd71559881a91b13d021b Mon Sep 17 00:00:00 2001 From: BlockyBlockling <51190350+BlockyBlockling@users.noreply.github.com> Date: Fri, 10 Oct 2025 17:26:28 +0200 Subject: [PATCH 1/3] Adding Discord Alert config Adding config options for discord alerting --- util/config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/config.go b/util/config.go index 65d1a76a2..f535e6177 100644 --- a/util/config.go +++ b/util/config.go @@ -278,7 +278,7 @@ type ConfigType struct { LdapMappings *LdapMappings `json:"ldap_mappings,omitempty"` LdapNeedTLS bool `json:"ldap_needtls,omitempty" env:"SEMAPHORE_LDAP_NEEDTLS"` - // Telegram, Slack, Rocket.Chat, Microsoft Teams, DingTalk, and Gotify alerting + // Telegram, Slack, Rocket.Chat, Microsoft Teams, DingTalk, Gotify and Discord alerting TelegramAlert bool `json:"telegram_alert,omitempty" env:"SEMAPHORE_TELEGRAM_ALERT"` TelegramChat string `json:"telegram_chat,omitempty" env:"SEMAPHORE_TELEGRAM_CHAT"` TelegramToken string `json:"telegram_token,omitempty" env:"SEMAPHORE_TELEGRAM_TOKEN"` @@ -293,6 +293,8 @@ type ConfigType struct { GotifyAlert bool `json:"gotify_alert,omitempty" env:"SEMAPHORE_GOTIFY_ALERT"` GotifyUrl string `json:"gotify_url,omitempty" env:"SEMAPHORE_GOTIFY_URL"` GotifyToken string `json:"gotify_token,omitempty" env:"SEMAPHORE_GOTIFY_TOKEN"` + DiscordAlert bool `json:"discord_alert,omitempty" env:"SEMAPHORE_DISCORD_ALERT"` + DiscordUrl string `json:"discord_url,omitempty" env:"SEMAPHORE_DISCORD_URL"` // oidc settings OidcProviders map[string]OidcProvider `json:"oidc_providers,omitempty" env:"SEMAPHORE_OIDC_PROVIDERS"` From 4bf59bd07bbc2620b4c0fe1fa8a3da93fc36990e Mon Sep 17 00:00:00 2001 From: Blocky Blockling Date: Fri, 10 Oct 2025 19:05:25 +0200 Subject: [PATCH 2/3] Fixing config change indentation --- util/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/config.go b/util/config.go index f535e6177..121a8e1b3 100644 --- a/util/config.go +++ b/util/config.go @@ -293,8 +293,8 @@ type ConfigType struct { GotifyAlert bool `json:"gotify_alert,omitempty" env:"SEMAPHORE_GOTIFY_ALERT"` GotifyUrl string `json:"gotify_url,omitempty" env:"SEMAPHORE_GOTIFY_URL"` GotifyToken string `json:"gotify_token,omitempty" env:"SEMAPHORE_GOTIFY_TOKEN"` - DiscordAlert bool `json:"discord_alert,omitempty" env:"SEMAPHORE_DISCORD_ALERT"` - DiscordUrl string `json:"discord_url,omitempty" env:"SEMAPHORE_DISCORD_URL"` + DiscordAlert bool `json:"discord_alert,omitempty" env:"SEMAPHORE_DISCORD_ALERT"` + DiscordUrl string `json:"discord_url,omitempty" env:"SEMAPHORE_DISCORD_URL"` // oidc settings OidcProviders map[string]OidcProvider `json:"oidc_providers,omitempty" env:"SEMAPHORE_OIDC_PROVIDERS"` From 11f5e720b1677cb599c744bce07699d69bd257b5 Mon Sep 17 00:00:00 2001 From: Blocky Blockling Date: Sat, 11 Oct 2025 18:15:42 +0200 Subject: [PATCH 3/3] Adding discord notification config --- cli/setup/setup.go | 5 +++ services/tasks/TaskRunner_logging.go | 1 + services/tasks/alert.go | 63 +++++++++++++++++++++++++++ services/tasks/alert_test_sender.go | 1 + services/tasks/templates/discord.tmpl | 24 ++++++++++ 5 files changed, 94 insertions(+) create mode 100644 services/tasks/templates/discord.tmpl diff --git a/cli/setup/setup.go b/cli/setup/setup.go index 19d76b0fc..54b9d680a 100644 --- a/cli/setup/setup.go +++ b/cli/setup/setup.go @@ -160,6 +160,11 @@ func InteractiveSetup(conf *util.ConfigType) { askValue("Slack Webhook URL", "", &conf.SlackUrl) } + askConfirmation("Enable discord alerts?", false, &conf.DiscordAlert) + if conf.DiscordAlert { + askValue("Discord Webhook URL", "", &conf.DiscordUrl) + } + askConfirmation("Enable Rocket.Chat alerts?", false, &conf.RocketChatAlert) if conf.RocketChatAlert { askValue("Rocket.Chat Webhook URL", "", &conf.RocketChatUrl) diff --git a/services/tasks/TaskRunner_logging.go b/services/tasks/TaskRunner_logging.go index 0dca84e7d..b8d0e0828 100644 --- a/services/tasks/TaskRunner_logging.go +++ b/services/tasks/TaskRunner_logging.go @@ -128,6 +128,7 @@ func (t *TaskRunner) SetStatus(status task_logger.TaskStatus) { t.sendMicrosoftTeamsAlert() t.sendDingTalkAlert() t.sendGotifyAlert() + t.sendDiscordAlert() } for _, l := range t.statusListeners { diff --git a/services/tasks/alert.go b/services/tasks/alert.go index c1b088cfb..490e89bc8 100644 --- a/services/tasks/alert.go +++ b/services/tasks/alert.go @@ -504,6 +504,69 @@ func (t *TaskRunner) sendGotifyAlert() { } } +func (t *TaskRunner) sendDiscordAlert() { + if !util.Config.DiscordAlert || !t.alert { + return + } + + if t.Template.SuppressSuccessAlerts && t.Task.Status == task_logger.TaskSuccessStatus { + return + } + + body := bytes.NewBufferString("") + author, version := t.alertInfos() + + alert := Alert{ + Name: t.Template.Name, + Author: author, + Color: t.alertColor("discord"), + Task: alertTask{ + ID: strconv.Itoa(t.Task.ID), + URL: t.taskLink(), + Result: t.Task.Status.Format(), + Version: version, + Desc: t.Task.Message, + }, + } + + tpl, err := template.ParseFS(templates, "templates/discord.tmpl") + + if err != nil { + t.Log("Can't parse discord alert template!") + panic(err) + } + + if err := tpl.Execute(body, alert); err != nil { + t.Log("Can't generate discord alert template!") + panic(err) + } + + if body.Len() == 0 { + t.Log("Buffer for discord alert is empty") + return + } + + t.Log("Attempting to send discord alert") + + resp, err := http.Post( + util.Config.DiscordUrl, + "application/json", + body, + ) + + if err != nil { + t.Log("Can't send discord alert! Error: " + err.Error()) + } else if resp.StatusCode != 200 { + t.Log("Can't send discord alert! Response code: " + strconv.Itoa(resp.StatusCode)) + } else { + t.Log("Sent successfully discord alert") + } + + if resp != nil { + defer resp.Body.Close() //nolint:errcheck + } +} + func (t *TaskRunner) alertInfos() (string, string) { version := "" diff --git a/services/tasks/alert_test_sender.go b/services/tasks/alert_test_sender.go index 304e2ac71..0f4e359fc 100644 --- a/services/tasks/alert_test_sender.go +++ b/services/tasks/alert_test_sender.go @@ -46,6 +46,7 @@ func SendProjectTestAlerts(project db.Project, store db.Store) (err error) { tr.sendMicrosoftTeamsAlert() tr.sendDingTalkAlert() tr.sendGotifyAlert() + tr.sendDiscordAlert() tr.sendMailAlert() return diff --git a/services/tasks/templates/discord.tmpl b/services/tasks/templates/discord.tmpl new file mode 100644 index 000000000..763fb48ee --- /dev/null +++ b/services/tasks/templates/discord.tmpl @@ -0,0 +1,24 @@ +{ + "username": "Semaphore", + "content": "", + "embeds": [{ + "title": "Task: {{ .Name }}", + "url": "{{ .Task.URL }}", + "description": "execution #{{ .Task.ID }}, status: {{ .Task.Result }}!", + "color": "{{ .Color }}", + "fields": [ + { + "name": "Author", + "value": "{{ .Author }}", + "inline": true + {{ if .Task.Version }} + }, + { + "name": "Version", + "value": "{{ .Task.Version }}", + "inline": true + {{ end }} + } + ] + }] +}