From be4f67218d40b5b6704057d2b6fa0a071884f3f3 Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Thu, 7 Nov 2024 02:56:56 -0600 Subject: [PATCH] Added sending himbucks --- command/himbucks.go | 51 +++++++++++++++++++++++++- lib/himbucks.go | 89 ++++++++++++++++++++++++++++++++++++++++++++- main.go | 22 ++++++++++- 3 files changed, 158 insertions(+), 4 deletions(-) diff --git a/command/himbucks.go b/command/himbucks.go index 66bd7e3..794b193 100644 --- a/command/himbucks.go +++ b/command/himbucks.go @@ -8,7 +8,7 @@ import ( "github.com/bwmarrin/discordgo" ) -func BalanceCommand(s *discordgo.Session, i *discordgo.InteractionCreate) (string, error) { +func BalanceGetCommand(s *discordgo.Session, i *discordgo.InteractionCreate) (string, error) { user, err := lib.GetUser(i) if err != nil { return "", err @@ -19,7 +19,7 @@ func BalanceCommand(s *discordgo.Session, i *discordgo.InteractionCreate) (strin return "", err } - return fmt.Sprintf("💸 You have %d himbucks! 💸", balance), nil + return fmt.Sprintf("💸 You have %d Himbucks! 💸", balance), nil } func BalanceSyncCommand(s *discordgo.Session, i *discordgo.InteractionCreate) (string, error) { @@ -49,3 +49,50 @@ func LeaderboardCommand(s *discordgo.Session, i *discordgo.InteractionCreate) (s return sb.String(), nil } + +func BalanceSendCommand(s *discordgo.Session, i *discordgo.InteractionCreate) (string, error) { + options := i.ApplicationCommandData().Options + + // Discord handles the user mention/tag and provides the correct user ID + var recipientID string + var amount int + + for _, opt := range options { + switch opt.Name { + case "user": + recipientID = opt.UserValue(nil).ID + case "amount": + amount = int(opt.IntValue()) + } + } + + // Validate amount + if amount <= 0 { + return "", fmt.Errorf("amount must be positive") + } + + // Get sender's info + sender, err := lib.GetUser(i) + if err != nil { + return "", fmt.Errorf("failed to get sender info: %w", err) + } + + // Don't allow sending to self + if sender.ID == recipientID { + return "", fmt.Errorf("you cannot send himbucks to yourself") + } + + // Get recipient's info + recipient, err := s.User(recipientID) + if err != nil { + return "", fmt.Errorf("failed to get recipient info: %w", err) + } + + // Send the himbucks + err = lib.SendBalance(sender.ID, recipientID, i.GuildID, amount) + if err != nil { + return "", fmt.Errorf("failed to send himbucks: %w", err) + } + + return fmt.Sprintf("💸 Successfully sent %d Himbucks to %s! 💸", amount, recipient.Username), nil +} diff --git a/lib/himbucks.go b/lib/himbucks.go index 4506513..c65210c 100644 --- a/lib/himbucks.go +++ b/lib/himbucks.go @@ -84,8 +84,95 @@ func GetBalance(discordID, guildID string) (int, error) { 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 SyncBalance() error { - // First perform the regular sync _, syncError := DBConnector.Sync() if syncError != nil { fmt.Println("Error syncing database:", syncError) diff --git a/main.go b/main.go index 11683e7..20a8de0 100644 --- a/main.go +++ b/main.go @@ -55,15 +55,35 @@ var ( Name: "syncbucks", Description: "Sync your himbucks balance with the database", }, + { + Name: "sendbucks", + Description: "Send himbucks to another user", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionUser, + Name: "user", + Description: "The user to send himbucks to", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionInteger, + Name: "amount", + Description: "Amount of himbucks to send", + Required: true, + MinValue: &[]float64{1}[0], + }, + }, + }, } commandHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate){ "ping": lib.HandleCommand("ping", 5*time.Second, command.PingCommand), "hs": lib.HandleCommand("hs", 10*time.Second, command.HsCommand), "markov": lib.HandleCommand("markov", 30*time.Second, command.MarkovCommand), - "himbucks": lib.HandleCommand("himbucks", 5*time.Second, command.BalanceCommand), + "himbucks": lib.HandleCommand("himbucks", 5*time.Second, command.BalanceGetCommand), "himboard": lib.HandleCommand("himboard", 5*time.Second, command.LeaderboardCommand), "syncbucks": lib.HandleCommand("syncbucks", 1800*time.Second, command.BalanceSyncCommand), + "sendbucks": lib.HandleCommand("sendbucks", 1800*time.Second, command.BalanceSendCommand), } )