diff --git a/alert/alert.go b/alert/alert.go index 59115e9..8ffc075 100644 --- a/alert/alert.go +++ b/alert/alert.go @@ -35,6 +35,7 @@ type ChannelType string const ( ChannelTypeDingTalk ChannelType = "dingtalk" + ChannelTypeTelegram ChannelType = "telegram" ) // Notification channel interface. diff --git a/alert/dingtalk.go b/alert/dingtalk.go index f33b417..beaecdc 100644 --- a/alert/dingtalk.go +++ b/alert/dingtalk.go @@ -10,7 +10,6 @@ var ( ) type DingTalkConfig struct { - Platform string // notify platform AtMobiles []string // mobiles for @ members IsAtAll bool // whether to @ all members Webhook string // webhook url @@ -33,7 +32,7 @@ func (dtc *DingTalkChannel) Name() string { } func (dtc *DingTalkChannel) Type() ChannelType { - return ChannelType(dtc.Config.Platform) + return ChannelTypeDingTalk } func (dtc *DingTalkChannel) Send(note *Notification) error { diff --git a/alert/telegram.go b/alert/telegram.go new file mode 100644 index 0000000..d92cbc6 --- /dev/null +++ b/alert/telegram.go @@ -0,0 +1,60 @@ +package alert + +import ( + "context" + + "github.com/go-telegram/bot" + "github.com/go-telegram/bot/models" + "github.com/pkg/errors" +) + +var ( + _ Channel = (*TelegramChannel)(nil) +) + +type TelegramConfig struct { + ApiToken string // Api token + ChatId string // Chat ID +} + +// TelegramChannel Telegram notification channel +type TelegramChannel struct { + Formatter Formatter // message formatter + ID string // channel id + Config TelegramConfig // channel config + + bot *bot.Bot +} + +func NewTelegramChannel(chID string, fmt Formatter, conf TelegramConfig) (*TelegramChannel, error) { + bot, err := bot.New(conf.ApiToken) + if err != nil { + return nil, err + } + + tc := &TelegramChannel{ID: chID, Formatter: fmt, Config: conf, bot: bot} + return tc, nil +} + +func (tc *TelegramChannel) Name() string { + return tc.ID +} + +func (tc *TelegramChannel) Type() ChannelType { + return ChannelTypeTelegram +} + +func (tc *TelegramChannel) Send(note *Notification) error { + msg, err := tc.Formatter.Format(note) + if err != nil { + return errors.WithMessage(err, "failed to format alert msg") + } + + _, err = tc.bot.SendMessage(context.Background(), &bot.SendMessageParams{ + ChatID: tc.Config.ChatId, + Text: msg, + ParseMode: models.ParseModeHTML, + }) + + return err +} diff --git a/alert/util.go b/alert/util.go index a2a370a..cd1b0e5 100644 --- a/alert/util.go +++ b/alert/util.go @@ -27,6 +27,14 @@ func parseAlertChannel(chID string, chmap map[string]interface{}, fmt Formatter) } return NewDingTalkChannel(chID, fmt, dtconf), nil + case ChannelTypeTelegram: + var tgconf TelegramConfig + if err := decodeChannelConfig(chmap, &tgconf); err != nil { + return nil, err + } + + return NewTelegramChannel(chID, fmt, tgconf) + // NOTE: add more channel types support here if needed default: return nil, ErrChannelTypeNotSupported(cht) diff --git a/config/config.yaml b/config/config.yaml index 0fc1c8a..8d80547 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -21,6 +21,11 @@ # secret: ${your_access_secret} # atMobiles: [] # isAtAll: false +# tgrobot: +# platform: telegram +# apiToken: ${your_api_token} +# chatId: ${your_chat_id} + # REST API Configurations # api: diff --git a/go.mod b/go.mod index 3de9b89..d75255f 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/gin-contrib/cors v1.3.1 github.com/gin-gonic/gin v1.7.7 github.com/go-playground/validator/v10 v10.4.1 + github.com/go-telegram/bot v1.2.2 github.com/mcuadros/go-defaults v1.2.0 github.com/mitchellh/mapstructure v1.4.3 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index aaa27dd..b110d3f 100644 --- a/go.sum +++ b/go.sum @@ -165,6 +165,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-telegram/bot v1.2.2 h1:LwGbSzjcSi0w4Ke8JUpgbBhJwwYTl2ITmhubeM2WvN8= +github.com/go-telegram/bot v1.2.2/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= diff --git a/log/hook/hook_test.go b/log/hook/hook_test.go index 2461ade..73de8f5 100644 --- a/log/hook/hook_test.go +++ b/log/hook/hook_test.go @@ -9,35 +9,50 @@ import ( ) var ( - dingrobot *alert.DingTalkChannel + channels []alert.Channel ) // Please set the following enviroments before running tests: // `TEST_DINGTALK_WEBHOOK`: DingTalk webhook; -// `TEST_DINGTALK_SECRET`: DingTalk secret. - +// `TEST_DINGTALK_SECRET`: DingTalk secret; +// `TEST_TELEGRAM_API_TOKEN`: Telegram API token; +// `TEST_TELEGRAM_CHAT_ID`: Telegram chat ID. func TestMain(m *testing.M) { - webhook := os.Getenv("TEST_DINGTALK_WEBHOOK") - secret := os.Getenv("TEST_DINGTALK_SECRET") + fmtter := alert.NewSimpleTextFormatter([]string{"log", "hook", "test"}) - if len(webhook) == 0 || len(secret) == 0 { - return + dtWebhook := os.Getenv("TEST_DINGTALK_WEBHOOK") + dtSecret := os.Getenv("TEST_DINGTALK_SECRET") + if len(dtWebhook) > 0 && len(dtSecret) > 0 { + dingrobot := alert.NewDingTalkChannel("dingrobot", fmtter, alert.DingTalkConfig{ + Webhook: dtWebhook, Secret: dtSecret, + }) + channels = append(channels, dingrobot) } - fmtter := alert.NewSimpleTextFormatter([]string{"log", "hook", "test"}) - dingrobot = alert.NewDingTalkChannel("dingrobot", fmtter, alert.DingTalkConfig{ - Platform: string(alert.ChannelTypeDingTalk), - Webhook: webhook, - Secret: secret, - }) + tgApiToken := os.Getenv("TEST_TELEGRAM_API_TOKEN") + tgChatId := os.Getenv("TEST_TELEGRAM_API_TOKEN") + if len(tgApiToken) > 0 && len(tgChatId) > 0 { + tgrobot, err := alert.NewTelegramChannel("tgrobot", fmtter, alert.TelegramConfig{ + ApiToken: tgApiToken, ChatId: tgChatId, + }) + if err != nil { + logrus.WithError(err).Fatal("Failed to new telegram channel") + } + + channels = append(channels, tgrobot) + } os.Exit(m.Run()) } func TestLogrusAddHooks(t *testing.T) { + if len(channels) == 0 { + return + } + // Add alert hook for logrus fatal/warn/error level hookLevels := []logrus.Level{logrus.FatalLevel, logrus.WarnLevel, logrus.ErrorLevel} - logrus.AddHook(NewAlertHook(hookLevels, []alert.Channel{dingrobot})) + logrus.AddHook(NewAlertHook(hookLevels, channels)) // Need to manually check if message sent to dingtalk group chat logrus.Warn("Test logrus add hooks warns")