diff --git a/Bot/Bot.fs b/Bot/Bot.fs index a6997f3..f5dad12 100644 --- a/Bot/Bot.fs +++ b/Bot/Bot.fs @@ -13,6 +13,7 @@ let guild = GuildEnvironment.guildId let hackerBattleConfig = DiscordConfiguration() let storeConfig = DiscordConfiguration() +let jpegConfig = DiscordConfiguration() //let stealConfig = DiscordConfiguration() let inviterConfig = DiscordConfiguration() let slotsConfig = DiscordConfiguration() @@ -26,6 +27,9 @@ hackerBattleConfig.Intents <- DiscordIntents.All storeConfig.TokenType <- TokenType.Bot storeConfig.Intents <- DiscordIntents.All +jpegConfig.TokenType <- TokenType.Bot +jpegConfig.Intents <- DiscordIntents.All + //stealConfig.TokenType <- TokenType.Bot //stealConfig.Intents <- DiscordIntents.All @@ -40,6 +44,7 @@ adminConfig.Intents <- DiscordIntents.All hackerBattleConfig.Token <- GuildEnvironment.tokenHackerBattle storeConfig.Token <- GuildEnvironment.tokenStore +jpegConfig.Token <- GuildEnvironment.tokenJpeg //stealConfig.Token <- GuildEnvironment.tokenSteal inviterConfig.Token <- GuildEnvironment.tokenInviter slotsConfig.Token <- GuildEnvironment.tokenSlots @@ -47,20 +52,21 @@ adminConfig.Token <- GuildEnvironment.tokenAdmin let hackerBattleBot = new DiscordClient(hackerBattleConfig) let storeBot = new DiscordClient(storeConfig) +let jpegBot = new DiscordClient(jpegConfig) //let stealBot = new DiscordClient(stealConfig) let inviterBot = new DiscordClient(inviterConfig) let slotsBot = new DiscordClient(slotsConfig) let adminBot = new DiscordClient(adminConfig) let hackerCommands = hackerBattleBot.UseSlashCommands() -let storeCommands = storeBot.UseSlashCommands() +let jpegCommands = jpegBot.UseSlashCommands() //let stealCommands = stealBot.UseSlashCommands() let inviterCommands = inviterBot.UseSlashCommands() let slotsCommands = slotsBot.UseSlashCommands() let adminCommands = adminBot.UseSlashCommands() hackerCommands.RegisterCommands(guild); -storeCommands.RegisterCommands(guild); +jpegCommands.RegisterCommands(guild); //stealCommands.RegisterCommands(guild); inviterCommands.RegisterCommands(guild); //slotsCommands.RegisterCommands(guild); @@ -69,6 +75,7 @@ adminCommands.RegisterCommands(guild) hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent)) hackerBattleBot.add_MessageCreated(AsyncEventHandler(HackerBattle.handleMessageCreated)) storeBot.add_ComponentInteractionCreated(AsyncEventHandler(Store.handleStoreEvents)) +jpegBot.add_ComponentInteractionCreated(AsyncEventHandler(Store.handleJpegEvents)) //stealBot.add_ComponentInteractionCreated(AsyncEventHandler(Thief.handleStealButton)) inviterBot.add_GuildMemberAdded(AsyncEventHandler(InviteTracker.handleGuildMemberAdded)) inviterBot.add_ComponentInteractionCreated(AsyncEventHandler(InviteTracker.handleButtonEvent)) @@ -96,6 +103,9 @@ let asdf _ (event : DSharpPlus.EventArgs.InteractionCreateEventArgs) = storeBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously GuildEnvironment.botClientStore <- Some storeBot +jpegBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously +GuildEnvironment.botClientJpeg <- Some jpegBot + slotsBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously GuildEnvironment.botClientSlots <- Some slotsBot diff --git a/Bot/Games/Store.fs b/Bot/Games/Store.fs index 4b8ef74..6f8c73f 100644 --- a/Bot/Games/Store.fs +++ b/Bot/Games/Store.fs @@ -25,12 +25,14 @@ let checkHasSufficientFunds (item : Item) player = else Error $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" | _ -> Error $"{item.Name} item cannot be bought" +let getTotalOwnedOfItem (item : Item) (inventory : Item list) = + inventory + |> List.countBy (fun i -> i.Id) + |> List.tryFind (fst >> ((=) item.Id)) + |> Option.map snd + let checkDoesntExceedStackCap (item : Item) player = - let itemCount = - player.Inventory - |> List.countBy (fun i -> i.Id) - |> List.tryFind (fst >> ((=) item.Id)) - |> Option.map snd + let itemCount = getTotalOwnedOfItem item player.Inventory match item.Attributes , itemCount with | CanStack max , Some count -> if count >= max @@ -54,6 +56,7 @@ let getItemEmbeds owned (items : StoreItem list) = |> List.countBy (fun item -> item.Item.Id) |> List.map (fun (id,count) -> items |> List.find (fun i -> i.Item.Id = id) , count ) |> List.map (fun (item,count) -> + let mutable titleText = item.Item.Name let embed = DiscordEmbedBuilder() use table = new DataTable() table.SetBorder(Border.None) @@ -64,19 +67,26 @@ let getItemEmbeds owned (items : StoreItem list) = item.Item.Attributes |> List.iter (function | Buyable price -> - values.Add("Price 💰", (if price = 0 then "Free" else $"{price} $GBT")) + if not owned then + values.Add("Price", (if price = 0 then "Free" else $"{price} $GBT")) | Attackable power -> - let title = match item.Item.Type with ItemType.Hack -> "$GBT Reward" | _ -> "Power" + let title = match item.Item.Type with ItemType.Hack -> "Reward" | _ -> "Power" values.Add($"{title}", string power) | RateLimitable time -> - let title = match item.Item.Type with ItemType.Hack -> "Cooldown" | ItemType.Shield -> "Active For" | _ -> "Expires" - let ts = TimeSpan.FromMinutes(int time) - let timeStr = if ts.Hours = 0 then $"{ts.Minutes} mins" else $"{ts.Hours} hours" - values.Add($"{title}", timeStr) + match item.Item.Type with + | ItemType.Hack -> () + | ItemType.Shield -> + let ts = TimeSpan.FromMinutes(int time) + let timeStr = if ts.Hours = 0 then $"{ts.Minutes} mins" else $"{ts.Hours} hours" + values.Add($"Active", timeStr) + | _ -> () | Stackable max -> - if owned - then values.Add($"Total Owned", $"{count}") - else values.Add($"Max Allowed", $"{max}") + if owned then + let totalOwned = getTotalOwnedOfItem item.Item (items |> List.map (fun i -> i.Item)) |> Option.defaultValue 1 + titleText <- $"{totalOwned}x " + titleText +// values.Add($"Total Owned", $"{count}") +// else values.Add($"Max Allowed", $"{max}") + () | Modifiable effects -> let fx = effects @@ -93,7 +103,7 @@ let getItemEmbeds owned (items : StoreItem list) = | _ -> ()) for title , _ in values do let column = table.Columns.Add(title) - column.SetWidth(40 / values.Count) + column.SetWidth(10) column.SetDataAlignment(TextAlignment.Center) column.SetHeaderBorder(Border.Bottom) column.SetDataBorder(Border.Top) @@ -101,30 +111,34 @@ let getItemEmbeds owned (items : StoreItem list) = let arr : obj array = values |> Seq.map snd |> Seq.cast |> Seq.toArray table.Rows.Add(arr) |> ignore - let split = table.ToPrettyPrintedString().Split("\n") - let text = split |> Array.skip 1 |> Array.take (split.Length - 3) |> String.concat "\n" embed .WithColor(WeaponClass.getClassEmbedColor item.Item) - .WithDescription($"```{text}```") - .WithTitle($"{item.Item.Name}") + .WithTitle(titleText) |> ignore + if table.Columns.Count > 0 then + let split = table.ToPrettyPrintedString().Split("\n") + let text = split |> Array.skip 1 |> Array.take (split.Length - 3) |> String.concat "\n" + embed.WithDescription($"```{text}```") |> ignore if String.IsNullOrWhiteSpace(item.Item.IconUrl) then embed else embed.WithThumbnail(item.Item.IconUrl)) |> List.map (fun e -> e.Build()) |> Seq.ofList -let getBuyItemsEmbed storeId (playerInventory : Inventory) (storeInventory : StoreItem list) = +let getBuyItemsEmbed storeId player (storeInventory : StoreItem list) = let embeds = getItemEmbeds false storeInventory let buttons = storeInventory |> List.map (fun item -> - let owned = playerInventory |> List.exists (fun i -> i.Id = item.Item.Id) + let owned = player.Inventory |> List.exists (fun i -> i.Id = item.Item.Id) let inStock = item.Available && (item.Stock > 0 || item.LimitStock = false) match owned , inStock with | false , true -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Buy {item.Item.Name}") | false , false -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"{item.Item.Name} (Out of Stock)", true) - | true , _ -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Own {item.Item.Name}", true) + | true , _ -> + match checkDoesntExceedStackCap item.Item player with + | Ok _ -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Buy {item.Item.Name}") + | Error _ -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Own {item.Item.Name}", true) :> DiscordComponent) let builder = @@ -142,11 +156,8 @@ let purchaseItemEmbed (item : Item) = embed.Title <- $"Purchased {item.Name}" match item.Type with | ItemType.Jpeg -> - if item.Id.Contains "RAFFLE" then - embed.Description <- $"Congratulations! You are in the draw for the {item.Name}. The winner will be announced shortly" - embed.ImageUrl <- item.Description - else - embed.Description <- $"Congratulations! You own the rights to the {item.Name} NFT. Please create a ticket in the support channel and we will transfer to your wallet" + embed.Description <- $"Congratulations! You are in the draw for the {item.Name}. The winner will be announced soon in the <#{GuildEnvironment.channelGiveaway}>" + embed.ImageUrl <- item.Description | _ -> embed.Description <- $"Purchased {item.Name}" embed @@ -178,9 +189,12 @@ let showJpegsEmbed (ctx : IDiscordContext) = PlayerInteractions.executePlayerAct player.Inventory |> Inventory.getItemsByType ItemType.Jpeg |> List.map (fun i -> { StoreId = "BACKALLEY" ; Item = i ; Stock = 1 ; LimitStock = false ; Available = true }) - let embeds = getItemEmbeds true jpegs - let builder = DiscordFollowupMessageBuilder().AddEmbeds(embeds).AsEphemeral(true) - do! ctx.FollowUp builder |> Async.AwaitTask + match jpegs with + | [] -> do! Messaging.sendFollowUpMessage ctx $"You currently do not own any jpegs or raffle tickets. Go to <#{GuildEnvironment.channelBackAlley}> to buy some" + | jpegs -> + let embeds = getItemEmbeds true jpegs + let builder = DiscordFollowupMessageBuilder().AddEmbeds(embeds).AsEphemeral(true) + do! ctx.FollowUp builder |> Async.AwaitTask }) let buy storeId (filterBy : ItemType option) (ctx : IDiscordContext) = @@ -192,7 +206,7 @@ let buy storeId (filterBy : ItemType option) (ctx : IDiscordContext) = match filterBy with | Some itemType -> items |> List.filter (fun item -> item.Item.Type = itemType) | None -> items - let itemStore = getBuyItemsEmbed storeId player.Inventory items' + let itemStore = getBuyItemsEmbed storeId player items' do! ctx.FollowUp itemStore |> Async.AwaitTask do! Analytics.buyItemCommand (ctx.GetDiscordMember()) storeId else @@ -299,6 +313,22 @@ let showStats (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction c do! ctx.FollowUp builder |> Async.AwaitTask }) +let handleJpegEvents _ (event : ComponentInteractionCreateEventArgs) = + let ctx = DiscordEventContext event :> IDiscordContext + let id = ctx.GetInteractionId() + let itemId = id.Split("-").[1] + let storeId = id.Split("-").[2] + match id with + | id when id.StartsWith("Buy") -> handleBuyItem ctx itemId + | id when id.StartsWith("ShowJpegInventory") -> buy storeId None ctx + | _ -> + task { + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- $"Incorrect Action identifier {id}" + do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask + } + let handleStoreEvents _ (event : ComponentInteractionCreateEventArgs) = let ctx = DiscordEventContext event :> IDiscordContext let id = ctx.GetInteractionId() @@ -326,18 +356,18 @@ let sendBackalleyEmbed (ctx : IDiscordContext) = let builder = DiscordMessageBuilder() let embed = DiscordEmbedBuilder() embed.ImageUrl <- "https://s7.gifyu.com/images/ezgif.com-gif-maker-23203b9dca779ba7cf.gif" - embed.Title <- "Jpeg Alley" + embed.Title <- "Jpeg Store" embed.Color <- DiscordColor.Black - embed.Description <- "Hey, what do you want kid? Did you come alone?" + embed.Description <- "Hey, what do you want kid?" builder.AddEmbed embed |> ignore - let button = DiscordButtonComponent(ButtonStyle.Success, $"ShowJpegInventory-0-BACKALLEY", $"Show me your stash") :> DiscordComponent + let button = DiscordButtonComponent(ButtonStyle.Success, $"ShowJpegInventory-0-BACKALLEY", $"NFT Raffles") :> DiscordComponent builder.AddComponents [| button |] |> ignore - do! GuildEnvironment.botClientStore.Value.SendMessageAsync(channel, builder) + do! GuildEnvironment.botClientJpeg.Value.SendMessageAsync(channel, builder) |> Async.AwaitTask |> Async.Ignore with e -> - printfn $"Error trying to get channel Jpeg Alley\n\n{e.Message}" + printfn $"Error trying to get channel Jpeg Store\n\n{e.Message}" } |> Async.RunSynchronously let sendArmoryEmbed (ctx : IDiscordContext) = @@ -362,56 +392,9 @@ let sendArmoryEmbed (ctx : IDiscordContext) = printfn $"Error trying to get channel Armory\n\n{e.Message}" } |> Async.RunSynchronously -type Store() = +type JpegStore() = inherit ApplicationCommandModule () - let enforceChannel (ctx : IDiscordContext) (storeFn : IDiscordContext -> Task) = - match ctx.GetChannel().Id with - | id when id = GuildEnvironment.channelArmory -> storeFn ctx - | _ -> - task { - let msg = $"You must go to <#{GuildEnvironment.channelArmory}> channel to buy or sell weapons" - do! Messaging.sendSimpleResponse ctx msg - } - -// let checkChannel (ctx : IDiscordContext) (storeFn : IDiscordContext -> Task) = -// let checkChannel (ctx : IDiscordContext) = -// match ctx.GetChannel().Id with -// | id when id = GuildEnvironment.channelBackAlley -> buy ItemType.Hack ctx -// | id when id = GuildEnvironment.channelArmory -> buy ItemType.Shield ctx -// | id when id = GuildEnvironment.channelMarket -> buy ItemType.Food ctx -// | id when id = GuildEnvironment.channelAccessoryShop -> buy ItemType.Accessory ctx -// | _ -> -// task { -// let msg = $"This channel doesn't have any items to sell. Try <#{GuildEnvironment.channelArmory}>" -// do! Messaging.sendSimpleResponse ctx msg -// } - -// [] -// member _.BuyItem (ctx : InteractionContext) = buy (DiscordInteractionContext ctx) - -// [] -// member _.BuyHack (ctx : InteractionContext) = -// enforceChannel (DiscordInteractionContext(ctx)) buy -// -// [] -// member this.BuyShield (ctx : InteractionContext) = -// enforceChannel (DiscordInteractionContext(ctx)) buy - -// [] -// member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Hacks" (Inventory.getItemsByType ItemType.Hack)) -// -// [] -// member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Shield)) -// -// [] -// member this.Consume (ctx : InteractionContext) = consume (DiscordInteractionContext ctx) -// - [] + [] member this.Inventory (ctx : InteractionContext) = - showJpegsEmbed (DiscordInteractionContext ctx) - -// [] -// member this.Stats (ctx : InteractionContext) = -// showStats (DiscordInteractionContext ctx) - + showJpegsEmbed (DiscordInteractionContext ctx) \ No newline at end of file diff --git a/Bot/GuildEnvironment.fs b/Bot/GuildEnvironment.fs index 072f4a0..0117be1 100644 --- a/Bot/GuildEnvironment.fs +++ b/Bot/GuildEnvironment.fs @@ -21,6 +21,7 @@ let tokenPlayerInteractions = getVar "TOKEN_PLAYER_INTERACTIONS" let tokenSteal = getVar "TOKEN_STEAL" let tokenHackerBattle = getVar "TOKEN_HACKER_BATTLE" let tokenStore = getVar "TOKEN_STORE" +let tokenJpeg = getVar "TOKEN_JPEG" let tokenInviter = getVar "TOKEN_INVITER" let tokenSlots = getVar "TOKEN_SLOTS" let tokenAdmin = getVar "TOKEN_ADMINBOT" @@ -38,6 +39,7 @@ let channelSlots = getId "CHANNEL_SLOTS" let channelBackAlley = getId "CHANNEL_BACKALLEY" let channelMarket = getId "CHANNEL_MARKET" let channelAccessoryShop = getId "CHANNEL_ACCESSORIES" +let channelGiveaway = getId "CHANNEL_GIVEAWAY" //let channelThievery = getId "CHANNEL_THIEVERY" let botIdHackerBattle = getId "BOT_HACKER_BATTLE" @@ -52,4 +54,5 @@ let roleAdmin = getId "ROLE_ADMIN" let mutable botClientRecruit : DiscordClient option = None let mutable botClientHacker : DiscordClient option = None let mutable botClientSlots : DiscordClient option = None +let mutable botClientJpeg : DiscordClient option = None let mutable botClientStore : DiscordClient option = None