Discordant.psm1

<#
    Code in this file will be added to the beginning of the .psm1. For example,
    you should place any using statements here.
#>

Function Connect-Discord {
    [cmdletbinding(
        DefaultParameterSetName = 'Rest'
    )]
    param (
        [Parameter(
            Mandatory,
            ParameterSetName = 'Rest'
        )]
        [switch]$RestClient,
        [Parameter(
            Mandatory,
            ParameterSetName = 'Socket'
        )]
        [switch]$SocketClient,
        [Discord.TokenType]$TokenType,
        [string]$Token,
        [switch]$Quiet
    )

    switch ($PSCmdlet.ParameterSetName) {
        'Rest' {
            $global:DiscordClient = [Discord.Rest.DiscordRestClient]::new()
        }
        'Socket' {
            $global:DiscordClient = [Discord.WebSocket.DiscordSocketClient]::new()
        }
    }
    $DiscordClient.LoginAsync($TokenType, $Token, $true).Wait()
    if (-not $Quiet.IsPresent) {
        $DiscordClient.LoginState
    }
}
Function Send-DiscordMessage {
    [OutputType([Discord.Rest.RestUserMessage])]
    [cmdletbinding()]
    param (
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-channelId'
        )]
        [Discord.Rest.RestGuild]$Guild,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-channelId'
        )]
        [uint64]$GuildId,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-channelId'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-channelId'
        )]
        [uint64]$ChannelId,
        [Parameter(
            Mandatory,
            ParameterSetName = 'channelObj'
        )]
        [Discord.Rest.RestTextChannel]$Channel,
        [Discord.RequestOptions]$RequestOptions,
        [Parameter(
            Mandatory
        )]
        [string]$MessageText
    )
    if ($PSCmdlet.ParameterSetName -like 'guildId*') {
        $Guild = Get-DiscordGuild -Id $GuildId
    }
    
    if ($PSCmdlet.ParameterSetName -like '*channelId') {
        $Channel = Get-DiscordChannel -Guild $Guild -ChannelId $ChannelId
    }

    $task = $Channel.SendMessageAsync($MessageText)
    $task.Wait()
    $task.Result
}
Function Get-DiscordChannel {
    [OutputType([Discord.Rest.RestGuildChannel[]], ParameterSetName = 'guildObj-all')]
    [OutputType([Discord.Rest.RestGuildChannel[]], ParameterSetName = 'guildId-all')]
    [OutputType([Discord.Rest.RestGuildChannel], ParameterSetName = 'guildObj-channelId')]
    [OutputType([Discord.Rest.RestGuildChannel], ParameterSetName = 'guildId-channelId')]
    [cmdletbinding()]
    param (
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-all'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-channelId'
        )]
        [Discord.Rest.RestGuild]$Guild,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-all'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-channelId'
        )]
        [uint64]$GuildId,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-channelId'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-channelId'
        )]
        [uint64]$ChannelId,
        [Discord.RequestOptions]$RequestOptions
    )
    if ($PSCmdlet.ParameterSetName -like 'guildId*') {
        $Guild = Get-DiscordGuild -Id $GuildId
    }

    $task = if ($PSCmdlet.ParameterSetName -like '*all') {
        $Guild.GetChannelsAsync($RequestOptions)
    } else {
        $Guild.GetChannelAsync($ChannelId, $RequestOptions)
    }
    $task.Wait()
    $task.Result | Sort-Object { $_.GetType().Name }
}
Function New-DiscordChannel {
    [OutputType([Discord.Rest.RestTextChannel], ParameterSetName = 'guildObj-text')]
    [OutputType([Discord.Rest.RestTextChannel], ParameterSetName = 'guildId-text')]
    [OutputType([Discord.Rest.RestVoiceChannel], ParameterSetName = 'guildObj-voice')]
    [OutputType([Discord.Rest.RestVoiceChannel], ParameterSetName = 'guildId-voice')]
    [cmdletbinding()]
    param (
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-text'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-voice'
        )]
        [Discord.Rest.RestGuild]$Guild,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-text'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-voice'
        )]
        [uint64]$GuildId,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-text'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-text'
        )]
        [switch]$TextChannel,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-voice'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-voice'
        )]
        [switch]$VoiceChannel,
        [Parameter(
            ParameterSetName = 'guildObj-text'
        )]
        [Parameter(
            ParameterSetName = 'guildId-text'
        )]
        [Discord.TextChannelProperties]$TextChannelProperties = $null,
        [Parameter(
            ParameterSetName = 'guildObj-voice'
        )]
        [Parameter(
            ParameterSetName = 'guildId-voice'
        )]
        [Discord.VoiceChannelProperties]$VoiceChannelProperties = $null,
        [Parameter(Mandatory)]
        [string]$Name
    )
    if ($PSCmdlet.ParameterSetName -like 'guildId*') {
        $Guild = Get-DiscordGuild -Id $GuildId
    }

    switch ($PSCmdlet.ParameterSetName.Split('-')[1]) {
        'text' {
            $task = $Guild.CreateTextChannelAsync($Name, $TextChannelProperties)
        }
        'voice' {
            $task = $Guild.CreateVoiceChannelAsync($Name, $VoiceChannelProperties)
        }
    }

    $task.Wait()
    $task.Result
}
Function New-DiscordTextChannelProperties {
    [OutputType([Discord.TextChannelProperties])]
    [cmdletBinding()]
    param (
        [string]$Topic,
        [switch]$Nsfw,
        [int]$SlowModeInterval,
        [switch]$Archived,
        [switch]$Locked,
        [string]$Name,
        [int]$Position,
        [int64]$CategoryId,
        [Discord.Overwrite]$Overwrite
    )
    $props = [Discord.TextChannelProperties]::new()
    foreach ($key in $PSBoundParameters.Keys) {
        $props.$key = $PSBoundParameters[$key]
    }
    $props
}
Function New-DiscordVoiceChannelProperties {
    [OutputType([Discord.VoiceChannelProperties])]
    [cmdletBinding()]
    param (
        [int]$Bitrate,
        [UInt64]$CategoryId,
        [string]$Name,
        [Discord.Overwrite[]]$PermissionOverwrites,
        [int]$Position,
        [string]$RTCRegion,
        [int]$UserLimit
    )
    $props = [Discord.VoiceChannelProperties]::new()
    foreach ($key in $PSBoundParameters.Keys) {
        $props.$key = $PSBoundParameters[$key]
    }
    $props
}
Function Remove-DiscordChannel {
    [cmdletbinding()]
    param (
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj'
        )]
        [Discord.Rest.RestGuild]$Guild,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId'
        )]
        [string]$GuildId,
        [Parameter(Mandatory)]
        [ulong]$ChannelId,
        [switch]$Async
    )
    if ($PSCmdlet.ParameterSetName -eq 'guildId') {
        $guild = Get-DiscordGuild -Id $GuildId
    }

    $channel = Get-DiscordChannel -Guild $guild -ChannelId $ChannelId
    $task = $channel.DeleteAsync()
    if (-not $Async.IsPresent) {
        $task.Wait()
    }
}
Function Set-DiscordChannel {
    [cmdletbinding(
        DefaultParameterSetName = 'notype'
    )]
    param (
        # Category, Text, Voice
        [Parameter(
            ParameterSetName = 'textChannel',
            Mandatory
        )]
        [Parameter(
            ParameterSetName = 'voiceChannel',
            Mandatory
        )]
        [Parameter(
            ParameterSetName = 'notype',
            Mandatory
        )]
        [UInt64]$ChannelId,
        # Category, Text, Voice
        [Parameter(
            ParameterSetName = 'textChannel',
            Mandatory
        )]
        [Parameter(
            ParameterSetName = 'voiceChannel',
            Mandatory
        )]
        [Parameter(
            ParameterSetName = 'notype',
            Mandatory
        )]
        [UInt64]$GuildId,
        # Category, Text, Voice
        [Parameter(
            ParameterSetName = 'textChannel'
        )]
        [Parameter(
            ParameterSetName = 'voiceChannel'
        )]
        [Parameter(
            ParameterSetName = 'notype'
        )]
        [string]$Name,
        # Category, Text, Voice
        [Parameter(
            ParameterSetName = 'textChannel'
        )]
        [Parameter(
            ParameterSetName = 'voiceChannel'
        )]
        [Parameter(
            ParameterSetName = 'notype'
        )]
        [int]$Position,
        # Category, Text, Voice
        [Parameter(
            ParameterSetName = 'textChannel'
        )]
        [Parameter(
            ParameterSetName = 'voiceChannel'
        )]
        [Parameter(
            ParameterSetName = 'notype'
        )]
        [UInt64]$CategoryId,
        # Category, Text, Voice
        [Parameter(
            ParameterSetName = 'textChannel'
        )]
        [Parameter(
            ParameterSetName = 'voiceChannel'
        )]
        [Parameter(
            ParameterSetName = 'notype'
        )]
        [Discord.OverwritePermissions]$PermissionOverwrites,
        # Text
        [Parameter(
            ParameterSetName = 'textChannel'
        )]
        [string]$Topic,
        # Text
        [Parameter(
            ParameterSetName = 'textChannel'
        )]
        [bool]$IsNsfw,
        # Text
        [Parameter(
            ParameterSetName = 'textChannel'
        )]
        [int]$SlowModeInterval,
        # Text
        [Parameter(
            ParameterSetName = 'textChannel'
        )]
        [bool]$Locked,
        # Text
        [Parameter(
            ParameterSetName = 'textChannel'
        )]
        [Discord.ThreadArchiveDuration]$AutoArchiveDuration,
        # Voice
        [Parameter(
            ParameterSetName = 'voiceChannel'
        )]
        [int]$Bitrate,
        # Voice
        [Parameter(
            ParameterSetName = 'voiceChannel'
        )]
        [int]$UserLimit,
        # Voice
        [Parameter(
            ParameterSetName = 'voiceChannel'
        )]
        [string]$RTCRegion,
        [Discord.RequestOptions]$RequestOptions = $null,
        [switch]$Async
    )
    # Get the channel
    $channel = Get-DiscordChannel -ChannelId $ChannelId -GuildId $GuildId
    Write-Verbose "Parameter set: $($PSCmdlet.ParameterSetName)"
    # start the script block string array
    $scriptBlockStr = @('param ($props)')
    # collect the valid properties
    switch ($PSCmdlet.ParameterSetName) {
        'notype' {
            $cp = [Discord.GuildChannelProperties]::new()
        }
        'textChannel' {
            $cp = [Discord.TextChannelProperties]::new()
        }
        'voiceChannel' {
            $cp = [Discord.VoiceChannelProperties]::new()
        }
    }
    # append the array
    foreach ($key in ($PSBoundParameters.Keys | Where-Object { $cp.PSObject.Properties.Name -contains $_ })) {
        $scriptBlockStr += "`$props.$key = `$$key"
    }
    # build the script block
    $sb = [scriptblock]::Create(($scriptBlockStr -join "`n"))
    # create the action
    $action = [System.Action[Discord.TextChannelProperties]]$sb
    # call modifyAsync
    $task = $channel.ModifyAsync($action)
    if (-not $Async.IsPresent) {
        $task.Wait()
    }
}
Function Get-DiscordGlobalCommand {
    [OutputType([Discord.Rest.RestApplicationCommand], ParameterSetName = 'byId')]
    [OutputType([Discord.Rest.RestApplicationCommand[]], ParameterSetName = 'all')]
    [cmdletbinding(
        DefaultParameterSetName = 'all'
    )]
    param (
        [Parameter(
            ParameterSetName = 'byId'
        )]
        [ValidateNotNullOrEmpty()]
        [UInt64]$Id,
        [Discord.RequestOptions]$RequestOptions
    )
    switch ($PSCmdlet.ParameterSetName) {
        'all' {
            $task = $DiscordClient.GetGlobalApplicationCommandsAsync($RequestOptions)
        }
        'byId' {
            $task = $DiscordClient.GetGlobalApplicationCommandAsync($Id, $RequestOptions)
        }
    }
    $task.Wait()
    $task.Result
}
Function Get-DiscordGuildCommand {
    [OutputType([Discord.Rest.RestApplicationCommand], ParameterSetName = 'guildId-byId')]
    [OutputType([Discord.Rest.RestApplicationCommand], ParameterSetName = 'guildObj-byId')]
    [OutputType([Discord.Rest.RestApplicationCommand[]], ParameterSetName = 'guildId-all')]
    [OutputType([Discord.Rest.RestApplicationCommand[]], ParameterSetName = 'guildObj-all')]
    [cmdletbinding(
        DefaultParameterSetName = 'guildId-all'
    )]
    param (
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-all'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-byId'
        )]
        [ValidateNotNullOrEmpty()]
        [UInt64]$GuildId,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-all'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-byId'
        )]
        [ValidateNotNullOrEmpty()]
        [Discord.Rest.RestGuild]$Guild,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-byId'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-byId'
        )]
        [ValidateNotNullOrEmpty()]
        [UInt64]$CommandId,
        [Discord.RequestOptions]$RequestOptions
    )
    if ($PSCmdlet.ParameterSetName -like 'guildObj-*') {
        $GuildId = $Guild.Id
    }
    $task = $DiscordClient.GetGuildApplicationCommands($GuildId, $RequestOptions)
    $task.Wait()
    if ($PSCmdlet.ParameterSetName -like '*all') {
        $task.Result
    } else {
        $task.Result.Where({ $_.Id -eq $CommandId })
    }
}
Function New-DiscordGlobalCommand {
    [OutputType([Discord.Rest.RestGlobalCommand])]
    [cmdletbinding()]
    param (
        [Discord.ApplicationCommandType]$Type = 'Slash',
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Description
    )

    switch ($Type) {
        'Slash' {
            $cb = [Discord.SlashCommandBuilder]::new()
            $cb.Name = $Name
            $cb.Description = $Description
        }
        default {
            Throw "Command type '$Type' not yet implemented in module."
        }
    }
    $task = $DiscordClient.CreateGlobalApplicationCommand($cb.Build())
    $task.Wait()
    $task.Result
}
Function New-DiscordGuildCommand {
    [OutputType([Discord.Rest.RestGuildCommand])]
    [cmdletbinding(
        DefaultParameterSetName = 'byId'
    )]
    param (
        [Parameter(
            Mandatory,
            ParameterSetName = 'byId'
        )]
        [ValidateNotNullOrEmpty()]
        [UInt64]$GuildId,
        [Parameter(
            Mandatory,
            ParameterSetName = 'byObj'
        )]
        [ValidateNotNullOrEmpty()]
        [Discord.Rest.RestGuild]$Guild,
        [Discord.ApplicationCommandType]$Type = 'Slash',
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Description,
        [Discord.SlashCommandBuilder]$CommandBuilder
    )
    if ($PSCmdlet.ParameterSetName -eq 'byId') {
        $Guild = Get-DiscordGuild -Id $GuildId
    }

    switch ($Type) {
        'Slash' {
            if ($PSBoundParameters.Keys -notcontains 'CommandBuilder') {
                $cb = [Discord.SlashCommandBuilder]::new()
                $cb.Name = $Name
                $cb.Description = $Description
            } else {
                $cb = $CommandBuilder
            }
        }
        default {
            Throw "Command type '$Type' not yet implemented in module."
        }
    }
    $task = $Guild.CreateApplicationCommandAsync($cb.Build())
    $task.Wait()
    $task.Result
}
Function New-DiscordSlashCommand {
    [OutputType([Discord.SlashCommandBuilder])]
    [cmdletbinding(
        DefaultParameterSetName = 'byId'
    )]
    param (
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [string]$Description,
        [Alias('CommandOptions')]
        [Discord.SlashCommandOptionBuilder[]]$Options
    )
    $cb = [Discord.SlashCommandBuilder]::new()
    $cb.Name = $Name
    $cb.Description = $Description

    if ($PSBoundParameters.Keys -contains 'Options') {
        $cb.Options = $Options
    }

    $cb
}
Function New-DiscordSlashCommandOption {
    [OutputType([Discord.SlashCommandOptionBuilder])]
    [cmdletbinding()]
    param (
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [string]$Description,
        [switch]$IsAutocomplete,
        [switch]$Required,
        [hashtable]$Choices,
        [Discord.ApplicationCommandOptionType]$Type

    )
    $co = [Discord.SlashCommandOptionBuilder]::new()

    $co.Name = $Name

    if ($PSBoundParameters.Keys -contains 'Description') {
        $co.Description = $Description
    }

    if ($PSBoundParameters.Keys -contains 'IsAutocomplete') {
        $co.IsAutocomplete = $IsAutocomplete.IsPresent
    }

    if ($Required.IsPresent) {
        $co.IsRequired = $true
    }

    if ($PSBoundParameters.Keys -contains 'Type') {
        $co.Type = $Type
    }

    if ($PSBoundParameters.Keys -contains 'Choices') {
        foreach ($key in $Choices.Keys) {
            $co.AddChoice($key, $Choices[$key]) | Out-Null
        }
    }

    $co
}
Function Remove-DiscordGuildCommand {
    [cmdletbinding(
        DefaultParameterSetName = ''
    )]
    param (
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-commandId'
        )]
        [ValidateNotNullOrEmpty()]
        [UInt64]$GuildId,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-commandId'
        )]
        [ValidateNotNullOrEmpty()]
        [Discord.Rest.RestGuild]$Guild,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-commandId'
        )]
        [ValidateNotNullOrEmpty()]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-commandId'
        )]
        [ValidateNotNullOrEmpty()]
        [ulong]$CommandId,
        [Parameter(
            Mandatory,
            ParameterSetName = 'commandObj'
        )]
        [ValidateNotNullOrEmpty()]
        [Discord.Rest.RestGuildCommand]$Command
    )

    if ($PSCmdlet.ParameterSetName -like 'guildId-*') {
        $Guild = Get-DiscordGuild -Id $GuildId
    }

    if ($PSCmdlet.ParameterSetName -like '*-commandId') {
        $task = $Guild.GetApplicationCommandAsync($CommandId, $null)
        $task.Wait()
        $Command = $task.Result
    }

    $task = $Command.DeleteAsync()
    $task.Wait()
    $task.Result
}
Function Get-DiscordGuild {
    [OutputType([Discord.Rest.RestGuild], ParameterSetName = 'byId')]
    [OutputType([Discord.Rest.RestGuild[]], ParameterSetName = 'all')]
    [cmdletbinding(
        DefaultParameterSetName = 'all'
    )]
    param (
        [Parameter(
            ParameterSetName = 'byId'
        )]
        [ValidateNotNullOrEmpty()]
        [UInt64]$Id,
        [Discord.RequestOptions]$RequestOptions = $null
    )
    switch ($PSCmdlet.ParameterSetName) {
        'all' {
            $task = $DiscordClient.GetGuildsAsync($RequestOptions)
        }
        'byId' {
            $task = $DiscordClient.GetGuildAsync($Id, $RequestOptions)
        }
    }
    $task.Wait()
    $task.Result
}
Function Parse-DiscordInteraction {
    [OutputType([Discord.Rest.RestSlashCommand])]
    [cmdletbinding()]
    param (
        [string]$PublicKey,
        [string]$Signature,
        [string]$TimeStamp,
        [string]$Body
    )
    $task = $DiscordClient.ParseHttpInteractionAsync($PublicKey, $Signature, $TimeStamp, $Body)
    $task.Wait()
    $task.Result
}
Function Test-DiscordInteraction {
    [OutputType([bool])]
    [cmdletbinding()]
    param (
        [string]$PublicKey,
        [string]$Signature,
        [string]$TimeStamp,
        [string]$Body
    )
    if (-not (Get-Variable -Name DiscordClient -Scope Global -ErrorAction Ignore)) {
        $DiscordClient = [Discord.Rest.DiscordRestClient]::new()
    }
    $DiscordClient.IsValidHttpInteraction($PublicKey, $Signature, $TimeStamp, $Body)
}
Function Get-DiscordRole {
    [OutputType([Discord.Rest.RestRole[]], ParameterSetName = 'guildObj-all')]
    [OutputType([Discord.Rest.RestRole[]], ParameterSetName = 'guildId-all')]
    [OutputType([Discord.Rest.RestRole], ParameterSetName = 'guildObj-roleId')]
    [OutputType([Discord.Rest.RestRole], ParameterSetName = 'guildId-roleId')]#>
    [cmdletbinding()]
    param (
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-all'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-roleId'
        )]
        [Discord.Rest.RestGuild]$Guild,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-all'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-roleId'
        )]
        [uint64]$GuildId,
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildObj-roleId'
        )]
        [Parameter(
            Mandatory,
            ParameterSetName = 'guildId-roleId'
        )]
        [uint64]$RoleId
    )
    if ($PSCmdlet.ParameterSetName -like 'guildId*') {
        $Guild = Get-DiscordGuild -Id $GuildId
    }

    if ($PSCmdlet.ParameterSetName -like '*all') {
        $Guild.Roles.ForEach({ $_ })
    } else {
        $Guild.Roles.Where({ $_.Id -eq $RoleId })
    }
}
<#
    Code in this file will be added to the end of the .psm1. For example,
    you should set variables or other environment settings here.
#>


# Load Assemblies
$libPath = Join-Path $PSScriptRoot lib
Get-ChildItem $libPath -Filter *.dll | ForEach-Object {
    Add-Type -Path $_.FullName
}