diff --git a/README.md b/README.md index df9dbac99..3f78b66f6 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,9 @@ Add your dotenv file: ``` SLACK_TOKEN= +TELEGRAM_BOT_TOKEN= +TELEGRAM_INIT_TOKEN= + BINANCE_API_KEY= BINANCE_API_SECRET= diff --git a/go.mod b/go.mod index 9139ff4d9..60e112549 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 gonum.org/v1/gonum v0.8.1 + gopkg.in/tucnak/telebot.v2 v2.3.5 gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c ) diff --git a/go.sum b/go.sum index 5e03dd20f..cfae5b9bb 100644 --- a/go.sum +++ b/go.sum @@ -276,6 +276,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= @@ -443,6 +444,8 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/tucnak/telebot.v2 v2.3.5 h1:TdMJTlG8kvepsvZdy/gPeYEBdwKdwFFjH1AQTua9BOU= +gopkg.in/tucnak/telebot.v2 v2.3.5/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go index f3dbbc898..330cf518c 100644 --- a/pkg/cmd/run.go +++ b/pkg/cmd/run.go @@ -22,6 +22,7 @@ import ( "github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/cmd/cmdutil" "github.com/c9s/bbgo/pkg/notifier/slacknotifier" + "github.com/c9s/bbgo/pkg/notifier/telegramnotifier" "github.com/c9s/bbgo/pkg/slack/slacklog" "github.com/c9s/bbgo/pkg/types" ) @@ -121,6 +122,20 @@ func runConfig(basectx context.Context, userConfig *bbgo.Config) error { } } + // for telegram + telegramBotToken := viper.GetString("telegram-bot-token") + telegramInitToken := viper.GetString("telegram-init-token") + if len(telegramBotToken) > 0 && len(telegramInitToken) > 0 { + + log.Infof("adding telegram notifier") + var notifier = telegramnotifier.New(telegramBotToken, telegramInitToken) + + // start telegram bot + go notifier.Bot.Start() + + notification.AddNotifier(notifier) + } + environ.Notifiability = notification if userConfig.Notifications != nil { diff --git a/pkg/notifier/telegramnotifier/telegram.go b/pkg/notifier/telegramnotifier/telegram.go new file mode 100644 index 000000000..1fa8e34bd --- /dev/null +++ b/pkg/notifier/telegramnotifier/telegram.go @@ -0,0 +1,88 @@ +package telegramnotifier + +import ( + "fmt" + "time" + + log "github.com/sirupsen/logrus" + tb "gopkg.in/tucnak/telebot.v2" +) + +type Notifier struct { + Bot *tb.Bot + chatUser *tb.User + channel string +} + +type NotifyOption func(notifier *Notifier) + +// start bot daemon +func New(botToken, initToken string, options ...NotifyOption) *Notifier { + bot, err := tb.NewBot(tb.Settings{ + // You can also set custom API URL. + // If field is empty it equals to "https://api.telegram.org". + // URL: "http://195.129.111.17:8012", + + Token: botToken, + Poller: &tb.LongPoller{Timeout: 10 * time.Second}, + }) + + if err != nil { + panic(err) + } + + chatUser := &tb.User{} + + // init check initToken and then set sender id + bot.Handle("/init", func(m *tb.Message) { + if m.Text == initToken { + bot.Send(m.Sender, "Bot initialized") + chatUser = m.Sender + } else { + bot.Send(m.Sender, "Error: bot intialize failed. Init token not match!") + } + }) + + bot.Handle("/bbgo", func(m *tb.Message) { + if m.Sender == chatUser { + bot.Send(chatUser, "bbgo!") + } else { + log.Warningf("Incorrect user tried to access bot! sender id: %s", m.Sender) + } + }) + + notifier := &Notifier{ + chatUser: chatUser, + Bot: bot, + } + + for _, o := range options { + o(notifier) + } + + return notifier +} + +func (n *Notifier) Notify(format string, args ...interface{}) { + n.NotifyTo(n.channel, format, args...) +} + +func (n *Notifier) NotifyTo(channel, format string, args ...interface{}) { + var telegramArgsOffset = -1 + + var nonTelegramArgs = args + if telegramArgsOffset > -1 { + nonTelegramArgs = args[:telegramArgsOffset] + } + + log.Infof(format, nonTelegramArgs...) + + _, err := n.Bot.Send(n.chatUser, fmt.Sprintf(format, nonTelegramArgs...)) + if err != nil { + log.WithError(err). + WithField("chatUser", n.chatUser). + Errorf("telegram error: %s", err.Error()) + } + + return +}