open System open System.IO open System.Threading.Tasks open DSharpPlus open DSharpPlus.Entities open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Emzi0767.Utilities type HackType = | Virus = 0 | Ransom = 1 | DDos = 2 | Worm = 3 | Crack = 4 | Injection = 5 type DefenseType = | Firewall = 0 | PortScan = 1 | Encryption = 2 | Cypher = 3 | Hardening = 4 | Sanitation = 5 type Player = { DiscordId : uint64 Hacks : HackType list Defenses : DefenseType list Bank : int64 } let mutable players : Player list = [] type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule () let newPlayer (membr : uint64) = // let rand = System.Random(System.Guid.NewGuid().GetHashCode()) // let hacks = // [0..2] // |> Set.map (fun _ -> enum(rand.Next(0, 6))) // let defns = // [0..2] // |> Set.map (fun _ -> enum(rand.Next(0, 6))) { DiscordId = membr Hacks = [ HackType.Virus ; HackType.Worm ; HackType.Injection ] Defenses = [ DefenseType.Cypher ; DefenseType.Sanitation ; DefenseType.Firewall ] Bank = 0L } type JoeBot() = inherit ApplicationCommandModule () [] member _.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 [] member _.RemoveHackerRole (ctx : InteractionContext) = async { for role in ctx.Member.Roles do if role.Name = "Hacker" then do! ctx.Member.RevokeRoleAsync(role) |> Async.AwaitTask do! ctx.CreateResponseAsync("You are now lame", true) |> Async.AwaitTask } |> Async.StartAsTask :> Task [] member this.Hack (ctx : InteractionContext, [] player : DiscordUser) = let constructButtons (playerId : uint64) (weapons : 'a list) = weapons |> Seq.map (fun hack -> // TODO:L Button ID should be a GUID and we should keep an in-memory store of the buttons we're waiting for DiscordButtonComponent( ButtonStyle.Primary, $"Hack-{hack}-{player.Id}", $"{hack}")) players |> List.tryFind (fun p -> p.DiscordId = ctx.Member.Id) |> function | Some player -> async { let builder = DiscordInteractionResponseBuilder() builder.AddEmbed (this.Embed()) |> ignore constructButtons player.DiscordId player.Hacks |> Seq.cast |> builder.AddComponents |> ignore builder.AsEphemeral true |> ignore do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask } |> Async.StartAsTask :> Task | None -> async { let builder = DiscordInteractionResponseBuilder() builder.Content <- $"You are not currently a hacker, first use the /redpill command to become one" builder.AsEphemeral true |> ignore do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask } |> Async.StartAsTask :> Task [] member _.StartMatch (ctx : InteractionContext, [] player : DiscordUser) = async { // We won't be able to find the user if they are Away or Sleeping apparently let ( result , discordMember ) = ctx.Guild.Members.TryGetValue(player.Id) if result then let yes = DiscordButtonComponent( ButtonStyle.Primary, "first_button", "I do") let no = DiscordButtonComponent( ButtonStyle.Danger, "second_button", "No thank you") // let yes = DiscordButtonComponent( // ButtonStyle.Primary, // $"yes_for_{ctx.Member.Id}", // "I do") // let no = DiscordButtonComponent( // ButtonStyle.Danger, // $"no_for_{ctx.Member.Id}", // "No thank you") // builder.AddComponents(yes, no) |> ignore let builder = DiscordMessageBuilder() let builder = builder.AddComponents(yes, no) use img = new FileStream("challenge.jpg", FileMode.Open) builder.WithFile(img) |> ignore builder.Content <- $"You have been challenged by {player.Username}, do you accept?" for channel in ctx.Guild.Channels do if channel.Value.Name = "battle-1" then do! channel.Value.SendMessageAsync builder |> Async.AwaitTask |> Async.Ignore let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true builder.Content <- $"Sending challenge to {player.Username}" do! ctx.CreateResponseAsync (builder) |> Async.AwaitTask else let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true builder.Content <- $"Unable to find user in this server" do! ctx.CreateResponseAsync (builder) |> Async.AwaitTask } |> Async.StartAsTask :> Task member _.Embed () = let builder = DiscordEmbedBuilder() builder.Color <- Optional(DiscordColor.PhthaloGreen) builder.Description <- "Pick the hack you wish to use. " let author = DiscordEmbedBuilder.EmbedAuthor() author.Name <- "Joebot Pro" author.Url <- "https://ferano.io" author.IconUrl <- "https://i.kym-cdn.com/entries/icons/original/000/028/861/cover3.jpg" builder.Author <- author builder.Build() [] member this.TestEmbed (ctx : InteractionContext) = async { do! ctx.CreateResponseAsync (this.Embed() , true) |> Async.AwaitTask } |> Async.StartAsTask :> Task let handleButtonEvent (client : DiscordClient) (event : ComponentInteractionCreateEventArgs) = async { match event.Id with | id when id.StartsWith("Hack") -> let split = event.Id.Split("-") let ( resultHack , hackType ) = Enum.TryParse(typedefof, split.[1]) let ( resultId , target ) = UInt64.TryParse split.[2] match resultHack , resultId with | true , true -> let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true builder.Content <- $"Hack has been sent to {target}!" do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask let builder = DiscordMessageBuilder() // builder..Add(UserMention(target)) builder.WithContent($"{event.User.Username} has sent a hack to ") |> ignore let battleChannel = (event.Guild.GetChannel(927444uL)) do! battleChannel.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 | _ -> () } |> Async.StartAsTask :> Task let config = DiscordConfiguration() config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk" config.TokenType <- TokenType.Bot config.Intents <- DiscordIntents.All //config.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace let client = new DiscordClient(config) client.add_ComponentInteractionCreated(AsyncEventHandler(handleButtonEvent)) let slash = client.UseSlashCommands() slash.RegisterCommands(922419263275425832uL); client.ConnectAsync () |> Async.AwaitTask |> Async.RunSynchronously Task.Delay(-1) |> Async.AwaitTask |> Async.RunSynchronously client.DisconnectAsync () |> Async.AwaitTask |> Async.RunSynchronously