#!/opt/bin/perl use strict; use Socket; use IO::Socket::INET; use YAML; use Encode; use Event; use Net::Knuddels; use Algorithm::MarkovChain; use String::Similarity; use List::Util; use Time::HiRes; my @CHANNELS = ( 'Flirt', 'Flirt Private', 'Singles 11-14', 'Singles 15-17', 'Singles 11-14 2', 'Singles 15-17 2', 'Flirt 2', 'Flirt Private 2', 'Singles 11-14 3', 'Singles 15-17 3', 'Flirt 3', 'Flirt Private 3', 'Singles 11-14 4', 'Singles 15-17 4', 'Flirt 4', 'Flirt Private 4', 'Singles 11-14 5', 'Singles 15-17 5', 'Flirt 5', 'Flirt Private 5', 'Singles 11-14 6', 'Singles 15-17 6', 'Flirt 6', 'Flirt Private 6', 'Singles 11-14 7', 'Singles 15-17 7', 'Flirt 7', 'Flirt Private 7', 'Singles 11-14 8', 'Singles 15-17 8', 'Flirt 8', 'Flirt Private 8', ); my $logdir = "logs"; my $Knick = "ich bin suess"; my $Kpass = "qwerty"; my $client; my $seed = Load do { open my $fh, "<", "markovbot.dat"; binmode $fh, ":utf8"; local $/; <$fh> }; $seed ||= []; my $fwd = new Algorithm::MarkovChain; my $rev = new Algorithm::MarkovChain; my %freq; my $word_cnt; sub word { $_[0] =~ /(\w+)/ ? lc $1 : (); } sub seed_msg { my $msg = $_[0]; return if $msg =~ /leck|fick|möse|mose|moese|uschi|usci|ushi|fikkn |bumsen|wichs|wix|popp|popen|fuck|dreck|laber|saugen|pussy|\bsuck\b |\bblas|piss|schluck|fingern|spritz|\bloch|\bsteck|vögel|voegel|vogel |hure|strich|sklave|handschell|slave |stengel|penis|schwanz|pimmel|steifen|ejak|sperma|\btitt|\bass|\bsack|\bsaft\b |\bcs\b|\bts\b|\brs\b|\bicq\b |cam\b|\bmsn\b|\bbot\b|\bchat.*bot\b|tanga|schlafen|sex|\bsau\b |intim|arsch|fotze|dumm|schnauze|klappe|rasiert|fresse|maul\b|\bmaul|rosett?e |zieh.*aus|nackt |sätz|saetz|setze|\bsatz |sc?h?wul|schwuchtel|transe|transv|schlampe|tussi|nutte|mädel |admin|nachgeburt |(?-i:[A-Z]{4,}) |\S{20,} /xi; my @msg = $msg =~ m/(\S+)/g; $freq{word $_}++ for @msg; $word_cnt += @msg; $fwd->seed (symbols => \@msg, longest => 10); $rev->seed (symbols => [reverse @msg], longest => 10); } for (@$seed) { seed_msg $_; } my %grammar_reply = qw( ich du du ich mir dir dir mir mein dein dein mein frau mann mädel junge mädchen junge freund freundin freundin freund girls boys boys girls huhu hi hi hi hallo hi typen mädels ); sub gen_reply { my ($msg) = @_; my @msg = $msg =~ /(\S+)/g; @msg = map { my $word = word $_; $grammar_reply{$word} or $freq{$word} < $word_cnt * 0.002 && $freq{$word} >= 2 && 4 < length $word ? $word : () } @msg; my $reply; my $best = 2; for (1..10) { shift @msg if @msg > $_; my $len = rand() ** 3 * 15 + 2; my @r = reverse $rev->spew (complete => [reverse @msg], length => $len, stop_at_terminal => 1); @r = $fwd->spew (complete => \@r, length => $len, stop_at_terminal => 1); my $r = join " ", @r; my $b = (List::Util::sum map $freq{word $_} / $word_cnt, @r) / @r; ($reply, $best) = ($r, $b) if $b < $best; } $reply; } while (0) { my $r = gen_reply scalar <>; print "$r\n\n"; } Event->signal (signal => "INT", cb => sub { Event::unloop(-1) }); Event->timer (interval => 60, cb => sub { open my $fh, ">", "markovbot.dat~" or return; print $fh Encode::encode_utf8 Dump $seed; close $fh; rename "markovbot.dat~", "markovbot.dat"; }); sub logit { my ($msg, $file, $src, $dst, $room) = @_; mkdir $logdir; my $fh; unless (open $fh, ">>:utf8", Encode::encode_utf8 "$logdir/$file") { warn "Couldn't open for appending $logdir/$src: $!\n"; return; } print $fh "$room\t$src\t$dst\t$msg\n"; } #################################################################################### ########################## MAIN START ############################################## #################################################################################### $client = new Net::Knuddels::Client PeerAddr => "213.61.5.150:2710", command_wait => sub { my ($client, $wait) = @_; Event->timer (after => $wait, cb => sub { $client->command_cb }); }; Event->io ( fd => $client->fh, poll => 'r', cb => sub { $client->ready or $_[0]->w->cancel; }); $client->login; #$client->register (ALL => sub { # use Dumpvalue; # print "---\n"; # Dumpvalue->new (compactDump => 1, veryCompact => 1, quoteHighBit => 1, tick => '"')->dumpValue ([@_]); #}); $client->register (login => sub { $client->enter_room ($_, $Knick, $Kpass) for @CHANNELS; }); $client->register (msg_room => sub { my ($room, $user, $msg) = @_; }); my @queue; Event->timer (interval => 1, cb => sub { my $msg = shift @queue or return; logit ($msg->[2], $msg->[0], $Knick, $msg->[0], $msg->[1]); $client->send_priv_msg (@$msg); }); my $some_room; $client->register (room_info => sub { print "JOIN ROOM: $_[0]\n"; $some_room = $_[0]; }); Event->timer (after => 60, interval => 60, cb => sub { $client->send_priv_msg ("James", $some_room, "/knuschel"); }); my %next_time; $client->register (msg_priv_nondup => sub { my ($room, $src, $dst, $msg) = @_; my $NOW = Time::HiRes::time; $msg =~ s/\260[^\260]*\260//g; print "($room) $src >> $msg\n"; logit ($msg, $src, $src, $dst, $room); return if $next_time{$src} > time; # do not talk unnaturally often my $reply = gen_reply $msg; push @$seed, $msg; seed_msg $msg; my $delay = 30 * (rand) ** 5 + 0.3 * length $reply; $next_time{$src} = time + $delay; print "($room) $src << $reply ($delay)\n"; Event->timer (at => $NOW + $delay, cb => sub { push @queue, [$src, $room, $reply]; }); }); Event::loop;