From e40fc4248243d5ff79492c082fee387d91d146a5 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 15 Jan 2022 21:13:33 +0700 Subject: [PATCH] Restructuring project for new bots --- Commands.fs | 287 ----------------- Degenz.sln | 34 ++ HackerBattle/.dockerignore | 25 ++ HackerBattle/Commands.fs | 290 ++++++++++++++++++ HackerBattle/Dockerfile | 18 ++ Functions.fs => HackerBattle/Functions.fs | 63 ++-- .../HackerBattle.fsproj | 14 +- Program.fs => HackerBattle/Program.fs | 26 +- .../paket.references | 3 +- Shared/Shared.fsproj | 11 + Shared/Types.fs | 67 ++++ Shared/paket.references | 2 + Store/.dockerignore | 25 ++ Store/Dockerfile | 18 ++ Store/Program.fs | 4 + Store/Store.fsproj | 19 ++ Store/paket.references | 7 + Types.fs | 88 ------ challenge.jpg | Bin 24925 -> 0 bytes paket.dependencies | 2 +- paket.lock | 74 +++-- 21 files changed, 621 insertions(+), 456 deletions(-) delete mode 100644 Commands.fs create mode 100644 Degenz.sln create mode 100644 HackerBattle/.dockerignore create mode 100644 HackerBattle/Commands.fs create mode 100644 HackerBattle/Dockerfile rename Functions.fs => HackerBattle/Functions.fs (66%) rename discord-bot.fsproj => HackerBattle/HackerBattle.fsproj (62%) rename Program.fs => HackerBattle/Program.fs (88%) rename paket.references => HackerBattle/paket.references (86%) create mode 100644 Shared/Shared.fsproj create mode 100644 Shared/Types.fs create mode 100644 Shared/paket.references create mode 100644 Store/.dockerignore create mode 100644 Store/Dockerfile create mode 100644 Store/Program.fs create mode 100644 Store/Store.fsproj create mode 100644 Store/paket.references delete mode 100644 Types.fs delete mode 100644 challenge.jpg diff --git a/Commands.fs b/Commands.fs deleted file mode 100644 index 2fad0bd..0000000 --- a/Commands.fs +++ /dev/null @@ -1,287 +0,0 @@ -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 battleChannel = 930363007781978142uL - -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.Username 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(5)) (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 - |> builder.AddComponents - |> ignore - - builder.AsEphemeral true |> ignore - - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - - } |> Async.StartAsTask - :> Task - else - async { - let builder = DiscordInteractionResponseBuilder() - let timestamp = updatedAttacks |> List.rev |> List.head |> fun a -> a.Timestamp // This should be the next expiring timestamp - let timeRemaining = TimeSpan.FromMinutes(15) - (DateTime.UtcNow - timestamp) - builder.Content <- $"No more hacks available, 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) - if updatedDefenses.Length < 2 then - 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 - |> builder.AddComponents - |> ignore - - builder.AsEphemeral true |> ignore - - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - else - let builder = DiscordInteractionResponseBuilder() - let timestamp = updatedDefenses |> List.rev |> List.head |> fun a -> a.Timestamp // This should be the next expiring timestamp - let timeRemaining = TimeSpan.FromMinutes(15) - (DateTime.UtcNow - timestamp) - builder.Content <- $"Cannot add new defense, please wait {timeRemaining.Minutes} minutes and {timeRemaining.Seconds} seconds to add another defense" - - 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 <- Functions.statusFormat player - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - } - | None -> notYetAHackerMsg ctx |> Async.AwaitTask - } |> Async.StartAsTask - :> Task - -let leaderboard (ctx : InteractionContext) = - async { - let builder = DiscordInteractionResponseBuilder() - builder.IsEphemeral <- true - let content = - players - |> List.sortByDescending (fun p -> p.Bank) - |> List.mapi (fun i p -> $"{i + 1}. {p.Bank} {p.Name}") - |> String.concat "\n" - builder.Content <- if not <| String.IsNullOrEmpty content then content else "There are no active hackers" - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> 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 channel = (event.Guild.GetChannel(battleChannel)) - do! channel.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 channel = (event.Guild.GetChannel(battleChannel)) - do! channel.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 channel = (event.Guild.GetChannel(battleChannel)) - do! channel.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 diff --git a/Degenz.sln b/Degenz.sln new file mode 100644 index 0000000..ec17336 --- /dev/null +++ b/Degenz.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "HackerBattle", "HackerBattle\HackerBattle.fsproj", "{2A437756-3D5D-467D-9497-DF9789DB99CC}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Shared", "Shared\Shared.fsproj", "{5F34C24E-BA4E-4E57-9141-812775687360}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Store", "Store\Store.fsproj", "{CD88B0A6-DE42-4087-9B33-48FF84201633}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2A437756-3D5D-467D-9497-DF9789DB99CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A437756-3D5D-467D-9497-DF9789DB99CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A437756-3D5D-467D-9497-DF9789DB99CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A437756-3D5D-467D-9497-DF9789DB99CC}.Release|Any CPU.Build.0 = Release|Any CPU + {5F34C24E-BA4E-4E57-9141-812775687360}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F34C24E-BA4E-4E57-9141-812775687360}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F34C24E-BA4E-4E57-9141-812775687360}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F34C24E-BA4E-4E57-9141-812775687360}.Release|Any CPU.Build.0 = Release|Any CPU + {CD88B0A6-DE42-4087-9B33-48FF84201633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD88B0A6-DE42-4087-9B33-48FF84201633}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD88B0A6-DE42-4087-9B33-48FF84201633}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD88B0A6-DE42-4087-9B33-48FF84201633}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/HackerBattle/.dockerignore b/HackerBattle/.dockerignore new file mode 100644 index 0000000..38bece4 --- /dev/null +++ b/HackerBattle/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/HackerBattle/Commands.fs b/HackerBattle/Commands.fs new file mode 100644 index 0000000..57983d2 --- /dev/null +++ b/HackerBattle/Commands.fs @@ -0,0 +1,290 @@ +module DegenzGame.Commands + +open System +open System.Threading.Tasks +open DSharpPlus +open DSharpPlus.Entities +open DSharpPlus.EventArgs +open DSharpPlus.SlashCommands +open DegenzGame.Types +open DegenzGame.Functions +open MongoDB.Driver + +[] +// Degenz Server +//let battleChannel = 930363007781978142uL +// My server +let battleChannel = 927449884204867664uL + +let mongo = MongoClient("mongodb://localhost:27017") +let db = mongo.GetDatabase("degenz-game") +let players = db.GetCollection("players") + +let tryFindPlayer (id : uint64) : Async = + async { + let filter = Builders.Filter.Eq((fun p -> p.DiscordId), id) + let! player = players.FindAsync(filter) |> Async.AwaitTask + return match player.ToEnumerable() |> Seq.toList with + | [] -> None + | p::_ -> Some p + } + +let addHackerRole (ctx : InteractionContext) = + async { + let! player = tryFindPlayer ctx.Member.Id + let! newPlayer = + match player with + | Some _ -> async.Return false + | None -> + async { + let p = (newPlayer ctx.Member.Username ctx.Member.Id) + do! players.InsertOneAsync p |> Async.AwaitTask + + 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) = + async { + for role in ctx.Member.Roles do + if role.Name = "Hacker" then + do! ctx.Member.RevokeRoleAsync(role) + |> Async.AwaitTask + // TODO: Check the result of this delete operation + let! _ = players.DeleteOneAsync (fun p -> p.DiscordId = ctx.Member.Id) |> Async.AwaitTask + do! ctx.CreateResponseAsync("You are now lame", true) + |> Async.AwaitTask + } |> Async.StartAsTask + :> Task + +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 + let! attacker = tryFindPlayer ctx.Member.Id + let! defender = tryFindPlayer target.Id + match attacker , defender with + | Some attacker , Some defender -> + let updatedAttacks = removeExpiredActions (TimeSpan.FromMinutes(5)) (fun (atk : Attack) -> atk.Timestamp) attacker.Attacks + let filter = Builders.Filter.Eq((fun p -> p.DiscordId), attacker.DiscordId) + let update = Builders.Update.Set((fun p -> p.Attacks), updatedAttacks) + let! _ = players.UpdateOneAsync(filter, update) |> Async.AwaitTask + if updatedAttacks.Length < 2 then + 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 + |> builder.AddComponents + |> ignore + + builder.AsEphemeral true |> ignore + + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + + else + let builder = DiscordInteractionResponseBuilder() + let timestamp = updatedAttacks |> Array.rev |> Array.head |> fun a -> a.Timestamp // This should be the next expiring timestamp + let timeRemaining = TimeSpan.FromMinutes(15) - (DateTime.UtcNow - timestamp) + builder.Content <- $"No more hacks available, 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 + + | None , _ -> do! notYetAHackerMsg ctx + | _ , 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 defend (ctx : InteractionContext) = + async { + let! player = tryFindPlayer ctx.Member.Id + match player with + | Some player -> + let updatedDefenses = removeExpiredActions (TimeSpan.FromMinutes(60)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses + let filter = Builders.Filter.Eq((fun p -> p.DiscordId), player.DiscordId) + let update = Builders.Update.Set((fun p -> p.Defenses), updatedDefenses) + let! _ = players.UpdateOneAsync(filter, update) |> Async.AwaitTask + if updatedDefenses.Length < 2 then + 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 + |> builder.AddComponents + |> ignore + + builder.AsEphemeral true |> ignore + + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + else + let builder = DiscordInteractionResponseBuilder() + let timestamp = updatedDefenses |> Array.rev |> Array.head |> fun a -> a.Timestamp // This should be the next expiring timestamp + let timeRemaining = TimeSpan.FromMinutes(15) - (DateTime.UtcNow - timestamp) + builder.Content <- $"Cannot add new defense, please wait {timeRemaining.Minutes} minutes and {timeRemaining.Seconds} seconds to add another defense" + + builder.AsEphemeral true |> ignore + + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + | None -> do! notYetAHackerMsg ctx + } |> Async.StartAsTask + :> Task + +let status (ctx : InteractionContext) = + async { + let! player = tryFindPlayer ctx.Member.Id + match player with + | Some p -> + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- Functions.statusFormat p + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + | None -> do! notYetAHackerMsg ctx + } |> Async.StartAsTask + :> Task + +let leaderboard (ctx : InteractionContext) = + async { + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + + let! leaders = players.Find(fun _ -> true).SortBy(fun p -> p.Bank).Limit(10).ToListAsync() |> Async.AwaitTask + let content = + leaders.ToArray() + |> Array.mapi (fun i p -> $"{i + 1}. {p.Bank} {p.Name}") + |> String.concat "\n" + builder.Content <- if not <| String.IsNullOrEmpty content then content else "There are no active hackers" + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + } |> Async.StartAsTask + :> Task + +let handleAttack (event : ComponentInteractionCreateEventArgs) = + let updatePlayer amount attack p = + { p with Attacks = Array.append [| attack |] p.Attacks ; Bank = MathF.Max(p.Bank + amount, 0f) } + async { + let split = event.Id.Split("-") + let ( resultHack , weapon ) = Weapon.TryParse(split.[1]) + let ( resultId , targetId ) = UInt64.TryParse split.[2] + let! resultPlayer = tryFindPlayer event.User.Id + let! resultTarget = tryFindPlayer targetId + match resultPlayer , resultTarget , resultHack , resultId with + | Some player , Some target , true , true -> + let wasSuccessfulHack = + target.Defenses + |> Seq.toArray + |> Array.map (fun dfn -> int dfn.DefenseType) + |> Array.map (calculateDamage weapon) + |> Array.contains Weak + match wasSuccessfulHack with + | false -> + let prize = 0.1726f + let attack = { HackType = enum(weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } } + let filter = Builders.Filter.Eq((fun p -> p.DiscordId), player.DiscordId) + let! _ = players.ReplaceOneAsync(filter, updatePlayer prize attack player) |> Async.AwaitTask + + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- $"Successfully hacked {split.[3]} using {weapon}! 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 channel = (event.Guild.GetChannel(battleChannel)) + do! channel.SendMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + | true -> + 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 = enum(weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } } + let filter = Builders.Filter.Eq((fun p -> p.DiscordId), player.DiscordId) + let! _ = players.ReplaceOneAsync(filter, updatePlayer loss attack player) |> Async.AwaitTask + + let builder = DiscordMessageBuilder() + builder.WithContent($"{event.User.Username} failed to hack <@{targetId}>!") |> ignore + let channel = (event.Guild.GetChannel(battleChannel)) + do! channel.SendMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + | _ -> + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- "Error occurred processing attack" + do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + } + +let handleDefense (event : ComponentInteractionCreateEventArgs) = + async { + let split = event.Id.Split("-") + let ( shieldResult , shield ) = Shield.TryParse(split.[1]) + let! playerResult = tryFindPlayer event.User.Id + match playerResult , shieldResult with + | Some player , true -> + 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 } + let filter = Builders.Filter.Eq((fun p -> p.DiscordId), player.DiscordId) + let update = Builders.Update.Set((fun p -> p.Defenses), Array.append [| defense |] player.Defenses ) + let! _ = players.UpdateOneAsync(filter, update) |> Async.AwaitTask + + let builder = DiscordMessageBuilder() + builder.WithContent($"{event.User.Username} has protected their system!") |> ignore + let channel = event.Guild.Channels.Values |> Seq.find (fun c -> c.Name = "battle-1") + do! channel.SendMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + | _ -> + 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 diff --git a/HackerBattle/Dockerfile b/HackerBattle/Dockerfile new file mode 100644 index 0000000..e7a72b7 --- /dev/null +++ b/HackerBattle/Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /src +COPY ["HackerBattle/HackerBattle.fsproj", "HackerBattle/"] +RUN dotnet restore "HackerBattle/HackerBattle.fsproj" +COPY . . +WORKDIR "/src/HackerBattle" +RUN dotnet build "HackerBattle.fsproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "HackerBattle.fsproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "HackerBattle.dll"] diff --git a/Functions.fs b/HackerBattle/Functions.fs similarity index 66% rename from Functions.fs rename to HackerBattle/Functions.fs index 831dadf..6ac32be 100644 --- a/Functions.fs +++ b/HackerBattle/Functions.fs @@ -1,52 +1,52 @@ -module Joebot.Functions +module DegenzGame.Functions open System -open System.Threading.Tasks open DSharpPlus open DSharpPlus.Entities open DSharpPlus.SlashCommands -open Joebot.Types +open DegenzGame.Types +open MongoDB.Bson let hackDescription = "" - + let statusFormat player = $"Hack Inventory: {player.Weapons} Shield Inventory: {player.Shields} -Active Hacks: {player.Attacks} -Active Defenses: {player.Defenses} +Active Hacks: {player.Attacks |> Array.toList} +Active Defenses: {player.Defenses |> Array.toList} Bank: {player.Bank}" - let newPlayer nickname (membr : uint64) = - let h1 = [| Virus ; Ransom |] - let h2 = [| DDos ; Worm |] - let h3 = [| Crack ; Injection |] - let d1 = [| Firewall ; PortScan |] - let d2 = [| Encryption ; Cypher |] - let d3 = [| Hardening ; Sanitation |] - + 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 getRandom (actions : 'a array) = actions.[rand.Next(0,2)] - - let weapons = [ getRandom h1 ; getRandom h2 ; getRandom h3 ] - let shields = [ getRandom d1 ; getRandom d2 ; getRandom d3 ] - - { DiscordId = membr + + let weapons = [| getRandom h1 ; getRandom h2 ; getRandom h3 |] + let shields = [| getRandom d1 ; getRandom d2 ; getRandom d3 |] + + { Id = BsonObjectId(ObjectId.GenerateNewId()) + DiscordId = membr Name = nickname Weapons = weapons Shields = shields - Attacks = [] - Defenses = [] + Attacks = [||] + Defenses = [||] Bank = 0f } -let constructButtons (actionType : string) (playerInfo : string) (weapons : 'a list) = +let constructButtons (actionType : string) (playerInfo : string) (weapons : 'a array) = weapons |> Seq.map (fun hack -> DiscordButtonComponent( ButtonStyle.Primary, $"{actionType}-{hack}-{playerInfo}", $"{hack}")) - + let createSimpleResponseAsync msg (ctx : InteractionContext) = async { let builder = DiscordInteractionResponseBuilder() @@ -54,14 +54,13 @@ let createSimpleResponseAsync msg (ctx : InteractionContext) = builder.AsEphemeral true |> ignore do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask - } |> Async.StartAsTask - :> Task + } let notYetAHackerMsg = createSimpleResponseAsync "You are not currently a hacker, first use the /redpill command to become one" let removeExpiredActions timespan (timestamp : 'a -> DateTime) actions = actions - |> List.filter (fun act -> + |> Array.filter (fun act -> if DateTime.UtcNow - (timestamp act) < timespan then true else false) @@ -71,16 +70,16 @@ let constructEmbed message = builder.Color <- Optional(DiscordColor.PhthaloGreen) builder.Description <- message let author = DiscordEmbedBuilder.EmbedAuthor() - author.Name <- "Joebot Pro" + author.Name <- "Degenz Hacker Game" author.Url <- "https://twitter.com/degenzgame" author.IconUrl <- "https://pbs.twimg.com/profile_images/1473192843359309825/cqjm0VQ4_400x400.jpg" builder.Author <- author builder.Build() - -let calculateDamage (hack : IClass) (protection : IClass) = - let hackClass = hack.GetClass() - let protectionClass = protection.GetClass() + +let calculateDamage (hack : int) (shield : int) = + let hackClass = getClass hack + let protectionClass = getClass shield match hackClass , protectionClass with | h , p when h = p -> Weak | _ -> Strong - + diff --git a/discord-bot.fsproj b/HackerBattle/HackerBattle.fsproj similarity index 62% rename from discord-bot.fsproj rename to HackerBattle/HackerBattle.fsproj index 2a6a6ee..d52adce 100644 --- a/discord-bot.fsproj +++ b/HackerBattle/HackerBattle.fsproj @@ -3,21 +3,23 @@ Exe net6.0 - discord_bot + hacker-game + Linux - - PreserveNewest - + + - - + + + + \ No newline at end of file diff --git a/Program.fs b/HackerBattle/Program.fs similarity index 88% rename from Program.fs rename to HackerBattle/Program.fs index feb5c8f..0a6d931 100644 --- a/Program.fs +++ b/HackerBattle/Program.fs @@ -1,4 +1,4 @@ -module Joebot.Program +module DegenzGame.Program open System open System.Threading.Tasks @@ -7,30 +7,31 @@ open DSharpPlus.Entities open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Emzi0767.Utilities -open Joebot.Types -open Joebot.Commands +open DegenzGame.Types +open DegenzGame.Commands +open MongoDB.Driver type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule () -type JoeBot() = +type HackerGame() = inherit ApplicationCommandModule () - + [] member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx - + [] member _.RemoveHackerRole (ctx : InteractionContext) = Commands.removeHackerRole ctx - + [] member this.AttackCommand (ctx : InteractionContext, [] target : DiscordUser) = Commands.attack ctx target - + [] member this.DefendCommand (ctx : InteractionContext) = Commands.defend ctx - + [] member this.Status (ctx : InteractionContext) = Commands.status ctx - + [] member this.Leaderboard (ctx : InteractionContext) = Commands.leaderboard ctx @@ -46,7 +47,10 @@ client.add_ComponentInteractionCreated(AsyncEventHandler(handleButtonEvent)) let slash = client.UseSlashCommands() -slash.RegisterCommands(922414052708327494uL); +// My server +slash.RegisterCommands(922419263275425832uL); +// Degenz +//slash.RegisterCommands(922414052708327494uL); client.ConnectAsync () |> Async.AwaitTask diff --git a/paket.references b/HackerBattle/paket.references similarity index 86% rename from paket.references rename to HackerBattle/paket.references index 9c14efd..5753ebc 100644 --- a/paket.references +++ b/HackerBattle/paket.references @@ -3,4 +3,5 @@ DSharpPlus // DSharpPlus.CommandsNext // DSharpPlus.Interactivity DSharpPlus.SlashCommands -LiteDB.FSharp + +MongoDB.Driver diff --git a/Shared/Shared.fsproj b/Shared/Shared.fsproj new file mode 100644 index 0000000..47c6c0a --- /dev/null +++ b/Shared/Shared.fsproj @@ -0,0 +1,11 @@ + + + + net6.0 + true + + + + + + \ No newline at end of file diff --git a/Shared/Types.fs b/Shared/Types.fs new file mode 100644 index 0000000..9b9650b --- /dev/null +++ b/Shared/Types.fs @@ -0,0 +1,67 @@ +module DegenzGame.Types + +open System +open MongoDB.Bson + +type ActionClass = + | Network + | Exploit + | Penetration + +type Weapon = + | Virus = 0 + | Ransom = 1 + | Worm = 2 + | DDos = 3 + | Crack = 4 + | Injection = 5 + +type Shield = + | Firewall = 0 + | PortScan = 1 + | Encryption = 2 + | Hardening = 4 + | Sanitation = 5 + | Cypher = 3 + +let getClass = function + | 0 | 1 -> Network + | 2 | 3 -> Exploit + | 4 | _ -> Penetration + +type HackResult = + | Strong + | Weak + +[] +type DiscordPlayer = { + Id : uint64 + Name : string +} + +[] +type Attack = { + HackType : Weapon + Target : DiscordPlayer + Timestamp : DateTime +} + +[] +type Defense = { + DefenseType : Shield + Timestamp : DateTime +} + +[] +type Player = { + Id : BsonObjectId + DiscordId : uint64 + Name : string + Weapons : Weapon array + Shields : Shield array + Attacks : Attack array + Defenses : Defense array + Bank : single +} + + diff --git a/Shared/paket.references b/Shared/paket.references new file mode 100644 index 0000000..7e4dfad --- /dev/null +++ b/Shared/paket.references @@ -0,0 +1,2 @@ +FSharp.Core +MongoDB.Driver diff --git a/Store/.dockerignore b/Store/.dockerignore new file mode 100644 index 0000000..38bece4 --- /dev/null +++ b/Store/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/Store/Dockerfile b/Store/Dockerfile new file mode 100644 index 0000000..603db69 --- /dev/null +++ b/Store/Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /src +COPY ["Store/Store.fsproj", "Store/"] +RUN dotnet restore "Store/Store.fsproj" +COPY . . +WORKDIR "/src/Store" +RUN dotnet build "Store.fsproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Store.fsproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Store.dll"] diff --git a/Store/Program.fs b/Store/Program.fs new file mode 100644 index 0000000..f115430 --- /dev/null +++ b/Store/Program.fs @@ -0,0 +1,4 @@ + + +// For more information see https://aka.ms/fsharp-console-apps +printfn "Hello from F#" \ No newline at end of file diff --git a/Store/Store.fsproj b/Store/Store.fsproj new file mode 100644 index 0000000..e69619e --- /dev/null +++ b/Store/Store.fsproj @@ -0,0 +1,19 @@ + + + + Exe + net6.0 + Linux + + + + + + + + + + + + + \ No newline at end of file diff --git a/Store/paket.references b/Store/paket.references new file mode 100644 index 0000000..5753ebc --- /dev/null +++ b/Store/paket.references @@ -0,0 +1,7 @@ +FSharp.Core +DSharpPlus +// DSharpPlus.CommandsNext +// DSharpPlus.Interactivity +DSharpPlus.SlashCommands + +MongoDB.Driver diff --git a/Types.fs b/Types.fs deleted file mode 100644 index 4717a3d..0000000 --- a/Types.fs +++ /dev/null @@ -1,88 +0,0 @@ -module Joebot.Types - -open System - -type ActionClass = - | Network - | Exploit - | Penetration - -type IClass = abstract GetClass : unit -> ActionClass - -type Weapon = - | Virus - | Ransom - | Worm - | DDos - | Crack - | Injection - interface IClass with - member this.GetClass () = - match this with - | Virus | Ransom -> Exploit - | DDos | Worm -> Network - | Crack | Injection -> Penetration - static member TryParse weapon = - match weapon with - | "Virus" -> Some Virus - | "Ransom" -> Some Ransom - | "Worm" -> Some Worm - | "DDos" -> Some DDos - | "Crack" -> Some Crack - | "Injection" -> Some Injection - | _ -> None - -type Shield = - | Firewall - | PortScan - | Encryption - | Cypher - | Hardening - | Sanitation - interface IClass with - member this.GetClass () = - match this with - | Firewall | PortScan -> Network - | Encryption | Cypher -> Exploit - | Hardening | Sanitation -> Penetration - static member TryParse shield = - match shield with - | "Firewall" -> Some Firewall - | "PortScan" -> Some PortScan - | "Encryption" -> Some Encryption - | "Cypher" -> Some Cypher - | "Hardening" -> Some Hardening - | "Sanitation" -> Some Sanitation - | _ -> None - -type HackResult = - | Strong - | Weak - -type DiscordPlayer = { - Id : uint64 - Name : string -} - -type Attack = { - HackType : Weapon - Target : DiscordPlayer - Timestamp : DateTime -} - -type Defense = { - DefenseType : Shield - Timestamp : DateTime -} - -type Player = { - DiscordId : uint64 - Name : string - Weapons : Weapon list - Shields : Shield list - Attacks : Attack list - Defenses : Defense list - Bank : single -} - - diff --git a/challenge.jpg b/challenge.jpg deleted file mode 100644 index bce5e38d1fee670299465b7f5a455301831d2976..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24925 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 z 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 diff --git a/paket.dependencies b/paket.dependencies index a935bee..f9261fa 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -10,4 +10,4 @@ source https://nuget.emzi0767.com/api/v3/index.json nuget DSharpPlus >= 4.2.0-nightly-01054 nuget DSharpPlus.SlashCommands >= 4.2.0-nightly-01054 -nuget LiteDB.FSharp 2.16.0 +nuget MongoDB.Driver diff --git a/paket.lock b/paket.lock index f161594..ac4ebf6 100644 --- a/paket.lock +++ b/paket.lock @@ -1,35 +1,16 @@ STORAGE: NONE RESTRICTION: || (== net6.0) (== netstandard2.0) (== netstandard2.1) NUGET - remote: https://nuget.emzi0767.com/api/v3/index.json - DSharpPlus (4.2.0-nightly-01054) - Emzi0767.Common (>= 2.6.2) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Newtonsoft.Json (>= 13.0.1) - System.Memory (>= 4.5.4) - System.Net.Http (>= 4.3.4) - System.Net.WebSockets (>= 4.3) - System.Net.WebSockets.Client (>= 4.3.2) - System.Runtime.InteropServices.RuntimeInformation (>= 4.3) - System.Threading.Channels (>= 5.0) - DSharpPlus.SlashCommands (4.2.0-nightly-01054) - DSharpPlus (>= 4.2.0-nightly-01054) - Microsoft.Extensions.DependencyInjection (>= 5.0.1) remote: https://api.nuget.org/v3/index.json + DnsClient (1.5) + Microsoft.Win32.Registry (>= 5.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net471)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net471)) (&& (== netstandard2.1) (< netstandard2.0)) Emzi0767.Common (2.6.2) System.Collections.Immutable (>= 5.0) System.Memory (>= 4.5.4) System.Runtime.CompilerServices.Unsafe (>= 5.0) System.ValueTuple (>= 4.5) FSharp.Core (6.0.1) - LiteDB (4.1.4) - System.Reflection (>= 4.3) - System.Reflection.TypeExtensions (>= 4.3) - LiteDB.FSharp (2.16) - FSharp.Core (>= 4.7.2) - LiteDB (>= 4.1.4 < 5.0) - Newtonsoft.Json (>= 13.0.1) - TypeShape (>= 9.0) Microsoft.Bcl.AsyncInterfaces (6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) Microsoft.Extensions.DependencyInjection (6.0) @@ -49,6 +30,24 @@ NUGET Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) + Microsoft.Win32.Registry (5.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= monoandroid) (< netstandard1.3)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (== netstandard2.0) (== netstandard2.1) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (>= uap10.1)) (== netstandard2.0) (== netstandard2.1) + System.Security.AccessControl (>= 5.0) + System.Security.Principal.Windows (>= 5.0) + MongoDB.Bson (2.14.1) + System.Runtime.CompilerServices.Unsafe (>= 5.0) + MongoDB.Driver (2.14.1) + MongoDB.Bson (>= 2.14.1) + MongoDB.Driver.Core (>= 2.14.1) + MongoDB.Libmongocrypt (>= 1.3) + MongoDB.Driver.Core (2.14.1) + DnsClient (>= 1.4) + MongoDB.Bson (>= 2.14.1) + MongoDB.Libmongocrypt (>= 1.3) + SharpCompress (>= 0.30.1) + System.Buffers (>= 4.5.1) + MongoDB.Libmongocrypt (1.3) Newtonsoft.Json (13.0.1) runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.debian.9-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) @@ -93,7 +92,10 @@ NUGET runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.ubuntu.18.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - System.Buffers (4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (== netstandard2.0) (== netstandard2.1) + SharpCompress (0.30.1) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) + System.Text.Encoding.CodePages (>= 5.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (== netstandard2.1) + System.Buffers (4.5.1) System.Collections (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -294,9 +296,6 @@ NUGET System.IO (>= 4.3) System.Reflection.Primitives (>= 4.3) System.Runtime (>= 4.3) - System.Reflection.Emit.ILGeneration (4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (== netstandard2.0) (&& (== netstandard2.1) (< netstandard2.0)) (&& (== netstandard2.1) (< portable-net45+wp8)) (&& (== netstandard2.1) (>= uap10.1)) - System.Reflection.Emit.LightWeight (4.7) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) - System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (== netstandard2.0) (&& (== netstandard2.1) (< netstandard2.0)) (&& (== netstandard2.1) (< portable-net45+wp8)) (&& (== netstandard2.1) (>= uap10.1)) System.Reflection.Extensions (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -306,7 +305,6 @@ NUGET Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - System.Reflection.TypeExtensions (4.7) System.Resources.ResourceManager (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -345,6 +343,8 @@ NUGET System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) System.Runtime.Extensions (>= 4.3) + System.Security.AccessControl (6.0) + System.Security.Principal.Windows (>= 5.0) - restriction: || (&& (== net6.0) (>= net461)) (== netstandard2.0) (== netstandard2.1) System.Security.Claims (4.3) System.Collections (>= 4.3) System.Globalization (>= 4.3) @@ -440,6 +440,9 @@ NUGET Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) + System.Text.Encoding.CodePages (6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (== netstandard2.1) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) + System.Runtime.CompilerServices.Unsafe (>= 6.0) System.Text.Encoding.Extensions (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -464,6 +467,17 @@ NUGET Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) System.ValueTuple (4.5) - TypeShape (10.0) - FSharp.Core (>= 4.5.4) - System.Reflection.Emit.LightWeight (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) + remote: https://nuget.emzi0767.com/api/v3/index.json + DSharpPlus (4.2.0-nightly-01059) + Emzi0767.Common (>= 2.6.2) + Microsoft.Extensions.Logging.Abstractions (>= 5.0) + Newtonsoft.Json (>= 13.0.1) + System.Memory (>= 4.5.4) + System.Net.Http (>= 4.3.4) + System.Net.WebSockets (>= 4.3) + System.Net.WebSockets.Client (>= 4.3.2) + System.Runtime.InteropServices.RuntimeInformation (>= 4.3) + System.Threading.Channels (>= 5.0) + DSharpPlus.SlashCommands (4.2.0-nightly-01059) + DSharpPlus (>= 4.2.0-nightly-01059) + Microsoft.Extensions.DependencyInjection (>= 5.0.1)