Backlogのデスクトップ通知

## はじめに
Backlogを利用しているのですが諸事情でアプリ版を使用できずブラウザ版を使用しております。
ブラウザ版だとメール通知のみでデスクトップ通知に対応していないため、デスクトップ通知ツールを作成しました。

## サンプルコード
お知らせをウォッチして未読があればデスクトップ通知するサンプルコードです。
**※定期ウォッチするにはタスクスケジューラ等で定期実行する必要があります**

```get-backlog-unread.ps1
function Get-Backlog-Notification() {
    param (
        [string]$api_url = 'https://jamt.backlog.com/',
        [string]$api_key = '{APIキーを設定}'
    )

    # 通知済リスト取得
    $noticed_list  = @();
    $filename = $path + '\tmp\noticed_list'
    if (test-path '.\tmp') {
        if (test-path $fileName) {
            $file = New-Object System.IO.StreamReader($fileName, [System.Text.Encoding]::GetEncoding("utf-8"))
            while (($line = $file.ReadLine()) -ne $null)
            {
                $noticed_list += $line
            }
            Write-Host("")
            $file.Close()
            new-item -Path $fileName -Type File -Force
        } else {
            new-item -Path $fileName -Type File
        }
    } else {
        new-item -Path .\tmp -Type Directory
        new-item -Path $fileName -Type File
    }

    # お知らせ一覧の取得（https://developer.nulab.com/ja/docs/backlog/api/2/get-notification/）
    [string]$url = $api_url + 'api/v2/notifications?apiKey=' + $api_key
    $res = Invoke-WebRequest -ContentType "application/json; charset=utf-8" $url

    # 文字化け対応
    $jsonCorrected = [Text.Encoding]::UTF8.GetString(
        [Text.Encoding]::GetEncoding(28591).GetBytes($res.Content)
    )
    $jsonCorrected | ConvertFrom-Json | ForEach-Object {
        $_ | ForEach-Object {
            # 値取得
            $alreadyRead = $_.alreadyRead
            $issueKey = $_.issue.issueKey
            $summary = $_.issue.summary
            $commentId = $_.comment.id
            $content = $_.comment.content
    
            # 未読判定
            # if ($alreadyRead -eq 'True') {
            if ($alreadyRead -ne 'True') {
                # 通知済判定
                if (!($noticed_list.Contains($issueKey))) {
                    # 通知
                    $subject = 'Balcklog'
                    $message = '[' + $issueKey + ']' + $summary + "`r`n" + $content
                    $callback_command = 'start ' + $api_url + 'view/' + $issueKey + '#comment-' + $commentId
                    echo $callback_command
                    .\desktop-notification.ps1 $message $subject $callback_command
                }
                
                # 通知済リスト更新
                $file = New-Object System.IO.StreamWriter($fileName, $true, [System.Text.Encoding]::GetEncoding("utf-8"))
                $file.WriteLine($issueKey)
                Write-Host("")
                $file.Close()
            }
        }
    }
}

# 実行中のパス取得/移動
$path = Split-Path -Parent $MyInvocation.MyCommand.Path
Set-Location $path

Get-Backlog-Notification
```

```desktop-notification.ps1
#reference from: https://swfz.hatenablog.com/entry/2021/05/19/200001

param(
    [string]$Prompt = 'メッセージ',
    [string]$Title  = '通知',
    $CallBack = ''
)

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

function Show-NotifyIcon {
    param(
        [string]$Prompt = 'メッセージ',
        [string]$Title  = '通知',
        [scriptblock]$CallBack = {}
    )
    
    # [System.Drawing.Icon]::ExtractAssociatedIcon('.\backlog.ico')
    [Windows.Forms.NotifyIcon]$notifyIcon =
        New-Object -TypeName Windows.Forms.NotifyIcon -Property @{
            BalloonTipIcon  = [Windows.Forms.ToolTipIcon]::Info
            BalloonTipText  = $Prompt
            BalloonTipTitle = $Title
            Icon    = [Drawing.SystemIcons]::Information
            Text    = $Title
            Visible = $true
        }
    
    # イベント定義
    $notifyIcon.add_BalloonTipClicked( $CallBack )
    
    [int]$timeout = 5 # sec

    [DateTimeOffset]$finishTime = 
        [DateTimeOffset]::UtcNow.AddSeconds( $timeout )

    $notifyIcon.ShowBalloonTip( $timeout )
    
    # そのままだとイベントが走らない＆すぐに消えてしまうので適当wait
    while ( [DateTimeOffset]::UtcNow -lt $finishTime ) {
        Start-Sleep -Milliseconds 1
    }
    # $notifyIcon.Dispose()
}

$parameters = $MyInvocation.BoundParameters
$parameters.CallBack = [scriptblock]::Create( $CallBack )
Show-NotifyIcon @parameters
```

```launch_ps.js
var shell = WScript.CreateObject("WScript.Shell");
var args = WScript.Arguments;

var cmd = ""
for(var i=0; i < args.length; i++) {
  cmd += " " + args(i)
}
var ret = shell.Run("powershell.exe -ExecutionPolicy RemoteSigned -File" + cmd, 0, true);
WScript.Quit(ret);
```

## 各ファイルの説明
| ファイル名 | 処理概要 |
| ---- | ---- |
| get-backlog-unread.ps1 | Backlog APIを実行し、未読のお知らせを取得します。<br>※BacklogのAPIキーを取得して設定する必要があります |
| desktop-notification.ps1 | `get-backlog-unread.ps1`で呼び出し、デスクトップ通知を表示します。 |
| launch_ps.js | ps1ファイルをタスクスケジューラで実行すると前面で処理されてしまい、作業の邪魔でしかないためWSH経由でバックグラウンド実行するバッチです。 |

Backlog APIについては[こちら](https://developer.nulab.com/ja/docs/backlog/#)をご参照ください。
通知は一律でのInformationアイコンになってしまい、変更することができないのが非常に残念です。。。