diff --git a/lib/PBot/Core/AntiFlood.pm b/lib/PBot/Core/AntiFlood.pm index 3f66ee02..59a310c4 100644 --- a/lib/PBot/Core/AntiFlood.pm +++ b/lib/PBot/Core/AntiFlood.pm @@ -25,8 +25,10 @@ sub initialize { my ($self, %conf) = @_; # flags for 'validated' field - $self->{NICKSERV_VALIDATED} = (1 << 0); - $self->{NEEDS_CHECKBAN} = (1 << 1); + use constant { + NICKSERV_VALIDATED => (1 << 0), + NEEDS_CHECKBAN => (1 << 1), + }; $self->{channels} = {}; # per-channel statistics, e.g. for optimized tracking of last spoken nick for enter-abuse detection, etc $self->{nickflood} = {}; # statistics to track nickchange flooding @@ -188,6 +190,29 @@ sub check_flood { return; } + my $needs_checkban = 0; + + if (defined $context && defined $context->{tags}) { + my $tags = $self->{pbot}->{irc}->get_tags($context->{tags}); + + if (defined $tags->{account}) { + my $nickserv_account = $tags->{account}; + my $current_nickserv_account = $self->{pbot}->{messagehistory}->{database}->get_current_nickserv_account($account); + + if ($self->{pbot}->{registry}->get_value('irc', 'debug_tags')) { + $self->{pbot}->{logger}->log("($account) $mask got account-tag $nickserv_account\n"); + } + + if ($current_nickserv_account ne $nickserv_account) { + $self->{pbot}->{logger}->log("[MH] ($account) $mask updating NickServ to $nickserv_account\n"); + $self->{pbot}->{messagehistory}->{database}->set_current_nickserv_account($account, $nickserv_account); + $self->{pbot}->{messagehistory}->{database}->update_nickserv_account($account, $nickserv_account, scalar gettimeofday); + $self->{pbot}->{messagehistory}->{database}->link_aliases($account, $mask, $nickserv_account); + $needs_checkban = 1; + } + } + } + my $channels; if ($mode == MSG_NICKCHANGE) { $channels = $self->{pbot}->{nicklist}->get_channels($oldnick); @@ -205,14 +230,16 @@ sub check_flood { if ($chan =~ /^#/ and $mode == MSG_DEPARTURE) { # remove validation on PART or KICK so we check for ban-evasion when user returns at a later time my $chan_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($account, $chan, 'validated'); - if ($chan_data->{validated} & $self->{NICKSERV_VALIDATED}) { - $chan_data->{validated} &= ~$self->{NICKSERV_VALIDATED}; + if ($chan_data->{validated} & NICKSERV_VALIDATED) { + $chan_data->{validated} &= ~NICKSERV_VALIDATED; $self->{pbot}->{messagehistory}->{database}->update_channel_data($account, $chan, $chan_data); } next; } - if ($self->{pbot}->{capabilities}->userhas($u, 'is-whitelisted')) { next; } + next if $self->{pbot}->{capabilities}->userhas($u, 'is-whitelisted'); + + $self->check_bans($account, $mask, $chan) if $needs_checkban; if ($max_messages > $self->{pbot}->{registry}->get_value('messagehistory', 'max_messages')) { $self->{pbot}->{logger}->log("Warning: max_messages greater than max_messages limit; truncating.\n"); @@ -223,7 +250,7 @@ sub check_flood { if ($chan =~ m/^#/) { my $validated = $self->{pbot}->{messagehistory}->{database}->get_channel_data($account, $chan, 'validated')->{'validated'}; - if ($validated & $self->{NEEDS_CHECKBAN} or not $validated & $self->{NICKSERV_VALIDATED}) { + if ($validated & NEEDS_CHECKBAN or not $validated & NICKSERV_VALIDATED) { if ($mode == MSG_DEPARTURE) { # don't check for evasion on PART/KICK } elsif ($mode == MSG_NICKCHANGE) { @@ -575,8 +602,8 @@ sub devalidate_accounts { foreach my $account (@message_accounts) { my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($account, $channel, 'validated'); - if (defined $channel_data and $channel_data->{validated} & $self->{NICKSERV_VALIDATED}) { - $channel_data->{validated} &= ~$self->{NICKSERV_VALIDATED}; + if (defined $channel_data and $channel_data->{validated} & NICKSERV_VALIDATED) { + $channel_data->{validated} &= ~NICKSERV_VALIDATED; #$self->{pbot}->{logger}->log("Devalidating account $account\n"); $self->{pbot}->{messagehistory}->{database}->update_channel_data($account, $channel, $channel_data); @@ -605,16 +632,16 @@ sub check_bans { if (defined $current_nickserv_account and length $current_nickserv_account) { $self->{pbot}->{logger}->log("anti-flood: [check-bans] current nickserv [$current_nickserv_account] found for $mask\n") if $debug_checkban >= 2; my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($message_account, $channel, 'validated'); - if ($channel_data->{validated} & $self->{NEEDS_CHECKBAN}) { - $channel_data->{validated} &= ~$self->{NEEDS_CHECKBAN}; + if ($channel_data->{validated} & NEEDS_CHECKBAN) { + $channel_data->{validated} &= ~NEEDS_CHECKBAN; $self->{pbot}->{messagehistory}->{database}->update_channel_data($message_account, $channel, $channel_data); } } else { if (not exists $self->{pbot}->{irc_capabilities}->{'account-notify'}) { # mark this account as needing check-bans when nickserv account is identified my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($message_account, $channel, 'validated'); - if (not $channel_data->{validated} & $self->{NEEDS_CHECKBAN}) { - $channel_data->{validated} |= $self->{NEEDS_CHECKBAN}; + if (not $channel_data->{validated} & NEEDS_CHECKBAN) { + $channel_data->{validated} |= NEEDS_CHECKBAN; $self->{pbot}->{messagehistory}->{database}->update_channel_data($message_account, $channel, $channel_data); } $self->{pbot}->{logger}->log("anti-flood: [check-bans] no account for $mask; marking for later validation\n") if $debug_checkban >= 1; @@ -685,8 +712,8 @@ sub check_bans { $self->{pbot}->{logger} ->log("anti-flood: [check-bans] $mask [$alias] evaded $baninfo->{mask} in $baninfo->{channel}, but within 5 seconds of establishing ban; giving another chance\n"); my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($message_account, $channel, 'validated'); - if ($channel_data->{validated} & $self->{NICKSERV_VALIDATED}) { - $channel_data->{validated} &= ~$self->{NICKSERV_VALIDATED}; + if ($channel_data->{validated} & NICKSERV_VALIDATED) { + $channel_data->{validated} &= ~NICKSERV_VALIDATED; $self->{pbot}->{messagehistory}->{database}->update_channel_data($message_account, $channel, $channel_data); } $do_not_validate = 1; @@ -792,8 +819,8 @@ sub check_bans { ); } my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($message_account, $channel, 'validated'); - if ($channel_data->{validated} & $self->{NICKSERV_VALIDATED}) { - $channel_data->{validated} &= ~$self->{NICKSERV_VALIDATED}; + if ($channel_data->{validated} & NICKSERV_VALIDATED) { + $channel_data->{validated} &= ~NICKSERV_VALIDATED; $self->{pbot}->{messagehistory}->{database}->update_channel_data($message_account, $channel, $channel_data); } return; @@ -802,8 +829,8 @@ sub check_bans { unless ($do_not_validate) { my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($message_account, $channel, 'validated'); - if (not $channel_data->{validated} & $self->{NICKSERV_VALIDATED}) { - $channel_data->{validated} |= $self->{NICKSERV_VALIDATED}; + if (not $channel_data->{validated} & NICKSERV_VALIDATED) { + $channel_data->{validated} |= NICKSERV_VALIDATED; $self->{pbot}->{messagehistory}->{database}->update_channel_data($message_account, $channel, $channel_data); } } @@ -825,7 +852,7 @@ sub on_endofwhois { foreach my $channel (@$channels) { next unless $channel =~ /^#/; my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($id, $channel, 'validated'); - if ($channel_data->{validated} & $self->{NEEDS_CHECKBAN} or not $channel_data->{validated} & $self->{NICKSERV_VALIDATED}) { + if ($channel_data->{validated} & NEEDS_CHECKBAN or not $channel_data->{validated} & NICKSERV_VALIDATED) { $self->check_bans($id, $hostmask, $channel); } } @@ -870,7 +897,7 @@ sub on_accountnotify { my $mask = $event->{event}->{from}; my ($nick, $user, $host) = $mask =~ m/^([^!]+)!([^@]+)@(.*)/; - my $account = $event->{event}->{args}[0]; + my $account = $event->{event}{args}[0]; my $id = $self->{pbot}->{messagehistory}->{database}->get_message_account($nick, $user, $host); $self->{pbot}->{messagehistory}->{database}->update_hostmask_data($mask, {last_seen => scalar gettimeofday}); diff --git a/lib/PBot/Core/Handlers/Cap.pm b/lib/PBot/Core/Handlers/Cap.pm index c94f2dd3..1fc3b04c 100644 --- a/lib/PBot/Core/Handlers/Cap.pm +++ b/lib/PBot/Core/Handlers/Cap.pm @@ -10,6 +10,8 @@ package PBot::Core::Handlers::Cap; use PBot::Imports; use parent 'PBot::Core::Class'; +use POSIX qw/EXIT_FAILURE/; + sub initialize { my ($self, %conf) = @_; @@ -22,28 +24,16 @@ sub initialize { sub on_cap { my ($self, $event_type, $event) = @_; - # configure client capabilities that PBot currently supports - my %desired_caps = ( - 'account-notify' => 1, - 'extended-join' => 1, - - # TODO: unsupported capabilities worth looking into - 'away-notify' => 0, - 'chghost' => 0, - 'identify-msg' => 0, - 'multi-prefix' => 0, - ); - if ($event->{event}->{args}->[0] eq 'LS') { my $capabilities; - my $caps_done = 0; + my $caps_listed = 0; if ($event->{event}->{args}->[1] eq '*') { # more CAP LS messages coming $capabilities = $event->{event}->{args}->[2]; } else { # final CAP LS message - $caps_done = 1; + $caps_listed = 1; $capabilities = $event->{event}->{args}->[1]; } @@ -51,36 +41,20 @@ sub on_cap { my @caps = split /\s+/, $capabilities; + # store available capabilities foreach my $cap (@caps) { my $value; - if ($cap =~ /=/) { - ($cap, $value) = split /=/, $cap; - } else { - $value = 1; - } + ($cap, $value) = split /=/, $cap; + $value //= 1; - # store available capability $self->{pbot}->{irc_capabilities_available}->{$cap} = $value; - - # request desired capabilities - if ($desired_caps{$cap}) { - $self->{pbot}->{logger}->log("Requesting client capability $cap\n"); - $event->{conn}->sl("CAP REQ :$cap"); - } } - # capability negotiation done - # now we either start SASL authentication or we send CAP END - if ($caps_done) { - # start SASL authentication if enabled - if ($self->{pbot}->{registry}->get_value('irc', 'sasl')) { - $self->{pbot}->{logger}->log("Requesting client capability sasl\n"); - $event->{conn}->sl("CAP REQ :sasl"); - } else { - $self->{pbot}->{logger}->log("Completed client capability negotiation\n"); - $event->{conn}->sl("CAP END"); - } + # all capabilities listed? + if ($caps_listed) { + # request desired capabilities + $self->request_caps($event); } } elsif ($event->{event}->{args}->[0] eq 'ACK') { @@ -89,7 +63,10 @@ sub on_cap { my @caps = split /\s+/, $event->{event}->{args}->[1]; foreach my $cap (@caps) { - $self->{pbot}->{irc_capabilities}->{$cap} = 1; + my ($key, $val) = split '=', $cap; + $val //= 1; + + $self->{pbot}->{irc_capabilities}->{$key} = $val; if ($cap eq 'sasl') { # begin SASL authentication @@ -111,4 +88,45 @@ sub on_cap { return 1; } +sub request_caps { + my ($self, $event) = @_; + + # configure client capabilities that PBot currently supports + my %desired_caps = ( + 'account-notify' => 1, + 'account-tag' => 1, + 'extended-join' => 1, + 'message-tags' => 1, + # sasl is gated by the irc.sasl registry entry instead + + # TODO: unsupported capabilities worth looking into + 'away-notify' => 0, + 'chghost' => 0, + 'identify-msg' => 0, + 'multi-prefix' => 0, + ); + + foreach my $cap (keys $self->{pbot}->{irc_capabilities_available}->%*) { + # request desired capabilities + if ($desired_caps{$cap}) { + $self->{pbot}->{logger}->log("Requesting client capability $cap\n"); + $event->{conn}->sl("CAP REQ :$cap"); + } + } + + # request SASL capability if enabled, otherwise end cap negotiation + if ($self->{pbot}->{registry}->get_value('irc', 'sasl')) { + if (not exists $self->{pbot}->{irc_capabilities_available}->{sasl}) { + $self->{pbot}->{logger}->log("SASL is not supported by this IRC server\n"); + $self->{pbot}->exit(EXIT_FAILURE); + } + + $self->{pbot}->{logger}->log("Requesting client capability sasl\n"); + $event->{conn}->sl("CAP REQ :sasl"); + } else { + $self->{pbot}->{logger}->log("Completed client capability negotiation\n"); + $event->{conn}->sl("CAP END"); + } +} + 1; diff --git a/lib/PBot/Core/Handlers/Chat.pm b/lib/PBot/Core/Handlers/Chat.pm index 0979cca7..44c04bbf 100644 --- a/lib/PBot/Core/Handlers/Chat.pm +++ b/lib/PBot/Core/Handlers/Chat.pm @@ -52,18 +52,19 @@ sub on_notice { sub on_public { my ($self, $event_type, $event) = @_; - my ($from, $nick, $user, $host, $text) = ( + my ($from, $nick, $user, $host, $text, $tags) = ( $event->{event}->{to}->[0], $event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->{args}->[0], + $event->{event}->{args}->[1], ); ($nick, $user, $host) = $self->{pbot}->{irchandlers}->normalize_hostmask($nick, $user, $host); # send text to be processed for bot commands, anti-flood enforcement, etc - $event->{interpreted} = $self->{pbot}->{interpreter}->process_line($from, $nick, $user, $host, $text); + $event->{interpreted} = $self->{pbot}->{interpreter}->process_line($from, $nick, $user, $host, $text, $tags); return 1; } @@ -82,17 +83,18 @@ sub on_action { sub on_msg { my ($self, $event_type, $event) = @_; - my ($nick, $user, $host, $text) = ( + my ($nick, $user, $host, $text, $tags) = ( $event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->{args}->[0], + $event->{event}->{args}->[1], ); ($nick, $user, $host) = $self->{pbot}->{irchandlers}->normalize_hostmask($nick, $user, $host); # send text to be processed as a bot command, in "channel" $nick - $event->{interpreted} = $self->{pbot}->{interpreter}->process_line($nick, $nick, $user, $host, $text, 1); + $event->{interpreted} = $self->{pbot}->{interpreter}->process_line($nick, $nick, $user, $host, $text, $tags, 1); return 1; } diff --git a/lib/PBot/Core/Handlers/SASL.pm b/lib/PBot/Core/Handlers/SASL.pm index c22575b7..bde5f9c6 100644 --- a/lib/PBot/Core/Handlers/SASL.pm +++ b/lib/PBot/Core/Handlers/SASL.pm @@ -10,7 +10,7 @@ package PBot::Core::Handlers::SASL; use PBot::Imports; use parent 'PBot::Core::Class'; -use POSIX qw/EXIT_SUCCESS EXIT_FAILURE/; +use POSIX qw/EXIT_FAILURE/; use Encode; use MIME::Base64; @@ -60,7 +60,7 @@ sub on_sasl_authenticate { sub on_rpl_loggedin { my ($self, $event_type, $event) = @_; - $self->{pbot}->{logger}->log($event->{event}->{args}->[1] . "\n"); + $self->{pbot}->{logger}->log($event->{event}->{args}->[3] . "\n"); return 1; } diff --git a/lib/PBot/Core/IRC.pm b/lib/PBot/Core/IRC.pm index fb8ead26..b9de2cc7 100644 --- a/lib/PBot/Core/IRC.pm +++ b/lib/PBot/Core/IRC.pm @@ -260,6 +260,23 @@ sub timeout { return $self->{_timeout}; } +# parses message tags into a hashref +sub get_tags { + my ($self, $tags) = @_; + + $self->{_pbot}->{logger}->log("Message tags: [$tags]\n"); + + my @list = split ';', $tags; + my %hash; + + foreach my $tag (@list) { + my ($key, $val) = split '=', $tag; + $hash{$key} = $val; + } + + return \%hash; +} + 1; __END__ diff --git a/lib/PBot/Core/IRC/Connection.pm b/lib/PBot/Core/IRC/Connection.pm index 51243805..548ae05b 100644 --- a/lib/PBot/Core/IRC/Connection.pm +++ b/lib/PBot/Core/IRC/Connection.pm @@ -508,9 +508,9 @@ sub handler { croak "Not enough arguments to handler()"; } - print STDERR "Trying to handle event '$ev'.\n" if $self->{_debug}; + print STDERR "--- Trying to handle event '$ev'.\n" if $self->{_debug}; - if ($self->{_debug}) { + if ($self->{_debug} > 1) { use Data::Dumper; print STDERR "ev: ", Dumper($ev), "\nevent: ", Dumper($event), "\n"; } @@ -535,7 +535,7 @@ sub handler { confess "Bad parameter passed to handler(): rp=$rp"; } - print STDERR "Handler for '$ev' called.\n" if $self->{_debug}; + print STDERR "--- Handler for '$ev' called.\n" if $self->{_debug}; return 1; } @@ -838,7 +838,7 @@ sub oper { # appropriate handler. Takes no args, really. sub parse { my ($self) = shift; - my ($from, $type, $message, @stuff, $itype, $ev, @lines, $line); + my ($from, $type, $message, @stuff, $itype, $ev, @lines, $line, $tags); my $n; @@ -920,7 +920,9 @@ sub parse { # Spurious backslashes are for the benefit of cperl-mode. # Assumption: all non-numeric message types begin with a letter } elsif ( - $line =~ /^:? + $line =~ /^ + (?:\@\S+\s)? # Optional message tags + :? # Initial colon (?:[][}{\w\\\`^|\-]+? # The nick (valid nickname chars) ! # The nick-username separator .+? # The username @@ -932,6 +934,8 @@ sub parse { /x ) # That ought to do it for now... { + $tags = undef; + $tags = $1 if $line =~ s/^@(\S+)\s//; $line = substr $line, 1 if $line =~ /^:/; # Patch submitted for v.0.72 @@ -939,7 +943,7 @@ sub parse { # ($from, $line) = split ":", $line, 2; ($from, $line) = $line =~ /^(?:|)(\S+\s+[^:]+):?(.*)/; - print STDERR "from: [$from], line: [$line]\n" if $self->{_debug}; + print STDERR "from: [$from], line: [$line]\n" if $self->{_debug} > 2; ($from, $type, @stuff) = split /\s+/, $from; $type = lc $type; @@ -986,8 +990,9 @@ sub parse { # quotemeta($_))); /$from/ } # ($self->ignore($type), $self->ignore("all")); - # Add $line to @stuff for the handlers + # Add $line and $tags to @stuff for the handlers push @stuff, $line if defined $line; + push @stuff, $tags if defined $tags; # Now ship it off to the appropriate handler and forget about it. if ($itype eq "ctcp") { # it's got CTCP in it! @@ -1251,6 +1256,9 @@ sub privmsg { my $buf = CORE::join '', @_; my $length = $self->{_maxlinelen} - 11 - length($to); + + print STDERR "privmsg trunc length: $length; msg len: " . (length $buf) . "\n" if $self->{_debug}; + my $line; if (ref($to) =~ /^(GLOB|IO::Socket)/) { @@ -1261,8 +1269,11 @@ sub privmsg { } else { while (length($buf) > 0) { ($line, $buf) = unpack("a$length a*", $buf); - if (ref $to eq 'ARRAY') { $self->sl("PRIVMSG ", CORE::join(',', @$to), " :$line"); } - else { $self->sl("PRIVMSG $to :$line"); } + if (ref $to eq 'ARRAY') { + $self->sl("PRIVMSG ", CORE::join(',', @$to), " :$line"); + } else { + $self->sl("PRIVMSG $to :$line"); + } } } } @@ -1319,7 +1330,7 @@ sub schedule { unless ($coderef) { croak 'Not enough arguments to Connection->schedule()'; } unless (ref($coderef) eq 'CODE') { croak 'Second argument to schedule() isn\'t a coderef'; } - print STDERR "Scheduling event with time [$time]\n" if $self->{_debug}; + print STDERR "Scheduling event with time [$time]\n" if $self->{_debug} > 1; $time += time; $self->parent->enqueue_scheduled_event($time, $coderef, $self, @_); } @@ -1332,7 +1343,7 @@ sub schedule_output_event { unless ($coderef) { croak 'Not enough arguments to Connection->schedule()'; } unless (ref($coderef) eq 'CODE') { croak 'Second argument to schedule() isn\'t a coderef'; } - print STDERR "Scheduling output event with time [$time] [$_[0]]\n" if $self->{_debug}; + print STDERR "Scheduling output event with time [$time] [$_[0]]\n" if $self->{_debug} > 1; $time += time; $self->parent->enqueue_output_event($time, $coderef, $self, @_); } @@ -1395,6 +1406,10 @@ sub sl { if ($self->{_slcount} < 10) { $self->{_slcount}++; $self->{_lastsl} = time; + + ### DEBUG DEBUG DEBUG + if ($self->{_debug} > 1) { print STDERR "S-> 0 " . (length ($line) + 2) . " $line\n"; } + return $self->schedule_output_event(0, \&sl_real, $line); } @@ -1413,7 +1428,7 @@ sub sl { } ### DEBUG DEBUG DEBUG - if ($self->{_debug}) { print STDERR "S-> $seconds $line\n"; } + if ($self->{_debug} > 1) { print STDERR "S-> $seconds " . (length ($line) + 2) . " $line\n"; } $self->schedule_output_event($seconds, \&sl_real, $line); } @@ -1428,7 +1443,7 @@ sub sl_real { unless ($line) { croak "Not enough arguments to sl_real()"; } ### DEBUG DEBUG DEBUG - if ($self->{_debug}) { print STDERR ">>> $line\n"; } + if ($self->{_debug}) { print STDERR ">>> (" . (length ($line) + 2) . ") $line\n"; } return unless defined $self->socket; diff --git a/lib/PBot/Core/Interpreter.pm b/lib/PBot/Core/Interpreter.pm index f4bdf679..34b9c0b9 100644 --- a/lib/PBot/Core/Interpreter.pm +++ b/lib/PBot/Core/Interpreter.pm @@ -38,7 +38,7 @@ sub initialize { # this is the main entry point for a message to be parsed into commands # and to execute those commands and process their output sub process_line { - my ($self, $from, $nick, $user, $host, $text, $is_command) = @_; + my ($self, $from, $nick, $user, $host, $text, $tags, $is_command) = @_; # lowercase `from` field for case-insensitivity $from = lc $from; @@ -57,6 +57,7 @@ sub process_line { host => $host, # hostname/ip address hostmask => "$nick!$user\@$host", # full hostmask text => $text, # message contents + tags => $tags, # message tags }; # add hostmask to user/message tracking database and get their account id diff --git a/lib/PBot/Core/StdinReader.pm b/lib/PBot/Core/StdinReader.pm index ace9ccba..531ded05 100644 --- a/lib/PBot/Core/StdinReader.pm +++ b/lib/PBot/Core/StdinReader.pm @@ -68,7 +68,7 @@ sub stdin_reader { my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick'); # process input as a bot command - return $self->{pbot}->{interpreter}->process_line('stdin@pbot', $botnick, "stdin", "pbot", $input, 1); + return $self->{pbot}->{interpreter}->process_line('stdin@pbot', $botnick, "stdin", "pbot", $input, undef, 1); } 1; diff --git a/lib/PBot/VERSION.pm b/lib/PBot/VERSION.pm index c6ee7ad9..678e26b6 100644 --- a/lib/PBot/VERSION.pm +++ b/lib/PBot/VERSION.pm @@ -25,8 +25,8 @@ use PBot::Imports; # These are set by the /misc/update_version script use constant { BUILD_NAME => "PBot", - BUILD_REVISION => 4605, - BUILD_DATE => "2023-01-24", + BUILD_REVISION => 4606, + BUILD_DATE => "2023-01-27", }; sub initialize {}