#!/usr/bin/perl use strict; no strict 'refs'; use warnings; use IO::Socket; use IO::Select; use OpenBSD::Pledge; use OpenBSD::Unveil; my $confpath = "botnow.conf"; our %conf; foreach my $line (readarray($confpath)) { if ($line =~ /^#/ or $line =~ /^\s*$/) { # skip comments and whitespace next; } elsif ($line =~ /^([-_a-zA-Z0-9]+)\s*=\s*([[:print:]]+)$/) { $conf{$1} = $2; } else { die "ERROR: botnow.conf format invalid: $line"; } } # Name of local network $conf{localnet} = $conf{localnet} || "ircnow"; # Internal IPv4 address and plaintext port $conf{host} = $conf{host} || "127.0.0.1"; $conf{port} = $conf{port} || 1337; # Bouncer hostname chomp($conf{hostname} = $conf{hostname} || `hostname`); # External IPv4 address, plaintext and ssl port $conf{ip4} = $conf{ip4} or die "ERROR: botnow.conf: ip4"; $conf{ip6} = $conf{ip6} or die "ERROR: botnow.conf: ip6"; $conf{ip6subnet} = $conf{ip6subnet} or die "ERROR: botnow.conf: ip6subnet"; $conf{plainport} = $conf{plainport} || 1337; $conf{sslport} = $conf{sslport} || 31337; # Nick and password of bot -- Make sure to add to oper block $conf{nick} = $conf{nick} || "botnow"; $conf{pass} = $conf{pass} or die "ERROR: botnow.conf: pass"; # Comma-separated list of channels for requesting bouncers $conf{chans} = $conf{chans} || "#ircnow"; #Join chans on localnet? $conf{localchans} = defined($conf{localchans}) && ($conf{localchans} =~ /^true/i); # Number of words in password $conf{passlength} = $conf{passlength} || 3; # Mail from address if (!defined($conf{mailname})) { if ($conf{mailfrom} =~ /^([^@]+)@/) { $conf{mailname} = $1 or die "ERROR: botnow.conf mailname"; } } # ZNC install directory $conf{zncdir} = $conf{zncdir} || "/home/znc/home/znc"; # NSD zone dir $conf{zonedir} = $conf{zonedir} || "/var/nsd/zones/master/"; # Network Interface Config File $conf{hostnameif} = $conf{hostnameif} || "/etc/hostname.vio0"; # Verbosity: 0 (no errors), 1 (errors), 2 (warnings), 3 (diagnostic) use constant { NONE => 0, ERRORS => 1, WARNINGS => 2, ALL => 3, }; $conf{verbose} = $conf{verbose} || ERRORS; # Terms of Service; don't edit lines with the word EOF $conf{terms} = $conf{terms} || "IRCNow: Of the User, By the User, For the User. Rules: no porn, no illegal drugs, no gambling, no slander, no warez, no promoting violence, no spam, illegal cracking, or DDoS. Only one account per person. Don't share passwords. Full terms: https://ircnow.org/terms.php"; $conf{ipv6path} = "ipv6s"; # ipv6 file path $conf{netpath} = "networks"; # networks file path $conf{expires} = $conf{expires} || 1800; # time before captcha expires if(defined($conf{die})) { die $conf{die}; } my @modules; if (defined($conf{modules})) { @modules = split(/\s+/, $conf{modules}); } use lib './'; foreach my $mod (@modules) { require "$mod.pm"; } foreach my $mod (@modules) { my $init = "${mod}::init"; $init->(); } our @networks; my @bots; my @months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); my @days = qw(Sun Mon Tue Wed Thu Fri Sat Sun); my @chans = split /[,\s]+/m, $conf{chans}; my @teamchans; if (defined($conf{teamchans})) { @teamchans = split /[,\s]+/m, $conf{teamchans}; } my $call; my $botnick = $conf{nick}; my $host = $conf{host}; my $port = $conf{port}; my $pass = $conf{pass}; my $localnet = $conf{localnet}; my $staff = $conf{staff}; my @stafflist = split(/ /,$staff); my $verbose = $conf{verbose}; my $ipv6path = $conf{ipv6path}; my $netpath = $conf{netpath}; my $expires = $conf{expires}; my $localchans = $conf{localchans}; unveil("./", "r") or die "Unable to unveil $!"; unveil("$confpath", "r") or die "Unable to unveil $!"; unveil("$netpath", "r") or die "Unable to unveil $!"; unveil("$ipv6path", "rwc") or die "Unable to unveil $!"; unveil() or die "Unable to lock unveil $!"; #dns and inet for sockets, proc and exec for figlet #rpath for reading file, wpath for writing file, cpath for creating path #flock, fattr for sqlite pledge( qw(stdio rpath wpath cpath inet dns proc exec flock fattr) ) or die "Unable to pledge: $!"; # Read from filename and return array of lines without trailing newlines sub readarray { my ($filename) = @_; open(my $fh, '<', $filename) or die "Could not read file '$filename' $!"; chomp(my @lines = <$fh>); close $fh; return @lines; } # Read from filename and return as string sub readstr { my ($filename) = @_; open my $fh, '<', $filename or die "Could not read file '$filename' $!"; my $str = do { local $/; <$fh> }; close $fh; return $str; } # Write str to filename sub writefile { my ($filename, $str) = @_; open(my $fh, '>', "$filename") or die "Could not write to $filename"; print $fh $str; close $fh; } # Append str to filename sub appendfile { my ($filename, $str) = @_; open(my $fh, '>>', "$filename") or die "Could not append to $filename"; print $fh $str; close $fh; } # Return list of networks from filename # To add multiple servers for a single network, simply create a new entry with # the same net name; znc ignores addnetwork commands when a network already exists sub readnetworks { my ($filename) = @_; my @lines = readarray($filename); my @networks; foreach my $line (@lines) { if ($line =~ /^#/ or $line =~ /^\s*$/) { # skip comments and whitespace next; } elsif ($line =~ /^\s*([-a-zA-Z0-9]+)\s*([-_.:a-zA-Z0-9]+)\s*(~|\+)?([0-9]+)\s*$/) { my ($name, $server, $port) = ($1, $2, $4); my $trustcerts; if (!defined($3)) { $trustcerts = 0; } elsif ($3 eq "~") { # Use SSL but trust all certs $port = "+".$port; $trustcerts = 1; } else { # Use SSL and verify certs $port = "+".$port; $trustcerts = 0; } push(@networks, {"name" => $name, "server" => $server, "port" => $port, "trustcerts" => $trustcerts }); } else { die "network format invalid: $line\n"; } } return @networks; } @networks = readnetworks($netpath); # networks must be sorted to avoid multiple connections @networks = sort @networks; # create sockets my $sel = IO::Select->new( ); my $lastnet = ""; foreach my $network (@networks) { # avoid duplicate connections if ($lastnet eq $network->{name}) { next; } $lastnet = $network->{name}; my $socket = IO::Socket::INET->new(PeerAddr=>$host, PeerPort=>$port, Proto=>'tcp', Timeout=>'300') || print "Failed to establish connection\n"; $sel->add($socket); my $bot = {("sock" => $socket), %$network}; push(@bots, $bot); putserv($bot, "NICK $botnick"); putserv($bot, "USER $botnick * * :$botnick"); } while(my @ready = $sel->can_read) { my ($bot, $response); my ($sender, $val); foreach my $socket (@ready) { foreach my $b (@bots) { if($socket == $b->{sock}) { $bot = $b; last; } } if (!defined($response = <$socket>)) { debug(ERRORS, "ERROR ".$bot->{name}." has no response:"); next; } if ($response =~ /^PING :(.*)\r\n$/i) { putserv($bot, "PONG :$1"); } elsif ($response =~ /^:irc.znc.in (.*) (.*) :(.*)\r\n$/) { my ($type, $target, $text) = ($1, $2, $3); if ($type eq "001" && $target =~ /^$botnick.?$/ && $text eq "Welcome to ZNC") { } elsif ($type eq "NOTICE" && $target =~ /^$botnick.?$/ && $text eq "*** To connect now, you can use /quote PASS :, or /quote PASS /: to connect to a specific network.") { } elsif ($type eq "NOTICE" && $target =~ /^$botnick.?$/ && $text eq "*** You need to send your password. Configure your client to send a server password.") { } elsif ($type eq "464" && $target =~ /^$botnick.?$/ && $text eq "Password required") { putserv($bot, "PASS $botnick/$bot->{name}:$pass"); if ($bot->{name} =~ /^$localnet$/i) { putserv($bot, "OPER $botnick $pass"); putserv($bot, "PRIVMSG *status :LoadMod --type=user controlpanel"); putserv($bot, "PRIVMSG *controlpanel :get Admin $botnick"); putserv($bot, "PRIVMSG *controlpanel :get Nick cloneuser"); foreach my $chan (@teamchans) { putserv($bot, "JOIN $chan"); } } if ($bot->{name} !~ /^$localnet$/i or $localchans) { foreach my $chan (@chans) { putserv($bot, "JOIN $chan"); } } } elsif ($type eq "464" && $target =~ /^$botnick.?$/ && $text eq "Invalid Password") { die "ERROR: Wrong Username/Password: $bot->{name}"; } else { debug(ERRORS, "Unexpected bncnow.pl 257: type: $type, target: $target, text: $text"); } } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) PRIVMSG ([^ ]+) :(.*)\r\n$/i) { my ($hostmask, $nick, $host, $target, $text) = ($1, $2, $3, $4, $5); if ($hostmask eq '*status!znc@znc.in' && $target =~ /^$botnick.?$/) { if ($text =~ /Network ([[:ascii:]]+) doesn't exist./) { debug(ERRORS, "nonexistent: $1"); } elsif ($text eq "You are currently disconnected from IRC. Use 'connect' to reconnect.") { debug(ERRORS, "disconnected: $bot->{name}"); } elsif ($text =~ /Unable to load module (.*): Module (.*) already loaded./) { debug(ALL, "Module $1 already loaded\n"); } elsif ($text =~ /^Disconnected from IRC.*$/) { debug(ERRORS, "$bot->{name}: $text"); } elsif ($text =~ /^|/) { debug(ERRORS, "$bot->{name}: $text"); } else { debug(ERRORS, "Unexpected bncnow.pl 273: $response"); } } elsif ($text =~ /^!([[:graph:]]+)\s*(.*)/) { my ($cmd, $text) = ($1, $2); my $hand = $staff; # TODO fix later if ($target =~ /^#/) { foreach my $c (@{$call->{pub}}) { if ($cmd eq $c->{cmd}) { my $proc = $c->{proc}; $proc->($bot, $nick, $host, $hand, $target, $text); } } } else { foreach my $c (@{$call->{msg}}) { if ($cmd eq $c->{cmd}) { my $proc = $c->{proc}; $proc->($bot, $nick, $host, $hand, $text); } } } } else { my $hand = $staff; # TODO fix later if ($target =~ /^#/) { foreach my $c (@{$call->{pubm}}) { my $proc = $c->{proc}; $proc->($bot, $nick, $host, $hand, $target, $text); } } else { foreach my $c (@{$call->{msgm}}) { my $proc = $c->{proc}; $proc->($bot, $nick, $host, $hand, $text); } } } debug(ALL, "$hostmask $target $text"); } elsif($response =~ /^:([^ ]+) NOTICE ([^ ]+) :(.*)\r\n$/i) { my ($hostmask, $target, $text) = ($1, $2, $3); if ($hostmask =~ /([^!]+)!([^@]+@[^@ ]+)/) { my ($nick, $host) = ($1, $2); my $hand = $staff; # TODO fix later foreach my $c (@{$call->{notc}}) { # if ($text eq $c->{mask}) { # TODO fix later my $proc = $c->{proc}; $proc->($bot, $nick, $host, $hand, $text, $target); # } } # TODO use CTCR # CTCP replies if ($hostmask ne '*status!znc@znc.in') { if ($text =~ /^(PING|VERSION|TIME|USERINFO) (.*)$/i) { my ($key, $val) = ($1, $2); my $id = SQLite::id("irc", "nick", $nick, $expires); SQLite::set("irc", "id", $id, "ctcp".lc($key), $val); SQLite::set("irc", "id", $id, "localtime", time()); } } } debug(ALL, "$hostmask NOTICE $target $text"); #:portlane.se.quakenet.org NOTICE guava :Highest connection count: 1541 (1540 clients) #:portlane.se.quakenet.org NOTICE guava :on 2 ca 2(4) ft 20(20) tr } elsif($response =~ /^:([^ ]+) MODE ([^ ]+) ([^ ]+)\s*(.*)\r\n$/i) { my ($hostmask, $chan, $change, $targets) = ($1, $2, $3, $4); if ($hostmask =~ /([^!]+)!([^@]+@[^@ ]+)/) { my ($nick, $host) = ($1, $2); my $hand = $staff; # TODO fix later foreach my $c (@{$call->{mode}}) { # TODO filter by mask my $proc = $c->{proc}; $proc->($bot, $nick, $host, $hand, $chan, $change, $targets); } } debug(ALL, "$hostmask MODE $chan $change $targets"); #:guava!guava@guava.guava.ircnow.org MODE guava :+Ci #:ChanServ!services@services.irc.ircnow.org MODE #testing +q jrmu #:jrmu!jrmu@jrmu.staff.ircnow.org MODE #testing +o jrmu #Unexpected bncnow.pl 460: :irc.guava.ircnow.org MODE guava :+o } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) JOIN :?(.*)\r\n$/i) { my ($hostmask, $nick, $host, $chan) = ($1, $2, $3, $4); my $hand = $staff; # TODO fix later foreach my $c (@{$call->{join}}) { my $proc = $c->{proc}; $proc->($bot, $nick, $host, $hand, $chan); } debug(ALL, "$hostmask JOIN $chan"); #:jrmu!jrmu@jrmu.staff.ircnow.org JOIN :#testing } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) PART ([^ ]+) :(.*)\r\n$/i) { my ($hostmask, $nick, $host, $chan, $text) = ($1, $2, $3, $4, $5); my $hand = $staff; # TODO fix later foreach my $c (@{$call->{part}}) { # if ($text eq $c->{mask}) { # TODO fix later my $proc = $c->{proc}; $proc->($bot, $nick, $host, $hand, $chan, $text); # } } debug(ALL, "$hostmask PART $chan :$text"); #:jrmu!jrmu@jrmu.staff.ircnow.org PART #testing : } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) KICK (#[^ ]+) ([^ ]+) :(.*)\r\n$/i) { my ($hostmask, $nick, $host, $chan, $kicked, $text) = ($1, $2, $3, $4, $5, $6); my $hand = $staff; # TODO fix later foreach my $c (@{$call->{kick}}) { # if ($text eq $c->{mask}) { # TODO fix later my $proc = $c->{proc}; $proc->($bot, $nick, $host, $hand, $chan, $text); # } } debug(ALL, "$hostmask KICK $chan $kicked :$text"); #jrmu!jrmu@jrmu.users.undernet.org KICK #ircnow guava :this is a test } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) NICK :?(.*)\r\n$/i) { my ($hostmask, $nick, $host, $text) = ($1, $2, $3, $4); debug(ALL, "$hostmask NICK $text"); #:Fly0nDaWaLL|dal!psybnc@do.not.h4ck.me NICK :nec|dal } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) QUIT :(.*)\r\n$/i) { my ($hostmask, $nick, $host, $text) = ($1, $2, $3, $4); debug(ALL, "$hostmask QUIT :$text"); #:Testah!~sid268081@aa38a510 QUIT :Client closed connection } elsif($response =~ /^NOTICE AUTH :(.*)\r\n$/i) { my ($text) = ($1); debug(ALL, "NOTICE AUTH: $text"); #NOTICE AUTH :*** Looking up your hostname #NOTICE AUTH: *** Looking up your hostname #NOTICE AUTH: *** Checking Ident #NOTICE AUTH: *** Got ident response #NOTICE AUTH: *** Found your hostname } elsif ($response =~ /^:([[:graph:]]+) (\d\d\d) $botnick.? :?(.*)\r?\n?\r$/i) { my ($server, $code, $text) = ($1, $2, $3); if ($code =~ /^001$/) { # Server Info debug(ERRORS, "connected: $bot->{name}"); } elsif ($code =~ /^0\d\d$/) { # Server Info debug(ALL, "$server $code $text"); } elsif ($code =~ /^2\d\d$/) { # Server Stats debug(ALL, "$server $code $text"); } elsif ($code == 301 && $text =~ /^([-_\|`a-zA-Z0-9]+) :([[:graph:]]+)/) { debug(ALL, "$text"); } elsif ($code == 307 && $text =~ /^([-_\|`a-zA-Z0-9]+) (.*)/) { my ($sender, $key) = ($1, "registered"); $val = $2 eq ":is a registered nick" ? "True" : "$2"; my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, "identified", $val); debug(ALL, "$key: $val"); } elsif ($code == 311 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([^:]+)\s+([^:]+) \* :([^:]*)/) { my ($sender, $key, $val) = ($1, "hostmask", "$1\!$2\@$3"); my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, $key, $val); debug(ALL, "$key: $val"); } elsif ($code == 312 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([^:]+) :([^:]+)/) { my ($sender, $key, $val) = ($1, "server", $2); my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, $key, $val); debug(ALL, "$key: $val"); } elsif ($code == 313 && $text =~ /^([-_\|`a-zA-Z0-9]+) :?(.*)/) { my ($sender, $key, $val) = ($1, "oper", ($2 eq "is an IRC operator" ? "True" : "$2")); my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, $key, $val); debug(ALL, "$key: $val"); } elsif ($code == 315 && $text =~ /^([-_\|`a-zA-Z0-9]+) :End of \/?WHO(IS)? list/) { debug(ALL, "End of WHOIS"); } elsif ($code == 317 && $text =~ /^([-_\|`a-zA-Z0-9]+) (\d+) (\d+) :?(.*)/) { ($sender, my $idle, my $epochtime) = ($1, $2, $3); my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, "idle", $idle); # SQLite::set("irc", "id", $id, "epochtime", time()); debug(ALL, "idle: $idle, epochtime: $epochtime"); } elsif ($code == 318 && $text =~ /^([-_\|`a-zA-Z0-9]+) :End of \/?WHOIS list/) { debug(ALL, "End of WHOIS"); } elsif ($code == 319 && $text =~ /^([-_\|`a-zA-Z0-9]+) :(.*)/) { my ($sender, $key, $val) = ($1, "chans", $2); my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, $key, $val); debug(ALL, "$key: $val"); } elsif ($code == 330 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([-_\|`a-zA-Z0-9]+) :?(.*)/) { my ($sender, $key, $val) = ($1, "identified", ($3 eq "is logged in as" ? "True" : $2)); my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, $key, $val); debug(ALL, "$key: $val"); } elsif ($code == 338 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([0-9a-fA-F:.]+) :actually using host/) { my ($sender, $key, $val) = ($1, "ip", $2); my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, $key, $val); debug(ALL, "$key: $val"); #Unexpected: efnet.port80.se 338 jrmu 206.253.167.44 :actually using host } elsif ($code == 378 && $text =~ /^([-_\|`a-zA-Z0-9]+) :is connecting from ([^ ]+)\s*([0-9a-fA-F:.]+)?/) { my ($sender, $key, $val) = ($1, "ip", $3); my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, $key, $val); debug(ALL, "$key: $val"); } elsif ($code == 671 && $text =~ /^([-_\|`a-zA-Z0-9]+) :is using a secure connection/) { my ($sender, $key, $val) = ($1, "ssl", "True"); my $id = SQLite::id("irc", "nick", $sender, $expires); SQLite::set("irc", "id", $id, $key, $val); debug(ALL, "$key: $val"); } elsif ($code =~ /^332$/) { # Topic # print "$text\r\n"; } elsif ($code =~ /^333$/) { # # print "$server $text\r\n"; #karatkievich.freenode.net 333 #ircnow jrmu!znc@206.253.167.44 1579277253 } elsif ($code =~ /^352$/) { # Hostmask #:datapacket.hk.quakenet.org 352 * znc guava.guava.ircnow.org *.quakenet.org guava H :0 guava # print "$server $code $text\r\n"; } elsif ($code =~ /^353$/) { # Names # print "$server $code $text\r\n"; } elsif ($code =~ /^366$/) { # End of names # print "$server $code $text\r\n"; } elsif ($code =~ /^37\d$/) { # MOTD # print "$server $code $text\r\n"; } elsif ($code =~ /^381$/) { # IRC Operator Verified # print "IRC Oper Verified\r\n"; } elsif ($code =~ /^401$/) { # IRC Operator Verified # print "IRC Oper Verified\r\n"; } elsif ($code =~ /^403$/) { # No such channel # debug(ERRORS, "$text"); } elsif ($code =~ /^422$/) { # MOTD missing # print "$server $code $text\r\n"; } elsif ($code =~ /^396$/) { # Display hostname # print "$server $code $text\r\n"; #Unexpected bncnow.pl 454: irc.guava.ircnow.org 396 guava.guava.ircnow.org :is your displayed hostname now } elsif ($code =~ /^464$/) { # Invalid password for oper foreach my $chan (@teamchans) { putserv($bot, "PRIVMSG $chan :$botnick oper password failed; the bot will be unable to view uncloaked IP addresses"); } } elsif ($code =~ /^477$/) { # Can't join channel foreach my $chan (@teamchans) { putserv($bot, "PRIVMSG $chan :ERROR: $botnick on $server: $text"); } } elsif ($code == 716 && $text =~ /^([-_\|`a-zA-Z0-9]+) :is in \+g mode \(server-side ignore.\)/) { debug(ALL, "$text"); } else { debug(ERRORS, "Unexpected bncnow.pl 454: $server $code $text"); } } else { debug(ERRORS, "Unexpected bncnow.pl 460: $response"); } } } sub putserv { my( $bot, $text )=@_; my $socket = $bot->{sock}; if ($text =~ /^([^:]+):([[:ascii:]]*)$/m) { my ($cmd, $line) = ($1, $2); my @lines = split /\r?\n/m, $line; foreach my $l (@lines) { print $socket "$cmd:$l\r\n"; } } else { print $socket "$text\r\n"; } } sub putserv { my( $bot, $text )=@_; my $socket = $bot->{sock}; if ($text =~ /^([^:]+):([[:ascii:]]*)$/m) { my ($cmd, $line) = ($1, $2); my @lines = split /\r?\n/m, $line; foreach my $l (@lines) { print $socket "$cmd:$l\r\n"; } } else { print $socket "$text\r\n"; } } sub putservlocalnet { my( $bot, $text )=@_; my $botlocalnet; foreach my $b (@bots) { if($b->{name} =~ /^$localnet$/i) { $botlocalnet = $b; last; } } putserv($botlocalnet, $text); } sub date { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); my $localtime = sprintf("%04d%02d%02d", $year+1900, $mon+1, $mday); return $localtime; } sub gettime { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); my $localtime = sprintf("%s %s %d %02d:%02d:%02d", $days[$wday], $months[$mon], $mday, $hour, $min, $sec); return $localtime; } sub whois { my( $socket, $target )=@_; print $socket "WHOIS $target $target\r\n"; } sub ctcp { my( $socket, $target )=@_; # print $socket "PRIVMSG $target :".chr(01)."CLIENTINFO".chr(01)."\r\n"; # print $socket "PRIVMSG $target :".chr(01)."FINGER".chr(01)."\r\n"; # print $socket "PRIVMSG $target :".chr(01)."SOURCE".chr(01)."\r\n"; print $socket "PRIVMSG $target :".chr(01)."TIME".chr(01)."\r\n"; # print $socket "PRIVMSG $target :".chr(01)."USERINFO".chr(01)."\r\n"; print $socket "PRIVMSG $target :".chr(01)."VERSION".chr(01)."\r\n"; # print $socket "PRIVMSG $target :".chr(01)."PING".chr(01)."\r\n"; } sub cbind { my ($type, $flags, $cmd, $proc) = @_; if ($type eq "pub") { push(@{$call->{pub}}, {cmd => $cmd, proc => $proc}); } elsif ($type eq "msg") { push(@{$call->{msg}}, {cmd => $cmd, proc => $proc}); } elsif ($type eq "notc") { push(@{$call->{notc}}, {mask => $cmd, proc => $proc}); } elsif ($type eq "mode") { push(@{$call->{mode}}, {mask => $cmd, proc => $proc}); } elsif ($type eq "join") { push(@{$call->{join}}, {mask => $cmd, proc => $proc}); } elsif ($type eq "partcall") { push(@{$call->{part}}, {mask => $cmd, proc => $proc}); } elsif ($type eq "pubm") { push(@{$call->{pubm}}, {mask => $cmd, proc => $proc}); } elsif ($type eq "msgm") { push(@{$call->{msgm}}, {mask => $cmd, proc => $proc}); } } sub debug { my ($level, $msg) = @_; if ($verbose >= $level) { print "$msg\n"; } } sub isstaff { my( $bot, $nick ) = @_; if( !( $bot->{name} =~ /^$localnet$/i ) ) { return 0; } my $lnick = lc $nick; foreach( @stafflist ) { if( $lnick eq $_ ) { return 1; } } return 0; }