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) }