commit c62b325b973092b8ce0cf3e11d5e273c570c3379 from: Izzy Blacklock date: Fri Aug 18 02:49:18 2023 UTC New parser for znc tables in chat Started breaking up the irc response parser up into smaller chunks. can now parse table responses from znc *status channel. More debug messages then necessary. no useful output at this point. Parses the field header and field data into an array of arrays. Need to capture from *controlpanel and *blockuser still commit - b15a3128cdaa0f3f1a2013f45cb8dd71560b7cd4 commit + c62b325b973092b8ce0cf3e11d5e273c570c3379 blob - /dev/null blob + 5de58945dcbd237991749bfa288156f3e452d889 (mode 755) --- /dev/null +++ bin/zncTest.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl +# + +use strict; +use warnings; + +use lib qw(./lib); +use IRCNOW::Database; + +use IRCNOW::IO::IRC; +use IRCNOW::Acct; + +use IRCNOW::IO qw(:DEBUG); +$verbosity=ALL; +#my $dbase='/var/www/botnow/botnow.db'; +#debug(INFO, "Loading Database $dbase"); +#my $botnowDB = IRCNOW::Database->new(dbpath=>$dbase); + +use Data::Dumper; + +#my $acct=new IRCNOW::Acct(); +#print $acct->newpass() . "\n"; + +my $irc = new IRCNOW::IO::IRC( + localnet => 'ircnow', + staff => 'izzyb', + nick => 'cliNow', + host => '127.0.0.1', + port => 1337, + pass => 'Secerate!pwd', + expires => 1800, + networks => 'ircnow', + chans => '#bottest', + teamchans => '', +); + + + +$irc->{bots}->[0]->{actions} = [ + 'privmsg *status :Traffic', + 'privmsg *status :listusers', + 'privmsg *controlpanel :listusers', + 'privmsg #bottest : ', +]; + + + +$irc->run(); + blob - 8f38a942c8bcdc6e0d6284a5c170fd6e22f82e2a blob + 419c632a0712d04cf232a732c16d45f3f4bbd529 --- lib/IRCNOW/IO/IRC.pm +++ lib/IRCNOW/IO/IRC.pm @@ -1,9 +1,13 @@ package IRCNOW::IO::IRC; use strict; use warnings; +use Carp; +use Data::Dumper; + use lib qw(./lib); use IRCNOW::IO qw(:DEBUG); + use IO::Socket; use IO::Select; @@ -27,6 +31,7 @@ our @bots; our $localnet; our @stafflist; our @networks; +our %tables; sub new { my $class = shift; @@ -36,6 +41,7 @@ sub new { call => $call, stafflist => \@stafflist, networks => \@networks, + tables => \%tables, }; # verify we have all required params my @required = qw(networks staff localnet nick host port pass expires chans teamchans); @@ -205,6 +211,98 @@ sub isstaff { return 0; } +sub parse_zncTable { + my $self = shift; + my $tableName = shift; + my $text = shift; + $self->{tables}->{$tableName} = {phase=>0,data=>[]} unless exists $self->{tables}->{$tableName}; + my $thisTable = $self->{tables}->{$tableName}; + if (substr($text, 0,1) eq '+') { + $thisTable->{phase}++; + if ($thisTable->{phase} >= 3 ) { # Third + is bottom of table + # end of table + $thisTable->{phase}=0; + debug(ALL, "Table Data for $tableName"); + debug(ALL, Dumper($thisTable)); + # XXX Do something useful here + } + } else { # this line begins with '|' + # | field1 | field2 | field3 | + my $fields = [ split ('\s*\|\s+', $text) ]; + shift @$fields; # Get rid of empty field before first | + $fields->[-1] =~ s/\s*\|$//; #remove trailing whitespace and | from last record + if ( $thisTable->{phase} == 1 ) { + # This must be the header line + $thisTable->{header} = $fields; + # Capture field names + } else { + # Capture rows of data + push @{$thisTable->{data}}, $fields; + } + } +} +# message in channel starting with '!' or a PRIVMSG to the bot. +# $response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) PRIVMSG ([^ ]+) :(.*)\r\n$/i) +sub parse_commands { + my ($self, $bot, $response, @params) = @_; + my ($hostmask, $sendnick, $host, $target, $text) = @params; + my $nick = $self->{nick}; + my $staff = $self->{staff}; + chomp($response); + #debug(ALL, "-->COMMAND--> $response"); + if ($hostmask eq '*status!znc@znc.in' && $target =~ /^$nick.?$/) { + debug(ALL, "TEXT: $text"); + 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 =~ /^[|+]/) { + my $tableName = "$target/" . $bot->{name}; + $self->parse_zncTable($tableName, $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"); +} + + sub parse_response { my ($self, $bot, $response) = @_; my $staff=$self->{staff}; @@ -213,12 +311,17 @@ sub parse_response { my $expires=$self->{expires}; my $chans=$self->{chans}; my $teamchans=$self->{teamchans}; + my $tables=$self->{tables}; 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); + chomp $response; + debug(ALL, "ZNC: Response = $response"); + debug(ALL, "ZNC: type = $type, target = $target, text = $text"); if ($type eq "464" && $target =~ /^$nick.?$/ && $text eq "Password required") { + debug(ALL, "ZNC: putserv( 'PASS $nick/$bot->{name}:$pass' )"); $self->putserv($bot, "PASS $nick/$bot->{name}:$pass"); if ($bot->{name} =~ /^$localnet$/i) { $self->putserv($bot, "OPER $nick $pass"); @@ -240,59 +343,13 @@ sub parse_response { 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"); + # my ($hostmask, $sendnick, $host, $target, $text) = ($1,$2,$3,$4,$5); + $self->parse_commands($bot, $response,$1,$2,$3,$4,$5); } elsif($response =~ /^:([^ ]+) NOTICE ([^ ]+) :(.*)\r\n$/i) { my ($hostmask, $target, $text) = ($1, $2, $3); + chomp $response; + debug(ALL, "NOTICE: ". $response); + debug(ALL, "NOTICE: hostmask = $hostmask, target = $target, text = $text"); if ($hostmask =~ /([^!]+)!([^@]+@[^@ ]+)/) { my ($sendnick, $host) = ($1, $2); my $hand = $staff; # TODO fix later @@ -313,11 +370,12 @@ sub parse_response { } } } - 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); + debug(ALL, "NOTICE: $response"); + debug(ALL, "NOTICE: hostmask = $hostmask, chan = $chan, change = $change, targets = $targets"); if ($hostmask =~ /([^!]+)!([^@]+@[^@ ]+)/) { my ($sendnick, $host) = ($1, $2); my $hand = $staff; # TODO fix later @@ -490,25 +548,46 @@ sub parse_response { } } + +sub parse_actions { + my $self=shift; + foreach my $b (@{$self->{bots}}) { + if (exists $b->{actions}) { + my $action = shift @{$b->{actions}} || ''; + if ($action ne "") { + debug(ALL, "ACTION <--- $action"); + $self->putserv($b,$action); + } + } + } +} + + 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; + while(1) { + my @ready = $sel->can_read(1); + if(@ready) { + 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); } - if (!defined($response = <$socket>)) { - debug(ERRORS, "ERROR ".$bot->{name}." has no response:"); - next; - } - $self->parse_response($bot,$response); + } else { # Nothing to read for 5 seconds, see if user input + # After processing pending messages + $self->parse_actions(); } } }