ひとりぶろぐ

価値ある情報をユーザー視点で発信するブログ

Growlの知られざる機能「Rules」で自宅待機のMacをTwitterからリモートコントロールする

   

何か面白いことができそうなGrowlのRules

Skitched 20140307 031802

OS Xに通知の仕組みを追加するGrowl。

Growl App
カテゴリ: 仕事効率化
価格: ¥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を設置すると動作を開始する

AppleScriptエディタでRules.scptを作成。これを以下のパスに設置。

/Users/ユーザー名/Library/Application Scripts/com.Growl.GrowlHelperApp/Rules.scpt

Growlを再起動すると、Rulesを使うかどうかの問い合わせが入り、受け入れればRulesが動作を開始し、通知を表示する直前に、毎回Rules.scptが実行されるようになります。

ちょっと隠し機能のような趣がありますね。

いったんRules.scptの存在が認識されると、Growlの「環境設定>一般」に「Use Rules」というチェックボックスが出現するので、Rulesの使用を中止したい場合は、ここのチェックボックスを外します。

Skitched 20140306 044801


何もしないRules.scptと仮引数notification

何もしない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 Twitter
host localhost
note description @hitoriblog 先輩はアホですね
note title hitoriblog_testからの@ツイート

TwitterApp
カテゴリ: Social Networking
販売元: Twitter, Inc.(サイズ: 3.9 MB)
全てのバージョンの評価: (667 件の評価)

通知が表示されるたびに、通知内容をAppleScriptで評価する機会を与えられるわけです。

Rules.scptでGrowlの設定を変更

返り値として決まったレコード(Rubyで言うハッシュ)、RuleResultを返すと、Growlの動作を変更できます。RuleResultのプロパティは、GrowlのAppleScript用語説明に詳細が載っています。

Skitched 20140306 045357

以下の例では、仮引数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の中身に指定できるのは、表示スタイルのリストに出てくるものです。

Skitched 20140306 134717

RuleResultで複数のプロパティを変更したい場合は、「{display:none, actions:”Speech”}」といった感じでレコードを記述します。

Rules.scptで通知の中身を書き換えてしまう

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等に引き継ぐ

処理をRuby / Pythonなどに引き継ぐ方法を考えてみました。

AppleScriptは文字列の操作が苦手(苦手なことばっかりだけど……)なので、Ruby / Pythonで処理した方がいいことが多いでしょう。

Ruby / Pythonに引き継ぎできれば、正規表現マッチングを含む文字列の処理、ネットワークを使った処理などで、できることが飛躍的に増えます。

AppleScriptはAppleScriptで、ほかのアプリケーションを使役するのが得意ですが、osascriptコマンドでRuby / Pythonから同等のことをするのも難しくありません。

仮引数notificationの主要なプロパティをどう引き継ぐかがポイントです。ここでは、環境変数に代入する手を使ってみました。

Skitched 20140307 030125

主要なプロパティを環境変数に設定した後で、Rules.scptと同じディレクトリに入れているtest.rbを呼び出しています。

Rules.scpt

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

test.rb

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をTwitterからリモートコントロールする

Skitched 20140307 035803

自宅でMacを起動させっぱなしにしておき、Growlに前項で紹介したようなRules.scptとtest.rbを仕込んでおく。

公式Twitterクライアントを起動、bot用のアカウントをアクティブにしておく。

Skitched 20140307 031622

あとは、通知の本文である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 App
カテゴリ: 仕事効率化
価格: ¥400

 - AppleScript, Mac, OS X, Python, Ruby, Twitter