Growlの知られざる機能「Rules」で自宅待機のMacをTwitterからリモートコントロールする
価値ある情報をユーザー視点で発信するブログ
もくじ

OS Xに通知の仕組みを追加するGrowl。
Growl 
カテゴリ: 仕事効率化
価格: ¥400
OS X Mavericksの今では、OSに通知の仕組み、通知センターが標準搭載されているので以前より影が薄くなっていますが、Growlの方が自由度が高く、コミュニティからも愛されているように思います。
Growlびいきの僕が気に入っている、Rulesという仕組みを紹介したいと思います。
通知の表示直前にAppleScriptを実行する機能です。ドキュメントはここにあります。Growl 2.1からの新機能のようです。
Growl – AppleScript Rules Documentation for Growl
記載がありませんが、OS X 10.8以降対応のようですね。OS X 10.7では、Rulesは動きませんでした。
似た機能にScriptActionというものがありますが、これはどうも不動作の機能のようです。少なくともOS X 10.9では動きませんでした。(続きは[Read More]から)
AppleScriptエディタでRules.scptを作成。これを以下のパスに設置。
/Users/ユーザー名/Library/Application Scripts/com.Growl.GrowlHelperApp/Rules.scpt
Growlを再起動すると、Rulesを使うかどうかの問い合わせが入り、受け入れればRulesが動作を開始し、通知を表示する直前に、毎回Rules.scptが実行されるようになります。
ちょっと隠し機能のような趣がありますね。
いったんRules.scptの存在が認識されると、Growlの「環境設定>一般」に「Use Rules」というチェックボックスが出現するので、Rulesの使用を中止したい場合は、ここのチェックボックスを外します。

何もしないRules.scptのスケルトンは以下のようになります。シンタックスハイライターがAppleScriptに対応していないので、変なハイライティングですいません。
using terms from application "Growl"
on evaluate notification with notification
--Rules go in here
--Ultimately return what you want Growl to do with the notification
end evaluate notification
end using terms from
Growl側から、仮引数 notification で通知情報が渡されてきます。
notificationの型はGrowlNotification。GrowlNotificationの持つプロパティはGrowlのAppleScript用語説明に書いてありますが、表にすると以下の通りです。
| Property | Access | Type | Description |
|---|---|---|---|
| app name | get/set | unicode text | The application name |
| host | get/set | unicode text | The computer from which the note came, localhost for the local machine |
| note description | get/set | unicode text | The note’s main text |
| note icon | get/set | Image | The icon the note will display with |
| note title | get/set | unicode text | The note’s title |
| note type | get/set | unicode text | The underlying note type |
| priority | get/set | GrowlPriority | The priority of the notification |
| sticky | get/set | boolean | Whether the notification will be sticky |
公式TwitterクライアントでMentionを受け取ったときは、notificationのプロパティは以下のようになります。
| Property | Value |
|---|---|
| app name | |
| host | localhost |
| note description | @hitoriblog 先輩はアホですね |
| note title | hitoriblog_testからの@ツイート |
Twitter
カテゴリ: Social Networking
販売元: Twitter, Inc.(サイズ: 3.9 MB)
全てのバージョンの評価: 


(667 件の評価)
通知が表示されるたびに、通知内容をAppleScriptで評価する機会を与えられるわけです。
返り値として決まったレコード(Rubyで言うハッシュ)、RuleResultを返すと、Growlの動作を変更できます。RuleResultのプロパティは、GrowlのAppleScript用語説明に詳細が載っています。

以下の例では、仮引数notificationのnote descriptionの中身を見て、「GrowlMac」が含まれるかどうかを判定しています。ignoring case構文でくくってあるので、文字列の比較においては、大文字、小文字を区別しませんが。
using terms from application "Growl"
on evaluate notification with notification
ignoring case
if notification's note description contains "GrowlMac" then
return {display:"Music Video"}
end if
end ignoring
end evaluate notification
end using terms from
if文が成立したときはRuleResultのレコード「{display:”Music Video”}」を返しているので、以降の通知の表示スタイルが「Music Video」に切り替わります。
プロパティdisplayの中身に指定できるのは、表示スタイルのリストに出てくるものです。

RuleResultで複数のプロパティを変更したい場合は、「{display:none, actions:”Speech”}」といった感じでレコードを記述します。
Rules.scptから「{notification return:notification}」といったレコードを返すと、通知の内容を書き換えてしまうこともできます。
以下は、仮引数として渡されたnotificationのdescription(通知本文)に「@Growl」が含まれているかどうか判定。含まれていたら、それを「@GrowlMac」に置換するというものです。仮引数として渡されたnotificationのdescriptionを書き換えて、「{notification return:notification}」を返り値にしていますね。AppleScriptは標準で備える文字列操作のための関数がウルトラ貧弱(!)なので、ここで併せて置換関数を定義しています。
on replace_chars(this_text, search_string, replacement_string)
set AppleScript's text item delimiters to the search_string
set the item_list to every text item of this_text
set AppleScript's text item delimiters to the replacement_string
set this_text to the item_list as string
set AppleScript's text item delimiters to ""
return this_text
end replace_chars
using terms from application "Growl"
on evaluate notification with notification
ignoring case
set description to notification's note description
if description contains "@Growl" then
set notification's note description to replace_chars(description, "@Growl", "@GrowlMac")
return {notification return:notification}
end if
end ignoring
end evaluate notification
end using terms from
処理をRuby / Pythonなどに引き継ぐ方法を考えてみました。
AppleScriptは文字列の操作が苦手(苦手なことばっかりだけど……)なので、Ruby / Pythonで処理した方がいいことが多いでしょう。
Ruby / Pythonに引き継ぎできれば、正規表現マッチングを含む文字列の処理、ネットワークを使った処理などで、できることが飛躍的に増えます。
AppleScriptはAppleScriptで、ほかのアプリケーションを使役するのが得意ですが、osascriptコマンドでRuby / Pythonから同等のことをするのも難しくありません。
仮引数notificationの主要なプロパティをどう引き継ぐかがポイントです。ここでは、環境変数に代入する手を使ってみました。

主要なプロパティを環境変数に設定した後で、Rules.scptと同じディレクトリに入れているtest.rbを呼び出しています。
on rulesdir() tell application "System Events" return quoted form of (POSIX path of (home folder) & "/Library/Application Scripts/com.Growl.GrowlHelperApp/") end tell end rulesdir using terms from application "Growl" on evaluate notification with notification do shell script " export GROWL_NOTE_DESCRIPTION=" & quoted form of notification's note description & "; export GROWL_HOST=" & quoted form of notification's host & "; export GROWL_APP_NAME=" & quoted form of notification's app name & "; export GROWL_NOTE_TITLE=" & quoted form of notification's note title & "; /usr/bin/ruby " & rulesdir() & "test.rb" end evaluate notification end using terms from
Rules.scptから呼び出されるtest.rbは以下のようになります。環境変数を参照して、Rules.scptから引き継いだプロパティを取得しています。
あとは、プロパティの中身を見て似るなり焼くなりいろんなことをすればよいということになります。以下のスクリプトは、app nameが「Twitter」で、なおかつnote titleが「hitoriblogからの@ツイート」だったら何かをする、というものです。
#!/usr/bin/ruby # -*- coding: utf-8 -*- $KCODE = 'u' require 'kconv' host = ENV['GROWL_HOST'] app_name = ENV['GROWL_APP_NAME'].toutf8 note_title = ENV['GROWL_NOTE_TITLE'].toutf8 note_description = ENV['GROWL_NOTE_DESCRIPTION'].toutf8 # Twitterアプリで @hitoriblog からのメンションの通知が来たら if app_name == "Twitter" && note_title == "hitoriblogからの@ツイート" # 何かする end
日本語が含まれる場合は、事前にUTF-8に変換しておかないとダメなようでした。
Pythonの場合は、環境変数の参照は、 os.environ[‘GROWL_APP_NAME’] といった感じになりますね。

自宅でMacを起動させっぱなしにしておき、Growlに前項で紹介したようなRules.scptとtest.rbを仕込んでおく。
公式Twitterクライアントを起動、bot用のアカウントをアクティブにしておく。

あとは、通知の本文であるnote descriptionを命令文と見立て、命令の内容に応じて何かをさせる。
#!/usr/bin/ruby
# -*- coding: utf-8 -*-
$KCODE = 'u'
require 'kconv'
my_master = "@hitoriblog"
my_account = "@hitoriblog_test"
host = ENV['GROWL_HOST']
app_name = ENV['GROWL_APP_NAME'].toutf8
note_title = ENV['GROWL_NOTE_TITLE'].toutf8
note_description = ENV['GROWL_NOTE_DESCRIPTION'].toutf8
# Twitterアプリで @hitoriblog からのメンションの通知が来たら
if app_name == "Twitter" && note_title == "hitoriblogからの@ツイート"
# tweet関数は別途定義する必要があります
case note_description
when "#{my_account} アップタイム教えれ" then
tweet("#{my_master} " + `uptime` + "デス!")
when "#{my_account} ディスク使用率教えれ" then
tweet("#{my_master} " + `df | awk '/disk0s2/{print $8}'` + "デス!")
else
tweet("#{my_master} 命令が分からないデス!")
end
end
こんな感じにしておくと、bot用アカウントへのメンションで、Twitter経由でどこからでも自宅のMacに何かをさせるということが実現できるわけです。
bot用のアカウントをアクティブにしておくのは、公式Twitterクライアントは、自分から自分へのメンションの場合、通知を出してくれないからです。
これは一例にすぎませんが、GrowlはRulesを使うと活用の道が広がりますね。遊んでみてください。
Growl 
カテゴリ: 仕事効率化
価格: ¥400