Set-WallpaperClock.psm1

using namespace System.Drawing
using namespace System.Windows.Forms

Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Windows.Forms

function Set-WallpaperClock {

    <#
        .SYNOPSIS
        This function randomly generates and applies a desktop wallpaper, based on the current time.
        It has several available flavours, to suit many needs.
        The function can also tie into Azure DevOps (ADO) to get current work information.
        .DESCRIPTION
        With the most basic iteration of this function, your desktop wallpaper will be replaced with a black background that displays the time and a nice greeting.
        Admin rights are required for this to run properly. So make sure to run the function from an elevated shell.
        Note that using the -sweary option can sometimes result in quite rude words being on your screen. So use at your discretion.
        Konnecting this function to ADO is achieved by using the New-ADOCredential cmdlet and entering the details specified within its help file.
        .PARAMETER withQuote
            This switch will add a randomly generated quote to your wallpaper. This is possibly the best feature of this function.
        .PARAMETER showDevOpsInfo
            This will make an API call to your DevOps instance and will then record information about work items in the current sprint on the wallpaper.
            This is good for impressing people.
        .PARAMETER Sweary
            This will change the text on the wallpaper to include swear words. This is lots of fun but don't use it if your boss is a swear word.
    #>

    [CmdletBinding()]
    param (

        [switch]$withQuote,
        [switch]$showDevOpsInfo,
        [switch]$Sweary

    )

    begin {


        #Get public and private function definition files.
        $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )

        #Dot source the files
        Foreach ($import in @($Public)) {
            Try {
                . $import.fullname
            }
            Catch {
                Write-Error -Message "Failed to import function $($import.fullname): $_"
            }
        }

        if ($showDevOpsInfo -eq $true) {

            $Creds = . "$PSScriptRoot\Private\ADO-Creds.ps1"
            $token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($Creds.patToken)"))
            $header = @{authorization = "Basic $($Token)" }

            $ADO = Get-AzureDevOps -patToken $Creds.patToken -organizationName $Creds.organizationName -projectName $Creds.projectName -userFullName $Creds.userFullName -userPrincipalName $Creds.userPrincipalName

        }

        Set-WallpaperClasses

        $timeText = Get-Timetext

        $ProgressPreference = 'SilentlyContinue'

        $path = (Get-ChildItem -Path C:\Windows\Personalization\DesktopImage).FullName

        if (-not $path) {
            $path = "%AppData%\Microsoft\Windows\Themes\TranscodedWallpaper"
        }

        $d = Get-Date
        $suffix = Get-DateOrdinalSuffix $d
        $dateFormatted = "{0} the {2}{3} of {1:MMMM} - {4}" -f $d.DayOfWeek, $d, $d.Day, $suffix, $d.Year

        $Quote = Get-Quote

        $TimeSuffix = @(
            "roughly"
            "approximately"
            "pretty much"
            "basically"
            "essentially"
            "probably"
            "most likely"
            "kind of"
            "more or less"
            "practically"
            "somewhere around"
            "pretty near"
            "mostly"
        )

        $Firstword = Get-Word -Type KindAdjective
    }

    process {


        if (-not $sweary) {

            $lines = "
 
 
 
            $(if ((get-date -Format "dddd") -eq "Wednesday") {
                "It is Wednesday, my dude."
            } else {
                "It is $dateformatted"
            })
 
            The time is $( get-random $TimeSuffix ) $timetext
            $(if ($withQuote){"
                $('#' * (5 + ($quote | Measure-Object -Character).Characters))
                # Quote of the day:$(' ' * (($quote | Measure-Object -Character).Characters - 16))#
                # $($Quote) #
                $('#' * (5 + ($quote | Measure-Object -Character).Characters))
                "
 
            })
            $(if ($ADO) {
                "You currently have $(($ADO).count) tickets in DevOps...`n"
 
                $(if(($ADO).count -lt 20) {
                    Foreach ($item in $ADO) {
                    "`t`t#";$item; "-"
                            $UriOrga = "https://dev.azure.com/$($Creds.OrganizationName)/$($Creds.projectName)/_apis/wit/workitems/$($item)?api-version=6.0"
                            $Results= Invoke-RestMethod -Uri $UriOrga -Method Get -ContentType "application/json" -Headers $header
                            if (($Results.fields.'System.Title').Length -gt 90) {
                                ($Results.fields.'System.Title').insert(90,"-`n -")
                            } else {
                                $Results.fields.'System.Title'
                            }
                            "`r`n"
                        }
                    } else {
            "
            That's too many things to display here, just do some work.
            "
                    })
                })
            I hope you have $(Get-PrefixAorAn $Firstword) $firstword $(Get-Word Adjective) day.
        "

        }
        else {

            $lines = "
 
 
 
            $(if ((get-date -Format "dddd") -eq "Wednesday") {
                "It is Wednesday, you $(Get-Word swear)."
            } else {
                "It is $dateformatted"
            })
 
            The time is $( get-random $TimeSuffix ) $timetext
 
            $(if ($withQuote){"
                $('#' * (5 + ($quote | Measure-Object -Character).Characters))
                # Quote of the day:$(' ' * (($quote | Measure-Object -Character).Characters - 16))#
                # $($Quote) #
                $('#' * (5 + ($quote | Measure-Object -Character).Characters))
                "
            })
 
            $(if ($ADO) {
                "You currently have $(($ADO).count) $(Get-Plural (Get-word Swear)) in DevOps...`n"
 
                $(if(($ADO).count -lt 20) {
                Foreach ($item in $ADO) {
                "`t`t#";$item; "-"
                        $UriOrga = "https://dev.azure.com/$($Creds.OrganizationName)/$($Creds.projectName)/_apis/wit/workitems/$($item)?api-version=6.0"
                        $Results= Invoke-RestMethod -Uri $UriOrga -Method Get -ContentType "application/json" -Headers $header
                        if (($Results.fields.'System.Title').Length -gt 90) {
                            ($Results.fields.'System.Title').insert(90,"-`n -")
                        } else {
                            $Results.fields.'System.Title'
                        }
                        "`r`n"
                    }
                } else {
            "
            That's too many things to display here, just do some work.
            "
                })
            })
            I hope you have $(Get-PrefixAorAn $Firstword) $firstword $(Remove-Plural (get-word swear)) day.
        "

        }
    }

    end {

        export-png -InputObject $lines -Path $path
        [Wallpaper]::SetWallpaper($path)
    }
}

Function New-ADOCredential {
    <#
        .SYNOPSIS
        This function allows you to store your DevOps information to use with the Set-WallpaperClock function.
        .DESCRIPTION
        Connecting to Azure DevOps (ADO) requires a PAT token, so please follow these instructions to get one > https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows
        Using this function will store your information in a file alongside the module, on your local machine. No data is tranferred away from your machine.
        Make sure you include the correct information for each param. If you need to update your PAT token in future, use the Update-PatToken function.
        .PARAMETER patToken
            This will be your generated PAT token from ADO
        .PARAMETER organizationName
            This is the Org name for your ADO instance.
            I.E: https://dev.azure.com/$organizationName
        .PARAMETER projectName
            Surprise, surprise - this is the name of your ADO project.
            I.E: https://dev.azure.com/$organizationName/$projectName
        .PARAMETER userFullName
            This should just be your full name as it appears in ADO.
        .PARAMETER userPrincipalName
            This should be your email as it appears in ADO.
 
 
    #>

    Param
    (
        [string]$patToken,
        [string]$organizationName,
        [string]$projectName,
        [string]$userFullName,
        [string]$userPrincipalName
    )

    $CredPath = "$PSScriptRoot\Private\ADO-Creds.ps1"

    $Creds = Get-content -path $CredPath

    $Creds -replace "__patToken__", "'$patToken'"`
        -replace "__organizationName__", "'$organizationName'"`
        -replace "__projectName__", "'$projectName'"`
        -replace "__userFullName__", "'$userFullName'"`
        -replace "__userPrincipalName__", "'$userPrincipalName'" | Set-Content $CredPath
}

Function Update-ADOPATToken {
    Param
    (
        [string]$patToken
    )

    $CredPath = "$PSScriptRoot\Private\ADO-Creds.ps1"

    $Creds = Get-content -path $CredPath

    $pattern = [regex]::Escape(($Creds[0] | select-string -Pattern "`'\s*(.*?)\s*`'").matches.value)

    $Creds -replace $pattern, "$patToken" | Set-Content $CredPath

}