Move some stuff around
This commit is contained in:
parent
69545bf3a6
commit
ef3d5c58f5
@ -1,6 +1,5 @@
|
|||||||
module Degenz.Bot
|
module Degenz.Bot
|
||||||
|
|
||||||
open System
|
|
||||||
open System.Threading.Tasks
|
open System.Threading.Tasks
|
||||||
open DSharpPlus
|
open DSharpPlus
|
||||||
open DSharpPlus.SlashCommands
|
open DSharpPlus.SlashCommands
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="paket.references" />
|
<Content Include="paket.references" />
|
||||||
<Compile Include="GameConfig.fs" />
|
|
||||||
<Compile Include="GuildEnvironment.fs" />
|
<Compile Include="GuildEnvironment.fs" />
|
||||||
|
<Compile Include="Game.fs" />
|
||||||
<Compile Include="Embeds.fs" />
|
<Compile Include="Embeds.fs" />
|
||||||
<Compile Include="Store.fs" />
|
<Compile Include="Store.fs" />
|
||||||
<Compile Include="Trainer.fs" />
|
<Compile Include="Trainer.fs" />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
module Degenz.Embeds
|
module Degenz.Embeds
|
||||||
|
|
||||||
open DSharpPlus.EventArgs
|
open DSharpPlus.EventArgs
|
||||||
open Degenz.Shared
|
open Degenz.Types
|
||||||
open DSharpPlus.Entities
|
open DSharpPlus.Entities
|
||||||
open AsciiTableFormatter
|
open AsciiTableFormatter
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ let constructEmbed message =
|
|||||||
|
|
||||||
let pickDefense actionId player =
|
let pickDefense actionId player =
|
||||||
let buttons =
|
let buttons =
|
||||||
constructButtons actionId (string player.DiscordId) (Player.shields player)
|
Messaging.constructButtons actionId (string player.DiscordId) (Player.shields player)
|
||||||
|> Seq.cast<DiscordComponent>
|
|> Seq.cast<DiscordComponent>
|
||||||
|
|
||||||
let embed =
|
let embed =
|
||||||
@ -50,7 +50,7 @@ let pickDefense actionId player =
|
|||||||
|
|
||||||
let pickHack actionId attacker defender =
|
let pickHack actionId attacker defender =
|
||||||
let buttons =
|
let buttons =
|
||||||
constructButtons actionId $"{defender.DiscordId}-{defender.Name}" (Player.hacks attacker)
|
Messaging.constructButtons actionId $"{defender.DiscordId}-{defender.Name}" (Player.hacks attacker)
|
||||||
|> Seq.cast<DiscordComponent>
|
|> Seq.cast<DiscordComponent>
|
||||||
|
|
||||||
let embed =
|
let embed =
|
||||||
|
15
Bot/Game.fs
Normal file
15
Bot/Game.fs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module Degenz.Game
|
||||||
|
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open DSharpPlus.SlashCommands
|
||||||
|
open Degenz.DbService
|
||||||
|
|
||||||
|
let executePlayerAction (ctx : InteractionContext) (dispatch : PlayerData -> Async<unit>) =
|
||||||
|
async {
|
||||||
|
let! playerResult = tryFindPlayer ctx.Member.Id
|
||||||
|
match playerResult with
|
||||||
|
| Some player -> do! dispatch player
|
||||||
|
| None -> do! Messaging.sendSimpleResponse ctx "You are currently not a hacker, first use the /redpill command to become one"
|
||||||
|
} |> Async.StartAsTask
|
||||||
|
:> Task
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
module Degenz.GameConfig
|
|
@ -7,8 +7,7 @@ open DSharpPlus.Entities
|
|||||||
open DSharpPlus.EventArgs
|
open DSharpPlus.EventArgs
|
||||||
open DSharpPlus.SlashCommands
|
open DSharpPlus.SlashCommands
|
||||||
open Degenz
|
open Degenz
|
||||||
open Degenz.Shared
|
open Degenz.Messaging
|
||||||
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)
|
||||||
@ -26,8 +25,8 @@ let checkIfPlayerIsAttackingThemselves defender attacker =
|
|||||||
|
|
||||||
let checkForExistingHack defenderId attacker =
|
let checkForExistingHack defenderId attacker =
|
||||||
attacker.Actions
|
attacker.Actions
|
||||||
|> removeExpiredActions
|
|> Player.removeExpiredActions
|
||||||
|> getAttacksFlat
|
|> Player.getAttacksFlat
|
||||||
|> Array.tryFind (fun (_,t,_) -> t.Id = defenderId)
|
|> Array.tryFind (fun (_,t,_) -> t.Id = defenderId)
|
||||||
|> function
|
|> function
|
||||||
| Some ( atk , target , _ ) ->
|
| Some ( atk , target , _ ) ->
|
||||||
@ -47,7 +46,7 @@ let checkIfHackHasCooldown hackId attacker =
|
|||||||
Ok attacker
|
Ok attacker
|
||||||
else
|
else
|
||||||
let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(5)) mostRecentHackAttack
|
let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(5)) mostRecentHackAttack
|
||||||
let item = armoury |> Array.find (fun i -> i.Id = hackId)
|
let item = Armoury.battleItems |> Array.find (fun i -> i.Id = hackId)
|
||||||
Error $"{item.Name} is currently on cooldown, wait {cooldown} to use it again."
|
Error $"{item.Name} is currently on cooldown, wait {cooldown} to use it again."
|
||||||
|
|
||||||
let checkIfInventoryIsEmpty attacker =
|
let checkIfInventoryIsEmpty attacker =
|
||||||
@ -62,8 +61,8 @@ let calculateDamage (hack : BattleItem) (shield : BattleItem) =
|
|||||||
|
|
||||||
let runHackerBattle defender hack =
|
let runHackerBattle defender hack =
|
||||||
Player.defenses defender
|
Player.defenses defender
|
||||||
|> removeExpiredActions
|
|> Player.removeExpiredActions
|
||||||
|> Array.map (fun dfn -> armoury |> Array.find (fun w -> w.Id = dfn.ActionId))
|
|> Array.map (fun dfn -> Armoury.battleItems |> Array.find (fun w -> w.Id = dfn.ActionId))
|
||||||
|> Array.map (calculateDamage (hack))
|
|> Array.map (calculateDamage (hack))
|
||||||
|> Array.contains Weak
|
|> Array.contains Weak
|
||||||
|
|
||||||
@ -74,7 +73,7 @@ let updateCombatants attacker defender hack prize =
|
|||||||
let attack = { ActionId = int hack ; Type = Attack { Target = target ; Result = 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 <| Player.modifyBank defender -prize ]
|
||||||
|> Async.Parallel
|
|> Async.Parallel
|
||||||
|> Async.Ignore
|
|> Async.Ignore
|
||||||
|
|
||||||
@ -115,11 +114,10 @@ let failedHack (event : ComponentInteractionCreateEventArgs) attacker defender h
|
|||||||
}
|
}
|
||||||
|
|
||||||
let attack (ctx : InteractionContext) (target : DiscordUser) =
|
let attack (ctx : InteractionContext) (target : DiscordUser) =
|
||||||
async {
|
Game.executePlayerAction ctx (fun attacker -> async {
|
||||||
let! attacker = DbService.tryFindPlayer ctx.Member.Id
|
|
||||||
let! defender = DbService.tryFindPlayer target.Id
|
let! defender = DbService.tryFindPlayer target.Id
|
||||||
match attacker , defender with
|
match defender with
|
||||||
| Some attacker , Some defender ->
|
| Some defender ->
|
||||||
let hackAttempt =
|
let hackAttempt =
|
||||||
checkForExistingHack defender.DiscordId attacker
|
checkForExistingHack defender.DiscordId attacker
|
||||||
|> Result.bind checkIfInventoryIsEmpty
|
|> Result.bind checkIfInventoryIsEmpty
|
||||||
@ -136,10 +134,8 @@ let attack (ctx : InteractionContext) (target : DiscordUser) =
|
|||||||
.AsEphemeral(true)
|
.AsEphemeral(true)
|
||||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
| None , _ -> do! notYetAHackerMsg ctx
|
| None -> do! sendSimpleResponse ctx "Your target is not connected to the network, they must join first by using the /redpill command"
|
||||||
| _ , None -> do! createSimpleResponseAsync "Your target is not connected to the network, they must join first by using the /redpill command" ctx
|
})
|
||||||
} |> Async.StartAsTask
|
|
||||||
:> Task
|
|
||||||
|
|
||||||
let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
||||||
async {
|
async {
|
||||||
@ -155,7 +151,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
|||||||
|> Result.bind (checkIfHackHasCooldown (int hack))
|
|> Result.bind (checkIfHackHasCooldown (int hack))
|
||||||
|> function
|
|> function
|
||||||
| Ok _ ->
|
| Ok _ ->
|
||||||
runHackerBattle defender (getItemFromArmoury <| int hack)
|
runHackerBattle defender (Armoury.getItem (int hack))
|
||||||
|> function
|
|> function
|
||||||
| false -> successfulHack event attacker defender hack
|
| false -> successfulHack event attacker defender hack
|
||||||
| true -> failedHack event attacker defender hack
|
| true -> failedHack event attacker defender hack
|
||||||
@ -175,10 +171,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
|||||||
}
|
}
|
||||||
|
|
||||||
let defend (ctx : InteractionContext) =
|
let defend (ctx : InteractionContext) =
|
||||||
async {
|
Game.executePlayerAction ctx (fun player -> async {
|
||||||
let! player = DbService.tryFindPlayer ctx.Member.Id
|
|
||||||
match player with
|
|
||||||
| Some player ->
|
|
||||||
if Player.shields 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)
|
||||||
@ -189,9 +182,7 @@ let defend (ctx : InteractionContext) =
|
|||||||
builder.AsEphemeral true |> ignore
|
builder.AsEphemeral true |> ignore
|
||||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
| None -> do! notYetAHackerMsg ctx
|
})
|
||||||
} |> Async.StartAsTask
|
|
||||||
:> Task
|
|
||||||
|
|
||||||
let handleDefense (event : ComponentInteractionCreateEventArgs) =
|
let handleDefense (event : ComponentInteractionCreateEventArgs) =
|
||||||
async {
|
async {
|
||||||
@ -200,7 +191,7 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
|
|||||||
let! playerResult = DbService.tryFindPlayer event.User.Id
|
let! playerResult = DbService.tryFindPlayer event.User.Id
|
||||||
match playerResult with
|
match playerResult with
|
||||||
| Some player ->
|
| Some player ->
|
||||||
let updatedDefenses = Player.defenses player |> removeExpiredActions
|
let updatedDefenses = Player.defenses player |> Player.removeExpiredActions
|
||||||
let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.ActionId = int shield)
|
let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.ActionId = int shield)
|
||||||
|
|
||||||
match alreadyUsedShield , updatedDefenses.Length < 2 with
|
match alreadyUsedShield , updatedDefenses.Length < 2 with
|
||||||
|
@ -5,15 +5,15 @@ open DSharpPlus.Entities
|
|||||||
open DSharpPlus
|
open DSharpPlus
|
||||||
open DSharpPlus.SlashCommands
|
open DSharpPlus.SlashCommands
|
||||||
open Degenz.Store
|
open Degenz.Store
|
||||||
open Degenz.Shared
|
open Degenz.Types
|
||||||
|
|
||||||
module Commands =
|
module Commands =
|
||||||
let newPlayer nickname (membr : uint64) =
|
let newPlayer nickname (membr : uint64) =
|
||||||
let rand = System.Random(System.Guid.NewGuid().GetHashCode())
|
let rand = System.Random(System.Guid.NewGuid().GetHashCode())
|
||||||
let randHack = rand.Next(0, 3)
|
let randHack = rand.Next(0, 3)
|
||||||
let randShield = rand.Next(6, 9)
|
let randShield = rand.Next(6, 9)
|
||||||
let hack = armoury |> Array.find (fun i -> i.Id = randHack)
|
let hack = Armoury.battleItems |> Array.find (fun i -> i.Id = randHack)
|
||||||
let shield = armoury |> Array.find (fun i -> i.Id = randShield)
|
let shield = Armoury.battleItems |> Array.find (fun i -> i.Id = randShield)
|
||||||
|
|
||||||
{ DiscordId = membr
|
{ DiscordId = membr
|
||||||
Name = nickname
|
Name = nickname
|
||||||
@ -72,21 +72,16 @@ module Commands =
|
|||||||
// :> Task
|
// :> Task
|
||||||
|
|
||||||
let status (ctx : InteractionContext) =
|
let status (ctx : InteractionContext) =
|
||||||
async {
|
Game.executePlayerAction ctx (fun player -> async {
|
||||||
let! maybePlayer = DbService.tryFindPlayer ctx.Member.Id
|
let updatedActions = Player.removeExpiredActions player.Actions
|
||||||
match maybePlayer with
|
|
||||||
| Some player ->
|
|
||||||
let updatedActions = removeExpiredActions player.Actions
|
|
||||||
let updatedPlayer = { player with Actions = updatedActions }
|
let updatedPlayer = { player with Actions = updatedActions }
|
||||||
let builder = DiscordInteractionResponseBuilder()
|
let builder = DiscordInteractionResponseBuilder()
|
||||||
builder.IsEphemeral <- true
|
builder.IsEphemeral <- true
|
||||||
builder.Content <- statusFormat updatedPlayer
|
builder.Content <- Messaging.statusFormat updatedPlayer
|
||||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
do! DbService.updatePlayer updatedPlayer
|
do! DbService.updatePlayer updatedPlayer
|
||||||
| None -> do! notYetAHackerMsg ctx
|
})
|
||||||
} |> Async.StartAsTask
|
|
||||||
:> Task
|
|
||||||
|
|
||||||
type PlayerInteractions() =
|
type PlayerInteractions() =
|
||||||
inherit ApplicationCommandModule ()
|
inherit ApplicationCommandModule ()
|
||||||
|
@ -5,7 +5,7 @@ open System.Threading.Tasks
|
|||||||
open DSharpPlus
|
open DSharpPlus
|
||||||
open DSharpPlus.Entities
|
open DSharpPlus.Entities
|
||||||
open DSharpPlus.SlashCommands
|
open DSharpPlus.SlashCommands
|
||||||
open Degenz.Shared
|
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" |]
|
let slots = [| "https://i.ibb.co/pKqZdr7/cherry.png" ; "https://i.ibb.co/JnghQsL/lemon.jpg" ; "https://i.ibb.co/1JTFPSs/seven.png" |]
|
||||||
|
|
||||||
@ -13,11 +13,8 @@ type SlotMachine() =
|
|||||||
inherit ApplicationCommandModule ()
|
inherit ApplicationCommandModule ()
|
||||||
|
|
||||||
[<SlashCommand("spin", "Want to try your luck?")>]
|
[<SlashCommand("spin", "Want to try your luck?")>]
|
||||||
member this.AttackCommand (ctx : InteractionContext) =
|
member this.Spin (ctx : InteractionContext) =
|
||||||
async {
|
Game.executePlayerAction ctx (fun player -> async {
|
||||||
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
|
|
||||||
match playerResult with
|
|
||||||
| Some player ->
|
|
||||||
let sleepTime = 1000
|
let sleepTime = 1000
|
||||||
let random = Random(System.Guid.NewGuid().GetHashCode())
|
let random = Random(System.Guid.NewGuid().GetHashCode())
|
||||||
let results = [ random.Next(0, 3) ; random.Next(0, 3) ; random.Next(0, 3)]
|
let results = [ random.Next(0, 3) ; random.Next(0, 3) ; random.Next(0, 3)]
|
||||||
@ -73,8 +70,5 @@ type SlotMachine() =
|
|||||||
do! ctx.Interaction.CreateFollowupMessageAsync(builder)
|
do! ctx.Interaction.CreateFollowupMessageAsync(builder)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|> Async.Ignore
|
|> Async.Ignore
|
||||||
|
})
|
||||||
|
|
||||||
| None -> do! notYetAHackerMsg ctx
|
|
||||||
|
|
||||||
} |> Async.StartAsTask
|
|
||||||
:> Task
|
|
||||||
|
86
Bot/Store.fs
86
Bot/Store.fs
@ -8,54 +8,27 @@ open DSharpPlus.EventArgs
|
|||||||
open DSharpPlus.SlashCommands
|
open DSharpPlus.SlashCommands
|
||||||
open Degenz
|
open Degenz
|
||||||
open Degenz.Embeds
|
open Degenz.Embeds
|
||||||
open Degenz.Shared
|
open Degenz.Messaging
|
||||||
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 {
|
||||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, Embeds.storeListing armoury)
|
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, Embeds.storeListing Armoury.battleItems)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
} |> Async.StartAsTask
|
} |> Async.StartAsTask
|
||||||
:> Task
|
:> Task
|
||||||
|
|
||||||
let buyItem (ctx : InteractionContext) itemId =
|
let buyItem (ctx : InteractionContext) itemType =
|
||||||
async {
|
Game.executePlayerAction ctx (fun player -> async {
|
||||||
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
|
let embed = DiscordEmbedBuilder()
|
||||||
let item = armoury |> Array.find (fun w -> w.Id = itemId)
|
|
||||||
match playerResult with
|
|
||||||
| Some player ->
|
|
||||||
let newBalance = player.Bank - item.Cost
|
|
||||||
if newBalance >= 0<GBT> then
|
|
||||||
let playerHasItem = player.Arsenal |> Array.exists (fun w -> item.Id = w.Id)
|
|
||||||
if not playerHasItem then
|
|
||||||
let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal }
|
|
||||||
do! DbService.updatePlayer p
|
|
||||||
do! createSimpleResponseAsync $"Successfully purchased {item.Name}! You now have {newBalance} remaining" ctx
|
|
||||||
else
|
|
||||||
do! createSimpleResponseAsync $"You already own this item!" ctx
|
|
||||||
else
|
|
||||||
do! createSimpleResponseAsync $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" ctx
|
|
||||||
| None -> do! notYetAHackerMsg ctx
|
|
||||||
} |> Async.StartAsTask
|
|
||||||
:> Task
|
|
||||||
|
|
||||||
let constructItemButtons playerInfo itemType (items : 'a array) =
|
// embed.Fields
|
||||||
items
|
|
||||||
|> Seq.map (fun item -> DiscordButtonComponent(ButtonStyle.Primary, $"{playerInfo}-{itemType}-{item}", $"{item}"))
|
do! ctx.CreateResponseAsync(embed, true)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
})
|
||||||
|
|
||||||
let sell (ctx : InteractionContext) =
|
let sell (ctx : InteractionContext) =
|
||||||
async {
|
Game.executePlayerAction ctx (fun player -> async {
|
||||||
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
|
|
||||||
match playerResult with
|
|
||||||
| Some player ->
|
|
||||||
let hasInventoryToSell = Array.length player.Arsenal > 0
|
let hasInventoryToSell = Array.length player.Arsenal > 0
|
||||||
if hasInventoryToSell then
|
if hasInventoryToSell then
|
||||||
let builder = DiscordInteractionResponseBuilder()
|
let builder = DiscordInteractionResponseBuilder()
|
||||||
@ -74,17 +47,12 @@ let sell (ctx : InteractionContext) =
|
|||||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
else
|
else
|
||||||
do! createSimpleResponseAsync "You currently have no inventory to sell" ctx
|
do! sendSimpleResponse ctx "You currently have no inventory to sell"
|
||||||
| None -> do! notYetAHackerMsg ctx
|
})
|
||||||
return ()
|
|
||||||
} |> Async.StartAsTask
|
|
||||||
:> Task
|
|
||||||
|
|
||||||
let updateArsenal player salePrice updatedArsenal = { player with Bank = player.Bank + salePrice ; Arsenal = updatedArsenal }
|
|
||||||
|
|
||||||
let sellItem (event : ComponentInteractionCreateEventArgs) player itemId =
|
let sellItem (event : ComponentInteractionCreateEventArgs) player itemId =
|
||||||
async {
|
async {
|
||||||
let item = armoury |> Array.find (fun i -> i.Id = itemId)
|
let item = Armoury.battleItems |> Array.find (fun i -> i.Id = itemId)
|
||||||
let updatedPlayer = { player with Bank = player.Bank + item.Cost ; Arsenal = player.Arsenal |> Array.filter (fun w -> w.Id <> itemId)}
|
let updatedPlayer = { player with Bank = player.Bank + item.Cost ; Arsenal = player.Arsenal |> Array.filter (fun w -> w.Id <> itemId)}
|
||||||
do! DbService.updatePlayer updatedPlayer
|
do! DbService.updatePlayer updatedPlayer
|
||||||
let builder = DiscordInteractionResponseBuilder()
|
let builder = DiscordInteractionResponseBuilder()
|
||||||
@ -94,6 +62,22 @@ let sellItem (event : ComponentInteractionCreateEventArgs) player itemId =
|
|||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let handleBuyItem (ctx : InteractionContext) itemId =
|
||||||
|
Game.executePlayerAction ctx (fun player -> async {
|
||||||
|
let item = Armoury.battleItems |> Array.find (fun w -> w.Id = itemId)
|
||||||
|
let newBalance = player.Bank - item.Cost
|
||||||
|
if newBalance >= 0<GBT> then
|
||||||
|
let playerHasItem = player.Arsenal |> Array.exists (fun w -> item.Id = w.Id)
|
||||||
|
if not playerHasItem then
|
||||||
|
let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal }
|
||||||
|
do! DbService.updatePlayer p
|
||||||
|
do! sendSimpleResponse ctx $"Successfully purchased {item.Name}! You now have {newBalance} remaining"
|
||||||
|
else
|
||||||
|
do! sendSimpleResponse ctx $"You already own this item!"
|
||||||
|
else
|
||||||
|
do! sendSimpleResponse ctx $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT"
|
||||||
|
})
|
||||||
|
|
||||||
let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
|
let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
|
||||||
async {
|
async {
|
||||||
let! playerResult = DbService.tryFindPlayer event.User.Id
|
let! playerResult = DbService.tryFindPlayer event.User.Id
|
||||||
@ -112,17 +96,15 @@ let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCrea
|
|||||||
|
|
||||||
type Store() =
|
type Store() =
|
||||||
inherit ApplicationCommandModule ()
|
inherit ApplicationCommandModule ()
|
||||||
|
// [<SlashCommand("view-store", "View items available for purchase")>]
|
||||||
|
|
||||||
[<SlashCommand("view-store", "View items available for purchase")>]
|
// member _.ViewStore (ctx : InteractionContext) = viewStore ctx
|
||||||
member _.ViewStore (ctx : InteractionContext) = viewStore ctx
|
|
||||||
|
|
||||||
[<SlashCommand("buy-hack", "Purchase a hack attack you can use to earn GoodBoyTokenz")>]
|
[<SlashCommand("buy-hack", "Purchase a hack attack you can use to earn GoodBoyTokenz")>]
|
||||||
member _.BuyHack (ctx : InteractionContext, [<Option("hack-id", "The ID of the hack you wish to purchase")>] hackId : HackId) =
|
member _.BuyHack (ctx : InteractionContext) = buyItem ctx Hack
|
||||||
buyItem ctx (int hackId)
|
|
||||||
|
|
||||||
[<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GoodBoyTokenz")>]
|
[<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GoodBoyTokenz")>]
|
||||||
member this.BuyShield (ctx : InteractionContext, [<Option("shield-id", "The ID of the shield you wish to purchase")>] shieldId : ShieldId) =
|
member this.BuyShield (ctx : InteractionContext) = buyItem ctx Shield
|
||||||
buyItem ctx (int shieldId)
|
|
||||||
|
|
||||||
[<SlashCommand("sell", "Sell an item in your inventory for GoodBoyTokenz")>]
|
[<SlashCommand("sell", "Sell an item in your inventory for GoodBoyTokenz")>]
|
||||||
member this.SellItem (ctx : InteractionContext) = sell ctx
|
member this.SellItem (ctx : InteractionContext) = sell ctx
|
||||||
|
@ -5,11 +5,11 @@ open DSharpPlus
|
|||||||
open DSharpPlus.Entities
|
open DSharpPlus.Entities
|
||||||
open DSharpPlus.EventArgs
|
open DSharpPlus.EventArgs
|
||||||
open DSharpPlus.SlashCommands
|
open DSharpPlus.SlashCommands
|
||||||
open Degenz.Shared
|
open Degenz.Types
|
||||||
open Degenz.Store
|
open Degenz.Messaging
|
||||||
|
|
||||||
let defaultHack = armoury |> Array.find (fun i -> i.Id = int HackId.Virus)
|
let defaultHack = Armoury.battleItems |> Array.find (fun i -> i.Id = int HackId.Virus)
|
||||||
let defaultShield = armoury |> Array.find (fun i -> i.Id = int ShieldId.Firewall)
|
let defaultShield = Armoury.battleItems |> Array.find (fun i -> i.Id = int ShieldId.Firewall)
|
||||||
|
|
||||||
let sendInitialEmbed (client : DiscordClient) =
|
let sendInitialEmbed (client : DiscordClient) =
|
||||||
async {
|
async {
|
||||||
@ -41,10 +41,10 @@ let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) =
|
|||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
match maybePlayer with
|
match maybePlayer with
|
||||||
| Some _ ->
|
| Some _ ->
|
||||||
do! Message.sendFollowUpMessageWithButton event step1Msg
|
do! sendFollowUpMessageWithButton event step1Msg
|
||||||
| None ->
|
| None ->
|
||||||
let msg = "Looks like an error occurred, you're not a registered degenerate. Please contact a moderator."
|
let msg = "Looks like an error occurred, you're not a registered degenerate. Please contact a moderator."
|
||||||
do! Message.sendFollowUpMessage event msg
|
do! sendFollowUpMessage event msg
|
||||||
}
|
}
|
||||||
|
|
||||||
let handleTrainerStep2 (event : ComponentInteractionCreateEventArgs) =
|
let handleTrainerStep2 (event : ComponentInteractionCreateEventArgs) =
|
||||||
@ -53,19 +53,19 @@ let handleTrainerStep2 (event : ComponentInteractionCreateEventArgs) =
|
|||||||
match result with
|
match result with
|
||||||
| Some player ->
|
| Some player ->
|
||||||
let weaponName = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
|
let weaponName = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
|
||||||
do! Message.updateMessageWithGreyedOutButtons event step1Msg
|
do! updateMessageWithGreyedOutButtons event step1Msg
|
||||||
|
|
||||||
let shieldMessage =
|
let shieldMessage =
|
||||||
if Player.shields player |> Array.isEmpty
|
if Player.shields player |> Array.isEmpty
|
||||||
then $"You do not have any Shields in your arsenal, here's a {defaultShield.Name} you can use for now"
|
then $"You do not have any Shields in your arsenal, here's a {defaultShield.Name} you can use for now"
|
||||||
else $"You currently have {weaponName} in your arsenal"
|
else $"You currently have {weaponName} in your arsenal"
|
||||||
|
|
||||||
do! Message.sendFollowUpMessage event
|
do! sendFollowUpMessage 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. "
|
||||||
+ $"{shieldMessage}. To enable it and protect your system, you can use the `/defend` slash command to choose a shield."
|
+ $"{shieldMessage}. To enable it and protect your system, you can use the `/defend` slash command to choose a shield."
|
||||||
+ $"\n\nRun the `/defend` command now and then select '{weaponName}'.")
|
+ $"\n\nRun the `/defend` command now and then select '{weaponName}'.")
|
||||||
| None ->
|
| None ->
|
||||||
do! Message.sendFollowUpMessage event $"Something went wrong, please contact a moderator"
|
do! sendFollowUpMessage event $"Something went wrong, please contact a moderator"
|
||||||
}
|
}
|
||||||
|
|
||||||
let defend (ctx : InteractionContext) =
|
let defend (ctx : InteractionContext) =
|
||||||
@ -100,7 +100,7 @@ let handleDefenseMsg = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let handleDefense (event : ComponentInteractionCreateEventArgs) =
|
let handleDefense (event : ComponentInteractionCreateEventArgs) =
|
||||||
let sendMessage' = Message.sendFollowUpMessage event
|
let sendMessage' = sendFollowUpMessage event
|
||||||
async {
|
async {
|
||||||
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
@ -116,7 +116,7 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
|
|||||||
do! Async.Sleep 4000
|
do! Async.Sleep 4000
|
||||||
do! sendMessage' $"Hacking attempt failed! {player.Name} defended hack from Degenz-Trainer and took {prize} from them! "
|
do! sendMessage' $"Hacking attempt failed! {player.Name} defended hack from Degenz-Trainer and took {prize} from them! "
|
||||||
do! Async.Sleep 3000
|
do! Async.Sleep 3000
|
||||||
do! Message.sendFollowUpMessageWithButton event handleDefenseMsg
|
do! sendFollowUpMessageWithButton event handleDefenseMsg
|
||||||
| None ->
|
| None ->
|
||||||
do! sendMessage' $"Something went wrong, please contact a moderator"
|
do! sendMessage' $"Something went wrong, please contact a moderator"
|
||||||
}
|
}
|
||||||
@ -127,19 +127,19 @@ let handleTrainerStep4 (event : ComponentInteractionCreateEventArgs) =
|
|||||||
match result with
|
match result with
|
||||||
| Some player ->
|
| Some player ->
|
||||||
let weaponName = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
|
let weaponName = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
|
||||||
do! Message.updateMessageWithGreyedOutButtons event handleDefenseMsg
|
do! updateMessageWithGreyedOutButtons event handleDefenseMsg
|
||||||
|
|
||||||
let hackMessage =
|
let hackMessage =
|
||||||
if Player.shields player |> Array.isEmpty
|
if Player.shields player |> Array.isEmpty
|
||||||
then $"You do not have any Hacks in your arsenal, here's a {defaultHack.Name} you can use for now"
|
then $"You do not have any Hacks in your arsenal, here's a {defaultHack.Name} you can use for now"
|
||||||
else $"You currently have {weaponName} in your arsenal"
|
else $"You currently have {weaponName} in your arsenal"
|
||||||
|
|
||||||
do! Message.sendFollowUpMessage event
|
do! sendFollowUpMessage event
|
||||||
($"Next why don't you try hacking me. {hackMessage}. To hack me and get some money, "
|
($"Next why don't you try hacking me. {hackMessage}. 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."
|
||||||
+ $"\n\nRun the `/hack` command now and pick me as your target, then click on the '{weaponName}' button.")
|
+ $"\n\nRun the `/hack` command now and pick me as your target, then click on the '{weaponName}' button.")
|
||||||
| None ->
|
| None ->
|
||||||
do! Message.sendInteractionEvent event $"Something went wrong, please contact a moderator"
|
do! sendInteractionEvent event $"Something went wrong, please contact a moderator"
|
||||||
}
|
}
|
||||||
|
|
||||||
let attack (ctx : InteractionContext) (target : DiscordUser) =
|
let attack (ctx : InteractionContext) (target : DiscordUser) =
|
||||||
@ -184,7 +184,7 @@ let handleAttackMsg = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
||||||
let sendMessage' = Message.sendFollowUpMessage event
|
let sendMessage' = sendFollowUpMessage event
|
||||||
async {
|
async {
|
||||||
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
@ -200,7 +200,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
|||||||
do! sendMessage' ("Look at that, you are now officially an elite haxor! By successfully hacking other people you can earn GoodBoyTokenz. "
|
do! sendMessage' ("Look at that, you are now officially an elite haxor! By successfully hacking other people you can earn GoodBoyTokenz. "
|
||||||
+ "Hacks take time to recover so check back in later once you've used all your hacks.")
|
+ "Hacks take time to recover so check back in later once you've used all your hacks.")
|
||||||
do! Async.Sleep 7000
|
do! Async.Sleep 7000
|
||||||
do! Message.sendFollowUpMessageWithButton event handleAttackMsg
|
do! sendFollowUpMessageWithButton event handleAttackMsg
|
||||||
| None ->
|
| None ->
|
||||||
do! sendMessage' $"Something went wrong, please contact a moderator"
|
do! sendMessage' $"Something went wrong, please contact a moderator"
|
||||||
}
|
}
|
||||||
@ -214,7 +214,7 @@ let handleTrainerStep6 (event : ComponentInteractionCreateEventArgs) =
|
|||||||
let builder = DiscordFollowupMessageBuilder()
|
let builder = DiscordFollowupMessageBuilder()
|
||||||
builder.IsEphemeral <- true
|
builder.IsEphemeral <- true
|
||||||
builder.Content <- "Get out of here!"
|
builder.Content <- "Get out of here!"
|
||||||
do! Message.updateMessageWithGreyedOutButtons event handleAttackMsg
|
do! updateMessageWithGreyedOutButtons event handleAttackMsg
|
||||||
do! event.Interaction.CreateFollowupMessageAsync(builder)
|
do! event.Interaction.CreateFollowupMessageAsync(builder)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|> Async.Ignore
|
|> Async.Ignore
|
||||||
@ -230,6 +230,6 @@ let handleButtonEvent (event : ComponentInteractionCreateEventArgs) =
|
|||||||
| 4 -> do! handleTrainerStep4 event
|
| 4 -> do! handleTrainerStep4 event
|
||||||
| 5 -> do! handleAttack event
|
| 5 -> do! handleAttack event
|
||||||
| 6 -> do! handleTrainerStep6 event
|
| 6 -> do! handleTrainerStep6 event
|
||||||
| _ -> do! Message.sendFollowUpMessage event "No action found"
|
| _ -> do! sendFollowUpMessage event "No action found"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
open System
|
open System
|
||||||
open System.Collections.Generic
|
open System.Collections.Generic
|
||||||
open Degenz.Shared
|
open System.Threading.Tasks
|
||||||
|
open Degenz.Types
|
||||||
open MongoDB.Bson
|
open MongoDB.Bson
|
||||||
open MongoDB.Bson.Serialization
|
open MongoDB.Bson.Serialization
|
||||||
open MongoDB.Driver
|
open MongoDB.Driver
|
||||||
@ -62,7 +63,7 @@ let private playerMap (player : PlayerData) = {
|
|||||||
let private mapBack (player : PlayerEntry) : PlayerData = {
|
let private mapBack (player : PlayerEntry) : PlayerData = {
|
||||||
DiscordId = player.DiscordId
|
DiscordId = player.DiscordId
|
||||||
Name = player.Name
|
Name = player.Name
|
||||||
Arsenal = player.Arsenal |> Array.map (fun w -> armoury |> Array.find (fun w' -> w = w'.Id))
|
Arsenal = player.Arsenal |> Array.map (fun w -> Armoury.battleItems |> Array.find (fun w' -> w = w'.Id))
|
||||||
Actions =
|
Actions =
|
||||||
let atks = player.Attacks |> Array.map attackToAction
|
let atks = player.Attacks |> Array.map attackToAction
|
||||||
let dfns = player.Defenses |> Array.map defenseToAction
|
let dfns = player.Defenses |> Array.map defenseToAction
|
||||||
@ -113,6 +114,7 @@ let updatePlayer (player : PlayerData) =
|
|||||||
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
|
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//let getTopPlayers amount =
|
//let getTopPlayers amount =
|
||||||
// async {
|
// async {
|
||||||
// return! players.FindAsync()
|
// return! players.FindAsync()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
module Degenz.Shared
|
namespace Degenz
|
||||||
|
|
||||||
open System
|
open System
|
||||||
open DSharpPlus
|
open DSharpPlus
|
||||||
@ -7,6 +7,9 @@ open DSharpPlus.EventArgs
|
|||||||
open DSharpPlus.SlashCommands
|
open DSharpPlus.SlashCommands
|
||||||
open Newtonsoft.Json
|
open Newtonsoft.Json
|
||||||
|
|
||||||
|
[<Microsoft.FSharp.Core.AutoOpen>]
|
||||||
|
module Types =
|
||||||
|
|
||||||
[<Measure>]
|
[<Measure>]
|
||||||
type mins
|
type mins
|
||||||
|
|
||||||
@ -73,6 +76,14 @@ type PlayerData =
|
|||||||
Actions : Action array
|
Actions : Action array
|
||||||
Bank : int<GBT> }
|
Bank : int<GBT> }
|
||||||
|
|
||||||
|
|
||||||
|
module Armoury =
|
||||||
|
let battleItems =
|
||||||
|
let file = System.IO.File.ReadAllText("Items.json")
|
||||||
|
JsonConvert.DeserializeObject<BattleItem array>(file)
|
||||||
|
|
||||||
|
let getItem itemId = battleItems |> Array.find (fun w -> w.Id = itemId)
|
||||||
|
|
||||||
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 = Shield)
|
let shields player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Shield)
|
||||||
@ -81,24 +92,24 @@ module Player =
|
|||||||
|> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | 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 removeExpiredActions actions =
|
||||||
|
actions
|
||||||
|
|> Array.filter (fun (act : Action) ->
|
||||||
|
let item = Armoury.battleItems |> Array.find (fun w -> w.Id = act.ActionId)
|
||||||
|
DateTime.UtcNow - act.Timestamp < TimeSpan.FromMinutes(int item.Cooldown))
|
||||||
|
|
||||||
|
let modifyBank player amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
|
||||||
|
|
||||||
let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | 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) =
|
|
||||||
async {
|
|
||||||
let builder = DiscordInteractionResponseBuilder()
|
|
||||||
builder.Content <- msg
|
|
||||||
builder.AsEphemeral true |> ignore
|
|
||||||
|
|
||||||
do!
|
module Messaging =
|
||||||
ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
type InteractiveMessage = {
|
||||||
|> Async.AwaitTask
|
ButtonId : string
|
||||||
|
ButtonText : string
|
||||||
|
Message : string
|
||||||
}
|
}
|
||||||
|
|
||||||
let notYetAHackerMsg =
|
|
||||||
createSimpleResponseAsync "You are currently not a hacker, first use the /redpill command to become one"
|
|
||||||
|
|
||||||
let hackDescription = ""
|
|
||||||
|
|
||||||
let statusFormat p =
|
let statusFormat p =
|
||||||
$"Hacks: {Player.hacks p |> Array.toList}
|
$"Hacks: {Player.hacks p |> Array.toList}
|
||||||
Shields: {Player.shields p |> Array.toList}
|
Shields: {Player.shields p |> Array.toList}
|
||||||
@ -110,19 +121,15 @@ let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleI
|
|||||||
weapons
|
weapons
|
||||||
|> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Success, $"{actionType}-{w.Id}-{playerInfo}", $"{w.Name}"))
|
|> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Success, $"{actionType}-{w.Id}-{playerInfo}", $"{w.Name}"))
|
||||||
|
|
||||||
let modifyPlayerBank player amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
|
let sendSimpleResponse (ctx: InteractionContext) msg =
|
||||||
|
async {
|
||||||
let armoury =
|
let builder = DiscordInteractionResponseBuilder()
|
||||||
let file = System.IO.File.ReadAllText("Items.json")
|
builder.Content <- msg
|
||||||
JsonConvert.DeserializeObject<BattleItem array>(file)
|
builder.AsEphemeral true |> ignore
|
||||||
|
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||||
type InteractiveMessage = {
|
|> Async.AwaitTask
|
||||||
ButtonId : string
|
|
||||||
ButtonText : string
|
|
||||||
Message : string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module Message =
|
|
||||||
let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
|
let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
|
||||||
async {
|
async {
|
||||||
let builder = DiscordFollowupMessageBuilder()
|
let builder = DiscordFollowupMessageBuilder()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user