Hatena::ブログ(Diary)

今日覚えたこと RSSフィード

2017.10.16

macOSのスティッキーズの内容をiCloud DriveやDropboxやGoogle Driveなどに置いてどこからでも見れるようにする

macOSにはスティッキーズっていうアプリが標準で入ってる。付箋のような感じでメモをデスクトップ上に貼れるアプリ。まあわかるよね。

これが結構好きでよく使ってるんだけど、最大の難点が、ローカルにデータを保存するという点。そのMacデスクトップを見ないと、メモった内容を確認できない。他のMaciPhoneから見ることができない。2017年にもなったというのにまだそんなのがある。

Dropboxなどを使って同期させる方法はあるんだけど、別に同期したいわけではない。それぞれのMacにはそれぞれのスティッキーズがあっていいんだけど、違うMacのスティッキーズもちょっと見たいことがあるっていう感じ。

調べてみると、スティッキーズのデータは~/Library/StickiesDatabaseというひとつのファイルにまとまってるようだ。さっそく中身を見てみたら、各メモの内容がRTFになって結合されてるという感じだった。わりとカンタンそう。

なので、このStickiesDatabaseのファイルからRTFをそれぞれ抜き出して、任意のディレクトリに .rtf ファイルとして書き出すスクリプトを書いてみた。

stickies-backup

#! /usr/bin/ruby

require 'tempfile'
require 'fileutils'

# paths
in_path = File.expand_path('~/Library/StickiesDatabase', __FILE__)
out_path = File.expand_path('~/Library/Mobile Documents/com~apple~CloudDocs/StickiesBackup', __FILE__)

# directory initialize
backup_path = File.join(out_path, `hostname`.chop)
FileUtils.rm_rf(backup_path)
FileUtils.mkdir_p(backup_path)

# load original stickies
data = File.binread(in_path)
bytes = data.bytes

# make backup
found = false
array = nil
bytes.each_index do |i|
  if bytes[i...(i+6)] == '{\rtf1'.bytes
    found = true
    array = []
  end
  array << bytes[i] if found
  if bytes[(i-5)...i] == [0x7D, 0x01, 0x00, 0x00, 0x00]
    array.slice!(-5, array.size)
    rtf_file = Tempfile.new('rtf')
    txt_file = Tempfile.new('txt')
    File.open(rtf_file.path, 'w+b') do |file|
      file.write(array.pack('C*'))
    end
    `textutil -convert txt "#{rtf_file.path}" -output "#{txt_file.path}"`
    first_line = File.read(txt_file.path).lines[0].chomp
    filename = first_line.gsub(/[:\/]/, '') + '.rtf'
    puts filename
    FileUtils.cp(rtf_file.path, File.join(backup_path, filename))
    found = false
  end
end

これを適当にstickies-backupみたいな名前つけて、実行権限を与えておく。

in_pathのところはStickiesDatabaseの場所なので、普通はそのままでいいと思う。out_pathのところは、とりあえずiCloud Drive上に「StickiesBackup」っていう勝手なフォルダを作ってその中に書き出すようにしてある。DropboxGoogle Driveなど、他のフォルダがいい人は書き換えてね。

使い方

このコマンドを実行すると、out_pathで指定したフォルダの中に、そのMacのホスト名(hostnameコマンドの出力ね)でさらにフォルダを作って(すでにあったら消して作り直す)、RTF形式のファイルを出力する。スティッキーズ1件あたり1ファイルになる。ファイル名は1行目の文字列にしてみた(同じファイル名があったら上書きされるから注意してね)。

iCloud Driveに書き出したなら、iOS11で搭載された「ファイル」アプリで表示することができる。もちろん他のMacからでもiCloud Driveを開けばファイルが見れる。

あくまでスティッキーズから出力するだけで、スティッキーズに書き込む機能はない。生成されたRTFを編集しても何の意味もない。スティッキーズはひとつのファイルに全部入ってる形式なので、外部から書き換えたりしたら壊れやすそうな気がして、更新系はやめといた。

このコマンドを手動で実行するのが面倒なら、cronやlaunchdでも使うか、ファイルの更新を検知して自動で起動するとかもできるかも。

仕組み

まあ短いコードなので説明するほどでもないけど、バイナリデータとしてロードして、"{\rtf1" っていうデータを見つけたら抽出開始、"}" のうしろに 0x01 0x00 0x00 0x00 が続いてるデータを見つけたら抽出終了としている。抜き出したデータをRTFファイルとして書き出す。

RTFファイルを、一度 textutil コマンドにかけてる。これはmacOSに最初から入ってるコマンドで、文書っぽいファイルをいろいろ変換してくれる機能を持つ。今回は、RTFをプレーンテキストに変換した。1行目の文字列をファイル名として使いたいため、RTFから書式を排除したテキストが欲しかったので使った。

あとは、textutilコマンドで得たテキストの1行目をファイル名として、RTFファイルをout_pathにコピーする形で出力してる。

便利だよ

個人的にはこれで結構便利になった。スティッキーズ好きな人はぜひ使ってみて。

2014.08.11

ディズニーストアのツムツムの在庫を自動で確認するTwitterのbotを作った

作ってみた。

こんな感じのTwitterアカウント

説明

以前も書いたけど、ツムツムが楽しい。そうなってくると、リアルツムツム、つまりぬいぐるみのツムツムが欲しくなるのは当然の流れ。

すでにいくつか持ってるんだけど、欲しいのに品切れで買えないツムがいくつかある。

ここで「在庫なし」ってなってるのが結構あるのがわかると思う。そして、わりと頻繁に在庫は増えるらしい。しかしすぐまた売り切れたりする。在庫だけじゃなく、新しく発売されるものもある。

仕方ないので、暇さえあればこのページで在庫をチェックしてたんだけど、それもだんだん面倒になってきて、自動化したいってことで、Twitterbotとして作ってみた。

在庫状況に変化があると自動的にツイートするよ。ここで「在庫あり」とか「新登場」っていうのを見たらすぐに買いに行けばおk。

まあこの情報は通販の在庫だけど、自分の体感としては店舗の在庫もこれに近い感じな気がしている。入荷のタイミングが近いというか。

興味ある人は、このアカウントをフォローするなどしてご利用ください。情報の正確性は保証しないけど。

仕組み

とりあえずコードはGitHubに置いた。

コマンドラインで動かすRubyスクリプト。実質プログラムは1ファイルしかないし、非常に短いので、まあ見ればわかると思う。

ディズニーストアのサイトの仕組み的に、JavaScriptAPIを呼び出して、商品1つずつの在庫状況を動的に取得してた。SKU(商品番号のようなもの)を渡すと在庫数を返すAPIがあるということ。なので、それをそのまま使う形で作った。

最初にページのHTMLを元にSKUの一覧を抽出して、ひとつずつAPIを呼び出して在庫を調べ、前回チェック時と比較して、変化があればツイートする。

ページのHTMLも、APIが返してくるXMLもシンプルだったので、ちゃんと真面目にパースするのではなく、正規表現で抜き出す感じでいけたので簡単だった。

これは単なるスクリプトなので、botとして自動で運用するには、cronか何かで定期的に実行する仕組みが必要。今回のbotアカウントは、自分が契約しているさくらインターネットサーバーでこのスクリプトを15分ごとに動かすようにしている。

また、ツイートするURLは設定ファイル config.rb で自由に決められるようなっていて、とりあえずGitHubに置いたコードのデフォルト状態ではディズニーストアのサイトの商品ページにしてあるけど、実際に動かしてる今回のbotアカウントでは、自分で用意した専用サイト(http://tsum.nacookan.com/)に飛ぶように変更している。Twitterに一度投稿しちゃうと修正ができないので、いきなり外部のストアに飛ぶのではなく、いったん自分が管理するURLに飛ばすようにすれば、そこの情報はいつでも直せるという感じ。

2014.03.18

今日の日付で自動的にフォルダを作る

ちょっとアイディアを思いついたので作ってみた。Gistに置いた。

これだけのちょっとしたやつ。

これは何?

今日の日付を名前にしてフォルダを作りたいことがときどきある。なんかわかんないけど、いろんな場面でそういうことがある。

いちいち作るのは面倒なので、手軽にしたい。

そこで以下のような仕様を考えた。

  • 指定したフォルダ内に、日付を名前にしたフォルダを作る。
    • フォルダ名のルールは YYYYMMDD + 連番(2桁) って感じ。例えば「2014031801」となる。
  • 作ったフォルダが空っぽの間は何もしない。
  • 作ったフォルダに何かファイルが置かれたら、連番を1つ増やして新しいフォルダを作る。
  • 日付が変わったら、前の日の空っぽのフォルダを新しい日付で作り直す。

つまり、必ず空っぽのフォルダが1つだけ用意されている感じ。そこに何かファイルを入れたら、新しい空っぽのフォルダを自動で作る。そのまま日付が変わったら、新しい日付で作り直す。

使い方

適当な場所にスクリプトを置いて実行権限を与える。自分の場合は ~/bin/mkdirdate に置いた。

これをcronで1分おきに実行するようにする。

* * * * * ~/bin/mkdirdate ~/Desktop > /dev/null 2>&1

こんな感じ。コマンドラインオプションでフォルダの作成場所を指定している。今回はデスクトップにしてみた。

全自動じゃなくても

cron以外でも、パスを通しておけば、mkdirdateコマンドとして使えるようになる。

コマンドラインオプションを渡さない場合はカレントディレクトリに作るようになってる。つまり、コマンドを叩くだけで、今いる場所に日付のディレクトリを作る。すでにその名前のディレクトリが存在していて中身が入ってる場合は、連番を1つ増やしてディレクトリを作る。

まああんまりそういう使い方は想定してないけど。

以前も作ったことがあった

ちなみに以前こういうのを作ったことがあった。

2009年の話。当時はWindowsを使っていた。今はWindowsは使ってない。しかもこれは自動じゃなくて手動。プログラムを手動で実行するとフォルダが作成され(てクリップボードにコピーされ)るという仕組みだった。

今回は、OS XやLinuxで動くようにRubyで書いて、しかも全自動になるようにした。今回の方がいい感じな気がする。

おわり

「自動であらかじめフォルダを作っておいて、その中にファイルが入れられたら、さらに次の新しいフォルダを作る」というアイディアを思いついたので作ってみたという感じ。まださっき作ったばかりなので、本当に実用的なのかどうかわかってない。

2013.04.08

MacがsayするCGI

Mac上で、Apache+CGIを動かして、外部から特定のURLにアクセスが来たら、そこで渡された文字列をsayコマンドに渡してMacから音声を出す、そんなことがやりたくて、作ってみた。まあ作ったってほどじゃない。

MacでCGIを動かす方法と一緒にメモ。

以下の「nacookan」のところは各自のOS Xユーザ名に置き換えてね。

sayコマンドの用意

上記に書いたけど、あらためてMountain Lionに合わせて言うと、

  1. システム環境設定
  2. 音声入力と読み上げ
  3. テキスト読み上げ
  4. システムの声
  5. カスタマイズ
  6. 「日本語(日本)」のところにある「Kyoko」を選択
  7. 「インストールしますか?」と聞かれたらインストールする
  8. システムの声
  9. 「Kyoko」を選択

CGIスクリプトの用意

ここからはターミナルの作業。

$ mkdir ~/Sites
$ touch ~/Sites/.localized
$ mkdir ~/Sites/say
$ vi ~/Sites/say/say.cgi

viで以下を記述。

#! /usr/bin/env ruby

require "cgi"

cgi = CGI.new
message = cgi['message'].gsub(/'/, '')
system "say '#{message}'"
cgi.out("text/plain"){ message }

viを終了させて、以下。

$ chmod +x ~/Sites/say/say.cgi

おわり。

Apacheの用意と起動

$ sudo vi /etc/apache2/users/nacookan.conf

viで以下を記述。

<Directory "/Users/nacookan/Sites/say/">
	AddHandler cgi-script cgi
	Options MultiViews ExecCGI
	AllowOverride none
	Order allow,deny
	Allow from all
</Directory>
$ sudo apachectl start

Lionまでは、システム環境設定→共有→Web共有で内蔵Apacheの設定ができたんだけど、Mountain Lionでは項目が消えたのでターミナルからやってるわけだ。Apache自体が消えたわけではないんだけどね。

使ってみる

ブラウザのアドレスバーに、「http://(MacのIP)/~nacookan/say/say.cgi?message=テストです」と入れてみる。

Kyokoさんの声(日本語版Siriでおなじみの声)で「テストです」って読み上げられるはず。

どう使うか

自分は、サーバー用途で稼働しているMac mini上でこのCGIを動かしている。別なLinuxサーバーで動いてるJenkinsが、svnへのコミットを検知して自動でビルド&テスト環境デプロイをするようになっていて、それが終わったタイミングでこのURLを叩いてMac miniから音声で通知されるようにしている。

他にも、まあ何か通知が必要なことがLAN内で発生したときに、このURLにメッセージを渡すだけで音声でお知らせしてくれるので、いろいろ使えると思う。

ログとかを監視して何か見つけたときにお知らせとか。天気予報や時報とか。

心配なこと

OSコマンドインジェクションの脆弱性大丈夫なのかな?シングルクォートは消したけど、これだけでいいのかよくわからない。いいやり方あるなら教えて欲しい。

まあ用途的に、広くインターネット上に公開しても仕方ないと思うので、どうせLAN内だけで使うことになると思う。

2012.12.11

REGZA DBR-M190でキーワード自動録画機能を実現する

先日買ったDBR-M190。この機種には、残念ながらキーワード録画機能がない。

事前にキーワードを指定しておくと、それにマッチした番組を自動で録画してくれる機能。昔使っていたSONYのスゴ録にはその機能があったし、そのあとで使っていたDIGAではDiMORAというサイトを利用するとその機能を実現できていた。

DBR-M190にはその機能が無いんだけど、Eメール録画予約機能というのがあって、決まった書式でメールを送ると、録画予約ができる。なので、これを利用して、キーワード録画ができるようにしてみた。

コード

コードはgithubに置いた。

やり方もREADME.mdに書いておいた。

REGZA側の設定は済んでいるとして、さらにプログラム側にもいくつか設定が必要。もろもろの知識がない人には難しいと思うけど、知識がある人にとっては難しいことは何もないので頑張って。

これを適当なサーバーで定期的に動かすようにしておけば、番組検索して、見つかったものをメールでREGZAに送って予約してくれるということになる。

REGZAのEメール録画予約機能について

結構微妙な機能というか、制限が多い。

  • 受信アカウントは、POP3じゃないといけないし、認証の暗号化はAPOPのみ
  • REGZAによるメールチェックは、最大でも2時間おきにしかできない(午前,午後の0,2,4,6,8,10時)
    • だから設定中と開発中にテストをするのが大変だった
  • チャンネル番号の概念がよくわからない
    • リモコン番号とチャンネル番号のマッピングをハードコーディングで持つことで回避した
  • REGZAから結果がメールで送られてくるけど、エラーメッセージが不親切でどこに問題があるのかよくわからない

だいぶ昔に作った機能がアップデートされずに残り続けてるような感じでもある。とはいえ、日時とチャンネルをメールで送るだけという超シンプル仕様で動作してくれるので、プログラムを書く側としてはうれしい。

認証とかもマジメに作ったら色々面倒だけど、POP3アカウントのログインと単純なパスワードだけで実現してるのとかはそういう意味ではいい。

テレビ王国について

これが実現できたのは、すべてはテレビ王国のおかげ。番組を検索することができて、検索結果をRSSで配信してくれている。

素晴らしいサービスだ。ありがとう。

これがサービス停止したらどうしようもない。

こまかい話

日付のファイル

同じ番組を何度も録画しないように、カレントディレクトリに日付のファイルを作って、その中に番組情報のパーマリンクを出力している。毎回そのファイルの中身をチェックして、既に出力されている番組は予約しないように制御している。

そのうちこのファイルがたくさんになると思うので、古いファイルは適当に消していいと思う。逆に、テストのときなど、同じ処理を何度もやりたいなら、このファイルを消せば、もう一度実行できる。

送信元アカウント

現在のコードでは、送信元をGmailに限定しているので、違うアカウントから送信した人はコードを書き直してください。

でもGmailだと、SMTP認証して送ったメールもGmailの「送信済みメール」に記録されるので、どんなメールを送ったのかがあとで確認できて便利。だからGmail使えば良いと思う。

おわり

まだ作ってから1日くらいしか動かしてないので、バグってるかもしれない。そのときはゴメンなさい。自分と違う機種、違う地域とかでうまく動作するか自信ない。

バグを見つけたと連絡もらってもどうしようもないので、自分で直してください。すみません。