Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 157 additions & 71 deletions services/tasks/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type alertTask struct {

type alertChat struct {
ID string
ThreadID string // ID темы (опционально)
}

func (t *TaskRunner) sendMailAlert() {
Expand Down Expand Up @@ -110,78 +111,163 @@ func (t *TaskRunner) sendMailAlert() {
}
}

func (t *TaskRunner) sendTelegramAlert() {
if !util.Config.TelegramAlert || !t.alert {
return
}

if t.Template.SuppressSuccessAlerts && t.Task.Status == task_logger.TaskSuccessStatus {
return
}
//func (t *TaskRunner) sendTelegramAlert() {
// if !util.Config.TelegramAlert || !t.alert {
// return
// }
//
// if t.Template.SuppressSuccessAlerts && t.Task.Status == task_logger.TaskSuccessStatus {
// return
// }
//
// chatID := util.Config.TelegramChat
// if t.alertChat != nil && *t.alertChat != "" {
// chatID = *t.alertChat
// }
//
// if chatID == "" {
// return
// }
//
// body := bytes.NewBufferString("")
// author, version := t.alertInfos()
//
Comment on lines +114 to +134
Copy link
Preview

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider removing the large commented-out block of the old sendTelegramAlert implementation to reduce dead code and improve readability.

Suggested change
//func (t *TaskRunner) sendTelegramAlert() {
// if !util.Config.TelegramAlert || !t.alert {
// return
// }
//
// if t.Template.SuppressSuccessAlerts && t.Task.Status == task_logger.TaskSuccessStatus {
// return
// }
//
// chatID := util.Config.TelegramChat
// if t.alertChat != nil && *t.alertChat != "" {
// chatID = *t.alertChat
// }
//
// if chatID == "" {
// return
// }
//
// body := bytes.NewBufferString("")
// author, version := t.alertInfos()
//
// Removed commented-out block of the old `sendTelegramAlert` implementation to reduce dead code and improve readability.

Copilot uses AI. Check for mistakes.

// alert := Alert{
// Name: t.Template.Name,
// Author: author,
// Color: t.alertColor("telegram"),
// Task: alertTask{
// ID: strconv.Itoa(t.Task.ID),
// URL: t.taskLink(),
// Result: t.Task.Status.Format(),
// Version: version,
// Desc: t.Task.Message,
// },
// Chat: alertChat{
// ID: chatID,
// },
// }
//
// tpl, err := template.ParseFS(templates, "templates/telegram.tmpl")
//
// if err != nil {
// t.Log("Can't parse telegram alert template!")
// panic(err)
// }
//
// if err := tpl.Execute(body, alert); err != nil {
// t.Log("Can't generate telegram alert template!")
// panic(err)
// }
//
// if body.Len() == 0 {
// t.Log("Buffer for telegram alert is empty")
// return
// }
//
// t.Log("Attempting to send telegram alert")
//
// resp, err := http.Post(
// fmt.Sprintf(
// "https://api.telegram.org/bot%s/sendMessage",
// util.Config.TelegramToken,
// ),
// "application/json",
// body,
// )
//
// if err != nil {
// t.Log("Can't send telegram alert! Error: " + err.Error())
// } else if resp.StatusCode != 200 {
// t.Log("Can't send telegram alert! Response code: " + strconv.Itoa(resp.StatusCode))
// }
//
// t.Log("Sent successfully telegram alert")
//}

chatID := util.Config.TelegramChat
if t.alertChat != nil && *t.alertChat != "" {
chatID = *t.alertChat
}

if chatID == "" {
return
}

body := bytes.NewBufferString("")
author, version := t.alertInfos()

alert := Alert{
Name: t.Template.Name,
Author: author,
Color: t.alertColor("telegram"),
Task: alertTask{
ID: strconv.Itoa(t.Task.ID),
URL: t.taskLink(),
Result: t.Task.Status.Format(),
Version: version,
Desc: t.Task.Message,
},
Chat: alertChat{
ID: chatID,
},
}

tpl, err := template.ParseFS(templates, "templates/telegram.tmpl")

if err != nil {
t.Log("Can't parse telegram alert template!")
panic(err)
}

if err := tpl.Execute(body, alert); err != nil {
t.Log("Can't generate telegram alert template!")
panic(err)
}

if body.Len() == 0 {
t.Log("Buffer for telegram alert is empty")
return
}

t.Log("Attempting to send telegram alert")

resp, err := http.Post(
fmt.Sprintf(
"https://api.telegram.org/bot%s/sendMessage",
util.Config.TelegramToken,
),
"application/json",
body,
)

if err != nil {
t.Log("Can't send telegram alert! Error: " + err.Error())
} else if resp.StatusCode != 200 {
t.Log("Can't send telegram alert! Response code: " + strconv.Itoa(resp.StatusCode))
}

t.Log("Sent successfully telegram alert")
func (t *TaskRunner) sendTelegramAlert() {
if !util.Config.TelegramAlert || !t.alert {
return
}

if t.Template.SuppressSuccessAlerts && t.Task.Status == task_logger.TaskSuccessStatus {
return
}

chatID := util.Config.TelegramChat
threadID := util.Config.TelegramThreadID // Новый параметр конфига

if t.alertChat != nil && *t.alertChat != "" {
chatID = *t.alertChat
}

if chatID == "" {
return
}

body := bytes.NewBufferString("")
author, version := t.alertInfos()

alert := Alert{
Name: t.Template.Name,
Author: author,
Color: t.alertColor("telegram"),
Task: alertTask{
ID: strconv.Itoa(t.Task.ID),
URL: t.taskLink(),
Result: t.Task.Status.Format(),
Version: version,
Desc: t.Task.Message,
},
Chat: alertChat{
ID: chatID,
ThreadID: threadID, // Добавляем ID темы
},
}

tpl, err := template.ParseFS(templates, "templates/telegram.tmpl")

if err != nil {
t.Log("Can't parse telegram alert template!")
panic(err)
}

if err := tpl.Execute(body, alert); err != nil {
t.Log("Can't generate telegram alert template!")
panic(err)
}

if body.Len() == 0 {
t.Log("Buffer for telegram alert is empty")
return
}

t.Log("Attempting to send telegram alert")

// Формируем базовый URL
apiUrl := fmt.Sprintf(
"https://api.telegram.org/bot%s/sendMessage",
util.Config.TelegramToken,
)

// Если указан threadID, добавляем его как параметр запроса
if threadID != "" {
apiUrl += fmt.Sprintf("?message_thread_id=%s", threadID)
}

resp, err := http.Post(
apiUrl,
"application/json",
body,
Comment on lines +253 to +261
Copy link
Preview

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appending message_thread_id as a query parameter duplicates the JSON payload and may not be supported by the Telegram API; it would be more reliable to include this field only in the JSON body generated by the template.

Suggested change
// Если указан threadID, добавляем его как параметр запроса
if threadID != "" {
apiUrl += fmt.Sprintf("?message_thread_id=%s", threadID)
}
resp, err := http.Post(
apiUrl,
"application/json",
body,
// Include threadID in the JSON payload if specified
if threadID != "" {
alert["message_thread_id"] = threadID
}
resp, err := http.Post(
apiUrl,
"application/json",
bytes.NewBuffer(body.Bytes()),

Copilot uses AI. Check for mistakes.

)

if err != nil {
t.Log("Can't send telegram alert! Error: " + err.Error())
} else if resp.StatusCode != 200 {
t.Log("Can't send telegram alert! Response code: " + strconv.Itoa(resp.StatusCode))
}

t.Log("Sent successfully telegram alert")
}

func (t *TaskRunner) sendSlackAlert() {
Expand Down
1 change: 1 addition & 0 deletions services/tasks/templates/telegram.tmpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"chat_id": "{{ .Chat.ID }}",
{{ if .Chat.ThreadID }}"message_thread_id": "{{ .Chat.ThreadID }}",{{ end }}
"parse_mode": "HTML",
"text": "<code>{{ .Name }}</code>\n#{{ .Task.ID }} <b>{{ .Task.Result }}</b> <code>{{ .Task.Version }}</code> - {{ .Task.Desc }}\nby {{ .Author }}\n{{ .Task.URL }}"
}
1 change: 1 addition & 0 deletions util/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ type ConfigType struct {
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"`
TelegramThreadID string `json:"telegram_thread_id" env:"SEMAPHORE_TELEGRAM_THREAD_ID"` // Новый параметр
Copy link
Preview

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding omitempty to the json:"telegram_thread_id" tag to keep this optional field consistent with other config values and prevent serializing empty strings.

Suggested change
TelegramThreadID string `json:"telegram_thread_id" env:"SEMAPHORE_TELEGRAM_THREAD_ID"` // Новый параметр
TelegramThreadID string `json:"telegram_thread_id,omitempty" env:"SEMAPHORE_TELEGRAM_THREAD_ID"` // Новый параметр

Copilot uses AI. Check for mistakes.

SlackAlert bool `json:"slack_alert,omitempty" env:"SEMAPHORE_SLACK_ALERT"`
SlackUrl string `json:"slack_url,omitempty" env:"SEMAPHORE_SLACK_URL"`
RocketChatAlert bool `json:"rocketchat_alert,omitempty" env:"SEMAPHORE_ROCKETCHAT_ALERT"`
Expand Down
Loading