From bb1da1df94cbd0a34ccca3df33fb3fb1e5f63f4e Mon Sep 17 00:00:00 2001 From: Marius Gavrilescu Date: Mon, 17 Mar 2014 01:40:48 +0200 Subject: [PATCH] Initial release --- Changes | 8 ++ MANIFEST | 7 ++ Makefile.PL | 16 +++ README | 31 ++++++ devbot | 87 +++++++++++++++ lib/App/Devbot.pm | 266 ++++++++++++++++++++++++++++++++++++++++++++++ t/App-Devbot.t | 53 +++++++++ 7 files changed, 468 insertions(+) create mode 100644 Changes create mode 100644 MANIFEST create mode 100644 Makefile.PL create mode 100644 README create mode 100755 devbot create mode 100644 lib/App/Devbot.pm create mode 100644 t/App-Devbot.t diff --git a/Changes b/Changes new file mode 100644 index 0000000..056d809 --- /dev/null +++ b/Changes @@ -0,0 +1,8 @@ +App::Devbot (0.001) 13 Jun 2013 + * Initial release +App::Devbot (0.001001) 15 Jun 2013 + * Log quits and nicks + * Add tests for quits and nicks + * Clean up tests +App::Devbot (0.001002) 15 Jun 2013 + * Fix typo: Compnent instead of Component \ No newline at end of file diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..16c0ba2 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,7 @@ +Changes +Makefile.PL +MANIFEST +README +devbot +t/App-Devbot.t +lib/App/Devbot.pm diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..9804782 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,16 @@ +use 5.014002; +use ExtUtils::MakeMaker; + +WriteMakefile( + NAME => 'App::Devbot', + VERSION_FROM => 'lib/App/Devbot.pm', + EXE_FILES => [ 'devbot' ], + ABSTRACT_FROM => 'lib/App/Devbot.pm', + AUTHOR => 'Marius Gavrilescu ', + LICENSE => 'perl', + PREREQ_PM => { + 'POE::Component::IRC' => 6.37, #6.37 is the version in Debian squeeze. Hope devbot works with 6.37 :-). + 'IRC::Utils' => 0, + 'File::Slurp' => 0, + }, +); diff --git a/README b/README new file mode 100644 index 0000000..a1480a5 --- /dev/null +++ b/README @@ -0,0 +1,31 @@ +App-Devbot version 0.001002 +======================= + +devbot is an IRC bot which provides various channel services that help software developers. + +Right now, it only provides channel logging and file storage. It might do more in the future. + +INSTALLATION + +To install this module type the following: + + perl Makefile.PL + make + make test + make install + +DEPENDENCIES + +This module requires these other modules and libraries: + + * POE::Component::IRC + * IRC::Utils + * File::Slurp + +COPYRIGHT AND LICENCE + +Copyright (C) 2013 by Marius Gavrilescu + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.14.2 or, +at your option, any later version of Perl 5 you may have available. diff --git a/devbot b/devbot new file mode 100755 index 0000000..299f9c3 --- /dev/null +++ b/devbot @@ -0,0 +1,87 @@ +#!/usr/bin/perl -wT +use v5.14; + +use App::Devbot; + +App::Devbot->run; + +__END__ + +=head1 NAME + +devbot - IRC bot which helps development + +=head1 SYNOPSIS + + # devbot will connect to OFTC with SSL, join #mychan and log everything there. Its nickname will be 'devbot'. + devbot --channel "#mychan" + + # devbot will connect to Freenode without SSL, join #asd and #qwe, and log everything there. Its nickname will be 'loggerbot'. + devbot --nick loggerbot --server chat.freenode.net --port 6667 --no-ssl --channel "#asd" --channel "#qwe" + + # devbot will identify to NickServ with password 'passWORD123', join #bestchan on OFTC and store files sent by channel users. + devbot --nick mybot --password passWORD123 --channel '#bestchan' --no-log --store-files + +=head1 DESCRIPTION + +devbot is an IRC bot which helps developers collaborate. + +Right now, it only does channel logging and file storage. It might do more in the future. + +=head1 OPTIONS + +=over + +=item B<--nick> I + +The nickname of devbot. Defaults to devbot. + +=item B<--password> I + +If supplied, identify to NickServ with this password + +=item B<--server> I + +The server to connect to. Defaults to irc.oftc.net. + +=item B<--port> I + +The port to connect to. Defaults to 6697. + +=item B<--ssl>, B<--no-ssl> + +B<--ssl> enables connecting to the server with SSL, B<--no-ssl> disables this. Defaults to B<--ssl>. + +=item B<--channel> I + +Makes devbot connect to I. Can be supplied multiple times for multiple channels. Has no default value. + +=item B<--log>, B<--no-log> + +B<--log> enables logging events to 'logs/I/I.txt'. B<--no-log> disables logging. Defaults to B<--log>. + +=item B<--store-files>, B<--no-store-files> + +B<--store-files> enables storing files received via DCC to 'files/I'. Files are only accepted if the sender and devbot share a channel. B. B<--no-store-files> disables storing files. Defaults to <--no-store-files>. + +=item B<--trace>, B<--no-trace> + +B<--trace> enables POE::Component::IRC::State tracing. Useful for debugging. B<--no-trace> disables tracing. Defaults to B<--no-trace>. + +=back + +=head1 CAVEATS + +As stated above, the B<--store-files> option should only be used on private channels where every user is trusted. + +=head1 AUTHOR + +Marius Gavrilescu, Emarius@ieval.roE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2013 by Marius Gavrilescu + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.14.2 or, +at your option, any later version of Perl 5 you may have available. diff --git a/lib/App/Devbot.pm b/lib/App/Devbot.pm new file mode 100644 index 0000000..019bcc7 --- /dev/null +++ b/lib/App/Devbot.pm @@ -0,0 +1,266 @@ +package App::Devbot 0.001002; +use v5.14; +use warnings; + +use POE; +use POE::Component::IRC::State; +use POE::Component::IRC::Plugin::AutoJoin; +use POE::Component::IRC::Plugin::NickServID; + +use File::Slurp qw/append_file/; +use IRC::Utils qw/parse_user/; + +use Getopt::Long; +use POSIX qw/strftime/; + +################################################## + +our $VERSION; + +my $nick='devbot'; +my $password; +my $server='irc.oftc.net'; +my $port=6697; +my $ssl=1; +my @channels; +my $trace=0; + +my $log=1; +my $store_files=0; + +GetOptions ( + "nick=s" => \$nick, + "password=s" => \$password, + "server=s" => \$server, + "port=i" => \$port, + "ssl!" => \$ssl, + "channel=s" => \@channels, + "log!" => \$log, + "store-files!" => \$store_files, + "trace!" => \$trace, +); + +my $irc; + +sub mode_char { + my ($channel, $nick)=@_; + return '~' if $irc->is_channel_owner($channel, $nick); + return '&' if $irc->is_channel_admin($channel, $nick); + return '@' if $irc->is_channel_operator($channel, $nick); + return '%' if $irc->is_channel_halfop($channel, $nick); + return '+' if $irc->has_channel_voice($channel, $nick); + return ' ' +} + +sub log_event{ + return unless $log; + my ($channel, @strings) = @_; + my $file=strftime '%F', localtime; + mkdir 'logs'; + mkdir "logs/$channel"; + append_file "logs/$channel/$file.txt", strftime ('%T ', localtime), @strings, "\n"; +} + +sub bot_start{ + $irc->plugin_add (NickServID => POE::Component::IRC::Plugin::NickServID->new(Password => $password)) if defined $password; + $irc->plugin_add (AutoJoin => POE::Component::IRC::Plugin::AutoJoin->new( + Channels => \@channels, + RejoinOnKick => 1, + Rejoin_delay => 10, + Retry_when_banned => 60, + )); + + $irc->yield(register => "all"); + $irc->yield( + connect => { + Nick => $nick, + Username => 'devbot', + Ircname => "devbot $VERSION", + Server => $server, + Port => $port, + UseSSL => $ssl, + } + ); +} + +sub on_public{ + my ($fulluser, $channels, $message)=@_[ARG0, ARG1, ARG2]; + my $nick=parse_user $fulluser; + + for (@$channels) { + my $mode_char=mode_char $_, $nick; + log_event $_, "<$mode_char$nick> $message"; + } +} + +sub on_ctcp_action{ + my ($fulluser, $channels, $message)=@_[ARG0, ARG1, ARG2]; + my $nick=parse_user $fulluser; + + log_event $_, "* $nick $message" for @$channels; +} + +sub on_join{ + my ($fulluser, $channel)=@_[ARG0, ARG1]; + my ($nick, $user, $host)=parse_user $fulluser; + + log_event $channel, "-!- $nick [$user\@$host] has joined $channel"; +} + +sub on_part{ + my ($fulluser, $channel, $message)=@_[ARG0, ARG1, ARG2]; + my ($nick, $user, $host)=parse_user $fulluser; + + log_event $channel, "-!- $nick [$user\@$host] has left $channel [$message]"; +} + +sub on_kick{ + my ($fulluser, $channel, $target, $message)=@_[ARG0, ARG1, ARG2, ARG3]; + my $nick=parse_user $fulluser; + + log_event $channel, "-!- $target was kicked from $channel by $nick [$message]"; +} + +sub on_mode{ + my ($fulluser, $channel, @args)=@_[ARG0 .. $#_]; + my $nick=parse_user $fulluser; + my $mode=join ' ', @args; + + log_event $channel, "-!- mode/$channel [$mode] by $nick"; +} + +sub on_topic{ + my ($fulluser, $channel, $topic)=@_[ARG0, ARG1, ARG2]; + my $nick=parse_user $fulluser; + + log_event $channel, "-!- $nick changed the topic of $channel to: $topic" if $topic; + log_event $channel, "-!- Topic unset by $nick on $channel" unless $topic; +} + +sub on_nick{ + my ($fulluser, $nick, $channels)=@_[ARG0, ARG1, ARG2]; + my $oldnick=parse_user $fulluser; + + log_event $_, "-!- $oldnick is now known as $nick" for @$channels; +} + +sub on_quit{ + my ($fulluser, $message, $channels)=@_[ARG0, ARG1, ARG2]; + my ($nick, $user, $host)=parse_user $fulluser; + + log_event $_, "-!- $nick [$user\@$host] has quit [$message]" for @$channels; +} + +sub on_dcc_request{ + return unless $store_files; + my ($fulluser, $type, $cookie, $name)=@_[ARG0, ARG1, ARG3, ARG4]; + my $nick=parse_user $fulluser; + return unless $type eq 'SEND'; + return unless $irc->nick_channels($nick); + return if $name =~ m,/,; + + mkdir 'files'; + $irc->yield(dcc_accept => $cookie, "files/$name"); +} + +sub run{ + $irc=POE::Component::IRC::State->spawn(); + + POE::Session->create( + inline_states => { + _start => \&bot_start, + irc_public => \&on_public, + irc_ctcp_action => \&on_ctcp_action, + irc_join => \&on_join, + irc_part => \&on_part, + irc_kick => \&on_kick, + irc_mode => \&on_mode, + irc_topic => \&on_topic, + irc_nick => \&on_nick, + irc_quit => \&on_quit, + irc_dcc_request => \&on_dcc_request + }, + options => { + trace => $trace + } + ); + + $poe_kernel->run(); +} + +1; + +__END__ + +=head1 NAME + +App::Devbot - IRC bot which helps development + +=head1 SYNOPSIS + + use App::Devbot; + App::Devbot->run; + +=head1 DESCRIPTION + +App::Devbot is an IRC bot which helps developers collaborate. + +Right now, it only does channel logging and file storage. It might do more in the future. + +=head1 OPTIONS + +=over + +=item B<--nick> I + +The nickname of devbot. Defaults to devbot. + +=item B<--password> I + +If supplied, identify to NickServ with this password + +=item B<--server> I + +The server to connect to. Defaults to irc.oftc.net. + +=item B<--port> I + +The port to connect to. Defaults to 6697. + +=item B<--ssl>, B<--no-ssl> + +B<--ssl> enables connecting to the server with SSL, B<--no-ssl> disables this. Defaults to B<--ssl>. + +=item B<--channel> I + +Makes devbot connect to I. Can be supplied multiple times for multiple channels. Has no default value. + +=item B<--log>, B<--no-log> + +B<--log> enables logging events to 'logs/I/I.txt'. B<--no-log> disables logging. Defaults to B<--log>. + +=item B<--store-files>, B<--no-store-files> + +B<--store-files> enables storing files received via DCC to 'files/I'. Files are only accepted if the sender and devbot share a channel. B. B<--no-store-files> disables storing files. Defaults to <--no-store-files>. + +=item B<--trace>, B<--no-trace> + +B<--trace> enables POE::Component::IRC::State tracing. Useful for debugging. B<--no-trace> disables tracing. Defaults to B<--no-trace>. + +=back + +=head1 CAVEATS + +As stated above, the B<--store-files> option should only be used on private channels where every user is trusted. + +=head1 AUTHOR + +Marius Gavrilescu, Emarius@ieval.roE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2013 by Marius Gavrilescu + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.14.2 or, +at your option, any later version of Perl 5 you may have available. diff --git a/t/App-Devbot.t b/t/App-Devbot.t new file mode 100644 index 0000000..02b67d4 --- /dev/null +++ b/t/App-Devbot.t @@ -0,0 +1,53 @@ +#!/usr/bin/perl -w +use v5.14; +use warnings; +no warnings 'redefine'; + +use Test::More tests => 11; +BEGIN { use_ok('App::Devbot') }; + +use POE; + +sub call_poe{ + my ($func, @args)=@_; + my @arglist; + $arglist[ARG0 + $_]=$args[$_] for 0 .. $#args; + $func->(@arglist) +} + +sub set_test{ + my ($expected, $testname) = @_; + *App::Devbot::log_event = sub { shift; is "@_", $expected, $testname }; +} + +*App::Devbot::mode_char = sub { ' ' }; + +set_test '< nick> Hello, world!', 'public'; +call_poe \&App::Devbot::on_public, 'nick!user@host', ['#channel'], 'Hello, world!'; + +set_test '* nick nicked', 'action'; +call_poe \&App::Devbot::on_ctcp_action, 'nick!user@host', ['#channel'], 'nicked'; + +set_test '-!- nick [user@host] has joined #channel', 'join'; +call_poe \&App::Devbot::on_join, 'nick!user@host', '#channel'; + +set_test '-!- nick [user@host] has left #channel [Leaving!]', 'part'; +call_poe \&App::Devbot::on_part, 'nick!user@host', '#channel', 'Leaving!'; + +set_test '-!- idiot was kicked from #channel by nick [no reason]', 'kick'; +call_poe \&App::Devbot::on_kick, 'nick!user@host', '#channel', 'idiot', 'no reason'; + +set_test '-!- mode/#channel [+oo mgv mgvx] by ChanServ', 'mode'; +call_poe \&App::Devbot::on_mode, 'ChanServ!user@host', '#channel', '+oo', 'mgv', 'mgvx'; + +set_test '-!- nick changed the topic of #channel to: Go away!', 'topic set'; +call_poe \&App::Devbot::on_topic, 'nick!user@host', '#channel', 'Go away!'; + +set_test '-!- Topic unset by nick on #channel', 'topic unset'; +call_poe \&App::Devbot::on_topic, 'nick!user@host', '#channel', ''; + +set_test '-!- nick is now known as newnick', 'nick'; +call_poe \&App::Devbot::on_nick, 'nick!user@host', 'newnick', ['#channel']; + +set_test '-!- nick [user@host] has quit [Quitting]', 'quit'; +call_poe \&App::Devbot::on_quit, 'nick!user@host', 'Quitting', ['#channel']; -- 2.39.2