2022-09-10 11:39:07 +00:00
|
|
|
package counter
|
|
|
|
|
|
|
|
import (
|
2024-04-04 17:04:06 +00:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
2024-04-04 16:25:03 +00:00
|
|
|
"time"
|
|
|
|
|
2022-09-10 11:39:07 +00:00
|
|
|
"github.com/pkg/errors"
|
2022-10-22 22:08:02 +00:00
|
|
|
"gorm.io/gorm"
|
|
|
|
"gorm.io/gorm/clause"
|
|
|
|
|
2024-04-04 17:04:06 +00:00
|
|
|
"github.com/Luzifer/go_helpers/v2/str"
|
2023-11-27 23:09:27 +00:00
|
|
|
"github.com/Luzifer/twitch-bot/v3/internal/helpers"
|
2022-11-02 21:38:14 +00:00
|
|
|
"github.com/Luzifer/twitch-bot/v3/pkg/database"
|
2022-09-10 11:39:07 +00:00
|
|
|
)
|
|
|
|
|
2022-10-22 22:08:02 +00:00
|
|
|
type (
|
2024-01-01 16:52:18 +00:00
|
|
|
counter struct {
|
2024-04-04 16:25:03 +00:00
|
|
|
Name string `gorm:"primaryKey"`
|
|
|
|
Value int64
|
|
|
|
FirstSeen time.Time
|
|
|
|
LastModified time.Time
|
2022-10-22 22:08:02 +00:00
|
|
|
}
|
|
|
|
)
|
2022-09-10 11:39:07 +00:00
|
|
|
|
2024-01-01 16:52:18 +00:00
|
|
|
func getCounterValue(db database.Connector, counterName string) (int64, error) {
|
|
|
|
var c counter
|
2022-09-10 11:39:07 +00:00
|
|
|
|
2023-11-27 23:09:27 +00:00
|
|
|
err := helpers.Retry(func() error {
|
|
|
|
err := db.DB().First(&c, "name = ?", counterName).Error
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
return nil
|
|
|
|
}
|
2022-09-10 11:39:07 +00:00
|
|
|
|
2023-11-27 23:09:27 +00:00
|
|
|
return err
|
|
|
|
})
|
2022-09-10 11:39:07 +00:00
|
|
|
|
2023-11-27 23:09:27 +00:00
|
|
|
return c.Value, errors.Wrap(err, "querying counter")
|
2022-09-10 11:39:07 +00:00
|
|
|
}
|
|
|
|
|
2024-01-01 16:52:18 +00:00
|
|
|
//revive:disable-next-line:flag-parameter
|
2024-04-04 16:25:03 +00:00
|
|
|
func updateCounter(db database.Connector, counterName string, value int64, absolute bool, atTime time.Time) error {
|
2022-09-10 11:39:07 +00:00
|
|
|
if !absolute {
|
2024-01-01 16:52:18 +00:00
|
|
|
cv, err := getCounterValue(db, counterName)
|
2022-09-10 11:39:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "getting previous value")
|
|
|
|
}
|
|
|
|
|
|
|
|
value += cv
|
|
|
|
}
|
|
|
|
|
2022-10-22 22:08:02 +00:00
|
|
|
return errors.Wrap(
|
2023-11-27 23:09:27 +00:00
|
|
|
helpers.RetryTransaction(db.DB(), func(tx *gorm.DB) error {
|
|
|
|
return tx.Clauses(clause.OnConflict{
|
|
|
|
Columns: []clause.Column{{Name: "name"}},
|
2024-04-04 16:25:03 +00:00
|
|
|
DoUpdates: clause.AssignmentColumns([]string{"last_modified", "value"}),
|
|
|
|
}).Create(counter{Name: counterName, Value: value, FirstSeen: atTime.UTC(), LastModified: atTime.UTC()}).Error
|
2023-11-27 23:09:27 +00:00
|
|
|
}),
|
2022-10-22 22:08:02 +00:00
|
|
|
"storing counter value",
|
2022-09-10 11:39:07 +00:00
|
|
|
)
|
|
|
|
}
|
2023-10-23 18:28:58 +00:00
|
|
|
|
|
|
|
func getCounterRank(db database.Connector, prefix, name string) (rank, count int64, err error) {
|
2024-01-01 16:52:18 +00:00
|
|
|
var cc []counter
|
2023-10-23 18:28:58 +00:00
|
|
|
|
2023-11-27 23:09:27 +00:00
|
|
|
if err = helpers.Retry(func() error {
|
|
|
|
return db.DB().
|
|
|
|
Order("value DESC").
|
|
|
|
Find(&cc, "name LIKE ?", prefix+"%").
|
|
|
|
Error
|
|
|
|
}); err != nil {
|
2023-10-23 18:28:58 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-04-04 17:04:06 +00:00
|
|
|
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")
|
|
|
|
}
|
2023-10-23 18:28:58 +00:00
|
|
|
|
2023-11-27 23:09:27 +00:00
|
|
|
err := helpers.Retry(func() error {
|
|
|
|
return db.DB().
|
2024-04-04 17:04:06 +00:00
|
|
|
Order(strings.Join([]string{col, dir}, " ")).
|
2023-11-27 23:09:27 +00:00
|
|
|
Limit(n).
|
|
|
|
Find(&cc, "name LIKE ?", prefix+"%").
|
|
|
|
Error
|
|
|
|
})
|
2023-10-23 18:28:58 +00:00
|
|
|
|
|
|
|
return cc, errors.Wrap(err, "querying counters")
|
|
|
|
}
|