summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorLibravatarWilliam Heimbigner <william.heimbigner@gmail.com>2007-06-06 21:30:25 +0000
committerLibravatarWilliam Heimbigner <william.heimbigner@gmail.com>2007-06-06 21:30:25 +0000
commit04c4d4ae0d6fdfe9e59a50e79b40047a08feefb7 (patch)
tree3db7aa17c73151e038f08e3cd0c1b2faffc0c482 /modules
parent110cb874fc58ae4a214889bb8d6545201a57dd2f (diff)
Added files
Diffstat (limited to 'modules')
-rw-r--r--modules/actions.pl48
-rw-r--r--modules/classes.pl124
-rw-r--r--modules/command.pl41
-rw-r--r--modules/event.pl264
-rw-r--r--modules/inspect.pl72
-rw-r--r--modules/log.pl38
-rw-r--r--modules/services.pl44
-rw-r--r--modules/util.pl165
-rw-r--r--modules/xml.pl39
9 files changed, 835 insertions, 0 deletions
diff --git a/modules/actions.pl b/modules/actions.pl
new file mode 100644
index 0000000..7d6717d
--- /dev/null
+++ b/modules/actions.pl
@@ -0,0 +1,48 @@
+use strict;
+use warnings;
+
+#package Actions;
+
+sub Actions::ban {
+ our ($conn, $event, $unmode, $chan, %dct, $id);
+ o_send( $conn, "mode $chan +b *!*\@$event->{host}" );
+ $unmode="mode $chan -b *!*\@$event->{host}";
+}
+
+sub Actions::kban {
+ our ($conn, $event, $unmode, $chan, %dct, $id);
+ o_send($conn, "mode $chan +b *!*\@$event->{host}");
+ o_send($conn, "kick $chan $event->{nick} :$dct{$id}{reason}");
+ $unmode = "mode $chan -b *!*\@$event->{host}";
+}
+
+sub Actions::kick {
+ our ($conn, $event, $unmode, $chan, %dct, $id);
+ o_send($conn, "kick $chan $event->{nick} :$dct{$id}{reason}");
+}
+
+sub Actions::none {
+ return;
+}
+
+sub Actions::quiet {
+ our ($conn, $event, $unmode, $chan, %dct, $id);
+ o_send( $conn, "mode $chan +b %*!*\@$event->{host}" );
+ $unmode = "mode $chan -b %*!*\@$event->{host}";
+}
+
+sub Actions::fmod_wiki {
+ our ($conn, $event, $unmode, $chan, %dct, $id);
+ o_send( $conn, "mode $chan -b *!*\@$event->{host}" );
+ o_send( $conn, "mode $chan +b *!*\@$event->{host}!#wikimedia-ops" );
+}
+
+sub Actions::killsub {
+ undef &Actions::ban;
+ undef &Actions::kban;
+ undef &Actions::kick;
+ undef &Actions::none;
+ undef &Actions::quiet;
+}
+
+return 1;
diff --git a/modules/classes.pl b/modules/classes.pl
new file mode 100644
index 0000000..74b2899
--- /dev/null
+++ b/modules/classes.pl
@@ -0,0 +1,124 @@
+use strict;
+use warnings;
+use Data::Dumper;
+#package Classes;
+
+sub Classes::dnsbl {
+ our (%aonx, $id, %dct, $event, $chan, $rev);
+ if (defined $rev) {
+ my $iaddr = hostip( "$rev$aonx{$id}{content}" );
+ my @dnsbl = unpack( 'C4', $iaddr ) if defined $iaddr;
+ $dct{$id} = $aonx{$id} if (@dnsbl);
+ }
+}
+
+sub Classes::floodqueue {
+ our (%aonx, $id, %dct, $event, $chan);
+ my @cut=split(/:/, $aonx{$id}{content});
+ $dct{$id} = $aonx{$id} if ( flood_add( $chan, $id, $event->{host}, int($cut[1]) ) == int($cut[0]) );
+}
+
+sub Classes::nickspam {
+ our (%aonx, $id, %dct, $event, $chan);
+ my @cut = split(/:/, $aonx{$id}{content});
+ if ( length $event->{args}->[0] >= int($cut[0]) ) {
+ %_ = map { $_=>$_ } lc keys %{$::sc{lc $chan}{users}};
+ my @uniq = grep( $_{$_}, split( / /, lc $event->{args}->[0]) );
+ $dct{$id} = $aonx{$id} if ( $#{ @uniq } >= int($cut[1]) );
+ }
+}
+
+my %cf=();
+my %bs=();
+
+sub Classes::splitflood {
+ our (%aonx, $id, %dct, $event, $chan);
+ my $text;
+ my @cut = split(/:/, $aonx{$id}{content});
+ $cf{$id}{timeout}=int($cut[1]);
+ if ($event->{type} =~ /^(public|notice|part|caction)$/) {
+ $text=$event->{args}->[0];
+ }
+ return unless defined($text);
+ return unless length($text) >= 10;
+ if (defined($bs{$id}{$text}) && (time <= $bs{$id}{$text} + 600)) {
+ $dct{$id}=$aonx{$id};
+ return;
+ }
+ push( @{$cf{$id}{$chan}{$text}}, time );
+ foreach my $nid ( keys %cf ) {
+ foreach my $xchan ( keys %{$cf{$nid}} ) {
+ next if $xchan eq 'timeout';
+ foreach my $host ( keys %{$cf{$nid}{$xchan}} ) {
+ next unless defined $cf{$nid}{$xchan}{$host}[0];
+ while ( time >= $cf{$nid}{$xchan}{$host}[0] + $cf{$nid}{'timeout'} ) {
+ last if ( $#{ $cf{$nid}{$xchan}{$host} } == 0 );
+ shift ( @{$cf{$nid}{$xchan}{$host}} );
+ }
+ }
+ }
+ }
+ if ( $#{ @{$cf{$id}{$chan}{$text}}}+1 == int($cut[0]) ) {
+ $dct{$id}=$aonx{$id};
+ $bs{$id}{$text} = time;
+ }
+}
+
+sub Classes::re {
+ our (%aonx, $id, %dct, $event, $chan);
+ my $match = $event->{args}->[0];
+ $match = $event->{nick} if ($event->{type} eq 'join');
+ if ( (defined $aonx{$id}{nocase}) && ($aonx{$id}{nocase}) ) {
+ $dct{$id}=$aonx{$id} if ($match =~ /$aonx{$id}{content}/i);
+ }
+ else {
+ $dct{$id}=$aonx{$id} if ($match =~ /$aonx{$id}{content}/);
+ }
+}
+
+sub Classes::nick {
+ our (%aonx, $id, %dct, $event, $chan);
+ if ( lc $event->{nick} eq lc $aonx{$id}{content} ) {
+ $dct{$id} = $aonx{$id};
+ }
+}
+
+sub Classes::ident {
+ our (%aonx, $id, %dct, $event, $chan);
+ if ( lc $event->{user} eq lc $aonx{$id}{content} ) {
+ $dct{$id} = $aonx{$id};
+ }
+}
+
+sub Classes::host {
+ our (%aonx, $id, %dct, $event, $chan);
+ if ( lc $event->{host} eq lc $aonx{$id}{content} ) {
+ $dct{$id} = $aonx{$id};
+ }
+}
+
+sub Classes::killsub {
+ undef &Classes::dnsbl;
+ undef &Classes::floodqueue;
+ undef &Classes::nickspam;
+ undef &Classes::re;
+}
+#$VAR1 = bless( {
+# 'to' => [
+# '##asb-testing'
+# ],
+# 'format' => 'mode',
+# 'from' => 'ChanServ!ChanServ@services.',
+# 'user' => 'ChanServ',
+# 'args' => [
+# '+o',
+# 'AntiSpamMetaBeta',
+# ''
+# ],
+# 'nick' => 'ChanServ',
+# 'type' => 'mode',
+# 'userhost' => 'ChanServ@services.',
+# 'host' => 'services.'
+# }, 'Net::IRC::Event' );
+
+return 1;
diff --git a/modules/command.pl b/modules/command.pl
new file mode 100644
index 0000000..87c768f
--- /dev/null
+++ b/modules/command.pl
@@ -0,0 +1,41 @@
+use warnings;
+use strict;
+
+sub do_command
+{
+ my ($conn, $event) = @_;
+ my $args = $event->{args}->[0];
+ my $from = $event->{from};
+ my $cmd = $args;
+ my $d1;
+ my $nick = lc $event->{nick};
+ foreach my $command ( @{$::commands->{command}} )
+ {
+ if (defined($command->{flag})) {
+ next unless defined($::xusers->{$nick});
+ next unless defined($::xusers->{$nick}->{flags});
+ next unless defined(grep {$_ eq $command->{flag}} split('', $::xusers->{$nick}->{flags}));
+ if ($::xusers->{$nick}->{host} ne 'IDENTIFY') {
+ next unless leq($::xusers->{$nick}->{host}, $event->{host});
+ }
+ else {
+ if ( $cmd =~ /$command->{cmd}/ ){
+ push (@{$::idqueue{$nick}}, [$cmd, $command, $event]);
+ $conn->sl("whois $nick $nick");
+ next;
+ }
+ }
+ }
+ if ($cmd=~/$command->{cmd}/) {
+ print "$event->{from} told me: $cmd \n";
+ eval $command->{content};
+ warn $@ if $@;
+ }
+ }
+}
+
+sub Command::killsub {
+ undef &do_command;
+}
+
+return 1;
diff --git a/modules/event.pl b/modules/event.pl
new file mode 100644
index 0000000..77583e4
--- /dev/null
+++ b/modules/event.pl
@@ -0,0 +1,264 @@
+use warnings;
+use strict;
+
+use Text::LevenshteinXS qw(distance);
+
+sub on_connect {
+ my ($conn, $event) = @_; # need to check for no services
+ $conn->privmsg( 'NickServ', "ghost $::settings->{nick} $::pass" ) if lc $event->{args}->[0] ne lc $::settings->{nick};
+}
+
+my @leven = ();
+
+sub on_join {
+ my ($conn, $event) = @_;
+ my $nick = lc $event->{nick};
+ my $chan = lc $event->{to}->[0];
+ if ( leq($conn->{_nick}, $nick) ) {
+ $::sc{$chan} = {};
+ $conn->privmsg('ChanServ', "op $chan" ) if (defined cs($chan)->{op}) && (cs($chan)->{op} eq 'yes');
+ }
+ $::sc{$chan}{users}{$nick} = {};
+ $::sc{$chan}{users}{$nick}{hostmask} = $event->{userhost};
+ $::sc{$chan}{users}{$nick}{op} = 0;
+ $::sc{$chan}{users}{$nick}{voice} = 0;
+ inspect( $conn, $event );
+ logg( $event );
+ if ( $#leven ne -1 ) {
+ my $ld = ( ( maxlen($nick, $leven[0]) - distance($nick, $leven[0]) ) / maxlen($nick, $leven[0]) );
+ my $mx = $leven[0];
+ foreach my $item ( @leven ) {
+ next if $nick eq $item; # avoid dups
+ my $tld = ( ( maxlen($nick, $item) - distance($nick, $item) ) / maxlen($nick, $item) );
+ if ($tld > $ld) {
+ $ld = $tld;
+ $mx = $item;
+ }
+ }
+ print "Best match for $nick was $mx with $ld\n"
+ }
+ push(@leven, $nick);
+ shift @leven if $#leven > 5;
+}
+
+sub on_part
+{
+ my ($conn, $event) = @_;
+ inspect( $conn, $event );
+ my $nick = lc $event->{nick};
+ logg( $event );
+ if ( leq( $conn->{_nick}, $nick ) )
+ {
+ delete( $::sc{lc $event->{to}->[0]} );
+ }
+ else
+ {
+ delete( $::sc{lc $event->{to}->[0]}{users}{$nick} );
+ }
+}
+
+sub on_msg
+{
+ my ($conn, $event) = @_;
+ do_command ($conn, $event)
+}
+
+sub on_public
+{
+ my ($conn, $event) = @_;
+ inspect( $conn, $event );
+ logg( $event );
+ do_command( $conn, $event )
+}
+
+sub on_notice
+{
+ my ($conn, $event) = @_;
+ inspect( $conn, $event );
+ logg( $event );
+ doServices($conn, $event);
+}
+
+sub on_errnickinuse
+{
+ my ($conn, $event) = @_;
+ $_ = ${$::settings->{altnicks}}[rand @{$::settings->{altnicks}}];
+ print "Nick is in use, trying $_\n";
+ $conn->nick($_);
+}
+
+sub on_quit
+{
+ my ($conn, $event) = @_;
+ my @channels=();
+ for ( keys %::sc ) {
+ push ( @channels, $_ ) if delete $::sc{lc $_}{users}{lc $event->{nick}};
+ }
+ $event->{to} = \@channels;
+ inspect( $conn, $event );
+ logg ( $event );
+}
+
+sub blah
+{
+ my ($self, $event) = @_;
+ 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;
+ }
+}
+
+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) = @_;
+ inspect($conn, $event) if ($event->{format} ne 'server');
+ if ($event->{format} eq 'server')
+ {
+ if ($event->{type} eq 'topic')
+ {
+ $::sc{lc $event->{args}->[1]}{topic}{text} = $event->{args}->[2];
+ }
+ elsif ($event->{type} eq 'topicinfo')
+ {
+ $::sc{lc $event->{args}->[1]}{topic}{time} = $event->{args}->[3];
+ $::sc{lc $event->{args}->[1]}{topic}{by} = $event->{args}->[2];
+ }
+ }
+ else
+ {
+ if ($event->{type} eq 'topic')
+ {
+ $::sc{lc $event->{to}->[0]}{topic}{text} = $event->{args}->[0];
+ }
+ logg($event);
+ }
+}
+
+sub on_nick {
+ my ($conn, $event) = @_;
+ my @channels=();
+ for ( keys %::sc )
+ {
+ if ( defined $::sc{lc $_}{users}{lc $event->{nick}} )
+ {
+ $::sc{lc $_}{users}{lc $event->{args}->[0]} = $::sc{lc $_}{users}{lc $event->{nick}};
+ delete( $::sc{lc $_}{users}{lc $event->{nick}} );
+ push ( @channels, lc $_ );
+ }
+ }
+ $event->{to} = \@channels;
+ inspect($conn, $event);
+ logg($event)
+}
+
+sub on_kick {
+ my ($conn, $event) = @_;
+ if (lc $event->{to}->[0] eq lc $::settings->{nick}) {
+ $conn->join($event->{args}->[0]);
+ }
+ logg( $event );
+}
+
+sub on_mode
+{
+ my ($conn, $event) = @_;
+ my $chan = lc $event->{to}->[0];
+ if ($chan =~ /^#/) {
+ my @modes = @{parse_modes($event->{args})};
+ foreach my $line ( @modes ) {
+ my @ex = @{$line};
+ if ( $ex[0] eq '+o' ) {
+ $::sc{$chan}{users}{lc $ex[1]}{op}=1;
+ if (lc $ex[1] eq lc $::settings->{nick}) {
+ doQueue($conn, $chan);
+ if ( $::channels->{channel}->{$chan}->{op} eq "when" ) {
+ $conn->schedule(600, sub { print "Deop timer called!\n"; $conn->privmsg('ChanServ', "op $chan -". $::settings->{nick})});
+ }
+ }
+ }
+ 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;
+ }
+ }
+ logg($event);
+ }
+}
+
+sub on_ctcp
+{
+ my ($conn, $event) = @_;
+ inspect($conn, $event);
+}
+
+sub whois_identified {
+ my ($conn, $event2) = @_;
+ my $who = lc $event2->{args}->[1];
+ if ( (defined( $::idqueue{$who} )) && ( @{$::idqueue{$who}} ) ) {
+ foreach my $item (@{$::idqueue{$who}}) {
+ my ($cmd, $command, $event) = @{$item};
+ if ( $cmd =~ /$command->{cmd}/ ){
+ print "$event->{from} told me $cmd \n";
+ eval $command->{content};
+ warn $@ if $@;
+ }
+ }
+ $::idqueue{$who} = [];
+ }
+}
+
+sub whois_end {
+ my ($conn, $event) = @_;
+ my $who = lc $event->{args}->[1];
+ $::idqueue{$who} = [];
+}
+
+sub on_bannedfromchan {
+ my ($conn, $event) = @_;
+ $conn->privmsg('ChanServ', "unban $event->{args}->[1]");
+}
+
+sub Event::killsub {
+ undef &on_connect;
+ undef &on_join;
+ undef &on_part;
+ undef &on_msg;
+ undef &on_notice;
+ undef &on_errnickinuse;
+ undef &on_quit;
+ undef &on_names;
+ undef &on_nick;
+ undef &on_kick;
+ undef &on_mode;
+ undef &on_ctcp;
+ undef &on_bannedfromchan;
+ undef &blah;
+ undef &irc_users;
+ undef &irc_topic;
+ undef &whois_identified;
+ undef &whois_end;
+ undef &on_public;
+}
+
+return 1;
diff --git a/modules/inspect.pl b/modules/inspect.pl
new file mode 100644
index 0000000..3837ba9
--- /dev/null
+++ b/modules/inspect.pl
@@ -0,0 +1,72 @@
+use warnings;
+use strict;
+
+use List::Util qw(first);
+
+#my @ignored = ();
+
+sub inspect {
+ our ($conn, $event) = @_;
+ my (%conx, %monx);
+ our (%aonx, %dct, $rev, $chan, $id);
+ %aonx=(); %dct=(); $rev=""; $chan=""; $id="";
+ my (@dnsbl, @unpakt, @uniq, @cut);
+ my ($match, $txtz, $iaddr);
+ my @override = [];
+ our $unmode='';
+ return if (defined(first { ( lc $event->{nick} eq lc $_ ) } @::eline));
+ return if (defined(first { ( lc $event->{user} eq lc $_ ) } @::eline));
+ return if (defined(first { ( lc $event->{host} eq lc $_ ) } @::eline));
+ $iaddr = hostip($event->{host});
+ $rev = join('.', reverse(unpack('C4', $iaddr))).'.' if (defined $iaddr);
+ %monx = defined($::channels->{channel}->{master}->{event}) ? %{$::channels->{channel}->{master}->{event}} : ();
+ ## NB: isn't there a better way to do this with grep, somehow?
+# foreach ( @ignored ) {
+# return if (lc $event->{nick} eq $_);
+# }
+ foreach $chan ( @{$event->{to}} ) {
+ next unless $chan =~ /^#/;
+ %conx = defined($::channels->{channel}->{lc $chan}->{event}) ? %{$::channels->{channel}->{lc $chan}->{event}} : ();
+ %aonx = (%monx, %conx);
+ foreach $id (keys %aonx) {
+ next unless ( defined(first { lc $_ eq $event->{type} } split(/[,:; ]+/, $aonx{$id}{type}) ) )
+ || ( lc $event->{type} eq lc $aonx{$id}{type} );
+# next unless ( defined($::classes->{class}->{$aonx{$id}{class}}));
+ eval "Classes::" . $aonx{$id}{class} . "();";
+ warn $@ if $@;
+ }
+ }
+ foreach ( keys %dct ) {
+ push( @override, split( /[ ,;]+/, $dct{$_}{override} ) ) if ( defined $dct{$_}{override} );
+ }
+ delete $dct{$_} foreach @override;
+ foreach $chan (@{$event->{to}}) {
+ foreach $id ( keys %dct ) {
+ $txtz = "$dct{$id}{risk} risk threat: ".
+ "Detected $event->{nick} $dct{$id}{reason} in $chan ";
+ $txtz = $txtz . commaAndify(getAlert(lc $chan, $dct{$id}{risk}, 'hilights')) if (getAlert(lc $chan, $dct{$id}{risk}, 'hilights'));
+ if (cs(lc $chan)->{op} ne 'no') {
+ if ($event->{type} eq 'topic') { #restore old topic
+ my $oldtopic = $::sc{lc $event->{to}->[0]}{topic}{text};
+ o_send( $conn, "topic $chan :$oldtopic");
+ o_send( $conn, "mode $chan +t");
+ }
+ eval "Actions::" . $dct{$id}{action} . "();";
+ warn $@ if $@;
+ my $lconn=$conn; my $lunmode = $unmode;
+ if ((int($dct{$id}{time}) ne 0) && ($unmode ne '')) {
+ $conn->schedule(int($dct{$id}{time}), sub { print "Timer called!\n"; o_send($lconn,$lunmode); });
+ }
+ }
+ $conn->privmsg($_, $txtz) foreach getAlert($chan, $dct{$id}{risk}, 'msgs');
+# push(@ignored, lc $event->{nick});
+# $conn->schedule(10, sub { @ignored = grep { $_ ne lc $event->{nick} } @ignored; });
+ }
+ }
+}
+
+sub Inspect::killsub {
+ undef &inspect;
+}
+
+return 1;
diff --git a/modules/log.pl b/modules/log.pl
new file mode 100644
index 0000000..6381110
--- /dev/null
+++ b/modules/log.pl
@@ -0,0 +1,38 @@
+use warnings;
+use strict;
+
+use String::Interpolate qw(interpolate);
+
+sub logg
+{
+ my ($event) = @_;
+ my @chans = @{$event->{to}};
+ my $fh;
+ @chans = ( $event->{args}->[0] ) if ($event->{type} eq 'kick');
+ my @time = ($::settings->{log}->{zone} eq 'local') ? localtime : gmtime;
+ foreach my $chan ( @chans)
+ {
+ $chan = lc $chan;
+ io(interpolate($::settings->{log}->{dir}))->mkpath;
+ $_='';
+ $_ = "<$event->{nick}> $event->{args}->[0]" if $event->{type} eq 'public';
+ $_ = "*** $event->{nick} has joined $chan" if $event->{type} eq 'join';
+ $_ = "*** $event->{nick} has left $chan" if $event->{type} eq 'part';
+ $_ = "* $event->{nick} $event->{args}->[0]" if $event->{type} eq 'caction';
+ $_ = "*** $event->{nick} is now known as $event->{args}->[0]" if $event->{type} eq 'nick';
+ $_ = "*** $event->{nick} has quit IRC" if $event->{type} eq 'quit';
+ $_ = "*** $event->{to}->[0] was kicked by $event->{nick}" if $event->{type} eq 'kick';
+ $_ = "-$event->{nick}- $event->{args}->[0]" if $event->{type} eq 'notice';
+ $_ = "*** $event->{nick} sets mode: ".join(" ",@{$event->{args}}) if $event->{type} eq 'mode';
+ $_ = "*** $event->{nick} changes topic to \"$event->{args}->[0]\"" if $event->{type} eq 'topic';
+ print Dumper($event) if ($_ eq '');
+ $_ = interpolate(strftime($::settings->{log}->{timefmt}, @time)) . $_ . "\n" unless $_ eq '';
+ $_ >> io(interpolate($::settings->{log}->{dir}).'/'.interpolate(strftime($::settings->{log}->{filefmt}, @time))) unless ($_ eq '');
+ }
+}
+
+sub Log::killsub {
+ undef &logg;
+}
+
+return 1;
diff --git a/modules/services.pl b/modules/services.pl
new file mode 100644
index 0000000..d110b60
--- /dev/null
+++ b/modules/services.pl
@@ -0,0 +1,44 @@
+use warnings;
+use strict;
+
+sub doServices {
+ my ($conn, $event) = @_;
+ if ($event->{from} eq 'NickServ!NickServ@services.')
+ {
+ print "NickServ: $event->{args}->[0]\n";
+ if ( $event->{args}->[0] eq 'This nickname is owned by someone else' )
+ {
+ $conn->privmsg( 'NickServ', "identify $::pass" );
+ }
+ elsif ( $event->{args}->[0] eq 'Password accepted - you are now recognized' )
+ {
+ $conn->join($_) foreach ( @{$::settings->{autojoins}} );
+ }
+ elsif ($event->{args}->[0] =~ /has been killed$/ )
+ {
+ $conn->nick( $::settings->{nick} );
+ }
+ elsif ($event->{args}->[0] =~ /Password Incorrect/ )
+ {
+ $conn->join($_) foreach ( @{$::settings->{autojoins}} );
+ }
+ }
+ elsif ($event->{from} eq 'ChanServ!ChanServ@services.')
+ {
+ print "ChanServ: $event->{args}->[0] \n";
+ if ($event->{args}->[0] =~ /You are already opped on \[.(.*).\]/)
+ {
+ doQueue($conn, $1);
+ }
+ elsif ( $event->{args}->[0] =~ /^All.*bans matching.*have been cleared on(.*)/)
+ {
+ $conn->join($1);
+ }
+ }
+}
+
+sub Services::killsub {
+ undef &doServices;
+}
+
+return 1;
diff --git a/modules/util.pl b/modules/util.pl
new file mode 100644
index 0000000..060eee0
--- /dev/null
+++ b/modules/util.pl
@@ -0,0 +1,165 @@
+#warning: if you add a function, put it into killsub!
+
+use warnings;
+use strict;
+
+my %sf;
+my %oq;
+
+my %RISKS =
+(
+ 'debug' => 10,
+ 'info' => 20,
+ 'low' => 30,
+ 'medium' => 40,
+ 'high' => 50,
+ 'opalert'=> 9001 #OVER NINE THOUSAND!!!
+);
+#leaves room for more levels if for some reason we end up needing more
+#theoretically, you should be able to change those numbers without any damage
+
+sub maxlen {
+ my ($a, $b) = @_;
+ my ($la, $lb) = (length($a), length($b));
+ return $la if ($la > $lb);
+ return $lb;
+}
+
+#cs: returns the xml settings for the specified chan, or default if there aren't any settings for that chan
+sub cs {
+ my ($chan) = @_;
+ $chan = lc $chan;
+ return $::channels->{channel}->{$chan} if ( defined($::channels->{channel}->{$chan}) );
+ return $::channels->{channel}->{default};
+}
+
+#this item is a stub, dur
+sub hostip {
+ return gethostbyname($_[0]);
+}
+
+# Send something that requires ops
+sub o_send {
+ my ( $conn, $send ) = @_;
+ my @splt = split(/ /, $send);
+ my $chan = lc $splt[1];
+ $oq{$chan} = [] unless defined($oq{$chan});
+ if ( cs($chan)->{op} ne 'no' ) {
+ print Dumper(lc $::settings->{nick}, $::sc{$chan}{users}{lc $::settings->{nick}});
+ print Dumper($send, $chan);
+ if ( $::sc{$chan}{users}{lc $::settings->{nick}}{op} eq 1) {
+ $conn->sl($send);
+ }
+ else {
+ push( @{$oq{$chan}},$send );
+ $conn->privmsg( 'chanserv', "op $chan" );
+ }
+ }
+}
+
+sub doQueue {
+ my ( $conn, $chan ) = @_;
+ return unless defined $oq{$chan};
+ $conn->sl(shift(@{$oq{$chan}})) while (@{$oq{$chan}});
+}
+
+sub flood_add {
+ my ( $chan, $id, $host, $to ) = @_;
+ push( @{$sf{$id}{$chan}{$host}}, time );
+ while ( time >= $sf{$id}{$chan}{$host}[0] + $to ) {
+ last if ( $#{ $sf{$id}{$chan}{$host} } == 0 );
+ shift( @{$sf{$id}{$chan}{$host}} );
+ }
+ return $#{ @{$sf{$id}{$chan}{$host}}}+1;
+}
+
+sub flood_process {
+ for my $id ( keys %sf ) {
+ for my $chan ( keys %{$sf{$id}} ) {
+ for my $host ( keys %{$sf{$id}{$chan}} ) {
+ next unless defined $sf{$id}{$chan}{$host}[0];
+ while ( time >= $sf{$id}{$chan}{$host}[0] + $sf{$id}{'timeout'} ) {
+ last if ( $#{ $sf{$id}{$chan}{$host} } == 0 );
+ shift ( @{$sf{$id}{$chan}{$host}} );
+ }
+ }
+ }
+ }
+}
+
+sub getAlert {
+ my ($c, $risk, $t) = @_;
+ @_ = ();
+ $c = lc $c;
+ foreach my $prisk ( keys %RISKS) {
+ if ( $RISKS{$risk} >= $RISKS{$prisk} ) {
+ push( @_, @{$::channels->{channel}->{master}->{$t}->{$prisk}} ) if defined $::channels->{channel}->{master}->{$t}->{$prisk};
+ push( @_, @{cs($c)->{$t}->{$prisk}} ) if defined cs($c)->{$t}->{$prisk};
+ }
+ }
+ return @_;
+}
+
+sub commaAndify {
+ my @seq = @_;
+ my $len = ($#seq);
+ my $last = $seq[$len];
+ return '' if $len eq -1;
+ return $seq[0] if $len eq 0;
+ return join( ' and ', $seq[0], $seq[1] ) if $len eq 1;
+ return join( ', ', splice(@seq,0,$len) ) . ', and ' . $last;
+}
+
+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 {
+ if ( defined( grep( /[abdefhIJkloqv]/,($c) ) ) ) { #modes that take args
+ push (@new_modes, [$t.$c, shift @args]);
+ }
+ elsif ( defined( grep( /[cgijLmnpPQrRstz]/, ($c) ) ) ) {
+ push (@new_modes, [$t.$c]);
+ }
+ else {
+ die "Unknown mode $c !\n";
+ }
+ }
+ }
+ return \@new_modes;
+}
+
+sub leq {
+ my ($s1, $s2) = @_;
+ return (lc $s1 eq lc $s2);
+}
+
+sub seq {
+ my ($n1, $n2) = @_;
+ return 0 unless defined($n1);
+ return 0 unless defined($n2);
+ return ($n1 eq $n2);
+}
+
+sub Util::killsub {
+ undef &cs;
+ undef &hostip;
+ undef &o_send;
+ undef &doQueue;
+ undef &flood_add;
+ undef &flood_process;
+ undef &getAlert;
+ undef &commaAndify;
+ undef &parse_modes;
+ undef &leq;
+ undef &seq;
+}
+
+return 1;
diff --git a/modules/xml.pl b/modules/xml.pl
new file mode 100644
index 0000000..2cb2505
--- /dev/null
+++ b/modules/xml.pl
@@ -0,0 +1,39 @@
+use warnings;
+use strict;
+
+use XML::Simple qw(:strict);
+
+my $xs1 = XML::Simple->new( KeyAttr => ['id'], Cache => [ qw/storable memcopy/ ]);
+
+sub readXML {
+ my ( $p ) = $::cset; #@_;
+ $p = 'default' if $p eq '';
+ $p = "config-$p";
+ $::settings = $xs1->XMLin( "$p/settings.xml", ForceArray => [qw/host/],
+ GroupTags => { altnicks => 'altnick', server => 'host', autojoins=> 'autojoin' });
+ $::channels = $xs1->XMLin( "$p/channels.xml", ForceArray => [qw/event debug info low medium high/] );
+ $::users = $xs1->XMLin( "$p/users.xml", ForceArray => 'person' );
+ $::xusers = $::users->{person};
+ $::commands = $xs1->XMLin( "$p/commands.xml", ForceArray => [qw/command/]);
+}
+
+sub writeXML {
+ my ( $p ) = @_;
+ $p = 'default' if $p eq '';
+ $p = "config-$p";
+ $xs1->XMLout($::settings, RootName => 'settings', KeyAttr => ['id'],
+ GroupTags => { altnicks => 'altnick', server => 'host', autojoins => 'autojoin' },
+ ValueAttr => { debug => 'content', nick => 'content', port => 'content',
+ realname => 'content', username => 'content', dir => 'content',
+ zone => 'content', filefmt => 'content', timefmt => 'content'}) > io("$p/settings.xml");
+ $xs1->XMLout($::channels, RootName => 'channels', KeyAttr => ['id']) > io("$p/channels.xml");
+ $xs1->XMLout($::users, RootName => 'people', KeyAttr => ['id']) > io("$p/users.xml");
+ $xs1->XMLout($::commands, RootName => 'commands', KeyAttr => ['id']) > io("$p/commands.xml");
+}
+
+sub Xml::killsub {
+ undef &readXML;
+ undef &writeXML;
+}
+
+return 1;