Redoing trainer flow and adding GuildEnvironment module

This commit is contained in:
Joseph Ferano 2022-01-21 22:46:33 +07:00
parent 5261713735
commit ff513331c6
8 changed files with 244 additions and 156 deletions

View File

@ -4,64 +4,53 @@ open System
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.SlashCommands
open Degenz
open Degenz.PlayerInteractions
open Degenz.SlotMachine
open Degenz.Trainer
//open Degenz.SlotMachine
open Degenz.HackerBattle
open Degenz.Store
open Emzi0767.Utilities
open dotenv.net
type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule ()
let playerInteractionsConfig = DiscordConfiguration()
let trainerConfig = DiscordConfiguration()
let hackerBattleConfig = DiscordConfiguration()
let storeConfig = DiscordConfiguration()
//let slotMachineConfig = DiscordConfiguration()
//config.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace
//let configs = [| playerInteractionsConfig ; trainerConfig ; hackerBattleConfig ; storeConfig ; slotMachineConfig ; |]
let configs = [| playerInteractionsConfig ; trainerConfig ; hackerBattleConfig ; storeConfig |]
//let configs = [| playerInteractionsConfig ; hackerBattleConfig ; storeConfig ; slotMachineConfig ; |]
let configs = [| playerInteractionsConfig ; hackerBattleConfig ; storeConfig |]
for conf in configs do
conf.TokenType <- TokenType.Bot
conf.Intents <- DiscordIntents.All
DotEnv.Load(DotEnvOptions(probeForEnv = true, probeLevelsToSearch = 5, overwriteExistingVars = false))
let guild = GuildEnvironment.guildId
let guild = Environment.GetEnvironmentVariable("DISCORD_GUILD") |> uint64
playerInteractionsConfig.Token <- Environment.GetEnvironmentVariable("BOT_PLAYER_INTERACTIONS")
trainerConfig.Token <- Environment.GetEnvironmentVariable("BOT_TRAINER")
hackerBattleConfig.Token <- Environment.GetEnvironmentVariable("BOT_HACKER_BATTLE")
storeConfig.Token <- Environment.GetEnvironmentVariable("BOT_STORE")
playerInteractionsConfig.Token <- GuildEnvironment.tokenPlayerInteractions
hackerBattleConfig.Token <- GuildEnvironment.tokenHackerBattle
storeConfig.Token <- GuildEnvironment.tokenStore
//slotMachineConfig.Token <- Environment.GetEnvironmentVariable("BOT_SLOT_MACHINE")
//config.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace
let playerInteractionsBot = new DiscordClient(playerInteractionsConfig)
let trainerBot = new DiscordClient(trainerConfig)
let hackerBattleBot = new DiscordClient(hackerBattleConfig)
let storeBot = new DiscordClient(storeConfig)
//let slotMachineBot = new DiscordClient(slotMachineConfig)
hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent))
trainerBot.add_ComponentInteractionCreated(AsyncEventHandler(Trainer.handleButtonEvent))
storeBot.add_ComponentInteractionCreated(AsyncEventHandler(Store.handleSellButtonEvents))
//let clients = [| storeBot ; trainerBot ; hackerBattleBot ; playerInteractionsBot ; slotMachineBot |]
let clients = [| storeBot ; trainerBot ; hackerBattleBot ; playerInteractionsBot |]
let clients = [| storeBot ; hackerBattleBot ; playerInteractionsBot |]
let sc1 = playerInteractionsBot.UseSlashCommands()
let sc2 = trainerBot.UseSlashCommands()
let sc3 = hackerBattleBot.UseSlashCommands()
let sc4 = storeBot.UseSlashCommands()
//let sc5 = slotMachineBot.UseSlashCommands()
sc1.RegisterCommands<PlayerInteractions>(guild);
sc2.RegisterCommands<Trainer>(guild);
sc3.RegisterCommands<HackerGame>(guild);
sc4.RegisterCommands<Store>(guild);
//sc5.RegisterCommands<SlotMachine>(guild);

View File

@ -0,0 +1,19 @@
[<Microsoft.FSharp.Core.RequireQualifiedAccess>]
module Degenz.GuildEnvironment
open System
open dotenv.net
DotEnv.Load(DotEnvOptions(probeForEnv = true, probeLevelsToSearch = 5, overwriteExistingVars = false))
let getVar str = Environment.GetEnvironmentVariable(str)
let getId str = getVar str |> uint64
let guildId = getId "DISCORD_GUILD"
let tokenPlayerInteractions = getVar "TOKEN_PLAYER_INTERACTIONS"
let tokenHackerBattle = getVar "TOKEN_HACKER_BATTLE"
let tokenStore = getVar "TOKEN_STORE"
let eventsChannelHackerBattle = getId "EVENTS_CHANNEL_HACKER_BATTLE"
let channelTraining = getId "CHANNEL_TRAINING"
let botHackerBattle = getId "BOT_HACKER_BATTLE"
let roleTrainee = getId "ROLE_TRAINEE"

View File

@ -9,12 +9,6 @@ open DSharpPlus.SlashCommands
open Degenz
open Degenz.Shared
[<Literal>]
// Degenz Server
//let battleChannel = 930363007781978142uL
// My server
let battleChannel = 927449884204867664uL
let attack (ctx : InteractionContext) (target : DiscordUser) =
async {
// TODO: We need to check if the player has any active embed hacks going, if not they can cheat
@ -133,7 +127,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
let builder = DiscordMessageBuilder()
builder.WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz") |> ignore
let channel = (event.Guild.GetChannel(battleChannel))
let channel = (event.Guild.GetChannel(GuildEnvironment.eventsChannelHackerBattle))
do! channel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
@ -151,7 +145,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
let builder = DiscordMessageBuilder()
builder.WithContent($"Hacking attempt failed! <@{targetId}> defended hack from {event.User.Username} and took {prize} from them! ") |> ignore
let channel = (event.Guild.GetChannel(battleChannel))
let channel = (event.Guild.GetChannel(GuildEnvironment.eventsChannelHackerBattle))
do! channel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
@ -194,10 +188,11 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
}
let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
async {
return! match event.Id with
let task =
match event.Id with
| id when id.StartsWith("Attack") -> handleAttack event
| id when id.StartsWith("Defend") -> handleDefense event
| id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent event
| _ ->
async {
let builder = DiscordInteractionResponseBuilder()
@ -206,6 +201,8 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEve
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
}
async {
return! task
} |> Async.StartAsTask
:> Task
@ -214,9 +211,18 @@ type HackerGame() =
[<SlashCommand("hack", "Send a hack attack to another player")>]
member this.AttackCommand (ctx : InteractionContext, [<Option("target", "The player you want to hack")>] target : DiscordUser) =
let hasTraineeRole = Seq.exists (fun (r : DiscordRole) -> r.Name = "trainee") ctx.Member.Roles
if ctx.Channel.Name = "training-dojo" && hasTraineeRole then
Trainer.attack ctx target
else
attack ctx target
[<SlashCommand("defend", "Create a passive defense that will last 24 hours")>]
member this.DefendCommand (ctx : InteractionContext) = defend ctx
member this.DefendCommand (ctx : InteractionContext) =
let hasTraineeRole = Seq.exists (fun (r : DiscordRole) -> r.Name = "Trainee") ctx.Member.Roles
if ctx.Channel.Name = "training-dojo" && hasTraineeRole then
Trainer.defend ctx
else
defend ctx

View File

@ -7,16 +7,17 @@
<RootNamespace>PlayerRegistration</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Content Include="paket.references" />
<Compile Include="Store.fs" />
<Compile Include="HackerBattle.fs" />
<Compile Include="SlotMachine.fs" />
<Compile Include="Trainer.fs" />
<Compile Include="PlayerInteractions.fs" />
<Compile Include="Bot.fs" />
<Content Include="Items.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="paket.references" />
<Compile Include="GuildEnvironment.fs" />
<Compile Include="Store.fs" />
<Compile Include="Trainer.fs" />
<Compile Include="HackerBattle.fs" />
<Compile Include="SlotMachine.fs" />
<Compile Include="PlayerInteractions.fs" />
<Compile Include="Bot.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DbService\DbService.fsproj" />

View File

@ -7,7 +7,8 @@ open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz.Shared
let sendMessage (event : ComponentInteractionCreateEventArgs) msg =
module Message =
let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
async {
let builder = DiscordFollowupMessageBuilder()
builder.IsEphemeral <- true
@ -15,98 +16,82 @@ let sendMessage (event : ComponentInteractionCreateEventArgs) msg =
do! event.Interaction.CreateFollowupMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
do! Async.Sleep 4000
}
let handleInitialDialog (event : ComponentInteractionCreateEventArgs) =
let sendMessage' = sendMessage event
let sendFollowUpMessageWithButton (event : ComponentInteractionCreateEventArgs) buttonId msg =
async {
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
let builder = DiscordFollowupMessageBuilder()
let button = DiscordButtonComponent(ButtonStyle.Primary, buttonId, "Got it") :> DiscordComponent
builder.AddComponents [| button |] |> ignore
builder.IsEphemeral <- true
builder.Content <- msg
do! event.Interaction.CreateFollowupMessageAsync(builder)
|> Async.AwaitTask
let! result = DbService.tryFindPlayer event.User.Id
match result with
| Some player ->
do! sendMessage' "The Degenz world is a dangerous place. I'm going to teach you how to protect yourself from other degenerates."
do! sendMessage' "And in the process, I'll also show you how to hack these sheeple, so you can earn some cash."
do! sendMessage' "First thing is first, let's get your system protected. Let's put up a shield."
let weaponName = player.Shields |> Array.tryHead |> Option.defaultValue Shield.Firewall
do! sendMessage' $"You currently have {weaponName} in your arsenal. To enable it and protect your system, run the `/defend` command and select '{weaponName}'"
| None ->
do! sendMessage' $"Something went wrong, please contact a moderator"
|> Async.Ignore
}
let handleDefense (event : ComponentInteractionCreateEventArgs) =
let sendMessage' = sendMessage event
let sendInteractionEvent (event : ComponentInteractionCreateEventArgs) msg =
async {
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|> Async.AwaitTask
let! result = DbService.tryFindPlayer event.User.Id
match result with
| Some player ->
let prize = 0.223f
do! sendMessage' $"{event.User.Username} has protected their system!"
do! sendMessage' "Ok, good, let me make sure that worked. I'll try to hack you now"
do! sendMessage' $"Hacking attempt failed! {player.Name} defended hack from Degenz-Trainer and took {prize} from them! "
do! sendMessage' "Great, I wasn't able to hack you. Great job! Because you had your system protected, when I tried to hack you, I lost some money and had to give it to you"
do! sendMessage' "But you can make even more by successfully hacking your target. Why don't you try hacking me?"
let weaponName = player.Weapons |> Array.tryHead |> Option.defaultValue Hack.Virus
do! sendMessage' $"You currently have {weaponName} equipped. To attempt a hack, type the '/hack' command and select {weaponName}."
| None ->
do! sendMessage' $"Something went wrong, please contact a moderator"
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- msg
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
}
let handleAttack (event : ComponentInteractionCreateEventArgs) =
let sendMessage' = sendMessage event
let sendInteractionEventWithButton (event : ComponentInteractionCreateEventArgs) buttonId msg =
async {
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|> Async.AwaitTask
let! result = DbService.tryFindPlayer event.User.Id
match result with
| Some player ->
let prize = 1.337f // LEET
do! sendMessage' $"{player.Name} successfully hacked Degenz-Trainer for a total of {prize} GoodBoyTokenz"
do! sendMessage' "Looks like you got the hang of it. By successfully hacking other people you can earn some GoodBoyTokenz"
do! sendMessage' "I think we're done for now. If you wish to purchase more hacks or shields, you go to the store to purchase them."
do! sendMessage' "Alright you degenerate, off you go!"
| None ->
do! sendMessage' $"Something went wrong, please contact a moderator"
let builder = DiscordInteractionResponseBuilder()
let button = DiscordButtonComponent(ButtonStyle.Primary, buttonId, "Got it") :> DiscordComponent
builder.AddComponents [| button |] |> ignore
builder.IsEphemeral <- true
builder.Content <- msg
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
}
let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
async {
match event.Id with
| id when id.StartsWith("Trainer") -> do! handleInitialDialog event
| id when id.StartsWith("Defend") -> do! handleDefense event
| id when id.StartsWith("Attack") -> do! handleAttack event
| _ -> do! sendMessage event "No action found"
}
|> Async.StartAsTask
:> Task
let sendInitialEmbed (client : DiscordClient) =
async {
let! channel = client.GetChannelAsync(933298431521333258uL) |> Async.AwaitTask
let! channel = client.GetChannelAsync(GuildEnvironment.channelTraining) |> Async.AwaitTask
let builder = DiscordMessageBuilder()
builder.Content <- "Welcome to the trainer bot, are you ready to get started?"
let button = DiscordButtonComponent(ButtonStyle.Success, $"Trainer-trainer-1", $"Get started") :> DiscordComponent
builder.Content <- "Welcome to the hacker dojo you degenerate, are you ready to get started?"
let button = DiscordButtonComponent(ButtonStyle.Success, $"Trainer-1", $"Get started") :> DiscordComponent
builder.AddComponents [| button |] |> ignore
do! channel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
} |> Async.RunSynchronously
type Trainer() =
inherit ApplicationCommandModule ()
let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) =
async {
let msg = "First time, eh? Beautopia is a dangerous place. I'm going to teach you how to protect yourself from other degenerates. "
+ "And in the process, I'll also show you how to hack some sheeple, so you can earn some cash."
do! Message.sendInteractionEventWithButton event "Trainer-2" msg
[<SlashCommand("defend", "Create a passive defense that will last 24 hours")>]
member this.DefendCommand (ctx : InteractionContext) =
let! membr = event.Guild.GetMemberAsync(event.User.Id) |> Async.AwaitTask
let role = event.Guild.GetRole(GuildEnvironment.roleTrainee)
do! membr.GrantRoleAsync(role) |> Async.AwaitTask
}
let handleTrainerStep2 (event : ComponentInteractionCreateEventArgs) =
async {
let! result = DbService.tryFindPlayer event.User.Id
match result with
| Some player ->
let weaponName = player.Shields |> Array.tryHead |> Option.defaultValue Shield.Firewall
do! Message.sendFollowUpMessage event
($"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."
+ $"\n\nRun the `/defend` command now and then select '{weaponName}'.")
| None ->
do! Message.sendFollowUpMessage event $"Something went wrong, please contact a moderator"
}
let defend (ctx : InteractionContext) =
async {
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
match playerResult with
| Some player ->
let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick a defense to mount for 24 hours") |> ignore
builder.AddEmbed (constructEmbed "Pick a defense to mount for 8 hours") |> ignore
constructButtons "Defend" (string player.DiscordId) player.Shields
|> Seq.cast<DiscordComponent>
@ -128,12 +113,46 @@ type Trainer() =
} |> Async.StartAsTask
:> Task
[<SlashCommand("hack", "Send a hack attack to another player")>]
member this.Attack (ctx : InteractionContext) =
let handleDefense (event : ComponentInteractionCreateEventArgs) =
let sendMessage' = Message.sendFollowUpMessage event
async {
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
match playerResult with
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|> Async.AwaitTask
let! result = DbService.tryFindPlayer event.User.Id
match result with
| Some player ->
let prize = 0.223f
do! sendMessage' $"{event.User.Username} has protected their system!"
do! sendMessage' "Ok, good, let me make sure that worked. I'll try to hack you now"
do! sendMessage' $"Hacking attempt failed! {player.Name} defended hack from Degenz-Trainer and took {prize} from them! "
let msg = ("I wasn't able to hack you. Great job! Because you had your system protected when I tried to hack you, you took some money from me. "
+ "Shields only protect you for a certain amount of time, so remember to keep your system protected at all times.")
do! Message.sendFollowUpMessageWithButton event "Trainer-3" msg
| None ->
do! sendMessage' $"Something went wrong, please contact a moderator"
}
let handleTrainerStep3 (event : ComponentInteractionCreateEventArgs) =
async {
let! result = DbService.tryFindPlayer event.User.Id
match result with
| Some player ->
// TODO: There's a potential bug here where if the player sold their weapons, they'll get stuck
let weaponName = player.Weapons |> Array.tryHead |> Option.defaultValue Hack.Virus
do! Message.sendFollowUpMessage event
($"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."
+ $"\n\nRun the `/hack` command now and pick me as your target, then click on the '{weaponName}' button.")
| None ->
do! Message.sendFollowUpMessage event $"Something went wrong, please contact a moderator"
}
let attack (ctx : InteractionContext) (target : DiscordUser) =
async {
let isRightTarget = target.Id = GuildEnvironment.botHackerBattle
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
match isRightTarget , playerResult with
| true , Some player ->
let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick an attack to use on your target") |> ignore
@ -146,7 +165,15 @@ type Trainer() =
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
| None ->
| false , _ ->
let builder = DiscordInteractionResponseBuilder()
builder.Content <- "You picked the wrong target, you dufus. Try again, this time pick me!"
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
| _ ->
let builder = DiscordInteractionResponseBuilder()
builder.Content <- "Error, please contact a moderator"
@ -157,3 +184,43 @@ type Trainer() =
} |> Async.StartAsTask
:> Task
let handleAttack (event : ComponentInteractionCreateEventArgs) =
let sendMessage' = Message.sendFollowUpMessage event
async {
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|> Async.AwaitTask
let! result = DbService.tryFindPlayer event.User.Id
match result with
| Some player ->
let prize = 2
do! sendMessage' $"{player.Name} successfully hacked Degenz-Trainer for a total of {prize} 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.")
let msg = ("I think we're done. You are going to need more hacks and shields if you want to survive in this crazy world. "
+ "Remember to go check out the store and purchase whatever you need to add to your arsenal."
+ "\n\nAlright you degenerate, off you go!")
do! Message.sendFollowUpMessageWithButton event "Trainer-4" msg
| None ->
do! sendMessage' $"Something went wrong, please contact a moderator"
}
let handleTrainerStep4 (event : ComponentInteractionCreateEventArgs) =
async {
let! membr = event.Guild.GetMemberAsync(event.User.Id) |> Async.AwaitTask
let role = event.Guild.GetRole(GuildEnvironment.roleTrainee)
do! membr.RevokeRoleAsync(role) |> Async.AwaitTask
}
let handleButtonEvent (event : ComponentInteractionCreateEventArgs) =
async {
match event.Id with
| id when id.StartsWith("Trainer") ->
let split = id.Split("-")
match int split.[1] with
| 1 -> do! handleTrainerStep1 event
| 2 -> do! handleTrainerStep2 event
| 3 -> do! handleTrainerStep3 event
| _ -> do! Message.sendFollowUpMessage event "No action found"
| _ -> do! Message.sendFollowUpMessage event "No action found"
}

View File

@ -1,5 +1,6 @@
FSharp.Core
DSharpPlus
DSharpPlus.Interactivity
DSharpPlus.SlashCommands
dotenv.net
DanielStout.AsciiTableFormatter

View File

@ -7,7 +7,8 @@ nuget FSharp.Core >= 6.0.0
source https://nuget.emzi0767.com/api/v3/index.json
nuget DSharpPlus >= 4.2.0-nightly-01054
nuget DSharpPlus >= 4.2.0-nightly-01061
nuget DSharpPlus.Interactivity >= 4.2.0-nightly-01061
nuget DSharpPlus.SlashCommands >= 4.2.0-nightly-01061
nuget MongoDB.Driver

View File

@ -2,6 +2,7 @@ STORAGE: NONE
RESTRICTION: || (== net6.0) (== netstandard2.0) (== netstandard2.1)
NUGET
remote: https://api.nuget.org/v3/index.json
ConcurrentHashSet (1.3)
DanielStout.AsciiTableFormatter (1.1)
DnsClient (1.5)
Microsoft.Win32.Registry (>= 5.0)
@ -481,6 +482,9 @@ NUGET
System.Net.WebSockets.Client (>= 4.3.2)
System.Runtime.InteropServices.RuntimeInformation (>= 4.3)
System.Threading.Channels (>= 5.0)
DSharpPlus.Interactivity (4.2.0-nightly-01061)
ConcurrentHashSet (>= 1.1)
DSharpPlus (>= 4.2.0-nightly-01061)
DSharpPlus.SlashCommands (4.2.0-nightly-01061)
DSharpPlus (>= 4.2.0-nightly-01061)
Microsoft.Extensions.DependencyInjection (>= 5.0.1)