twitch-bot/internal/actors/counter/database.go
2024-04-07 13:37:13 +02:00

125 lines
2.7 KiB
Go

package counter
import (
"fmt"
"strings"
"time"
"github.com/pkg/errors"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"github.com/Luzifer/go_helpers/v2/str"
"github.com/Luzifer/twitch-bot/v3/internal/helpers"
"github.com/Luzifer/twitch-bot/v3/pkg/database"
)
type (
counter struct {
Name string `gorm:"primaryKey"`
Value int64
FirstSeen time.Time
LastModified time.Time
}
)
func getCounterValue(db database.Connector, counterName string) (int64, error) {
var c counter
err := helpers.Retry(func() error {
err := db.DB().First(&c, "name = ?", counterName).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return err
})
return c.Value, errors.Wrap(err, "querying counter")
}
//revive:disable-next-line:flag-parameter
func updateCounter(db database.Connector, counterName string, value int64, absolute bool, atTime time.Time) error {
if !absolute {
cv, err := getCounterValue(db, counterName)
if err != nil {
return errors.Wrap(err, "getting previous value")
}
value += cv
}
return errors.Wrap(
helpers.RetryTransaction(db.DB(), func(tx *gorm.DB) error {
return tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "name"}},
DoUpdates: clause.AssignmentColumns([]string{"last_modified", "value"}),
}).Create(counter{Name: counterName, Value: value, FirstSeen: atTime.UTC(), LastModified: atTime.UTC()}).Error
}),
"storing counter value",
)
}
func getCounterRank(db database.Connector, prefix, name string) (rank, count int64, err error) {
var cc []counter
if err = helpers.Retry(func() error {
return db.DB().
Order("value DESC").
Find(&cc, "name LIKE ?", prefix+"%").
Error
}); err != nil {
return 0, 0, errors.Wrap(err, "querying counters")
}
for i, c := range cc {
count++
if c.Name == name {
rank = int64(i + 1)
}
}
return rank, count, nil
}
func getCounterTopList(db database.Connector, prefix string, n int, orderBy ...string) ([]counter, error) {
var (
cc []counter
order string
validOrderCols = []string{"first_seen", "last_modified", "name", "value"}
validOrderDirs = []string{"ASC", "DESC"}
)
if len(orderBy) == 0 || orderBy[0] == "" {
order = "value DESC"
} else {
order = orderBy[0]
}
col, dir, _ := strings.Cut(order, " ")
if col == "" {
col = "value"
}
if dir == "" {
dir = "ASC"
}
if !str.StringInSlice(col, validOrderCols) {
return nil, fmt.Errorf("invalid orderBy column")
}
if !str.StringInSlice(dir, validOrderDirs) {
return nil, fmt.Errorf("invalid orderBy direction")
}
err := helpers.Retry(func() error {
return db.DB().
Order(strings.Join([]string{col, dir}, " ")).
Limit(n).
Find(&cc, "name LIKE ?", prefix+"%").
Error
})
return cc, errors.Wrap(err, "querying counters")
}