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
+94
View File
@@ -0,0 +1,94 @@
package levelrepo
import (
"context"
"database/sql"
)
type Level struct {
GuildID int64
UserID int64
Level int
XP int64
}
type Repo struct {
db *sql.DB
}
func NewRepo(db *sql.DB) *Repo {
return &Repo{db: db}
}
func (r *Repo) GetLevel(ctx context.Context, guildID, userID int64) (*Level, error) {
const q = `
SELECT level, xp
FROM levels
WHERE guild = $1 AND user_id = $2
`
row := r.db.QueryRowContext(ctx, q, guildID, userID)
var lvl Level
lvl.GuildID = guildID
lvl.UserID = userID
switch err := row.Scan(&lvl.Level, &lvl.XP); err {
case sql.ErrNoRows:
// default new user
lvl.Level = 0
lvl.XP = 0
return &lvl, nil
case nil:
return &lvl, nil
default:
return nil, err
}
}
func (r *Repo) UpsertLevel(ctx context.Context, lvl *Level) error {
const q = `
INSERT INTO levels (guild, user_id, level, xp)
VALUES ($1, $2, $3, $4)
ON CONFLICT (guild, user_id)
DO UPDATE SET level = EXCLUDED.level, xp = EXCLUDED.xp
`
_, err := r.db.ExecContext(ctx, q, lvl.GuildID, lvl.UserID, lvl.Level, lvl.XP)
return err
}
func (r *Repo) SetLevel(ctx context.Context, guildID, userID int64, level int) error {
const q = `
INSERT INTO levels (guild, user_id, level, xp)
VALUES ($1, $2, $3, 99)
ON CONFLICT (guild, user_id)
DO UPDATE SET level = EXCLUDED.level, xp = 99
`
_, err := r.db.ExecContext(ctx, q, guildID, userID, level)
return err
}
func (r *Repo) TopLevels(ctx context.Context, guildID int64, limit int) ([]*Level, error) {
const q = `
SELECT user_id, level, xp
FROM levels
WHERE guild = $1
ORDER BY level DESC, xp DESC, user_id ASC
LIMIT $2
`
rows, err := r.db.QueryContext(ctx, q, guildID, limit)
if err != nil {
return nil, err
}
defer rows.Close()
out := make([]*Level, 0, limit)
for rows.Next() {
lvl := &Level{GuildID: guildID}
if err := rows.Scan(&lvl.UserID, &lvl.Level, &lvl.XP); err != nil {
return nil, err
}
out = append(out, lvl)
}
if err := rows.Err(); err != nil {
return nil, err
}
return out, nil
}
+138
View File
@@ -0,0 +1,138 @@
package settingsrepo
import (
"context"
"database/sql"
)
type Repo struct {
db *sql.DB
}
func NewRepo(db *sql.DB) *Repo {
return &Repo{db: db}
}
func (r *Repo) SetLevelSystemEnabled(ctx context.Context, guildID int64, enabled bool) error {
const q = `
INSERT INTO levelsettings (guild_id, levelsys)
VALUES ($1, $2)
ON CONFLICT (guild_id)
DO UPDATE SET levelsys = EXCLUDED.levelsys
`
_, err := r.db.ExecContext(ctx, q, guildID, enabled)
return err
}
func (r *Repo) IsLevelSystemEnabled(ctx context.Context, guildID int64) (bool, error) {
const q = `
SELECT levelsys
FROM levelsettings
WHERE guild_id = $1
`
row := r.db.QueryRowContext(ctx, q, guildID)
var enabled bool
switch err := row.Scan(&enabled); err {
case nil:
return enabled, nil
case sql.ErrNoRows:
return false, nil
default:
return false, err
}
}
func (r *Repo) GetLevelupChannelID(ctx context.Context, guildID int64) (int64, bool, error) {
const q = `
SELECT levelup_channel_id
FROM levelup
WHERE guild_id = $1
`
row := r.db.QueryRowContext(ctx, q, guildID)
var channelID sql.NullInt64
switch err := row.Scan(&channelID); err {
case nil:
if !channelID.Valid {
return 0, false, nil
}
return channelID.Int64, true, nil
case sql.ErrNoRows:
return 0, false, nil
default:
return 0, false, err
}
}
func (r *Repo) GetRoleRewardForLevel(ctx context.Context, guildID int64, level int) (roleID int64, ok bool, err error) {
const q = `
SELECT role
FROM levelrewards
WHERE guild_id = $1 AND levelreq = $2
`
row := r.db.QueryRowContext(ctx, q, guildID, level)
var role sql.NullInt64
switch err := row.Scan(&role); err {
case nil:
if !role.Valid {
return 0, false, nil
}
return role.Int64, true, nil
case sql.ErrNoRows:
return 0, false, nil
default:
return 0, false, err
}
}
func (r *Repo) SetRoleRewardForLevel(ctx context.Context, guildID int64, level int, roleID int64) error {
const q = `
INSERT INTO levelrewards (guild_id, levelreq, role)
VALUES ($1, $2, $3)
ON CONFLICT (guild_id, levelreq)
DO UPDATE SET role = EXCLUDED.role
`
_, err := r.db.ExecContext(ctx, q, guildID, level, roleID)
return err
}
func (r *Repo) SetLevelUpChannel(ctx context.Context, guildID int64, channelID int64) error {
const q = `
INSERT INTO levelup (guild_id, levelup_channel_id)
VALUES ($1, $2)
ON CONFLICT (guild_id)
DO UPDATE SET levelup_channel_id = EXCLUDED.levelup_channel_id
`
_, err := r.db.ExecContext(ctx, q, guildID, channelID)
return err
}
func (r *Repo) SetLevelUpMessage(ctx context.Context, guildID int64, message string) error {
const q = `
UPDATE levelsettings
SET message = $1
WHERE guild_id = $2
`
_, err := r.db.ExecContext(ctx, q, message, guildID)
return err
}
func (r *Repo) GetLevelUpMessage(ctx context.Context, guildID int64) (string, error) {
const q = `
SELECT message
FROM levelsettings
WHERE guild_id = $1
`
row := r.db.QueryRowContext(ctx, q, guildID)
var message sql.NullString
switch err := row.Scan(&message); err {
case nil:
if !message.Valid {
return "GG {user}, you reached level {level}!", nil
}
return message.String, nil
case sql.ErrNoRows:
return "GG {user}, you reached level {level}!", nil
default:
return "", err
}
}