lightningtipbot/internal/telegram/bot.go
gohumble e385cb7bcf Merge branch 'main' into log
# Conflicts:
#	go.mod
#	internal/api/lightning.go
#	internal/api/middleware.go
#	internal/api/server.go
#	internal/lndhub/lndhub.go
2022-08-31 22:23:52 +02:00

150 lines
4 KiB
Go

package telegram
import (
"fmt"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/LightningTipBot/LightningTipBot/internal/runtime/mutex"
limiter "github.com/LightningTipBot/LightningTipBot/internal/rate"
"github.com/eko/gocache/store"
"github.com/LightningTipBot/LightningTipBot/internal"
"github.com/LightningTipBot/LightningTipBot/internal/lnbits"
"github.com/LightningTipBot/LightningTipBot/internal/storage"
gocache "github.com/patrickmn/go-cache"
log "github.com/sirupsen/logrus"
tb "gopkg.in/lightningtipbot/telebot.v3"
)
type TipBot struct {
DB *Databases
Bunt *storage.DB
ShopBunt *storage.DB
Telegram *tb.Bot
Client *lnbits.Client
limiter map[string]limiter.Limiter
Cache
}
type Cache struct {
*store.GoCacheStore
}
var (
botWalletInitialisation = sync.Once{}
telegramHandlerRegistration = sync.Once{}
)
// NewBot migrates data and creates a new bot
func NewBot() TipBot {
gocacheClient := gocache.New(5*time.Minute, 10*time.Minute)
gocacheStore := store.NewGoCache(gocacheClient, nil)
// create sqlite databases
dbs := AutoMigration()
limiter.Start()
return TipBot{
DB: dbs,
Client: lnbits.NewClient(internal.Configuration.Lnbits.AdminKey, internal.Configuration.Lnbits.Url),
Bunt: createBunt(internal.Configuration.Database.BuntDbPath),
ShopBunt: createBunt(internal.Configuration.Database.ShopBuntDbPath),
Telegram: newTelegramBot(),
Cache: Cache{GoCacheStore: gocacheStore},
}
}
// newTelegramBot will create a new Telegram bot.
func newTelegramBot() *tb.Bot {
tgb, err := tb.NewBot(tb.Settings{
Token: internal.Configuration.Telegram.ApiKey,
Poller: &tb.LongPoller{Timeout: 60 * time.Second},
ParseMode: tb.ModeMarkdown,
Verbose: false,
})
if err != nil {
panic(err)
}
return tgb
}
// initBotWallet will create / initialize the bot wallet
// todo -- may want to derive user wallets from this specific bot wallet (master wallet), since lnbits usermanager extension is able to do that.
func (bot TipBot) initBotWallet() error {
botWalletInitialisation.Do(func() {
_, err := bot.initWallet(bot.Telegram.Me)
if err != nil {
log.WithFields(log.Fields{
"module": "telegram",
"func": "initBotWallet"}).Errorln(fmt.Sprintf("Could not initialize bot wallet: %s", err.Error()))
return
}
})
return nil
}
// GracefulShutdown will gracefully shutdown the bot
// It will wait for all mutex locks to unlock before shutdown.
func (bot *TipBot) GracefulShutdown() {
t := time.NewTicker(time.Second * 10)
log.Infof("[shutdown] Graceful shutdown (timeout=10s).")
for {
select {
case <-t.C:
// timer expired
log.Infof("[shutdown] Graceful shutdown timeout reached. Forcing shutdown.")
return
default:
// check if all mutex locks are unlocked
if mutex.IsEmpty() {
log.Infof("[shutdown] Graceful shutdown successful.")
return
}
}
time.Sleep(time.Second)
log.Tracef("[shutdown] Trying graceful shutdown...")
}
}
// Start will initialize the Telegram bot and lnbits.
func (bot *TipBot) Start() {
log.WithFields(log.Fields{
"module": "telegram",
"func": "Start",
"user": bot.Telegram.Me.Username}).Infof("Authorized on account")
// initialize the bot wallet
err := bot.initBotWallet()
if err != nil {
log.Errorf("Could not initialize bot wallet: %s", err.Error())
}
// register telegram handlers
bot.registerTelegramHandlers()
// download bot avatar once
bot.downloadMyProfilePicture()
// edit worker collects messages to edit and
// periodically edits them
bot.startEditWorker()
// register callbacks for invoices
initInvoiceEventCallbacks(bot)
// register callbacks for user state changes
initializeStateCallbackMessage(bot)
// start the telegram bot
go bot.Telegram.Start()
// gracefully shutdown
exit := make(chan os.Signal, 1) // we need to reserve to buffer size 1, so the notifier are not blocked
// we need to catch SIGTERM and SIGSTOP
signal.Notify(exit, os.Interrupt, syscall.SIGTERM, syscall.SIGSTOP)
<-exit
// gracefully shutdown
bot.GracefulShutdown()
}