PowerShell:Windows ストアのアプリの更新履歴をチェックする

執筆日時:

f:id:daruyanagi:20170303143002p:plain

これをチェックするツールがほしかったんだけど、WPF や ASP.NET Web Pages ではストアに「おまえ BOT だろ」と怒られてうまくいかなかった。けれど、似たようなコードを PowerShell で試すと、なぜかうまくいく……。原因はわかんないけど(調べる気が起きない)、PowerShell でできるんなら、PowerShell でやるか―って感じで頑張ってみた。

基本方針は

って感じ。

function Check-WindowsStore
{
$urls =@(
"https://www.microsoft.com/ja-jp/store/p/swarm/9wzdncrdrsq1",
:(途中省略)
)

重複は無駄なので削る

$urls = $urls | Get-Unique

foreach ($url in $urls) { try {

ストアからテキストをダウンロード

$request = Invoke-WebRequest -Uri $url

アプリの表示名を記憶

$title = $request.AllElements .FindById("page-title").innerText

新しいバージョンノートを記憶

$new = $request.AllElements .FindById("version-notes").innerText

if ($new) {

最初だけ:更新履歴を保存する .temp フォルダーを掘る

$path = $env:USERPROFILE | Join-Path -ChildPath ".temp" if (Test-Path $path) { } else { New-Item $path -ItemType Directory }

バージョンノートを保存するファイル名を記憶

(パスに使えない文字は排除しておく)

$path = $path | Join-Path -ChildPath ($title -replace "/", " " -replace ":", " ") $old_path = $path + ".old.txt" # 質素な差分管理 $new_path = $path + ".txt"

前の更新履歴が保存されていたら……

if (Test-Path $new_path) {

名前を変えて保存

$old = [System.IO.File]::ReadAllText($new_path)

if ($old -eq $new) { $status = "-" # 変化なし

} else { [System.IO.File]:: WriteAllText($old_path, $old) [System.IO.File]:: WriteAllText($new_path, $new) $status = "*" # 変化あり } } else # 新しい更新履歴 { [System.IO.File]::WriteAllText($new_path, $new) $status = "+" # 新規作成 } } else # $new が nil { $status = "?" # そもそもリリースノートがない }

[PSCustomObject] @{ Status = $status; Title = $title; Url=$url } } catch [Exception] { $status = "!" # エラー

[PSCustomObject] @{ Status = $status; Title = $title; Url=$url } } finally { Write-Host "$status $url" $request = $null } }

Write-Host "Complete" }

この前は Slack のバージョン履歴が更新されたので、

Check-WindowsStore
| where { $_.Status -ne "?" }
| where { $_.Status -ne "-" }
| Format-Table

を実行すると、

Status Title Url
------ ----- ---
*      Slack https://www.microsoft.com/ja-jp/store/p/……

が出力される。

個人的お勉強ポイント

PowerShell はいろいろ省略記法とかあって、カッコよく書くのはまだまだ難しいなって思った。ぼちぼちうまくなりたい。

エスケープは ` で(今回は結局使わなかったけど)

タブは `t で入力できる。改行は `n だけど、CRLF にしたかったら `r`n にする。

ループでオブジェクトをポイポイすれば yield return みたいになる

$result = @()

に結果をストアしていかなくてもいいみたい。なーんだ!

JoinPath は | で繋ぐとネストが避けられて美しい

sevenb.jp

Invoke-WebRequest はすっげえ便利

特定の id の中身がほしい場合はこれだけ。

$request = Invoke-WebRequest $url
$request.AllElements.FindById("page-title").innerText

リンクや画像だけをストアしてるメンバーもある!

XPath 使えたらもっと便利なのになぁ(涎