From c1e4653a5c7ae34dd23d442f67a171eff466aad3 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 18:41:30 +0200 Subject: [PATCH 01/15] feat(copaing): time between last event --- xp/member.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/xp/member.go b/xp/member.go index 4f1e6ee..bcfcedf 100644 --- a/xp/member.go +++ b/xp/member.go @@ -1,11 +1,15 @@ package xp import ( + "context" + "fmt" "github.com/anhgelus/gokord" "github.com/anhgelus/gokord/utils" "github.com/bwmarrin/discordgo" "github.com/redis/go-redis/v9" "gorm.io/gorm" + "strconv" + "time" ) type Copaing struct { @@ -17,6 +21,10 @@ type Copaing struct { var redisClient *redis.Client +const ( + LastEvent = "last_event" +) + func GetCopaing(discordID string, guildID string) *Copaing { c := Copaing{DiscordID: discordID, GuildID: guildID} return c.Load() @@ -42,6 +50,57 @@ func (c *Copaing) AddXP(s *discordgo.Session, m *discordgo.Member, xp uint, fn f } } +func (c *Copaing) SetLastEvent() { + client, err := getRedisClient() + if err != nil { + utils.SendAlert("xp/member.go - Getting redis client (set)", err.Error()) + return + } + u := c.GetUserBase() + t := time.Now().Unix() + err = client.Set(context.Background(), fmt.Sprintf( + "%s:%s", + u.GenKey(), + LastEvent, + ), strconv.FormatInt(t, 10), 0).Err() + if err != nil { + utils.SendAlert("xp/member.go - Setting last event", err.Error(), "time", t, "base_key", u.GenKey()) + return + } +} + +func (c *Copaing) HourSinceLastEvent() uint { + client, err := getRedisClient() + if err != nil { + utils.SendAlert("xp/member.go - Getting redis client (get)", err.Error()) + return 0 + } + u := c.GetUserBase() + res := client.Get(context.Background(), fmt.Sprintf("%s:%s", u.GenKey(), LastEvent)) + if res.Err() != nil { + utils.SendAlert("xp/member.go - Getting last event", res.Err().Error(), "base_key", u.GenKey()) + return 0 + } + t := time.Now().Unix() + last, err := strconv.Atoi(res.Val()) + if err != nil { + utils.SendAlert( + "xp/member.go - Converting time fetched into int", + res.Err().Error(), + "base_key", + u.GenKey(), + "val", + res.Val(), + ) + return 0 + } + return utils.HoursOfUnix(t - int64(last)) +} + +func (c *Copaing) GetUserBase() *gokord.UserBase { + return &gokord.UserBase{DiscordID: c.DiscordID, GuildID: c.GuildID} +} + func getRedisClient() (*redis.Client, error) { if redisClient == nil { var err error From 2aaeb2115b25952b71113a683b466c00cb92e690 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 18:47:52 +0200 Subject: [PATCH 02/15] feat(xp): basic reducer --- xp/events.go | 2 ++ xp/level.go | 7 +++++++ xp/member.go | 5 ++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/xp/events.go b/xp/events.go index 4940c0b..c3b843c 100644 --- a/xp/events.go +++ b/xp/events.go @@ -30,6 +30,7 @@ func OnMessage(s *discordgo.Session, m *discordgo.MessageCreate) { return } c := GetCopaing(m.Author.ID, m.GuildID) + onLastEventUpdate(c) // add xp trimmed := utils.TrimMessage(strings.ToLower(m.Content)) m.Member.User = m.Author @@ -62,6 +63,7 @@ func OnVoiceUpdate(s *discordgo.Session, e *discordgo.VoiceStateUpdate) { if e.Member.User.Bot { return } + onLastEventUpdate(GetCopaing(e.UserID, e.GuildID)) cfg := config.GetGuildConfig(e.GuildID) if cfg.IsDisabled(e.ChannelID) { return diff --git a/xp/level.go b/xp/level.go index 671bf2e..574a127 100644 --- a/xp/level.go +++ b/xp/level.go @@ -42,3 +42,10 @@ func onNewLevel(s *discordgo.Session, m *discordgo.Member, level uint) { } } } + +func onLastEventUpdate(c *Copaing) { + h := c.HourSinceLastEvent() + c.XP -= Lose(h, c.XP) + c.Save() + c.SetLastEvent() +} diff --git a/xp/member.go b/xp/member.go index bcfcedf..fc01ec0 100644 --- a/xp/member.go +++ b/xp/member.go @@ -2,6 +2,7 @@ package xp import ( "context" + "errors" "fmt" "github.com/anhgelus/gokord" "github.com/anhgelus/gokord/utils" @@ -77,7 +78,9 @@ func (c *Copaing) HourSinceLastEvent() uint { } u := c.GetUserBase() res := client.Get(context.Background(), fmt.Sprintf("%s:%s", u.GenKey(), LastEvent)) - if res.Err() != nil { + if errors.Is(res.Err(), redis.Nil) { + return 0 + } else if res.Err() != nil { utils.SendAlert("xp/member.go - Getting last event", res.Err().Error(), "base_key", u.GenKey()) return 0 } From f00941a17ad1c789b7354c685d84386ed7904163 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 18:56:21 +0200 Subject: [PATCH 03/15] feat(xp): storage of already removed xp --- xp/level.go | 9 ++++++- xp/member.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/xp/level.go b/xp/level.go index 574a127..2ec6106 100644 --- a/xp/level.go +++ b/xp/level.go @@ -45,7 +45,14 @@ func onNewLevel(s *discordgo.Session, m *discordgo.Member, level uint) { func onLastEventUpdate(c *Copaing) { h := c.HourSinceLastEvent() - c.XP -= Lose(h, c.XP) + l := Lose(h, c.XP) + xp := c.XPAlreadyRemoved() + if l-xp < 0 { + utils.SendWarn("lose - xp already removed is negative", "lose", l, "xp", xp) + c.XP = 0 + } else { + c.XP -= l - c.XPAlreadyRemoved() + } c.Save() c.SetLastEvent() } diff --git a/xp/member.go b/xp/member.go index fc01ec0..b9073f3 100644 --- a/xp/member.go +++ b/xp/member.go @@ -23,7 +23,8 @@ type Copaing struct { var redisClient *redis.Client const ( - LastEvent = "last_event" + LastEvent = "last_event" + AlreadyRemoved = "already_removed" ) func GetCopaing(discordID string, guildID string) *Copaing { @@ -88,8 +89,8 @@ func (c *Copaing) HourSinceLastEvent() uint { last, err := strconv.Atoi(res.Val()) if err != nil { utils.SendAlert( - "xp/member.go - Converting time fetched into int", - res.Err().Error(), + "xp/member.go - Converting time fetched into int (last event)", + err.Error(), "base_key", u.GenKey(), "val", @@ -100,6 +101,73 @@ func (c *Copaing) HourSinceLastEvent() uint { return utils.HoursOfUnix(t - int64(last)) } +func (c *Copaing) AddXPAlreadyRemoved(xp uint) uint { + client, err := getRedisClient() + if err != nil { + utils.SendAlert("xp/member.go - Getting redis client (set)", err.Error()) + return 0 + } + u := c.GetUserBase() + exp := xp + c.XPAlreadyRemoved() + err = client.Set(context.Background(), fmt.Sprintf( + "%s:%s", + u.GenKey(), + AlreadyRemoved, + ), exp, 0).Err() + if err != nil { + utils.SendAlert( + "xp/member.go - Setting last event", + err.Error(), + "xp already removed", + exp, + "base_key", + u.GenKey(), + ) + return 0 + } + return exp +} + +func (c *Copaing) XPAlreadyRemoved() uint { + client, err := getRedisClient() + if err != nil { + utils.SendAlert("xp/member.go - Getting redis client (xp)", err.Error()) + return 0 + } + u := c.GetUserBase() + res := client.Get(context.Background(), fmt.Sprintf("%s:%s", u.GenKey(), AlreadyRemoved)) + if errors.Is(res.Err(), redis.Nil) { + return 0 + } else if res.Err() != nil { + utils.SendAlert("xp/member.go - Getting already removed", res.Err().Error(), "base_key", u.GenKey()) + return 0 + } + xp, err := strconv.Atoi(res.Val()) + if err != nil { + utils.SendAlert( + "xp/member.go - Converting time fetched into int (already removed)", + err.Error(), + "base_key", + u.GenKey(), + "val", + res.Val(), + ) + return 0 + } + if xp < 0 { + utils.SendAlert( + "xp/member.go - Assertion xp >= 0", + "xp is negative", + "base_key", + u.GenKey(), + "xp", + xp, + ) + return 0 + } + return uint(xp) +} + func (c *Copaing) GetUserBase() *gokord.UserBase { return &gokord.UserBase{DiscordID: c.DiscordID, GuildID: c.GuildID} } From 9de1f3f90b2d94215f14458a11a951cb143a8dc2 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 18:59:10 +0200 Subject: [PATCH 04/15] feat(xp): xp update without reseting last event --- xp/level.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/xp/level.go b/xp/level.go index 2ec6106..14d0e95 100644 --- a/xp/level.go +++ b/xp/level.go @@ -56,3 +56,18 @@ func onLastEventUpdate(c *Copaing) { c.Save() c.SetLastEvent() } + +func onXPUpdate(c *Copaing) { + h := c.HourSinceLastEvent() + l := Lose(h, c.XP) + xp := c.XPAlreadyRemoved() + if l-xp < 0 { + utils.SendWarn("lose - xp already removed is negative", "lose", l, "xp", xp) + c.XP = 0 + c.AddXPAlreadyRemoved(0) + } else { + c.XP -= l - c.XPAlreadyRemoved() + c.AddXPAlreadyRemoved(l - xp) + } + c.Save() +} From 0a6ecf6af7dc8df0a2255ddb7c022cfad6452493 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 19:06:33 +0200 Subject: [PATCH 05/15] feat(xp): implements xp reducer --- commands/rank.go | 17 +++++++++++++---- commands/top.go | 1 + xp/events.go | 4 ++-- xp/level.go | 4 ++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/commands/rank.go b/commands/rank.go index cd37a0c..ecf9a08 100644 --- a/commands/rank.go +++ b/commands/rank.go @@ -9,7 +9,8 @@ import ( func Rank(s *discordgo.Session, i *discordgo.InteractionCreate) { optMap := utils.GenerateOptionMap(i) - c := xp.Copaing{DiscordID: i.Member.User.ID, GuildID: i.GuildID} + c := xp.GetCopaing(i.User.ID, i.GuildID) // current copaing = member who used /rank + xp.LastEventUpdate(c) // update xp and reset last event msg := "Votre niveau" m := i.Member var err error @@ -24,17 +25,25 @@ func Rank(s *discordgo.Session, i *discordgo.InteractionCreate) { } m, err = s.GuildMember(i.GuildID, u.ID) if err != nil { - utils.SendAlert("rank.go - Fetching guild member", err.Error()) + utils.SendAlert( + "rank.go - Fetching guild member", + err.Error(), + "discord_id", + u.ID, + "guild_id", + i.GuildID, + ) err = resp.Message("Erreur : impossible de récupérer le membre").IsEphemeral().Send() if err != nil { utils.SendAlert("rank.go - Reply error fetching guild member", err.Error()) } return } - c.DiscordID = u.ID + c.DiscordID = u.ID // current copaing = member targeted by member who wrote /rank + c.Load() // reload copaing (change line before) + xp.XPUpdate(c) // update xp without resetting event msg = fmt.Sprintf("Le niveau de %s", m.DisplayName()) } - c.Load() lvl := xp.Level(c.XP) nxtLvl := xp.XPForLevel(lvl + 1) err = resp.Message(fmt.Sprintf( diff --git a/commands/top.go b/commands/top.go index bc444a3..6df259f 100644 --- a/commands/top.go +++ b/commands/top.go @@ -9,6 +9,7 @@ import ( ) func Top(s *discordgo.Session, i *discordgo.InteractionCreate) { + xp.LastEventUpdate(xp.GetCopaing(i.User.ID, i.GuildID)) resp := utils.ResponseBuilder{C: s, I: i} err := resp.IsDeferred().Send() if err != nil { diff --git a/xp/events.go b/xp/events.go index c3b843c..655e9fd 100644 --- a/xp/events.go +++ b/xp/events.go @@ -30,7 +30,7 @@ func OnMessage(s *discordgo.Session, m *discordgo.MessageCreate) { return } c := GetCopaing(m.Author.ID, m.GuildID) - onLastEventUpdate(c) + LastEventUpdate(c) // add xp trimmed := utils.TrimMessage(strings.ToLower(m.Content)) m.Member.User = m.Author @@ -63,7 +63,7 @@ func OnVoiceUpdate(s *discordgo.Session, e *discordgo.VoiceStateUpdate) { if e.Member.User.Bot { return } - onLastEventUpdate(GetCopaing(e.UserID, e.GuildID)) + LastEventUpdate(GetCopaing(e.UserID, e.GuildID)) cfg := config.GetGuildConfig(e.GuildID) if cfg.IsDisabled(e.ChannelID) { return diff --git a/xp/level.go b/xp/level.go index 14d0e95..b179cdb 100644 --- a/xp/level.go +++ b/xp/level.go @@ -43,7 +43,7 @@ func onNewLevel(s *discordgo.Session, m *discordgo.Member, level uint) { } } -func onLastEventUpdate(c *Copaing) { +func LastEventUpdate(c *Copaing) { h := c.HourSinceLastEvent() l := Lose(h, c.XP) xp := c.XPAlreadyRemoved() @@ -57,7 +57,7 @@ func onLastEventUpdate(c *Copaing) { c.SetLastEvent() } -func onXPUpdate(c *Copaing) { +func XPUpdate(c *Copaing) { h := c.HourSinceLastEvent() l := Lose(h, c.XP) xp := c.XPAlreadyRemoved() From 84bf4caed210b442f75e9e55d4c69b6ca9c6a5c9 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 19:10:21 +0200 Subject: [PATCH 06/15] feat(xp): add +1 in lose function xp part --- README.md | 2 +- xp/functions.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bedf971..44cee92 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ message (number of different rune) - $xp-vocal(x)=0.01 x^{1.3}+1$ where $x$ is the time spent in vocal (in second) - $level(x)=0.2 \sqrt{x}$ where $x$ is the xp - $level^{-1}(x)=(5x)^2$ where $x$ is the level -- $lose(x,y)= x^2\cdot 10^{-2+\ln(x/85)}\cdot\lfloor y/500 \rfloor$ where $x$ is the inactivity time (hour) and $y$ is the xp +- $lose(x,y)= x^2\cdot 10^{-2+\ln(x/85)}\cdot\lfloor y/500 +1 \rfloor$ where $x$ is the inactivity time (hour) and $y$ is the xp ## Technologies diff --git a/xp/functions.go b/xp/functions.go index 196c3d3..63c7fb7 100644 --- a/xp/functions.go +++ b/xp/functions.go @@ -28,6 +28,6 @@ func XPForLevel(level uint) uint { func Lose(time uint, xp uint) uint { return uint(math.Floor( - math.Pow(float64(time), 2) * math.Pow(10, -2+math.Log(float64(time/85))) * math.Floor(float64(xp/500)), + math.Pow(float64(time), 2) * math.Pow(10, -2+math.Log(float64(time/85))) * math.Floor(float64(xp/500)+1), )) } From 9f9c27f42298d51a356743724095a6dd79196dfe Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 19:15:37 +0200 Subject: [PATCH 07/15] feat(debug): adapt xp functions while debug is enabled --- xp/functions.go | 10 +++++++++- xp/member.go | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/xp/functions.go b/xp/functions.go index 63c7fb7..7ab57f2 100644 --- a/xp/functions.go +++ b/xp/functions.go @@ -1,6 +1,9 @@ package xp -import "math" +import ( + "github.com/anhgelus/gokord" + "math" +) func XPMessage(length uint, diversity uint) uint { return uint(math.Floor( @@ -27,6 +30,11 @@ func XPForLevel(level uint) uint { } func Lose(time uint, xp uint) uint { + if gokord.Debug { + return uint(math.Floor( + math.Pow(float64(time), 3) * math.Pow(10, -2+math.Log(float64(time))) * math.Floor(float64(xp/500)+1), + )) // a little bit faster to lose xp + } return uint(math.Floor( math.Pow(float64(time), 2) * math.Pow(10, -2+math.Log(float64(time/85))) * math.Floor(float64(xp/500)+1), )) diff --git a/xp/member.go b/xp/member.go index b9073f3..977f5a0 100644 --- a/xp/member.go +++ b/xp/member.go @@ -9,6 +9,7 @@ import ( "github.com/bwmarrin/discordgo" "github.com/redis/go-redis/v9" "gorm.io/gorm" + "math" "strconv" "time" ) @@ -98,6 +99,9 @@ func (c *Copaing) HourSinceLastEvent() uint { ) return 0 } + if gokord.Debug { + return uint(math.Floor(float64(t-int64(last)) / 60)) // not hours of unix, is minutes of unix + } return utils.HoursOfUnix(t - int64(last)) } From e78c8d8c422bf470924109c186cff9f987b095fd Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 19:30:02 +0200 Subject: [PATCH 08/15] fix(command): /rank prochain niveau not working --- commands/rank.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commands/rank.go b/commands/rank.go index ecf9a08..fe05ba8 100644 --- a/commands/rank.go +++ b/commands/rank.go @@ -9,8 +9,8 @@ import ( func Rank(s *discordgo.Session, i *discordgo.InteractionCreate) { optMap := utils.GenerateOptionMap(i) - c := xp.GetCopaing(i.User.ID, i.GuildID) // current copaing = member who used /rank - xp.LastEventUpdate(c) // update xp and reset last event + c := xp.GetCopaing(i.Member.User.ID, i.GuildID) // current copaing = member who used /rank + xp.LastEventUpdate(c) // update xp and reset last event msg := "Votre niveau" m := i.Member var err error @@ -45,13 +45,13 @@ func Rank(s *discordgo.Session, i *discordgo.InteractionCreate) { msg = fmt.Sprintf("Le niveau de %s", m.DisplayName()) } lvl := xp.Level(c.XP) - nxtLvl := xp.XPForLevel(lvl + 1) + nxtLvlXP := xp.XPForLevel(lvl + 1) err = resp.Message(fmt.Sprintf( "%s : **%d**\n> XP : %d\n> Prochain niveau dans %d XP", msg, lvl, c.XP, - nxtLvl-lvl, + nxtLvlXP-xp.XPForLevel(lvl), )).Send() if err != nil { utils.SendAlert("rank.go - Sending rank", err.Error()) From 76745af0e841518835a73e6e3590a1b85e579163 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 19:40:42 +0200 Subject: [PATCH 09/15] fix(xp): strange comportment with -= --- xp/level.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/xp/level.go b/xp/level.go index b179cdb..aeb7d91 100644 --- a/xp/level.go +++ b/xp/level.go @@ -51,7 +51,7 @@ func LastEventUpdate(c *Copaing) { utils.SendWarn("lose - xp already removed is negative", "lose", l, "xp", xp) c.XP = 0 } else { - c.XP -= l - c.XPAlreadyRemoved() + c.XP = c.XP - l + c.XPAlreadyRemoved() } c.Save() c.SetLastEvent() @@ -63,10 +63,9 @@ func XPUpdate(c *Copaing) { xp := c.XPAlreadyRemoved() if l-xp < 0 { utils.SendWarn("lose - xp already removed is negative", "lose", l, "xp", xp) - c.XP = 0 c.AddXPAlreadyRemoved(0) } else { - c.XP -= l - c.XPAlreadyRemoved() + c.XP = c.XP - l + xp c.AddXPAlreadyRemoved(l - xp) } c.Save() From 5655170779702fb8e390ab948bee7cff1f3fde87 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 19:42:32 +0200 Subject: [PATCH 10/15] fix(xp): already removed never reset --- xp/member.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/xp/member.go b/xp/member.go index 977f5a0..bbf013c 100644 --- a/xp/member.go +++ b/xp/member.go @@ -70,6 +70,15 @@ func (c *Copaing) SetLastEvent() { utils.SendAlert("xp/member.go - Setting last event", err.Error(), "time", t, "base_key", u.GenKey()) return } + err = client.Set(context.Background(), fmt.Sprintf( + "%s:%s", + u.GenKey(), + AlreadyRemoved, + ), "0", 0).Err() + if err != nil { + utils.SendAlert("xp/member.go - Setting already removed to 0", err.Error(), "time", t, "base_key", u.GenKey()) + return + } } func (c *Copaing) HourSinceLastEvent() uint { @@ -120,7 +129,7 @@ func (c *Copaing) AddXPAlreadyRemoved(xp uint) uint { ), exp, 0).Err() if err != nil { utils.SendAlert( - "xp/member.go - Setting last event", + "xp/member.go - Setting already removed", err.Error(), "xp already removed", exp, From d673c055d26b9e3e56d136864946c7dbcd271426 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 15 Apr 2024 19:46:25 +0200 Subject: [PATCH 11/15] fix(command): (again) bad xp shown for /rank in prochain niveau --- commands/rank.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/rank.go b/commands/rank.go index fe05ba8..d056de1 100644 --- a/commands/rank.go +++ b/commands/rank.go @@ -51,7 +51,7 @@ func Rank(s *discordgo.Session, i *discordgo.InteractionCreate) { msg, lvl, c.XP, - nxtLvlXP-xp.XPForLevel(lvl), + nxtLvlXP-c.XP, )).Send() if err != nil { utils.SendAlert("rank.go - Sending rank", err.Error()) From c3dc81bd29318a4916bf8fc383a0d1a5ca90fa64 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Tue, 16 Apr 2024 15:15:30 +0200 Subject: [PATCH 12/15] fix(reducer): sql error integer > int64max --- main.go | 11 +++++++++++ xp/level.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index ed84b8a..1a9693a 100644 --- a/main.go +++ b/main.go @@ -120,7 +120,18 @@ func main() { } func afterInit(dg *discordgo.Session) { + // handlers dg.AddHandler(xp.OnMessage) dg.AddHandler(xp.OnVoiceUpdate) dg.AddHandler(xp.OnLeave) + + // setup timer for periodic reducer + //d := 24 * time.Hour + //if gokord.Debug { + // // reduce for debug + // d = time.Minute + //} + //utils.NewTimer(d, func(stop chan struct{}) { + // xp.PeriodicReducer(dg) + //}) } diff --git a/xp/level.go b/xp/level.go index aeb7d91..bbcc71f 100644 --- a/xp/level.go +++ b/xp/level.go @@ -47,26 +47,69 @@ func LastEventUpdate(c *Copaing) { h := c.HourSinceLastEvent() l := Lose(h, c.XP) xp := c.XPAlreadyRemoved() + oldXP := c.XP if l-xp < 0 { utils.SendWarn("lose - xp already removed is negative", "lose", l, "xp", xp) c.XP = 0 } else { - c.XP = c.XP - l + c.XPAlreadyRemoved() + calc := int(c.XP) - int(l) + int(c.XPAlreadyRemoved()) + if calc < 0 { + c.XP = 0 + } else { + c.XP = uint(calc) + } + } + if oldXP != c.XP { + c.Save() } - c.Save() c.SetLastEvent() } func XPUpdate(c *Copaing) { + oldXP := c.XP + if oldXP == 0 { + return + } h := c.HourSinceLastEvent() l := Lose(h, c.XP) xp := c.XPAlreadyRemoved() if l-xp < 0 { - utils.SendWarn("lose - xp already removed is negative", "lose", l, "xp", xp) + utils.SendWarn("lose - xp_removed is negative", "lose", l, "xp removed", xp) c.AddXPAlreadyRemoved(0) } else { - c.XP = c.XP - l + xp - c.AddXPAlreadyRemoved(l - xp) + calc := int(c.XP) - int(l) + int(xp) + if calc < 0 { + c.AddXPAlreadyRemoved(c.XP) + c.XP = 0 + } else { + c.XP = uint(calc) + c.AddXPAlreadyRemoved(l - xp) + } + } + if oldXP != c.XP { + utils.SendDebug("Save XP", "old", oldXP, "new", c.XP, "user", c.DiscordID) + c.Save() } - c.Save() } + +//func PeriodicReducer(s *discordgo.Session) { +// var wg sync.WaitGroup +// for _, g := range s.State.Guilds { +// for _, m := range utils.FetchGuildUser(s, g.ID) { +// if m.User.Bot { +// continue +// } +// wg.Add(1) +// go func() { +// utils.SendDebug("Async reducer", "user", m.DisplayName(), "guild", g.Name) +// c := GetCopaing(m.User.ID, g.ID) +// XPUpdate(c) +// wg.Done() +// }() +// } +// wg.Wait() // finish the entire guild before starting another +// utils.SendDebug("Guild finished", "guild", g.Name) +// time.Sleep(10 * time.Second) // sleep prevents from spamming the Discord API +// } +// utils.SendDebug("Periodic reduce finished", "len(guilds)", len(s.State.Guilds)) +//} From ef39c156f94b854796bb1c2adddfd40a5acaeca8 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Tue, 16 Apr 2024 15:23:15 +0200 Subject: [PATCH 13/15] feat(reducer): role update on new level --- commands/rank.go | 4 ++-- commands/top.go | 2 +- xp/events.go | 4 ++-- xp/level.go | 30 +++++++++++++++++++++++++++--- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/commands/rank.go b/commands/rank.go index d056de1..219977d 100644 --- a/commands/rank.go +++ b/commands/rank.go @@ -10,7 +10,7 @@ import ( func Rank(s *discordgo.Session, i *discordgo.InteractionCreate) { optMap := utils.GenerateOptionMap(i) c := xp.GetCopaing(i.Member.User.ID, i.GuildID) // current copaing = member who used /rank - xp.LastEventUpdate(c) // update xp and reset last event + xp.LastEventUpdate(s, c) // update xp and reset last event msg := "Votre niveau" m := i.Member var err error @@ -41,7 +41,7 @@ func Rank(s *discordgo.Session, i *discordgo.InteractionCreate) { } c.DiscordID = u.ID // current copaing = member targeted by member who wrote /rank c.Load() // reload copaing (change line before) - xp.XPUpdate(c) // update xp without resetting event + xp.XPUpdate(s, c) // update xp without resetting event msg = fmt.Sprintf("Le niveau de %s", m.DisplayName()) } lvl := xp.Level(c.XP) diff --git a/commands/top.go b/commands/top.go index 6df259f..c0f4dae 100644 --- a/commands/top.go +++ b/commands/top.go @@ -9,7 +9,7 @@ import ( ) func Top(s *discordgo.Session, i *discordgo.InteractionCreate) { - xp.LastEventUpdate(xp.GetCopaing(i.User.ID, i.GuildID)) + xp.LastEventUpdate(s, xp.GetCopaing(i.User.ID, i.GuildID)) resp := utils.ResponseBuilder{C: s, I: i} err := resp.IsDeferred().Send() if err != nil { diff --git a/xp/events.go b/xp/events.go index 655e9fd..ebb7f40 100644 --- a/xp/events.go +++ b/xp/events.go @@ -30,7 +30,7 @@ func OnMessage(s *discordgo.Session, m *discordgo.MessageCreate) { return } c := GetCopaing(m.Author.ID, m.GuildID) - LastEventUpdate(c) + LastEventUpdate(s, c) // add xp trimmed := utils.TrimMessage(strings.ToLower(m.Content)) m.Member.User = m.Author @@ -63,7 +63,7 @@ func OnVoiceUpdate(s *discordgo.Session, e *discordgo.VoiceStateUpdate) { if e.Member.User.Bot { return } - LastEventUpdate(GetCopaing(e.UserID, e.GuildID)) + LastEventUpdate(s, GetCopaing(e.UserID, e.GuildID)) cfg := config.GetGuildConfig(e.GuildID) if cfg.IsDisabled(e.ChannelID) { return diff --git a/xp/level.go b/xp/level.go index bbcc71f..12e5a28 100644 --- a/xp/level.go +++ b/xp/level.go @@ -43,7 +43,23 @@ func onNewLevel(s *discordgo.Session, m *discordgo.Member, level uint) { } } -func LastEventUpdate(c *Copaing) { +func (c *Copaing) OnNewLevel(s *discordgo.Session, level uint) { + m, err := s.GuildMember(c.GuildID, c.DiscordID) + if err != nil { + utils.SendAlert( + "xp/level.go - Getting member for new level", + err.Error(), + "discord_id", + c.DiscordID, + "guild_id", + c.GuildID, + ) + return + } + onNewLevel(s, m, level) +} + +func LastEventUpdate(s *discordgo.Session, c *Copaing) { h := c.HourSinceLastEvent() l := Lose(h, c.XP) xp := c.XPAlreadyRemoved() @@ -61,11 +77,15 @@ func LastEventUpdate(c *Copaing) { } if oldXP != c.XP { c.Save() + lvl := Level(c.XP) + if Level(oldXP) != Level(c.XP) { + c.OnNewLevel(s, lvl) + } } c.SetLastEvent() } -func XPUpdate(c *Copaing) { +func XPUpdate(s *discordgo.Session, c *Copaing) { oldXP := c.XP if oldXP == 0 { return @@ -89,6 +109,10 @@ func XPUpdate(c *Copaing) { if oldXP != c.XP { utils.SendDebug("Save XP", "old", oldXP, "new", c.XP, "user", c.DiscordID) c.Save() + lvl := Level(c.XP) + if Level(oldXP) != Level(c.XP) { + c.OnNewLevel(s, lvl) + } } } @@ -103,7 +127,7 @@ func XPUpdate(c *Copaing) { // go func() { // utils.SendDebug("Async reducer", "user", m.DisplayName(), "guild", g.Name) // c := GetCopaing(m.User.ID, g.ID) -// XPUpdate(c) +// XPUpdate(s, c) // wg.Done() // }() // } From 7fe68106bec7133981ac91026156977e0c7b4ca1 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Tue, 16 Apr 2024 15:24:25 +0200 Subject: [PATCH 14/15] feat(reducer): periodic reducer updating xp --- main.go | 18 ++++++++++-------- xp/level.go | 44 +++++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/main.go b/main.go index 1a9693a..d8cdbc2 100644 --- a/main.go +++ b/main.go @@ -3,10 +3,12 @@ package main import ( "flag" "github.com/anhgelus/gokord" + "github.com/anhgelus/gokord/utils" "github.com/anhgelus/les-copaings-bot/commands" "github.com/anhgelus/les-copaings-bot/config" "github.com/anhgelus/les-copaings-bot/xp" "github.com/bwmarrin/discordgo" + "time" ) var token string @@ -126,12 +128,12 @@ func afterInit(dg *discordgo.Session) { dg.AddHandler(xp.OnLeave) // setup timer for periodic reducer - //d := 24 * time.Hour - //if gokord.Debug { - // // reduce for debug - // d = time.Minute - //} - //utils.NewTimer(d, func(stop chan struct{}) { - // xp.PeriodicReducer(dg) - //}) + d := 24 * time.Hour + if gokord.Debug { + // reduce time for debug + d = time.Minute + } + utils.NewTimer(d, func(stop chan struct{}) { + xp.PeriodicReducer(dg) + }) } diff --git a/xp/level.go b/xp/level.go index 12e5a28..bbe11a4 100644 --- a/xp/level.go +++ b/xp/level.go @@ -5,6 +5,8 @@ import ( "github.com/anhgelus/les-copaings-bot/config" "github.com/bwmarrin/discordgo" "slices" + "sync" + "time" ) func onNewLevel(s *discordgo.Session, m *discordgo.Member, level uint) { @@ -116,24 +118,24 @@ func XPUpdate(s *discordgo.Session, c *Copaing) { } } -//func PeriodicReducer(s *discordgo.Session) { -// var wg sync.WaitGroup -// for _, g := range s.State.Guilds { -// for _, m := range utils.FetchGuildUser(s, g.ID) { -// if m.User.Bot { -// continue -// } -// wg.Add(1) -// go func() { -// utils.SendDebug("Async reducer", "user", m.DisplayName(), "guild", g.Name) -// c := GetCopaing(m.User.ID, g.ID) -// XPUpdate(s, c) -// wg.Done() -// }() -// } -// wg.Wait() // finish the entire guild before starting another -// utils.SendDebug("Guild finished", "guild", g.Name) -// time.Sleep(10 * time.Second) // sleep prevents from spamming the Discord API -// } -// utils.SendDebug("Periodic reduce finished", "len(guilds)", len(s.State.Guilds)) -//} +func PeriodicReducer(s *discordgo.Session) { + var wg sync.WaitGroup + for _, g := range s.State.Guilds { + for _, m := range utils.FetchGuildUser(s, g.ID) { + if m.User.Bot { + continue + } + wg.Add(1) + go func() { + utils.SendDebug("Async reducer", "user", m.DisplayName(), "guild", g.Name) + c := GetCopaing(m.User.ID, g.ID) + XPUpdate(s, c) + wg.Done() + }() + } + wg.Wait() // finish the entire guild before starting another + utils.SendDebug("Guild finished", "guild", g.Name) + time.Sleep(10 * time.Second) // sleep prevents from spamming the Discord API + } + utils.SendDebug("Periodic reduce finished", "len(guilds)", len(s.State.Guilds)) +} From b4e72dcae10265bbc1595f2e520b1407cf8e250d Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Tue, 16 Apr 2024 15:44:02 +0200 Subject: [PATCH 15/15] docs(debug): better debug log --- xp/level.go | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/xp/level.go b/xp/level.go index bbe11a4..4830298 100644 --- a/xp/level.go +++ b/xp/level.go @@ -78,11 +78,22 @@ func LastEventUpdate(s *discordgo.Session, c *Copaing) { } } if oldXP != c.XP { - c.Save() lvl := Level(c.XP) - if Level(oldXP) != Level(c.XP) { + if Level(oldXP) != lvl { + utils.SendDebug( + "Level changed", + "old", + Level(oldXP), + "new", + lvl, + "discord_id", + c.DiscordID, + "guild_id", + c.GuildID, + ) c.OnNewLevel(s, lvl) } + c.Save() } c.SetLastEvent() } @@ -109,12 +120,23 @@ func XPUpdate(s *discordgo.Session, c *Copaing) { } } if oldXP != c.XP { - utils.SendDebug("Save XP", "old", oldXP, "new", c.XP, "user", c.DiscordID) - c.Save() lvl := Level(c.XP) - if Level(oldXP) != Level(c.XP) { + if Level(oldXP) != lvl { + utils.SendDebug( + "Level updated", + "old", + Level(oldXP), + "new", + lvl, + "discord_id", + c.DiscordID, + "guild_id", + c.GuildID, + ) c.OnNewLevel(s, lvl) } + utils.SendDebug("Save XP", "old", oldXP, "new", c.XP, "user", c.DiscordID) + c.Save() } } @@ -127,15 +149,14 @@ func PeriodicReducer(s *discordgo.Session) { } wg.Add(1) go func() { - utils.SendDebug("Async reducer", "user", m.DisplayName(), "guild", g.Name) c := GetCopaing(m.User.ID, g.ID) XPUpdate(s, c) wg.Done() }() } wg.Wait() // finish the entire guild before starting another - utils.SendDebug("Guild finished", "guild", g.Name) - time.Sleep(10 * time.Second) // sleep prevents from spamming the Discord API + utils.SendDebug("Periodic reduce, guild finished", "guild", g.Name) + time.Sleep(10 * time.Second) // sleep prevents from spamming the Discord API and the database } utils.SendDebug("Periodic reduce finished", "len(guilds)", len(s.State.Guilds)) }