mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 20:01:17 +00:00
156 lines
3.8 KiB
Go
156 lines
3.8 KiB
Go
package quotedb
|
|
|
|
import (
|
|
"math/rand"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/Luzifer/twitch-bot/v3/internal/helpers"
|
|
"github.com/Luzifer/twitch-bot/v3/pkg/database"
|
|
)
|
|
|
|
type (
|
|
quote struct {
|
|
ID uint64 `gorm:"primaryKey"`
|
|
Channel string `gorm:"not null;uniqueIndex:ensure_sort_idx;size:32"`
|
|
CreatedAt int64 `gorm:"uniqueIndex:ensure_sort_idx"`
|
|
Quote string
|
|
}
|
|
)
|
|
|
|
func addQuote(db database.Connector, channel, quoteStr string) error {
|
|
return errors.Wrap(
|
|
helpers.RetryTransaction(db.DB(), func(tx *gorm.DB) error {
|
|
return tx.Create("e{
|
|
Channel: channel,
|
|
CreatedAt: time.Now().UnixNano(),
|
|
Quote: quoteStr,
|
|
}).Error
|
|
}),
|
|
"adding quote to database",
|
|
)
|
|
}
|
|
|
|
func delQuote(db database.Connector, channel string, quoteIdx int) error {
|
|
_, createdAt, _, err := getQuoteRaw(db, channel, quoteIdx)
|
|
if err != nil {
|
|
return errors.Wrap(err, "fetching specified quote")
|
|
}
|
|
|
|
return errors.Wrap(
|
|
helpers.RetryTransaction(db.DB(), func(tx *gorm.DB) error {
|
|
return tx.Delete("e{}, "channel = ? AND created_at = ?", channel, createdAt).Error
|
|
}),
|
|
"deleting quote",
|
|
)
|
|
}
|
|
|
|
func getChannelQuotes(db database.Connector, channel string) ([]string, error) {
|
|
var qs []quote
|
|
if err := helpers.Retry(func() error {
|
|
return db.DB().Where("channel = ?", channel).Order("created_at").Find(&qs).Error
|
|
}); err != nil {
|
|
return nil, errors.Wrap(err, "querying quotes")
|
|
}
|
|
|
|
var quotes []string
|
|
for _, q := range qs {
|
|
quotes = append(quotes, q.Quote)
|
|
}
|
|
|
|
return quotes, nil
|
|
}
|
|
|
|
func getMaxQuoteIdx(db database.Connector, channel string) (int, error) {
|
|
var count int64
|
|
if err := helpers.Retry(func() error {
|
|
return db.DB().
|
|
Model("e{}).
|
|
Where("channel = ?", channel).
|
|
Count(&count).
|
|
Error
|
|
}); err != nil {
|
|
return 0, errors.Wrap(err, "getting quote count")
|
|
}
|
|
|
|
return int(count), nil
|
|
}
|
|
|
|
func getQuote(db database.Connector, channel string, quote int) (int, string, error) {
|
|
quoteIdx, _, quoteText, err := getQuoteRaw(db, channel, quote)
|
|
return quoteIdx, quoteText, err
|
|
}
|
|
|
|
func getQuoteRaw(db database.Connector, channel string, quoteIdx int) (int, int64, string, error) {
|
|
if quoteIdx == 0 {
|
|
max, err := getMaxQuoteIdx(db, channel)
|
|
if err != nil {
|
|
return 0, 0, "", errors.Wrap(err, "getting max quote idx")
|
|
}
|
|
quoteIdx = rand.Intn(max) + 1 // #nosec G404 // no need for cryptographic safety
|
|
}
|
|
|
|
var q quote
|
|
err := helpers.Retry(func() error {
|
|
return db.DB().
|
|
Where("channel = ?", channel).
|
|
Limit(1).
|
|
Offset(quoteIdx - 1).
|
|
First(&q).Error
|
|
})
|
|
|
|
switch {
|
|
case err == nil:
|
|
return quoteIdx, q.CreatedAt, q.Quote, nil
|
|
|
|
case errors.Is(err, gorm.ErrRecordNotFound):
|
|
return 0, 0, "", nil
|
|
|
|
default:
|
|
return 0, 0, "", errors.Wrap(err, "getting quote from DB")
|
|
}
|
|
}
|
|
|
|
func setQuotes(db database.Connector, channel string, quotes []string) error {
|
|
return errors.Wrap(
|
|
helpers.RetryTransaction(db.DB(), func(tx *gorm.DB) error {
|
|
if err := tx.Where("channel = ?", channel).Delete("e{}).Error; err != nil {
|
|
return errors.Wrap(err, "deleting quotes for channel")
|
|
}
|
|
|
|
t := time.Now()
|
|
for _, quoteStr := range quotes {
|
|
if err := tx.Create("e{
|
|
Channel: channel,
|
|
CreatedAt: t.UnixNano(),
|
|
Quote: quoteStr,
|
|
}).Error; err != nil {
|
|
return errors.Wrap(err, "adding quote")
|
|
}
|
|
|
|
t = t.Add(time.Nanosecond) // Increase by one ns to adhere to unique index
|
|
}
|
|
|
|
return nil
|
|
}),
|
|
"replacing quotes",
|
|
)
|
|
}
|
|
|
|
func updateQuote(db database.Connector, channel string, idx int, quoteStr string) error {
|
|
_, createdAt, _, err := getQuoteRaw(db, channel, idx)
|
|
if err != nil {
|
|
return errors.Wrap(err, "fetching specified quote")
|
|
}
|
|
|
|
return errors.Wrap(
|
|
helpers.RetryTransaction(db.DB(), func(tx *gorm.DB) error {
|
|
return tx.Where("channel = ? AND created_at = ?", channel, createdAt).
|
|
Update("quote", quoteStr).
|
|
Error
|
|
}),
|
|
"updating quote",
|
|
)
|
|
}
|