From cd15a08e371e782f17cadc4447775ce360672a5c Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Thu, 3 Feb 2022 02:59:11 +0700 Subject: [PATCH] Make most commands deferred/follow up so they don't fail. bug fixes --- Bot/Embeds.fs | 55 +++++++-------- Bot/Game.fs | 20 +++++- Bot/HackerBattle.fs | 160 +++++++++++++++++++------------------------- Bot/Items.json | 6 +- Bot/Store.fs | 27 ++++---- Bot/Trainer.fs | 42 ++++++------ Shared/Shared.fs | 17 +++-- 7 files changed, 162 insertions(+), 165 deletions(-) diff --git a/Bot/Embeds.fs b/Bot/Embeds.fs index 9acf8b1..87be0bd 100644 --- a/Bot/Embeds.fs +++ b/Bot/Embeds.fs @@ -1,23 +1,24 @@ module Degenz.Embeds +open System open DSharpPlus.EventArgs open Degenz.Types open DSharpPlus.Entities open AsciiTableFormatter -let hackGif = "https://s10.gifyu.com/images/Hacker-Degenz-V2.gif" -let shieldGif = "https://s10.gifyu.com/images/Defense-Degenz-V2.gif" +let hackGif = "https://s10.gifyu.com/images/Hacker-Degenz-V20ce8eb832734aa62-min.gif" +let shieldGif = "https://s10.gifyu.com/images/Defense-Degenz-V2-min.gif" let getHackGif = function - | HackId.Virus -> "https://s10.gifyu.com/images/Attack-DegenZ.gif" - | HackId.RemoteAccess -> "https://s10.gifyu.com/images/Mind-Control-Degenz-V2.gif" - | HackId.Worm -> "https://s10.gifyu.com/images/WormBugAttack_Degenz.gif" + | HackId.Virus -> "https://s10.gifyu.com/images/Attack-DegenZ-1.gif" + | HackId.RemoteAccess -> "https://s10.gifyu.com/images/Mind-Control-Degenz-V2-min.gif" + | HackId.Worm -> "https://s10.gifyu.com/images/WormBugAttack_Degenz-min.gif" | _ -> hackGif let getShieldGif = function - | ShieldId.Firewall -> "https://s10.gifyu.com/images/Defense-GIF-1-Degenz.gif" - | ShieldId.Encryption -> "https://s10.gifyu.com/images/Encryption-Degenz-V2.gif" - | ShieldId.Cypher -> "https://s10.gifyu.com/images/Matrix_Degenz.gif" + | ShieldId.Firewall -> "https://s10.gifyu.com/images/Defense-GIF-1-Degenz-min.gif" + | ShieldId.Encryption -> "https://s10.gifyu.com/images/Encryption-Degenz-V2-1-min.gif" + | ShieldId.Cypher -> "https://s10.gifyu.com/images/Cypher-Smaller.gif" | _ -> shieldGif @@ -40,10 +41,10 @@ let pickDefense actionId player = let embed = DiscordEmbedBuilder() .WithColor(DiscordColor.Blurple) - .WithDescription("Pick a defense to mount for 10 hours") + .WithDescription("Pick a shield to protect yourself from hacks") .WithImageUrl(shieldGif) - DiscordInteractionResponseBuilder() + DiscordFollowupMessageBuilder() .AddComponents(buttons) .AddEmbed(embed) .AsEphemeral true @@ -59,21 +60,12 @@ let pickHack actionId attacker defender = .WithDescription("Pick the hack that you want to use") .WithImageUrl(hackGif) - DiscordInteractionResponseBuilder() + DiscordFollowupMessageBuilder() .AddComponents(buttons) .AddEmbed(embed.Build()) .AsEphemeral true -let responseSuccessfulHack defenderName hack prize = - let embed = DiscordEmbedBuilder() - embed.ImageUrl <- getHackGif hack - - DiscordInteractionResponseBuilder() - .WithContent($"Successfully hacked {defenderName} using {hack}! You just won {prize} GoodBoyTokenz!") - .AddEmbed(embed.Build()) - .AsEphemeral(true) - -let responseSuccessfulHackTrainer (hack : BattleItem) = +let responseSuccessfulHack (hack : BattleItem) = let embed = DiscordEmbedBuilder() embed.ImageUrl <- getHackGif (enum(hack.Id)) @@ -82,11 +74,11 @@ let responseSuccessfulHackTrainer (hack : BattleItem) = .AddEmbed(embed.Build()) .AsEphemeral(true) -let responseCreatedShield shield = - DiscordInteractionResponseBuilder() - .AddEmbed(DiscordEmbedBuilder().WithImageUrl(getShieldGif shield)) +let responseCreatedShield (shield : BattleItem) = + DiscordFollowupMessageBuilder() + .AddEmbed(DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum(shield.Id)))) .AsEphemeral(true) - .WithContent($"Mounted a {shield} defense for 6 hours") + .WithContent($"Mounted {shield.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours") let responseCreatedShieldTrainer (shield : BattleItem) = DiscordFollowupMessageBuilder() @@ -120,11 +112,14 @@ let storeListing store = store |> Array.groupBy (fun (bi : BattleItem) -> bi.Type) |> Array.map (fun ( itemType , items ) -> - items - |> Array.map (fun item -> { Name = item.Name ; Cost = string item.Cost ; Class = string item.Class }) - |> Formatter.Format - |> sprintf "**%As**\n``` %s ```" itemType - |> constructEmbed) + let msg = + items + |> Array.map (fun item -> { Name = item.Name ; Cost = string item.Cost ; Class = string item.Class }) + |> Formatter.Format + |> sprintf "**%As**\n``` %s ```" itemType + DiscordEmbedBuilder() + .WithDescription(msg) + .Build()) DiscordInteractionResponseBuilder() .AddEmbeds(embeds) diff --git a/Bot/Game.fs b/Bot/Game.fs index 42f142d..c4889b4 100644 --- a/Bot/Game.fs +++ b/Bot/Game.fs @@ -1,15 +1,24 @@ module Degenz.Game +open System open System.Threading.Tasks +open DSharpPlus +open DSharpPlus.Entities open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Degenz.DbService +open Microsoft.VisualBasic let HackPrize = 10 let ShieldPrize = 5 let executePlayerInteraction (ctx : InteractionContext) (dispatch : PlayerData -> Async) = async { + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- "Content" + do! ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder) + |> Async.AwaitTask let! playerResult = tryFindPlayer ctx.Member.Id match playerResult with | Some player -> do! dispatch player @@ -18,12 +27,17 @@ let executePlayerInteraction (ctx : InteractionContext) (dispatch : PlayerData - :> Task // TODO: Create an abstraction for these two helper functions -let executePlayerEvent (ctx : ComponentInteractionCreateEventArgs) (dispatch : PlayerData -> Async) = +let executePlayerEvent (event : ComponentInteractionCreateEventArgs) (dispatch : PlayerData -> Async) = async { - let! playerResult = tryFindPlayer ctx.User.Id + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- "Content" + do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder) + |> Async.AwaitTask + let! playerResult = tryFindPlayer event.User.Id match playerResult with | Some player -> do! dispatch player - | None -> do! Messaging.sendInteractionEvent ctx "You are currently not a hacker, first use the /redpill command to become one" + | None -> do! Messaging.sendInteractionEvent event "You are currently not a hacker, first use the /redpill command to become one" } |> Async.StartAsTask :> Task diff --git a/Bot/HackerBattle.fs b/Bot/HackerBattle.fs index 970377d..5f1699b 100644 --- a/Bot/HackerBattle.fs +++ b/Bot/HackerBattle.fs @@ -79,9 +79,10 @@ let successfulHack (event : ComponentInteractionCreateEventArgs) attacker defend async { do! updateCombatants attacker defender hack Game.HackPrize - let embed = Embeds.responseSuccessfulHack defender.Name hack Game.HackPrize - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) + let embed = Embeds.responseSuccessfulHack (Armory.getItem (int hack)) + do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask + |> Async.Ignore let builder = Embeds.eventSuccessfulHack event defender.DiscordId Game.HackPrize let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle) @@ -122,28 +123,29 @@ let attack (ctx : InteractionContext) (target : DiscordUser) = |> function | Ok _ -> let embed = Embeds.pickHack "Attack" attacker defender - ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) + ctx.FollowUpAsync(embed) |> Async.AwaitTask + |> Async.Ignore | Error msg -> let builder = - DiscordInteractionResponseBuilder() + DiscordFollowupMessageBuilder() .WithContent(msg) .AsEphemeral(true) - ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + ctx.FollowUpAsync(builder) |> Async.AwaitTask + |> Async.Ignore | None -> do! sendSimpleResponse ctx "Your target is not connected to the network, they must join first by using the /redpill command" }) let handleAttack (event : ComponentInteractionCreateEventArgs) = - async { + Game.executePlayerEvent event (fun attacker -> async { let split = event.Id.Split("-") let hack = enum(int split.[1]) let ( resultId , targetId ) = UInt64.TryParse split.[2] - let! resultPlayer = DbService.tryFindPlayer event.User.Id let! resultTarget = DbService.tryFindPlayer targetId - match resultPlayer , resultTarget , true , resultId with - | Some attacker , Some defender , true , true -> + match resultTarget , true , resultId with + | Some defender , true , true -> do! attacker |> Player.removeExpiredActions |> checkForExistingTarget defender.DiscordId @@ -154,103 +156,78 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) = |> function | false -> successfulHack event attacker defender hack | true -> failedHack event attacker defender hack - | Error msg -> - let builder = - DiscordInteractionResponseBuilder() - .WithContent(msg) - .AsEphemeral(true) - event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - | _ -> - let builder = DiscordInteractionResponseBuilder() - builder.IsEphemeral <- true - builder.Content <- "Error occurred processing attack" - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - } + | Error msg -> Messaging.sendFollowUpMessage event msg + | _ -> do! Messaging.sendFollowUpMessage event "Error occurred processing attack" + }) let defend (ctx : InteractionContext) = Game.executePlayerInteraction ctx (fun player -> async { if Player.shields player |> Array.length > 0 then let embed = Embeds.pickDefense "Defend" player - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) + do! ctx.FollowUpAsync(embed) |> Async.AwaitTask + |> Async.Ignore else - let builder = DiscordInteractionResponseBuilder() - builder.Content <- $"You currently do not have any Shields to protect you. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one." - builder.AsEphemeral true |> ignore - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask + let msg = $"You currently do not have any Shields to protect you. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one." + do! Messaging.sendFollowUpMessageFromCtx ctx msg }) let handleDefense (event : ComponentInteractionCreateEventArgs) = - async { + Game.executePlayerEvent event (fun player -> async { let split = event.Id.Split("-") - let shield = enum(int split.[1]) - let! playerResult = DbService.tryFindPlayer event.User.Id - match playerResult with - | Some player -> - let updatedDefenses = player |> Player.removeExpiredActions |> Player.defenses - let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.ActionId = int shield) + let shieldId = enum(int split.[1]) + let updatedDefenses = player |> Player.removeExpiredActions |> Player.defenses + let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.ActionId = int shieldId) - match alreadyUsedShield , updatedDefenses.Length < 2 with - | false , true -> - let embed = Embeds.responseCreatedShield shield - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) - |> Async.AwaitTask - let defense = { ActionId = int shield ; Type = Defense ; Timestamp = DateTime.UtcNow } - do! DbService.updatePlayer <| { player with Actions = Array.append [| defense |] player.Actions } - let builder = DiscordMessageBuilder() - builder.WithContent($"{event.User.Username} has protected their system!") |> ignore - let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle) - do! channel.SendMessageAsync(builder) - |> Async.AwaitTask - |> Async.Ignore - | _ , false -> - let builder = DiscordInteractionResponseBuilder() - builder.IsEphemeral <- true - let timestamp = updatedDefenses |> Array.rev |> Array.head |> fun a -> a.Timestamp // This should be the next expiring timestamp - let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(6)) timestamp - builder.Content <- $"You are only allowed two shields at a time. Wait {cooldown} minutes to add another shield" - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - do! DbService.updatePlayer <| { player with Actions = updatedDefenses } - | true , _ -> - let builder = DiscordInteractionResponseBuilder() - builder.IsEphemeral <- true - let timestamp = updatedDefenses |> Array.find (fun d -> d.ActionId = int shield) |> fun a -> a.Timestamp - let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(6)) timestamp - builder.Content <- $"{shield} shield is already in use. Wait {cooldown} minutes to use this shield again" - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - do! DbService.updatePlayer <| { player with Actions = updatedDefenses } - - | _ -> - let builder = DiscordInteractionResponseBuilder() + match alreadyUsedShield , updatedDefenses.Length < 2 with + | false , true -> + let embed = Embeds.responseCreatedShield (Armory.getItem (int shieldId)) + do! event.Interaction.CreateFollowupMessageAsync(embed) + |> Async.AwaitTask + |> Async.Ignore + let defense = { ActionId = int shieldId ; Type = Defense ; Timestamp = DateTime.UtcNow } + do! DbService.updatePlayer <| { player with Actions = Array.append [| defense |] player.Actions } + let builder = DiscordMessageBuilder() + builder.WithContent($"{event.User.Username} has protected their system!") |> ignore + let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle) + do! channel.SendMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + | _ , false -> + let builder = DiscordFollowupMessageBuilder() builder.IsEphemeral <- true - builder.Content <- "Error parsing Button Id" - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - } + let timestamp = updatedDefenses |> Array.rev |> Array.head |> fun a -> a.Timestamp // This should be the next expiring timestamp + let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(6)) timestamp + builder.Content <- $"You are only allowed two shields at a time. Wait {cooldown} minutes to add another shield" + do! event.Interaction.CreateFollowupMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + do! DbService.updatePlayer <| { player with Actions = updatedDefenses } + | true , _ -> + let builder = DiscordFollowupMessageBuilder() + builder.IsEphemeral <- true + let timestamp = updatedDefenses |> Array.find (fun d -> d.ActionId = int shieldId) |> fun a -> a.Timestamp + let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(6)) timestamp + builder.Content <- $"{shieldId} shield is already in use. Wait {cooldown} minutes to use this shield again" + do! event.Interaction.CreateFollowupMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + do! DbService.updatePlayer <| { player with Actions = updatedDefenses } + }) let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) = - let task = - match event.Id with - | id when id.StartsWith("Attack") -> handleAttack event - | id when id.StartsWith("Defend") -> handleDefense event - | id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent event - | _ -> - async { - let builder = DiscordInteractionResponseBuilder() - builder.IsEphemeral <- true - builder.Content <- $"Incorrect Action identifier {event.Id}" - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - } - async { - return! task - } |> Async.StartAsTask - :> Task + match event.Id with + | id when id.StartsWith("Attack") -> handleAttack event + | id when id.StartsWith("Defend") -> handleDefense event + | id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent event |> Async.StartAsTask :> Task + | _ -> + task { + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- $"Incorrect Action identifier {event.Id}" + do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + } type HackerGame() = inherit ApplicationCommandModule () @@ -273,7 +250,6 @@ type HackerGame() = member this.TestAutoComplete (ctx : InteractionContext) = async { let builder = DiscordInteractionResponseBuilder() -// builder.WithContent("Not working") builder.IsEphemeral <- true do! ctx.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask diff --git a/Bot/Items.json b/Bot/Items.json index 21dd48a..c5ed359 100644 --- a/Bot/Items.json +++ b/Bot/Items.json @@ -49,7 +49,7 @@ }, "Cost": 100, "Power": 50, - "Cooldown": 240 + "Cooldown": 600 }, { "Id": 7, @@ -62,7 +62,7 @@ }, "Cost": 100, "Power": 50, - "Cooldown": 240 + "Cooldown": 600 }, { "Id": 8, @@ -75,6 +75,6 @@ }, "Cost": 100, "Power": 50, - "Cooldown": 240 + "Cooldown": 600 } ] diff --git a/Bot/Store.fs b/Bot/Store.fs index e1c8bd0..3546b09 100644 --- a/Bot/Store.fs +++ b/Bot/Store.fs @@ -25,11 +25,11 @@ let buyItem (ctx : InteractionContext) itemId = if not playerHasItem then let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal } do! DbService.updatePlayer p - do! sendSimpleResponse ctx $"Successfully purchased {item.Name}! You now have {newBalance} remaining" + do! sendFollowUpMessageFromCtx ctx $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining" else - do! sendSimpleResponse ctx $"You already own this item!" + do! sendFollowUpMessageFromCtx ctx $"You already own this item!" else - do! sendSimpleResponse ctx $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" + do! sendFollowUpMessageFromCtx ctx $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" }) @@ -37,7 +37,7 @@ let sell (ctx : InteractionContext) = Game.executePlayerInteraction ctx (fun player -> async { let hasInventoryToSell = Array.length player.Arsenal > 0 if hasInventoryToSell then - let builder = DiscordInteractionResponseBuilder() + let builder = DiscordFollowupMessageBuilder() builder.AddEmbed (constructEmbed "Pick the item you wish to sell.") |> ignore Array.chunkBySize 5 player.Arsenal @@ -50,8 +50,9 @@ let sell (ctx : InteractionContext) = |> ignore) builder.AsEphemeral true |> ignore - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + do! ctx.FollowUpAsync(builder) |> Async.AwaitTask + |> Async.Ignore else do! sendSimpleResponse ctx "You currently have no inventory to sell" }) @@ -65,11 +66,11 @@ let handleBuyItem (ctx : InteractionContext) itemId = if not playerHasItem then let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal } do! DbService.updatePlayer p - do! sendSimpleResponse ctx $"Successfully purchased {item.Name}! You now have {newBalance} remaining" + do! sendFollowUpMessageFromCtx ctx $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining" else - do! sendSimpleResponse ctx $"You already own this item!" + do! sendFollowUpMessageFromCtx ctx $"You already own this item!" else - do! sendSimpleResponse ctx $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" + do! sendFollowUpMessageFromCtx ctx $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" }) let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) = @@ -78,23 +79,25 @@ let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCrea let item = Armory.getItem itemId let updatedPlayer = { player with Bank = player.Bank + item.Cost ; Arsenal = player.Arsenal |> Array.filter (fun w -> w.Id <> itemId)} do! DbService.updatePlayer updatedPlayer - let builder = DiscordInteractionResponseBuilder() + let builder = DiscordFollowupMessageBuilder() builder.IsEphemeral <- true builder.Content <- $"Sold {item.Type} {item.Name} for {item.Cost}! Current Balance: {updatedPlayer.Bank}" - do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) + do! event.Interaction.CreateFollowupMessageAsync(builder) |> Async.AwaitTask + |> Async.Ignore }) let status (ctx : InteractionContext) = Game.executePlayerInteraction ctx (fun player -> async { let updatedPlayer = Player.removeExpiredActions player - let builder = DiscordInteractionResponseBuilder() + let builder = DiscordFollowupMessageBuilder() let embed = DiscordEmbedBuilder() embed.AddField("Arsenal", Messaging.statusFormat updatedPlayer) |> ignore builder.AddEmbed(embed) |> ignore builder.IsEphemeral <- true - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + do! ctx.FollowUpAsync(builder) |> Async.AwaitTask + |> Async.Ignore do! DbService.updatePlayer updatedPlayer }) diff --git a/Bot/Trainer.fs b/Bot/Trainer.fs index 6e6fb01..0135ac8 100644 --- a/Bot/Trainer.fs +++ b/Bot/Trainer.fs @@ -30,14 +30,15 @@ let sendInitialEmbed (client : DiscordClient) = let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) = Game.executePlayerEvent event (fun player -> async { - let weaponName = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name - let shieldMessage = + let shieldMessage , weaponName = if Player.shields player |> Array.isEmpty - then $"You do not have any Shields in your arsenal, take this {defaultShield.Name}, you can use it for now.\n\n" - else $"Looks like you have `{weaponName}` in your arsenal… 👀\n\n" + then $"You do not have any Shields in your arsenal, take this {defaultShield.Name}, you can use it for now.\n\n" , defaultShield.Name + else + let name = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name + $"Looks like you have `{name}` in your arsenal… 👀\n\n" , name - do! sendInteractionEvent event + do! sendFollowUpMessage event ("Beautopia© is a dangerous place...\n" + "Quick, put up a SHIELD 🛡 before another Degen hacks you, and steals your 💰$GBT.\n" + shieldMessage @@ -51,10 +52,12 @@ let defend (ctx : InteractionContext) = match player.Arsenal with | [||] -> { player with Arsenal = [| defaultShield |] } | _ -> player + let embed = Embeds.pickDefense "Trainer-2" playerWithShields - do! ctx.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) + do! ctx.FollowUpAsync(embed) |> Async.AwaitTask + |> Async.Ignore }) let handleDefenseMsg = { @@ -69,8 +72,6 @@ let handleDefenseMsg = { let handleDefense (event : ComponentInteractionCreateEventArgs) = Game.executePlayerEvent event (fun player -> async { let sendMessage' = sendFollowUpMessage event - do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate) - |> Async.AwaitTask let shield = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield let embed = Embeds.responseCreatedShieldTrainer shield do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore @@ -84,20 +85,19 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) = let handleTrainerStep3 (event : ComponentInteractionCreateEventArgs) = Game.executePlayerEvent event (fun player -> async { - let weaponName = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name - do! updateMessageWithGreyedOutButtons event handleDefenseMsg - - let hackMessage = + let hackMessage , weaponName = if Player.shields player |> Array.isEmpty - then $"You do not have any Hacks in your arsenal, take this {defaultHack.Name}, you can use it for now.\n\n" - else $"Looks like you have `{weaponName}` in your arsenal...\n\n" + then $"You do not have any Hacks in your arsenal, take this `{defaultHack.Name}`, you can use it for now.\n\n" , defaultHack.Name + else + let name = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultHack |> fun w -> w.Name + $"Looks like you have `{name}` in your arsenal...\n\n" , name do! sendFollowUpMessage event ("Now let’s **HACK!** 💻\n\n" + "I want you to **HACK ME**, and try to steal my 💰$GBT...\n\n" + hackMessage + "To deploy it, you need to run the `/hack` slash command.\n" - + $"Type the `/hack` command now, then choose me - <@{GuildEnvironment.botHackerBattle}> as your target, and select - `{weaponName}`") + + $"Type the `/hack` command now, then choose me - <@{GuildEnvironment.botHackerBattle}> as your target, and select `{weaponName}`") }) let attack (ctx : InteractionContext) (target : DiscordUser) = @@ -111,26 +111,26 @@ let attack (ctx : InteractionContext) (target : DiscordUser) = | _ -> player let embed = Embeds.pickHack "Trainer-4" playerWithAttacks player - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) + do! ctx.FollowUpAsync(embed) |> Async.AwaitTask + |> Async.Ignore | false -> - let builder = DiscordInteractionResponseBuilder() + let builder = DiscordFollowupMessageBuilder() builder.Content <- "You picked the wrong target, you dufus. Try again, this time pick me!" builder.IsEphemeral <- true - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + do! ctx.FollowUpAsync(builder) |> Async.AwaitTask + |> Async.Ignore }) let handleAttack (event : ComponentInteractionCreateEventArgs) = Game.executePlayerEvent event (fun player -> async { let sendMessage' = sendFollowUpMessage event - do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate) - |> Async.AwaitTask do! Async.Sleep 1000 let hack = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultHack - let embed = Embeds.responseSuccessfulHackTrainer hack + let embed = Embeds.responseSuccessfulHack hack do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore do! Async.Sleep 3000 do! sendMessage' diff --git a/Shared/Shared.fs b/Shared/Shared.fs index cf61b8e..bfd3d8a 100644 --- a/Shared/Shared.fs +++ b/Shared/Shared.fs @@ -137,17 +137,17 @@ module Messaging = actions |> Array.map (fun act -> match act.Type with - | Attack atk -> $"Hacked {atk.Target} at {act.Timestamp.ToLongDateString()}" + | Attack atk -> $"Hacked {atk.Target.Name} at {act.Timestamp.ToShortTimeString()}" | Defense -> let item = Armory.getItem act.ActionId - let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(int item.Cooldown)) + let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(int item.Cooldown)) act.Timestamp $"{item.Name} active for {cooldown}") |> String.concat "\n" let statusFormat p = $"**Hacks:** {Player.hacks p |> battleItemFormat}\n **Shields:** {Player.shields p |> battleItemFormat}\n -**Hack Attacks:** {Player.attacks p |> actionFormat}\n +**Hack Attacks:**\n{Player.attacks p |> actionFormat}\n **Active Shields:** {Player.defenses p |> actionFormat}" let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleItem array) = @@ -173,6 +173,16 @@ module Messaging = |> Async.Ignore } + let sendFollowUpMessageFromCtx (ctx : InteractionContext) msg = + async { + let builder = DiscordFollowupMessageBuilder() + builder.IsEphemeral <- true + builder.Content <- msg + do! ctx.FollowUpAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + } + let sendFollowUpMessageWithEmbed (event : ComponentInteractionCreateEventArgs) (embed : DiscordEmbed) msg = async { let builder = @@ -206,7 +216,6 @@ module Messaging = builder.Content <- interactiveMessage.Message do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask - |> Async.Ignore } let sendInteractionEvent (event : ComponentInteractionCreateEventArgs) msg =