aboutsummaryrefslogtreecommitdiff
path: root/user
diff options
context:
space:
mode:
Diffstat (limited to 'user')
-rw-r--r--user/level.go96
-rw-r--r--user/member.go6
-rw-r--r--user/state.go91
-rw-r--r--user/xp.go6
4 files changed, 108 insertions, 91 deletions
diff --git a/user/level.go b/user/level.go
index 654ecd1..07df16f 100644
--- a/user/level.go
+++ b/user/level.go
@@ -1,6 +1,7 @@
package user
import (
+ "context"
"slices"
"sync"
"time"
@@ -45,7 +46,7 @@ func onNewLevel(s bot.Session, m *user.Member, level uint) {
}
}
-func (c *Copaing) OnNewLevel(s *discordgo.Session, level uint) {
+func (c *CopaingCached) onNewLevel(s *discordgo.Session, level uint) {
m, err := s.GuildAPI().Member(c.GuildID, c.DiscordID)
if err != nil {
s.Logger().Error("getting member for new level", "error", err, "user", c.DiscordID, "guild", c.GuildID)
@@ -54,66 +55,61 @@ func (c *Copaing) OnNewLevel(s *discordgo.Session, level uint) {
onNewLevel(s, m, level)
}
-func PeriodicReducer(s *discordgo.Session) {
- wg := &sync.WaitGroup{}
- var cs []*Copaing
- if err := gokord.DB.Find(&cs).Error; err != nil {
- s.Logger().Error("fetching all copaings", "error", err)
- return
- }
- cxps := make([]*cXP, len(cs))
- for i, c := range cs {
- if i%25 == 24 {
- wg.Wait() // prevents spamming the DB
- }
- wg.Add(1)
- go func() {
- defer wg.Done()
- xp, err := c.GetXP(s.Logger())
- if err != nil {
- s.Logger().Error("getting xp", "error", err, "copaing", c.ID, "guild", c.GuildID)
- xp = 0
- }
- cxps[i] = &cXP{
- Cxp: xp,
- copaing: c,
- }
- }()
- }
- wg.Wait()
- i := 0
+func PeriodicReducer(ctx context.Context, s *discordgo.Session) {
+ PeriodicSaver(ctx, s)
+
+ s.Logger().Debug("periodic reducer")
+
+ state := GetState(ctx)
+
+ n := 0
+ var wg sync.WaitGroup
for _, g := range s.GuildAPI().State.Guilds() {
- i++
- wg.Add(1)
- go func() {
- defer wg.Done()
- cfg := config.GetGuildConfig(g)
- res := gokord.DB.
- Model(&CopaingXP{}).
- Where("guild_id = ? and created_at < ?", g, exp.TimeStampNDaysBefore(cfg.DaysXPRemains)).
- Delete(&CopaingXP{})
- if res.Error != nil {
- s.Logger().Error("removing old xp", "error", res.Error, "guild", g)
- }
- s.Logger().Debug("guild cleaned", "guild", g, "rows affected", res.RowsAffected)
- }()
+ n++
+ cfg := config.GetGuildConfig(g)
+ res := gokord.DB.
+ Model(&CopaingXP{}).
+ Where("guild_id = ? and created_at < ?", g, exp.TimeStampNDaysBefore(cfg.DaysXPRemains)).
+ Delete(&CopaingXP{})
+ if res.Error != nil {
+ s.Logger().Error("removing old xp", "error", res.Error, "guild", g)
+ continue
+ }
+ s.Logger().Debug("guild cleaned", "guild", g, "rows affected", res.RowsAffected)
+
+ wg.Go(func() {
+ syncCopaings(ctx, s, state.Copaings(g))
+ })
}
+
wg.Wait()
- for i, c := range cxps {
+
+ s.Logger().Debug("periodic reduce finished", "guilds affected", n)
+}
+
+func syncCopaings(ctx context.Context, s *discordgo.Session, ccs []CopaingCached) {
+ for i, cc := range ccs {
if i%50 == 49 {
s.Logger().Debug("sleeping...")
time.Sleep(15 * time.Second) // prevents spamming the API
}
- oldXp := c.GetXP()
- cp := c.Copaing()
- xp, err := cp.GetXP(s.Logger())
+ oldXp := cc.XPs
+ err := cc.Sync(ctx)
if err != nil {
- s.Logger().Error("getting xp of copaing", "error", err, "copaing", cp.ID, "guild", cp.GuildID)
+ s.Logger().Error("syncing copaing", "error", err, "copaing", cc.ID, "guild", cc.GuildID)
continue
}
+ xp := cc.XPs
if exp.Level(oldXp) != exp.Level(xp) {
- cp.OnNewLevel(s, exp.Level(xp))
+ cc.onNewLevel(s, exp.Level(xp))
}
}
- s.Logger().Debug("periodic reduce finished", "guilds affected", i)
+}
+
+func PeriodicSaver(ctx context.Context, s bot.Session) {
+ s.Logger().Debug("saving state in DB")
+ err := saveStateInDB(ctx)
+ if err != nil {
+ panic(err)
+ }
}
diff --git a/user/member.go b/user/member.go
index 9c9ad1f..4969f8a 100644
--- a/user/member.go
+++ b/user/member.go
@@ -32,7 +32,7 @@ func GetCopaing(ctx context.Context, discordID string, guildID string) *CopaingC
cc, err := state.Copaing(guildID, discordID)
if err != nil {
c := Copaing{DiscordID: discordID, GuildID: guildID}
- if err := c.Load(ctx); err != nil {
+ if err := c.load(); err != nil {
panic(err)
}
cc = FromCopaing(&c)
@@ -40,7 +40,7 @@ func GetCopaing(ctx context.Context, discordID string, guildID string) *CopaingC
return cc
}
-func (c *Copaing) Load(ctx context.Context) error {
+func (c *Copaing) load() error {
err := gokord.DB.
Where("discord_id = ? and guild_id = ?", c.DiscordID, c.GuildID).
Preload("CopaingXPs").
@@ -49,8 +49,6 @@ func (c *Copaing) Load(ctx context.Context) error {
if err != nil {
return err
}
- state := GetState(ctx)
- _, err = state.CopaingAdd(c, 0)
return err
}
diff --git a/user/state.go b/user/state.go
index 84f2852..540d496 100644
--- a/user/state.go
+++ b/user/state.go
@@ -2,31 +2,46 @@ package user
import (
"context"
+ "errors"
"sync"
+ "time"
"github.com/nyttikord/gokord/state"
)
+var ErrSyncingUnsavedData = errors.New("trying to sync unsaved data")
+
type CopaingCached struct {
- ID uint `gorm:"primarykey"`
- DiscordID string `gorm:"not null"`
- GuildID string `gorm:"not null"`
+ ID uint
+ DiscordID string
+ GuildID string
XPs uint
XPToAdd uint
+ lastSync time.Time // time.Time of the lastSync
}
// copaing turns a CopaingCached into a Copaing.
-// This operation is heavy.
-func (cc *CopaingCached) copaing(ctx context.Context) *Copaing {
- c := Copaing{DiscordID: cc.DiscordID, GuildID: cc.GuildID}
- if err := c.Load(ctx); err != nil {
+// This operation get the copaing synced with the database.
+// It doesn't:
+// - save the copaing in the database, use CopaingCached.SaveInDB for that;
+// - sync the copaing cached, use CopaingCached.Sync for that.
+// TL;DR: don't use this method, unless you know what are you doing.
+func (cc *CopaingCached) copaing() *Copaing {
+ c := &Copaing{DiscordID: cc.DiscordID, GuildID: cc.GuildID}
+ if err := c.load(); err != nil {
panic(err)
}
- return &c
+ return c
}
func (cc *CopaingCached) Sync(ctx context.Context) error {
- synced, err := GetState(ctx).CopaingAdd(cc.copaing(ctx), cc.XPToAdd)
+ if cc.mustSave() {
+ return ErrSyncingUnsavedData
+ }
+ synced := FromCopaing(cc.copaing())
+ synced.XPs += cc.XPToAdd
+ synced.XPToAdd = cc.XPToAdd
+ err := synced.Save(ctx)
if err != nil {
return err
}
@@ -44,7 +59,7 @@ func (cc *CopaingCached) Save(ctx context.Context) error {
}
func (cc *CopaingCached) SaveInDB(ctx context.Context) error {
- c := cc.copaing(ctx)
+ c := cc.copaing()
c.CopaingXPs = append(c.CopaingXPs, CopaingXP{CopaingID: c.ID, XP: cc.XPToAdd, GuildID: c.GuildID})
err := c.Save()
if err != nil {
@@ -55,12 +70,33 @@ func (cc *CopaingCached) SaveInDB(ctx context.Context) error {
}
func (cc *CopaingCached) Delete(ctx context.Context) error {
- c := cc.copaing(ctx)
+ c := cc.copaing()
err := c.Delete()
if err != nil {
return err
}
- return GetState(ctx).CopaingRemove(c)
+ state := GetState(ctx)
+
+ state.mu.Lock()
+ defer state.mu.Unlock()
+
+ return state.storage.Delete(KeyCopaingCached(c))
+}
+
+func (cc *CopaingCached) mustSave() bool {
+ return cc.XPToAdd > 0
+}
+
+func saveStateInDB(ctx context.Context) error {
+ for _, v := range GetState(ctx).storage {
+ if v.mustSave() {
+ err := v.SaveInDB(ctx)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
}
func FromCopaing(c *Copaing) *CopaingCached {
@@ -116,28 +152,17 @@ func (s *State) Copaing(guildID, copaingID string) (*CopaingCached, error) {
return &mC, nil
}
-// CopaingAdd does not call Copaing.Load!
-func (s *State) CopaingAdd(c *Copaing, xpToAdd uint) (*CopaingCached, error) {
- var err error
- var cc *CopaingCached
- if cc, err = s.Copaing(c.GuildID, c.DiscordID); err == nil {
- cc.XPs = calcXP(c) + xpToAdd
- } else {
- cc = FromCopaing(c)
- }
- cc.XPToAdd = xpToAdd
-
- s.mu.Lock()
- defer s.mu.Unlock()
-
- return cc, s.storage.Write(KeyCopaingCached(c), *cc)
-}
-
-func (s *State) CopaingRemove(c *Copaing) error {
- s.mu.Lock()
- defer s.mu.Unlock()
+func (s *State) Copaings(guild string) []CopaingCached {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
- return s.storage.Delete(KeyCopaingCached(c))
+ var ccs []CopaingCached
+ for _, cc := range s.storage {
+ if cc.GuildID == guild {
+ ccs = append(ccs, cc)
+ }
+ }
+ return ccs
}
func calcXP(c *Copaing) uint {
diff --git a/user/xp.go b/user/xp.go
index 246b097..985b5f8 100644
--- a/user/xp.go
+++ b/user/xp.go
@@ -97,16 +97,14 @@ func GetBestXP(logger *slog.Logger, guildId string, n uint, d int) ([]CopaingAcc
logger.Error("scanning rows", "error", err, "copaing", c.ID, "guild", c.GuildID)
continue
}
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
xp, err := c.GetXPForDays(logger, uint(d))
if err != nil {
logger.Error("fetching xp", "error", err, "copaing", c.ID, "guild", c.GuildID)
return
}
l = append(l, &cXP{Cxp: xp, copaing: &c})
- }()
+ })
}
wg.Wait()
slices.SortFunc(l, func(a, b *cXP) int {