lightningtipbot/internal/telegram/database.go
gohumble 94eff98f60
bunt transaction generalization (#73)
* add generalized bunt database transaction

* use bunt tx for inline faucet and send

* use bunt tx for inline receive

* use bunt tx for inline send

* use bunt tx for pay

* fix typo

* readd context

* add generalized bunt database transaction

* use bunt tx for inline faucet and send

* use bunt tx for inline receive

* use bunt tx for inline send

* use bunt tx for pay

* fix typo

* readd context

* some fixes

* tigher error checking

* add CreatedAt and UpdatedAt flags

* check error in GetTransaction

* add bunt transaction package and rename stuff

* use transaction.Base as function receiver for transaction functions

* add transaction.Base.Set function

* fix tiptooltip bunt index

* refactor

* auto update updated at

* refactor

* remove balance from error

* fix updated at

* faucet errors

* add generalized bunt database transaction

* use bunt tx for inline faucet and send

* use bunt tx for inline receive

* use bunt tx for inline send

* use bunt tx for pay

* fix typo

* readd context

* some fixes

* tigher error checking

* add generalized bunt database transaction

* use bunt tx for inline faucet and send

* use bunt tx for inline send

* add CreatedAt and UpdatedAt flags

* check error in GetTransaction

* add bunt transaction package and rename stuff

* use transaction.Base as function receiver for transaction functions

* add transaction.Base.Set function

* fix tiptooltip bunt index

* refactor

* auto update updated at

* fix updated at

* faucet errors

* refactor

* remove balance from error

* rebase fixes

* fix imports

* fix de string

* fix receive and send

* faucet language

* remove pay constructor

* release faucet

* remove redundant inTransaction

* remove constructor

* remove constructor

* transaction get pointer receiver

* send race condition

* inactivate in db

Co-authored-by: LightningTipBot <hWOofXvq4m@mail.com>
2021-10-08 00:46:52 +03:00

120 lines
3.6 KiB
Go

package telegram
import (
"fmt"
"github.com/LightningTipBot/LightningTipBot/internal"
"github.com/LightningTipBot/LightningTipBot/internal/storage"
"github.com/tidwall/buntdb"
"reflect"
"strconv"
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/LightningTipBot/LightningTipBot/internal/lnbits"
tb "gopkg.in/tucnak/telebot.v2"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
const (
MessageOrderedByReplyToFrom = "message.reply_to_message.from.id"
TipTooltipKeyPattern = "tip-tool-tip:*"
)
func createBunt() *storage.DB {
// create bunt database
bunt := storage.NewBunt(internal.Configuration.Database.BuntDbPath)
// create bunt database index for ascending (searching) TipTooltips
err := bunt.CreateIndex(MessageOrderedByReplyToFrom, TipTooltipKeyPattern, buntdb.IndexJSON(MessageOrderedByReplyToFrom))
if err != nil {
panic(err)
}
return bunt
}
func migration() (db *gorm.DB, txLogger *gorm.DB) {
txLogger, err := gorm.Open(sqlite.Open(internal.Configuration.Database.TransactionsPath), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true, FullSaveAssociations: true})
if err != nil {
panic("Initialize orm failed.")
}
orm, err := gorm.Open(sqlite.Open(internal.Configuration.Database.DbPath), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true, FullSaveAssociations: true})
if err != nil {
panic("Initialize orm failed.")
}
err = orm.AutoMigrate(&lnbits.User{})
if err != nil {
panic(err)
}
err = txLogger.AutoMigrate(&Transaction{})
if err != nil {
panic(err)
}
return orm, txLogger
}
func GetUserByTelegramUsername(toUserStrWithoutAt string, bot TipBot) (*lnbits.User, error) {
toUserDb := &lnbits.User{}
tx := bot.Database.Where("telegram_username = ?", strings.ToLower(toUserStrWithoutAt)).First(toUserDb)
if tx.Error != nil || toUserDb.Wallet == nil {
err := tx.Error
if toUserDb.Wallet == nil {
err = fmt.Errorf("%s | user @%s has no wallet", tx.Error, toUserStrWithoutAt)
}
return nil, err
}
return toUserDb, nil
}
// GetLnbitsUser will not update the user in Database.
// this is required, because fetching lnbits.User from a incomplete tb.User
// will update the incomplete (partial) user in storage.
// this function will accept users like this:
// &tb.User{ID: toId, Username: username}
// without updating the user in storage.
func GetLnbitsUser(u *tb.User, bot TipBot) (*lnbits.User, error) {
user := &lnbits.User{Name: strconv.Itoa(u.ID)}
tx := bot.Database.First(user)
if tx.Error != nil {
errmsg := fmt.Sprintf("[GetUser] Couldn't fetch %s from Database: %s", GetUserStr(u), tx.Error.Error())
log.Warnln(errmsg)
user.Telegram = u
return user, tx.Error
}
return user, nil
}
// GetUser from Telegram user. Update the user if user information changed.
func GetUser(u *tb.User, bot TipBot) (*lnbits.User, error) {
user, err := GetLnbitsUser(u, bot)
if err != nil {
return user, err
}
go func() {
userCopy := bot.copyLowercaseUser(u)
if !reflect.DeepEqual(userCopy, user.Telegram) {
// update possibly changed user details in Database
user.Telegram = userCopy
err = UpdateUserRecord(user, bot)
if err != nil {
log.Warnln(fmt.Sprintf("[UpdateUserRecord] %s", err.Error()))
}
}
}()
return user, err
}
func UpdateUserRecord(user *lnbits.User, bot TipBot) error {
user.Telegram = bot.copyLowercaseUser(user.Telegram)
user.UpdatedAt = time.Now()
tx := bot.Database.Save(user)
if tx.Error != nil {
errmsg := fmt.Sprintf("[UpdateUserRecord] Error: Couldn't update %s's info in Database.", GetUserStr(user.Telegram))
log.Errorln(errmsg)
return tx.Error
}
log.Debugf("[UpdateUserRecord] Records of user %s updated.", GetUserStr(user.Telegram))
return nil
}