Create IDiscordContext abstraction to simplify code

This commit is contained in:
Joseph Ferano 2022-02-13 00:49:18 +07:00
parent d563303f90
commit dd33492418
7 changed files with 187 additions and 225 deletions

View File

@ -1,7 +1,7 @@
module Degenz.Embeds
open System
open DSharpPlus.EventArgs
open Degenz.Messaging
open Degenz.Types
open DSharpPlus.Entities
@ -96,9 +96,9 @@ let responseCreatedShield (shield : BattleItem) =
.AddEmbed(embed)
.AsEphemeral(true)
let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) target prize =
let eventSuccessfulHack (ctx : IDiscordContext) target prize =
DiscordMessageBuilder()
.WithContent($"{event.User.Username} successfully hacked <@{target.DiscordId}> for a total of {prize} GoodBoyTokenz")
.WithContent($"{ctx.GetDiscordMember().Username} successfully hacked <@{target.DiscordId}> for a total of {prize} GoodBoyTokenz")
let getBuyItemsEmbed (player : PlayerData) (itemType : ItemType) (store : BattleItem array) =
let embeds , buttons =
@ -166,7 +166,7 @@ let getArsenalEmbed (player : PlayerData) =
DiscordEmbedBuilder()
.AddField( "Arsenal", Arsenal.statusFormat player ))
let getAchievementEmbed (player : PlayerData) description achievement =
let getAchievementEmbed description achievement =
let embed = DiscordEmbedBuilder()
GuildEnvironment.botUserHackerBattle

View File

@ -6,6 +6,7 @@ open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz.DbService
open Degenz.Messaging
module Game =
let HackPrize = 10<GBT>
@ -28,34 +29,17 @@ module Game =
| BattleClass.Penetration -> ( ShieldId.Cypher , HackId.RemoteAccess )
| BattleClass.Exploit -> ( ShieldId.Encryption , HackId.Worm )
let executePlayerInteraction (ctx : InteractionContext) (dispatch : PlayerData -> Async<unit>) =
let executePlayerAction (ctx : IDiscordContext) (dispatch : PlayerData -> Async<unit>) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- "Content"
do! ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder)
|> Async.AwaitTask
let! playerResult = tryFindPlayer ctx.Member.Id
do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
let! playerResult = tryFindPlayer (ctx.GetDiscordMember().Id)
match playerResult with
| Some player -> do! dispatch player
| None -> do! Messaging.sendFollowUpMessageFromCtx ctx "You are currently not a hacker, first use the /redpill command to become one"
} |> Async.StartAsTask
:> Task
// TODO J: Create an abstraction for these two helper functions
let executePlayerEvent (event : ComponentInteractionCreateEventArgs) (dispatch : PlayerData -> Async<unit>) =
async {
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 event "You are currently not a hacker, first use the /redpill command to become one"
} |> Async.StartAsTask
:> Task
| None -> do! Messaging.sendSimpleResponse ctx "You are currently not a hacker, first use the /redpill command to become one"
} |> Async.StartAsTask :> Task
module Player =
let getItems itemType (player : PlayerData) = player.Arsenal |> Array.filter (fun i -> i.Type = itemType)

View File

@ -90,39 +90,37 @@ let updateCombatants attacker defender hack prize =
|> Async.Parallel
|> Async.Ignore
let successfulHack (event : ComponentInteractionCreateEventArgs) attacker defender hack =
let successfulHack (ctx : IDiscordContext) attacker defender hack =
async {
do! updateCombatants attacker defender hack Game.HackPrize
let embed = Embeds.responseSuccessfulHack true defender.DiscordId (Armory.getItem hack)
do! event.Interaction.CreateFollowupMessageAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp embed |> Async.AwaitTask
let builder = Embeds.eventSuccessfulHack event defender Game.HackPrize
let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle)
let builder = Embeds.eventSuccessfulHack ctx defender Game.HackPrize
let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelEventsHackerBattle)
do! channel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
let failedHack (event : ComponentInteractionCreateEventArgs) attacker defender hack =
let failedHack (ctx : IDiscordContext) attacker defender hack =
async {
let msg = $"Hack failed! {defender.Name} was able to mount a successful defense! You lost {Game.ShieldPrize} $GBT!"
do! sendFollowUpMessage event msg
do! sendFollowUpMessage ctx msg
do! updateCombatants attacker defender hack -Game.ShieldPrize
let builder = DiscordMessageBuilder()
builder.WithContent($"Hacking attempt failed! <@{defender.DiscordId}> defended hack from {event.User.Username} and stole {Game.ShieldPrize} $GBT from them! ") |> ignore
let channel = (event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle))
builder.WithContent($"Hacking attempt failed! <@{defender.DiscordId}> defended hack from {ctx.GetDiscordMember().Username} and stole {Game.ShieldPrize} $GBT from them! ") |> ignore
let channel = (ctx.GetGuild().GetChannel(GuildEnvironment.channelEventsHackerBattle))
do! channel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
let attack (target : DiscordUser) (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun attacker -> async {
let attack (target : DiscordUser) (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun attacker -> async {
let! defender = DbService.tryFindPlayer target.Id
match defender with
| Some defender ->
@ -135,20 +133,18 @@ let attack (target : DiscordUser) (ctx : InteractionContext) =
|> function
| Ok atkr ->
let embed = Embeds.pickHack "Attack" atkr defender false
ctx.FollowUpAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
| Error msg -> sendFollowUpMessageFromCtx ctx msg
ctx.FollowUp(embed) |> Async.AwaitTask
| Error msg -> sendFollowUpMessage ctx msg
| None ->
if target.IsBot
then do! sendFollowUpMessageFromCtx ctx $"{target.Username} is a bot, pick a real human to hack"
else do! sendFollowUpMessageFromCtx ctx "Your target is not connected to the network, they must join first by using the /redpill command"
then do! sendFollowUpMessage ctx $"{target.Username} is a bot, pick a real human to hack"
else do! sendFollowUpMessage ctx "Your target is not connected to the network, they must join first by using the /redpill command"
})
let handleAttack (event : ComponentInteractionCreateEventArgs) =
Game.executePlayerEvent event (fun attacker -> async {
let split = event.Id.Split("-")
let handleAttack (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun attacker -> async {
let split = ctx.GetInteractionId().Split("-")
let hackId = int split.[1]
let hack = enum<HackId>(hackId)
let ( resultId , targetId ) = UInt64.TryParse split.[2]
@ -165,28 +161,26 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
| Ok atkr ->
runHackerBattle defender (Armory.getItem (int hackId))
|> function
| false -> successfulHack event atkr defender hackId
| true -> failedHack event attacker defender hackId
| Error msg -> Messaging.sendFollowUpMessage event msg
| _ -> do! Messaging.sendFollowUpMessage event "Error occurred processing attack"
| false -> successfulHack ctx atkr defender hackId
| true -> failedHack ctx attacker defender hackId
| Error msg -> Messaging.sendFollowUpMessage ctx msg
| _ -> do! Messaging.sendFollowUpMessage ctx "Error occurred processing attack"
})
let defend (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let defend (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
if Player.getShields player |> Array.length > 0 then
let p = Player.removeExpiredActions false player
let embed = Embeds.pickDefense "Defend" p false
do! ctx.FollowUpAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp embed |> Async.AwaitTask
else
let msg = $"You currently do not have any Shields to protect yourself from hacks. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one."
do! Messaging.sendFollowUpMessageFromCtx ctx msg
do! Messaging.sendFollowUpMessage ctx msg
})
let handleDefense (event : ComponentInteractionCreateEventArgs) =
Game.executePlayerEvent event (fun player -> async {
let split = event.Id.Split("-")
let handleDefense (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let split = ctx.GetInteractionId().Split("-")
let shieldId = int split.[1]
let shield = Armory.getItem shieldId
@ -194,16 +188,14 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
|> checkPlayerOwnsWeapon shieldId
>>= checkPlayerHasShieldSlotsAvailable shield
>>= checkItemHasCooldown shieldId
|> handleResultWithResponseFromEvent event (fun p -> async {
|> handleResultWithResponseFromEvent ctx (fun p -> async {
let embed = Embeds.responseCreatedShield (Armory.getItem shieldId)
do! event.Interaction.CreateFollowupMessageAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp embed |> Async.AwaitTask
let defense = { ActionId = 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)
builder.WithContent($"{ctx.GetDiscordMember().Username} has protected their system!") |> ignore
let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelEventsHackerBattle)
do! channel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
@ -211,40 +203,38 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
})
let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
let eventCtx = DiscordEventContext event :> IDiscordContext
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
| id when id.StartsWith("Attack") -> handleAttack eventCtx
| id when id.StartsWith("Defend") -> handleDefense eventCtx
| id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent eventCtx |> 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
builder.Content <- $"Incorrect Action identifier {eventCtx.GetInteractionId()}"
do! eventCtx.Respond InteractionResponseType.ChannelMessageWithSource builder |> Async.AwaitTask
}
let arsenal (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let arsenal (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let updatedPlayer = Player.removeExpiredActions false player
let builder = DiscordFollowupMessageBuilder()
let embed = DiscordEmbedBuilder()
embed.AddField("Arsenal", Arsenal.statusFormat updatedPlayer) |> ignore
builder.AddEmbed(embed) |> ignore
builder.IsEphemeral <- true
do! ctx.FollowUpAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp(builder) |> Async.AwaitTask
do! DbService.updatePlayer updatedPlayer
})
type HackerGame() =
inherit ApplicationCommandModule ()
let enforceChannels (ctx : InteractionContext) (trainerFn : InteractionContext -> Task) (battleFn : InteractionContext -> Task) =
match ctx.Channel.Id with
let enforceChannels (ctx : IDiscordContext) (trainerFn : IDiscordContext -> Task) (battleFn : IDiscordContext -> Task) =
match ctx.GetChannel().Id with
| id when id = GuildEnvironment.channelTraining ->
let hasTraineeRole = Seq.exists (fun (r : DiscordRole) -> r.Id = GuildEnvironment.roleTrainee) ctx.Member.Roles
let hasTraineeRole = Seq.exists (fun (r : DiscordRole) -> r.Id = GuildEnvironment.roleTrainee) (ctx.GetDiscordMember().Roles)
if hasTraineeRole then
trainerFn ctx
else
@ -263,15 +253,15 @@ type HackerGame() =
[<SlashCommand("arsenal", "Get the Hacks and Shields you own, and which ones are active")>]
member this.Arsenal (ctx : InteractionContext) =
enforceChannels ctx (Trainer.handleArsenal) arsenal
enforceChannels (DiscordInteractionContext ctx) (Trainer.handleArsenal) arsenal
[<SlashCommand("hack", "Send a hack attack to another player")>]
member this.AttackCommand (ctx : InteractionContext, [<Option("target", "The player you want to hack")>] target : DiscordUser) =
enforceChannels ctx (Trainer.attack target) (attack target)
enforceChannels (DiscordInteractionContext ctx) (Trainer.attack target) (attack target)
[<SlashCommand("shield", "Create a passive shield that will protect you for a certain time")>]
member this.ShieldCommand (ctx : InteractionContext) =
enforceChannels ctx Trainer.defend defend
enforceChannels (DiscordInteractionContext ctx) Trainer.defend defend
// [<SlashCommand("test-autocomplete", "Create a passive defense that will last 24 hours")>]
member this.TestAutoComplete (ctx : InteractionContext) =

View File

@ -5,6 +5,7 @@ open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.SlashCommands
open Degenz.Messaging
open Degenz.Types
let slots = [| "https://i.ibb.co/pKqZdr7/cherry.png" ; "https://i.ibb.co/JnghQsL/lemon.jpg" ; "https://i.ibb.co/1JTFPSs/seven.png" |]
@ -14,7 +15,7 @@ type SlotMachine() =
[<SlashCommand("spin", "Want to try your luck?")>]
member this.Spin (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
Game.executePlayerAction (DiscordInteractionContext ctx) (fun player -> async {
let sleepTime = 1000
let random = Random(System.Guid.NewGuid().GetHashCode())
let results = [ random.Next(0, 3) ; random.Next(0, 3) ; random.Next(0, 3)]

View File

@ -6,7 +6,6 @@ open DSharpPlus
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz
open Degenz.Embeds
open Degenz.Messaging
let checkHasSufficientFunds (item : BattleItem) player =
@ -29,47 +28,41 @@ let checkHasItemsInArsenal itemType player =
then Ok player
else Error $"You currently have no {itemType}s in your arsenal to sell!"
let buy itemType (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let buy itemType (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let itemStore = Embeds.getBuyItemsEmbed player itemType Armory.battleItems
do! ctx.Interaction.CreateFollowupMessageAsync(itemStore)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp itemStore |> Async.AwaitTask
})
let sell itemType (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let sell itemType (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
match checkHasItemsInArsenal itemType player with
| Ok _ ->
let itemStore = Embeds.getSellItemsEmbed itemType player
do! ctx.FollowUpAsync(itemStore)
|> Async.AwaitTask
|> Async.Ignore
| Error e -> do! sendFollowUpMessageFromCtx ctx e
| Ok _ -> let itemStore = Embeds.getSellItemsEmbed itemType player
do! ctx.FollowUp(itemStore) |> Async.AwaitTask
| Error e -> do! sendFollowUpMessage ctx e
})
// TODO: When you buy a shield, prompt the user to activate it
let handleBuyItem (event : ComponentInteractionCreateEventArgs) itemId =
Game.executePlayerEvent event (fun player -> async {
let handleBuyItem (ctx : IDiscordContext) itemId =
Game.executePlayerAction ctx (fun player -> async {
let item = Armory.battleItems |> Array.find (fun w -> w.Id = itemId)
do! player
|> checkHasSufficientFunds item
>>= checkAlreadyOwnsItem item
|> handleResultWithResponseFromEvent event (fun player -> async {
|> handleResultWithResponseFromEvent ctx (fun player -> async {
let newBalance = player.Bank - item.Cost
let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal }
do! DbService.updatePlayer p
do! sendFollowUpMessage event $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining"
do! sendFollowUpMessage ctx $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining"
})
})
let handleSell (event : ComponentInteractionCreateEventArgs) itemId =
Game.executePlayerEvent event (fun player -> async {
let handleSell (ctx : IDiscordContext) itemId =
Game.executePlayerAction ctx (fun player -> async {
let item = Armory.getItem itemId
do! player
|> checkSoldItemAlready item
|> handleResultWithResponseFromEvent event (fun player -> async {
|> handleResultWithResponseFromEvent ctx (fun player -> async {
let updatedPlayer = {
player with
Bank = player.Bank + item.Cost
@ -80,29 +73,30 @@ let handleSell (event : ComponentInteractionCreateEventArgs) itemId =
else player.Actions
}
do! DbService.updatePlayer updatedPlayer
do! sendFollowUpMessage event $"Sold {item.Type} {item.Name} for {item.Cost}! Current Balance: {updatedPlayer.Bank}"
do! sendFollowUpMessage ctx $"Sold {item.Type} {item.Name} for {item.Cost}! Current Balance: {updatedPlayer.Bank}"
})
})
let handleStoreEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
let itemId = int <| event.Id.Split("-").[1]
match event.Id with
| id when id.StartsWith("Buy") -> handleBuyItem event itemId
| id when id.StartsWith("Sell") -> handleSell event itemId
let ctx = DiscordEventContext event :> IDiscordContext
let id = ctx.GetInteractionId()
let itemId = int <| id.Split("-").[1]
match id with
| id when id.StartsWith("Buy") -> handleBuyItem ctx itemId
| id when id.StartsWith("Sell") -> handleSell ctx itemId
| _ ->
task {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"Incorrect Action identifier {event.Id}"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
builder.Content <- $"Incorrect Action identifier {id}"
do! ctx.Respond InteractionResponseType.ChannelMessageWithSource builder |> Async.AwaitTask
}
type Store() =
inherit ApplicationCommandModule ()
let enforceChannel (ctx : InteractionContext) (storeFn : InteractionContext -> Task) =
match ctx.Channel.Id with
let enforceChannel (ctx : IDiscordContext) (storeFn : IDiscordContext -> Task) =
match ctx.GetChannel().Id with
| id when id = GuildEnvironment.channelArmory -> storeFn ctx
| _ ->
task {
@ -111,14 +105,14 @@ type Store() =
}
[<SlashCommand("buy-hack", "Purchase a hack attack you can use to earn GoodBoyTokenz")>]
member _.BuyHack (ctx : InteractionContext) = enforceChannel ctx (buy ItemType.Hack)
member _.BuyHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy ItemType.Hack)
[<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GoodBoyTokenz")>]
member this.BuyShield (ctx : InteractionContext) = enforceChannel ctx (buy ItemType.Shield)
member this.BuyShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy ItemType.Shield)
[<SlashCommand("sell-hack", "Sell a hack for GoodBoyTokenz")>]
member this.SellHack (ctx : InteractionContext) = enforceChannel ctx (sell ItemType.Hack)
member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell ItemType.Hack)
[<SlashCommand("sell-shield", "Sell a shield for GoodBoyTokenz")>]
member this.SellShield (ctx : InteractionContext) = enforceChannel ctx (sell ItemType.Shield)
member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell ItemType.Shield)

View File

@ -4,7 +4,6 @@ open System.Text
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz.Types
open Degenz.Messaging
@ -30,8 +29,8 @@ let sendInitialEmbed (client : DiscordClient) =
|> Async.Ignore
} |> Async.RunSynchronously
let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) =
Game.executePlayerEvent event (fun player -> async {
let handleTrainerStep1 (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let shieldMessage , weaponName =
if Player.getShields player |> Array.isEmpty
then $"You do not have any Shields in your arsenal, take the `{defaultShield.Name}` shield, you can use it for now.\n\n" , defaultShield.Name
@ -39,20 +38,19 @@ let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) =
let name = Player.getShields player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
$"Looks like you have `{name}` in your arsenal… 👀\n\n" , name
let membr = event.User :?> DiscordMember
let role = event.Guild.GetRole(GuildEnvironment.roleTrainee)
do! membr.GrantRoleAsync(role)
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
do! ctx.GetDiscordMember().GrantRoleAsync(role)
|> Async.AwaitTask
do! sendFollowUpMessage event
do! sendFollowUpMessage ctx
("Beautopia© is a dangerous place... quick, put up a SHIELD 🛡 before another Degen hacks you, and steals your 💰$GBT.\n\n"
+ shieldMessage
+ "To enable it, you need to run the `/shield` slash command.\n\n"
+ $"Type the `/shield` command now, then select - `{weaponName}`\n")
})
let defend (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let defend (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let playerWithShields =
match Player.getShields player with
| [||] -> { player with Arsenal = [| defaultShield |] }
@ -60,9 +58,7 @@ let defend (ctx : InteractionContext) =
let embed = Embeds.pickDefense "Trainer-2" playerWithShields true
do! ctx.FollowUpAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp(embed) |> Async.AwaitTask
})
let handleDefenseMsg shieldId hackId = {
@ -73,25 +69,25 @@ let handleDefenseMsg shieldId hackId = {
+ "Shields only protect you for a LIMITED TIME, so remember to keep them mounted at all times, or risk getting hacked!"
}
let handleDefense (event : ComponentInteractionCreateEventArgs) =
Game.executePlayerEvent event (fun player -> async {
let sendMessage' = sendFollowUpMessage event
let split = event.Id.Split("-")
let handleDefense (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let sendMessage' = sendFollowUpMessage ctx
let split = ctx.GetInteractionId().Split("-")
let shieldId = enum<ShieldId>(int split.[2])
let shield = Armory.getItem (int shieldId)
let embed = Embeds.responseCreatedShield (shield)
do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore
let embed = Embeds.responseCreatedShield shield
do! ctx.FollowUp embed |> Async.AwaitTask
do! Async.Sleep 4000
let weakHack = Game.getGoodAgainst shield.Class
do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{snd weakHack}**"
do! Async.Sleep 5000
do! sendMessage' $"❌ HACKING FAILED!\n\n{player.Name} defended hack from <@{GuildEnvironment.botIdHackerBattle}>!"
do! Async.Sleep 4000
do! sendFollowUpMessageWithButton event (handleDefenseMsg shieldId (snd weakHack))
do! sendFollowUpMessageWithButton ctx (handleDefenseMsg shieldId (snd weakHack))
})
let handleTrainerStep3 (event : ComponentInteractionCreateEventArgs) =
Game.executePlayerEvent event (fun player -> async {
let handleTrainerStep3 (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let hackMessage , weaponName =
if Player.getHacks 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" , defaultHack.Name
@ -99,7 +95,7 @@ let handleTrainerStep3 (event : ComponentInteractionCreateEventArgs) =
let name = Player.getHacks player |> Array.tryHead |> Option.defaultValue defaultHack |> fun w -> w.Name
$"Looks like you have `{name}` in your arsenal...\n\n" , name
do! sendFollowUpMessage event
do! sendFollowUpMessage ctx
("Now lets **HACK!** 💻\n\n"
+ "I want you to **HACK ME**...\n\n"
+ hackMessage
@ -107,8 +103,8 @@ let handleTrainerStep3 (event : ComponentInteractionCreateEventArgs) =
+ $"Type the `/hack` command now, then choose me - <@{GuildEnvironment.botIdHackerBattle}> as your target, and select `{weaponName}`")
})
let attack (target : DiscordUser) (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let attack (target : DiscordUser) (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let isRightTarget = target.Id = GuildEnvironment.botIdHackerBattle
match isRightTarget with
| true ->
@ -119,27 +115,23 @@ let attack (target : DiscordUser) (ctx : InteractionContext) =
let bot = { player with DiscordId = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
let embed = Embeds.pickHack "Trainer-4" playerWithAttacks bot true
do! ctx.FollowUpAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp(embed) |> Async.AwaitTask
| false ->
let builder = DiscordFollowupMessageBuilder()
builder.Content <- "You picked the wrong target, you dufus. Try again, this time pick me!"
builder.IsEphemeral <- true
do! ctx.FollowUpAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp(builder) |> Async.AwaitTask
})
let handleAttack (event : ComponentInteractionCreateEventArgs) =
Game.executePlayerEvent event (fun player -> async {
let sendMessage' = sendFollowUpMessage event
let handleAttack (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let sendMessage' = sendFollowUpMessage ctx
do! Async.Sleep 1000
let hack = Player.getHacks player |> Array.tryHead |> Option.defaultValue defaultHack
let embed = Embeds.responseSuccessfulHack false GuildEnvironment.botIdHackerBattle hack
do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore
do! ctx.FollowUp(embed) |> Async.AwaitTask
do! Async.Sleep 5000
do! sendMessage'
("🎉 **Congratulations**\n\n"
@ -201,43 +193,37 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
|> Array.append player.Arsenal
}
do! DbService.updatePlayer updatedPlayer
do! sendFollowUpMessage event (sb.ToString())
do! sendFollowUpMessage ctx (sb.ToString())
else
do! sendFollowUpMessage event ($"Your training is now complete. If you want to buy more **HACKS & SHIELDS**, go to the <#{GuildEnvironment.channelArmory}> **NOW** and type the `/buy-hack` and `/buy-shield` commands! 😱")
let role = event.Guild.GetRole(GuildEnvironment.roleTrainee)
let ``member`` = event.User :?> DiscordMember
do! ``member``.RevokeRoleAsync(role)
do! sendFollowUpMessage ctx ($"Your training is now complete. If you want to buy more **HACKS & SHIELDS**, go to the <#{GuildEnvironment.channelArmory}> **NOW** and type the `/buy-hack` and `/buy-shield` commands! 😱")
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
do! ctx.GetDiscordMember().RevokeRoleAsync(role)
|> Async.AwaitTask
})
let handleArsenal (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let handleArsenal (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let updatedPlayer = Player.removeExpiredActions false player
let embed = Embeds.getArsenalEmbed updatedPlayer
do! ctx.FollowUpAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp(embed) |> Async.AwaitTask
do! Async.Sleep 3000
let embed = Embeds.getAchievementEmbed player "You completed the Training Dojo and collected loot." trainerAchievement
do! ctx.FollowUpAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
let embed = Embeds.getAchievementEmbed "You completed the Training Dojo and collected loot." trainerAchievement
do! ctx.FollowUp(embed) |> Async.AwaitTask
do! Async.Sleep 2000
let role = ctx.Guild.GetRole(GuildEnvironment.roleTrainee)
do! ctx.Member.RevokeRoleAsync(role)
|> Async.AwaitTask
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
do! ctx.GetDiscordMember().RevokeRoleAsync(role) |> Async.AwaitTask
do! sendFollowUpMessageFromCtx ctx $"Now get out of there and go hack other Degenz in the <#{GuildEnvironment.channelBattle}> channel!"
do! sendFollowUpMessage ctx $"Now get out of there and go hack other Degenz in the <#{GuildEnvironment.channelBattle}> channel!"
})
let handleButtonEvent (event : ComponentInteractionCreateEventArgs) =
let handleButtonEvent (ctx : IDiscordContext) =
async {
let split = event.Id.Split("-")
let split = ctx.GetInteractionId().Split("-")
match int split.[1] with
| 1 -> do! handleTrainerStep1 event |> Async.AwaitTask
| 2 -> do! handleDefense event |> Async.AwaitTask
| 3 -> do! handleTrainerStep3 event |> Async.AwaitTask
| 4 -> do! handleAttack event |> Async.AwaitTask
| _ -> do! sendFollowUpMessage event "No action found"
| 1 -> do! handleTrainerStep1 ctx |> Async.AwaitTask
| 2 -> do! handleDefense ctx |> Async.AwaitTask
| 3 -> do! handleTrainerStep3 ctx |> Async.AwaitTask
| 4 -> do! handleAttack ctx |> Async.AwaitTask
| _ -> do! sendFollowUpMessage ctx "No action found"
}

View File

@ -1,6 +1,7 @@
namespace Degenz
open System
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
@ -71,7 +72,7 @@ module Types =
type Action =
{ ActionId : int
Type : ActionType
Timestamp : System.DateTime }
Timestamp : DateTime }
[<CLIMutable>]
type PlayerData =
@ -98,6 +99,48 @@ module Messaging =
Message : string
}
type DiscordContext =
| Interaction of InteractionContext
| Event of ComponentInteractionCreateEventArgs
type IDiscordContext =
abstract member Respond : InteractionResponseType -> DiscordInteractionResponseBuilder -> Task
abstract member FollowUp : DiscordFollowupMessageBuilder -> Task
abstract member GetDiscordMember : unit -> DiscordMember
abstract member GetGuild : unit -> DiscordGuild
abstract member GetInteractionId : unit -> string
abstract member GetChannel : unit -> DiscordChannel
type DiscordInteractionContext(ctx : InteractionContext) =
interface IDiscordContext with
member this.Respond responseType builder =
async {
do! ctx.Interaction.CreateResponseAsync(responseType, builder) |> Async.AwaitTask
} |> Async.StartAsTask :> Task
member this.FollowUp(builder) =
async {
do! ctx.Interaction.CreateFollowupMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore
} |> Async.StartAsTask :> Task
member this.GetDiscordMember() = ctx.Member
member this.GetGuild() = ctx.Guild
member this.GetInteractionId() = string ctx.InteractionId
member this.GetChannel() = ctx.Channel
type DiscordEventContext(ctx : ComponentInteractionCreateEventArgs) =
interface IDiscordContext with
member this.Respond responseType builder =
async {
do! ctx.Interaction.CreateResponseAsync(responseType, builder) |> Async.AwaitTask
} |> Async.StartAsTask :> Task
member this.FollowUp(builder) =
async {
do! ctx.Interaction.CreateFollowupMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore
} |> Async.StartAsTask :> Task
member this.GetDiscordMember() = ctx.User :?> DiscordMember
member this.GetGuild() = ctx.Guild
member this.GetInteractionId() = ctx.Id
member this.GetChannel() = ctx.Channel
let getTimeText isCooldown (timespan : TimeSpan) timestamp =
let span =
if isCooldown
@ -116,82 +159,46 @@ module Messaging =
let minutesRemaining = if remaining.Hours = 0 then remaining.Minutes + 1 else remaining.Minutes
$"{hours}{minutesRemaining}min"
let sendSimpleResponse (ctx: InteractionContext) msg =
let sendSimpleResponse (ctx: IDiscordContext) msg =
async {
let builder = DiscordInteractionResponseBuilder()
builder.Content <- msg
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
do! ctx.Respond InteractionResponseType.ChannelMessageWithSource builder |> Async.AwaitTask
}
let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
let sendFollowUpMessage (ctx : IDiscordContext) msg =
async {
let builder = DiscordFollowupMessageBuilder()
builder.IsEphemeral <- true
builder.Content <- msg
do! event.Interaction.CreateFollowupMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp(builder) |> Async.AwaitTask
}
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 =
DiscordFollowupMessageBuilder()
.AsEphemeral(true)
.WithContent(msg)
.AddEmbed(embed)
do! event.Interaction.CreateFollowupMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
let sendFollowUpMessageWithButton (event : ComponentInteractionCreateEventArgs) interactiveMessage =
let sendFollowUpMessageWithButton (ctx : IDiscordContext) interactiveMessage =
async {
let builder = DiscordFollowupMessageBuilder()
let button = DiscordButtonComponent(ButtonStyle.Success, interactiveMessage.ButtonId, interactiveMessage.ButtonText) :> DiscordComponent
builder.AddComponents [| button |] |> ignore
builder.IsEphemeral <- true
builder.Content <- interactiveMessage.Message
do! event.Interaction.CreateFollowupMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
do! ctx.FollowUp(builder) |> Async.AwaitTask
}
let updateMessageWithGreyedOutButtons (event : ComponentInteractionCreateEventArgs) interactiveMessage =
let updateMessageWithGreyedOutButtons (ctx : IDiscordContext) interactiveMessage =
async {
let builder = DiscordInteractionResponseBuilder()
let button = DiscordButtonComponent(ButtonStyle.Success, interactiveMessage.ButtonId, interactiveMessage.ButtonText, true) :> DiscordComponent
builder.AddComponents [| button |] |> ignore
builder.IsEphemeral <- true
builder.Content <- interactiveMessage.Message
do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
|> Async.AwaitTask
}
let sendInteractionEvent (event : ComponentInteractionCreateEventArgs) msg =
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- msg
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
do! ctx.Respond InteractionResponseType.UpdateMessage builder |> Async.AwaitTask
}
let handleResultWithResponse ctx fn (player : Result<PlayerData, string>) =
match player with
| Ok p -> fn p
| Error e -> async { do! sendFollowUpMessageFromCtx ctx e }
| Error e -> async { do! sendFollowUpMessage ctx e }
let handleResultWithResponseFromEvent event fn (player : Result<PlayerData, string>) =
match player with