197 lines
6.3 KiB
Forth

namespace Degenz
open System
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Newtonsoft.Json
[<Microsoft.FSharp.Core.AutoOpen>]
module ResultHelpers =
let (>>=) x f = Result.bind f x
[<Microsoft.FSharp.Core.AutoOpen>]
module Types =
[<Measure>]
type mins
[<Measure>]
type GBT
type BattleClass =
| Network
| Exploit
| Penetration
type HackId =
| Virus = 0
| RemoteAccess = 1
| Worm = 2
type ShieldId =
| Firewall = 6
| Encryption = 7
| Cypher = 8
type ItemType =
| Hack
| Shield
type BattleItem = {
Id : int
Name : string
Cost : int<GBT>
Type : ItemType
Class : BattleClass
Power : int
Cooldown : int<mins>
}
type HackResult =
| Strong
| Weak
[<CLIMutable>]
type DiscordPlayer = { Id: uint64; Name: string }
[<CLIMutable>]
type AttackResult = {
Result : bool
Target : DiscordPlayer
}
type ActionType =
| Attack of AttackResult
| Defense
[<CLIMutable>]
type Action =
{ ActionId : int
Type : ActionType
Timestamp : DateTime }
[<CLIMutable>]
type PlayerData =
{ DiscordId : uint64
Name : string
Arsenal : BattleItem array
Actions : Action array
Bank : int<GBT> }
module Armory =
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 Messaging =
type InteractiveMessage = {
ButtonId : string
ButtonText : string
Message : string
}
let getTimeTillCooldownFinishes (timespan : TimeSpan) timestamp =
let remaining = timespan - (DateTime.UtcNow - timestamp)
let plural amount = if amount = 1 then "" else "s"
let ``and`` = if remaining.Hours > 0 then "and " else ""
let hours = if remaining.Hours > 0 then $"{remaining.Hours} hour{plural remaining.Hours} {``and``}" else String.Empty
let totalMins = remaining.Minutes + 1
let minutes = if totalMins > 0 then $"{totalMins} minute{plural totalMins}" else "1 minute"
$"{hours}{minutes}"
let battleItemFormat (items : BattleItem array) =
match items with
| [||] -> "None"
| _ -> items |> Array.toList |> List.map (fun i -> i.Name) |> String.concat ", "
let actionFormat (actions : Action array) =
match actions with
| [||] -> "None"
| _ ->
actions
|> Array.map (fun act ->
match act.Type with
| Attack atk -> $"Hacked {atk.Target.Name} at {act.Timestamp.ToShortTimeString()}"
| Defense ->
let item = Armory.getItem act.ActionId
let cooldown = getTimeTillCooldownFinishes (System.TimeSpan.FromMinutes(int item.Cooldown)) act.Timestamp
$"{item.Name} active for {cooldown}")
|> String.concat "\n"
let sendSimpleResponse (ctx: InteractionContext) msg =
async {
let builder = DiscordInteractionResponseBuilder()
builder.Content <- msg
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
async {
let builder = DiscordFollowupMessageBuilder()
builder.IsEphemeral <- true
builder.Content <- msg
do! event.Interaction.CreateFollowupMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
let 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 =
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
}
let updateMessageWithGreyedOutButtons (event : ComponentInteractionCreateEventArgs) 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
}