module Degenz.Messaging open System open System.Threading.Tasks open DSharpPlus open DSharpPlus.Entities open DSharpPlus.EventArgs open DSharpPlus.SlashCommands type InteractiveMessage = { ButtonId : string ButtonText : string Message : string } type DiscordContext = | Interaction of InteractionContext | Event of ComponentInteractionCreateEventArgs type IDiscordContext = abstract member Respond : InteractionResponseType -> Task abstract member Respond : InteractionResponseType * DiscordInteractionResponseBuilder -> Task abstract member FollowUp : DiscordFollowupMessageBuilder -> Task abstract member GetDiscordMember : unit -> DiscordMember abstract member GetGuild : unit -> DiscordGuild abstract member GetInteractionId : unit -> string abstract member GetChannel : unit -> DiscordChannel abstract member GetContext : unit -> obj type DiscordInteractionContext(ctx : InteractionContext) = interface IDiscordContext with member this.Respond responseType = async { do! ctx.Interaction.CreateResponseAsync(responseType) |> Async.AwaitTask } |> Async.StartAsTask :> Task member this.Respond (responseType, builder) = async { do! ctx.Interaction.CreateResponseAsync(responseType, builder) |> Async.AwaitTask } |> Async.StartAsTask :> Task member this.FollowUp(builder) = async { do! ctx.Interaction.CreateFollowupMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore } |> Async.StartAsTask :> Task member this.GetDiscordMember() = ctx.Member member this.GetGuild() = ctx.Guild member this.GetInteractionId() = string ctx.InteractionId member this.GetChannel() = ctx.Channel member this.GetContext() = ctx type DiscordEventContext(ctx : ComponentInteractionCreateEventArgs) = interface IDiscordContext with member this.Respond responseType = async { do! ctx.Interaction.CreateResponseAsync(responseType) |> Async.AwaitTask } |> Async.StartAsTask :> Task member this.Respond (responseType, builder) = async { do! ctx.Interaction.CreateResponseAsync(responseType, builder) |> Async.AwaitTask } |> Async.StartAsTask :> Task member this.FollowUp(builder) = async { do! ctx.Interaction.CreateFollowupMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore } |> Async.StartAsTask :> Task member this.GetDiscordMember() = ctx.User :?> DiscordMember member this.GetGuild() = ctx.Guild member this.GetInteractionId() = ctx.Id member this.GetChannel() = ctx.Channel member this.GetContext() = ctx let getTimeText isCooldown (timespan : TimeSpan) timestamp = let span = if isCooldown then timespan - (DateTime.UtcNow - timestamp) else (DateTime.UtcNow - timestamp) let plural amount = if amount = 1 then "" else "s" let ``and`` = if span.Hours > 0 then "and " else "" let hours = if span.Hours > 0 then $"{span.Hours} hour{plural span.Hours} {``and``}" else String.Empty let totalMins = span.Minutes let minutes = if totalMins > 0 then $"{totalMins} minute{plural totalMins}" else "1 minute" $"{hours}{minutes}" let getShortTimeText (timespan : TimeSpan) timestamp = let remaining = timespan - (DateTime.UtcNow - timestamp) let hours = if remaining.Hours > 0 then $"{remaining.Hours}h " else String.Empty let minutesRemaining = if remaining.Hours = 0 then remaining.Minutes + 1 else remaining.Minutes $"{hours}{minutesRemaining}min" let defer (ctx: IDiscordContext) = async { let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true do! ctx.Respond(InteractionResponseType.DeferredChannelMessageWithSource, builder) |> Async.AwaitTask } let sendSimpleResponse (ctx: IDiscordContext) msg = async { let builder = DiscordInteractionResponseBuilder() builder.Content <- msg builder.AsEphemeral true |> ignore do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask } let sendFollowUpMessage (ctx : IDiscordContext) msg = async { let builder = DiscordFollowupMessageBuilder() builder.IsEphemeral <- true builder.Content <- msg 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 = async { let builder = DiscordFollowupMessageBuilder() let button = DiscordButtonComponent(ButtonStyle.Success, interactiveMessage.ButtonId, interactiveMessage.ButtonText) :> DiscordComponent builder.AddComponents [| button |] |> ignore builder.IsEphemeral <- true builder.Content <- interactiveMessage.Message do! ctx.FollowUp(builder) |> Async.AwaitTask } let updateMessageWithGreyedOutButtons (ctx : IDiscordContext) interactiveMessage = async { let builder = DiscordInteractionResponseBuilder() let button = DiscordButtonComponent(ButtonStyle.Success, interactiveMessage.ButtonId, interactiveMessage.ButtonText, true) :> DiscordComponent builder.AddComponents [| button |] |> ignore builder.IsEphemeral <- true builder.Content <- interactiveMessage.Message do! ctx.Respond(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask }