functions/Watch-DbaUpdate.ps1

Function Watch-DbaUpdate
{
<#
.SYNOPSIS
Just for fun - checks the PowerShell Gallery ever few hours for updates to dbatools - notifies max every 6 hours.
 
.DESCRIPTION
Only supports Windows 10. Not sure how to make the notification last longer (like Slack does).
     
Anyone know how to make it clickable so that it opens an URL?
 
.NOTES
Tags: JustForFun
dbatools PowerShell module (https://dbatools.io, clemaire@gmail.com)
Copyright (C) 2016 Chrissy LeMaire
 
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
 
.LINK
https://dbatools.io/Watch-DbaUpdate
 
.EXAMPLE
Watch-DbaUpdate
 
Watches the gallery for updates to dbatools.
#>
    
    BEGIN
    {
        function Show-Notification (
            $title = "dbatools update",
            $text = "Version $galleryversion is now available"
        )
        {
            $null = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
            $templatetype = [Windows.UI.Notifications.ToastTemplateType]::ToastImageAndText02
            $template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent($templatetype)
            
            #Convert to .NET type for XML manipuration
            $toastXml = [xml]$template.GetXml()
            $null = $toastXml.GetElementsByTagName("text").AppendChild($toastXml.CreateTextNode($title))
            
            $image = $toastXml.GetElementsByTagName("image")
            # unsure why $PSScriptRoot isnt't working here
            $base = $module.ModuleBase
            
            $image.setAttribute("src", "$base\bin\thor.png")
            $image.setAttribute("alt", "thor")
            
            #Convert back to WinRT type
            $xml = New-Object Windows.Data.Xml.Dom.XmlDocument
            $xml.LoadXml($toastXml.OuterXml)
            
            $toast = [Windows.UI.Notifications.ToastNotification]::new($xml)
            $toast.Tag = "PowerShell"
            $toast.Group = "PowerShell"
            # This doesn't work
            $toast.ExpirationTime = [DateTimeOffset]::Now.AddHours(6)
            
            $notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($text)
            $notifier.Show($toast)
        }
        
        function Create-Task
        {
            $script = {
                try
                {
                    # create a task, check every 3 hours
                    $action = New-ScheduledTaskAction –Execute 'powershell.exe' -Argument '–NoProfile -NoLogo -NonInteractive -WindowStyle Hidden Watch-DbaUpdate'
                    $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).Date -RepetitionInterval (New-TimeSpan -Hours 3)
                    $principal = New-ScheduledTaskPrincipal -LogonType S4U -UserId (whoami)
                    $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([timespan]::Zero) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable -DontStopOnIdleEnd
                    $task = Register-ScheduledTask -Principal $principal -TaskName 'dbatools version check' -Action $action -Trigger $trigger -Settings $settings -ErrorAction Stop
                    return $true    
                }
                catch
                {
                    return $false
                }
            }
            
            # Needs admin creds to setup the kind of PowerShell window that doesn't appear for a millisecond
            # which is a millisecond too long
            If (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
            {
                return (Start-Process powershell -Verb runAs -ArgumentList $script.tostring() -ErrorAction Stop)
            }
            else
            {
                return (Invoke-Command -ScriptBlock $script -ErrorAction Stop)
            }
        }
    }
    
    PROCESS
    {
        if (([Environment]::OSVersion).Version.Major -lt 10)
        {
            Write-Warning "This command only supports Windows 10 and above"
            return
        }
        
        if ($null -eq (Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue))
        {
            Write-Warning "Watch-DbaUpdate runs as a Scheduled Task which must be created. This will only happen once."
            
            $result = Create-Task
            
            if ($result -eq $false)
            {
                Write-Warning "Couldn't create task :("
                return
            }
            else
            {
                $module = Get-Module -Name dbatools
                Write-Warning "Task created! A notication should appear momentarily. Here's something cute to look at in the interim."
                Show-Notification -title "dbatools ❤ you" -text "come hang out at dbatools.io/slack"
                return
            }
        }
        
        # leave this in for the scheduled task
        $module = Get-Module -Name dbatools
        
        if (!$module)
        {
            Import-Module dbatools
            $module = Get-Module -Name dbatools
        }
        
        $galleryversion = (Find-Module -Name dbatools -Repository PSGallery).Version
        $localversion = $module.Version
        
        if ($galleryversion -le $localversion) { return }
        
        $file = "$env:LOCALAPPDATA\dbatools\watchupdate.xml"
        
        $new = [pscustomobject]@{
            NotifyTime = (Get-Date)
            NotifyVersion = $galleryversion
        }
        
        if (Test-Path $file)
        {
            $old = Import-Clixml -Path $file -ErrorAction SilentlyContinue
            
            if ($old.NotifyTime -lt (Get-Date).AddHours(-6))
            {
                Export-Clixml -InputObject $new -Path $file
                Show-Notification
            }
        }
        else
        {
            $directory = Split-Path $file
            
            if (!(Test-Path $directory))
            {
                $null = New-Item -ItemType Directory -Path $directory
            }
            
            Export-Clixml -InputObject $new -Path $file
            Show-Notification
        }
    }
}