feat: publish standalone worker
Separate worker packaging and service lifecycle from the control plane.
Этот коммит содержится в:
66
internal/notify/telegram.go
Обычный файл
66
internal/notify/telegram.go
Обычный файл
@@ -0,0 +1,66 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
|
||||
"rsgit.ru/rsmon/rsmon/app/models"
|
||||
)
|
||||
|
||||
// Telegram sends a text message to the given chat ID using the given Telegram
|
||||
// bot credential. chatID is the numeric Telegram chat id assigned by Telegram
|
||||
// to a private chat, group or channel.
|
||||
func Telegram(cred *models.NotificationCredential, chatID int64, text string) error {
|
||||
if cred == nil {
|
||||
return fmt.Errorf("credential is nil")
|
||||
}
|
||||
if cred.Kind != models.CredentialKindTelegram {
|
||||
return fmt.Errorf("credential %d is not telegram (kind=%s)", cred.ID, cred.Kind)
|
||||
}
|
||||
|
||||
token, err := cred.GetSecret()
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt telegram token: %w", err)
|
||||
}
|
||||
if token == "" {
|
||||
return fmt.Errorf("credential %d has empty token", cred.ID)
|
||||
}
|
||||
|
||||
apiURL := ""
|
||||
if cred.APIURL != nil && *cred.APIURL != "" {
|
||||
apiURL = *cred.APIURL
|
||||
}
|
||||
|
||||
bot, err := tgbotapi.NewBotAPIWithAPIEndpoint(token, telegramEndpoint(apiURL))
|
||||
if err != nil {
|
||||
return fmt.Errorf("init telegram bot: %w", err)
|
||||
}
|
||||
|
||||
msg := tgbotapi.NewMessage(chatID, text)
|
||||
if _, err := bot.Send(msg); err != nil {
|
||||
return fmt.Errorf("send telegram via credential %d: %w", cred.ID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// telegramEndpoint returns the Bot API endpoint pattern that tgbotapi expects
|
||||
// (".../bot%s/%s"). If rawURL is empty, returns the default Telegram endpoint.
|
||||
//
|
||||
// rawURL may be a base URL with optional basic-auth credentials in the
|
||||
// userinfo (https://user:pass@host/) — net/http applies the userinfo as the
|
||||
// Authorization header automatically, so reverse proxies with basic auth
|
||||
// work transparently.
|
||||
//
|
||||
// We deliberately avoid url.Parse here: it percent-encodes the literal "%"
|
||||
// in "/bot%s/%s" to "/bot%25s/%25s", which makes tgbotapi's fmt.Sprintf
|
||||
// produce a malformed URL (verified against the deploy.rscz.ru proxy).
|
||||
// Stripping the path and appending the bot-method pattern as a string is
|
||||
// both simpler and safe.
|
||||
func telegramEndpoint(rawURL string) string {
|
||||
if rawURL == "" {
|
||||
return tgbotapi.APIEndpoint
|
||||
}
|
||||
return strings.TrimRight(rawURL, "/") + "/bot%s/%s"
|
||||
}
|
||||
Ссылка в новой задаче
Block a user