First pass on thief game
This commit is contained in:
		
							parent
							
								
									dd33492418
								
							
						
					
					
						commit
						bb9c04fe6e
					
				
							
								
								
									
										10
									
								
								Bot/Bot.fs
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Bot/Bot.fs
									
									
									
									
									
								
							@ -6,6 +6,7 @@ open DSharpPlus.SlashCommands
 | 
				
			|||||||
open Degenz
 | 
					open Degenz
 | 
				
			||||||
open Degenz.HackerBattle
 | 
					open Degenz.HackerBattle
 | 
				
			||||||
open Degenz.Store
 | 
					open Degenz.Store
 | 
				
			||||||
 | 
					open Degenz.Thief
 | 
				
			||||||
open Emzi0767.Utilities
 | 
					open Emzi0767.Utilities
 | 
				
			||||||
//open Degenz.SlotMachine
 | 
					//open Degenz.SlotMachine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,12 +37,13 @@ let storeBot = new DiscordClient(storeConfig)
 | 
				
			|||||||
//let slotMachineBot = new DiscordClient(slotMachineConfig)
 | 
					//let slotMachineBot = new DiscordClient(slotMachineConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//let clients = [| hackerBattleBot ; storeBot ; slotMachineBot |]
 | 
					//let clients = [| hackerBattleBot ; storeBot ; slotMachineBot |]
 | 
				
			||||||
let sc1 = hackerBattleBot.UseSlashCommands()
 | 
					let hackerCommands = hackerBattleBot.UseSlashCommands()
 | 
				
			||||||
let sc2 = storeBot.UseSlashCommands()
 | 
					let storeCommands = storeBot.UseSlashCommands()
 | 
				
			||||||
//let sc3 = slotMachineBot.UseSlashCommands()
 | 
					//let sc3 = slotMachineBot.UseSlashCommands()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sc1.RegisterCommands<HackerGame>(guild);
 | 
					hackerCommands.RegisterCommands<HackerGame>(guild);
 | 
				
			||||||
sc2.RegisterCommands<Store>(guild);
 | 
					hackerCommands.RegisterCommands<StealGame>(guild);
 | 
				
			||||||
 | 
					storeCommands.RegisterCommands<Store>(guild);
 | 
				
			||||||
//sc3.RegisterCommands<SlotMachine>(guild);
 | 
					//sc3.RegisterCommands<SlotMachine>(guild);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent))
 | 
					hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent))
 | 
				
			||||||
 | 
				
			|||||||
@ -16,9 +16,10 @@
 | 
				
			|||||||
        <Compile Include="PlayerInteractions.fs" />
 | 
					        <Compile Include="PlayerInteractions.fs" />
 | 
				
			||||||
        <Compile Include="Embeds.fs" />
 | 
					        <Compile Include="Embeds.fs" />
 | 
				
			||||||
        <Compile Include="Store.fs" />
 | 
					        <Compile Include="Store.fs" />
 | 
				
			||||||
 | 
					        <Compile Include="Thief.fs" />
 | 
				
			||||||
 | 
					        <Compile Include="SlotMachine.fs" />
 | 
				
			||||||
        <Compile Include="Trainer.fs" />
 | 
					        <Compile Include="Trainer.fs" />
 | 
				
			||||||
        <Compile Include="HackerBattle.fs" />
 | 
					        <Compile Include="HackerBattle.fs" />
 | 
				
			||||||
        <Compile Include="SlotMachine.fs" />
 | 
					 | 
				
			||||||
        <Compile Include="Bot.fs" />
 | 
					        <Compile Include="Bot.fs" />
 | 
				
			||||||
    </ItemGroup>
 | 
					    </ItemGroup>
 | 
				
			||||||
    <ItemGroup>
 | 
					    <ItemGroup>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										19
									
								
								Bot/Game.fs
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								Bot/Game.fs
									
									
									
									
									
								
							@ -38,7 +38,24 @@ module Game =
 | 
				
			|||||||
            let! playerResult = tryFindPlayer (ctx.GetDiscordMember().Id)
 | 
					            let! playerResult = tryFindPlayer (ctx.GetDiscordMember().Id)
 | 
				
			||||||
            match playerResult with
 | 
					            match playerResult with
 | 
				
			||||||
            | Some player -> do! dispatch player
 | 
					            | Some player -> do! dispatch player
 | 
				
			||||||
            | None -> do! Messaging.sendSimpleResponse ctx "You are currently not a hacker, first use the /redpill command to become one"
 | 
					            | None -> do! Messaging.sendFollowUpMessage ctx "You are currently not a hacker, first use the /redpill command to become one"
 | 
				
			||||||
 | 
					        } |> Async.StartAsTask :> Task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let executePlayerWithTargetAction (targetPlayer : DiscordUser) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async<unit>) =
 | 
				
			||||||
 | 
					        async {
 | 
				
			||||||
 | 
					            let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
					            builder.IsEphemeral <- true
 | 
				
			||||||
 | 
					            builder.Content <- "Content"
 | 
				
			||||||
 | 
					            do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
 | 
				
			||||||
 | 
					            let! playerResult = tryFindPlayer (ctx.GetDiscordMember().Id)
 | 
				
			||||||
 | 
					            let! targetResult = tryFindPlayer targetPlayer.Id
 | 
				
			||||||
 | 
					            match playerResult , targetResult with
 | 
				
			||||||
 | 
					            | Some player , Some target -> do! dispatch player target
 | 
				
			||||||
 | 
					            | None , _ -> do! Messaging.sendFollowUpMessage ctx "You are currently not a hacker, first use the /redpill command to become one"
 | 
				
			||||||
 | 
					            | _ , None ->
 | 
				
			||||||
 | 
					                if targetPlayer.IsBot
 | 
				
			||||||
 | 
					                    then do! Messaging.sendFollowUpMessage ctx $"{targetPlayer.Username} is a bot, pick a real human to hack"
 | 
				
			||||||
 | 
					                    else do! Messaging.sendFollowUpMessage ctx "Your target is not connected to the network, they must join first by using the /redpill command"
 | 
				
			||||||
        } |> Async.StartAsTask :> Task
 | 
					        } |> Async.StartAsTask :> Task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module Player =
 | 
					module Player =
 | 
				
			||||||
 | 
				
			|||||||
@ -120,26 +120,18 @@ let failedHack (ctx : IDiscordContext) attacker defender hack =
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let attack (target : DiscordUser) (ctx : IDiscordContext) =
 | 
					let attack (target : DiscordUser) (ctx : IDiscordContext) =
 | 
				
			||||||
    Game.executePlayerAction ctx (fun attacker -> async {
 | 
					    Game.executePlayerWithTargetAction target ctx (fun attacker defender -> async {
 | 
				
			||||||
        let! defender = DbService.tryFindPlayer target.Id
 | 
					      do! attacker
 | 
				
			||||||
        match defender with
 | 
					          |> checkAlreadyHackedTarget defender.DiscordId
 | 
				
			||||||
        | Some defender ->
 | 
					          <!> (Player.removeExpiredActions true)
 | 
				
			||||||
          do! attacker
 | 
					          >>= checkHasEmptyHacks
 | 
				
			||||||
              |> checkAlreadyHackedTarget defender.DiscordId
 | 
					          >>= checkTargetHasMoney defender
 | 
				
			||||||
              <!> (Player.removeExpiredActions true)
 | 
					          >>= checkPlayerIsAttackingThemselves defender
 | 
				
			||||||
              >>= checkHasEmptyHacks
 | 
					          |> function
 | 
				
			||||||
              >>= checkTargetHasMoney defender
 | 
					             | Ok atkr ->
 | 
				
			||||||
              >>= checkPlayerIsAttackingThemselves defender
 | 
					                 let embed = Embeds.pickHack "Attack" atkr defender false
 | 
				
			||||||
              |> function
 | 
					                 ctx.FollowUp(embed) |> Async.AwaitTask
 | 
				
			||||||
                 | Ok atkr ->
 | 
					             | Error msg -> sendFollowUpMessage ctx msg
 | 
				
			||||||
                     let embed = Embeds.pickHack "Attack" atkr defender false
 | 
					 | 
				
			||||||
                     ctx.FollowUp(embed) |> Async.AwaitTask
 | 
					 | 
				
			||||||
                 | Error msg -> sendFollowUpMessage ctx msg
 | 
					 | 
				
			||||||
        | None ->
 | 
					 | 
				
			||||||
            if target.IsBot
 | 
					 | 
				
			||||||
                then do! sendFollowUpMessage ctx $"{target.Username} is a bot, pick a real human to hack"
 | 
					 | 
				
			||||||
                else do! sendFollowUpMessage ctx "Your target is not connected to the network, they must join first by using the /redpill command"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let handleAttack (ctx : IDiscordContext) =
 | 
					let handleAttack (ctx : IDiscordContext) =
 | 
				
			||||||
@ -208,6 +200,7 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEve
 | 
				
			|||||||
    | id when id.StartsWith("Attack")  -> handleAttack eventCtx
 | 
					    | id when id.StartsWith("Attack")  -> handleAttack eventCtx
 | 
				
			||||||
    | id when id.StartsWith("Defend")  -> handleDefense eventCtx
 | 
					    | id when id.StartsWith("Defend")  -> handleDefense eventCtx
 | 
				
			||||||
    | id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent eventCtx |> Async.StartAsTask :> Task
 | 
					    | id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent eventCtx |> Async.StartAsTask :> Task
 | 
				
			||||||
 | 
					    | id when id.StartsWith("Steal")   -> Thief.handleSteal eventCtx
 | 
				
			||||||
    | _ ->
 | 
					    | _ ->
 | 
				
			||||||
        task {
 | 
					        task {
 | 
				
			||||||
            let builder = DiscordInteractionResponseBuilder()
 | 
					            let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,8 @@ module Commands =
 | 
				
			|||||||
          Name = nickname
 | 
					          Name = nickname
 | 
				
			||||||
          Arsenal = [| hack ; shield |]
 | 
					          Arsenal = [| hack ; shield |]
 | 
				
			||||||
          Actions  = [||]
 | 
					          Actions  = [||]
 | 
				
			||||||
 | 
					          XP = 0
 | 
				
			||||||
 | 
					          Stats = PlayerStats.empty
 | 
				
			||||||
          Bank = 100<GBT> }
 | 
					          Bank = 100<GBT> }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let addHackerRole (ctx : InteractionContext) =
 | 
					    let addHackerRole (ctx : InteractionContext) =
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										102
									
								
								Bot/Thief.fs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								Bot/Thief.fs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					module Degenz.Thief
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					open System
 | 
				
			||||||
 | 
					open System.Threading.Tasks
 | 
				
			||||||
 | 
					open DSharpPlus
 | 
				
			||||||
 | 
					open DSharpPlus.Entities
 | 
				
			||||||
 | 
					open DSharpPlus.SlashCommands
 | 
				
			||||||
 | 
					open Degenz.Messaging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let getRandomStealBtnLabels () =
 | 
				
			||||||
 | 
					    let rand = Random(Guid.NewGuid().GetHashCode())
 | 
				
			||||||
 | 
					    let affirmative = [| "LFG" ; "YOLO" ; "IDGAF" |]
 | 
				
			||||||
 | 
					    let negative = [| "NOPE" ; "IM OUT" ; "BAIL" |]
 | 
				
			||||||
 | 
					    ( affirmative.[rand.Next(0, 3)] , negative.[rand.Next(0, 3)] )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let chanceOfSuccessMsg = function
 | 
				
			||||||
 | 
					    | amt when amt < 0.20 -> "Looking pretty bad"
 | 
				
			||||||
 | 
					    | amt when amt < 0.50 -> "I mean, maybe"
 | 
				
			||||||
 | 
					    | amt when amt < 0.80 -> "I think you got this"
 | 
				
			||||||
 | 
					    | _                   -> "Totally worth it"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let targetEvaluationMsg = function
 | 
				
			||||||
 | 
					    | amt when amt < 0.20 -> "but man, they look swole."
 | 
				
			||||||
 | 
					    | amt when amt < 0.50 -> "but they look a little confident"
 | 
				
			||||||
 | 
					    | amt when amt < 0.80 -> "and they're looking a little nervous"
 | 
				
			||||||
 | 
					    | _                   -> "and they look weak af man"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let payoutChance targetBank chance = targetBank * 0.1 * (1.0 - chance)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let getStealEmbed chance (target : PlayerData) (player : PlayerData) =
 | 
				
			||||||
 | 
					    let buttons =
 | 
				
			||||||
 | 
					        let yes , no = getRandomStealBtnLabels ()
 | 
				
			||||||
 | 
					        [ DiscordButtonComponent(ButtonStyle.Success, $"Steal-yes-{target.DiscordId}-{target.Name}-{chance}", yes)
 | 
				
			||||||
 | 
					          DiscordButtonComponent(ButtonStyle.Danger, $"Steal-no", no) ]
 | 
				
			||||||
 | 
					        |> Seq.cast<DiscordComponent>
 | 
				
			||||||
 | 
					    let embed =
 | 
				
			||||||
 | 
					        DiscordEmbedBuilder()
 | 
				
			||||||
 | 
					          .AddField("Chance of Success", $"{chanceOfSuccessMsg chance}", true)
 | 
				
			||||||
 | 
					          .WithDescription($"{target.Name} is coming towards you in a dark alley, {targetEvaluationMsg chance}")
 | 
				
			||||||
 | 
					          .WithImageUrl("https://cdnb.artstation.com/p/assets/images/images/017/553/457/large/maarten-hof-backalley-mainshot.jpg")
 | 
				
			||||||
 | 
					          .WithTitle($"Steal Money")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DiscordFollowupMessageBuilder()
 | 
				
			||||||
 | 
					        .AddEmbed(embed)
 | 
				
			||||||
 | 
					        .AddComponents(buttons)
 | 
				
			||||||
 | 
					        .AsEphemeral(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let steal target (ctx : IDiscordContext) =
 | 
				
			||||||
 | 
					    Game.executePlayerWithTargetAction target ctx (fun attacker defender -> async {
 | 
				
			||||||
 | 
					        let winPercentage = if attacker.Stats.Strength > defender.Stats.Strength then 1.0 else 0.0
 | 
				
			||||||
 | 
					        let embed = getStealEmbed winPercentage defender attacker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do! ctx.FollowUp(embed) |> Async.AwaitTask
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let handleSteal (ctx : IDiscordContext) =
 | 
				
			||||||
 | 
					    let split = ctx.GetInteractionId().Split("-")
 | 
				
			||||||
 | 
					    let answer = split.[1]
 | 
				
			||||||
 | 
					    if answer = "yes" then
 | 
				
			||||||
 | 
					        Game.executePlayerAction ctx (fun player -> async {
 | 
				
			||||||
 | 
					            let targetId = uint64 split.[2]
 | 
				
			||||||
 | 
					            let targetName = split.[3]
 | 
				
			||||||
 | 
					            let chance = double split.[4]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let rand = Random(Guid.NewGuid().GetHashCode())
 | 
				
			||||||
 | 
					            let result = rand.NextDouble()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if chance >= result then
 | 
				
			||||||
 | 
					                let! target = DbService.tryFindPlayer targetId
 | 
				
			||||||
 | 
					                do! Messaging.sendFollowUpMessage ctx $"You stole {targetName} money"
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                do! Messaging.sendFollowUpMessage ctx "You failed miserably"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return ()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        async {
 | 
				
			||||||
 | 
					            let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
					            builder.Content <- "I thought better of it"
 | 
				
			||||||
 | 
					            do! ctx.Respond InteractionResponseType.UpdateMessage builder |> Async.AwaitTask
 | 
				
			||||||
 | 
					        } |> Async.StartAsTask :> Task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type StealGame() =
 | 
				
			||||||
 | 
					    inherit ApplicationCommandModule ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let enforceChannel (ctx : IDiscordContext) (storeFn : IDiscordContext -> Task) =
 | 
				
			||||||
 | 
					        match ctx.GetChannel().Id with
 | 
				
			||||||
 | 
					        | id when id = GuildEnvironment.channelArmory -> storeFn ctx
 | 
				
			||||||
 | 
					        | _ ->
 | 
				
			||||||
 | 
					            task {
 | 
				
			||||||
 | 
					                let msg = $"You must go to <#{GuildEnvironment.channelArmory}> channel to buy or sell weapons"
 | 
				
			||||||
 | 
					                do! Messaging.sendSimpleResponse ctx msg
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [<SlashCommand("steal", "Steal some money from another player, but you might go to prison if caught")>]
 | 
				
			||||||
 | 
					    member this.Steal (ctx : InteractionContext, [<Option("target", "Who do you want to steal from?")>] target : DiscordUser) =
 | 
				
			||||||
 | 
					//        enforceChannel (DiscordInteractionContext ctx) (steal target)
 | 
				
			||||||
 | 
					        steal target (DiscordInteractionContext ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -67,6 +67,8 @@ let private mapBack (player : PlayerEntry) : PlayerData = {
 | 
				
			|||||||
        let atks = player.Attacks |> Array.map attackToAction
 | 
					        let atks = player.Attacks |> Array.map attackToAction
 | 
				
			||||||
        let dfns = player.Defenses |> Array.map defenseToAction
 | 
					        let dfns = player.Defenses |> Array.map defenseToAction
 | 
				
			||||||
        Array.append atks dfns
 | 
					        Array.append atks dfns
 | 
				
			||||||
 | 
					    Stats = PlayerStats.empty
 | 
				
			||||||
 | 
					    XP = 0
 | 
				
			||||||
    Bank = player.Bank * 1<GBT>
 | 
					    Bank = player.Bank * 1<GBT>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -74,12 +74,31 @@ module Types =
 | 
				
			|||||||
          Type : ActionType
 | 
					          Type : ActionType
 | 
				
			||||||
          Timestamp : DateTime }
 | 
					          Timestamp : DateTime }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    type StatAmount = int
 | 
				
			||||||
 | 
					    type XPAmount = int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    type AttributeId =
 | 
				
			||||||
 | 
					        | Strength = 0
 | 
				
			||||||
 | 
					        | Cunning = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    type PlayerStats = {
 | 
				
			||||||
 | 
					        Strength : int
 | 
				
			||||||
 | 
					        Focus : int
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    with static member empty = { Strength = 0 ; Focus = 0 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    type PlayerXP = {
 | 
				
			||||||
 | 
					        Amount : XPAmount
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [<CLIMutable>]
 | 
					    [<CLIMutable>]
 | 
				
			||||||
    type PlayerData =
 | 
					    type PlayerData =
 | 
				
			||||||
        { DiscordId : uint64
 | 
					        { DiscordId : uint64
 | 
				
			||||||
          Name : string
 | 
					          Name : string
 | 
				
			||||||
          Arsenal : BattleItem array
 | 
					          Arsenal : BattleItem array
 | 
				
			||||||
          Actions : Action array
 | 
					          Actions : Action array
 | 
				
			||||||
 | 
					          Stats : PlayerStats
 | 
				
			||||||
 | 
					          XP : int
 | 
				
			||||||
          Bank : int<GBT> }
 | 
					          Bank : int<GBT> }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module Armory =
 | 
					module Armory =
 | 
				
			||||||
@ -175,6 +194,15 @@ module Messaging =
 | 
				
			|||||||
            do! ctx.FollowUp(builder) |> Async.AwaitTask
 | 
					            do! ctx.FollowUp(builder) |> Async.AwaitTask
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let sendFollowUpEmbed (ctx : IDiscordContext) embed =
 | 
				
			||||||
 | 
					        async {
 | 
				
			||||||
 | 
					            let builder =
 | 
				
			||||||
 | 
					                DiscordFollowupMessageBuilder()
 | 
				
			||||||
 | 
					                    .AsEphemeral(true)
 | 
				
			||||||
 | 
					                    .AddEmbed(embed)
 | 
				
			||||||
 | 
					            do! ctx.FollowUp(builder) |> Async.AwaitTask
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let sendFollowUpMessageWithButton (ctx : IDiscordContext) interactiveMessage =
 | 
					    let sendFollowUpMessageWithButton (ctx : IDiscordContext) interactiveMessage =
 | 
				
			||||||
        async {
 | 
					        async {
 | 
				
			||||||
            let builder = DiscordFollowupMessageBuilder()
 | 
					            let builder = DiscordFollowupMessageBuilder()
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user