PageRenderTime 48ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/bot.pl

http://perl-irc-bot.googlecode.com/
Perl | 435 lines | 355 code | 75 blank | 5 comment | 37 complexity | 35be73cb91ea3e392b2eaf8d608a778e MD5 | raw file
  1. use strict;
  2. use warnings;
  3. use POE qw(Component::IRC
  4. Component::IRC::Plugin::CPAN::LinksToDocs
  5. Component::IRC::Plugin::URI::Find
  6. Component::IRC::Plugin::Google::Calculator
  7. Component::IKC::Server
  8. Component::IKC::Specifier
  9. );
  10. use Data::Dumper;
  11. use POE::Component::IKC::ClientLite;
  12. use DateTime;
  13. use constant PATH => $ENV{HOME} ."/irc/";
  14. use Encode qw/encode decode/;
  15. use URI;
  16. use URI::Escape;
  17. use YAML::Syck;
  18. use Web::Scraper;
  19. use LWP::UserAgent;
  20. use Net::Twitter;
  21. use Me2day;
  22. my $nickname = 'Perl_^^';
  23. my $ircname = 'Perl Bot :-)';
  24. my $server = 'irc.hanirc.org';
  25. my $translator = 'http://j2k.naver.com/j2k.php/korean/';
  26. my $dic_url = 'http://endic.naver.com/search.nhn?kind=keyword&query=';
  27. my $naver_map_url = 'http://map.naver.com/?query=';
  28. my $lang = "ko";
  29. my $google_url = 'http://www.google.com/search?hl='.$lang.'&q=';
  30. my $ping_time = 0;
  31. my $info;
  32. my @channels = ('#perl');
  33. my $base_url = "";
  34. binmode(STDOUT, ":utf8");
  35. my $config;
  36. if (-f 'config.yaml') {
  37. $config = LoadFile('config.yaml');
  38. }
  39. my $name = "Client$$";
  40. my $remote;
  41. my $twit = Net::Twitter->new(
  42. username => $config->{twitter}->{username},
  43. password => $config->{twitter}->{password},
  44. );
  45. my $me2day = Me2day->new(
  46. username => $config->{me2day}->{username},
  47. user_key => $config->{me2day}->{user_key},
  48. app_key => $config->{me2day}->{app_key},
  49. );
  50. POE::Component::IKC::Server->spawn(
  51. port => 31337,
  52. name => 'AppServer',
  53. );
  54. POE::Session->create(
  55. inline_states => {
  56. _start => \&service_start,
  57. update => \&service_update,
  58. did_something => \&service_response,
  59. }
  60. );
  61. unless (-d PATH) {
  62. mkdir PATH;
  63. print STDERR "Created Directory ".PATH."\n";
  64. }
  65. my $irc = POE::Component::IRC->spawn(
  66. nick => $nickname,
  67. ircname => $ircname,
  68. server => $server,
  69. ) or die "Oh noooo! $!";
  70. POE::Session->create(
  71. package_states => [
  72. main => [ qw(_default _start irc_001 irc_public irc_msg irc_urifind_uri irc_join irc_part irc_quit irc_353) ],
  73. ],
  74. heap => { irc => $irc },
  75. );
  76. POE::Kernel->run();
  77. sub service_start {
  78. my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
  79. my $service_name = "apphanirc";
  80. $kernel->alias_set($service_name);
  81. $kernel->call( IKC => publish => $service_name, ["update"] );
  82. }
  83. sub service_update {
  84. my ( $kernel, $heap, $request ) = @_[ KERNEL, HEAP, ARG0 ];
  85. $kernel->delay_set( did_something => 1, $request );
  86. }
  87. sub service_response {
  88. my ( $kernel, $heap, $res ) = @_[ KERNEL, HEAP, ARG0 ];
  89. my @data = @{ $res };
  90. $irc->yield( privmsg => "#perl" => $data[0] );
  91. }
  92. sub _encode {
  93. encode("cp949", decode("utf-8", shift));
  94. }
  95. sub _start {
  96. my $heap = $_[HEAP];
  97. my $irc = $heap->{irc};
  98. $irc->yield( register => 'all' );
  99. $irc->plugin_add( 'LinksToDocs' => POE::Component::IRC::Plugin::CPAN::LinksToDocs->new );
  100. $irc->plugin_add( 'UriFind' => POE::Component::IRC::Plugin::URI::Find->new );
  101. $irc->plugin_add( 'GoogleCalc' => POE::Component::IRC::Plugin::Google::Calculator->new );
  102. $irc->yield( connect => { } );
  103. return;
  104. }
  105. sub irc_001 {
  106. my $sender = $_[SENDER];
  107. my $irc = $sender->get_heap();
  108. print "Connected to ", $irc->server_name(), "\n";
  109. $irc->yield( join => $_ ) for @channels;
  110. return;
  111. }
  112. sub irc_msg {
  113. my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
  114. my $nick = ( split /!/, $who)[0];
  115. my $me = $where->[0];
  116. my $irc = $sender->get_heap();
  117. my ($channel,$target);
  118. my @block;
  119. if ($what =~ /^!([a-z]+)\s?(.*?)?$/) {
  120. my ($command, $desc) = ($1,$2);
  121. if ($command eq 'blah') {
  122. @block = split /\s+/, $desc;
  123. $channel = shift @block;
  124. my $blah = join(" ", @block);
  125. if ($channel && $channel =~ /^\#.*/ && $blah) {
  126. $irc->yield( privmsg => $channel => $blah );
  127. }
  128. }
  129. elsif ($command eq 'kick') {
  130. ($channel, $target) = split /\s+/, $desc;
  131. unless ($target) {
  132. $irc->yield( privmsg => $nick => _encode($config->{command}->{kick}) );
  133. } else {
  134. $irc->yield( kick => $channel => $target );
  135. }
  136. }
  137. elsif ($command eq 'oper') {
  138. ($channel, $target) = split /\s+/, $desc;
  139. $irc->yield( mode => $channel => "+oo" => $target );
  140. }
  141. elsif ($command eq 'reload') { # Config Reloader
  142. if (-f "config.yaml") {
  143. $config = LoadFile("config.yaml");
  144. $irc->yield( privmsg => $nick => "reloaded config file" );
  145. }
  146. }
  147. }
  148. }
  149. sub irc_public {
  150. my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
  151. my $nick = ( split /!/, $who )[0];
  152. my $channel = $where->[0];
  153. $channel =~ s/#//;
  154. unless ($remote) {
  155. $remote = create_ikc_client(
  156. port => 31338,
  157. name => $name,
  158. timeout => 1,
  159. );
  160. }
  161. $remote->post_respond( 'application/update', encode("utf-8", decode("cp949","[hanIRC] $nick: ".$what)) );
  162. unless(-d PATH.$channel) {
  163. print STDERR "Created Directory ". PATH.$channel . " for IRC Channel ".$channel."\n";
  164. mkdir PATH.$channel;
  165. }
  166. # Filtering by aero
  167. my $str = decode("euc-kr", $what);
  168. return 0 if $str =~ m/^\x{3151}/;
  169. my $time = DateTime->now( time_zone => 'Asia/Tokyo' );
  170. my $filepath = PATH.$channel."/".$time->ymd.".log";
  171. open my $fh, ">>:utf8", $filepath;
  172. my $desc = "[".$time->hms."] <".decode("cp949", $nick)."> ".
  173. decode("cp949", $what)."\n";
  174. print $fh $desc;
  175. print $desc;
  176. close $fh;
  177. my $irc = $sender->get_heap();
  178. # $command, $desc
  179. if ($what =~ /^!([a-z0-9]+)\s?(.*?)?$/) {
  180. my ($command, $desc) = ($1, $2);
  181. if ($command =~ /^(?:fish|nolog|kick|code|dic|search|map|twitter|me2day)$/ && !$desc) {
  182. $irc->yield( privmsg => "#".$channel => $nick." : "._encode($config->{command}->{$command}) );
  183. }
  184. elsif ($command eq 'kick') {
  185. $irc->yield( kick => "#".$channel => $desc );
  186. }
  187. elsif ($command eq 'j2k') {
  188. my $address = $translator;
  189. if ($desc) {
  190. $address .= $desc;
  191. }
  192. elsif ($base_url) {
  193. $address .= $base_url;
  194. }
  195. else {
  196. return;
  197. }
  198. $irc->yield( privmsg => "#".$channel => $address );
  199. }
  200. elsif ($command eq 'help') {
  201. foreach my $comment (@{ $config->{command}->{help} }) {
  202. $irc->yield( privmsg => "#".$channel => _encode($comment) );
  203. }
  204. }
  205. elsif ($command eq 'dic') {
  206. my $html = scraper {
  207. process 'table>tr>td[class="p3"]', text => 'TEXT';
  208. };
  209. my $data = $html->scrape(URI->new($dic_url.URI::Escape::uri_escape($desc)));
  210. my $text = $data->{text};
  211. $text = encode("cp949", $text) if utf8::valid($text);
  212. my @voca = split /\d/, $text;
  213. for my $dic (@voca) {
  214. $irc->yield( privmsg => "#".$channel => $dic );
  215. }
  216. }
  217. elsif($command eq 'map')
  218. {
  219. my $address = $naver_map_url . URI::Escape::uri_escape($desc);
  220. $irc->yield(privmsg=>"#".$channel=>$address);
  221. }
  222. elsif ($command eq 'search') {
  223. my $html = scraper {
  224. process 'div[class="g"]', text => 'TEXT';
  225. };
  226. my $data = $html->scrape(URI->new($google_url.URI::Escape::uri_escape($desc)));
  227. my $text = $data->{text};
  228. $text = encode("cp949", $text) if utf8::valid($text);
  229. my @entry = split /\d/, $text;
  230. for my $content (@entry) {
  231. $irc->yield( notic => "#".$channel => $content );
  232. }
  233. }
  234. elsif ($command eq 'lang') {
  235. if (!$desc || $desc !~ /^(?:en|ko|ja)$/) {
  236. $irc->yield( privmsg => "#".$channel => $nick." : "._encode($config->{command}->{lang}) );
  237. } else {
  238. $lang = $desc;
  239. $irc->yield( privmsg => "#".$channel => $nick." : "._encode($config->{log}->{lang}). ' => '.$lang );
  240. }
  241. }
  242. elsif ($command eq 'twitter') {
  243. $twit->update(encode("utf-8", decode("cp949", $nick. ": ".$desc)));
  244. $irc->yield( privmsg => "#".$channel => $nick ." : ". _encode($config->{log}->{twitter}) );
  245. }
  246. elsif ($command eq 'me2day') {
  247. my $res;
  248. if ($desc =~ /@\:/) {
  249. my ($post, $tag) = split /@\:/, $desc; #/
  250. $tag .= " me2irc";
  251. $res = $me2day->create_post( body => encode("utf-8", decode("cp949",$nick." : ".$post)), tags => encode("utf-8", decode("cp949", $tag)) );
  252. } else {
  253. $res = $me2day->create_post( body => encode("utf-8", decode("cp949",$nick." : ".$desc)), tags => "me2irc" );
  254. }
  255. if ($res->status_line =~ /200/) {
  256. $res->content =~ /<permalink>(.*?)<\/permalink>/;
  257. my $permalink = $1;
  258. $irc->yield( privmsg => "#".$channel => $nick ." : ". _encode($config->{log}->{me2day}). " ".$permalink );
  259. }
  260. }
  261. elsif ($command eq 'attention') {
  262. if ($info->{"#".$channel}) {
  263. $irc->yield( privmsg => "#".$channel => join(" ", @{ $info->{"#".$channel} }) );
  264. }
  265. }
  266. }
  267. }
  268. sub irc_join {
  269. my ($sender, $who, $where) = @_[SENDER, ARG0, ARG1];
  270. my $nick = ( split /!/, $who )[0];
  271. unless ($remote) {
  272. $remote = create_ikc_client(
  273. port => 31338,
  274. name => $name,
  275. timeout => 1,
  276. );
  277. }
  278. $remote->post_respond( 'application/update', encode("utf-8", decode("cp949","[hanIRC] $nick joined $where")) );
  279. }
  280. sub irc_part {
  281. my ($sender, $who, $where, $msg) = @_[SENDER, ARG0 .. ARG2];
  282. my $nick = (split /!/, $who)[0];
  283. unless ($remote) {
  284. $remote = create_ikc_client(
  285. port => 31338,
  286. name => $name,
  287. timeout => 1,
  288. );
  289. }
  290. $remote->post_respond( 'application/update', encode("utf-8", decode("cp949","[hanIRC] $nick parted $where")) );
  291. }
  292. sub irc_quit {
  293. my ($sender, $who, $msg) = @_[SENDER, ARG0, ARG1];
  294. my $nick = (split /!/, $who)[0];
  295. unless ($remote) {
  296. $remote = create_ikc_client(
  297. port => 31338,
  298. name => $name,
  299. timeout => 1,
  300. );
  301. }
  302. $remote->post_respond( 'application/update', encode("utf-8", decode("cp949","[hanIRC] $nick is quit")) );
  303. }
  304. sub irc_353 {
  305. my ($sender, $server, $desc, $desc_a) = @_[SENDER, ARG0 .. ARG2];
  306. my ($temp, $channel, $members) = @{ $desc_a };
  307. @{ $info->{$channel} }= map { s/@//; $_ } split /\s/, $members;
  308. }
  309. sub _default {
  310. my ($event, $args) = @_[ARG0 .. $#_];
  311. my @output = ( "$event: " );
  312. # irc_rss_notify($event);
  313. for my $arg (@$args) {
  314. if ( ref $arg eq 'ARRAY' ) {
  315. push( @output, '[' . join(' ,', @$arg ) . ']' );
  316. }
  317. else {
  318. push ( @output, "'$arg'" );
  319. }
  320. }
  321. print join ' ', @output, "\n";
  322. return 0;
  323. }
  324. # IRC RSS Notify
  325. sub irc_rss_notify {
  326. my ($event) = shift;
  327. if ($event eq 'irc_ping') {
  328. $ping_time++;
  329. if ($ping_time % 5 == 0) {
  330. system("perl aggregate.pl");
  331. }
  332. if ($ping_time % 7 == 0) {
  333. my $data = LoadFile("result.yaml");
  334. foreach my $key (keys %{ $data }) {
  335. foreach my $feed (@{ $data->{$key} }) {
  336. $irc->yield( privmsg => '#perl' => "[ ". $feed->{type} . " ] " .
  337. _encode($feed->{title}). '<'.($feed->{author} || "Unknown").'> :: '. $feed->{link} );
  338. }
  339. }
  340. }
  341. }
  342. }
  343. # URL Find : Title Notify
  344. sub irc_urifind_uri {
  345. my ($who, $channel, $url, $obj, $msg) = @_[ARG0 .. ARG4];
  346. my $ua = LWP::UserAgent->new;
  347. my $res = $ua->get($url);
  348. if ($res->is_success) {
  349. $base_url = $url;
  350. my $html = scraper {
  351. process 'title', title => 'TEXT';
  352. };
  353. my $data = $html->scrape(URI->new($url));
  354. return 0 unless $data->{title};
  355. my $title = $data->{title};
  356. $title = encode('cp949', $title) if utf8::valid($title);
  357. $irc->yield( privmsg => $channel => $title );
  358. $channel =~ s/#//;
  359. }
  360. }