Fixing some trainer bugs, mapping value cause Mongo hates F#

This commit is contained in:
Joseph Ferano 2022-01-31 02:34:56 +07:00
parent 7c8a460d5b
commit 7b48ff0a9b
9 changed files with 161 additions and 125 deletions

View File

@ -26,7 +26,6 @@ for conf in configs do
conf.TokenType <- TokenType.Bot conf.TokenType <- TokenType.Bot
conf.Intents <- DiscordIntents.All conf.Intents <- DiscordIntents.All
let guild = GuildEnvironment.guildId let guild = GuildEnvironment.guildId
playerInteractionsConfig.Token <- GuildEnvironment.tokenPlayerInteractions playerInteractionsConfig.Token <- GuildEnvironment.tokenPlayerInteractions

View File

@ -84,7 +84,7 @@ let responseSuccessfulHackTrainer defenderName (hack : BattleItem) prize =
embed.ImageUrl <- getHackGif (enum<HackId>(hack.Id)) embed.ImageUrl <- getHackGif (enum<HackId>(hack.Id))
DiscordFollowupMessageBuilder() DiscordFollowupMessageBuilder()
.WithContent($"Successfully hacked {defenderName} using {hack}! You just won {prize} GoodBoyTokenz!") .WithContent($"Successfully hacked {defenderName} using {hack.Name}! You just won {prize} GoodBoyTokenz!")
.AddEmbed(embed.Build()) .AddEmbed(embed.Build())
.AsEphemeral(true) .AsEphemeral(true)

View File

@ -8,6 +8,7 @@ open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands open DSharpPlus.SlashCommands
open Degenz open Degenz
open Degenz.Shared open Degenz.Shared
open Degenz.Store
let getTimeTillCooldownFinishes (timespan : TimeSpan) timestamp = let getTimeTillCooldownFinishes (timespan : TimeSpan) timestamp =
let timeRemaining = timespan - (DateTime.UtcNow - timestamp) let timeRemaining = timespan - (DateTime.UtcNow - timestamp)
@ -42,7 +43,7 @@ let checkIfHackHasCooldown hackId attacker =
|> Array.tryFind (fun a -> a.ActionId = hackId) |> Array.tryFind (fun a -> a.ActionId = hackId)
|> function |> function
| Some a -> a.Timestamp | Some a -> a.Timestamp
| None -> DateTime.MinValue | None -> DateTime.MinValue;
if DateTime.UtcNow - mostRecentHackAttack > TimeSpan.FromMinutes(5) then if DateTime.UtcNow - mostRecentHackAttack > TimeSpan.FromMinutes(5) then
Ok attacker Ok attacker
else else
@ -56,7 +57,7 @@ let checkIfInventoryIsEmpty attacker =
| _ -> Ok attacker | _ -> Ok attacker
let calculateDamage (hack : BattleItem) (shield : BattleItem) = let calculateDamage (hack : BattleItem) (shield : BattleItem) =
if hack.Power > shield.Power if hack.Class = shield.Class
then Strong then Strong
else Weak else Weak
@ -71,7 +72,7 @@ let updateCombatants attacker defender hack prize =
let updatePlayer amount attack p = let updatePlayer amount attack p =
{ p with Actions = Array.append [| attack |] p.Actions ; Bank = max (p.Bank + amount) 0<GBT> } { p with Actions = Array.append [| attack |] p.Actions ; Bank = max (p.Bank + amount) 0<GBT> }
let target = { Id = defender.DiscordId ; Name = defender.Name } let target = { Id = defender.DiscordId ; Name = defender.Name }
let attack = { ActionId = int hack ; Type = Attack ( target , prize > 0<GBT> ) ; Timestamp = DateTime.UtcNow } let attack = { ActionId = int hack ; Type = Attack { Target = target ; Result = prize > 0<GBT> } ; Timestamp = DateTime.UtcNow }
[ DbService.updatePlayer <| updatePlayer prize attack attacker [ DbService.updatePlayer <| updatePlayer prize attack attacker
DbService.updatePlayer <| modifyPlayerBank defender -prize ] DbService.updatePlayer <| modifyPlayerBank defender -prize ]
@ -179,7 +180,7 @@ let defend (ctx : InteractionContext) =
let! player = DbService.tryFindPlayer ctx.Member.Id let! player = DbService.tryFindPlayer ctx.Member.Id
match player with match player with
| Some player -> | Some player ->
if Player.defenses player |> Array.length > 0 then if Player.shields player |> Array.length > 0 then
let embed = Embeds.pickDefense "Defend" player let embed = Embeds.pickDefense "Defend" player
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed)
|> Async.AwaitTask |> Async.AwaitTask

View File

@ -32,7 +32,7 @@
"Case": "Hack" "Case": "Hack"
}, },
"Class": { "Class": {
"Case": "Network" "Case": "Exploit"
}, },
"Cost": 100, "Cost": 100,
"Power": 50, "Power": 50,
@ -45,7 +45,7 @@
"Case": "Hack" "Case": "Hack"
}, },
"Class": { "Class": {
"Case": "Network" "Case": "Exploit"
}, },
"Cost": 100, "Cost": 100,
"Power": 50, "Power": 50,
@ -58,7 +58,7 @@
"Case": "Hack" "Case": "Hack"
}, },
"Class": { "Class": {
"Case": "Network" "Case": "Penetration"
}, },
"Cost": 100, "Cost": 100,
"Power": 50, "Power": 50,
@ -71,7 +71,7 @@
"Case": "Hack" "Case": "Hack"
}, },
"Class": { "Class": {
"Case": "Network" "Case": "Penetration"
}, },
"Cost": 100, "Cost": 100,
"Power": 50, "Power": 50,
@ -110,7 +110,7 @@
"Case": "Shield" "Case": "Shield"
}, },
"Class": { "Class": {
"Case": "Network" "Case": "Exploit"
}, },
"Cost": 100, "Cost": 100,
"Power": 50, "Power": 50,
@ -123,7 +123,7 @@
"Case": "Shield" "Case": "Shield"
}, },
"Class": { "Class": {
"Case": "Network" "Case": "Penetration"
}, },
"Cost": 100, "Cost": 100,
"Power": 50, "Power": 50,
@ -136,7 +136,7 @@
"Case": "Shield" "Case": "Shield"
}, },
"Class": { "Class": {
"Case": "Network" "Case": "Penetration"
}, },
"Cost": 100, "Cost": 100,
"Power": 50, "Power": 50,
@ -149,7 +149,7 @@
"Case": "Shield" "Case": "Shield"
}, },
"Class": { "Class": {
"Case": "Network" "Case": "Exploit"
}, },
"Cost": 100, "Cost": 100,
"Power": 50, "Power": 50,

View File

@ -1,77 +1,48 @@
module Degenz.PlayerInteractions module Degenz.PlayerInteractions
open System
open System.Threading.Tasks open System.Threading.Tasks
open AsciiTableFormatter
open DSharpPlus.Entities open DSharpPlus.Entities
open DSharpPlus open DSharpPlus
open DSharpPlus.SlashCommands open DSharpPlus.SlashCommands
open Degenz open Degenz.Store
open Degenz.Shared open Degenz.Shared
module Commands = module Commands =
// let newPlayer nickname (membr : uint64) = let newPlayer nickname (membr : uint64) =
// let h1 = [| Weapon.Virus ; Weapon.Ransom |]
// let h2 = [| Weapon.DDos ; Weapon.Worm |]
// let h3 = [| Weapon.Crack ; Weapon.Injection |]
// let d1 = [| Shield.Firewall ; Shield.PortScan |]
// let d2 = [| Shield.Encryption ; Shield.Cypher |]
// let d3 = [| Shield.Hardening ; Shield.Sanitation |]
// let rand = System.Random(System.Guid.NewGuid().GetHashCode()) let rand = System.Random(System.Guid.NewGuid().GetHashCode())
// let getRandom (actions : 'a array) = actions.[rand.Next(0, max 0 (actions.Length - 1))] let randHack = rand.Next(0, 3)
// let randShield = rand.Next(3, 6)
// let weapons = [| getRandom hackInventory |] let hack = armoury |> Array.find (fun i -> i.Id = randHack)
// let shields = [| getRandom shieldInventory |] let shield = armoury |> Array.find (fun i -> i.Id = randShield)
//
// { DiscordId = membr
// Name = nickname
// Weapons = weapons
// Shields = shields
// Attacks = [||]
// Defenses = [||]
// Bank = 15 }
// let addHackerRole (ctx : InteractionContext) = { DiscordId = membr
// async { Name = nickname
// let! player = DbService.tryFindPlayer ctx.Member.Id Arsenal = [| hack ; shield |]
// let! newPlayer = Actions = [||]
// match player with Bank = 100<GBT> }
// | Some _ -> async.Return false
// | None ->
// async {
// do! newPlayer ctx.Member.DisplayName ctx.Member.Id
// |> DbService.insertNewPlayer
//
// for role in ctx.Guild.Roles do
// if role.Value.Name = "Hacker" then
// do! ctx.Member.GrantRoleAsync(role.Value)
// |> Async.AwaitTask
// return true
// }
// if newPlayer then
// do! ctx.CreateResponseAsync("You are now an elite haxxor", true)
// |> Async.AwaitTask
// else
// do! ctx.CreateResponseAsync("Already registered as an elite haxxor", true)
// |> Async.AwaitTask
//
// } |> Async.StartAsTask
// :> Task
// let removeHackerRole (ctx : InteractionContext) = let addHackerRole (ctx : InteractionContext) =
// async { async {
// for role in ctx.Member.Roles do let! player = DbService.tryFindPlayer ctx.Member.Id
// if role.Name = "Hacker" then let! newPlayer =
// do! ctx.Member.RevokeRoleAsync(role) match player with
// |> Async.AwaitTask | Some _ -> async.Return false
| None ->
async {
do! newPlayer ctx.Member.DisplayName ctx.Member.Id
|> DbService.insertNewPlayer
return true
}
if newPlayer then
do! ctx.CreateResponseAsync("You are now an elite haxxor", true)
|> Async.AwaitTask
else
do! ctx.CreateResponseAsync("Already registered as an elite haxxor", true)
|> Async.AwaitTask
// do! DbService.removePlayer ctx.Member.Id } |> Async.StartAsTask
:> Task
// do! ctx.CreateResponseAsync("You are now lame", true)
// |> Async.AwaitTask
// } |> Async.StartAsTask
// :> Task
[<CLIMutable>] [<CLIMutable>]
type LeaderboardEntry = { type LeaderboardEntry = {
@ -103,18 +74,17 @@ module Commands =
let status (ctx : InteractionContext) = let status (ctx : InteractionContext) =
async { async {
let! player = DbService.tryFindPlayer ctx.Member.Id let! maybePlayer = DbService.tryFindPlayer ctx.Member.Id
match player with match maybePlayer with
| Some p -> | Some player ->
// let updatedAttacks = p.Attacks |> removeExpiredActions (TimeSpan.FromHours(24)) (fun (atk : Attack) -> atk.Timestamp) let updatedActions = removeExpiredActions player.Actions
// let updatedDefenses = p.Defenses |> removeExpiredActions (TimeSpan.FromHours(6)) (fun (p : Defense) -> p.Timestamp) let updatedPlayer = { player with Actions = updatedActions }
// let updatedPlayer = { p with Attacks = updatedAttacks ; Defenses = updatedDefenses }
// do! DbService.updatePlayer updatedPlayer
let builder = DiscordInteractionResponseBuilder() let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true builder.IsEphemeral <- true
// builder.Content <- statusFormat updatedPlayer builder.Content <- statusFormat updatedPlayer
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask |> Async.AwaitTask
do! DbService.updatePlayer updatedPlayer
| None -> do! notYetAHackerMsg ctx | None -> do! notYetAHackerMsg ctx
} |> Async.StartAsTask } |> Async.StartAsTask
:> Task :> Task
@ -122,11 +92,8 @@ module Commands =
type PlayerInteractions() = type PlayerInteractions() =
inherit ApplicationCommandModule () inherit ApplicationCommandModule ()
// [<SlashCommand("redpill", "Take the redpill and become a hacker")>] [<SlashCommand("redpill", "Take the redpill and become a hacker")>]
// member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx
// [<SlashCommand("bluepill", "Take the bluepill and become lame")>]
// member _.RemoveHackerRole (ctx : InteractionContext) = Commands.removeHackerRole ctx
[<SlashCommand("status", "Get your current status like bank account, and active hacks and defenses")>] [<SlashCommand("status", "Get your current status like bank account, and active hacks and defenses")>]
member this.Status (ctx : InteractionContext) = Commands.status ctx member this.Status (ctx : InteractionContext) = Commands.status ctx

View File

@ -1,5 +1,6 @@
module Degenz.Store module Degenz.Store
open System
open System.Threading.Tasks open System.Threading.Tasks
open DSharpPlus.Entities open DSharpPlus.Entities
open DSharpPlus open DSharpPlus
@ -8,6 +9,15 @@ open DSharpPlus.SlashCommands
open Degenz open Degenz
open Degenz.Embeds open Degenz.Embeds
open Degenz.Shared open Degenz.Shared
open Newtonsoft.Json
let getItemFromArmoury id = armoury |> Array.find (fun w -> w.Id = id)
let removeExpiredActions actions =
actions
|> Array.filter (fun (act : Action) ->
let item = armoury |> Array.find (fun w -> w.Id = act.ActionId)
DateTime.UtcNow - act.Timestamp < TimeSpan.FromMinutes(int item.Cooldown))
let viewStore (ctx : InteractionContext) = let viewStore (ctx : InteractionContext) =
async { async {

View File

@ -6,6 +6,7 @@ open DSharpPlus.Entities
open DSharpPlus.EventArgs open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands open DSharpPlus.SlashCommands
open Degenz.Shared open Degenz.Shared
open Degenz.Store
let defaultHack = armoury |> Array.find (fun i -> i.Id = int HackId.Virus) let defaultHack = armoury |> Array.find (fun i -> i.Id = int HackId.Virus)
let defaultShield = armoury |> Array.find (fun i -> i.Id = int ShieldId.Firewall) let defaultShield = armoury |> Array.find (fun i -> i.Id = int ShieldId.Firewall)
@ -54,7 +55,7 @@ let handleTrainerStep2 (event : ComponentInteractionCreateEventArgs) =
let! result = DbService.tryFindPlayer event.User.Id let! result = DbService.tryFindPlayer event.User.Id
match result with match result with
| Some player -> | Some player ->
let weaponName = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield let weaponName = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
do! Message.sendInteractionEvent event do! Message.sendInteractionEvent event
($"First things first, let's get your system protected. Let's enable a shield to protect you from potential hackers. " ($"First things first, let's get your system protected. Let's enable a shield to protect you from potential hackers. "
+ $"You currently have {weaponName} in your arsenal. To enable it and protect your system, you can use the `/defend` slash command to choose a shield." + $"You currently have {weaponName} in your arsenal. To enable it and protect your system, you can use the `/defend` slash command to choose a shield."
@ -92,11 +93,11 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
async { async {
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate) do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|> Async.AwaitTask |> Async.AwaitTask
let! result = DbService.tryFindPlayer event.User.Id let! maybePlayer = DbService.tryFindPlayer event.User.Id
match result with match maybePlayer with
| Some player -> | Some player ->
let prize = 0.223f let prize = 0.223f
let shield = player.Arsenal |> Array.tryHead |> Option.defaultValue defaultShield let shield = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield
let embed = Embeds.responseCreatedShieldTrainer shield let embed = Embeds.responseCreatedShieldTrainer shield
do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore
do! Async.Sleep 2000 do! Async.Sleep 2000
@ -116,7 +117,7 @@ let handleTrainerStep4 (event : ComponentInteractionCreateEventArgs) =
let! result = DbService.tryFindPlayer event.User.Id let! result = DbService.tryFindPlayer event.User.Id
match result with match result with
| Some player -> | Some player ->
let weaponName = player.Arsenal |> Array.tryHead |> Option.defaultValue defaultHack let weaponName = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
do! Message.sendInteractionEvent event do! Message.sendInteractionEvent event
($"Next why don't you try hacking me. You currently have {weaponName} equipped. To hack me and get some money, " ($"Next why don't you try hacking me. You currently have {weaponName} equipped. To hack me and get some money, "
+ $" you can use the '/hack' slash command and select a user to hack, then choose the hack attack you wish to use." + $" you can use the '/hack' slash command and select a user to hack, then choose the hack attack you wish to use."

View File

@ -7,6 +7,68 @@ open MongoDB.Bson
open MongoDB.Bson.Serialization open MongoDB.Bson.Serialization
open MongoDB.Driver open MongoDB.Driver
[<CLIMutable>]
type AttackAction =
{ ActionId : int
Result : bool
Target : DiscordPlayer
Timestamp : DateTime }
[<CLIMutable>]
type DefenseAction =
{ ActionId : int
Timestamp : DateTime }
[<CLIMutable>]
type PlayerEntry =
{ DiscordId : uint64
Name : string
Arsenal : int array
Attacks : AttackAction array
Defenses : DefenseAction array
Bank : int }
let private actionToAttack (action : Action) (hack : AttackResult) =
{ ActionId = action.ActionId
Result = hack.Result
Target = hack.Target
Timestamp = action.Timestamp }
let private actionToDefense (action : Action) =
{ ActionId = action.ActionId
Timestamp = action.Timestamp }
let private attackToAction (attack : AttackAction) =
{ ActionId = attack.ActionId
Type = Attack { Target = attack.Target ; Result = attack.Result }
Timestamp = attack.Timestamp }
let private defenseToAction (action : DefenseAction) =
{ ActionId = action.ActionId
Type = Defense
Timestamp = action.Timestamp }
let private playerMap (player : PlayerData) = {
DiscordId = player.DiscordId
Name = player.Name
Arsenal = player.Arsenal |> Array.map (fun w -> w.Id)
Attacks = [||]
Defenses = [||]
Bank = int player.Bank
}
let private mapBack (player : PlayerEntry) : PlayerData = {
DiscordId = player.DiscordId
Name = player.Name
Arsenal = player.Arsenal |> Array.map (fun w -> armoury |> Array.find (fun w' -> w = w'.Id))
Actions =
let atks = player.Attacks |> Array.map attackToAction
let dfns = player.Defenses |> Array.map defenseToAction
Array.append atks dfns
Bank = player.Bank * 1<GBT>
}
let mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING")) let mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING"))
let db = mongo.GetDatabase("degenz") let db = mongo.GetDatabase("degenz")
let players = db.GetCollection<BsonDocument>("players") let players = db.GetCollection<BsonDocument>("players")
@ -20,30 +82,32 @@ let tryFindPlayer (id : uint64) =
| p -> return p | p -> return p
.GetValue("Player") .GetValue("Player")
.ToBsonDocument() .ToBsonDocument()
|> BsonSerializer.Deserialize<PlayerData> |> BsonSerializer.Deserialize<PlayerEntry>
|> mapBack
|> Some |> Some
} }
let insertNewPlayer (player : PlayerData) = let insertNewPlayer (player : PlayerData) =
async { async {
let dict = [ KeyValuePair("Player" , player.ToBsonDocument() :> Object) ] let p = playerMap player
let dict = [ KeyValuePair("Player" , p.ToBsonDocument() :> Object) ]
do! BsonDocument(dict) do! BsonDocument(dict)
|> players.InsertOneAsync |> players.InsertOneAsync
|> Async.AwaitTask |> Async.AwaitTask
} }
let deletePlayer (player : PlayerData) = //let deletePlayer (player : PlayerData) =
async { // async {
let dict = [ KeyValuePair("Player" , player.ToBsonDocument() :> Object) ] // let dict = [ KeyValuePair("Player" , player.ToBsonDocument() :> Object) ]
do! BsonDocument(dict) // do! BsonDocument(dict)
|> players.InsertOneAsync // |> players.InsertOneAsync
|> Async.AwaitTask // |> Async.AwaitTask
} // }
//
let updatePlayer player = let updatePlayer (player : PlayerData) =
async { async {
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", player.DiscordId) let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", player.DiscordId)
let update = Builders<BsonDocument>.Update.Set("Player", player) let update = Builders<BsonDocument>.Update.Set("Player", playerMap player)
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
} }

View File

@ -55,15 +55,17 @@ type HackResult =
[<CLIMutable>] [<CLIMutable>]
type DiscordPlayer = { Id: uint64; Name: string } type DiscordPlayer = { Id: uint64; Name: string }
type Attack = { [<CLIMutable>]
type AttackResult = {
Result : bool Result : bool
Target : DiscordPlayer Target : DiscordPlayer
} }
type ActionType = type ActionType =
| Attack of target : DiscordPlayer * result : bool | Attack of AttackResult
| Defense | Defense
[<CLIMutable>]
type Action = type Action =
{ ActionId : int { ActionId : int
Type : ActionType Type : ActionType
@ -79,13 +81,13 @@ type PlayerData =
module Player = module Player =
let hacks player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Hack) let hacks player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Hack)
let shields player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Hack) let shields player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Shield)
let attacks player = let attacks player =
player.Actions player.Actions
|> Array.choose (fun act -> match act.Type with Attack (t,r) -> Some (act,t,r) | Defense -> None) |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None)
let defenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense _ -> true | _ -> false) let defenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense _ -> true | _ -> false)
let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack (t,r) -> Some (act,t,r) | Defense -> None) let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None)
let createSimpleResponseAsync msg (ctx: InteractionContext) = let createSimpleResponseAsync msg (ctx: InteractionContext) =
async { async {
@ -105,29 +107,21 @@ let hackDescription = ""
let statusFormat p = let statusFormat p =
$"Hacks: {Player.hacks p |> Array.toList} $"Hacks: {Player.hacks p |> Array.toList}
Shields: {Player.defenses p |> Array.toList} Shields: {Player.shields p |> Array.toList}
Hack Attacks: {Player.attacks p |> Array.toList} Hack Attacks: {Player.attacks p |> Array.toList}
Active Defenses: {Player.defenses p |> Array.toList} Active Defenses: {Player.defenses p |> Array.toList}
Bank: {p.Bank}" Bank: {p.Bank}"
let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleItem array) =
weapons
|> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Primary, $"{actionType}-{w.Id}-{playerInfo}", $"{w.Name}"))
let modifyPlayerBank player amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
let armoury = let armoury =
let file = System.IO.File.ReadAllText("Items.json") let file = System.IO.File.ReadAllText("Items.json")
JsonConvert.DeserializeObject<BattleItem array>(file) JsonConvert.DeserializeObject<BattleItem array>(file)
let getItemFromArmoury id = armoury |> Array.find (fun w -> w.Id = id)
let constructButtons (actionType: string) (playerInfo: string) (weapons: 'a array) =
weapons
|> Seq.map (fun hack -> DiscordButtonComponent(ButtonStyle.Primary, $"{actionType}-{hack}-{playerInfo}", $"{hack}"))
let removeExpiredActions actions =
actions
|> Array.filter (fun (act : Action) ->
let item = armoury |> Array.find (fun w -> w.Id = act.ActionId)
DateTime.UtcNow - act.Timestamp < TimeSpan.FromMinutes(int item.Cooldown))
let modifyPlayerBank player amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
module Message = module Message =
let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg = let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
async { async {