Safariタブ巡回AppleScript

「逮捕されようプロジェクト」という運動をツイッターで知ったので、その昔ある複数商品管理ページから価格を変更する案件をクラウドソーシングで請けていた知人用に作製したユーザーインターフェース自動処理 AppleScript を、先の自動投稿に関連した自分のタンブラーのタグ編集用途にしたものを紹介します。

スクリプトを実行すると、Twitterタグが付けられている最新20件の投稿を新規ウィンドウ内にタブ表示し、ウィンドウを最前面に持ってきた後にタブを巡回しながら入力要素にフォーカスしタグを削除していくというものです。

property BASE_URL : "tagged.php"
property TUM_URL1 : "https://www.tumblr.com/edit/"
property TUM_URL2 : "/?redirect_to=http%3A%2F%2Fhkitago.tumblr.com%2Fpost%2F"
property focusTagJS : "document.querySelector('.tag-input-wrapper').childNodes[0].childNodes[0].childNodes[0].childNodes[0].focus();"
try
	set tumPostIds to do shell script "curl -s " & BASE_URL
	set text item delimiters of AppleScript to ","
	set tumPostIdItems to text items of tumPostIds
	set {firstId, restId} to {item 1 of tumPostIdItems, rest of tumPostIdItems}
	tell application "Safari"
		make new document at end of documents with properties {URL:TUM_URL1 & firstId & TUM_URL2 & firstId}
		set theTabCount to number of tabs in window 1
		set pinnedTabCount to theTabCount - 1
		tell window 1
			repeat with theIds in restId
				make new tab at end of tabs with properties {URL:TUM_URL1 & theIds & TUM_URL2 & theIds}
			end repeat
		end tell
		set allTabCount to number of tabs in window 1
		set firstTabCount to pinnedTabCount + 1
		activate
		set tabcount to number of tabs in front window
		repeat with t from firstTabCount to allTabCount
			tell front window
				set current tab to tab t
			end tell
			repeat until (do JavaScript "document.readyState" in tab t of front window) is "complete" and (do JavaScript "document.contains(document.querySelector('.tag-input-wrapper'))" in tab t of front window) is true
			end repeat
			do JavaScript focusTagJS in tab t of front window
			tell application "System Events"
				key code 51
			end tell
			if (t = allTabCount) then
				tell front window
					set current tab to tab firstTabCount
				end tell
			end if
		end repeat
	end tell
end try

まず準備として BASE_URL にはカンマ区切りで投稿 ID を羅列した文字列を返す tagged.php を用意し、編集ウィンドウの URL を構成する TUM_URL1TUM_URL2 を記述しておきました。focusTagJS 定数はタグ入力要素を選択する JavaScript です。

次に CURL を使って tumPostIds 変数へ BASE_URL が返す文字列を入れるのですが、オンライン上のものを読む時は -A オプションを使った方が安全かもしれません。 tumPostIdItems 配列に変換した後に先頭の ID と2番目以降の ID に分けるのは、新規ウィンドウを作るか新規タブを作るかの分岐を目的としています。全てのタブ作成処理が終わったら activate でウィンドウが最前面に現れます。

2019年10月11日更新:「タブを固定」機能を使っている場合にうまく巡回しない問題があり、新規ウィンドウを作った直後に theTabCount を取得し固定されたタブの数を pinnedTabCount で取得しておくようにしました。

そして tabcount へウィンドウ内のタブ数を代入したら、ページの読み込みと操作する要素の有無を確認を repeat until で待機してからキャレットを移動して delete キーで既存のタグを削除し nextTab へ次のタブ数を入れ、仮に全タブ数を上回っていたら1を入れることをタブ数分繰り返し最後に先頭のタブを表示します。

2019年10月11日更新:固定タブを含む全てのタブ数をallTabCountへ、固定タブの最後に位置する最初のタブをfirstTabCountへ代入しループするようにしました。また、set current tab to tab tを処理の最初に持ってくることでキャレットの動作を待たずに速度を上げることができました。最後のタブの処理である条件を判定して先頭のタブに戻すようにしています。

以上を踏まえて例のプロジェクト用に作ったものがこちらです。

投稿者: hkitago

個人事業主でウェブと iOS, Android アプリの開発者で一児の父親。JavaScript, ActionScript, AppleScript, PHP, SQL, ObjC, Swift, Java の読書実行試験運用管理を生業とし、Bind, Postfix, Apache を MacOS で使い、エディタは Vi, mi, Kod, Smultron, TextWrangler を経て Coda, Xcode, Android Studio といった IDE と CotEditor を重用しています。Pokémon GO Trainer Code: 2491 5857 6842