OS X 10.10からJavaScript for Automation (通称 JXA)が導入されて、オートメーションのためのスクリプトがより書きやすくなりました。
そのJXAを使って、「AppStore.appを起動させてから、購入済みのアプリをInstalllさせる」というスクリプトを書いてみました。
その結果が次の地味なgifです。購入済みリストからMouseposéを探しだしてインストールさせています。
JXAスクリプトとChefやAnsibleなんかを組み合わせると、自動でストアアプリも入れられるようになる…かもしれません。
前提
- OS X 10.10 以降であること
- AppStore.appが特定のApple IDでサインイン済みであること
- Accessibilityでスクリプトを実行するアプリケーション (Terminal.appやScript Editor.appなど) に許可があること
インタプリタの起動
導入部分は次の記事が詳しいので参考にしてください。
とりあえず今回はコンソールからインタラクティブモードで実行させます。
$ osascript -l JavaScript -i
インタプリタ上で次のようにするだけで、AppStoreアプリが起動します。
>> Application("App Store").activate()
=> true
最小化ボタンのクリック
AppStore.appではapp.buttons[1]が最小化ボタンのようだったので、試しにスクリプトで最小化ボタンをクリックさせてみました。
>> system = Application("System Events")
=> Application("System Events")
>> app = system.processes["App Store"].windows["App Store"]
=> Application("System Events").processes.byName("App Store").windows.byName("App Store")
>> app.buttons[1].description()
=> "minimize button"
>> app.buttons[1].click()
=> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").buttons.at(1)
Application('System Events')はUIオートメーションのためのアプリケーションで、このアプリから他のプロセスのウィンドウを触りにいっています。
UI要素の列挙
app.entireContents()でUI要素のツリー内容がダンプされます。このコマンドで操作したい対象の見当をつけるとよいでしょう。
>> app.entireContents() # ↓ 実際は改行や整列なし
=> [Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(0).groups.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(0).groups.at(0).buttons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(0).groups.at(0).buttons.at(1),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(1),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(1).radioButtons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(2),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(2).radioButtons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(3),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(3).radioButtons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(4),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(4).radioButtons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(5),
:
なお、properties()やattributes()で、オブジェクトの属性などの列挙もできるようです。
- app.properties()
- app.attributes()
Purchasesボタンのクリック
上のタブから「購入済み」ボタンをクリックさせてみます。先ほどのダンプからそれっぽいものを見つけて、description()してみます。
>> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(4).radioButtons.at(0).description()
=> "Purchases"
Purchasesなので、これをクリックさせてみましょう。
>> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(4).radioButtons.at(0).click()
ちなみに、上記の式は次の式と等価です。
>> app.toolbars[0].groups[4].radioButtons[0].click()
これで、「購入済み」タブに移動して、例えば次のような購入済みのアプリの一覧が表示されるはずです。
Installボタンのクリック
「購入済み」タブで、再度app.entireContents()してみます。
>> app.entireContents()
=> [...
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).uiElements.byName("Mouseposé"),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).uiElements.byName("Mouseposé").groups.at(0),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).uiElements.byName("Mouseposé").groups.at(0).images.at(0),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé"),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé").uiElements.byName("Mouseposé"),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé").uiElements.byName("Mouseposé").staticTexts.byName("Mouseposé"),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(1),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(1).staticTexts.byName("Boinx Software"),
:
適当にアプリ名を取ってみましょう。
>> app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.name()[0] => "Mouseposé"
このリストから、直でボタンクリックしたかったのですが、columns.at(0).uiElements.at(8)あたりの要素をクリックしてもうまくいかなかったので、アプリ詳細ページに飛ばして、そこでインストールすることにしました。
>> app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé").uiElements.byName("Mouseposé").staticTexts.byName("Mouseposé").click()
=> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé").uiElements.byName("Mouseposé").staticTexts.byName("Mouseposé")
移動後に、
>> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).groups.at(0).groups.at(0).buttons.at(0).description()
=> "Install, Mouseposé, Free"
>> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).groups.at(0).groups.at(0).buttons.at(0).click()
=> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).groups.at(0).groups.at(0).buttons.at(0)
でインストールすることができました。
まとめ
上記の結果をまとめて関数化してみました。
通信状態によって、画面遷移に時間がかかることがあるので、てきとうにdelay()で数秒待ったりしてやって、できたスクリプトが次の通りです。
function installFromAppStore(targetAppName) {
var AppStoreApp = "App Store"
var app = Application("System Events").processes[AppStoreApp].windows[AppStoreApp]
Application(AppStoreApp).activate()
delay(3)
app.toolbars[0].groups[4].radioButtons[0].click()
delay(3)
var tableRows = app.groups[0].groups[0].scrollAreas[0].uiElements[0].tables[0].columns[0].uiElements
var len = tableRows.length
for (var i = 1; i < len; ++i) {
var elem = tableRows[i].lists[0].groups[0].uiElements
var name = elem.name()[0]
if (name === targetAppName) {
elem.byName(targetAppName).uiElements.byName(targetAppName).staticTexts.byName(targetAppName).click()
delay(3)
app.groups[0].groups[0].scrollAreas[0].uiElements[0].groups[0].groups[0].buttons[0].click()
delay(3)
break
}
}
}
installFromAppStore("Mouseposé")
問題点
上記のスクリプトでストアアプリをインストールすることはできましたが、今のところ次のような問題があります。
- 現状、最大1つしかインストールできない (同時にやって大丈夫かどうか不明)
- インストール完了を知る術がない
- AppStoreのレイアウトが変わるたびに追従させる必要がある
- インストール自動化したりしたら、Appleに怒られちゃうかもしれない
(おまけ) アプリの検索
検索フィールドの文字列を取ったり、
>> app.toolbars.at(0).groups.at(6).textFields.at(0).value() => "foobar" // 検索フィールドにてきとうに入れてみた文字列
検索フィールドに文字列を入れたり、
>> app.toolbars.at(0).groups.at(6).textFields.at(0).value = "newValue!!" => "newValue!!"
検索フィールドの文字列で検索できたりします。
>> app.toolbars.at(0).groups.at(6).textFields.at(0).buttons.at(0).click()
関連リンク
- 知らないうちにMacがシステム標準でJavaScriptで操作できるようになってた (JXA) - Qiita
- JavaScript for Automation Release Notes
- Getting Started with JavaScript for Automation on Yosemite – MacStories
- Home · dtinth/JXA-Cookbook Wiki · GitHub
- JavaScript for OSX Automation AppleScriptの代替をJavaScriptでやるサンプル Advent Calendar 2014 - Qiita




0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。