176 lines
8.2 KiB
Forth

module Degenz.Admin
open System
open System.IO
open System.Reflection
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz.Messaging
open Npgsql.FSharp
type InitEmbeds =
| Dojo = 0
| Whitelist = 1
| Slots = 2
| JpegStore = 3
| Armory = 4
let handleGuildDownloadReady (_ : DiscordClient) (event : GuildDownloadCompletedEventArgs) =
task {
let ( _ , guild ) = event.Guilds.TryGetValue(GuildEnvironment.guildId)
let! commands = guild.GetApplicationCommandsAsync()
let ( _ , adminRole ) = guild.Roles.TryGetValue(GuildEnvironment.roleAdmin)
let permission = DiscordApplicationCommandPermission(adminRole, true)
let commands = commands |> Seq.map (fun com -> DiscordGuildApplicationCommandPermissions(com.Id, [ permission ]))
do! guild.BatchEditApplicationCommandPermissionsAsync(commands) |> Async.AwaitTask |> Async.Ignore
return ()
} :> Task
let sendEmbed embed (ctx : IDiscordContext) =
task {
match embed with
| InitEmbeds.Dojo -> Trainer.sendInitialEmbed ctx
| InitEmbeds.Whitelist -> InviteTracker.sendInitialEmbed ctx
| InitEmbeds.Slots -> SlotMachine.sendInitialEmbedFromSlashCommand ctx
| InitEmbeds.JpegStore -> Store.sendBackalleyEmbed ctx
| InitEmbeds.Armory -> Store.sendArmoryEmbed ctx
| _ -> ()
do! Messaging.sendSimpleResponse ctx "Sent!"
} :> Task
let getAllReactions (msg : DiscordMessage) (ctx : IDiscordContext) : Task<DiscordUser seq> =
task {
let mutable listOfUsers = ResizeArray<DiscordUser>(512)
let mutable emojiCount = 0
for reaction in msg.Reactions do
let mutable count = 0
for r in 0..reaction.Count / 100 do
let rs =
if count > 0 then
msg.GetReactionsAsync(reaction.Emoji, 100, listOfUsers.[listOfUsers.Count - 1].Id) |> Async.AwaitTask |> Async.RunSynchronously
else
msg.GetReactionsAsync(reaction.Emoji, 100) |> Async.AwaitTask |> Async.RunSynchronously
listOfUsers.AddRange(rs)
count <- count + 1
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
builder.Content <- $"Reading {emojiCount + 1} out of {msg.Reactions.Count}: {reaction.Emoji.Name} with {reaction.Count} reactions"
do! ctx.FollowUp builder
do! Async.Sleep 1800
emojiCount <- emojiCount + 1
return listOfUsers
}
let getCsvFromUsersList users =
users
|> List.ofSeq
|> List.map (fun (user : DiscordUser) ->
{| Id = user.Id ; FullName = $"{user.Username}#{user.Discriminator}" |})
|> List.distinctBy (fun u -> u.Id)
|> List.map (fun user -> $"{user.Id},{user.FullName}")
|> String.concat "\n"
|> (+) "Discord Id Num,Username\n"
let getCsvFromTotalInvites (users : {| Username : string ; DiscordId : uint64 ; TotalInvites : int |} seq) =
users
|> Seq.map (fun user -> $"{user.DiscordId},{user.Username},{user.TotalInvites}")
|> String.concat "\n"
|> (+) "Discord Id Num,Username,Total Invites\n"
let getUserInvites userIds =
GuildEnvironment.connectionString
|> Sql.connect
|> Sql.parameters [ "dids" , Sql.stringArray (userIds |> Seq.toArray) ]
|> Sql.query """
SELECT usr.display_name, inviter, count(invite_id) AS total_invites FROM invite
JOIN invited_user iu ON invite.id = iu.invite_id
JOIN "user" usr ON invite.inviter = usr.discord_id
WHERE iu.accepted = true AND inviter = ANY (@dids)
GROUP BY inviter, usr.display_name
ORDER BY total_invites DESC;
"""
|> Sql.executeAsync (fun read -> {| Username = read.string "display_name" ; DiscordId = read.string "inviter" |> uint64 ; TotalInvites = read.int "total_invites" |})
|> Async.AwaitTask
let getUsersFromMessageReactions (channel : DiscordChannel) (message : string) (ctx : IDiscordContext) =
task {
do! Messaging.defer ctx
let ( result , mId ) = UInt64.TryParse(message)
if result then
let! msg = channel.GetMessageAsync(message |> uint64) |> Async.AwaitTask
let! reactions = getAllReactions msg ctx
let csv = getCsvFromUsersList reactions
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
use ms = new MemoryStream(Text.Encoding.ASCII.GetBytes(csv)) :> Stream
let dict = Collections.Generic.Dictionary()
dict.Add("users.csv", ms)
builder.AddFiles(dict) |> ignore
do! ctx.FollowUp(builder)
else
do! Messaging.sendSimpleResponse ctx $"The message ID provided was not a valid: {message}"
} :> Task
let getUserInvitesFromReactions (channel : DiscordChannel) (message : string) (ctx : IDiscordContext) =
task {
do! Messaging.defer ctx
let! msg = channel.GetMessageAsync(message |> uint64) |> Async.AwaitTask
let! users = getAllReactions msg ctx
try
let! invites = getUserInvites (users |> Seq.map (fun u -> string u.Id))
let csv = getCsvFromTotalInvites (invites)
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
use ms = new MemoryStream(Text.Encoding.ASCII.GetBytes(csv)) :> Stream
let dict = Collections.Generic.Dictionary()
dict.Add("users.csv", ms)
builder.AddFiles(dict) |> ignore
do! ctx.FollowUp(builder)
with ex ->
printfn $"exception {ex.Message}"
} :> Task
type AdminBot() =
inherit ApplicationCommandModule ()
let enforceAdmin (ctx : IDiscordContext) (adminFn : IDiscordContext -> Task) =
let isAdmin = Seq.exists (fun (role : DiscordRole) -> role.Id = GuildEnvironment.roleAdmin) (ctx.GetDiscordMember().Roles)
if isAdmin then
adminFn ctx
else
Messaging.sendSimpleResponse ctx $"You are not admin" |> Async.StartAsTask :> Task
[<SlashCommandPermissions(Permissions.Administrator)>]
[<SlashCommand("admin-invites", "Get total invites from a specific user", false)>]
member this.GetAttributions (ctx : InteractionContext, [<Option("player", "The player you want to check")>] user : DiscordUser) =
enforceAdmin (DiscordInteractionContext ctx) (InviteTracker.getInvitedUsersForId user)
[<SlashCommandPermissions(Permissions.Administrator)>]
[<SlashCommand("admin-whitelist-stock", "Set whitelist stock", false)>]
member this.SetStock (ctx : InteractionContext, [<Option("amount", "Set the amount of WL available for purchase")>] amount : int64) =
enforceAdmin (DiscordInteractionContext ctx) (InviteTracker.setCurrentWhitelistStock (int amount * 1<GBT>))
[<SlashCommandPermissions(Permissions.Administrator)>]
[<SlashCommand("admin-send-embed", "Set whitelist stock", false)>]
member this.SendEmbedToChannel (ctx : InteractionContext, [<Option("embed", "Which embed to send")>] embed : InitEmbeds) =
enforceAdmin (DiscordInteractionContext ctx) (sendEmbed embed)
[<SlashCommandPermissions(Permissions.Administrator)>]
[<SlashCommand("admin-get-msg-reactions", "Set whitelist stock", false)>]
member this.GetMessageReactions (ctx : InteractionContext,
[<Option("channel", "The channel where the message is")>] channel : DiscordChannel,
[<Option("message-id", "The ID of the message with all the reactions")>] messageId : string) =
enforceAdmin (DiscordInteractionContext ctx) (getUsersFromMessageReactions channel messageId)
[<SlashCommandPermissions(Permissions.Administrator)>]
[<SlashCommand("admin-get-invites-table", "Invites Table", false)>]
member this.GetInvitesFromReactedMessages (ctx : InteractionContext,
[<Option("channel", "The channel where the message is")>] channel : DiscordChannel,
[<Option("message-id", "The ID of the message with all the reactions")>] messageId : string) =
enforceAdmin (DiscordInteractionContext ctx) (getUserInvitesFromReactions channel messageId)