many update

This commit is contained in:
2025-05-26 20:35:50 -06:00
parent 3af77b6bea
commit 0a730be6f7
12 changed files with 608 additions and 177 deletions

View File

@ -14,8 +14,15 @@ func HandleCommand(commandName string, cooldownDuration time.Duration, handler C
return
}
// Get user information (handle both guild and DM contexts)
user, userErr := GetUser(i)
if userErr != nil {
RespondWithError(s, i, "Error getting user information: "+userErr.Error())
return
}
// Get or create user and guild profile
_, createUserError := GetOrCreateUserWithGuild(i.Member.User.ID, i.Member.User.Username, i.GuildID)
_, createUserError := GetOrCreateUserWithGuild(user.ID, user.Username, i.GuildID)
if createUserError != nil {
RespondWithError(s, i, "Error creating user profile: "+createUserError.Error())

90
lib/config.go Normal file
View File

@ -0,0 +1,90 @@
package lib
import (
"os"
"strconv"
"time"
)
// Config holds all configuration values
type Config struct {
// Discord settings
DiscordToken string
// Himbucks settings
HimbucksPerReward int
MessageCountThreshold int
CooldownPeriod time.Duration
// Markov settings
MarkovDefaultMessages int
MarkovMaxMessages int
MarkovCacheSize int
// Database settings
MaxOpenConns int
MaxIdleConns int
ConnMaxLifetime time.Duration
// Command cooldowns (in seconds)
PingCooldown int
HsCooldown int
MarkovCooldown int
HimbucksCooldown int
HimboardCooldown int
SendbucksCooldown int
}
var AppConfig *Config
// LoadConfig loads configuration from environment variables
func LoadConfig() *Config {
config := &Config{
// Discord settings
DiscordToken: getEnv("DISCORD_TOKEN", ""),
// Himbucks settings
HimbucksPerReward: getEnvInt("HIMBUCKS_PER_REWARD", 10),
MessageCountThreshold: getEnvInt("MESSAGE_COUNT_THRESHOLD", 5),
CooldownPeriod: time.Duration(getEnvInt("HIMBUCKS_COOLDOWN_MINUTES", 1)) * time.Minute,
// Markov settings
MarkovDefaultMessages: getEnvInt("MARKOV_DEFAULT_MESSAGES", 100),
MarkovMaxMessages: getEnvInt("MARKOV_MAX_MESSAGES", 1000),
MarkovCacheSize: getEnvInt("MARKOV_CACHE_SIZE", 10),
// Database settings
MaxOpenConns: getEnvInt("DB_MAX_OPEN_CONNS", 25),
MaxIdleConns: getEnvInt("DB_MAX_IDLE_CONNS", 5),
ConnMaxLifetime: time.Duration(getEnvInt("DB_CONN_MAX_LIFETIME_MINUTES", 5)) * time.Minute,
// Command cooldowns (in seconds)
PingCooldown: getEnvInt("PING_COOLDOWN_SECONDS", 5),
HsCooldown: getEnvInt("HS_COOLDOWN_SECONDS", 10),
MarkovCooldown: getEnvInt("MARKOV_COOLDOWN_SECONDS", 30),
HimbucksCooldown: getEnvInt("HIMBUCKS_COOLDOWN_SECONDS", 5),
HimboardCooldown: getEnvInt("HIMBOARD_COOLDOWN_SECONDS", 5),
SendbucksCooldown: getEnvInt("SENDBUCKS_COOLDOWN_SECONDS", 1800),
}
AppConfig = config
return config
}
// getEnv gets an environment variable with a default value
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
// getEnvInt gets an environment variable as an integer with a default value
func getEnvInt(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if intValue, err := strconv.Atoi(value); err == nil {
return intValue
}
}
return defaultValue
}

View File

@ -16,6 +16,15 @@ import (
var DBClient *sql.DB
var DBConnector *libsql.Connector
// Prepared statements
var (
stmtGetBalance *sql.Stmt
stmtUpdateBalance *sql.Stmt
stmtGetLeaderboard *sql.Stmt
stmtGetUserProfile *sql.Stmt
stmtUpdateProfile *sql.Stmt
)
func InitDB() error {
// Determine DB path based on /data directory existence
var dbPath string
@ -31,9 +40,24 @@ func InitDB() error {
os.Exit(1)
}
// Configure connection pool using config values
db.SetMaxOpenConns(AppConfig.MaxOpenConns)
db.SetMaxIdleConns(AppConfig.MaxIdleConns)
db.SetConnMaxLifetime(AppConfig.ConnMaxLifetime)
// Test the connection
if err := db.Ping(); err != nil {
return fmt.Errorf("failed to ping database: %w", err)
}
DBClient = db
return runMigrations()
if err := runMigrations(); err != nil {
return err
}
// Prepare frequently used statements
return prepareStatements()
}
type Migration struct {
@ -140,3 +164,61 @@ func runMigrations() error {
log.Println("Database migrations completed successfully")
return nil
}
func prepareStatements() error {
var err error
// Prepare balance query
stmtGetBalance, err = DBClient.Prepare(`
SELECT gp.currency_balance
FROM guild_profiles gp
JOIN users u ON gp.user_id = u.id
WHERE u.discord_id = ? AND gp.guild_id = ?`)
if err != nil {
return fmt.Errorf("failed to prepare balance query: %w", err)
}
// Prepare leaderboard query
stmtGetLeaderboard, err = DBClient.Prepare(`
SELECT u.username, gp.currency_balance, gp.message_count
FROM guild_profiles gp
JOIN users u ON gp.user_id = u.id
WHERE gp.guild_id = ?
ORDER BY gp.currency_balance DESC
LIMIT ?`)
if err != nil {
return fmt.Errorf("failed to prepare leaderboard query: %w", err)
}
// Prepare user profile query
stmtGetUserProfile, err = DBClient.Prepare(`
SELECT message_count, last_reward_at
FROM guild_profiles
WHERE user_id = ? AND guild_id = ?`)
if err != nil {
return fmt.Errorf("failed to prepare user profile query: %w", err)
}
log.Println("Prepared statements initialized successfully")
return nil
}
// CleanupDB closes all prepared statements
func CleanupDB() {
if stmtGetBalance != nil {
stmtGetBalance.Close()
}
if stmtUpdateBalance != nil {
stmtUpdateBalance.Close()
}
if stmtGetLeaderboard != nil {
stmtGetLeaderboard.Close()
}
if stmtGetUserProfile != nil {
stmtGetUserProfile.Close()
}
if stmtUpdateProfile != nil {
stmtUpdateProfile.Close()
}
log.Println("Database cleanup completed")
}

View File

@ -8,12 +8,6 @@ import (
"github.com/bwmarrin/discordgo"
)
const (
HimbucksPerReward = 10
MessageCountThreshold = 5
CooldownPeriod = time.Minute
)
type HimbucksEntry struct {
Username string
Balance int
@ -41,8 +35,8 @@ func ProcessHimbucks(s *discordgo.Session, m *discordgo.MessageCreate, ctx *Proc
}
messageCount++
shouldReward := messageCount >= MessageCountThreshold &&
(!lastRewardAt.Valid || time.Since(lastRewardAt.Time) >= CooldownPeriod)
shouldReward := messageCount >= AppConfig.MessageCountThreshold &&
(!lastRewardAt.Valid || time.Since(lastRewardAt.Time) >= AppConfig.CooldownPeriod)
if shouldReward {
_, err = tx.Exec(`
@ -51,7 +45,7 @@ func ProcessHimbucks(s *discordgo.Session, m *discordgo.MessageCreate, ctx *Proc
message_count = 0,
last_reward_at = CURRENT_TIMESTAMP
WHERE user_id = ? AND guild_id = ?`,
HimbucksPerReward, ctx.UserID, ctx.GuildID)
AppConfig.HimbucksPerReward, ctx.UserID, ctx.GuildID)
} else {
_, err = tx.Exec(`
UPDATE guild_profiles
@ -69,12 +63,7 @@ func ProcessHimbucks(s *discordgo.Session, m *discordgo.MessageCreate, ctx *Proc
func GetBalance(discordID, guildID string) (int, error) {
var balance int
err := DBClient.QueryRow(`
SELECT gp.currency_balance
FROM guild_profiles gp
JOIN users u ON gp.user_id = u.id
WHERE u.discord_id = ? AND gp.guild_id = ?`,
discordID, guildID).Scan(&balance)
err := stmtGetBalance.QueryRow(discordID, guildID).Scan(&balance)
if err == sql.ErrNoRows {
return 0, nil
}
@ -173,14 +162,7 @@ func SendBalance(fromDiscordID, toDiscordID, guildID string, amount int) error {
}
func GetLeaderboard(guildID string, limit int) ([]HimbucksEntry, error) {
rows, err := DBClient.Query(`
SELECT u.username, gp.currency_balance, gp.message_count
FROM guild_profiles gp
JOIN users u ON gp.user_id = u.id
WHERE gp.guild_id = ?
ORDER BY gp.currency_balance DESC
LIMIT ?`,
guildID, limit)
rows, err := stmtGetLeaderboard.Query(guildID, limit)
if err != nil {
return nil, fmt.Errorf("failed to get leaderboard: %w", err)
}