@nqounetです。

最近、IdobataというツールをPerl入学式のサポーターたちで使い始めました。

Idobataはチャットツールなのですが、webhookを経由して色々と通知を受け取れるのでなかなか快適です。

しかもなかなか充実しています。

折角なので、Perl入学式についての発言があったら通知するようなbotを作ってみました。

だいたいこんな感じ

メインスクリプトの全体像は大体こんな感じです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/usr/bin/env perl
use utf8;
use v5.12;

use AnyEvent::Twitter::Stream;
use Encode;
use Furl;

use Config::Pit;
my $config = pit_get(
'nqounet@twitter.com',
require => {
consumer_key => 'consumer_key',
consumer_secret => 'consumer_secret',
token => 'access_token',
token_secret => 'access_token_secret',
}
);

my $furl = Furl->new;
my $done = AE::cv;

my $listener = AnyEvent::Twitter::Stream->new(
method => 'filter',
track => 'Perl入学式',
on_tweet => sub {
my $tweet = shift;
$furl->post(
'https://idobata.io/hook/generic/xxxxxxxxxxxxxxx',
['Content-Type' => 'application/x-www-form-urlencoded'],
[source => encode_utf8(qq{$tweet->{user}{name} : $tweet->{text}})]
);
},
%{$config}
);

$done->recv;

まずはアプリケーションを登録

TwitterはAPIが充実していますが、使う場合は登録が必要です。

登録は英語ですが、日本語の解説を読みながらだと簡単です。

アクセストークンが必要なのでアクセストークンも発行しておきます。

発行後、しばらく待っていると発行されてページに情報が表示されるようになります。

OAuthしましょう

各種トークンの読み込みはConfig::Pitを使っていますが、パスワードを書かないようにしているだけですので、ハッシュで指定してもかまいません。

OAuthを使用するので、Net::OAuthもインストールが必要です。

Payload URL

Idobataで任意のwebhookを受信するためには、Genericを選択します。

設定にあるEndpoint URLに対してデータを送信することになります。

通信にはFurlを使用することにします。

慣れているならばLWP::UserAgentなどでも全く問題ありません。

httpsでの通信になるのでIO::Socket::SSLLWP::Protocol::httpsもインストールが必要な場合があります。

ツイートをフィルタリング

ここではAnyEvent::Twitter::Streamを使うことにします。

インスタンスを生成する時にmethodfilterを指定し、フィルタリングする内容をtrackに指定すると、該当するツイートが流れてきた時に、on_tweetが実行されます。

また、Twitterでは、HTTPSでの接続を推奨しているため、AnyEvent::Twitter::StreamのドキュメントにあるとおりNet::SSLeayもcpanfileに書いてインストールするようにしておきましょう。

サーバーに常駐させる

このまま実行するとターミナルを開けたままにしておかないといけないので、デーモン化してサーバーに常駐させます。

デーモン化するのはApp::Daemonがとてもお手軽です。

1
2
use App::Daemon qw(daemonize);
daemonize();

これが書いてあるスクリプトでstart,stop,statusなどのコマンドが使えるようになります。

また、デーモン化しないためには-Xというオプションをつけるとデーモン化しないので動作確認ができます。

また、不意のエラーから復活させるためProcletで監視させることにしたので、デーモン化するのはProcletの方です。

つまり、デーモン化したProcletによってロボットを監視する構成です。

また、Procletでloggerを指定することで、プログラム本体はログの出力を意識する必要がなくなりますので、非常に便利です。

サーバーに常駐させるスクリプトを作るときには、この構成が手軽で良さそうな気がします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env perl
use utf8;
use v5.12;

use Proclet;
use Path::Tiny qw(path cwd);
use File::RotateLogs;

use App::Daemon qw(daemonize);
daemonize();

my $logfile = join '/', cwd->absolute, 'log', 'app.log';
path($logfile)->parent->mkpath;

my $logger = File::RotateLogs->new(
logfile => qq{$logfile-%Y%m%d},
linkname => $logfile,
rotationtime => 86400,
maxage => 86400 * 14,
);

my $proclet = Proclet->new(logger => sub { $logger->print(@_); });
$proclet->service(
code => [qw(perl main.pl)],
tag => 'bot',
);
$proclet->run;

このファイルも加えた結果、cpanfileはこうなりました。

なお、デバッグ用にData::Printerも追加しています。

1
2
3
4
5
6
7
8
9
10
11
12
requires 'perl', '5.012001';
requires 'AnyEvent::Twitter::Stream';
requires 'Furl';
requires 'Config::Pit';
requires 'Net::OAuth';
requires 'Net::SSLeay';
requires 'IO::Socket::SSL';
requires 'App::Daemon';
requires 'Proclet';
requires 'Path::Tiny';
requires 'File::RotateLogs';
requires 'Data::Printer';

carton installでインストールされたモジュールは98個になっていました。

…多いですね。

しばらくはこの構成で様子を見てみようと思います。

参考になる資料