From 14db80ac9a1698074e90a29597ad0417b9a4323c Mon Sep 17 00:00:00 2001 From: Sergey Zhmylove Date: Wed, 13 Jan 2016 22:38:14 +0300 Subject: [PATCH] Added sayto feature --- README.md | 4 ++ patch/Bot.pm.patch | 43 ++++++++---- src/config.pl | 8 ++- src/jplbot.pl | 172 +++++++++++++++++++++++++++++++++++---------- 4 files changed, 175 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 09cdbd4..ec6af89 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ The command list follows: * user: -- * fortune -- show random thesis from fortune(6). * http://uri -- show information about first found URI. +* sayto/user/txt -- send txt to user's private when bot founds him or her presence notification. Original paper (in russian) is available via [Tune-IT blogs](http://www.tune-it.ru/web/korg/home/-/blogs/пишем-простенького-jabber-бота-на-perl). Feel free to change 'time' into 'scalar localtime' in the code if you prefer human-readable format :) @@ -26,3 +27,6 @@ Feel free to change 'time' into 'scalar localtime' in the code if you prefer hum * src/config.pl -- configuration file included when needed. * patch/Bot.pm.patch -- patch for Net::Jabber::Bot to avoid some warnings, add password functionality and comment-out message parser to perform it manually. +## What if ... +If you liked this bot, you can send me a postcard to [s@zhmylove.ru](mailto:s@zhmylove.ru) and star this project on the github. +If you want to propose some features, code improvements or bug reports, just use "Issues". Yep, I'll read them. diff --git a/patch/Bot.pm.patch b/patch/Bot.pm.patch index af9a128..6279d48 100644 --- a/patch/Bot.pm.patch +++ b/patch/Bot.pm.patch @@ -1,5 +1,5 @@ --- Bot.pm.old 2016-01-09 19:49:41.801668822 +0300 -+++ Bot.pm 2016-01-13 15:39:49.231884763 +0300 ++++ Bot.pm 2016-01-13 22:36:11.792165653 +0300 @@ -49,17 +49,18 @@ has 'loop_sleep_time' => (isa => PosNum, is => 'rw', default => 5); has 'process_timeout' => (isa => PosNum, is => 'rw', default => 5); @@ -22,27 +22,37 @@ has 'forum_join_time' => (isa => HashRef[Int], is => 'rw', default => sub{{}}); # List of when we joined each forum has 'out_messages_per_second' => (isa => PosNum, is => 'rw', default => sub{5}); has 'message_delay' => (isa => PosNum, is => 'rw', default => sub {1/5}); -@@ -67,6 +68,8 @@ +@@ -67,6 +68,10 @@ has 'max_message_size' => (isa => HundredInt, is => 'rw', default => 1000000); has 'max_messages_per_hour' => (isa => PosInt, is => 'rw', default => 1000000); +has 'JidDB' => (isa => HashRef[HashRef[Str]], is => 'rw', required => 1); ++has 'SayToDB' => (isa => HashRef[HashRef[HashRef[HashRef[Str]]]], is => 'rw', required => 1); ++has 'SayTo' => (isa => Maybe[CodeRef], is => 'rw', default => sub{undef}); + # Initialize this hour's message count. has 'messages_sent_today' => (isa => 'HashRef', is => 'ro', default => sub{{(localtime)[7] => {(localtime)[2] => 0}}}); -@@ -294,6 +297,10 @@ +@@ -294,6 +299,18 @@ safetey: 166 +=item B + +Database of JIDs per forum ++ ++=item B ++ ++Database of sayto messages per forum ++ ++=item B ++ ++Function which reacts on presence + =back =cut -@@ -422,7 +429,7 @@ +@@ -422,7 +439,7 @@ $self->Process(5); foreach my $forum (keys %{$self->forums_and_responses}) { @@ -51,7 +61,7 @@ } INFO("Connected to server '" . $self->server . "' successfully"); -@@ -443,12 +450,14 @@ +@@ -443,12 +460,14 @@ sub JoinForum { my $self = shift; my $forum_name = shift; @@ -66,7 +76,7 @@ ); $self->forum_join_time->{$forum_name} = time; -@@ -649,21 +658,7 @@ +@@ -649,21 +668,7 @@ # Determine if this message was addressed to me. (groupchat only) my $bot_address_from; @@ -89,7 +99,7 @@ # Call the message callback if it's defined. if( defined $self->message_function) { -@@ -789,6 +784,20 @@ +@@ -789,6 +794,25 @@ my $status = $presence->GetStatus(); $status = "." if(!defined $status); @@ -97,20 +107,25 @@ + $forum = (split '@', $forum)[0]; + + if($type eq 'unavailable') { # Delete disconnected user -+ if (defined ${ $self->JidDB->{$forum} }{$nick}) { -+ delete ${ $self->JidDB->{$forum} }{$nick}; ++ if(defined $self->JidDB->{$forum}->{$nick}) { ++ delete $self->JidDB->{$forum}->{$nick}; + } + + return; + } + + # "register" user on any other types -+ ${ $self->JidDB->{$forum} }{$nick} = 1; ++ $self->JidDB->{$forum}->{$nick} = 1; ++ ++ # call SayTo subroutine if needed ++ if(defined $self->SayToDB->{$forum}->{$nick}) { ++ &{ $self->SayTo }($self, $forum, $nick); ++ } + DEBUG("Presence From $from t=$type s=$status"); DEBUG("Presence XML: " . $presence->GetXML()); } -@@ -896,7 +905,8 @@ +@@ -896,7 +920,8 @@ Assures message size does not exceed a limit and chops it into pieces if need be. NOTE: non-printable characters (unicode included) will be stripped before sending to the server via: @@ -120,7 +135,7 @@ =cut -@@ -989,7 +999,8 @@ +@@ -989,7 +1014,8 @@ # Strip out anything that's not a printable character # Now with unicode support? @@ -130,7 +145,7 @@ my $message_length = length($message_chunk); DEBUG("Sending message $yday-$hour-$messages_this_hour $message_length bytes to $recipient"); -@@ -1134,6 +1145,22 @@ +@@ -1134,6 +1160,22 @@ $self->jabber_client->Subscription(type=>"unsubscribe", to=>$user); $self->jabber_client->Subscription(type=>"unsubscribed",to=>$user); } @@ -148,7 +163,7 @@ + + return 0 if(!defined $forum || !defined $nick); + -+ return ${ $self->JidDB->{$forum} }{$nick} || 0; ++ return $self->JidDB->{$forum}->{$nick} || 0; +} =back diff --git a/src/config.pl b/src/config.pl index 77cea37..e0d0df4 100644 --- a/src/config.pl +++ b/src/config.pl @@ -4,6 +4,8 @@ $name = 'AimBot'; # Path to file for karma saving routine $karmafile = '/tmp/karma'; +# Path to file for sayto saving routine +$karmafile = '/tmp/sayto'; # Address of XMPP server of the bot's account $server = 'zhmylove.ru'; # Port of XMPP server of the bot's account @@ -26,6 +28,10 @@ 'бело-коричневый', 'коричневый', ); # Minimum number of colors to select from @colors -$minimum_colors = 3; +$colors_minimum = 3; +# Maximum number of sayto messages per forum +$sayto_max = 128; +# 1 week before cleanup +$sayto_keep_time = 604800; # ### USER VARIABLES SECTION END ############################################# diff --git a/src/jplbot.pl b/src/jplbot.pl index e554a1d..ed86851 100755 --- a/src/jplbot.pl +++ b/src/jplbot.pl @@ -14,6 +14,8 @@ # See comments in the 'config.pl' our $name = 'AimBot'; our $karmafile = '/tmp/karma'; +our $saytofile = '/tmp/sayto'; +our $sayto_keep_time = 604800; our $server = 'zhmylove.ru'; our $port = 5222; our $username = 'aimbot'; @@ -27,7 +29,8 @@ 'бело-синий', 'синий', 'бело-коричневый', 'коричневый', ); -our $minimum_colors = 3; +our $colors_minimum = 3; +our $sayto_max = 128; unless (my $ret = do './config.pl') { warn "couldn't parse config.pl: $@" if $@; @@ -38,29 +41,49 @@ srand; store {}, $karmafile unless -r $karmafile; my %karma = %{retrieve($karmafile)}; -my $last_bomb_time = 0; +store {}, $saytofile unless -r $saytofile; +my %sayto = %{retrieve($saytofile)}; +my %jid_DB = (); my %bomb_time; my %bomb_correct; my %bomb_resourse; my %bomb_nick; -my $col_count = int($minimum_colors + ($#colors - $minimum_colors + 1) * rand); +my $last_bomb_time = 0; +my $col_count = int($colors_minimum + ($#colors - $colors_minimum + 1) * rand); my %col_hash; +my %forum_list; $col_hash{lc($_)} = 1 for @colors; -my %jid_DB = (); +$forum_list{$_} = [] for keys %forum_passwords; # [] due to Bot.pm.patch my $qname = quotemeta($name); my $bot_address = "https://github.com/tune-it/jplbot"; +my $rb = "[\x{20}\x{22}\x{26}\x{27}\x{2f}\x{3a}\x{3c}\x{3e}\x{40}]"; +my $rB = "[^$rb]"; $SIG{INT} = \&shutdown; $SIG{TERM} = \&shutdown; binmode STDOUT, ':utf8'; -my $rb = "[\x{20}\x{22}\x{26}\x{27}\x{2f}\x{3a}\x{3c}\x{3e}\x{40}]"; -my $rB = "[^$rb]"; sub shutdown { store \%karma, $karmafile and say "Karma saved to: $karmafile"; + store \%sayto, $saytofile and say "Sayto saved to: $saytofile"; exit 0; } +sub say_to { + my ($bot, $room, $dst) = @_; + + return unless (defined $sayto{$room} && defined $sayto{$room}->{$dst}); + + foreach my $src (keys $sayto{$room}->{$dst}) { + $bot->SendPersonalMessage("$room\@$conference_server/$dst", + "Тебе писал $src: [" . $sayto{$room}->{$dst}->{$src}->{'text'}); + + delete $sayto{$room}->{$dst}->{$src}; + + delete $sayto{$room}->{$dst} unless scalar keys $sayto{$room}->{$dst}; + } +} + sub bomb_user { my ($bot, $user) = @_; my $to = $bomb_resourse{lc($user)}; @@ -85,8 +108,22 @@ sub bomb_user { sub background_checks { my $bot = shift; store \%karma, $karmafile; + foreach(keys %bomb_time){ - bomb_user($bot, $_) if (time > $bomb_time{lc($_)} + 60); + bomb_user($bot, $_) if (time > + $bomb_time{lc($_)} + $loop_sleep_time); + } + + foreach my $room (keys %forum_passwords) { + foreach my $dst (keys $sayto{$room}) { + foreach my $src (keys $sayto{$room}->{$dst}) { + delete $sayto{$room}->{$dst}->{$src} if ( time > + $sayto{$room}->{$dst}->{$src}->{'time'} + $sayto_keep_time + ); + } + + delete $sayto{$room}->{$dst} unless scalar keys $sayto{$room}->{$dst}; + } } } @@ -118,31 +155,19 @@ sub new_bot_message { "$from: " . time); } - when (/(?:ubunt|убунт)/i) { - $bot->SendGroupMessage($msg{'reply_to'}, - "убунта нинужна >_<") if int(2*rand); - } - - when (/^(?:(?:добро|все|ребя)\w*)*\s*утр/i || /^утр\w*\s*[.!]*\s*$/i) { + when (/emacs/i) { $bot->SendGroupMessage($msg{'reply_to'}, - "$from: и тебе доброе утро!"); + "use vim or die;") if int(2*rand); } - when (/^ку[\s!]*\b/i || /^(?:всем\s*)?прив\w*[.\s!]*$/i || - /^здаро\w*\s*/) { + when (/sudo/) { $bot->SendGroupMessage($msg{'reply_to'}, - "Привет, привет!"); + "sudo нинужно >_<") if int(2*rand); } - when (/^пыщь?(?:-пыщь?)?[.\s!]*$/i) { - $bot->SendGroupMessage($msg{'reply_to'}, - "$from: пыщь-пыщь, дави прыщь!"); - } - - when (/^(?:доброй|спокойной|всем)?\s*ночи[.\s!]*$/i || - /^[\w.,\s]*[шс]пать[.\s!]*$/i) { + when (/(?:ubunt|убунт)/i) { $bot->SendGroupMessage($msg{'reply_to'}, - "Сладких снов!"); + "убунта нинужна >_<") if int(2*rand); } when (/^help\s*$/i) { @@ -151,16 +176,18 @@ sub new_bot_message { $bot->SendPersonalMessage($msg{'reply_to'} . "/$from", "Краткая справка: \n" . - " bomb nick -- установить бомбу\n" . - " date -- вывести дату\n" . - " fortune -- вывеси цитату\n" . - " karma nick -- вывести карму\n" . - " sayto /nick/ -- сказать пользователю\n" . - " time -- вывести время\n" . + " bomb nick -- установить бомбу\n" . + " date -- вывести дату\n" . + " fortune -- вывеси цитату\n" . + " karma nick -- вывести карму\n" . + " sayto /nick/text -- сказать пользователю\n" . + " time -- вывести время\n" . "\n" . "Вопросы и предложения: $bot_address\n" . + "В благодарность вы можете нажать Star на странице проекта. " . + "Это совершенно бесплатно\n" . "Чмоки ;-)" - ); + ); } when (/^(?:fortune|ф)\s*$/i) { @@ -252,6 +279,78 @@ sub new_bot_message { my $response = $ua->get($uri); } + when (/^test$/) { + return unless defined $sayto{$forum}; + + foreach my $src (keys $sayto{$forum}) { + say $src; + foreach my $dst (keys $sayto{$forum}->{$src}) { + say " $dst"; + say " " . $sayto{$forum}->{$src}->{$dst}->{'text'}; + say " " . $sayto{$forum}->{$src}->{$dst}->{'time'}; + } + } + } + + when (m{^sayto[^/]*/([^/]*)/(.*)$}s) { + my $sayto_to = $1; + my $sayto_txt = $2; + + if ($bot->IsInRoom($forum, $sayto_to)) { + $bot->SendGroupMessage($msg{'reply_to'}, + "$sayto_to: смотри, тебе пишет $from!"); + + return; + } + + if (defined $sayto{$forum}) { + if (scalar keys $sayto{$forum} > $sayto_max) { + $bot->SendGroupMessage($msg{'reply_to'}, + "$from: у меня кончилось место :("); + + return; + } + + if (defined $sayto{$forum}->{$sayto_to}->{$from} && + defined $sayto{$forum}->{$sayto_to}->{$from}->{'text'}) { + $bot->SendGroupMessage($msg{'reply_to'}, + "$from: предыдущее значение: [" . + $sayto{$forum}->{$sayto_to}->{$from}->{'text'} . + "]"); + } + } + + $sayto{$forum}->{$sayto_to}->{$from} = { + 'text' => $sayto_txt, + 'time' => time, + }; + + $bot->SendGroupMessage($msg{'reply_to'}, + "$from: замётано."); + } + + when (/^(?:(?:добро|все|ребя)\w*)*\s*утр/i || /^утр\w*\s*[.!]*\s*$/i) { + $bot->SendGroupMessage($msg{'reply_to'}, + "$from: и тебе доброе утро!"); + } + + when (/^ку[\s!]*\b/i || /^(?:всем\s*)?прив\w*[.\s!]*$/i || + /^здаро\w*\s*/) { + $bot->SendGroupMessage($msg{'reply_to'}, + "Привет, привет!"); + } + + when (/^пыщь?(?:-пыщь?)?[.\s!]*$/i) { + $bot->SendGroupMessage($msg{'reply_to'}, + "$from: пыщь-пыщь, дави прыщь!"); + } + + when (/^(?:доброй|спокойной|всем)?\s*ночи[.\s!]*$/i || + /^[\w.,\s]*[шс]пать[.\s!]*$/i) { + $bot->SendGroupMessage($msg{'reply_to'}, + "Сладких снов!"); + } + when (sub{return $col_hash{lc($_)} || 0}) { return unless defined $bomb_correct{lc($from)}; @@ -276,12 +375,12 @@ sub new_bot_message { given ($msg{'body'}) { - when (/^(?:karma|карма)$rb*?$qnick$/i) { + when (/^(?:karma|карма)$rb*?$qnick\s*?$/i) { $bot->SendGroupMessage($msg{'reply_to'}, "$from: карма $nick " . ($karma{lc($nick)}||0)); } - when (/^(?:bomb|бомба)$rb*?$qnick$/i) { + when (/^(?:bomb|бомба)$rb*?$qnick\s*?$/i) { if ($from eq $nick) { $bot->SendGroupMessage($msg{'reply_to'}, "$from: привык забавляться сам с собой?"); @@ -356,9 +455,6 @@ sub new_bot_message { } } -my %forum_list; -$forum_list{$_} = [] for keys %forum_passwords; # [] due to Bot.pm.patch - my $bot = Net::Jabber::Bot->new( server => $server, conference_server => $conference_server, @@ -374,6 +470,8 @@ sub new_bot_message { forums_and_responses => \%forum_list, forums_passwords => \%forum_passwords, JidDB => \%jid_DB, + SayTo => \&say_to, + SayToDB => \%sayto, ); $bot->Start();