Write-Slack.psm1

class SlackMessage
{
    #region Fields
    [string] $channel
    [string] $text
    [string] $icon_emoji
    [string] $icon_url
    [string] $username
    [SlackAttachment[]] $attachments
    [bool]$unfurl_links = $true
    #endregion

    #region Public Methods
      
    AddAttachment([Object] $input)            
    {
        $a = New-Object SlackAttachment
        if($input.GetType().IsValueType -or $input.GetType().ToString() -eq "System.string")
        {
            $a.text = $input
        }
        else
        {
            $a.SetProperties($input)
        }
        $this.AddAttachment($a)
    }
    AddAttachment([Object[]] $input)
    {
        foreach($i in $input)
        {
            $this.AddAttachment($i)
        }
    }
      
    AddAttachment([Object] $input, [String[]] $names)
    {
        $a = New-Object SlackAttachment
        $a.SetProperties($input,$names)
        $this.AddAttachment($a)
    }
    AddAttachment([Object[]] $input, [String[]] $names)
    {
        foreach($i in $input)
        {
            $this.AddAttachment($i,$names)
        }
    }
      
    AddAttachment([SlackAttachment] $input)
    {
        $this.attachments += $input
        if($this.Attachments.Count -ge 100)
            {throw new "No more than 100 attachments are allowed"}
    }
      
    [string] GetJson()
    {
        return $this.GetJson(5)
    }
    [string] GetJson([int] $Depth)
    {
        $newValue=""
        $value = $this | ConvertTo-Json -Depth $Depth
        foreach($s in $value.Split("`n"))
        {
            if(-not($s -match ".*null.*" ))
            {
                $newValue += $s
            }
        }

        return $newValue
    }
      
    SetChannel([string] $input)
    {
        if($input[0] -eq '@' -or $input[0] -eq '#')
        { 
            $this.channel = $input
        }
        else 
        {
            $this.channel = '#'+$input
        }
    }

    SetColor([string] $input)
    {
        foreach($a in $this.attachments)
        {
            $a.SetcColor($input)
        }
    }

    Send()
    {
        Set-StrictMode -Off
   
        $ErrorActionPreference = "Continue"
        $Site = $Global:SlackWebHookUri
        $ErrorActionPreference = "Stop"

        $this.Send($site)
    }
    Send([string] $site)
    {
        if(-not($site)){Throw New-Object System.ArgumentNullException "`$URI Parameter must be provided or `$Global:SlackWebHookUri must be set."}
        $json =$this.GetJson()
        $result = Invoke-WebRequest -Method POST -ContentType "application/json" -Body $json -Uri $site
    }
      
    #endregion
}

class SlackAttachment
{
    #region Fields
      
    [string]$fallback
    [string]$color
    [string]$pretext
    [string]$author_name
    [string]$author_link
    [string]$author_icon
    [string]$title
    [string]$title_link
    [string]$text
    [SlackField[]]$fields
    [string]$image_url
    [string]$thumb_url
    [string]$footer
    [string]$footer_icon
    [int] $ts
    [bool]$unfurl_links
      
    #endregion
            
    #region Public Methods
      
    SetTimeStamp([datetime] $value)
    { $this.ts = [int](get-date($value).ToUniversalTime() -UFormat %s) }
            
    SetColor([string] $input)
    {
        if($input -match '\#?[A-Fa-f0-9]{6}')
        {
            if($input[0] -eq '#')
                { $this.color = $input }
            else
                { $this.color = '#'+$input }
        }
        elseif ($input-match '^(\d{1,3}\,\d{1,3}\,\d{1,3})$')
        {
            $RGB = $input.split(",")
            $red = [convert]::Tostring($RGB[0],16)
            $green = [convert]::Tostring($RGB[1],16)
            $blue  = [convert]::Tostring($RGB[2],16)

            if ($red.Length -eq 1)
                {$red = '0' + $red}
            if ($green.Length -eq 1)
                {$green = '0'+$green}
            if ($blue.Length -eq 1)
                {$blue = '0'+$blue}

            $this.SetColor(('#'+$red+$green+$blue))
        }
        else
            { $this.SetcColor($input) }
    }
      
    #endregion
      
    #region Private Methods
      
    hidden SetProperties([Object] $input)
    {
        $this.SetProperties($input, $null)
    }
      
    hidden SetProperties([Object] $input, [String[]] $propNames)
    {
        $this.InternalSetProperties($input, $PropNames)
    }
      
    hidden InternalSetProperties([Object] $input, [String[]] $PropNames)
    {     
        if($propNames)
            {$this.SetFromMemberDefinitions(($input | get-member -Name $PropNames), $input)}
        else
            {$this.SetFromMemberDefinitions($PropNames, $input)}
    }
      
    hidden SetFromMemberDefinitions([Microsoft.PowerShell.Commands.MemberDefinition[]] $props, [Object] $input)
    {
        if($props -eq $null)
        {
            $f = New-Object SlackField
            $f.title = $input.GetType().Name
            $f.value = $input.ToString()
            $this.fields+=$f
            return
        }
            
        if($props.Length -gt 100)
            {Write-Warning "Some properties will be ignored,Slack max 100"} 
            
        foreach ($p in $props)
        {
            $f = New-Object SlackField
            $f.title = $p.Name
            $f.value = $input | select -expand $p.Name
            $f.short = ($f.title.Length+$f.value.Length) -lt 50
            $this.fields+=$f
        }
    }
      
    hidden SetcColor([System.Drawing.Color] $Color)
    {
        $colorString = "#" + $Color.R.ToString("X2") + $Color.G.ToString("X2") + $Color.B.ToString("X2");
        $this.color = $colorString
    }
      
    #endregion
}

class SlackField
{
    #region Fields
      
    [string]$title
    [string]$value
    [bool]$short
      
    #endregion
}

<#
    .DESCRIPTION
        Converts object(s) to slack messages
    .PARAMETER Text
        The message you would like to send.
    .PARAMETER Payload
        The message you would like to send.
    .PARAMETER Properties
        The names of the properties that should be included.
    .PARAMETER UseAllProperties
        Use all properties.
    .Example
        ConvertTo-Slack "My Message"
    .INPUTS
        System.Object
    .OUTPUTS
        SlackMessage
    .NOTES
#>

Function ConvertTo-Slack
{
    [CmdletBinding()]
    #region Params
    Param 
    (
        [ValidateNotNullOrEmpty()]
        [Parameter( Position=0,ValueFromPipeline,HelpMessage="The message/object you would like to send.")]
        [object[]]$Payload,
            
        [ValidateNotNullOrEmpty()]
        [Parameter(HelpMessage="The message text.")]
        [string]$Text,
            
        [ValidateNotNullOrEmpty()]
        [Parameter(HelpMessage="The names of the properties that should be included (Use of payload is required.)")]
        [String[]]$Properties,
            
        [Parameter(HelpMessage="Use all properties.")]
        [switch]$UseAllProperties
    )
    #endregion
    Begin
    { 
        $ErrorActionPreference ="stop"
        $SlackMsg = New-Object SlackMessage

        if($Text)
            { $SlackMsg.Text = $Text }

        $PayloadItems=,@{}
        $PayloadItems.Clear()
    }
    Process
    {
        foreach($value in $Payload)
            { $PayloadItems+=$value }
    }
    End
    {
        $PayloadItems = $PayloadItems -ne $null
        if($PayloadItems.Count -eq 1 -and $PayloadItems[0].GetType() -eq [SlackMessage] )
            { $SlackMsg =$PayloadItems[0]-as [SlackMessage] }
        elseif($PayloadItems.Count -eq 1 -and ($PayloadItems[0].GetType().IsValueType -or $PayloadItems[0].GetType().ToString() -eq "System.string"))
            { $SlackMsg.Text = $PayloadItems[0] }
        else
        {
            foreach($value in $PayloadItems)
            {
                if($UseAllProperties)
                    { $slackMsg.AddAttachment($value,($PayloadItems[0] | Get-Member -MemberType Property | Select -Property Name -ExpandProperty Name)) }
                elseif($Properties)
                    { $slackMsg.AddAttachment($value,$Properties) }
                else
                    { $slackMsg.AddAttachment($value) }
            }
        }
        return $slackMsg
    }
}

<#
    .DESCRIPTION
        Writes to slack
    .PARAMETER Text
        The message you would like to send.
    .PARAMETER Channel
        Used to provide the '#channel' or '@indivulal'.
    .PARAMETER UserName
        The sender of the message.
    .PARAMETER Uri
        The WebHook URI - alternately you can set `$Global:SlackWebHookUri
    .PARAMETER Payload
        The message you would like to send.
    .PARAMETER Properties
        The names of the properties that should be included.
    .PARAMETER UseAllProperties
        Use all properties.
    .PARAMETER Icon_Emoji
        The emoji you would like to use for the message icon.
    .PARAMETER $Icon_Url
        The Icon url.
    .Example
        Write-Slack "My Message"
    .Example
        Write-Slack -$Payload [OBJ] -Text "My Message" -UserName "My Robot" -Channel "@mySlackUser"
#>

Function Write-Slack
{
    [CmdletBinding()]
    #region Params
    Param 
    (
        [ValidateNotNullOrEmpty()]
        [Parameter( Position=0,ValueFromPipeline,HelpMessage="The message you would like to send.")]
        [object[]]$Payload,
            
        [ValidateNotNullOrEmpty()]
        [Parameter(HelpMessage="The message text.")]
        [string]$Text,
            
        [ValidateNotNullOrEmpty()]
        [Parameter(HelpMessage="The names of the properties that should be included.")]
        [String[]]$Properties,
            
        [Parameter(HelpMessage="Use all properties.")]
        [switch]$UseAllProperties,
            
        [ValidateNotNullOrEmpty()]
        [Parameter(HelpMessage="The URL of the Slack Web Hook custom intagration - alternately you can set `$Global:SlackWebHookUri.")]
        [string]$SlackWebHookUri,
            
        [ValidateNotNullOrEmpty()]
        [Parameter(HelpMessage="Icon Emoji Text")]
        [string]$Icon_Emoji,
            
        [ValidateNotNullOrEmpty()]
        [Parameter(HelpMessage="Icon url")]
        [string]$Icon_Url,
            
        [ValidateNotNullOrEmpty()]
        [Parameter(HelpMessage="Message from name")]
        [string]$Username,
            
        [ValidateNotNullOrEmpty()]
        [Parameter(HelpMessage="The name of the channel or person you would like to send to")]
        [string]$Channel
    )
    #endregion
    Begin
    {
        $ErrorActionPreference ="stop"
        $PayloadItems=,@{}
        $PayloadItems.Clear()

        function Sendit ([SlackMessage] $SlkMsg)
        {
            if($Text)
                { $SlkMsg.Text =$Text }
            if($Icon_Emoji)
                { $SlkMsg.Icon_Emoji =$Icon_Emoji }
            if($Icon_Url)
                { $SlkMsg.Icon_Url =$Icon_Url }
            if($Username)
                { $SlkMsg.Username =$Username }
            if($Channel)
                { $SlkMsg.SetChannel($Channel) }
            if($SlackWebHookUri)
                { $SlkMsg.Send($SlackWebHookUri) }
            else
                { $SlkMsg.Send() }
        }
    }
    Process
    {           
        foreach($value in $Payload)
        {
            if($value -is [SlackMessage])
                { Sendit($value -as[SlackMessage]) }
            else
                { $PayloadItems+=$value }
        }
    }
    End
    {
        $PayloadItems = $PayloadItems -ne $null
        if($PayloadItems)
        {
            if($UseAllProperties)
                { $slackMsg = ConvertTo-Slack -payload $PayloadItems -Properties ($PayloadItems[0] | Get-Member -MemberType Property | Select -Property Name -ExpandProperty Name) }
            elseif($Properties)
                { $slackMsg = ConvertTo-Slack -payload $PayloadItems -Properties $Properties }
            else
                { $slackMsg = ConvertTo-Slack -payload $PayloadItems }

            Sendit($slackMsg)
        }
    }
}

export-modulemember -function Write-Slack
export-modulemember -function ConvertTo-Slack