サーバー側での処理結果をPushbulletで通知する

# 背景

サーバー側で起動したスクリプトの結果を何らかの方法で通知したくなることがよくあります。企業として利用する場合には古式ゆかしいメールや、最近ではSlackに通知することもあるかと思います。しかし個人用途で外部メール配信を設定したり、独自にSlackのチームを作ったりするのもなかなか面倒なものです。そこで今回はPushbulletというサービスを使った通知の仕組みを構築してみます。

# Pushbulletとは

Pushbulletはどういうサービスなのか？どのようにアカウントを取得するのか？などは他の記事で詳しく紹介されています。

- [PCと携帯端末でデータ同期できるアプリ『Pushbullet』の活用法 ｜ ライフハッカー［日本版］](http://www.lifehacker.jp/2014/04/140404pushbullet.html)
- [「Pushbullet」最高。 パソコン、スマホ間のデータ送信が信じられないほどラクに！ : ギズモード・ジャパン](http://www.gizmodo.jp/2015/01/pushbullet.html)
- [第377回　Pushbulletで通知をお手軽にしてみよう：Ubuntu Weekly Recipe｜gihyo.jp … 技術評論社](http://gihyo.jp/admin/serial/01/ubuntu-recipe/0377)

これらを要約するとだいたい下記のとおりです。

- スマートフォンとPCとで下記のデータのやりとりができる。
    - テキストメッセージ
    - ファイル
    - リンク
- SMSをWeb上で管理することができる。

すべての機能が[Web APIで提供されている](https://docs.pushbullet.com/)ため、他のサービスと連携させるなどプログラマブルに利用することができます。テキストメッセージ機能を使えば様々な通知を集約することができそうです。

# 準備

## Pushbulletアカウントの取得

[PushbulletのWebサイト](https://www.pushbullet.com/)でアカウントを取得します。GoogleとFacebookの認証を利用することができます。特に難しいことはなかったので、ここでは割愛します。

## Pushbullet APIのアクセストークンの取得

Pushbullet APIを介して通知を送るので、APIのアクセストークンを取得しておく必要があります。ログイン後のメニューから「Setting」→「Accout」を選択します。

<img width="437" alt="スクリーンショット 2016-03-07 13.28.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/5823/c47e220f-5b0c-5337-bf29-87198efd3db0.png">

メインカラムの中で「Access Tokens」という項目を見つけられると思います。

<img width="403" alt="スクリーンショット 2016-03-07 20.16.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/5823/d4c2f2d1-0c32-2178-61f0-f5e64a593a5f.png">

この「Create Access Token」をクリックするとアクセストークンが生成され、その文字列が表示されます。この文字列は２度と表示することはできないのでメモっておきましょう。

# 通知スクリプト

通知スクリプトを作成します。今回の目的はサーバー側で何らかの処理が実行されたことおよびその処理の実行結果の通知なので、標準出力をパイプで受け取って通知することにしましょう。

通知を作成するのは[APIドキュメントの中のcreate-pushの項目](https://docs.pushbullet.com/#create-push)で説明されています。このページでは `curl` を用いた通知の例が示されていますが、BashではJSONの扱いなどが死ぬほど面倒なので、Perlで書き直すことにします。今さらPerlかよ、とは言わないように。


```pl:pushbullet-pushes.pl
#!/usr/bin/env perl

use strict;
use warnings;
use Data::Dumper;
use HTTP::Tiny;
use Encode;
use Encode::Guess qw/shift-jis euc-jp 7bit-jis/;
use JSON::PP qw/encode_json decode_json/;

my $config = load_config( "$ENV{HOME}/.pushbullet.json" );
my $access_token = $ENV{PUSHBULLET_ACCESS_TOKEN} // $config->{pushbullet_access_token} // "";
my $endpoint = "https://api.pushbullet.com/v2/pushes";

my $title = $ARGV[0];
my $body = "";
while ( <STDIN> ) {
    chomp $_;
    $body .= "$_\n";
}

my $decoder = Encode::Guess->guess( $body );
if ( ref $decoder ) {
    $body = $decoder->decode( $body );
}

my $json_data = {
    title => $title,
    body => $body,
    type => "note",
};

my $headers = {
    'Access-Token' => $access_token,
    'Content-Type' => 'application/json',
};

my $ua = HTTP::Tiny->new;
my $response = $ua->post( $endpoint, {
    'headers' => \%$headers,
    'content' => encode_json( $json_data ),
} );

unless ( $response->{success} ) {
    print STDERR Dumper $response;
    exit 1;
}
exit 0;

sub load_config {
    my $file = shift;
    return {} unless -f $file;
    open my $fh, '<', $file or die $!;
    $config = decode_json( join '', <$fh> );
    close $fh;
    return $config;
}
```

環境変数からアクセストークンを取得し、第１引数から通知タイトルを取得、標準入力から通知ボディを取得しています。環境変数を設定するのが面倒な場合は、設定ファイル（`~/.pushbullet.json`）に記載しておくこともできる親切設計です。

簡単に実装するならこんな感じかと思いますが、エラー処理などをきちんと実装したい人はぜひご自分で。

## 必須ライブラリ

できるだけ標準ライブラリで構成しましたが、HTTPSの通信のために `IO::Socket::SSL` だけはインストールする必要があります。RedHat系なら下記の通りにパッケージをインストールしておけば問題無いでしょう。

```console
$ sudo yum install perl-IO-Socket-SSL
```

`cpanm` でインストールするなら下記の通りです。

```console
$ cpanm IO::Socket::SSL
```

# 使用例

## 長い時間のかかる処理の終了を知らせる

数10分〜数時間かかるようなスクリプトを実行すると、終了時間を知りたかったり、その出力を知りたかったりすることがあります。しかし終了時刻に端末の前にいる可能性は低いので、どこでも通知を得られるPushbulletはうってつけの通知先です。

```console
$ some-command | PUSHBULLET_ACCESS_TOKEN="YOUR_ACCESS_TOKEN" /path/to/pushbullet-pushes.pl "Notification title"
```

## 定期実行するスクリプトの実行結果を知らせる

下記のようなファイルを所定のPATHに配置すれば、2ヶ月に1度、早朝4時に `cron` で実行してくれます。これくらい処理の間隔が空いていると、ちゃんと実行されているのか不安になります。そんな場合にも実行結果をPushbulletに飛ばすことで安心感を得られるようになります。Pushbulletに通知するロジックを実行スクリプトの中に仕込むこともできますが、標準出力を受け取る仕組みにしたことで、スクリプト内に本質的な処理とは無関係な処理を書かずにすみます。

```bash:/etc/cron.d/something-update
PUSHBULLET_ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
0 4 1 */2 * root /path/to/something-update.sh | /path/to/pushbullet-pushes.pl "Updated something"
```

# 補足

大方の通知システムの根本的な問題として、通知システム自体にトラブルがあった際には通知が届きません。万全を期する場合には外部へのメール配信を設定しておくなどが必要となりますが、今回の目的でそこまでする必要もないでしょう。