root/lang/perl/tiarra/trunk/module/Auto/Notify.pm @ 37855

Revision 37855, 12.2 kB (checked in by topia, 4 years ago)

support boxcar growl/provider (but not tested yet)

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id URL Date Rev Author
Line 
1# -----------------------------------------------------------------------------
2# $Id$
3# -----------------------------------------------------------------------------
4package Auto::Notify;
5use strict;
6use warnings;
7use base qw(Module);
8use Module::Use qw(Auto::AliasDB Tools::HTTPClient Auto::Utils);
9use Auto::AliasDB;
10use Tools::HTTPClient; # >= r11345
11use Auto::Utils;
12use HTTP::Request::Common;
13
14sub new {
15  my ($class) = shift;
16  my $this = $class->SUPER::new(@_);
17
18  $this->config_reload(undef);
19
20  return $this;
21}
22
23sub config_reload {
24  my ($this, $old_config) = @_;
25
26  my $regex = join '|', (
27    (map { "(?:$_)" } $this->config->regex_keyword('all')),
28    (map { "(?i:\Q$_\E)" } map { split /,/ } $this->config->keyword('all')),
29   );
30  eval {
31    $this->{regex} = qr/$regex/;
32  }; if ($@) {
33    $this->_runloop->notify_error($@);
34  }
35
36  $this->{blocks} = [];
37  foreach my $blockname (map {split /\s+/} $this->config->blocks('all')) {
38      my $block = $this->config->get($blockname, 'block');
39      if (!defined $block) {
40          die "not found block: $blockname";
41      }
42      my $type = $block->type;
43      if (!defined $type) {
44          die "type definition not found in block";
45      }
46      my $meth = $this->can('config_'.$type);
47      if (!defined $meth) {
48          die "unknown type: $type";
49      }
50      $this->$meth($block);
51      push(@{$this->{blocks}}, $block);
52  }
53
54  return $this;
55}
56
57sub message_arrived {
58  my ($this,$msg,$sender) = @_;
59  my @result = ($msg);
60
61  # サーバーからのメッセージか?
62  if ($sender->isa('IrcIO::Server')) {
63      # PRIVMSGか?
64      if ($msg->command eq 'PRIVMSG') {
65          my $text = $msg->param(1);
66          my $full_ch_name = $msg->param(0);
67
68          if ($text =~ $this->{regex} && Mask::match_deep_chan(
69              [Mask::array_or_all_chan($this->config->mask('all'))],
70              $msg->prefix,$full_ch_name)) {
71
72              foreach my $block (@{$this->{blocks}}) {
73                  my $type = $block->type;
74                  my $meth = $this->can('send_'.$type);
75                  eval {
76                      $this->$meth($block, $text, $msg, $sender, $full_ch_name);
77                  }; if ($@) {
78                      $this->_runloop->notify_warn(__PACKAGE__." send failed: $@");
79                  }
80              }
81
82          }
83      }
84  }
85
86  return @result;
87}
88
89sub strip_mirc_formatting {
90    my ($this, $text) = @_;
91    $text =~ s/(?:\x03\d\d?(?:,\d\d?)?|[\x0f\x02\x1f\x16])//g;
92    $text;
93}
94
95sub config_im_kayac {
96    my ($this, $config) = @_;
97
98    if ($config->secret) {
99        # signature required
100        require Digest::SHA;
101    }
102
103    1;
104}
105
106sub send_im_kayac {
107    my ($this, $config, $text, $msg, $sender, $full_ch_name) = @_;
108
109    my $url = "http://im.kayac.com/api/post/" . $config->user;
110    $text = Auto::AliasDB->stdreplace(
111        $msg->prefix,
112        $config->format || $this->config->format || '[tiarra][#(channel):#(nick.now)] #(text)',
113        $msg, $sender,
114        channel => $full_ch_name,
115        raw_channel => Auto::Utils::get_raw_ch_name($msg, 0),
116        text => $this->strip_mirc_formatting($text),
117       );
118    my @data = (message => $text);
119    if ($config->secret) {
120        push(@data, sig => Digest::SHA->new(1)
121                 ->add($text . $config->secret)->hexdigest);
122    } elsif ($config->password) {
123        push(@data, password => $config->password);
124    }
125    my $runloop = $this->_runloop;
126    Tools::HTTPClient->new(
127        Request => POST($url, \@data),
128       )->start(
129           Callback => sub {
130               my $stat = shift;
131               if (!ref($stat)) {
132                   $runloop->notify_warn(__PACKAGE__." im.kayac.com: post failed: $stat");
133               } elsif ($stat->{Content} !~ /"result":\s*"(?:ok|posted)"/) {
134                   # http://im.kayac.com/#docs
135                   # (but actually responce is '"result": "ok"')
136                   (my $content = $stat->{Content}) =~ s/\s+/ /;
137                   $runloop->notify_warn(__PACKAGE__." im.kayac.com: post failed: $content");
138               }
139           },
140          );
141}
142
143
144sub config_prowl {
145    my ($this, $config) = @_;
146
147    require Crypt::SSLeay; # https support
148    require URI;
149
150    my $url = URI->new("https://prowl.weks.net/publicapi/verify");
151    $url->query_form(apikey => $config->apikey);
152    my $runloop = $this->_runloop;
153    Tools::HTTPClient->new(
154        Request => GET($url->as_string()),
155       )->start(
156           Callback => sub {
157               my $stat = shift;
158               $runloop->notify_warn(__PACKAGE__." verify failed: $stat")
159                   unless ref($stat);
160               ## FIXME: check response (should check 'error')
161           },
162          );
163}
164
165sub send_prowl {
166    my ($this, $config, $text, $msg, $sender, $full_ch_name) = @_;
167
168    my $url = URI->new("https://prowl.weks.net/publicapi/add");
169    $text = Auto::AliasDB->stdreplace(
170        $msg->prefix,
171        $config->format || $this->config->format || '[tiarra][#(channel):#(nick.now)] #(text)',
172        $msg, $sender,
173        channel => $full_ch_name,
174        raw_channel => Auto::Utils::get_raw_ch_name($msg, 0),
175        text => $this->strip_mirc_formatting($text),
176       );
177    my @data = (apikey => $config->apikey,
178                priority => $config->priority || 0,
179                application => $config->application || 'tiarra',
180                event => $config->event || 'keyword',
181                description => $text);
182    $url->query_form(@data);
183
184    my $runloop = $this->_runloop;
185    Tools::HTTPClient->new(
186        Request => GET($url->as_string()),
187       )->start(
188           Callback => sub {
189               my $stat = shift;
190               if (!ref($stat)) {
191                   $runloop->notify_warn(__PACKAGE__." prowl: post failed: $stat");
192               } elsif ($stat->{Content} !~ /<success /) {
193                   (my $content = $stat->{Content}) =~ s/\s+/ /;
194                   $runloop->notify_warn(__PACKAGE__." prowl: post failed: $content");
195               }
196           },
197          );
198}
199
200sub config_boxcar {
201    my ($this, $config) = @_;
202
203    my $runloop = $this->_runloop;
204    if (!$config->provider_key) {
205        # growl mode
206        require Crypt::SSLeay; # https support
207        if (!$config->user || !$config->password) {
208            $runloop->notify_warn(__PACKAGE__." boxcar (Growl): please set user and/or password");
209        }
210    } elsif ($config->email_hash) {
211        # ok
212    } elsif ($config->email) {
213        # needs to hash email
214        require Digest::MD5;
215    } elsif ($config->token && $config->secret) {
216        # ok
217    } else {
218        $runloop->notify_warn(__PACKAGE__." boxcar (Provider): please set email-hash, email or token and secret");
219    }
220
221}
222
223sub send_boxcar {
224    my ($this, $config, $text, $msg, $sender, $full_ch_name) = @_;
225
226    $text = $this->strip_mirc_formatting($text);
227    my $screen_name = Auto::AliasDB->stdreplace(
228        $msg->prefix,
229        $config->screenname_format || '[tiarra][#(channel):#(nick.now)]',
230        $msg, $sender,
231        channel => $full_ch_name,
232        raw_channel => Auto::Utils::get_raw_ch_name($msg, 0),
233        text => $text,
234       );
235    $text = Auto::AliasDB->stdreplace(
236        $msg->prefix,
237        $config->format || $this->config->format || '#(text)',
238        $msg, $sender,
239        channel => $full_ch_name,
240        raw_channel => Auto::Utils::get_raw_ch_name($msg, 0),
241        text => $text,
242       );
243    my @data = ('notification[from_screen_name]' => $screen_name,
244                'notification[message]' => $text);
245
246    my $runloop = $this->_runloop;
247    if (!$config->provider_key) {
248        # Growl mode
249        Tools::HTTPClient->new(
250            Request => POST("https://boxcar.io/notifications", \@data),
251           )->start(
252               Callback => sub {
253                   my $stat = shift;
254                   if (!ref($stat)) {
255                       $runloop->notify_warn(__PACKAGE__." boxcar: post failed: $stat");
256                   } elsif ($stat->{Content} !~ /^\s*$/) {
257                       (my $content = $stat->{Content}) =~ s/\s+/ /;
258                       $runloop->notify_warn(__PACKAGE__." boxcar: post failed: $content");
259                   }
260               },
261              );
262    } else {
263        if ($config->email_hash) {
264            push(@data, email=>$config->email_hash);
265        } elsif ($config->email) {
266            push(@data, email=>Digest::MD5->new->add($config->email)->hexdigest);
267        } else {
268            push(@data,
269                 token => $config->token,
270                 secret => $config->secret);
271        }
272        Tools::HTTPClient->new(
273            Request => POST("http://boxcar.io/devices/providers/".
274                                $config->provider_key."/notifications", \@data),
275           )->start(
276               Callback => sub {
277                   my $stat = shift;
278                   if (!ref($stat)) {
279                       $runloop->notify_warn(__PACKAGE__." boxcar: post failed: $stat");
280                   } elsif ($stat->{Content} !~ /^\s*$/) {
281                       (my $content = $stat->{Content}) =~ s/\s+/ /;
282                       $runloop->notify_warn(__PACKAGE__." boxcar: post failed: $content");
283                   }
284               },
285              );
286
287    }
288
289}
290
2911;
292
293=pod
294info: 名前が呼ばれると、その発言をim.kayac.comに送信する
295default: off
296
297# 反応する人のマスクを指定します。
298# 省略すると全員に反応します。
299mask: * *!*@*
300
301# 反応するキーワードを正規表現で指定します。
302# 複数指定したい時は複数行指定してください。
303-regex-keyword: (?i:fugahoge)
304
305# 反応するキーワードを指定します。
306# 複数指定したい時は,(コンマ)で区切るか、複数行指定してください。
307keyword: hoge
308
309# メッセージのフォーマットを指定します。
310# デフォルト値: [tiarra][#(channel):#(nick.now)] #(text)
311# #(channel) のかわりに #(raw_channel) を利用するとネットワーク名がつきません。
312format: [tiarra][#(channel):#(nick.now)] #(text)
313
314# 使用するブロックを指定します
315-blocks: im prowl boxcar-growl boxcar-provider
316
317im {
318
319# 通知先のタイプを指定します。
320type: im_kayac
321
322# im.kayac.comで登録したユーザ名を入力します。
323# im.kayac.comについては http://im.kayac.com/#docs を参考にしてください。
324user: username
325
326# im.kayac.comで秘密鍵認証を選択した場合は設定してください。
327# 省略すると認証なしになります。
328-secret: some secret
329
330# im.kayac.comでパスワード認証を選択した場合は設定してください。
331# 省略すると認証なしになります。
332# secret と両方指定した場合は secret が優先されています。
333-password: some password
334
335}
336
337prowl {
338
339# 通知先のタイプを指定します。
340type: prowl
341
342# 通知先ごとにフォーマットを指定できます。
343# この例では先頭に時刻を追加しています。
344-format: #(date:%H:%M:%S) [#(channel):#(nick.now)] #(text)
345
346# Prowl で表示された apikey を入力します。
347# Prowl については http://prowl.weks.net/ を参考にしてください。
348-apikey: XXXXXX
349
350# http://forums.cocoaforge.com/viewtopic.php?f=45&t=20339
351priority: 0
352application: tiarra
353event: keyword
354
355}
356
357boxcar-growl {
358# 利用する前にサービスリストに Growl を追加しておいてください。
359
360type: boxcar
361
362# Boxcar のユーザー名を指定します。必須です。
363user:
364
365# Boxcar のパスワードを指定します。必須です。
366password:
367
368# スクリーンネームのフォーマットを指定できます。
369# デフォルト値: [tiarra][#(channel):#(nick.now)]
370screenname-format: #(date:%H:%M:%S) [#(channel):#(nick.now)] #(text)
371
372# 通知先ごとにフォーマットを指定できます。
373# この例では先頭に時刻を追加しています。
374# Boxcar ではスクリーンネームが別になるので、個別指定をお勧めします。
375format: #(date:%H:%M:%S) [#(channel):#(nick.now)] #(text)
376
377}
378
379boxcar-provider {
380# 自分用 provider を立てて利用するタイプです。
381# http://boxcar.io/site/providers からサインアップしてください。
382# このとき、 curl のコマンドライン中にある token と secret は
383# サインアップ直後にしか表示されないので、忘れずメモしてください。
384# (もちろんwebhookを立てればいつでも取得できますが……)
385type: boxcar
386
387# provider の API key を指定します。これがないと Growl モードになります。
388provider-key: XXXXXX
389
390# 通知先の指定をします。
391# token と secret, email, email-hash のいずれかを指定するようにしてください。
392
393# トークン。サインアップ直後の curl のコマンドライン中にあります。
394-token: XXXXXX
395
396# シークレット。サインアップ直後の curl のコマンドライン中にあります。
397-secret: XXXXXXXX
398
399# メールアドレス。 Digest::MD5 が必要です。
400-email: XXXX@XXXX
401
402# メールアドレスのMD5ハッシュ。 Digest::MD5 は必要ありません。
403-email-hash: xxxxxx
404
405# スクリーンネームのフォーマットを指定できます。
406# デフォルト値: [tiarra][#(channel):#(nick.now)]
407screenname-format: #(date:%H:%M:%S) [#(channel):#(nick.now)] #(text)
408
409# 通知先ごとにフォーマットを指定できます。
410# この例では先頭に時刻を追加しています。
411# Boxcar ではスクリーンネームが別になるので、個別指定をお勧めします。
412format: #(date:%H:%M:%S) [#(channel):#(nick.now)] #(text)
413
414}
415
416
417=cut
Note: See TracBrowser for help on using the browser.