PushOverAndOver.psm1

enum PushoverSound {
    Alien
    Bike
    Bugle
    Cashregister
    Classical
    Climb
    Cosmic
    Echo
    Falling
    Gamelan
    Incoming
    Intermission
    Magic
    Mechanical
    None
    Persistent
    Pianobar
    Pushover
    Siren
    Spacealarm
    Tugboat
    Updown
    Vibrate
}

enum PushoverPriority {
    NoAlert = -2
    Quite = -1
    Normal = 0
    HighPriority = 1
    HighPriorityAndConfirmation = 2
}

$PushoverCredentials = "./.pushover.cred"
function Send-PushoverNotification {
    <#
    .SYNOPSIS
    Function for sending notifications via Pushover service.
    See https://pushover.net/

    .DESCRIPTION
    You can send Notification or Glance.
    Notification is displayed as a popup or in notification area on your device.
    Glance is displayed on small displays like Apple Watch without sound or vibration.

    .PARAMETER User
    Pushover user name, usually starts with "u".

    .PARAMETER ApiToken
    Pushover API token generated when creating an application, usually starts with "a".

    .PARAMETER Glance
    Switch indicating that a glance will be sent instead of ordinary notification. The glance is displayed on small devices like smart watches.
#>



    [CmdletBinding(SupportsShouldProcess = $true)] 
    Param(
        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]
        $User,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]
        $ApiToken,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [Io.FileInfo]
        $CredentialsPath,  

        [Parameter(
            Mandatory = $true,
            Position = 0,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Message to be sent",
            ParameterSetName = "Notification")]
        [ValidateNotNullOrEmpty()]
        [string]
        $Message,

        [Parameter(
            Mandatory = $false,
            Position = 1,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Title for your message")]
        [string]
        $Title,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Url which will be sent with the message",
            ParameterSetName = "Notification")]
        [ValidateNotNullOrEmpty()]
        [uri]
        $Url,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Title for your message",
            ParameterSetName = "Notification")]
        [string]
        $UrlTitle,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Device to deliver your notification to")]
        [string]
        $Device,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Attached image file",
            ParameterSetName = "Notification")]
        [Io.FileInfo]
        $Attachment,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Sound to play",
            ParameterSetName = "Notification")]
        [PushoverSound]
        $Sound,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Notification priority",
            ParameterSetName = "Notification")]
        [PushoverPriority]
        $Priority,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Timestamp for your message",
            ParameterSetName = "Notification")]
        [datetime]
        $Timestamp,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Retry interval is seconds for HighPriorityAndConfirmation notifications",
            ParameterSetName = "Notification")]
        [int]
        $Retry,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "How many seconds your notification will continue to be retried",
            ParameterSetName = "Notification")]
        [int]
        $Expire,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Notification confirmation callback URL",
            ParameterSetName = "Notification")]
        [uri]
        $Callback,

        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "Glance")]
        [switch]
        $Glance = $false,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "Glance")]
        [string]
        $Text,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "Glance")]
        [string]
        $Subtext,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "Glance")]
        [int]
        $Count,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "Glance")]
        [int]
        $Percent



    )

    $attrs = $PSCmdlet.MyInvocation.BoundParameters

    if ( $User -and $ApiToken ) {
        $attrs.Add("User", $User)
        $attrs.Add("Token", $ApiToken)
        Write-Debug "Setting credentials from parameters"
    }
    elseif ( $CredentialsPath -and (Test-Path -PathType Leaf -Path $CredentialsPath) ) { 
        $cred = Import-Clixml -Path $CredentialsPath
        $attrs.Add("User", $cred.UserName)
        $attrs.Add("Token", ($cred.Password | ConvertFrom-SecureString -AsPlainText) )
        Write-Debug "Reading credentials from file $CredentialsPath"
    }
    elseif ( Test-Path -Path $PushoverCredentials ) {
        $cred = Import-Clixml -Path $PushoverCredentials
        $attrs.Add("User", $cred.UserName)
        $attrs.Add("Token", ($cred.Password | ConvertFrom-SecureString -AsPlainText) )
        Write-Debug "Reading credentials from file $PushoverCredentials"
    }
    else {
        Write-Error "User and/or ApiToken parameters are missing - add them as parameters or run Set-PushoverCredentials function to persistently store them."
        return
    }

    $body = [System.Net.Http.MultipartFormDataContent]::new()
    
    $url = $Glance ? 'https://api.pushover.net/1/glances.json' : 'http://api.pushover.net/1/messages.json'

    if ($Attachment) {
        $fileStream = [System.IO.FileStream]::new($Attachment.FullName, [System.IO.FileMode]::Open)
        $header = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
        $header.Name = "attachment"
        $header.FileName = $Attachment.Name
        $content = [System.Net.Http.StreamContent]::new($FileStream)
        $content.Headers.ContentDisposition = $header
        $content.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse("image/jpeg")
        $body.Add($content)
    }

    $validParams = @{ 
        Message   = "message"
        Device    = "device"
        Title     = "title"
        Url       = "url"
        UrlTitle  = "url_title"
        Timestamp = "timestamp"
        Priority  = "priority"
        Sound     = "sound" 
        User      = "user"
        Token     = "token"
        Expire    = "expire"
        Retry     = "retry"
        Callback  = "callback"
        Text      = "text"
        Subtext   = "subtext"
        Count     = "count"
        Percent   = "percent"
    }



    if ($attrs.Priority) { $attrs.Priority = [int]$attrs.Priority }
    if ($attrs.Sound) { $attrs.Sound = ([string]$attrs.Sound).toLower() }
    if ($attrs.Timestamp) { 
        $date0 = Get-Date -Date '01/01/1970'
        $attrs.Timestamp = (New-TimeSpan -Start $date0 -End $attrs.Timestamp).TotalSeconds 
    }

    foreach ($param in $validParams.GetEnumerator()) {
        $val = $attrs[$param.Name] 
        if ( $null -ne $val) {
    
            $header = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
            $header.Name = $param.Value
            $content = [System.Net.Http.StringContent]::new($val)
            $content.Headers.ContentDisposition = $header
            $body.Add($content)
            Write-Debug "Send-PushoverNotification, param: $($param.Value) = $val"
        }

    } 

    if ($PSCmdlet.ShouldProcess($attrs.Message, "Sending notification using Pushover service")) {
        Invoke-RestMethod -Uri $url -Method Post -Body $body
    }
}

function Set-PushoverCredentials {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true,
            Position = 0,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Pushover user name (30 chars) "u..."')]
        [ValidateNotNullOrEmpty()]
        [string]
        $User,

        [Parameter(Mandatory = $true,
            Position = 1,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Pushover API Token (30 chars) "a..."')]
        [ValidateNotNullOrEmpty()]
        [string]
        $ApiToken,

        [Parameter(Mandatory = $false)]
        [switch]
        $Force = $false
    )

    if ( ( Test-Path -Path $PushoverCredentials) -and -not ($Force) ) {
        Write-Error "Pushover credentials file $PushoverCredentials alredy exists, use -Force if you want to re-create the file"
        return
    }

    $ApiTokenSecure = ConvertTo-SecureString -String $ApiToken -AsPlainText -Force
    
    New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $ApiTokenSecure | Export-Clixml -Path $PushoverCredentials
}