feat: leveling system

This commit is contained in:
2026-03-17 15:17:36 +00:00
parent 0354fdc032
commit 2bce787c05
24 changed files with 1840 additions and 3 deletions
+134
View File
@@ -0,0 +1,134 @@
package level
import (
"context"
"math/rand"
"time"
"velox-bot/internal/db/repos/levelrepo"
)
type SettingsProvider interface {
IsLevelSystemEnabled(ctx context.Context, guildID int64) (bool, error)
GetLevelupChannelID(ctx context.Context, guildID int64) (int64, bool, error)
GetRoleRewardForLevel(ctx context.Context, guildID int64, level int) (roleID int64, ok bool, err error)
SetLevelUpChannel(ctx context.Context, guildID int64, channelID int64) error
SetLevelUpMessage(ctx context.Context, guildID int64, message string) error
GetLevelUpMessage(ctx context.Context, guildID int64) (string, error)
}
type Result struct {
LeveledUp bool
NewLevel int
RoleRewardID int64 // 0 if none
LevelupMessage string // you can fill this later
LevelupChannel int64 // 0 means "use current channel"
UpdatedXP int64
UpdatedLevel int
}
type Service struct {
levels *levelrepo.Repo
settings SettingsProvider
rand *rand.Rand
}
func New(levels *levelrepo.Repo, settings SettingsProvider) *Service {
return &Service{
levels: levels,
settings: settings,
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
}
}
// HandleMessage mirrors your Python gain_xp behavior, minus Discord specifics.
func (s *Service) HandleMessage(ctx context.Context, guildID, userID int64) (*Result, error) {
enabled, err := s.settings.IsLevelSystemEnabled(ctx, guildID)
if err != nil || !enabled {
return &Result{LeveledUp: false}, err
}
lvl, err := s.levels.GetLevel(ctx, guildID, userID)
if err != nil {
return nil, err
}
// XP gain rules
shouldGainXP := false
if lvl.Level < 5 {
shouldGainXP = true
} else {
// random.randint(1, level//4) == 1
div := lvl.Level / 4
if div < 1 {
div = 1
}
if s.rand.Intn(div)+1 == 1 {
shouldGainXP = true
}
}
if !shouldGainXP {
return &Result{
LeveledUp: false,
UpdatedXP: lvl.XP,
UpdatedLevel: lvl.Level,
}, nil
}
// This assumes your setXp() adds some fixed XP per hit; adapt amount as needed.
xpPerMessage := s.rand.Intn(3) + 1
lvl.XP += int64(xpPerMessage)
res := &Result{
LeveledUp: false,
UpdatedXP: lvl.XP,
UpdatedLevel: lvl.Level,
}
// Level-up rule: if xp >= 100 -> level++
if lvl.XP >= 100 {
lvl.Level++
lvl.XP = 0 // or keep overflow, depending on Python behavior
res.LeveledUp = true
res.NewLevel = lvl.Level
res.UpdatedXP = lvl.XP
res.UpdatedLevel = lvl.Level
roleID, ok, err := s.settings.GetRoleRewardForLevel(ctx, guildID, lvl.Level)
if err != nil {
return nil, err
}
if ok {
res.RoleRewardID = roleID
}
chID, ok, err := s.settings.GetLevelupChannelID(ctx, guildID)
if err != nil {
return nil, err
}
if ok {
res.LevelupChannel = chID
}
}
// Persist
if err := s.levels.UpsertLevel(ctx, lvl); err != nil {
return nil, err
}
return res, nil
}
func (s *Service) SetLevel(ctx context.Context, guildID, userID int64, level int) error {
return s.levels.SetLevel(ctx, guildID, userID, level)
}
func (s *Service) GetLevel(ctx context.Context, guildID, userID int64) (*levelrepo.Level, error) {
return s.levels.GetLevel(ctx, guildID, userID)
}
func (s *Service) TopLevels(ctx context.Context, guildID int64, limit int) ([]*levelrepo.Level, error) {
return s.levels.TopLevels(ctx, guildID, limit)
}