diff --git a/Bot/Bot.fsproj b/Bot/Bot.fsproj index 16fe536..81c8a2a 100644 --- a/Bot/Bot.fsproj +++ b/Bot/Bot.fsproj @@ -4,14 +4,16 @@ Exe net6.0 Linux - PlayerRegistration + Degenz PreserveNewest + + diff --git a/Bot/Embeds.fs b/Bot/Embeds.fs new file mode 100644 index 0000000..b360615 --- /dev/null +++ b/Bot/Embeds.fs @@ -0,0 +1,85 @@ +module Degenz.Embeds + +open DSharpPlus.EventArgs +open Degenz.Shared +open DSharpPlus.Entities +open AsciiTableFormatter + +let constructEmbed message = + let builder = DiscordEmbedBuilder() + builder.Color <- Optional(DiscordColor.PhthaloGreen) + builder.Description <- message + let author = DiscordEmbedBuilder.EmbedAuthor() + author.Name <- "Degenz Hacker Game" + author.Url <- "https://twitter.com/degenzgame" + author.IconUrl <- "https://pbs.twimg.com/profile_images/1473192843359309825/cqjm0VQ4_400x400.jpg" + builder.Author <- author + builder.Build() + +let pickDefense actionId player = + let buttons = + constructButtons actionId (string player.DiscordId) player.Shields + |> Seq.cast + + let embed = + DiscordEmbedBuilder() + .WithColor(DiscordColor.PhthaloGreen) + .WithDescription("Pick a defense to mount for 10 hours") + .WithImageUrl("https://s10.gifyu.com/images/Defense-Degenz-V2.gif") + + DiscordInteractionResponseBuilder() + .AddComponents(buttons) + .AddEmbed(embed) + .AsEphemeral true + +let pickHack actionId attacker defender = + let buttons = + constructButtons actionId $"{defender.DiscordId}-{defender.Name}" attacker.Weapons + |> Seq.cast + + let embed = + DiscordEmbedBuilder() + .WithColor(DiscordColor.PhthaloGreen) + .WithDescription("Pick the hack that you want to use") + .WithImageUrl("https://s10.gifyu.com/images/Hacker-Degenz-V2.gif") + + DiscordInteractionResponseBuilder() + .AddComponents(buttons) + .AddEmbed(embed.Build()) + .AsEphemeral true + +let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) targetId prize = + DiscordMessageBuilder() + .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz") + +[] +type Table = { + Name : string + Cost : string + Class : string +} + +let storeListing store = + let embeds = + store + |> Array.map (fun ( itemType , items ) -> + items + |> Array.map (fun (item : Item) -> + let itemClass = + if itemType = ItemType.Hack + then weaponInventory + |> Array.find (fun w -> item.Name = string w) + |> int + |> getClass + else shieldInventory + |> Array.find (fun w -> item.Name = string w) + |> int + |> getClass + { Name = item.Name ; Cost = string item.Cost ; Class = string itemClass }) + |> Formatter.Format + |> sprintf "**%A**\n``` %s ```" itemType + |> constructEmbed) + + DiscordInteractionResponseBuilder() + .AddEmbeds(embeds) + .AsEphemeral(true) diff --git a/Bot/GameConfig.fs b/Bot/GameConfig.fs new file mode 100644 index 0000000..a4d0894 --- /dev/null +++ b/Bot/GameConfig.fs @@ -0,0 +1 @@ +module Degenz.GameConfig diff --git a/Bot/HackerBattle.fs b/Bot/HackerBattle.fs index a061759..3d017c4 100644 --- a/Bot/HackerBattle.fs +++ b/Bot/HackerBattle.fs @@ -11,7 +11,6 @@ open Degenz.Shared let attack (ctx : InteractionContext) (target : DiscordUser) = async { - // TODO: We need to check if the player has any active embed hacks going, if not they can cheat let! attacker = DbService.tryFindPlayer ctx.Member.Id let! defender = DbService.tryFindPlayer target.Id match attacker , defender with @@ -21,23 +20,9 @@ let attack (ctx : InteractionContext) (target : DiscordUser) = |> removeExpiredActions (TimeSpan.FromMinutes(15)) (fun (atk : Attack) -> atk.Timestamp) do! DbService.updatePlayer <| { attacker with Attacks = updatedAttacks } if updatedAttacks.Length < 2 then - let embed = DiscordEmbedBuilder() - embed.Color <- Optional(DiscordColor.PhthaloGreen) - embed.Description <- "Pick the hack that you want to use" - embed.ImageUrl <- "https://s10.gifyu.com/images/Hacker-Degenz-V2.gif" + let embed = Embeds.pickHack "Attack" attacker defender - let builder = DiscordInteractionResponseBuilder() - builder.AddEmbed (embed.Build()) |> ignore - - let defenderInfo = $"{defender.DiscordId}-{target.Username}" - constructButtons "Attack" defenderInfo attacker.Weapons - |> Seq.cast - |> builder.AddComponents - |> ignore - - builder.AsEphemeral true |> ignore - - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) |> Async.AwaitTask else @@ -64,17 +49,9 @@ let defend (ctx : InteractionContext) = let updatedDefenses = removeExpiredActions (TimeSpan.FromHours(24)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses do! DbService.updatePlayer <| { player with Defenses = updatedDefenses } if updatedDefenses.Length < 3 then - let builder = DiscordInteractionResponseBuilder() - builder.AddEmbed (constructEmbed "Pick a defense to mount for 24 hours") |> ignore + let embed = Embeds.pickDefense "Defend" player - constructButtons "Defend" (string player.DiscordId) player.Shields - |> Seq.cast - |> builder.AddComponents - |> ignore - - builder.AsEphemeral true |> ignore - - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) |> Async.AwaitTask else let builder = DiscordInteractionResponseBuilder() @@ -115,9 +92,11 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) = // let prize = 1.337f // LEET let prize = 13 let attack = { HackType = enum(int weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } } - // TODO: Make a single update instead of two - do! DbService.updatePlayer <| updatePlayer prize attack player - do! DbService.updatePlayer { target with Bank = Math.Max(target.Bank - prize, 0)} + + let! _ = + [ DbService.updatePlayer <| updatePlayer prize attack player + DbService.updatePlayer { target with Bank = Math.Max(target.Bank - prize, 0)} ] + |> Async.Parallel let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true @@ -125,9 +104,8 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) = do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask - let builder = DiscordMessageBuilder() - builder.WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz") |> ignore - let channel = (event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle)) + let builder = Embeds.eventSuccessfulHack event targetId prize + let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle) do! channel.SendMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore diff --git a/Bot/Store.fs b/Bot/Store.fs index 944c7a6..f80c099 100644 --- a/Bot/Store.fs +++ b/Bot/Store.fs @@ -6,9 +6,9 @@ open DSharpPlus open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Degenz +open Degenz.Embeds open Degenz.Shared open Newtonsoft.Json -open AsciiTableFormatter let store = try @@ -17,52 +17,9 @@ let store = |> Array.groupBy (fun (i : Item) -> i.ItemType) with _ -> [||] -//[] -//type Table() = -// member val Name = "" with get , set -// member val Cost = "" with get , set -// member val Class = "" with get , set -// - -[] -type Table = { - Name : string - Cost : string - Class : string -} - - -let storeListing = - store - |> Array.map (fun ( itemType , items ) -> - let itemsData = - items - |> Array.map (fun (item : Item) -> - let itemClass = - if itemType = ItemType.Hack - then weaponInventory - |> Array.find (fun w -> item.Name = string w) - |> int - |> getClass - else shieldInventory - |> Array.find (fun w -> item.Name = string w) - |> int - |> getClass - { Name = item.Name ; Cost = string item.Cost ; Class = string itemClass }) - - let table = Formatter.Format(ResizeArray(itemsData)) - - (constructEmbed $"**{itemType}s**\n```{table}```")) - let viewStore (ctx : InteractionContext) = async { - let builder = DiscordInteractionResponseBuilder() - - builder.AddEmbeds(storeListing) - .AsEphemeral(true) - |> ignore - - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, Embeds.storeListing store) |> Async.AwaitTask } |> Async.StartAsTask :> Task diff --git a/Bot/Trainer.fs b/Bot/Trainer.fs index ebe23aa..ce2ff83 100644 --- a/Bot/Trainer.fs +++ b/Bot/Trainer.fs @@ -7,47 +7,6 @@ open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Degenz.Shared -module Message = - let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg = - async { - let builder = DiscordFollowupMessageBuilder() - builder.IsEphemeral <- true - builder.Content <- msg - do! event.Interaction.CreateFollowupMessageAsync(builder) - |> Async.AwaitTask - |> Async.Ignore - } - - let sendFollowUpMessageWithButton (event : ComponentInteractionCreateEventArgs) buttonId msg = - async { - let builder = DiscordFollowupMessageBuilder() - let button = DiscordButtonComponent(ButtonStyle.Primary, buttonId, "Got it") :> DiscordComponent - builder.AddComponents [| button |] |> ignore - builder.IsEphemeral <- true - builder.Content <- msg - do! event.Interaction.CreateFollowupMessageAsync(builder) - |> Async.AwaitTask - |> Async.Ignore - } - - let sendInteractionEvent (event : ComponentInteractionCreateEventArgs) msg = - async { - let builder = DiscordInteractionResponseBuilder() - builder.IsEphemeral <- true - builder.Content <- msg - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask - } - - let sendInteractionEventWithButton (event : ComponentInteractionCreateEventArgs) buttonId msg = - async { - let builder = DiscordInteractionResponseBuilder() - let button = DiscordButtonComponent(ButtonStyle.Primary, buttonId, "Got it") :> DiscordComponent - builder.AddComponents [| button |] |> ignore - builder.IsEphemeral <- true - builder.Content <- msg - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask - } - let sendInitialEmbed (client : DiscordClient) = async { let! channel = client.GetChannelAsync(GuildEnvironment.channelTraining) |> Async.AwaitTask @@ -92,17 +51,9 @@ let defend (ctx : InteractionContext) = let! playerResult = DbService.tryFindPlayer ctx.Member.Id match playerResult with | Some player -> - let builder = DiscordInteractionResponseBuilder() - builder.AddEmbed (constructEmbed "Pick a defense to mount for 8 hours") |> ignore + let embed = Embeds.pickDefense "Trainer-3" player - constructButtons "Trainer-3" (string player.DiscordId) player.Shields - |> Seq.cast - |> builder.AddComponents - |> ignore - - builder.AsEphemeral true |> ignore - - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) |> Async.AwaitTask | None -> let builder = DiscordInteractionResponseBuilder() @@ -158,17 +109,9 @@ let attack (ctx : InteractionContext) (target : DiscordUser) = let! playerResult = DbService.tryFindPlayer ctx.Member.Id match isRightTarget , playerResult with | true , Some player -> - let builder = DiscordInteractionResponseBuilder() - builder.AddEmbed (constructEmbed "Pick an attack to use on your target") |> ignore + let embed = Embeds.pickHack "Trainer-5" player player - constructButtons "Trainer-5" (string player.DiscordId) player.Weapons - |> Seq.cast - |> builder.AddComponents - |> ignore - - builder.AsEphemeral true |> ignore - - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) |> Async.AwaitTask | false , _ -> let builder = DiscordInteractionResponseBuilder() diff --git a/Shared/Shared.fs b/Shared/Shared.fs index 4413a44..5861f51 100644 --- a/Shared/Shared.fs +++ b/Shared/Shared.fs @@ -3,6 +3,7 @@ module Degenz.Shared open System open DSharpPlus open DSharpPlus.Entities +open DSharpPlus.EventArgs open DSharpPlus.SlashCommands type ItemType = @@ -105,17 +106,6 @@ let constructButtons (actionType: string) (playerInfo: string) (weapons: 'a arra let removeExpiredActions timespan (timestamp: 'a -> DateTime) actions = actions |> Array.filter (fun act -> DateTime.UtcNow - (timestamp act) < timespan) -let constructEmbed message = - let builder = DiscordEmbedBuilder() - builder.Color <- Optional(DiscordColor.PhthaloGreen) - builder.Description <- message - let author = DiscordEmbedBuilder.EmbedAuthor() - author.Name <- "Degenz Hacker Game" - author.Url <- "https://twitter.com/degenzgame" - author.IconUrl <- "https://pbs.twimg.com/profile_images/1473192843359309825/cqjm0VQ4_400x400.jpg" - builder.Author <- author - builder.Build() - let calculateDamage (hack: int) (shield: int) = let hackClass = getClass hack let protectionClass = getClass shield @@ -123,3 +113,45 @@ let calculateDamage (hack: int) (shield: int) = match hackClass, protectionClass with | h, p when h = p -> Weak | _ -> Strong + +module Message = + let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg = + async { + let builder = DiscordFollowupMessageBuilder() + builder.IsEphemeral <- true + builder.Content <- msg + do! event.Interaction.CreateFollowupMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + } + + let sendFollowUpMessageWithButton (event : ComponentInteractionCreateEventArgs) buttonId msg = + async { + let builder = DiscordFollowupMessageBuilder() + let button = DiscordButtonComponent(ButtonStyle.Primary, buttonId, "Got it") :> DiscordComponent + builder.AddComponents [| button |] |> ignore + builder.IsEphemeral <- true + builder.Content <- msg + do! event.Interaction.CreateFollowupMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + } + + let sendInteractionEvent (event : ComponentInteractionCreateEventArgs) msg = + async { + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- msg + do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask + } + + let sendInteractionEventWithButton (event : ComponentInteractionCreateEventArgs) buttonId msg = + async { + let builder = DiscordInteractionResponseBuilder() + let button = DiscordButtonComponent(ButtonStyle.Primary, buttonId, "Got it") :> DiscordComponent + builder.AddComponents [| button |] |> ignore + builder.IsEphemeral <- true + builder.Content <- msg + do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask + } +