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"
|
2022-01-14 16:18:07 +00:00
|
|
|
"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
|
2022-01-13 18:36:06 +00:00
|
|
|
r.set = true
|
2022-01-13 14:15:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *TelegramReply) RemoveKeyboard() {
|
|
|
|
r.menu.ReplyKeyboardRemove = true
|
2022-01-13 18:36:06 +00:00
|
|
|
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)
|
2022-01-13 18:36:06 +00:00
|
|
|
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
|
2022-01-13 18:36:06 +00:00
|
|
|
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)
|
2022-01-14 16:18:07 +00:00
|
|
|
a.Telegram.EmitAuthorized(a)
|
2022-01-13 17:58:04 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-13 18:36:06 +00:00
|
|
|
func (a *TelegramAuthorizer) StartAuthorizing() {
|
|
|
|
a.Telegram.authorizing = true
|
|
|
|
}
|
|
|
|
|
2022-01-14 16:18:07 +00:00
|
|
|
//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:"-"`
|
|
|
|
|
2022-01-13 18:36:06 +00:00
|
|
|
// 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
|
2022-01-13 18:36:06 +00:00
|
|
|
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
|
2022-01-13 18:36:06 +00:00
|
|
|
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
|
2022-01-14 16:18:07 +00:00
|
|
|
|
|
|
|
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) {
|
2022-01-13 18:36:06 +00:00
|
|
|
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 {
|
2022-01-14 16:18:07 +00:00
|
|
|
log.Warn("[telegram] telegram is set to private mode, skipping message")
|
2022-01-13 18:36:06 +00:00
|
|
|
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 {
|
2022-01-13 18:36:06 +00:00
|
|
|
log.WithError(err).Errorf("[telegram] response handling error")
|
2022-01-13 14:24:51 +00:00
|
|
|
}
|
2022-01-13 14:15:05 +00:00
|
|
|
}
|
|
|
|
|
2022-01-13 18:36:06 +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) {
|
2022-01-13 18:36:06 +00:00
|
|
|
authorizer := tm.newAuthorizer(m)
|
2022-01-14 18:52:46 +00:00
|
|
|
reply := tm.newReply(m)
|
2022-01-13 18:36:06 +00:00
|
|
|
if err := responder(m.Payload, reply, authorizer); err != nil {
|
2022-01-14 16:18:07 +00:00
|
|
|
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
|
2022-01-13 18:36:06 +00:00
|
|
|
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},
|
|
|
|
}
|
|
|
|
}
|
2022-01-14 07:03:19 +00:00
|
|
|
|
2022-01-14 16:18:07 +00:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 07:03:19 +00:00
|
|
|
type TelegramSession struct {
|
|
|
|
Owner *telebot.User `json:"owner"`
|
|
|
|
OwnerChat *telebot.Chat `json:"chat"`
|
|
|
|
|
2022-01-14 16:18:07 +00:00
|
|
|
// Subscribers stores the Chat objects
|
|
|
|
Subscribers map[int64]time.Time `json:"chats"`
|
2022-01-14 07:03:19 +00:00
|
|
|
}
|
|
|
|
|
2022-01-14 16:18:07 +00:00
|
|
|
func NewTelegramSession() *TelegramSession {
|
|
|
|
return &TelegramSession{
|
|
|
|
Owner: nil,
|
|
|
|
OwnerChat: nil,
|
|
|
|
Subscribers: make(map[int64]time.Time),
|
2022-01-14 07:03:19 +00:00
|
|
|
}
|
|
|
|
}
|