Move further code to Commands module

This commit is contained in:
Joseph Ferano 2022-01-11 13:49:43 +07:00
parent 248e8ed64d
commit 86354e2efe
4 changed files with 268 additions and 246 deletions

258
Commands.fs Normal file
View File

@ -0,0 +1,258 @@
module Joebot.Commands
open System
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Joebot.Types
open Joebot.Functions
let mutable players : Player list = []
let addHackerRole (ctx : InteractionContext) =
async {
for role in ctx.Guild.Roles do
if role.Value.Name = "Hacker" then
do! ctx.Member.GrantRoleAsync(role.Value)
|> Async.AwaitTask
let player = players |> List.tryFind (fun p -> int64 p.DiscordId = int64 ctx.Member.Id)
players <-
match player with
| Some _ -> players
| None -> (newPlayer ctx.Member.Id)::players
if Option.isSome player then
do! ctx.CreateResponseAsync("Already registered as an elite haxxor", true)
|> Async.AwaitTask
else
do! ctx.CreateResponseAsync("You are now an elite haxxor", true)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let removeHackerRole (ctx : InteractionContext) =
async {
for role in ctx.Member.Roles do
if role.Name = "Hacker" then
do! ctx.Member.RevokeRoleAsync(role)
|> Async.AwaitTask
players <- players |> List.filter (fun p -> p.DiscordId <> ctx.User.Id)
do! ctx.CreateResponseAsync("You are now lame", true)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let attack (ctx : InteractionContext) (target : DiscordUser) =
// TODO: We need to check if the player has any active embed hacks going, if not they can cheat
let attacker = players |> List.tryFind (fun p -> p.DiscordId = ctx.Member.Id)
let defender = players |> List.tryFind (fun p -> p.DiscordId = target.Id)
match attacker , defender with
| Some attacker , Some defender ->
let updatedAttacks = removeExpiredActions (TimeSpan.FromMinutes(15)) (fun (atk : Attack) -> atk.Timestamp) attacker.Attacks
players <-
players
|> List.map (fun p -> if p.DiscordId = attacker.DiscordId then { p with Attacks = updatedAttacks } else p)
if updatedAttacks.Length < 2 then
async {
let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick the hack you wish to use.") |> ignore
let defenderInfo = $"{defender.DiscordId}-{target.Username}"
constructButtons "Attack" defenderInfo attacker.Weapons
|> Seq.cast<DiscordComponent>
|> builder.AddComponents
|> ignore
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
else
async {
let builder = DiscordInteractionResponseBuilder()
let timeRemaining = TimeSpan.FromMinutes(15) - (DateTime.UtcNow - updatedAttacks.Head.Timestamp)
builder.Content <- $"You already hacked, please wait {timeRemaining.Minutes} minutes and {timeRemaining.Seconds} seconds to attempt another hack"
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
| None , _ -> notYetAHackerMsg ctx
| _ , None -> createSimpleResponseAsync "Your target is not connected to the network, they must join first by using the /redpill command" ctx
let defend (ctx : InteractionContext) =
players
|> List.tryFind (fun p -> p.DiscordId = ctx.Member.Id)
|> function
| Some player ->
async {
let updatedDefenses = removeExpiredActions (TimeSpan.FromMinutes(60)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses
players <-
players
|> List.map (fun p -> if p.DiscordId = player.DiscordId then { p with Defenses = updatedDefenses } else p)
let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick a defense to mount for a duration of time") |> ignore
constructButtons "Defend" (string player.DiscordId) player.Shields
|> Seq.cast<DiscordComponent>
|> builder.AddComponents
|> ignore
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
| None -> notYetAHackerMsg ctx
let status (ctx : InteractionContext) =
async {
return!
match players |> List.tryFind (fun p -> p.DiscordId = ctx.Member.Id) with
| Some player ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"%A{player}"
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
| None -> notYetAHackerMsg ctx |> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let handleAttack (event : ComponentInteractionCreateEventArgs) =
let updatePlayer amount attack p = {
p with Attacks = attack::p.Attacks
Bank = MathF.Max(p.Bank + amount, 0f)
}
async {
let split = event.Id.Split("-")
let resultHack = Weapon.TryParse(split.[1])
let ( resultId , targetId ) = UInt64.TryParse split.[2]
return!
match resultHack , resultId with
| Some weapon , true ->
let hackType = weapon
players
|> List.find (fun p -> p.DiscordId = targetId)
|> fun p -> p.Defenses
|> List.map (fun dfn -> dfn.DefenseType)
|> List.map (calculateDamage hackType)
|> List.contains Weak
|> function
| false ->
async {
let prize = 0.1726f
let attack = { HackType = hackType ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } }
players <-
players
|> List.map (fun p -> if p.DiscordId = event.User.Id then updatePlayer prize attack p else p)
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"Successfully hacked {split.[3]} using {hackType}! You just won {prize} genz!"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
|> Async.AwaitTask
let builder = DiscordMessageBuilder()
builder.WithContent($"{event.User.Username} successfully hacked <@{targetId}>!") |> ignore
let battleChannel = (event.Guild.GetChannel(927449884204867664uL))
do! battleChannel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
| true ->
async {
let builder = DiscordInteractionResponseBuilder()
let loss = -0.0623f
builder.IsEphemeral <- true
builder.Content <- $"Hack failed! {split.[3]} was able to mount a successful defense! You lost {loss} genz!"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
|> Async.AwaitTask
let attack = { HackType = hackType ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } }
players <-
players
|> List.map (fun p -> if p.DiscordId = event.User.Id then updatePlayer loss attack p else p)
let builder = DiscordMessageBuilder()
builder.WithContent($"{event.User.Username} failed to hack <@{targetId}>!") |> ignore
let battleChannel = (event.Guild.GetChannel(927449884204867664uL))
do! battleChannel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
| _ ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- "Error parsing Button Id"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
}
let handleDefense (event : ComponentInteractionCreateEventArgs) =
async {
let split = event.Id.Split("-")
return!
match Shield.TryParse(split.[1]) with
| Some shield ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"Mounted a {shield} defense for 1 hour"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
|> Async.AwaitTask
let defense = { DefenseType = shield ; Timestamp = DateTime.UtcNow }
players <-
players
|> List.map (fun p -> { p with Defenses = defense::p.Defenses })
let builder = DiscordMessageBuilder()
builder.WithContent($"{event.User.Username} has protected their system!") |> ignore
let battleChannel = (event.Guild.GetChannel(927449884204867664uL))
do! battleChannel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
| _ ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- "Error parsing Button Id"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
}
let handleButtonEvent (client : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
async {
return! match event.Id with
| id when id.StartsWith("Attack") -> handleAttack event
| id when id.StartsWith("Defend") -> handleDefense event
| _ ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"Incorrect Action identifier {event.Id}"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
} |> Async.StartAsTask
:> Task

View File

@ -1,14 +1,14 @@
module Joebot.Commands module Joebot.Functions
open System open System
open System.Threading.Tasks open System.Threading.Tasks
open DSharpPlus open DSharpPlus
open DSharpPlus.Entities open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands open DSharpPlus.SlashCommands
open Emzi0767.Utilities
open Joebot.Types open Joebot.Types
let hackDescription = ""
let newPlayer (membr : uint64) = let newPlayer (membr : uint64) =
let h1 = [| Virus ; Ransom |] let h1 = [| Virus ; Ransom |]
let h2 = [| DDos ; Worm |] let h2 = [| DDos ; Worm |]

View File

@ -10,265 +10,28 @@ open Emzi0767.Utilities
open Joebot.Types open Joebot.Types
open Joebot.Commands open Joebot.Commands
let mutable players : Player list = []
type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule () type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule ()
type JoeBot() = type JoeBot() =
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) = member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx
async {
for role in ctx.Guild.Roles do
if role.Value.Name = "Hacker" then
do! ctx.Member.GrantRoleAsync(role.Value)
|> Async.AwaitTask
let player = players |> List.tryFind (fun p -> int64 p.DiscordId = int64 ctx.Member.Id)
players <-
match player with
| Some _ -> players
| None -> (newPlayer ctx.Member.Id)::players
if Option.isSome player then
do! ctx.CreateResponseAsync("Already registered as an elite haxxor", true)
|> Async.AwaitTask
else
do! ctx.CreateResponseAsync("You are now an elite haxxor", true)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
[<SlashCommand("bluepill", "Take the bluepill and become lame")>] [<SlashCommand("bluepill", "Take the bluepill and become lame")>]
member _.RemoveHackerRole (ctx : InteractionContext) = member _.RemoveHackerRole (ctx : InteractionContext) = Commands.removeHackerRole ctx
async {
for role in ctx.Member.Roles do
if role.Name = "Hacker" then
do! ctx.Member.RevokeRoleAsync(role)
|> Async.AwaitTask
players <- players |> List.filter (fun p -> p.DiscordId <> ctx.User.Id)
do! ctx.CreateResponseAsync("You are now lame", true)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
[<SlashCommand("hack", "Send a hack attack to another player")>] [<SlashCommand("hack", "Send a hack attack to another player")>]
member this.AttackCommand (ctx : InteractionContext, [<Option("target", "The player you want to hack")>] target : DiscordUser) = member this.AttackCommand (ctx : InteractionContext, [<Option("target", "The player you want to hack")>] target : DiscordUser) =
// TODO: We need to check if the player has any active embed hacks going, if not they can cheat Commands.attack ctx target
let attacker = players |> List.tryFind (fun p -> p.DiscordId = ctx.Member.Id)
let defender = players |> List.tryFind (fun p -> p.DiscordId = target.Id)
match attacker , defender with
| Some attacker , Some defender ->
let updatedAttacks = removeExpiredActions (TimeSpan.FromMinutes(15)) (fun (atk : Attack) -> atk.Timestamp) attacker.Attacks
players <-
players
|> List.map (fun p -> if p.DiscordId = attacker.DiscordId then { p with Attacks = updatedAttacks } else p)
if updatedAttacks.Length < 2 then
async {
let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick the hack you wish to use.") |> ignore
let defenderInfo = $"{defender.DiscordId}-{target.Username}"
constructButtons "Attack" defenderInfo attacker.Weapons
|> Seq.cast<DiscordComponent>
|> builder.AddComponents
|> ignore
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
else
async {
let builder = DiscordInteractionResponseBuilder()
let timeRemaining = TimeSpan.FromMinutes(15) - (DateTime.UtcNow - updatedAttacks.Head.Timestamp)
builder.Content <- $"You already hacked, please wait {timeRemaining.Minutes} minutes and {timeRemaining.Seconds} seconds to attempt another hack"
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
| None , _ -> notYetAHackerMsg ctx
| _ , None -> createSimpleResponseAsync "Your target is not connected to the network, they must join first by using the /redpill command" ctx
[<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) = member this.DefendCommand (ctx : InteractionContext) = Commands.defend ctx
players
|> List.tryFind (fun p -> p.DiscordId = ctx.Member.Id)
|> function
| Some player ->
async {
let updatedDefenses = removeExpiredActions (TimeSpan.FromMinutes(60)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses
players <-
players
|> List.map (fun p -> if p.DiscordId = player.DiscordId then { p with Defenses = updatedDefenses } else p)
let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick a defense to mount for a duration of time") |> ignore
constructButtons "Defend" (string player.DiscordId) player.Shields
|> Seq.cast<DiscordComponent>
|> builder.AddComponents
|> ignore
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
| None -> notYetAHackerMsg 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) = member this.Status (ctx : InteractionContext) = Commands.status ctx
async {
return!
match players |> List.tryFind (fun p -> p.DiscordId = ctx.Member.Id) with
| Some player ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"%A{player}"
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
| None -> notYetAHackerMsg ctx |> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let handleAttack (event : ComponentInteractionCreateEventArgs) =
let updatePlayer amount attack p = {
p with Attacks = attack::p.Attacks
Bank = MathF.Max(p.Bank + amount, 0f)
}
async {
let split = event.Id.Split("-")
let resultHack = Weapon.TryParse(split.[1])
let ( resultId , targetId ) = UInt64.TryParse split.[2]
return!
match resultHack , resultId with
| Some weapon , true ->
let hackType = weapon
players
|> List.find (fun p -> p.DiscordId = targetId)
|> fun p -> p.Defenses
|> List.map (fun dfn -> dfn.DefenseType)
|> List.map (calculateDamage hackType)
|> List.contains Weak
|> function
| false ->
async {
let prize = 0.1726f
let attack = { HackType = hackType ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } }
players <-
players
|> List.map (fun p -> if p.DiscordId = event.User.Id then updatePlayer prize attack p else p)
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"Successfully hacked {split.[3]} using {hackType}! You just won {prize} genz!"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
|> Async.AwaitTask
let builder = DiscordMessageBuilder()
builder.WithContent($"{event.User.Username} successfully hacked <@{targetId}>!") |> ignore
let battleChannel = (event.Guild.GetChannel(927449884204867664uL))
do! battleChannel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
| true ->
async {
let builder = DiscordInteractionResponseBuilder()
let loss = -0.0623f
builder.IsEphemeral <- true
builder.Content <- $"Hack failed! {split.[3]} was able to mount a successful defense! You lost {loss} genz!"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
|> Async.AwaitTask
let attack = { HackType = hackType ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } }
players <-
players
|> List.map (fun p -> if p.DiscordId = event.User.Id then updatePlayer loss attack p else p)
let builder = DiscordMessageBuilder()
builder.WithContent($"{event.User.Username} failed to hack <@{targetId}>!") |> ignore
let battleChannel = (event.Guild.GetChannel(927449884204867664uL))
do! battleChannel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
| _ ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- "Error parsing Button Id"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
}
let handleDefense (event : ComponentInteractionCreateEventArgs) =
async {
let split = event.Id.Split("-")
return!
match Shield.TryParse(split.[1]) with
| Some shield ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"Mounted a {shield} defense for 1 hour"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
|> Async.AwaitTask
let defense = { DefenseType = shield ; Timestamp = DateTime.UtcNow }
players <-
players
|> List.map (fun p -> { p with Defenses = defense::p.Defenses })
let builder = DiscordMessageBuilder()
builder.WithContent($"{event.User.Username} has protected their system!") |> ignore
let battleChannel = (event.Guild.GetChannel(927449884204867664uL))
do! battleChannel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
}
| _ ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- "Error parsing Button Id"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
}
let handleButtonEvent (client : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
async {
return! match event.Id with
| id when id.StartsWith("Attack") -> handleAttack event
| id when id.StartsWith("Defend") -> handleDefense event
| _ ->
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"Incorrect Action identifier {event.Id}"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
} |> Async.StartAsTask
:> Task
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

@ -15,7 +15,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Types.fs" /> <Compile Include="Types.fs" />
<Compile Include="Joebot.fs" /> <Compile Include="Functions.fs" />
<Compile Include="Commands.fs" />
<Compile Include="Program.fs" /> <Compile Include="Program.fs" />
</ItemGroup> </ItemGroup>
<Import Project=".paket\Paket.Restore.targets" /> <Import Project=".paket\Paket.Restore.targets" />