commit 6be16d90c2213d61bf83fc1306d62ea0917d5845 from: Izzy Blacklock date: Thu Aug 03 21:43:26 2023 UTC Moved main IRC socket processing and response loop from botnow commit - 1eb65f132eae3f020aec86b81dd5dd762c771e60 commit + 6be16d90c2213d61bf83fc1306d62ea0917d5845 blob - e3f95c126844123d2a48346fcaf8d02cd15b0186 blob + 37959c6e810c6e626b717ec160ca5eac51091798 --- lib/IRCNOW/IO/IRC.pm +++ lib/IRCNOW/IO/IRC.pm @@ -196,5 +196,314 @@ sub isstaff { } return 0; } + +sub parse_response { + my ($self, $bot, $response) = @_; + my $staff=$self->{staff}; + my $nick=$self->{nick}; + my $pass=$self->{pass}; + my $expires=$self->{expires}; + my $chans=$self->{chans}; + my $teamchans=$self->{teamchans}; + + if ($response =~ /^PING :(.*)\r\n$/i) { + $self->putserv($bot, "PONG :$1"); + } elsif ($response =~ /^:irc.znc.in (.*) (.*) :(.*)\r\n$/) { + my ($type, $target, $text) = ($1, $2, $3); + if ($type eq "464" && $target =~ /^$nick.?$/ && $text eq "Password required") { + $self->putserv($bot, "PASS $nick/$bot->{name}:$pass"); + if ($bot->{name} =~ /^$localnet$/i) { + $self->putserv($bot, "OPER $nick $pass"); + $self->putserv($bot, "PRIVMSG *status :LoadMod --type=user controlpanel"); + $self->putserv($bot, "PRIVMSG *controlpanel :get Admin $nick"); + $self->putserv($bot, "PRIVMSG *controlpanel :get Nick cloneuser"); + foreach my $chan (@$teamchans) { + $self->putserv($bot, "JOIN $chan"); + } + } + if ($bot->{name} !~ /^$localnet$/i) { + foreach my $chan (@$chans) { + $self->putserv($bot, "JOIN $chan"); + } + } + } elsif ($type eq "464" && $target =~ /^$nick.?$/ && $text eq "Invalid Password") { + die "ERROR: Wrong Username/Password: $bot->{name}"; + } else { + debug(ALL, "Debug type: $type, target: $target, text: $text"); + } + } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) PRIVMSG ([^ ]+) :(.*)\r\n$/i) { + chomp($response); + debug(ALL, "-->COMMAND--> $response"); + my ($hostmask, $sendnick, $host, $target, $text) = ($1, $2, $3, $4, $5); + if ($hostmask eq '*status!znc@znc.in' && $target =~ /^$nick.?$/) { + if ($text =~ /Network ([[:ascii:]]+) doesn't exist./) { + debug(ERRORS, "ERROR: nonexistent: $1"); + } elsif ($text eq "You are currently disconnected from IRC. Use 'connect' to reconnect.") { + debug(ERRORS, "ERROR: 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, "ERROR: $bot->{name}: $text"); + } elsif ($text =~ /^|/) { + debug(ERRORS, "ERROR: $bot->{name}: $text"); + } else { + debug(ERRORS, "ERROR: Unexpected: $response"); + } + } elsif ($text =~ /^!([[:graph:]]+)\s*(.*)/) { + my ($cmd, $text) = ($1, $2); + debug(ALL, qq{<>}); + my $hand = $staff; # TODO fix later + if ($target =~ /^#/) { + foreach my $c (@{$call->{pub}}) { + if ($cmd eq $c->{cmd}) { + my $proc = $c->{proc}; + $proc->($bot, $sendnick, $host, $hand, $target, $text); + } + } + } else { + foreach my $c (@{$call->{msg}}) { + if ($cmd eq $c->{cmd}) { + my $proc = $c->{proc}; + $proc->($bot, $sendnick, $host, $hand, $text); + } + } + } + } else { + my $hand = $staff; # TODO fix later + if ($target =~ /^#/) { + foreach my $c (@{$call->{pubm}}) { + my $proc = $c->{proc}; + $proc->($bot, $sendnick, $host, $hand, $target, $text); + } + } else { + foreach my $c (@{$call->{msgm}}) { + my $proc = $c->{proc}; + $proc->($bot, $sendnick, $host, $hand, $text); + } + } + } + debug(ALL, "$hostmask $target $text"); + } elsif($response =~ /^:([^ ]+) NOTICE ([^ ]+) :(.*)\r\n$/i) { + my ($hostmask, $target, $text) = ($1, $2, $3); + if ($hostmask =~ /([^!]+)!([^@]+@[^@ ]+)/) { + my ($sendnick, $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, $sendnick, $host, $hand, $text, $target); + # } + } + # CTCP replies + if ($hostmask ne '*status!znc@znc.in') { + if ($text =~ /^(PING|VERSION|TIME|USERINFO) (.*)$/i) { + my ($key, $val) = ($1, $2); + my $id = BotNow::SQLite::id("irc", "nick", $sendnick, $expires); + BotNow::SQLite::set("irc", "id", $id, "ctcp".lc($key), $val); + BotNow::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 ($sendnick, $host) = ($1, $2); + my $hand = $staff; # TODO fix later + foreach my $c (@{$call->{mode}}) { + # TODO filter by mask + my $proc = $c->{proc}; + $proc->($bot, $sendnick, $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, $sendnick, $host, $chan) = ($1, $2, $3, $4); + my $hand = $staff; # TODO fix later + foreach my $c (@{$call->{join}}) { + my $proc = $c->{proc}; + $proc->($bot, $sendnick, $host, $hand, $chan); + } + debug(ALL, "$hostmask JOIN $chan"); +#:jrmu!jrmu@jrmu.staff.ircnow.org JOIN :#testing + } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) PART ([^ ]+) :(.*)\r\n$/i) { + my ($hostmask, $sendnick, $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, $sendnick, $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, $sendnick, $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, $sendnick, $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, $sendnick, $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, $sendnick, $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) $nick.? :?(.*)\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"); + my $val = $2 eq ":is a registered nick" ? "True" : "$2"; + my $id = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::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 = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::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 = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::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 = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::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+) :?(.*)/) { + my ($sender, $idle, $epochtime) = ($1, $2, $3); + my $id = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::SQLite::set("irc", "id", $id, "idle", $idle); +# BotNow::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 = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::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 = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::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 = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::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 = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::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 = BotNow::SQLite::id("irc", "nick", $sender, $expires); + BotNow::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) { + $self->putserv($bot, "PRIVMSG $chan :$nick oper password failed; the bot will be unable to view uncloaked IP addresses"); + } + } elsif ($code =~ /^477$/) { # Can't join channel + foreach my $chan (@$teamchans) { + $self->putserv($bot, "PRIVMSG $chan :ERROR: $nick 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 { + chomp($response); + debug(ERRORS, "--> $response"); + } +} + +sub run { + my $self=shift; + my $bots=$self->{bots}; + my $call=$self->{call}; + my $sel = $self->{sockets}; + while(my @ready = $sel->can_read) { + my ($bot, $response); + 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; + } + $self->parse_response($bot,$response); + } + } +} + + 1;