Finish refactoring DbService. Rename project to PlayerInteractions

This commit is contained in:
Joseph Ferano 2022-01-16 02:19:23 +07:00
parent 7b2f6ba861
commit 162f6af03f
13 changed files with 157 additions and 151 deletions

View File

@ -1,6 +1,6 @@
module DegenzGame.DbService module DegenzGame.DbService
open DegenzGame.Shared open System
open DegenzGame.Shared open DegenzGame.Shared
open MongoDB.Bson open MongoDB.Bson
open MongoDB.Driver open MongoDB.Driver
@ -16,7 +16,7 @@ let players = db.GetCollection<PlayerEntry>("players")
let tryFindPlayer (id : uint64) : Async<Player option> = let tryFindPlayer (id : uint64) : Async<Player option> =
async { async {
let filter = Builders<PlayerEntry>.Filter.Eq((fun p -> p.Player.DiscordId), id) let filter = Builders<PlayerEntry>.Filter.Eq((fun e -> e.Player.DiscordId), id)
let! player = players.FindAsync<PlayerEntry>(filter) |> Async.AwaitTask let! player = players.FindAsync<PlayerEntry>(filter) |> Async.AwaitTask
return match player.ToEnumerable() |> Seq.toList with return match player.ToEnumerable() |> Seq.toList with
| [] -> None | [] -> None
@ -33,7 +33,27 @@ let insertNewPlayer (player : Player) =
let removePlayer (memberId : uint64) = let removePlayer (memberId : uint64) =
async { async {
// TODO: Check the result of this delete operation // TODO: Check the result of this delete operation
return! players.DeleteOneAsync (fun p -> p.Player.DiscordId = memberId) return! players.DeleteOneAsync (fun e -> e.Player.DiscordId = memberId)
|> Async.AwaitTask |> Async.AwaitTask
|> Async.Ignore |> Async.Ignore
} }
let updateAttacks (playerId : uint64) (attacks : Attack array) =
async {
let filter = Builders<PlayerEntry>.Filter.Eq((fun e -> e.Player.DiscordId), playerId)
let update = Builders<PlayerEntry>.Update.Set((fun e -> e.Player.Attacks), attacks)
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
}
let updatePlayer player =
async {
let filter = Builders<PlayerEntry>.Filter.Eq((fun e -> e.Player.DiscordId), player.DiscordId)
let update = Builders<PlayerEntry>.Update.Set((fun e -> e.Player), player)
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
}
let getTopPlayers number =
async {
let! entries = players.Find(fun _ -> true).SortBy(fun e -> e.Player.Bank).Limit(Nullable<int>(number)).ToListAsync() |> Async.AwaitTask
return entries |> Seq.map (fun e -> e.Player)
}

View File

@ -9,7 +9,7 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Shared", "Shared\Shared.fsp
EndProject EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Store", "Store\Store.fsproj", "{CD88B0A6-DE42-4087-9B33-48FF84201633}" Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Store", "Store\Store.fsproj", "{CD88B0A6-DE42-4087-9B33-48FF84201633}"
EndProject EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PlayerRegistration", "PlayerRegistration\PlayerRegistration.fsproj", "{FF9E58A6-1A1D-4DEC-B52D-265F215BF315}" Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PlayerInteractions", "PlayerInteractions\PlayerInteractions.fsproj", "{FF9E58A6-1A1D-4DEC-B52D-265F215BF315}"
EndProject EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "DbService", "DbService\DbService.fsproj", "{B1D3E1CC-451C-42D4-B054-D64E75E1A3B9}" Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "DbService", "DbService\DbService.fsproj", "{B1D3E1CC-451C-42D4-B054-D64E75E1A3B9}"
EndProject EndProject

View File

@ -7,9 +7,6 @@ open DSharpPlus.Entities
open DSharpPlus.EventArgs open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands open DSharpPlus.SlashCommands
open DegenzGame.Shared open DegenzGame.Shared
open DegenzGame.DbService
open DegenzGame.Functions
open MongoDB.Driver
[<Literal>] [<Literal>]
// Degenz Server // Degenz Server
@ -17,18 +14,17 @@ open MongoDB.Driver
// My server // My server
let battleChannel = 927449884204867664uL let battleChannel = 927449884204867664uL
let attack (ctx : InteractionContext) (target : DiscordUser) = let attack (ctx : InteractionContext) (target : DiscordUser) =
async { async {
// TODO: We need to check if the player has any active embed hacks going, if not they can cheat // TODO: We need to check if the player has any active embed hacks going, if not they can cheat
let! attacker = tryFindPlayer ctx.Member.Id let! attacker = DbService.tryFindPlayer ctx.Member.Id
let! defender = tryFindPlayer target.Id let! defender = DbService.tryFindPlayer target.Id
match attacker , defender with match attacker , defender with
| Some attacker , Some defender -> | Some attacker , Some defender ->
let updatedAttacks = removeExpiredActions (TimeSpan.FromMinutes(5)) (fun (atk : Attack) -> atk.Timestamp) attacker.Attacks let updatedAttacks =
let filter = Builders<Player>.Filter.Eq((fun p -> p.DiscordId), attacker.DiscordId) attacker.Attacks
let update = Builders<Player>.Update.Set((fun p -> p.Attacks), updatedAttacks) |> removeExpiredActions (TimeSpan.FromMinutes(5)) (fun (atk : Attack) -> atk.Timestamp)
let! _ = players.UpdateOneAsync(filter, update) |> Async.AwaitTask do! DbService.updateAttacks attacker.DiscordId updatedAttacks
if updatedAttacks.Length < 2 then if updatedAttacks.Length < 2 then
let builder = DiscordInteractionResponseBuilder() let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick the hack you wish to use.") |> ignore builder.AddEmbed (constructEmbed "Pick the hack you wish to use.") |> ignore
@ -62,13 +58,11 @@ let attack (ctx : InteractionContext) (target : DiscordUser) =
let defend (ctx : InteractionContext) = let defend (ctx : InteractionContext) =
async { async {
let! player = tryFindPlayer ctx.Member.Id let! player = DbService.tryFindPlayer ctx.Member.Id
match player with match player with
| Some player -> | Some player ->
let updatedDefenses = removeExpiredActions (TimeSpan.FromMinutes(60)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses let updatedDefenses = removeExpiredActions (TimeSpan.FromMinutes(60)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses
let filter = Builders<Player>.Filter.Eq((fun p -> p.DiscordId), player.DiscordId) do! DbService.updatePlayer <| { player with Defenses = updatedDefenses }
let update = Builders<Player>.Update.Set((fun p -> p.Defenses), updatedDefenses)
let! _ = players.UpdateOneAsync(filter, update) |> Async.AwaitTask
if updatedDefenses.Length < 2 then if updatedDefenses.Length < 2 then
let builder = DiscordInteractionResponseBuilder() let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick a defense to mount for a duration of time") |> ignore builder.AddEmbed (constructEmbed "Pick a defense to mount for a duration of time") |> ignore
@ -96,36 +90,6 @@ let defend (ctx : InteractionContext) =
} |> Async.StartAsTask } |> Async.StartAsTask
:> Task :> Task
let status (ctx : InteractionContext) =
async {
let! player = tryFindPlayer ctx.Member.Id
match player with
| Some p ->
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- Functions.statusFormat p
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
| None -> do! notYetAHackerMsg ctx
} |> Async.StartAsTask
:> Task
let leaderboard (ctx : InteractionContext) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
let! leaders = players.Find(fun _ -> true).SortBy(fun p -> p.Bank).Limit(10).ToListAsync() |> Async.AwaitTask
let content =
leaders.ToArray()
|> Array.mapi (fun i p -> $"{i + 1}. {p.Bank} {p.Name}")
|> String.concat "\n"
builder.Content <- if not <| String.IsNullOrEmpty content then content else "There are no active hackers"
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let handleAttack (event : ComponentInteractionCreateEventArgs) = let handleAttack (event : ComponentInteractionCreateEventArgs) =
let updatePlayer amount attack p = let updatePlayer amount attack p =
{ p with Attacks = Array.append [| attack |] p.Attacks ; Bank = MathF.Max(p.Bank + amount, 0f) } { p with Attacks = Array.append [| attack |] p.Attacks ; Bank = MathF.Max(p.Bank + amount, 0f) }
@ -133,8 +97,8 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
let split = event.Id.Split("-") let split = event.Id.Split("-")
let ( resultHack , weapon ) = Weapon.TryParse(split.[1]) let ( resultHack , weapon ) = Weapon.TryParse(split.[1])
let ( resultId , targetId ) = UInt64.TryParse split.[2] let ( resultId , targetId ) = UInt64.TryParse split.[2]
let! resultPlayer = tryFindPlayer event.User.Id let! resultPlayer = DbService.tryFindPlayer event.User.Id
let! resultTarget = tryFindPlayer targetId let! resultTarget = DbService.tryFindPlayer targetId
match resultPlayer , resultTarget , resultHack , resultId with match resultPlayer , resultTarget , resultHack , resultId with
| Some player , Some target , true , true -> | Some player , Some target , true , true ->
let wasSuccessfulHack = let wasSuccessfulHack =
@ -147,8 +111,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
| false -> | false ->
let prize = 0.1726f let prize = 0.1726f
let attack = { HackType = enum<Weapon>(weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } } let attack = { HackType = enum<Weapon>(weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } }
let filter = Builders<Player>.Filter.Eq((fun p -> p.DiscordId), player.DiscordId) do! DbService.updatePlayer <| updatePlayer prize attack player
let! _ = players.ReplaceOneAsync(filter, updatePlayer prize attack player) |> Async.AwaitTask
let builder = DiscordInteractionResponseBuilder() let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true builder.IsEphemeral <- true
@ -171,8 +134,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
|> Async.AwaitTask |> Async.AwaitTask
let attack = { HackType = enum<Weapon>(weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } } let attack = { HackType = enum<Weapon>(weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } }
let filter = Builders<Player>.Filter.Eq((fun p -> p.DiscordId), player.DiscordId) do! DbService.updatePlayer <| updatePlayer loss attack player
let! _ = players.ReplaceOneAsync(filter, updatePlayer loss attack player) |> Async.AwaitTask
let builder = DiscordMessageBuilder() let builder = DiscordMessageBuilder()
builder.WithContent($"{event.User.Username} failed to hack <@{targetId}>!") |> ignore builder.WithContent($"{event.User.Username} failed to hack <@{targetId}>!") |> ignore
@ -192,7 +154,7 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
async { async {
let split = event.Id.Split("-") let split = event.Id.Split("-")
let ( shieldResult , shield ) = Shield.TryParse(split.[1]) let ( shieldResult , shield ) = Shield.TryParse(split.[1])
let! playerResult = tryFindPlayer event.User.Id let! playerResult = DbService.tryFindPlayer event.User.Id
match playerResult , shieldResult with match playerResult , shieldResult with
| Some player , true -> | Some player , true ->
let builder = DiscordInteractionResponseBuilder() let builder = DiscordInteractionResponseBuilder()
@ -202,9 +164,7 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
|> Async.AwaitTask |> Async.AwaitTask
let defense = { DefenseType = shield ; Timestamp = DateTime.UtcNow } let defense = { DefenseType = shield ; Timestamp = DateTime.UtcNow }
let filter = Builders<Player>.Filter.Eq((fun p -> p.DiscordId), player.DiscordId) do! DbService.updatePlayer <| { player with Defenses = Array.append [| defense |] player.Defenses }
let update = Builders<Player>.Update.Set((fun p -> p.Defenses), Array.append [| defense |] player.Defenses )
let! _ = players.UpdateOneAsync(filter, update) |> Async.AwaitTask
let builder = DiscordMessageBuilder() let builder = DiscordMessageBuilder()
builder.WithContent($"{event.User.Username} has protected their system!") |> ignore builder.WithContent($"{event.User.Username} has protected their system!") |> ignore
@ -220,7 +180,7 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
|> Async.AwaitTask |> Async.AwaitTask
} }
let handleButtonEvent (client : DiscordClient) (event : ComponentInteractionCreateEventArgs) = let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
async { async {
return! match event.Id with return! match event.Id with
| id when id.StartsWith("Attack") -> handleAttack event | id when id.StartsWith("Attack") -> handleAttack event

View File

@ -14,7 +14,6 @@
<ReferencePathWithRefAssemblies Update="\home\joe\.nuget\packages\dsharpplus.slashcommands\4.2.0-nightly-01054\lib\netstandard2.0\DSharpPlus.SlashCommands.dll" /> <ReferencePathWithRefAssemblies Update="\home\joe\.nuget\packages\dsharpplus.slashcommands\4.2.0-nightly-01054\lib\netstandard2.0\DSharpPlus.SlashCommands.dll" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Functions.fs" />
<Compile Include="Commands.fs" /> <Compile Include="Commands.fs" />
<Compile Include="Program.fs" /> <Compile Include="Program.fs" />
<Content Include="paket.references" /> <Content Include="paket.references" />

View File

@ -18,12 +18,6 @@ type HackerGame() =
[<SlashCommand("defend", "Create a passive defense that will last a certain amount of time")>] [<SlashCommand("defend", "Create a passive defense that will last a certain amount of time")>]
member this.DefendCommand (ctx : InteractionContext) = Commands.defend ctx member this.DefendCommand (ctx : InteractionContext) = Commands.defend ctx
[<SlashCommand("status", "Get your current status like bank account, and active hacks and defenses")>]
member this.Status (ctx : InteractionContext) = Commands.status ctx
[<SlashCommand("leaderboard", "View the current list of players ranked by highest earnings")>]
member this.Leaderboard (ctx : InteractionContext) = Commands.leaderboard ctx
let config = DiscordConfiguration() let config = DiscordConfiguration()
config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk" config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk"
config.TokenType <- TokenType.Bot config.TokenType <- TokenType.Bot

View File

@ -4,6 +4,7 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<RootNamespace>PlayerRegistration</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Program.fs" /> <Compile Include="Program.fs" />

View File

@ -1,8 +1,9 @@
open System
open System.Threading.Tasks open System.Threading.Tasks
open DegenzGame.DbService open DSharpPlus.Entities
open DSharpPlus open DSharpPlus
open DSharpPlus.SlashCommands open DSharpPlus.SlashCommands
open DegenzGame
open DegenzGame.Shared open DegenzGame.Shared
module Commands = module Commands =
@ -30,14 +31,14 @@ module Commands =
let addHackerRole (ctx : InteractionContext) = let addHackerRole (ctx : InteractionContext) =
async { async {
let! player = tryFindPlayer ctx.Member.Id let! player = DbService.tryFindPlayer ctx.Member.Id
let! newPlayer = let! newPlayer =
match player with match player with
| Some _ -> async.Return false | Some _ -> async.Return false
| None -> | None ->
async { async {
do! newPlayer ctx.Member.Username ctx.Member.Id do! newPlayer ctx.Member.Username ctx.Member.Id
|> insertNewPlayer |> DbService.insertNewPlayer
for role in ctx.Guild.Roles do for role in ctx.Guild.Roles do
if role.Value.Name = "Hacker" then if role.Value.Name = "Hacker" then
@ -64,13 +65,44 @@ module Commands =
do! ctx.Member.RevokeRoleAsync(role) do! ctx.Member.RevokeRoleAsync(role)
|> Async.AwaitTask |> Async.AwaitTask
do! removePlayer ctx.Member.Id do! DbService.removePlayer ctx.Member.Id
do! ctx.CreateResponseAsync("You are now lame", true) do! ctx.CreateResponseAsync("You are now lame", true)
|> Async.AwaitTask |> Async.AwaitTask
} |> Async.StartAsTask } |> Async.StartAsTask
:> Task :> Task
let leaderboard (ctx : InteractionContext) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
let! leaders = DbService.getTopPlayers 10
let content =
leaders
|> Seq.toArray
|> Array.mapi (fun i p -> $"{i + 1}. {p.Bank} {p.Name}")
|> String.concat "\n"
builder.Content <- if not <| String.IsNullOrEmpty content then content else "There are no active hackers"
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let status (ctx : InteractionContext) =
async {
let! player = DbService.tryFindPlayer ctx.Member.Id
match player with
| Some p ->
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- statusFormat p
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
| None -> do! notYetAHackerMsg ctx
} |> Async.StartAsTask
:> Task
type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule () type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule ()
@ -83,6 +115,12 @@ type PlayerRegistration() =
[<SlashCommand("bluepill", "Take the bluepill and become lame")>] [<SlashCommand("bluepill", "Take the bluepill and become lame")>]
member _.RemoveHackerRole (ctx : InteractionContext) = Commands.removeHackerRole ctx member _.RemoveHackerRole (ctx : InteractionContext) = Commands.removeHackerRole ctx
[<SlashCommand("status", "Get your current status like bank account, and active hacks and defenses")>]
member this.Status (ctx : InteractionContext) = Commands.status ctx
[<SlashCommand("leaderboard", "View the current list of players ranked by highest earnings")>]
member this.Leaderboard (ctx : InteractionContext) = Commands.leaderboard ctx
let config = DiscordConfiguration() let config = DiscordConfiguration()
config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk" config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk"

View File

@ -1,9 +1,80 @@
module DegenzGame.Functions module DegenzGame.Shared
open System open System
open DSharpPlus open DSharpPlus
open DSharpPlus.Entities open DSharpPlus.Entities
open DegenzGame.Shared open DSharpPlus.SlashCommands
type ActionClass =
| Network
| Exploit
| Penetration
type Weapon =
| Virus = 0
| Ransom = 1
| Worm = 2
| DDos = 3
| Crack = 4
| Injection = 5
type Shield =
| Firewall = 0
| PortScan = 1
| Encryption = 2
| Hardening = 4
| Sanitation = 5
| Cypher = 3
let getClass = function
| 0 | 1 -> Network
| 2 | 3 -> Exploit
| 4 | _ -> Penetration
type HackResult =
| Strong
| Weak
[<CLIMutable>]
type DiscordPlayer = {
Id : uint64
Name : string
}
[<CLIMutable>]
type Attack = {
HackType : Weapon
Target : DiscordPlayer
Timestamp : DateTime
}
[<CLIMutable>]
type Defense = {
DefenseType : Shield
Timestamp : DateTime
}
[<CLIMutable>]
type Player = {
DiscordId : uint64
Name : string
Weapons : Weapon array
Shields : Shield array
Attacks : Attack array
Defenses : Defense array
Bank : single
}
let createSimpleResponseAsync msg (ctx : InteractionContext) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.Content <- msg
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
let notYetAHackerMsg = createSimpleResponseAsync "You are not currently a hacker, first use the /redpill command to become one"
let hackDescription = "" let hackDescription = ""

View File

@ -5,7 +5,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Types.fs" /> <Compile Include="Shared.fs" />
<Content Include="paket.references" /> <Content Include="paket.references" />
</ItemGroup> </ItemGroup>
<Import Project="..\.paket\Paket.Restore.targets" /> <Import Project="..\.paket\Paket.Restore.targets" />

View File

@ -1,77 +0,0 @@
module DegenzGame.Shared
open System
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.SlashCommands
type ActionClass =
| Network
| Exploit
| Penetration
type Weapon =
| Virus = 0
| Ransom = 1
| Worm = 2
| DDos = 3
| Crack = 4
| Injection = 5
type Shield =
| Firewall = 0
| PortScan = 1
| Encryption = 2
| Hardening = 4
| Sanitation = 5
| Cypher = 3
let getClass = function
| 0 | 1 -> Network
| 2 | 3 -> Exploit
| 4 | _ -> Penetration
type HackResult =
| Strong
| Weak
[<CLIMutable>]
type DiscordPlayer = {
Id : uint64
Name : string
}
[<CLIMutable>]
type Attack = {
HackType : Weapon
Target : DiscordPlayer
Timestamp : DateTime
}
[<CLIMutable>]
type Defense = {
DefenseType : Shield
Timestamp : DateTime
}
[<CLIMutable>]
type Player = {
DiscordId : uint64
Name : string
Weapons : Weapon array
Shields : Shield array
Attacks : Attack array
Defenses : Defense array
Bank : single
}
let createSimpleResponseAsync msg (ctx : InteractionContext) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.Content <- msg
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
let notYetAHackerMsg = createSimpleResponseAsync "You are not currently a hacker, first use the /redpill command to become one"