functions/Sync-QuibbleTask.ps1


function Sync-QuibbleTask {
<#
    .SYNOPSIS
        Synchronize Microsoft To Do tasks and Habitica todos
 
    .DESCRIPTION
        Synchronize Microsoft To Do tasks and Habitica todos
 
    .PARAMETER Confirm
        Confirm to proceed synchronization
 
    .PARAMETER Bidirectional
        Synchronize Microsoft To Do tasks and Habitica todos Bidirectionally
 
    .PARAMETER WhatIf
        Dry-Run the synchronization
 
    .NOTES
 
    .LINK
        https://github.com/tiksn/quibble
 
    .EXAMPLE
        Sync-QuibbleTask -Verbose
 
        Synchronize all to-dos
#>

    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'Low'
    )]
    param (
        [Parameter()]
        [switch]
        $Bidirectional
    )
    
    try {
        $habiticaCredentialSecretName = Get-PSFConfigValue -FullName quibble.Secrets.HabiticaCredentialSecretName -NotNull
        [pscredential]$habiticaCredential = Get-Secret -Name $habiticaCredentialSecretName
        if ($null -eq $habiticaCredential) {
            throw 'Habitica Credential is null'
        }
        if ($IsLinux -or $IsMacOS) {
            $habiticaCredentialPlain = [PSCustomObject] @{
                UserName = $habiticaCredential.UserName
                Password = $habiticaCredential.GetNetworkCredential().Password
            }
            [PSCustomObject]$habiticaCredential = $habiticaCredentialPlain
        }
        Connect-Habitica -Credential $habiticaCredential
        $hUser = Get-HabiticaUser
        Write-Information "Habitica user is $($hUser.profile.name)"

        Connect-MgGraph -Scopes @('User.Read', 'Tasks.Read', 'Tasks.ReadWrite')
        $mgUser = Get-MgUser
        Write-Information "Microsoft Graph user is $($mgUser.DisplayName)"   
    
        $msLists = Get-MgUserTodoList -UserId $mgUser.Id -All
    
        $hTags = Get-HabiticaTag
        $hTodos = Get-HabiticaTask -Type todos
        $hCompletedTodos = Get-HabiticaTask -Type completedTodos
    
        $associations = @()
    
        foreach ($msList in $msLists) {
            if ($msList.IsOwner -and ($msList.WellknownListName -eq 'none')) {
                foreach ($hTag in $hTags) {
                    if ($msList.DisplayName.Contains($hTag.name)) {
                        $associations += [PSCustomObject]@{
                            MsTodoList  = $msList
                            HabiticaTag = $hTag
                        }
                    }
                }
            }
        }
    
        foreach ($association in $associations) {
            $msTodoListTasks = Get-MgUserTodoListTask -TodoTaskListId $association.MsTodoList.Id -UserId $mgUser.Id -All
            Write-PSFMessage -Level SomewhatVerbose -Message "Microsoft To-Do List '$($association.MsTodoList.DisplayName)'"
            foreach ($msTodoListTask in $msTodoListTasks) {
                if (-not $msTodoListTask.Recurrence.Pattern.Type) {
                    Write-PSFMessage -Level SomewhatVerbose -Message "Microsoft To-Do '$($msTodoListTask.Title)' $($msTodoListTask.Status)"
                    if ($msTodoListTask.Status -eq 'completed') {
                        foreach ($hTodo in $hTodos) {
                            if ($hTodo.text -eq $msTodoListTask.Title) {
                                Write-PSFMessage -Level SomewhatVerbose -Message "Habitica To-Do '$($hTodo.text)' will be completed"
                                if ($PSCmdlet.ShouldProcess(
                                        "Habitica To-Do '$($hTodo.text)' will be completed",
                                        $hTodo.text,
                                        'Complete')) {
                                    $hTodo | Complete-HabiticaTask
                                    Write-PSFMessage -Level SomewhatVerbose -Message "Habitica To-Do '$($hTodo.text)' completed"
                                }
                            }
                        }
                    }
                    elseif ($msTodoListTask.Status -eq 'notStarted') {
                        $msTodoListTaskTitle = $msTodoListTask.Title.Normalize([System.Text.NormalizationForm]::FormD)
                        $msTodoListTaskTitle = $msTodoListTaskTitle.Replace("’", "'")
                        $uni = [System.Text.Encoding]::Unicode.GetBytes($msTodoListTaskTitle)
                        $ascii = [System.Text.Encoding]::ASCII.GetString($uni)
                        $msTodoListTaskTitle = $ascii.Normalize([System.Text.NormalizationForm]::FormD)
                        $msTodoListTaskTitle = $msTodoListTaskTitle.Replace("$([char]0x0000)", '')
                        $msTodoListTaskTitle = $msTodoListTaskTitle.Replace('', '')
                        $hTodo = $hTodos | Where-Object { $PSItem.text -eq $msTodoListTaskTitle }
                        $hCompletedTodo = $hCompletedTodos | Where-Object { $PSItem.text -eq $msTodoListTaskTitle }
                        if ((-not $hTodo) -and (-not $hCompletedTodo)) {
                            Write-PSFMessage -Level SomewhatVerbose -Message "Habitica To-Do '$msTodoListTaskTitle' will be created"
                            if ($PSCmdlet.ShouldProcess(
                                    "Habitica To-Do '$($msTodoListTaskTitle)' will be created",
                                    $msTodoListTaskTitle,
                                    'Create')) {
                                New-HabiticaTask -Type todo -Tags $association.HabiticaTag.id -Text $msTodoListTaskTitle -Notes $msTodoListTask.Body.Content
                                Write-PSFMessage -Level SomewhatVerbose -Message "Habitica To-Do '$msTodoListTaskTitle' created"
                            }
                        }
                    }
                }
            }

            if ($Bidirectional) {
                Start-Sleep -Seconds 60
                $msTodoListTasks = Get-MgUserTodoListTask -TodoTaskListId $association.MsTodoList.Id -UserId $mgUser.Id -All
                $hTodos = Get-HabiticaTask -Type todos
                $hCompletedTodos = Get-HabiticaTask -Type completedTodos

                $hTodos = $hTodos | Where-Object { $PSItem.tags -contains $association.HabiticaTag.id }
                $hCompletedTodos = $hCompletedTodos | Where-Object { $PSItem.tags -contains $association.HabiticaTag.id }

                foreach ($hTodo in $hTodos) {
                    if (-not ($msTodoListTasks | Where-Object { $PSItem.Title -eq $hTodo.text })) {
                        $hTodoText = $hTodo.text
                        $hTodoNotes = $hTodo.notes
                        Write-PSFMessage -Level SomewhatVerbose -Message "Microsoft To-Do '$hTodoText' will be created"
                        if ($PSCmdlet.ShouldProcess(
                                "Microsoft To-Do '$($hTodoText)' will be created",
                                $hTodoText,
                                'Create')) {
                            $msTodoListTaskBody = [Microsoft.Graph.PowerShell.Models.MicrosoftGraphItemBody]::new()
                            $msTodoListTaskBody.Content = $hTodoNotes
                            $msTodoListTaskBody.ContentType = 'text'
                            New-MgUserTodoListTask -TodoTaskListId $association.MsTodoList.Id -UserId $mgUser.Id -Title $hTodoText -Body $msTodoListTaskBody
                            Write-PSFMessage -Level SomewhatVerbose -Message "Microsoft To-Do '$hTodoText' created"
                        }
                    }
                }
            }
        }
    }
    catch {
        throw $_
    }
    
}