summaryrefslogtreecommitdiffstats
path: root/lib/ASM/Event.pm
diff options
context:
space:
mode:
authorLibravatarJanik Kleinhoff <janik@kleinhoff.de>2015-09-24 01:32:11 +0000
committerLibravatarJanik Kleinhoff <janik@kleinhoff.de>2015-09-24 01:32:11 +0000
commit9b472795d26cd93d1bb58488ef60a062f5237295 (patch)
tree8572778595d145176e720a1b7168c73adbd64ed4 /lib/ASM/Event.pm
parentb93c3a24f14e0f64bc46b4945a65ae1bba62dc12 (diff)
Rework module paths
Diffstat (limited to 'lib/ASM/Event.pm')
-rw-r--r--lib/ASM/Event.pm887
1 files changed, 887 insertions, 0 deletions
diff --git a/lib/ASM/Event.pm b/lib/ASM/Event.pm
new file mode 100644
index 0000000..e6f4c23
--- /dev/null
+++ b/lib/ASM/Event.pm
@@ -0,0 +1,887 @@
+package ASM::Event;
+use warnings;
+use strict;
+
+use Data::Dumper;
+use Text::LevenshteinXS qw(distance);
+use IO::All;
+use POSIX qw(strftime);
+use Regexp::Wildcards;
+use HTTP::Request;
+
+sub cs {
+ my ($chan) = @_;
+ $chan = lc $chan;
+ $chan =~ s/^[@+]//;
+ return $::channels->{channel}->{$chan} if ( defined($::channels->{channel}->{$chan}) );
+ return $::channels->{channel}->{default};
+}
+
+sub maxlen {
+ my ($a, $b) = @_;
+ my ($la, $lb) = (length($a), length($b));
+ return $la if ($la > $lb);
+ return $lb;
+}
+
+sub new
+{
+ my $module = shift;
+ my ($conn, $inspector) = @_;
+ my $self = {};
+ $self->{CONN} = $conn;
+ $self->{INSPECTOR} = $inspector;
+ ASM::Util->dprint('Installing handler routines...', 'startup');
+ $conn->add_default_handler(\&blah);
+ $conn->add_handler('bannedfromchan', \&on_bannedfromchan);
+ $conn->add_handler('mode', \&on_mode);
+ $conn->add_handler('join', \&on_join);
+ $conn->add_handler('part', \&on_part);
+ $conn->add_handler('quit', \&on_quit);
+ $conn->add_handler('nick', \&on_nick);
+ $conn->add_handler('notice', \&on_notice);
+ $conn->add_handler('caction', \&on_public);
+ $conn->add_handler('msg', \&on_msg);
+ $conn->add_handler('namreply', \&on_names);
+ $conn->add_handler('endofnames', \&on_names);
+ $conn->add_handler('public', \&on_public);
+ $conn->add_handler('376', \&on_connect);
+ $conn->add_handler('topic', \&irc_topic);
+ $conn->add_handler('topicinfo', \&irc_topic);
+ $conn->add_handler('nicknameinuse', \&on_errnickinuse);
+ $conn->add_handler('bannickchange', \&on_bannickchange);
+ $conn->add_handler('kick', \&on_kick);
+ $conn->add_handler('cping', \&on_ctcp);
+ $conn->add_handler('cversion', \&on_ctcp);
+ $conn->add_handler('csource', \&on_ctcp_source);
+ $conn->add_handler('ctime', \&on_ctcp);
+ $conn->add_handler('cdcc', \&on_ctcp);
+ $conn->add_handler('cuserinfo', \&on_ctcp);
+ $conn->add_handler('cclientinfo', \&on_ctcp);
+ $conn->add_handler('cfinger', \&on_ctcp);
+ $conn->add_handler('354', \&on_whoxreply);
+ $conn->add_handler('315', \&on_whoxover);
+ $conn->add_handler('263', \&on_whofuckedup);
+ $conn->add_handler('account', \&on_account);
+ $conn->add_handler('ping', \&on_ping);
+ $conn->add_handler('banlist', \&on_banlist);
+ $conn->add_handler('dcc_open', \&dcc_open);
+ $conn->add_handler('chat', \&on_dchat);
+ $conn->add_handler('channelmodeis', \&on_channelmodeis);
+ $conn->add_handler('quietlist', \&on_quietlist);
+ $conn->add_handler('pong', \&on_pong);
+ $conn->add_handler('statsdebug', \&on_statsdebug);
+ $conn->add_handler('endofstats', \&on_endofstats);
+ $conn->add_handler('channelurlis', \&on_channelurlis);
+ $conn->add_handler('480', \&on_jointhrottled);
+ $conn->add_handler('invite', \&blah); # This doesn't need to be fancy; I just need it to go through inspect
+ bless($self);
+ return $self;
+}
+
+my $clearstatsp = 1;
+my %statsp = ();
+my %oldstatsp = ();
+
+sub on_jointhrottled
+{
+ my ($conn, $event) = @_;
+ my $chan = $event->{args}->[1];
+ ASM::Util->dprint("$event->{nick}: $chan: $event->{args}->[2]", 'snotice');
+ if ($event->{args}->[2] =~ /throttle exceeded, try again later/) {
+ $conn->schedule(5, sub { $conn->join($chan); });
+ }
+}
+
+sub on_statsdebug
+{
+ my ($conn, $event) = @_;
+ my ($char, $line) = ($event->{args}->[1], $event->{args}->[2]);
+ if ($char eq 'p') {
+ if ($clearstatsp) {
+ $clearstatsp = 0;
+ %oldstatsp = %statsp;
+ %statsp = ();
+ }
+ if ($line =~ /^(\d+) staff members$/) {
+ #this is the end of the report
+ } else {
+ my ($nick, $userhost) = split(" ", $line);
+ $userhost =~ s/\((.*)\)/$1/;
+ my ($user, $host) = split("@", $userhost);
+ $statsp{$nick}= [$user, $host];
+ }
+ }
+}
+
+sub on_endofstats
+{
+ my ($conn, $event) = @_;
+ if ($event->{args}->[1] eq 'p') {
+ $clearstatsp=1;
+ my $tmp = Dumper(\%statsp); chomp $tmp;
+ if ( join(',', sort(keys %oldstatsp)) ne join(',', sort(keys %statsp)) ) {
+ open(FH, '>>', 'statsplog.txt');
+ say FH strftime('%F %T ', gmtime) . join(',', sort(keys %statsp));
+ close(FH);
+ ASM::Util->dprint(join(",", keys %statsp), 'statsp');
+ }
+ # $event->{args}->[2] == "End of /STATS report"
+ #end of /stats p
+ }
+}
+
+my $lagcycles = 0;
+my $pongcount = 0;
+
+sub on_pong
+{
+ my ($conn, $event) = @_;
+ alarm 120;
+ $conn->schedule( 30, sub { $conn->sl("PING :" . time); } );
+ ASM::Util->dprint('Pong? ... Ping!', 'pingpong');
+ my $lag = time - $event->{args}->[0];
+ my @changes = $::fm->scan();
+ if (@changes) {
+ if ($::settingschanged) {
+ $::settingschanged = 0;
+ } else {
+ $conn->privmsg($::settings->{masterchan}, "Config files changed, auto rehash triggered. Check console for possible errors.");
+ ASM::XML->readXML();
+ my @strbl = io('string_blacklist.txt')->getlines;
+ chomp @strbl;
+ @::string_blacklist = @strbl;
+ }
+ }
+ if ($lag > 1) {
+ ASM::Util->dprint("Latency: $lag", 'latency');
+ }
+ if (($pongcount % 3) == 0) { #easiest way to do something roughly every 90 seconds
+ $conn->sl('STATS p');
+ }
+ if ((time - $::starttime) < 240 ) {
+ return; #we don't worry about lag if we've just started up and are still syncing etc.
+ }
+ if (($lag > 2) && ($lag < 5)) {
+ $conn->privmsg( $::settings->{masterchan}, "Warning: I'm currently lagging by $lag seconds.");
+ }
+ if ($lag >= 5) {
+ $lagcycles++;
+ if ($lagcycles >= 3) {
+ $conn->quit("Automatic restart triggered due to persistent lag. Freenode staff: If this is happening too frequently, please " .
+ "set a nickserv freeze on my account, and once my connection is stable, unfreeze the account and /kill me to tri" .
+ "gger a reconnect.");
+ } else {
+ $conn->privmsg( $::settings->{masterchan}, "Warning: I'm currently lagging by $lag seconds. This marks heavy lag cycle " .
+ "$lagcycles - automatic restart will be triggered after 3 lag cycles." );
+ }
+ }
+ if (($lag <= 5) && ($lagcycles > 0)) {
+ $lagcycles--;
+# $conn->privmsg( $::settings->{masterchan}, "Warning: Heavy lag cycle count has been reduced to $lagcycles" );
+ ASM::Util->dprint('$lag = ' . $lag . '; $lagcycles = ' . $lagcycles, 'latency');
+ }
+}
+
+sub on_dchat
+{
+ my ($conn, $event) = @_;
+ ASM::Util->dprint(Dumper($event), 'dcc');
+ if ( #(lc $event->{nick} eq 'afterdeath') &&
+ ($event->{args}->[0] ne '')) {
+ my $msg = $event->{args}->[0];
+ if ($msg =~ /^SPY (.*)/) {
+ my $chan = $1;
+ $::spy{lc $chan} = $event->{to}[0];
+ } elsif ($msg =~ /^STOPSPY (.*)/) {
+ delete $::spy{lc $1};
+ } elsif ($msg =~ /^RETRIEVE (\S+)/) {
+ my $chan = lc $1;
+ my $out = $event->{to}[0];
+ my @time = ($::settings->{log}->{zone} eq 'local') ? localtime : gmtime;
+ say $out 'Retrieving ' . "$::settings->{log}->{dir}${chan}/${chan}" . strftime($::settings->{log}->{filefmt}, @time);
+ open(FHX, "$::settings->{log}->{dir}${chan}/${chan}" . strftime($::settings->{log}->{filefmt}, @time));
+ while (<FHX>) {
+ print $out $_;
+ }
+ close FHX;
+ }
+ #lols we gots a chat message! :D
+ }
+}
+
+sub on_ping
+{
+ my ($conn, $event) = @_;
+ $conn->sl("PONG " . $event->{args}->[0]);
+# alarm 200;
+ ASM::Util->dprint('Ping? Pong!', 'pingpong');
+# ASM::Util->dprint(Dumper($event), 'pingpong');
+}
+
+sub on_account
+{
+ my ($conn, $event) = @_;
+ $::sn{lc $event->{nick}}{account} = lc $event->{args}->[0];
+}
+
+sub on_connect {
+ my ($conn, $event) = @_; # need to check for no services
+ $conn->sl("MODE $event->{args}->[0] +Q");
+ if (lc $event->{args}->[0] ne lc $::settings->{nick}) {
+ ASM::Util->dprint('Attempting to regain my main nick', 'startup');
+ $conn->privmsg( 'NickServ@services.', "regain $::settings->{nick} $::settings->{pass}" );
+ }
+ $conn->sl('CAP REQ :extended-join multi-prefix account-notify'); #god help you if you try to use this bot off freenode
+}
+
+sub on_join {
+ my ($conn, $event) = @_;
+ my $nick = lc $event->{nick};
+ my $chan = lc $event->{to}->[0];
+ my $rate;
+# alarm 200;
+ if ( lc $conn->{_nick} eq lc $nick) {
+ $::sc{$chan} = {};
+ mkdir($::settings->{log}->{dir} . $chan);
+ $::synced{$chan} = 0;
+ unless ( @::syncqueue ) {
+ $conn->sl('who ' . $chan . ' %tcnuhra,314');
+ $conn->sl('mode ' . $chan);
+ $conn->sl('mode ' . $chan . ' bq');
+ }
+ push @::syncqueue, $chan;
+ }
+ $::sc{$chan}{users}{$nick} = {};
+ $::sc{$chan}{users}{$nick}{hostmask} = $event->{userhost};
+ $::sc{$chan}{users}{$nick}{op} = 0;
+ $::sc{$chan}{users}{$nick}{voice} = 0;
+ $::sc{$chan}{users}{$nick}{jointime} = time;
+ $::sc{$chan}{users}{$nick}{msgtime} = 0;
+ if (defined($::sn{$nick})) {
+ my @mship = ();
+ if (defined($::sn{$nick}->{mship})) {
+ @mship = @{$::sn{$nick}->{mship}};
+ }
+ @mship = (@mship, $chan);
+ $::sn{$nick}->{mship} = \@mship;
+ } else {
+ $::sn{$nick} = {};
+ $::sn{$nick}->{mship} = [ $chan ];
+ }
+ $::sn{$nick}->{dnsbl} = 0;
+ $::sn{$nick}->{netsplit} = 0;
+ $::sn{$nick}->{gecos} = $event->{args}->[1];
+ $::sn{$nick}->{user} = $event->{user};
+ $::sn{$nick}->{host} = $event->{host};
+ $::sn{$nick}->{account} = lc $event->{args}->[0];
+ $::db->logg($event) if defined $::db;
+ $::log->logg( $event );
+ $::inspector->inspect( $conn, $event ) unless $::netsplit;
+}
+
+sub on_part
+{
+ my ($conn, $event) = @_;
+ my $nick = lc $event->{nick};
+ my $chan = lc $event->{to}->[0];
+ $::log->logg( $event );
+ $::db->logg( $event ) if defined $::db;
+ if (defined $::db and $event->{args}->[0] =~ /^requested by/) {
+ my $idx = $::db->actionlog( $event);
+ $::log->sqlIncident($chan, $idx) if $idx;
+ }
+# "to" => [ "#antispammeta" ],
+# "args" => [ "requested by ow (test)" ],
+# "nick" => "aoregcdu",
+ $::inspector->inspect( $conn, $event );
+ if (defined($::sn{$nick}) && defined($::sn{$nick}->{mship})) {
+ my @mship = @{$::sn{$nick}->{mship}};
+ @mship = grep { lc $_ ne $chan } @mship;
+ if ( @mship ) {
+ $::sn{$nick}->{mship} = \@mship;
+ } else {
+ delete($::sn{$nick});
+ }
+ }
+ if ( lc $conn->{_nick} eq $nick )
+ {
+ delete( $::sc{$chan} );
+ on_byechan($chan);
+ }
+ else
+ {
+ delete( $::sc{$chan}{users}{$nick} );
+ }
+}
+
+sub on_msg
+{
+ my ($conn, $event) = @_;
+ $::commander->command($conn, $event);
+ ASM::Util->dprint($event->{from} . " - " . $event->{args}->[0], 'msg');
+ if ((ASM::Util->notRestricted($event->{nick}, "nomsgs")) && ($event->{args}->[0] !~ /^;;/)) {
+# disabled by DL 130513 due to spammer abuse
+# $conn->privmsg($::settings->{masterchan}, $event->{from} . ' told me: ' . $event->{args}->[0]);
+ }
+}
+
+sub on_public
+{
+ my ($conn, $event) = @_;
+# alarm 200;
+ my $chan = lc $event->{to}[0];
+ $chan =~ s/^[+@]//;
+ $::log->logg( $event );
+ $::db->logg( $event ) if defined $::db;
+ if ($event->{args}->[0] =~ /(https?:\/\/bitly.com\/\w+|https?:\/\/bit.ly\/\w+|https?:\/\/j.mp\/\w+|https?:\/\/tinyurl.com\/\w+)/i) {
+ my $reqid = $::async->add( HTTP::Request->new( GET => $1 ) );
+ $::httpRequests{$reqid} = $event;
+ my ($response, $id) = $::async->wait_for_next_response( 1 );
+ if (defined($response)) {
+ on_httpResponse($conn, $id, $response);
+ }
+ else { $conn->schedule( 1, sub { checkHTTP($conn); } ); }
+ }
+ $::inspector->inspect( $conn, $event );
+ $::commander->command( $conn, $event );
+ $::sc{$chan}{users}{lc $event->{nick}}{msgtime} = time;
+}
+
+sub checkHTTP
+{
+ my ($conn) = @_;
+ my ($response, $id) = $::async->next_response();
+ if (defined ($response)) {
+ on_httpResponse($conn, $id, $response);
+ }
+ $conn->schedule( 1, sub { checkHTTP($conn); } );
+}
+
+sub on_httpResponse
+{
+ my ($conn, $id, $response) = @_;
+ my $event = $::httpRequests{$id};
+ delete $::httpRequests{$id};
+ $::inspector->inspect( $conn, $event, $response );
+}
+# if ($response->{_previous}->{_headers}->{location} =~ /^https?:\/\/bitly.com\/a\/warning/)
+
+sub on_notice
+{
+ my ($conn, $event) = @_;
+ return if ( $event->{to}->[0] eq '$*' ); # if this is a global notice FUCK THAT SHIT
+ $::log->logg( $event );
+ $::db->logg( $event ) if defined $::db;
+ $::inspector->inspect( $conn, $event );
+ $::services->doServices($conn, $event);
+}
+
+sub on_errnickinuse
+{
+ my ($conn, $event) = @_;
+ $_ = ${$::settings->{altnicks}}[rand @{$::settings->{altnicks}}];
+ ASM::Util->dprint("Nick is in use, trying $_", 'startup');
+ $conn->nick($_);
+}
+
+sub on_bannickchange
+{
+ my ($conn, $event) = @_;
+ $_ = ${$::settings->{altnicks}}[rand @{$::settings->{altnicks}}];
+ ASM::Util->dprint("Nick is in use, trying $_", 'startup');
+ $conn->nick($_);
+}
+
+sub on_quit
+{
+ my ($conn, $event) = @_;
+ my @channels=();
+ for ( keys %::sc ) {
+ push ( @channels, lc $_ ) if delete $::sc{lc $_}{users}{lc $event->{nick}};
+ }
+ $event->{to} = \@channels;
+ if (defined $::db) {
+ my $idx = $::db->actionlog($event);
+ $::log->sqlIncident( join(',', @channels), $idx ) if $idx;
+ $::db->logg( $event );
+ }
+ $::log->logg( $event );
+
+ if (($::netsplit == 0) && ($event->{args}->[0] eq "*.net *.split") && (lc $event->{nick} ne 'chanserv')) { #special, netsplit situation
+ $conn->privmsg($::settings->{masterchan}, "Entering netsplit mode - JOIN and QUIT inspection will be disabled for 60 minutes");
+ $::netsplit = 1;
+ $conn->schedule(60*60, sub { $::netsplit = 0; $conn->privmsg($::settings->{masterchan}, 'Returning to regular operation'); });
+ }
+ $::inspector->inspect( $conn, $event ) unless $::netsplit;
+ #ugh. Repurge some shit, hopefully this will fix some stuff where things are going wrong
+ foreach my $chan ( keys %::sc ) {
+ delete $::sc{$chan}{users}{lc $event->{nick}};
+ }
+ delete($::sn{lc $event->{nick}});
+}
+
+sub blah
+{
+ my ($self, $event) = @_;
+ ASM::Util->dprint(Dumper($event), 'misc');
+ $::inspector->inspect($self, $event);
+}
+
+sub irc_users
+{
+ my ( $channel, @users ) = @_;
+ for (@users)
+ {
+ my ( $op, $voice );
+ $op = 0; $voice = 0;
+ $op = 1 if s/^\@//;
+ $voice = 1 if s/^\+//;
+ $::sc{lc $channel}{users}{lc $_} = {};
+ $::sc{lc $channel}{users}{lc $_}{op} = $op;
+ $::sc{lc $channel}{users}{lc $_}{voice} = $voice;
+ $::sc{lc $channel}{users}{lc $_}{jointime} = 0;
+ }
+}
+
+sub on_names {
+ my ($conn, $event) = @_;
+ irc_users( $event->{args}->[2], split(/ /, $event->{args}->[3]) ) if ($event->{type} eq 'namreply');
+}
+
+sub irc_topic {
+ my ($conn, $event) = @_;
+ if ($event->{format} eq 'server')
+ {
+ my $chan = lc $event->{args}->[1];
+ if ($event->{type} eq 'topic')
+ {
+ $::sc{$chan}{topic}{text} = $event->{args}->[2];
+ }
+ elsif ($event->{type} eq 'topicinfo')
+ {
+ $::sc{$chan}{topic}{time} = $event->{args}->[3];
+ $::sc{$chan}{topic}{by} = $event->{args}->[2];
+ }
+ }
+ else
+ {
+ if ($event->{type} eq 'topic')
+ {
+ my $chan = lc $event->{to}->[0];
+ $::sc{$chan}{topic}{text} = $event->{args}->[0];
+ $::sc{$chan}{topic}{time} = time;
+ $::sc{$chan}{topic}{by} = $event->{from};
+ }
+ $::log->logg($event);
+ $::db->logg( $event ) if defined $::db;
+ $::inspector->inspect($conn, $event);
+ }
+}
+
+sub on_nick {
+ my ($conn, $event) = @_;
+ my @channels=();
+ my $oldnick = lc $event->{nick};
+ my $newnick = lc $event->{args}->[0];
+ foreach my $chan ( keys %::sc )
+ {
+ $chan = lc $chan;
+ if ( defined $::sc{$chan}{users}{$oldnick} )
+ {
+ if ($oldnick ne $newnick) { #otherwise a nick change where they're only
+ #changing the case of their nick means that
+ #ASM forgets about them.
+ $::sc{$chan}{users}{$newnick} = $::sc{$chan}{users}{$oldnick};
+ delete( $::sc{$chan}{users}{$oldnick} );
+ }
+ push ( @channels, $chan );
+ }
+ }
+
+ # unfortunately Net::IRC sucks at IRC so we have to implement this ourselves
+ if ($oldnick eq lc $conn->{_nick}) {
+ $conn->{_nick} = $event->{args}[0];
+ }
+
+ $::sn{$newnick} = $::sn{$oldnick} if ($oldnick ne $newnick);
+ $::db->logg( $event ) if defined $::db;
+ delete( $::sn{$oldnick}) if ($oldnick ne $newnick);
+ $event->{to} = \@channels;
+ $::log->logg($event);
+ # Well, the nick change actually was done from the old nick ... but
+ # by the time we process it, they already changed nicks. Therefore
+ # we'll pretend it's the *new* nick that generated the event.
+ $event->{nick} = $event->{args}[0];
+ $::inspector->inspect($conn, $event);
+}
+
+sub on_kick {
+ my ($conn, $event) = @_;
+ if (lc $event->{to}->[0] eq lc $::settings->{nick}) {
+ $conn->privmsg($::settings->{masterchan}, "I've been kicked from " . $event->{args}->[0] . ": " . $event->{args}->[1]);
+# $conn->join($event->{args}->[0]);
+ }
+ my $nick = lc $event->{to}->[0];
+ my $chan = lc $event->{args}->[0];
+ $::log->logg( $event );
+ if (defined $::db) {
+ $::db->logg( $event );
+ my $idx = $::db->actionlog($event);
+ $::log->sqlIncident($chan, $idx) if $idx;
+ }
+ if (defined($::sn{$nick}) && defined($::sn{$nick}->{mship})) {
+ my @mship = @{$::sn{$nick}->{mship}};
+ @mship = grep { lc $_ ne $chan } @mship;
+ if ( @mship ) {
+ $::sn{$nick}->{mship} = \@mship;
+ } else {
+ delete($::sn{$nick});
+ }
+ }
+ if ( lc $conn->{_nick} eq $nick )
+ {
+ delete( $::sc{lc $event->{args}->[0]} );
+ on_byechan(lc $event->{to}->[0]);
+ }
+ else
+ {
+ delete( $::sc{lc $event->{args}->[0]}{users}{$nick} );
+ }
+}
+
+sub parse_modes
+{
+ my ( $n ) = @_;
+ my @args = @{$n};
+ my @modes = split '', shift @args;
+ my @new_modes=();
+ my $t;
+ foreach my $c ( @modes ) {
+ if (($c eq '-') || ($c eq '+')) {
+ $t=$c;
+ }
+ else { #eIbq,k,flj,CFLMPQcgimnprstz
+ if ($t eq '+') {
+ if ( grep( /[eIbqkfljov]/,($c) ) ) { #modes that take args WHEN BEING ADDED
+ push (@new_modes, [$t.$c, shift @args]);
+ }
+ elsif ( grep( /[CFLMPQcgimnprstz]/, ($c) ) ) {
+ push (@new_modes, [$t.$c]);
+ }
+ else {
+ die "Unknown mode $c !\n";
+ }
+ } else {
+ if ( grep( /[eIbqov]/,($c) ) ) { #modes that take args WHEN BEING REMOVED
+ push (@new_modes, [$t.$c, shift @args]);
+ }
+ elsif ( grep( /[CFLMPQcgimnprstzkflj]/, ($c) ) ) {
+ push (@new_modes, [$t.$c]);
+ }
+ else {
+ die "Unknown mode $c !\n";
+ }
+ }
+ }
+ }
+ return \@new_modes;
+}
+
+sub on_channelmodeis
+{
+ my ($conn, $event) = @_;
+ my $chan = lc $event->{args}->[1];
+ my @temp = @{$event->{args}};
+ shift @temp; shift @temp;
+ my @modes = @{parse_modes(\@temp)};
+ foreach my $line ( @modes ) {
+ my @ex = @{$line};
+ my ($what, $mode) = split (//, $ex[0]);
+ if ($what eq '+') {
+ if (defined($ex[1])) {
+ push @{$::sc{$chan}{modes}}, $mode . ' ' . $ex[1];
+ } else {
+ push @{$::sc{$chan}{modes}}, $mode;
+ }
+ } else {
+ my @modes = grep {!/^$mode/} @{$::sc{$chan}{modes}};
+ $::sc{$chan}{modes} = \@modes;
+ }
+ }
+}
+
+sub whoGotHit
+{
+ my ($chan, $mask) = @_;
+ my $cvt = Regexp::Wildcards->new(type => 'jokers');
+ my @affected = ();
+ if ($mask !~ /^\$/) {
+ my @div = split(/\$/, $mask);
+ my $regex = $cvt->convert($div[0]);
+ foreach my $nick (keys %::sn) {
+ next unless defined($::sn{$nick}{user});
+ if (lc ($nick.'!'.$::sn{$nick}{user}.'@'.$::sn{$nick}{host}) =~ /^$regex$/i) {
+ push @affected, $nick if defined($::sc{$chan}{users}{$nick});
+ }
+ }
+ } elsif ($mask =~ /^\$a:(.*)/) {
+ my @div = split(/\$/, $1);
+ my $regex = $cvt->convert($div[0]);
+ foreach my $nick (keys %::sn) {
+ next unless defined($::sn{$nick}{account});
+ if (lc ($::sn{$nick}{account}) =~ /^$regex$/i) {
+ push @affected, $nick if defined($::sc{$chan}{users}{$nick});
+ }
+ }
+ }
+ return @affected;
+}
+
+sub on_mode
+{
+ my ($conn, $event) = @_;
+ my $chan = lc $event->{to}->[0];
+# holy shit, I feel so bad doing this
+# I have no idea how or why Net::IRC fucks up modes if they've got a ':' in one of the args
+# but you do what you must...
+ my @splitted = split(/ /, $::lastline); shift @splitted; shift @splitted; shift @splitted;
+ $event->{args}=\@splitted;
+ if ($chan =~ /^#/) {
+ my @modes = @{parse_modes($event->{args})};
+ ASM::Util->dprint(Dumper(\@modes), 'misc');
+ foreach my $line ( @modes ) {
+ my @ex = @{$line};
+
+ if ( $ex[0] eq '+o' ) { $::sc{$chan}{users}{lc $ex[1]}{op} = 1; }
+ elsif ( $ex[0] eq '-o' ) { $::sc{$chan}{users}{lc $ex[1]}{op} = 0; }
+ elsif ( $ex[0] eq '+v' ) { $::sc{$chan}{users}{lc $ex[1]}{voice} = 1; }
+ elsif ( $ex[0] eq '-v' ) { $::sc{$chan}{users}{lc $ex[1]}{voice} = 0; }
+
+ elsif ( $ex[0] eq '+b' ) {
+ $::sc{$chan}{bans}{$ex[1]} = { bannedBy => $event->{from}, bannedOn => time };
+ if (lc $event->{nick} !~ /^(floodbot)/) { #ignore the ubuntu floodbots 'cause they quiet people a lot
+ my @affected = whoGotHit($chan, $ex[1]);
+ if ( defined($::db) && (@affected) && (scalar @affected <= 4) ) {
+ foreach my $victim (@affected) {
+ my $idx = $::db->actionlog($event, 'ban', $victim);
+ $::log->sqlIncident( $chan, $idx ) if $idx;
+ }
+ }
+ if ($ex[1] =~ /^\*\!\*\@(.*)$/) {
+ my $ip = ASM::Util->getHostIP($1);
+ $::sc{$chan}{ipbans}{$ip} = { bannedBy => $event->{from}, bannedOn => time } if defined($ip);
+ }
+ }
+ }
+ elsif ( $ex[0] eq '-b' ) {
+ delete $::sc{$chan}{bans}{$ex[1]};
+ if ($ex[1] =~ /^\*\!\*\@(.*)$/) {
+ my $ip = ASM::Util->getHostIP($1);
+ delete $::sc{$chan}{ipbans}{$ip} if defined($ip);
+ }
+ }
+
+ elsif ( $ex[0] eq '+q' ) {
+ $::sc{$chan}{quiets}{$ex[1]} = { bannedBy => $event->{from}, bannedOn => time };
+ if (lc $event->{nick} !~ /^(floodbot)/) {
+ my @affected = whoGotHit($chan, $ex[1]);
+ if ( defined($::db) && (@affected) && (scalar @affected <= 4) ) {
+ foreach my $victim (@affected) {
+ my $idx = $::db->actionlog($event, 'quiet', $victim);
+ $::log->sqlIncident( $chan, $idx ) if $idx;
+ }
+ }
+ if ($ex[1] =~ /^\*\!\*\@(.*)$/) {
+ my $ip = ASM::Util->getHostIP($1);
+ $::sc{$chan}{ipquiets}{$ip} = { bannedBy => $event->{from}, bannedOn => time } if defined($ip);
+ }
+ }
+ }
+ elsif ( $ex[0] eq '-q' ) {
+ delete $::sc{$chan}{quiets}{$ex[1]};
+ if ($ex[1] =~ /^\*\!\*\@(.*)$/) {
+ my $ip = ASM::Util->getHostIP($1);
+ delete $::sc{$chan}{ipquiets}{$ip} if defined($ip);
+ }
+ }
+
+ else {
+ my ($what, $mode) = split (//, $ex[0]);
+ if ($what eq '+') {
+ if (defined($ex[1])) { push @{$::sc{$chan}{modes}}, $mode . ' ' . $ex[1]; }
+ else { push @{$::sc{$chan}{modes}}, $mode; }
+ } else {
+ my @modes = grep {!/^$mode/} @{$::sc{$chan}{modes}};
+ $::sc{$chan}{modes} = \@modes;
+ }
+ if ( ($ex[0] eq '+r') && (! defined($::watchRegged{$chan})) ) {
+ $::watchRegged{$chan} = 1;
+ $conn->schedule(60*45, sub { checkRegged($conn, $chan); });
+ }
+ }
+ }
+ $::log->logg($event);
+ }
+}
+
+sub checkRegged
+{
+ my ($conn, $chan) = @_;
+ if (grep {/^r/} @{$::sc{$chan}{modes}}
+ and not ((defined($::channels->{channel}{$chan}{monitor})) and ($::channels->{channel}{$chan}{monitor} eq "no")) )
+ {
+ my $tgt = $chan;
+ my $risk = "debug";
+ my $hilite=ASM::Util->commaAndify(ASM::Util->getAlert($tgt, $risk, 'hilights'));
+ my $txtz ="\x03" . $::RCOLOR{$::RISKS{$risk}} . "\u$risk\x03 risk threat [\x02$chan\x02] - channel appears to still be +r after 45 minutes; ping $hilite !att-$chan-$risk";
+ my @tgts = ASM::Util->getAlert($tgt, $risk, 'msgs');
+ ASM::Util->sendLongMsg($conn, \@tgts, $txtz)
+ }
+ delete $::watchRegged{$chan};
+}
+
+sub on_banlist
+{
+ my ($conn, $event) = @_;
+ my ($me, $chan, $ban, $banner, $bantime) = @{$event->{args}};
+ $::sc{lc $chan}{bans}{$ban} = { bannedBy => $banner, bannedOn => $bantime };
+ if ($ban =~ /^\*\!\*\@((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]))$/) {
+ # ASM::Util->dprint("banlist hostname $ban $1", 'sync');
+ my $ip = ASM::Util->getHostIP($1);
+ $::sc{lc $chan}{ipbans}{$ip} = { bannedBy => $banner, bannedOn => $bantime } if defined($ip);
+ }
+}
+
+sub on_quietlist
+{
+ my ($conn, $event) = @_;
+ my ($me, $chan, $mode, $ban, $banner, $bantime) = @{$event->{args}};
+ $::sc{lc $chan}{quiets}{$ban} = { bannedBy => $banner, bannedOn => $bantime };
+ if ($ban =~ /^\*\!\*\@((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]))$/) {
+ # ASM::Util->dprint("quietlist hostname $ban $1", 'sync');
+ my $ip = ASM::Util->getHostIP($1);
+ $::sc{lc $chan}{ipquiets}{$ip} = { bannedBy => $banner, bannedOn => $bantime } if defined($ip);
+ }
+}
+
+sub on_channelurlis
+{
+ my ($conn, $event) = @_;
+ $::sc{lc $event->{args}->[1]}{url} = $event->{args}->[2];
+}
+
+sub on_ctcp
+{
+ my ($conn, $event) = @_;
+ my $acct = lc $::sn{lc $event->{nick}}->{account};
+ ASM::Util->dprint(Dumper($event), 'ctcp');
+ if (($event->{type} eq 'cdcc') &&
+ (defined($::users->{person}->{$acct})) &&
+ (defined($::users->{person}->{$acct}->{flags})) &&
+ (grep {$_ eq 'c'} split('', $::users->{person}->{$acct}->{flags}))) {
+ ASM::Util->dprint(Dumper($event), 'dcc');
+ my @spit = split(/ /, $event->{args}->[0]);
+ if (($spit[0] eq 'CHAT') && ($spit[1] eq 'CHAT')) {
+ $::chat = Net::IRC::DCC::CHAT->new($conn, 0, lc $event->{nick}, $spit[2], $spit[3]);
+ }
+ } else {
+ $::inspector->inspect($conn, $event);
+ }
+}
+
+sub dcc_open
+{
+ my ($conn, $event) = @_;
+ $::dsock{lc $event->{nick}} = $event->{args}->[1];
+}
+
+sub on_ctcp_source
+{
+ my ($conn, $event) = @_;
+ $conn->ctcp_reply($event->{nick}, 'SOURCE https://gitlab.devlabs.linuxassist.net/asm/antispammeta/');
+}
+
+sub on_whoxreply
+{
+ my ($conn, $event) = @_;
+ return unless $event->{args}->[1] eq '314';
+ my ($tgt, $magic, $chan, $user, $host, $nick, $account, $gecos) = @{$event->{args}};
+ $nick = lc $nick; $chan = lc $chan;
+ if (!defined $::sn{lc $nick}) {
+ $::sn{$nick} = {};
+ $::sn{$nick}->{mship} = [$chan];
+ } else {
+ $::sn{$nick}->{mship} = [grep { lc $_ ne $chan } @{$::sn{$nick}->{mship}}];
+ push @{$::sn{$nick}->{mship}}, $chan;
+ }
+ $::sn{$nick}->{gecos} = $gecos;
+ $::sn{$nick}->{user} = $user;
+ $::sn{$nick}->{host} = $host;
+ $::sn{$nick}->{account} = lc $account;
+}
+
+sub on_whoxover
+{
+ my ($conn, $event) = @_;
+ my $chan = pop @::syncqueue;
+ $::synced{lc $event->{args}->[1]} = 1;
+ if (defined($chan) ){
+ $conn->sl('who ' . $chan . ' %tcnuhra,314');
+ $conn->sl('mode ' . $chan);
+ $conn->sl('mode ' . $chan . ' bq');
+ } else {
+ my $size = `ps -p $$ h -o size`;
+ my $cputime = `ps -p $$ h -o time`;
+ chomp $size; chomp $cputime;
+ my ($tx, $rx);
+ if ($conn->{_tx}/1024 > 1024) {
+ $tx = sprintf("%.2fMB", $conn->{_tx}/(1024*1024));
+ } else {
+ $tx = sprintf("%.2fKB", $conn->{_tx}/1024);
+ }
+ if ($conn->{_rx}/1024 > 1024) {
+ $rx = sprintf("%.2fMB", $conn->{_rx}/(1024*1024));
+ } else {
+ $rx = sprintf("%.2fKB", $conn->{_rx}/1024);
+ }
+ $conn->privmsg($::settings->{masterchan}, "Finished syncing after " . (time - $::starttime) . " seconds. " .
+ "I'm tracking " . (scalar (keys %::sn)) . " nicks" .
+ " across " . (scalar (keys %::sc)) . " tracked channels." .
+ " I'm using " . $size . "KB of RAM" .
+ ", have used " . $cputime . " of CPU time" .
+ ", have sent $tx of data, and received $rx of data.");
+ my %x = ();
+ foreach my $c (@{$::settings->{autojoins}}) { $x{$c} = 1; }
+ foreach my $cx (keys %::sc) { delete $x{$cx}; }
+ if (scalar (keys %x)) {
+ $conn->privmsg($::settings->{masterchan}, "Syncing appears to have failed for " . ASM::Util->commaAndify(keys %x));
+ }
+ }
+}
+
+sub on_whofuckedup
+{
+ my ($conn, $event) = @_;
+ ASM::Util->dprint('on_whofuckedup called!', 'sync');
+ if ($event->{args}->[1] eq "STATS") {
+#most likely this is getting called because we did stats p too often.
+#unfortunately the server doesn't let us know what exactly we called stats for.
+#anyways, we don't need to do anything for this
+ } else { #dunno why it got called, print the data and I'll add a handler for it.
+ ASM::Util->dprint(Dumper($event), 'sync');
+ }
+}
+
+sub on_bannedfromchan {
+ my ($conn, $event) = @_;
+ ASM::Util->dprint("I'm banned from " . $event->{args}->[1] . "... attempting to unban myself", 'startup');
+ $conn->privmsg('ChanServ', "unban $event->{args}->[1]");
+}
+
+sub on_byechan {
+ my ($chan) = @_;
+ #TODO do del event stuff
+}
+
+return 1;