himbot/lib/himbucks.go
Atridad Lahiji 4cf9c3295f
All checks were successful
Docker Deploy / build-and-push (push) Successful in 1m26s
HIMBOT RETURNS
2024-11-22 17:04:33 -06:00

199 lines
5.1 KiB
Go

package lib
import (
"database/sql"
"fmt"
"time"
"github.com/bwmarrin/discordgo"
)
const (
HimbucksPerReward = 10
MessageCountThreshold = 5
CooldownPeriod = time.Minute
)
type HimbucksEntry struct {
Username string
Balance int
MessageCount int
}
func ProcessHimbucks(s *discordgo.Session, m *discordgo.MessageCreate, ctx *ProcessContext) error {
tx, err := DBClient.Begin()
if err != nil {
return fmt.Errorf("failed to start transaction: %w", err)
}
defer tx.Rollback()
// Get current state
var messageCount int
var lastRewardAt sql.NullTime
err = tx.QueryRow(`
SELECT message_count, last_reward_at
FROM guild_profiles
WHERE user_id = ? AND guild_id = ?`,
ctx.UserID, ctx.GuildID).Scan(&messageCount, &lastRewardAt)
if err != nil {
return fmt.Errorf("failed to get message count: %w", err)
}
messageCount++
shouldReward := messageCount >= MessageCountThreshold &&
(!lastRewardAt.Valid || time.Since(lastRewardAt.Time) >= CooldownPeriod)
if shouldReward {
_, err = tx.Exec(`
UPDATE guild_profiles
SET currency_balance = currency_balance + ?,
message_count = 0,
last_reward_at = CURRENT_TIMESTAMP
WHERE user_id = ? AND guild_id = ?`,
HimbucksPerReward, ctx.UserID, ctx.GuildID)
} else {
_, err = tx.Exec(`
UPDATE guild_profiles
SET message_count = ?
WHERE user_id = ? AND guild_id = ?`,
messageCount, ctx.UserID, ctx.GuildID)
}
if err != nil {
return fmt.Errorf("failed to update profile: %w", err)
}
return tx.Commit()
}
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)
if err == sql.ErrNoRows {
return 0, nil
}
if err != nil {
return 0, fmt.Errorf("failed to get balance: %w", err)
}
return balance, nil
}
func SendBalance(fromDiscordID, toDiscordID, guildID string, amount int) error {
// Start database transaction
tx, err := DBClient.Begin()
if err != nil {
return fmt.Errorf("failed to start transaction: %w", err)
}
defer tx.Rollback()
// Get sender's user ID
var fromUserID string
err = tx.QueryRow(`
SELECT id
FROM users
WHERE discord_id = ?`, fromDiscordID).Scan(&fromUserID)
if err != nil {
return fmt.Errorf("sender not found: %w", err)
}
// Get recipient's user ID
var toUserID string
err = tx.QueryRow(`
SELECT id
FROM users
WHERE discord_id = ?`, toDiscordID).Scan(&toUserID)
if err != nil {
return fmt.Errorf("recipient not found: %w", err)
}
// Check if sender has sufficient balance
var senderBalance int
err = tx.QueryRow(`
SELECT currency_balance
FROM guild_profiles
WHERE user_id = ? AND guild_id = ?`,
fromUserID, guildID).Scan(&senderBalance)
if err != nil {
return fmt.Errorf("failed to get sender balance: %w", err)
}
if senderBalance < amount {
return fmt.Errorf("insufficient balance: have %d, trying to send %d", senderBalance, amount)
}
// Deduct from sender
_, err = tx.Exec(`
UPDATE guild_profiles
SET currency_balance = currency_balance - ?
WHERE user_id = ? AND guild_id = ?`,
amount, fromUserID, guildID)
if err != nil {
return fmt.Errorf("failed to deduct from sender: %w", err)
}
// Add to recipient
result, err := tx.Exec(`
UPDATE guild_profiles
SET currency_balance = currency_balance + ?
WHERE user_id = ? AND guild_id = ?`,
amount, toUserID, guildID)
if err != nil {
return fmt.Errorf("failed to add to recipient: %w", err)
}
// Check if recipient exists in guild_profiles
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to check rows affected: %w", err)
}
// If recipient doesn't have a profile in this guild, create one
if rowsAffected == 0 {
_, err = tx.Exec(`
INSERT INTO guild_profiles (user_id, guild_id, currency_balance, message_count)
VALUES (?, ?, ?, 0)`,
toUserID, guildID, amount)
if err != nil {
return fmt.Errorf("failed to create recipient profile: %w", err)
}
}
// Commit transaction
if err = tx.Commit(); err != nil {
return fmt.Errorf("failed to commit transaction: %w", err)
}
return nil
}
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)
if err != nil {
return nil, fmt.Errorf("failed to get leaderboard: %w", err)
}
defer rows.Close()
var entries []HimbucksEntry
for rows.Next() {
var entry HimbucksEntry
err := rows.Scan(&entry.Username, &entry.Balance, &entry.MessageCount)
if err != nil {
return nil, fmt.Errorf("failed to scan leaderboard entry: %w", err)
}
entries = append(entries, entry)
}
return entries, nil
}