bbgo_origin/pkg/interact/telegram.go

216 lines
5.5 KiB
Go
Raw Normal View History

2022-01-13 14:15:05 +00:00
package interact
import (
2022-01-13 17:58:04 +00:00
"context"
2022-01-13 14:15:05 +00:00
"fmt"
2022-01-13 18:57:39 +00:00
"strings"
"time"
2022-01-13 14:15:05 +00:00
log "github.com/sirupsen/logrus"
"gopkg.in/tucnak/telebot.v2"
)
type TelegramReply struct {
2022-01-14 18:52:46 +00:00
bot *telebot.Bot
chat *telebot.Chat
2022-01-13 14:15:05 +00:00
message string
menu *telebot.ReplyMarkup
buttons [][]telebot.Btn
2022-01-13 18:57:39 +00:00
set bool
2022-01-13 14:15:05 +00:00
}
2022-01-14 18:52:46 +00:00
func (r *TelegramReply) Send(message string) {
checkSendErr(r.bot.Send(r.chat, message))
}
2022-01-13 14:15:05 +00:00
func (r *TelegramReply) Message(message string) {
r.message = message
r.set = true
2022-01-13 14:15:05 +00:00
}
func (r *TelegramReply) RemoveKeyboard() {
r.menu.ReplyKeyboardRemove = true
r.set = true
2022-01-13 14:15:05 +00:00
}
func (r *TelegramReply) AddButton(text string) {
var button = r.menu.Text(text)
if len(r.buttons) == 0 {
r.buttons = append(r.buttons, []telebot.Btn{})
}
r.buttons[len(r.buttons)-1] = append(r.buttons[len(r.buttons)-1], button)
r.set = true
2022-01-13 14:15:05 +00:00
}
func (r *TelegramReply) build() {
var rows []telebot.Row
for _, buttons := range r.buttons {
rows = append(rows, telebot.Row(buttons))
}
r.menu.Reply(rows...)
}
2022-01-13 17:58:04 +00:00
type TelegramAuthorizer struct {
Telegram *Telegram
Message *telebot.Message
}
func (a *TelegramAuthorizer) Authorize() error {
a.Telegram.Owner = a.Message.Sender
a.Telegram.OwnerChat = a.Message.Chat
a.Telegram.authorizing = false
2022-01-13 17:58:04 +00:00
log.Infof("[interact][telegram] authorized owner %+v and chat %+v", a.Message.Sender, a.Message.Chat)
a.Telegram.EmitAuthorized(a)
2022-01-13 17:58:04 +00:00
return nil
}
func (a *TelegramAuthorizer) StartAuthorizing() {
a.Telegram.authorizing = true
}
//go:generate callbackgen -type Telegram
2022-01-13 14:15:05 +00:00
type Telegram struct {
2022-01-13 17:58:04 +00:00
Bot *telebot.Bot `json:"-"`
// Private is used to protect the telegram bot, users not authenticated can not see messages or sending commands
Private bool `json:"private,omitempty"`
authorizing bool
2022-01-13 17:58:04 +00:00
// Owner is the authorized bot owner
// This field is exported in order to be stored in file
Owner *telebot.User `json:"owner,omitempty"`
2022-01-13 17:58:04 +00:00
// OwnerChat is the chat of the authorized bot owner
// This field is exported in order to be stored in file
OwnerChat *telebot.Chat `json:"chat,omitempty"`
2022-01-13 14:15:05 +00:00
2022-01-13 14:24:51 +00:00
// textMessageResponder is used for interact to register its message handler
textMessageResponder Responder
2022-01-13 18:57:39 +00:00
commands []*Command
authorizedCallbacks []func(a *TelegramAuthorizer)
2022-01-13 14:24:51 +00:00
}
2022-01-13 17:58:04 +00:00
func (tm *Telegram) newAuthorizer(message *telebot.Message) *TelegramAuthorizer {
return &TelegramAuthorizer{
Telegram: tm,
Message: message,
}
}
func (tm *Telegram) SetTextMessageResponder(textMessageResponder Responder) {
tm.textMessageResponder = textMessageResponder
2022-01-13 14:15:05 +00:00
}
2022-01-13 17:58:04 +00:00
func (tm *Telegram) Start(context.Context) {
tm.Bot.Handle(telebot.OnText, func(m *telebot.Message) {
log.Infof("[telegram] onText: %+v", m)
if tm.Private && !tm.authorizing {
// ignore the message directly if it's not authorized yet
if tm.Owner == nil {
log.Warn("[telegram] telegram is set to private mode, skipping message")
return
} else if tm.Owner != nil && tm.Owner.ID != m.Sender.ID {
log.Warnf("[telegram] telegram is set to private mode, owner does not match: %d != %d", tm.Owner.ID, m.Sender.ID)
return
}
}
2022-01-13 14:15:05 +00:00
2022-01-13 17:58:04 +00:00
authorizer := tm.newAuthorizer(m)
2022-01-14 18:52:46 +00:00
reply := tm.newReply(m)
2022-01-13 17:58:04 +00:00
if tm.textMessageResponder != nil {
if err := tm.textMessageResponder(m.Text, reply, authorizer); err != nil {
log.WithError(err).Errorf("[telegram] response handling error")
2022-01-13 14:24:51 +00:00
}
2022-01-13 14:15:05 +00:00
}
if reply.set {
reply.build()
if _, err := tm.Bot.Send(m.Sender, reply.message, reply.menu); err != nil {
log.WithError(err).Errorf("[telegram] message send error")
}
2022-01-13 14:15:05 +00:00
}
})
2022-01-13 18:57:39 +00:00
var cmdList []telebot.Command
for _, cmd := range tm.commands {
if len(cmd.Desc) == 0 {
continue
}
cmdList = append(cmdList, telebot.Command{
Text: strings.ToLower(strings.TrimLeft(cmd.Name, "/")),
Description: cmd.Desc,
})
}
if err := tm.Bot.SetCommands(cmdList); err != nil {
log.WithError(err).Errorf("[telegram] set commands error")
}
2022-01-13 17:58:04 +00:00
go tm.Bot.Start()
2022-01-13 14:15:05 +00:00
}
2022-01-14 18:52:46 +00:00
func checkSendErr(m *telebot.Message, err error) {
if err != nil {
log.WithError(err).Errorf("[telegram] message send error")
}
}
2022-01-13 18:57:39 +00:00
func (tm *Telegram) AddCommand(cmd *Command, responder Responder) {
tm.commands = append(tm.commands, cmd)
tm.Bot.Handle(cmd.Name, func(m *telebot.Message) {
authorizer := tm.newAuthorizer(m)
2022-01-14 18:52:46 +00:00
reply := tm.newReply(m)
if err := responder(m.Payload, reply, authorizer); err != nil {
log.WithError(err).Errorf("[telegram] responder error")
2022-01-14 18:52:46 +00:00
checkSendErr(tm.Bot.Send(m.Sender, fmt.Sprintf("error: %v", err)))
2022-01-13 14:15:05 +00:00
return
}
// build up the response objects
if reply.set {
reply.build()
2022-01-14 18:52:46 +00:00
checkSendErr(tm.Bot.Send(m.Sender, reply.message, reply.menu))
2022-01-13 14:15:05 +00:00
}
})
}
2022-01-14 18:52:46 +00:00
func (tm *Telegram) newReply(m *telebot.Message) *TelegramReply {
2022-01-13 14:15:05 +00:00
return &TelegramReply{
2022-01-13 17:58:04 +00:00
bot: tm.Bot,
2022-01-14 18:52:46 +00:00
chat: m.Chat,
2022-01-13 14:15:05 +00:00
menu: &telebot.ReplyMarkup{ResizeReplyKeyboard: true},
}
}
func (tm *Telegram) RestoreSession(session *TelegramSession) {
log.Infof("[telegram] restoring telegram session: %+v", session)
if session.OwnerChat != nil {
tm.OwnerChat = session.OwnerChat
tm.Owner = session.Owner
if _, err := tm.Bot.Send(tm.OwnerChat, fmt.Sprintf("Hi %s, I'm back. Your telegram session is restored.", tm.Owner.Username)); err != nil {
log.WithError(err).Error("[telegram] can not send telegram message")
}
}
}
type TelegramSession struct {
Owner *telebot.User `json:"owner"`
OwnerChat *telebot.Chat `json:"chat"`
// Subscribers stores the Chat objects
Subscribers map[int64]time.Time `json:"chats"`
}
func NewTelegramSession() *TelegramSession {
return &TelegramSession{
Owner: nil,
OwnerChat: nil,
Subscribers: make(map[int64]time.Time),
}
}