From d955bfa06c81da41dc8c7df5feee11d114ca6621 Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Sat, 17 May 2014 20:08:19 +0000 Subject: [PATCH] Add centralized configuration registry module Allows changing of bot configuration values without needing to restart bot instance or needing to edit pbot.pl script. Registry will initially be populated with default values from pbot.pl, but if a registry file exists then the registry values will take precedence over the pbot.pl values. For instance, if you regset the bot trigger to '%' then the trigger will be '%' even if pbot.pl has '!' or something else explicitly set. Some registry items can have trigger hooks associated with them. For instance, the irc->botnick registry entry has a change_botnick_trigger associated with it which changes the IRC nick on the server when a new value is set via regset/regadd. Tons of other fixes and improvements throughout. --- PBot/AntiFlood.pm | 37 +++--- PBot/BanTracker.pm | 3 - PBot/BotAdminCommands.pm | 3 - PBot/BotAdmins.pm | 9 +- PBot/ChanOpCommands.pm | 6 +- PBot/ChanOps.pm | 13 +- PBot/Channels.pm | 20 ++- PBot/Commands.pm | 3 - PBot/DualIndexHashObject.pm | 45 ++----- PBot/FactoidCommands.pm | 15 +-- PBot/FactoidModuleLauncher.pm | 9 +- PBot/Factoids.pm | 18 ++- PBot/HashObject.pm | 3 - PBot/IRCHandlers.pm | 27 ++-- PBot/IgnoreList.pm | 17 +-- PBot/IgnoreListCommands.pm | 3 - PBot/Interpreter.pm | 30 ++--- PBot/LagChecker.pm | 3 - PBot/Logger.pm | 3 - PBot/MessageHistory.pm | 2 +- PBot/MessageHistory_SQLite.pm | 10 +- PBot/PBot.pm | 207 ++++++++++++++--------------- PBot/Quotegrabs.pm | 3 - PBot/Quotegrabs_Hashtable.pm | 3 - PBot/Quotegrabs_SQLite.pm | 3 - PBot/Registerable.pm | 3 - PBot/Registry.pm | 156 ++++++++++++++++++++++ PBot/RegistryCommands.pm | 240 ++++++++++++++++++++++++++++++++++ PBot/SelectHandler.pm | 3 - PBot/StdinReader.pm | 11 +- PBot/Timer.pm | 3 - PBot/VERSION.pm | 4 +- pbot.pl | 7 +- 33 files changed, 615 insertions(+), 307 deletions(-) create mode 100644 PBot/Registry.pm create mode 100644 PBot/RegistryCommands.pm diff --git a/PBot/AntiFlood.pm b/PBot/AntiFlood.pm index 1b2f417f..0de7ff7e 100644 --- a/PBot/AntiFlood.pm +++ b/PBot/AntiFlood.pm @@ -14,9 +14,6 @@ use strict; use feature 'switch'; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use PBot::DualIndexHashObject; use PBot::LagChecker; @@ -45,14 +42,10 @@ sub initialize { $self->{NICKSERV_VALIDATED} = (1<<0); $self->{NEEDS_CHECKBAN} = (1<<1); - $self->{ENTER_ABUSE_MAX_LINES} = 4; - $self->{ENTER_ABUSE_MAX_OFFENSES} = 3; - $self->{ENTER_ABUSE_MAX_SECONDS} = 20; - - $self->{channels} = {}; # per-channel statistics, e.g. for optimized tracking of last spoken nick for enter-abuse detection, etc + $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 - my $filename = delete $conf{banwhitelist_file} // $self->{pbot}->{data_dir} . '/ban_whitelist'; + my $filename = delete $conf{banwhitelist_file} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/ban_whitelist'; $self->{ban_whitelist} = PBot::DualIndexHashObject->new(name => 'BanWhitelist', filename => $filename); $self->{ban_whitelist}->load; @@ -212,7 +205,7 @@ sub check_flood { } # do not do flood processing for bot messages - if($nick eq $self->{pbot}->botnick) { + if($nick eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { $self->{channels}->{$channel}->{last_spoken_nick} = $nick; return; } @@ -229,9 +222,9 @@ sub check_flood { } } - if($max_messages > $self->{pbot}->{MAX_NICK_MESSAGES}) { - $self->{pbot}->logger->log("Warning: max_messages greater than MAX_NICK_MESSAGES; truncating.\n"); - $max_messages = $self->{pbot}->{MAX_NICK_MESSAGES}; + 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"); + $max_messages = $self->{pbot}->{registry}->get_value('messagehistory', 'max_messages'); } # check for ban evasion if channel begins with # (not private message) and hasn't yet been validated against ban evasion @@ -257,11 +250,15 @@ sub check_flood { if(defined $self->{channels}->{$channel}->{last_spoken_nick} and $nick eq $self->{channels}->{$channel}->{last_spoken_nick}) { my $messages = $self->{pbot}->{messagehistory}->{database}->get_recent_messages($account, $channel, 2, $self->{pbot}->{messagehistory}->{MSG_CHAT}); - if($messages->[1]->{timestamp} - $messages->[0]->{timestamp} <= $self->{ENTER_ABUSE_MAX_SECONDS}) { - if(++$channel_data->{enter_abuse} >= $self->{ENTER_ABUSE_MAX_LINES} - 1) { - $channel_data->{enter_abuse} = $self->{ENTER_ABUSE_MAX_LINES} / 2 - 1; - if(++$channel_data->{enter_abuses} >= $self->{ENTER_ABUSE_MAX_OFFENSES}) { - my $offenses = $channel_data->{enter_abuses} - $self->{ENTER_ABUSE_MAX_OFFENSES} + 1; + my $enter_abuse_max_lines = $self->{pbot}->{registry}->get_value('antiflood', 'enter_abuse_max_lines'); + my $enter_abuse_max_seconds = $self->{pbot}->{registry}->get_value('antiflood', 'enter_abuse_max_seconds'); + my $enter_abuse_max_offenses = $self->{pbot}->{registry}->get_value('antiflood', 'enter_abuse_max_offenses'); + + if($messages->[1]->{timestamp} - $messages->[0]->{timestamp} <= $enter_abuse_max_seconds) { + if(++$channel_data->{enter_abuse} >= $enter_abuse_max_lines - 1) { + $channel_data->{enter_abuse} = $enter_abuse_max_lines / 2 - 1; + if(++$channel_data->{enter_abuses} >= $enter_abuse_max_offenses) { + my $offenses = $channel_data->{enter_abuses} - $enter_abuse_max_offenses + 1; my $ban_length = $offenses ** $offenses * $offenses * 30; $self->{pbot}->chanops->ban_user_timed("*!$user\@$host", $channel, $ban_length); $ban_length = duration($ban_length); @@ -276,7 +273,7 @@ sub check_flood { $self->{pbot}->{messagehistory}->{database}->update_channel_data($account, $channel, $channel_data); } else { if($channel_data->{enter_abuse} > 0) { - #$self->{pbot}->logger->log("$nick $channel more than $self->{ENTER_ABUSE_MAX_SECONDS} seconds since last message, enter abuse counter reset\n"); + #$self->{pbot}->logger->log("$nick $channel more than $enter_abuse_max_seconds seconds since last message, enter abuse counter reset\n"); $channel_data->{enter_abuse} = 0; $self->{pbot}->{messagehistory}->{database}->update_channel_data($account, $channel, $channel_data); } @@ -396,7 +393,7 @@ sub unbanme { my ($self, $from, $nick, $user, $host, $arguments) = @_; my $channel = lc $arguments; - if(not defined $arguments or not defined $channel) { + if(not $arguments or not $channel) { return "/msg $nick Usage: unbanme "; } diff --git a/PBot/BanTracker.pm b/PBot/BanTracker.pm index 674bbc0e..4bda8307 100644 --- a/PBot/BanTracker.pm +++ b/PBot/BanTracker.pm @@ -11,9 +11,6 @@ package PBot::BanTracker; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Time::HiRes qw/gettimeofday/; use Time::Duration; use Data::Dumper; diff --git a/PBot/BotAdminCommands.pm b/PBot/BotAdminCommands.pm index 8befc59b..a0eed4de 100644 --- a/PBot/BotAdminCommands.pm +++ b/PBot/BotAdminCommands.pm @@ -8,9 +8,6 @@ package PBot::BotAdminCommands; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Carp (); sub new { diff --git a/PBot/BotAdmins.pm b/PBot/BotAdmins.pm index 1ca4c57a..31702648 100644 --- a/PBot/BotAdmins.pm +++ b/PBot/BotAdmins.pm @@ -8,9 +8,6 @@ package PBot::BotAdmins; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use PBot::DualIndexHashObject; use Carp (); @@ -58,7 +55,7 @@ sub initialize { sub add_admin { my $self = shift; - my ($name, $channel, $hostmask, $level, $password) = @_; + my ($name, $channel, $hostmask, $level, $password, $dont_save) = @_; $channel = lc $channel; $hostmask = lc $hostmask; @@ -69,7 +66,7 @@ sub add_admin { $self->{pbot}->logger->log("Adding new level $level admin: [$name] [$hostmask] for channel [$channel]\n"); - $self->save_admins; + $self->save_admins unless $dont_save; } sub remove_admin { @@ -144,7 +141,7 @@ sub export_admins { sub find_admin { my ($self, $from, $hostmask) = @_; - $from = $self->{pbot}->botnick if not defined $from; + $from = $self->{pbot}->{registry}->get_value('irc', 'botnick') if not defined $from; $hostmask = '.*' if not defined $hostmask; my $result = eval { diff --git a/PBot/ChanOpCommands.pm b/PBot/ChanOpCommands.pm index 9b42d11b..cc92f4d5 100644 --- a/PBot/ChanOpCommands.pm +++ b/PBot/ChanOpCommands.pm @@ -8,9 +8,6 @@ package PBot::ChanOpCommands; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Carp (); sub new { @@ -62,7 +59,8 @@ sub ban_user { $length = 60 * 60; # one hour } - return "" if $target =~ /\Q$self->{pbot}->botnick\E/i; + my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick'); + return "" if $target =~ /\Q$botnick\E/i; $self->{pbot}->chanops->ban_user_timed($target, $from, $length); return "/msg $nick $target banned in $from for $length seconds"; diff --git a/PBot/ChanOps.pm b/PBot/ChanOps.pm index 9b57a09e..30a94a47 100644 --- a/PBot/ChanOps.pm +++ b/PBot/ChanOps.pm @@ -8,9 +8,6 @@ package PBot::ChanOps; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Time::HiRes qw(gettimeofday); sub new { @@ -34,7 +31,10 @@ sub initialize { } $self->{pbot} = $pbot; - $self->{unban_timeout} = PBot::DualIndexHashObject->new(pbot => $pbot, name => 'Unban Timeouts', filename => "$pbot->{data_dir}/unban_timeouts"); + + $self->{unban_timeout} = PBot::DualIndexHashObject->new(pbot => $pbot, name => 'Unban Timeouts', filename => $pbot->{registry}->get_value('general', 'data_dir') . '/unban_timeouts'); + $self->{unban_timeout}->load; + $self->{op_commands} = {}; $self->{is_opped} = {}; @@ -57,7 +57,7 @@ sub gain_ops { sub lose_ops { my $self = shift; my $channel = shift; - $self->{pbot}->conn->privmsg("chanserv", "op $channel -" . $self->{pbot}->botnick); + $self->{pbot}->conn->privmsg("chanserv", "op $channel -" . $self->{pbot}->{registry}->get_value('irc', 'botnick')); } sub add_op_command { @@ -68,6 +68,7 @@ sub add_op_command { sub perform_op_commands { my $self = shift; my $channel = shift; + my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick'); $self->{pbot}->logger->log("Performing op commands...\n"); while(my $command = shift @{ $self->{op_commands}->{$channel} }) { @@ -75,7 +76,7 @@ sub perform_op_commands { $self->{pbot}->conn->mode($1, $2); $self->{pbot}->logger->log(" executing mode $1 $2\n"); } elsif($command =~ /^kick (.*?) (.*?) (.*)/i) { - $self->{pbot}->conn->kick($1, $2, $3) unless $1 =~ /\Q$self->{pbot}->botnick\E/i; + $self->{pbot}->conn->kick($1, $2, $3) unless $1 =~ /\Q$botnick\E/i; $self->{pbot}->logger->log(" executing kick on $1 $2 $3\n"); } } diff --git a/PBot/Channels.pm b/PBot/Channels.pm index f1ca2e9d..97a87819 100644 --- a/PBot/Channels.pm +++ b/PBot/Channels.pm @@ -8,9 +8,6 @@ package PBot::Channels; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Carp (); use PBot::HashObject; @@ -29,17 +26,16 @@ sub new { sub initialize { my ($self, %conf) = @_; - my $pbot = delete $conf{pbot} // Carp::croak("Missing pbot reference to Channels"); - my $filename = delete $conf{filename}; + $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to Channels"); - $self->{pbot} = $pbot; - $self->{channels} = PBot::HashObject->new(pbot => $pbot, name => 'Channels', filename => $filename); + $self->{channels} = PBot::HashObject->new(pbot => $self->{pbot}, name => 'Channels', filename => delete $conf{filename}); + $self->load_channels; - $pbot->commands->register(sub { $self->set(@_) }, "chanset", 40); - $pbot->commands->register(sub { $self->unset(@_) }, "chanunset", 40); - $pbot->commands->register(sub { $self->add(@_) }, "chanadd", 40); - $pbot->commands->register(sub { $self->remove(@_) }, "chanrem", 40); - $pbot->commands->register(sub { $self->list(@_) }, "chanlist", 10); + $self->{pbot}->commands->register(sub { $self->set(@_) }, "chanset", 40); + $self->{pbot}->commands->register(sub { $self->unset(@_) }, "chanunset", 40); + $self->{pbot}->commands->register(sub { $self->add(@_) }, "chanadd", 40); + $self->{pbot}->commands->register(sub { $self->remove(@_) }, "chanrem", 40); + $self->{pbot}->commands->register(sub { $self->list(@_) }, "chanlist", 10); } sub set { diff --git a/PBot/Commands.pm b/PBot/Commands.pm index 32144982..4d837978 100644 --- a/PBot/Commands.pm +++ b/PBot/Commands.pm @@ -13,9 +13,6 @@ use strict; use base 'PBot::Registerable'; -use vars qw($VERSION); -$VERSION = '1.0.0'; - use Carp (); sub new { diff --git a/PBot/DualIndexHashObject.pm b/PBot/DualIndexHashObject.pm index 1bb7fe3e..0ff3a42c 100644 --- a/PBot/DualIndexHashObject.pm +++ b/PBot/DualIndexHashObject.pm @@ -9,9 +9,6 @@ package PBot::DualIndexHashObject; use warnings; use strict; -use vars qw($VERSION); -$VERSION = "1.0"; - use Text::Levenshtein qw(fastdistance); use Carp (); @@ -30,18 +27,9 @@ sub new { sub initialize { my ($self, %conf) = @_; - my $name = delete $conf{name}; - if(not defined $name) { - $name = "dual index hash object"; - } - - my $filename = delete $conf{filename}; - if(not defined $filename) { - Carp::carp("Missing filename to DualIndexHashObject, will not be able to save to or load from file."); - } - - $self->{name} = $name; - $self->{filename} = $filename; + $self->{name} = delete $conf{name} // 'Dual Index hash object'; + $self->{filename} = delete $conf{filename} // Carp::carp("Missing filename to DualIndexHashObject, will not be able to save to or load from file."); + $self->{ignore_duplicates} = delete $conf{ignore_duplicates} // 0; $self->{hash} = {}; } @@ -50,7 +38,7 @@ sub load_hash_add { my ($self, $primary_index_key, $secondary_index_key, $hash, $i, $filename) = @_; if(defined $hash) { - if(exists $self->hash->{$primary_index_key}->{$secondary_index_key}) { + if(not $self->{ignore_duplicates} and exists $self->hash->{$primary_index_key}->{$secondary_index_key}) { if($i) { Carp::croak "Duplicate secondary_index_key '$secondary_index_key' found in $filename around line $i\n"; } else { @@ -67,10 +55,9 @@ sub load_hash_add { } sub load { - my $self = shift; - my $filename; + my ($self, $filename) = @_; - if(@_) { $filename = shift; } else { $filename = $self->filename; } + $filename = $self->filename if not defined $filename; if(not defined $filename) { Carp::carp "No $self->{name} filename specified -- skipping loading from file"; @@ -93,19 +80,13 @@ sub load { if($line =~ /^\[(.*)\]$/) { $primary_index_key = $1; - - if(exists $self->hash->{$primary_index_key} and $primary_index_key ne '.*') { - Carp::croak "Duplicate primary_index_key '$primary_index_key' at line $i of $filename\n"; - } - - $self->hash->{$primary_index_key} = {}; next; } if($line =~ /^<(.*)>$/) { $secondary_index_key = $1; - if(exists $self->hash->{$primary_index_key}->{$secondary_index_key}) { + if(not $self->{ignore_duplicates} and exists $self->hash->{$primary_index_key}->{$secondary_index_key}) { Carp::croak "Duplicate secondary_index_key '$secondary_index_key' at line $i of $filename\n"; } @@ -237,12 +218,12 @@ sub levenshtein_matches { } sub set { - my ($self, $primary_index_key, $secondary_index_key, $key, $value) = @_; + my ($self, $primary_index_key, $secondary_index_key, $key, $value, $dont_save) = @_; my $primary = $self->find_index($primary_index_key); if(not $primary) { - my $result = "No such $self->{name} object group '$primary_index_key'; similiar matches: "; + my $result = "No such $self->{name} object [$primary_index_key]; similiar matches: "; $result .= $self->levenshtein_matches($primary_index_key); return $result; } @@ -250,13 +231,13 @@ sub set { my $secondary = $self->find_index($primary, $secondary_index_key); if(not $secondary) { - my $result = "No such $self->{name} object '$secondary_index_key'; similiar matches: "; + my $result = "No such $self->{name} object [$primary_index_key] $secondary_index_key; similiar matches: "; $result .= $self->levenshtein_matches($primary, $secondary_index_key); return $result; } if(not defined $key) { - my $result = "[$self->{name}] (" . ($primary eq '.*' ? 'global' : $primary) . ") $secondary keys: "; + my $result = "[" . ($primary eq '.*' ? 'global' : $primary) . "] $secondary keys: "; my $comma = ''; foreach my $key (sort keys %{ $self->hash->{$primary}->{$secondary} }) { $result .= $comma . "$key => " . $self->hash->{$primary}->{$secondary}->{$key}; @@ -270,11 +251,11 @@ sub set { $value = $self->hash->{$primary}->{$secondary}->{$key}; } else { $self->hash->{$primary}->{$secondary}->{$key} = $value; - $self->save(); + $self->save unless $dont_save; } $primary = 'global' if $primary eq '.*'; - return "[$self->{name}] ($primary) $secondary: '$key' " . (defined $value ? "set to '$value'" : "is not set."); + return "[$primary] $secondary: '$key' " . (defined $value ? "set to '$value'" : "is not set."); } sub unset { diff --git a/PBot/FactoidCommands.pm b/PBot/FactoidCommands.pm index b79f2d61..ec148919 100644 --- a/PBot/FactoidCommands.pm +++ b/PBot/FactoidCommands.pm @@ -8,9 +8,6 @@ package PBot::FactoidCommands; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Carp (); use Time::Duration; use Time::HiRes qw(gettimeofday); @@ -87,7 +84,7 @@ sub call_factoid { my ($chan, $keyword, $args) = split / /, $arguments, 3; if(not defined $chan or not defined $keyword) { - return "Usage: !fact [arguments]"; + return "Usage: fact [arguments]"; } my ($channel, $trigger) = $self->{pbot}->factoids->find_factoid($chan, $keyword, $args, 1); @@ -105,7 +102,7 @@ sub factset { my ($channel, $trigger, $key, $value) = split / /, $arguments, 4 if defined $arguments; if(not defined $channel or not defined $trigger) { - return "Usage: factset [key ]" + return "Usage: factset [key [value]]"; } my $admininfo = $self->{pbot}->admins->loggedin($from, "$nick!$user\@$host"); @@ -194,13 +191,14 @@ sub factunset { sub list { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; - my $botnick = $self->{pbot}->botnick; my $text; if(not defined $arguments) { return "/msg $nick Usage: list "; } + # TODO - update this to use new MessageHistory API +=cut if($arguments =~/^messages\s+(.*)$/) { my ($mask_search, $channel_search, $text_search) = split / /, $1; @@ -215,12 +213,12 @@ sub list { $nickserv = $self->{pbot}->antiflood->message_history->{$history_mask}->{nickserv_account} if exists $self->{pbot}->antiflood->message_history->{$history_mask}->{nickserv_account}; if($history_mask =~ m/$mask_search/i) { + my $bot_trigger = $self->{pbot}->{registry}->get_value('general', 'trigger'); foreach my $history_channel (keys %{ $self->{pbot}->antiflood->message_history->{$history_mask}->{channels} }) { if($history_channel =~ m/$channel_search/i) { my @messages = @{ $self->{pbot}->antiflood->message_history->{$history_mask}->{channels}->{$history_channel}{messages} }; - for(my $i = 0; $i <= $#messages; $i++) { - next if $messages[$i]->{msg} =~ /^\Q$self->{pbot}->{trigger}\E?login/; # don't reveal login passwords + next if $messages[$i]->{msg} =~ /^\Q$bot_trigger\E?login/; # don't reveal login passwords print "$history_mask, $history_channel\n"; print "joinwatch: ", $self->{pbot}->antiflood->message_history->{$history_mask}->{channels}->{$history_channel}{join_watch}, "\n"; @@ -264,6 +262,7 @@ sub list { $self->{pbot}->logger->log($text); return "Messages:\n\n$text"; } +=cut if($arguments =~ /^modules$/i) { $from = '.*' if not defined $from or $from !~ /^#/; diff --git a/PBot/FactoidModuleLauncher.pm b/PBot/FactoidModuleLauncher.pm index 07044975..225344f5 100644 --- a/PBot/FactoidModuleLauncher.pm +++ b/PBot/FactoidModuleLauncher.pm @@ -8,9 +8,6 @@ package PBot::FactoidModuleLauncher; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use POSIX qw(WNOHANG); # for children process reaping use Carp (); use Text::Balanced qw(extract_delimited); @@ -55,7 +52,7 @@ sub execute_module { } my $module = $self->{pbot}->factoids->factoids->hash->{$channel}->{$trigger}->{action}; - my $module_dir = $self->{pbot}->module_dir; + my $module_dir = $self->{pbot}->{registry}->get_value('general', 'module_dir'); $self->{pbot}->logger->log("(" . (defined $from ? $from : "(undef)") . "): $nick!$user\@$host: Executing module $module $arguments\n"); @@ -187,8 +184,8 @@ sub module_pipe_reader { my ($self, $buf) = @_; my ($channel, $text) = split / /, $buf, 2; return if not defined $text or not length $text; - $text = $self->{pbot}->interpreter->truncate_result($channel, $self->{pbot}->{botnick}, 'undef', $text, $text, 0); - $self->{pbot}->antiflood->check_flood($channel, $self->{pbot}->{botnick}, $self->{pbot}->{username}, 'localhost', $text, 0, 0, 0); + $text = $self->{pbot}->interpreter->truncate_result($channel, $self->{pbot}->{registry}->get_value('irc', 'botnick'), 'undef', $text, $text, 0); + $self->{pbot}->antiflood->check_flood($channel, $self->{pbot}->{registry}->get_value('irc', 'botnick'), $self->{pbot}->{registry}->get_value('irc', 'username'), 'localhost', $text, 0, 0, 0); } 1; diff --git a/PBot/Factoids.pm b/PBot/Factoids.pm index 8d6a8e1d..055ea5cf 100644 --- a/PBot/Factoids.pm +++ b/PBot/Factoids.pm @@ -8,14 +8,12 @@ package PBot::Factoids; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use HTML::Entities; use Time::HiRes qw(gettimeofday); use Carp (); use POSIX qw(strftime); +use PBot::PBot qw($VERSION); use PBot::FactoidModuleLauncher; use PBot::DualIndexHashObject; @@ -48,6 +46,9 @@ sub initialize { $self->{factoidmodulelauncher} = PBot::FactoidModuleLauncher->new(pbot => $pbot); $self->{pbot}->{atexit}->register(sub { $self->save_factoids; return; }); + + $self->load_factoids; + $self->add_factoid('text', '.*', $self->{pbot}->{registry}->get_value('irc', 'botnick'), 'version', "/say $VERSION", 1); } sub load_factoids { @@ -80,7 +81,7 @@ sub save_factoids { sub add_factoid { my $self = shift; - my ($type, $channel, $owner, $trigger, $action) = @_; + my ($type, $channel, $owner, $trigger, $action, $dont_save) = @_; $type = lc $type; $channel = lc $channel; @@ -94,7 +95,7 @@ sub add_factoid { $self->factoids->hash->{$channel}->{$trigger}->{ref_user} = "nobody"; $self->factoids->hash->{$channel}->{$trigger}->{rate_limit} = 15; - $self->save_factoids; + $self->save_factoids unless $dont_save; } sub remove_factoid { @@ -104,6 +105,11 @@ sub remove_factoid { $channel = lc $channel; delete $self->factoids->hash->{$channel}->{$trigger}; + + if(not scalar keys $self->factoids->hash->{$channel}) { + delete $self->factoids->hash->{$channel}; + } + $self->save_factoids; } @@ -449,7 +455,7 @@ sub interpreter { if(defined $tonick) { # !tell foo about bar $self->{pbot}->logger->log("($from): $nick!$user\@$host) sent to $tonick\n"); - my $botnick = $self->{pbot}->botnick; + my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick'); # get rid of original caller's nick $result =~ s/^\/([^ ]+) \Q$nick\E:\s+/\/$1 /; diff --git a/PBot/HashObject.pm b/PBot/HashObject.pm index a978c35a..20d12309 100644 --- a/PBot/HashObject.pm +++ b/PBot/HashObject.pm @@ -9,9 +9,6 @@ package PBot::HashObject; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Text::Levenshtein qw(fastdistance); use Carp (); diff --git a/PBot/IRCHandlers.pm b/PBot/IRCHandlers.pm index 3916f225..e6159c94 100644 --- a/PBot/IRCHandlers.pm +++ b/PBot/IRCHandlers.pm @@ -8,9 +8,6 @@ package PBot::IRCHandlers; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Carp(); use Time::HiRes qw(gettimeofday); @@ -70,15 +67,16 @@ sub on_public { my $host = $event->host; my $text = $event->{args}[0]; - $self->pbot->interpreter->process_line($from, $nick, $user, $host, $text); + $self->{pbot}->interpreter->process_line($from, $nick, $user, $host, $text); } sub on_msg { my ($self, $conn, $event) = @_; my ($nick, $host) = ($event->nick, $event->host); my $text = $event->{args}[0]; + my $bot_trigger = $self->{pbot}->{registry}->get_value('general', 'trigger'); - $text =~ s/^\Q$self->{pbot}->{trigger}\E?(.*)/$self->{pbot}->{trigger}$1/; + $text =~ s/^\Q$bot_trigger\E?(.*)/$bot_trigger$1/; $event->{to}[0] = $nick; $event->{args}[0] = $text; $self->on_public($conn, $event); @@ -93,7 +91,7 @@ sub on_notice { if($nick eq "NickServ" && $text =~ m/This nickname is registered/) { $self->{pbot}->logger->log("Identifying with NickServ . . .\n"); - $conn->privmsg("nickserv", "identify " . $self->pbot->identify_password); + $conn->privmsg("nickserv", "identify " . $self->{pbot}->{registry}->get_value('irc', 'identify_password')); } if($nick eq "NickServ" && $text =~ m/You are now identified/) { @@ -143,7 +141,7 @@ sub on_mode { $self->{pbot}->bantracker->track_mode("$nick!$user\@$host", $mode, $target, $channel); } - if(defined $target && $target eq $self->{pbot}->botnick) { # bot targeted + if(defined $target && $target eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { # bot targeted if($mode eq "+o") { $self->{pbot}->logger->log("$nick opped me in $channel\n"); $self->{pbot}->chanops->{is_opped}->{$channel}{timeout} = gettimeofday + 300; # 5 minutes @@ -165,7 +163,7 @@ sub on_mode { $self->{pbot}->chanops->{unban_timeout}->save; } } - elsif($mode eq "+e" && $channel eq $self->{pbot}->botnick) { + elsif($mode eq "+e" && $channel eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { foreach my $chan (keys %{ $self->{pbot}->channels->channels->hash }) { if($self->channels->channels->hash->{$chan}{enabled}) { $self->{pbot}->logger->log("Joining channel: $chan\n"); @@ -185,7 +183,7 @@ sub on_join { my $message_account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host); $self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "JOIN", $self->{pbot}->{messagehistory}->{MSG_JOIN}); - $self->{pbot}->antiflood->check_flood($channel, $nick, $user, $host, "JOIN", 4, 60 * 30, $self->{pbot}->{messagehistory}->{MSG_JOIN}); + $self->{pbot}->antiflood->check_flood($channel, $nick, $user, $host, "JOIN", $self->{pbot}->{registry}->get_value('antiflood', 'max_join_flood'), 60 * 30, $self->{pbot}->{messagehistory}->{MSG_JOIN}); } sub on_kick { @@ -203,7 +201,7 @@ sub on_kick { my $text = "KICKED by $nick!$user\@$host ($reason)"; $self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, $text, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}); - $self->{pbot}->antiflood->check_flood($channel, $target_nick, $target_user, $target_host, $text, 4, 60 * 30, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}); + $self->{pbot}->antiflood->check_flood($channel, $target_nick, $target_user, $target_host, $text, $self->{pbot}->{registry}->get_value('antiflood', 'max_join_flood'), 60 * 30, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}); } } @@ -227,7 +225,7 @@ sub on_departure { $self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, $text, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}); } - $self->{pbot}->antiflood->check_flood($channel, $nick, $user, $host, $text, 4, 60 * 30, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}); + $self->{pbot}->antiflood->check_flood($channel, $nick, $user, $host, $text, $self->{pbot}->{registry}->get_value('antiflood', 'max_join_flood'), 60 * 30, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}); my $admin = $self->{pbot}->admins->find_admin($channel, "$nick!$user\@$host"); if(defined $admin and $admin->{loggedin}) { @@ -255,12 +253,7 @@ sub on_nickchange { $self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($newnick_account); $self->{pbot}->{messagehistory}->{database}->update_hostmask_data($newnick_account, { last_seen => scalar gettimeofday }); - $self->{pbot}->antiflood->check_flood("$nick!$user\@$host", $nick, $user, $host, "NICKCHANGE $newnick", 3, 60 * 30, $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}); -} - -sub pbot { - my $self = shift; - return $self->{pbot}; + $self->{pbot}->antiflood->check_flood("$nick!$user\@$host", $nick, $user, $host, "NICKCHANGE $newnick", $self->{pbot}->{registry}->get_value('antiflood', 'max_nick_flood'), 60 * 30, $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}); } 1; diff --git a/PBot/IgnoreList.pm b/PBot/IgnoreList.pm index cd587b27..4c693857 100644 --- a/PBot/IgnoreList.pm +++ b/PBot/IgnoreList.pm @@ -8,9 +8,6 @@ package PBot::IgnoreList; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Time::HiRes qw(gettimeofday); sub new { @@ -28,20 +25,16 @@ sub new { sub initialize { my ($self, %conf) = @_; - my $pbot = delete $conf{pbot}; - if(not defined $pbot) { - Carp::croak("Missing pbot reference to Channels"); - } + $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to Channels"); + $self->{filename} = delete $conf{filename}; - my $filename = delete $conf{filename}; - - $self->{pbot} = $pbot; $self->{ignore_list} = {}; $self->{ignore_flood_counter} = {}; $self->{last_timestamp} = {}; - $self->{filename} = $filename; - $pbot->timer->register(sub { $self->check_ignore_timeouts }, 10); + $self->load_ignores; + + $self->{pbot}->timer->register(sub { $self->check_ignore_timeouts }, 10); } sub add { diff --git a/PBot/IgnoreListCommands.pm b/PBot/IgnoreListCommands.pm index 35a3d40e..7180533f 100644 --- a/PBot/IgnoreListCommands.pm +++ b/PBot/IgnoreListCommands.pm @@ -8,9 +8,6 @@ package PBot::IgnoreListCommands; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Time::HiRes qw(gettimeofday); use Carp (); diff --git a/PBot/Interpreter.pm b/PBot/Interpreter.pm index 46ec7c7e..1aff6b8a 100644 --- a/PBot/Interpreter.pm +++ b/PBot/Interpreter.pm @@ -13,9 +13,6 @@ use base 'PBot::Registerable'; use LWP::UserAgent; use Carp (); -use vars qw($VERSION); -$VERSION = '1.0.0'; - sub new { if(ref($_[1]) eq 'HASH') { Carp::croak("Options to Interpreter should be key/value pairs, not hash reference"); @@ -91,7 +88,7 @@ sub process_line { my $has_url; my $has_code; my $nick_override; - my $mynick = $self->pbot->botnick; + my $mynick = $self->{pbot}->{registry}->get_value('irc', 'botnick'); $from = lc $from if defined $from; @@ -100,7 +97,7 @@ sub process_line { my $message_account = $pbot->{messagehistory}->get_message_account($nick, $user, $host); $pbot->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $from, $text, $pbot->{messagehistory}->{MSG_CHAT}); - $pbot->antiflood->check_flood($from, $nick, $user, $host, $text, $pbot->{MAX_FLOOD_MESSAGES}, 10, $pbot->{messagehistory}->{MSG_CHAT}) if defined $from; + $pbot->antiflood->check_flood($from, $nick, $user, $host, $text, $pbot->{registry}->get_value('antiflood', 'max_chat_flood'), 10, $pbot->{messagehistory}->{MSG_CHAT}) if defined $from; $text =~ s/^\s+//; $text =~ s/\s+$//; @@ -109,10 +106,12 @@ sub process_line { my $cmd_text = $text; $cmd_text =~ s/^\/me\s+//; - if($cmd_text =~ /^$pbot->{trigger}?\s*{\s*(.*)\s*}\s*$/) { + my $bot_trigger = $pbot->{registry}->get_value('general', 'trigger'); + + if($cmd_text =~ /^$bot_trigger?\s*{\s*(.*)\s*}\s*$/) { $has_code = $1 if length $1; $preserve_whitespace = 1; - } elsif($cmd_text =~ /^\Q$pbot->{trigger}\E(.*)$/) { + } elsif($cmd_text =~ /^\Q$bot_trigger\E(.*)$/) { $command = $1; } elsif($cmd_text =~ /^.?$mynick.?\s+(.*?)$/i) { $command = $1; @@ -147,8 +146,9 @@ sub process_line { sub truncate_result { my ($self, $from, $nick, $text, $original_result, $result, $paste) = @_; + my $max_msg_len = $self->{pbot}->{registry}->get_value('irc', 'max_msg_len'); - if(length $result > $self->{pbot}->max_msg_len) { + if(length $result > $max_msg_len) { my $link; if($paste) { $link = paste_sprunge("[" . (defined $from ? $from : "stdin") . "] <$nick> $text\n\n$original_result"); @@ -159,7 +159,7 @@ sub truncate_result { my $trunc = "... [truncated; see $link for full text.]"; $self->{pbot}->logger->log("Message truncated -- pasted to $link\n") if $paste; - my $trunc_len = length $result < $self->{pbot}->max_msg_len ? length $result : $self->{pbot}->max_msg_len; + my $trunc_len = length $result < $max_msg_len ? length $result : $max_msg_len; $result = substr($result, 0, $trunc_len); substr($result, $trunc_len - length $trunc) = $trunc; } @@ -169,7 +169,7 @@ sub truncate_result { sub handle_result { my ($self, $from, $nick, $user, $host, $text, $command, $result, $checkflood, $preserve_whitespace) = @_; - my ($pbot, $mynick) = ($self->{pbot}, $self->{pbot}->{botnick}); + my ($pbot, $mynick) = ($self->{pbot}, $self->{pbot}->{registry}->get_value('irc', 'botnick')); if(not defined $result or length $result == 0) { return; @@ -194,10 +194,10 @@ sub handle_result { if($result =~ s/^\/say\s+//i) { $pbot->conn->privmsg($from, $result) if defined $from && $from !~ /\Q$mynick\E/i; - $pbot->antiflood->check_flood($from, $pbot->{botnick}, $pbot->{username}, 'localhost', $result, 0, 0, 0) if $checkflood; + $pbot->antiflood->check_flood($from, $mynick, $pbot->{registry}->get_value('irc', 'username'), 'localhost', $result, 0, 0, 0) if $checkflood; } elsif($result =~ s/^\/me\s+//i) { $pbot->conn->me($from, $result) if defined $from && $from !~ /\Q$mynick\E/i; - $pbot->antiflood->check_flood($from, $pbot->{botnick}, $pbot->{username}, 'localhost', '/me ' . $result, 0, 0, 0) if $checkflood; + $pbot->antiflood->check_flood($from, $mynick, $pbot->{registry}->get_value('irc', 'username'), 'localhost', '/me ' . $result, 0, 0, 0) if $checkflood; } elsif($result =~ s/^\/msg\s+([^\s]+)\s+//i) { my $to = $1; if($to =~ /,/) { @@ -208,15 +208,15 @@ sub handle_result { } elsif($result =~ s/^\/me\s+//i) { $pbot->conn->me($to, $result) if $to !~ /\Q$mynick\E/i; - $pbot->antiflood->check_flood($to, $pbot->{botnick}, $pbot->{username}, 'localhost', '/me ' . $result, 0, 0, 0) if $checkflood; + $pbot->antiflood->check_flood($to, $mynick, $pbot->{registry}->get_value('irc', 'username'), 'localhost', '/me ' . $result, 0, 0, 0) if $checkflood; } else { $result =~ s/^\/say\s+//i; $pbot->conn->privmsg($to, $result) if $to !~ /\Q$mynick\E/i; - $pbot->antiflood->check_flood($to, $pbot->{botnick}, $pbot->{username}, 'localhost', $result, 0, 0, 0) if $checkflood; + $pbot->antiflood->check_flood($to, $mynick, $pbot->{registry}->get_value('irc', 'username'), 'localhost', $result, 0, 0, 0) if $checkflood; } } else { $pbot->conn->privmsg($from, $result) if defined $from && $from !~ /\Q$mynick\E/i; - $pbot->antiflood->check_flood($from, $pbot->{botnick}, $pbot->{username}, 'localhost', $result, 0, 0, 0) if $checkflood; + $pbot->antiflood->check_flood($from, $mynick, $pbot->{registry}->get_value('irc', 'username'), 'localhost', $result, 0, 0, 0) if $checkflood; } $pbot->logger->log("---------------------------------------------\n"); } diff --git a/PBot/LagChecker.pm b/PBot/LagChecker.pm index 83063c56..1a9c8ac5 100644 --- a/PBot/LagChecker.pm +++ b/PBot/LagChecker.pm @@ -11,9 +11,6 @@ use strict; use feature 'switch'; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use Time::HiRes qw(gettimeofday tv_interval); use Time::Duration; use Carp (); diff --git a/PBot/Logger.pm b/PBot/Logger.pm index ac0f2c29..83fd7a33 100644 --- a/PBot/Logger.pm +++ b/PBot/Logger.pm @@ -3,9 +3,6 @@ package PBot::Logger; use warnings; use strict; -use vars qw($VERSION); -$VERSION = '1.0.0'; - use Carp (); sub new { diff --git a/PBot/MessageHistory.pm b/PBot/MessageHistory.pm index 0c91f2b9..f9424af3 100644 --- a/PBot/MessageHistory.pm +++ b/PBot/MessageHistory.pm @@ -33,7 +33,7 @@ sub new { sub initialize { my ($self, %conf) = @_; $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - $self->{filename} = delete $conf{filename} // $self->{pbot}->{data_dir} . '/message_history.sqlite3'; + $self->{filename} = delete $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3'; $self->{database} = PBot::MessageHistory_SQLite->new(pbot => $self->{pbot}, filename => $self->{filename}); $self->{database}->begin(); diff --git a/PBot/MessageHistory_SQLite.pm b/PBot/MessageHistory_SQLite.pm index 40050a1d..1db96891 100644 --- a/PBot/MessageHistory_SQLite.pm +++ b/PBot/MessageHistory_SQLite.pm @@ -27,7 +27,7 @@ sub initialize { my ($self, %conf) = @_; $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference in " . __FILE__); - $self->{filename} = delete $conf{filename} // $self->{pbot}->{data_dir} . '/message_history.sqlite3'; + $self->{filename} = delete $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3'; $self->{pbot}->timer->register(sub { $self->commit_message_history }, 5); $self->{new_entries} = 0; @@ -433,8 +433,10 @@ sub recall_message_by_count { $self->{pbot}->logger->log($@) if $@; if(defined $ignore_command) { + my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick'); + my $bot_trigger = $self->{pbot}->{registry}->get_value('general', 'trigger'); foreach my $message (@$messages) { - next if $message->{msg} =~ m/^$self->{pbot}->{botnick}. $ignore_command/ or $message->{msg} =~ m/^$self->{pbot}->{trigger}$ignore_command/; + next if $message->{msg} =~ m/^$botnick. $ignore_command/ or $message->{msg} =~ m/^$bot_trigger$ignore_command/; return $message; } return undef; @@ -474,8 +476,10 @@ sub recall_message_by_text { $self->{pbot}->logger->log($@) if $@; if(defined $ignore_command) { + my $bot_trigger = $self->{pbot}->{registry}->get_value('general', 'trigger'); + my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick'); foreach my $message (@$messages) { - next if $message->{msg} =~ m/^$self->{pbot}->{botnick}. $ignore_command/ or $message->{msg} =~ m/^$self->{pbot}->{trigger}$ignore_command/; + next if $message->{msg} =~ m/^$botnick. $ignore_command/ or $message->{msg} =~ m/^$bot_trigger$ignore_command/; return $message; } return undef; diff --git a/PBot/PBot.pm b/PBot/PBot.pm index 4b88a585..fecb0eec 100644 --- a/PBot/PBot.pm +++ b/PBot/PBot.pm @@ -12,8 +12,14 @@ use warnings; use PBot::VERSION; -use vars qw($VERSION); -$VERSION = PBot::VERSION::BUILD_NAME . " revision " . PBot::VERSION::BUILD_REVISION . " " . PBot::VERSION::BUILD_DATE; +BEGIN { + use Exporter; + our @ISA = 'Exporter'; + our @EXPORT = qw($VERSION); + + our $VERSION = PBot::VERSION::BUILD_NAME . " revision " . PBot::VERSION::BUILD_REVISION . " " . PBot::VERSION::BUILD_DATE; + print "PBot version $VERSION\n"; +} # unbuffer stdout STDOUT->autoflush(1); @@ -21,6 +27,8 @@ STDOUT->autoflush(1); use Carp (); use PBot::Logger; +use PBot::Registry; + use PBot::SelectHandler; use PBot::StdinReader; @@ -69,73 +77,70 @@ sub new { sub initialize { my ($self, %conf) = @_; - my $log_file = delete $conf{log_file}; + # logger created first to allow other modules to log things + $self->{logger} = PBot::Logger->new(log_file => delete $conf{log_file}); - $self->{config_dir} = delete $conf{config_dir} // "$ENV{HOME}/pbot/config"; - $self->{data_dir} = delete $conf{data_dir} // "$ENV{HOME}/pbot/data"; - $self->{module_dir} = delete $conf{module_dir} // "$ENV{HOME}/pbot/modules"; + $self->{atexit} = PBot::Registerable->new(); + $self->{timer} = PBot::Timer->new(timeout => 10); + $self->{commands} = PBot::Commands->new(pbot => $self); - $self->{ircserver} = delete $conf{ircserver} // "irc.freenode.net"; - $self->{port} = delete $conf{port} // 6667; - $self->{SSL} = delete $conf{SSL} // 0; - $self->{SSL_ca_file} = delete $conf{SSL_ca_file} // undef; - $self->{SSL_ca_path} = delete $conf{SSL_ca_path} // undef; - $self->{botnick} = delete $conf{botnick} // "pbot3"; - $self->{username} = delete $conf{username} // "pbot3"; - $self->{ircname} = delete $conf{ircname} // "http://code.google.com/p/pbot2-pl/"; - $self->{identify_password} = delete $conf{identify_password} // ""; + my $config_dir = delete $conf{config_dir} // "$ENV{HOME}/pbot/config"; - $self->{max_msg_len} = delete $conf{max_msg_len} // 425; - $self->{MAX_FLOOD_MESSAGES} = delete $conf{MAX_FLOOD_MESSAGES} // 4; - $self->{MAX_NICK_MESSAGES} = delete $conf{MAX_NICK_MESSAGES} // 32; + # registry created, but not yet loaded, to allow modules to create default values and triggers + $self->{registry} = PBot::Registry->new(pbot => $self, filename => delete $conf{registry_file} // "$config_dir/registry"); - $self->{trigger} = delete $conf{trigger} // '!'; + $self->{registry}->add_default('text', 'general', 'config_dir', $config_dir); + $self->{registry}->add_default('text', 'general', 'data_dir', delete $conf{data_dir} // "$ENV{HOME}/pbot/data"); + $self->{registry}->add_default('text', 'general', 'module_dir', delete $conf{module_dir} // "$ENV{HOME}/pbot/modules"); + $self->{registry}->add_default('text', 'general', 'trigger', delete $conf{trigger} // '!'); - my $messagehistory_file = delete $conf{message_history_file}; - my $channels_file = delete $conf{channels_file}; - my $admins_file = delete $conf{admins_file}; - my $ignorelist_file = delete $conf{ignorelist_file}; + $self->{registry}->add_default('text', 'irc', 'max_msg_len', delete $conf{max_msg_len} // 425); + $self->{registry}->add_default('text', 'irc', 'ircserver', delete $conf{ircserver} // "irc.freenode.net"); + $self->{registry}->add_default('text', 'irc', 'port', delete $conf{port} // 6667); + $self->{registry}->add_default('text', 'irc', 'SSL', delete $conf{SSL} // 0); + $self->{registry}->add_default('text', 'irc', 'SSL_ca_file', delete $conf{SSL_ca_file} // 'none'); + $self->{registry}->set('irc', 'SSL_ca_file', 'private', 1); + $self->{registry}->add_default('text', 'irc', 'SSL_ca_path', delete $conf{SSL_ca_path} // 'none'); + $self->{registry}->set('irc', 'SSL_ca_path', 'private', 1); + $self->{registry}->add_default('text', 'irc', 'botnick', delete $conf{botnick} // "pbot3"); + $self->{registry}->add_default('text', 'irc', 'username', delete $conf{username} // "pbot3"); + $self->{registry}->add_default('text', 'irc', 'ircname', delete $conf{ircname} // "http://code.google.com/p/pbot2-pl/"); + $self->{registry}->add_default('text', 'irc', 'identify_password', delete $conf{identify_password} // ""); + $self->{registry}->set('irc', 'identify_password', 'private', 1); - my $factoids_file = delete $conf{factoids_file}; - my $export_factoids_path = delete $conf{export_factoids_path}; - my $export_factoids_site = delete $conf{export_factoids_site}; + $self->{registry}->add_trigger('irc', 'botnick', sub { $self->change_botnick_trigger(@_) }); - my $quotegrabs_file = delete $conf{quotegrabs_file}; - my $export_quotegrabs_path = delete $conf{export_quotegrabs_path}; - my $export_quotegrabs_site = delete $conf{export_quotegrabs_site}; + $self->{registry}->add_default('text', 'antiflood', 'max_join_flood', delete $conf{max_join_flood} // 4); + $self->{registry}->add_default('text', 'antiflood', 'max_chat_flood', delete $conf{max_chat_flood} // 4); + $self->{registry}->add_default('text', 'antiflood', 'max_enter_flood', delete $conf{max_enter_flood} // 4); + $self->{registry}->add_default('text', 'antiflood', 'max_nick_flood', delete $conf{max_nick_flood} // 3); - $self->{logger} = PBot::Logger->new(log_file => $log_file); - $self->{commands} = PBot::Commands->new(pbot => $self); - $self->{timer} = PBot::Timer->new(timeout => 10); - - $self->{atexit} = PBot::Registerable->new(); + $self->{registry}->add_default('text', 'antiflood', 'enter_abuse_max_lines', delete $conf{enter_abuse_max_lines} // 4); + $self->{registry}->add_default('text', 'antiflood', 'enter_abuse_max_seconds', delete $conf{enter_abuse_max_seconds} // 20); + $self->{registry}->add_default('text', 'antiflood', 'enter_abuse_max_offenses', delete $conf{enter_abuse_max_offenses} // 3); + $self->{registry}->add_default('text', 'messagehistory', 'max_messages', delete $conf{max_messages} // 32); + $self->{select_handler} = PBot::SelectHandler->new(pbot => $self); $self->{stdin_reader} = PBot::StdinReader->new(pbot => $self); - $self->{admins} = PBot::BotAdmins->new(pbot => $self, filename => $admins_file); + $self->{admins} = PBot::BotAdmins->new(pbot => $self, filename => delete $conf{admins_file}); $self->admins->load_admins(); - $self->admins->add_admin($self->{botnick}, '.*', "$self->{botnick}!stdin\@localhost", 60, 'admin'); - $self->admins->login($self->{botnick}, "$self->{botnick}!stdin\@localhost", 'admin'); $self->{factoids} = PBot::Factoids->new( pbot => $self, - filename => $factoids_file, - export_path => $export_factoids_path, - export_site => $export_factoids_site, + filename => delete $conf{factoids_file}, + export_path => delete $conf{export_factoids_path}, + export_site => delete $conf{export_factoids_site}, ); - $self->factoids->load_factoids() if defined $factoids_file; - $self->factoids->add_factoid('text', '.*', $self->{botnick}, 'version', "/say $VERSION"); - $self->{bantracker} = PBot::BanTracker->new(pbot => $self); $self->{lagchecker} = PBot::LagChecker->new(pbot => $self); - $self->{messagehistory} = PBot::MessageHistory->new(pbot => $self, filename => $messagehistory_file); + $self->{messagehistory} = PBot::MessageHistory->new(pbot => $self, filename => delete $conf{messagehistory_file}); $self->{antiflood} = PBot::AntiFlood->new(pbot => $self); - $self->{ignorelist} = PBot::IgnoreList->new(pbot => $self, filename => $ignorelist_file); - $self->{ignorelist}->load_ignores() if defined $ignorelist_file; + $self->{ignorelist} = PBot::IgnoreList->new(pbot => $self, filename => delete $conf{ignorelist_file}); $self->interpreter(PBot::Interpreter->new(pbot => $self)); $self->interpreter->register(sub { return $self->commands->interpreter(@_); }); @@ -148,21 +153,27 @@ sub initialize { $self->{irc} = PBot::IRC->new(); $self->{irchandlers} = PBot::IRCHandlers->new(pbot => $self); - $self->{channels} = PBot::Channels->new(pbot => $self, filename => $channels_file); - $self->channels->load_channels() if defined $channels_file; + $self->{channels} = PBot::Channels->new(pbot => $self, filename => delete $conf{channels_file}); $self->{chanops} = PBot::ChanOps->new(pbot => $self); $self->{chanopcmds} = PBot::ChanOpCommands->new(pbot => $self); - $self->{chanops}->{unban_timeout}->load; - $self->{quotegrabs} = PBot::Quotegrabs->new( pbot => $self, - filename => $quotegrabs_file, - export_path => $export_quotegrabs_path, - export_site => $export_quotegrabs_site, + filename => delete $conf{quotegrabs_file}, + export_path => delete $conf{export_quotegrabs_path}, + export_site => delete $conf{export_quotegrabs_site}, ); + # load registry entries from file to overwrite defaults + $self->{registry}->load; + + # create implicit bot-admin account for bot + my $botnick = $self->{registry}->get_value('irc', 'botnick'); + $self->admins->add_admin($botnick, '.*', "$botnick!stdin\@localhost", 60, 'admin', 1); + $self->admins->login($botnick, "$botnick!stdin\@localhost", 'admin'); + + # start timer $self->timer->start(); } @@ -171,47 +182,47 @@ sub initialize { sub connect { my ($self, $server) = @_; - $server = $self->ircserver if not defined $server; - if($self->{connected}) { # TODO: disconnect, clean-up, etc } + $server = $self->{registry}->get_value('irc', 'ircserver') if not defined $server; + $self->logger->log("Connecting to $server ...\n"); $self->conn($self->irc->newconn( - Nick => $self->{botnick}, - Username => $self->{username}, - Ircname => $self->{ircname}, + Nick => $self->{registry}->get_value('irc', 'botnick'), + Username => $self->{registry}->get_value('irc', 'username'), + Ircname => $self->{registry}->get_value('irc', 'ircname'), Server => $server, - SSL => $self->{SSL}, - SSL_ca_file => $self->{SSL_ca_file}, - SSL_ca_path => $self->{SSL_ca_path}, - Port => $self->{port})) + SSL => $self->{registry}->get_value('irc', 'SSL'), + SSL_ca_file => $self->{registry}->get_value('irc', 'SSL_ca_file'), + SSL_ca_path => $self->{registry}->get_value('irc', 'SSL_ca_path'), + Port => $self->{registry}->get_value('irc', 'port'))) or Carp::croak "$0: Can't connect to IRC server.\n"; $self->{connected} = 1; #set up default handlers for the IRC engine - $self->conn->add_handler([ 251,252,253,254,302,255 ], sub { $self->irchandlers->on_init(@_) }); - $self->conn->add_handler(376 , sub { $self->irchandlers->on_connect(@_) }); - $self->conn->add_handler('disconnect' , sub { $self->irchandlers->on_disconnect(@_) }); - $self->conn->add_handler('notice' , sub { $self->irchandlers->on_notice(@_) }); - $self->conn->add_handler('caction' , sub { $self->irchandlers->on_action(@_) }); - $self->conn->add_handler('public' , sub { $self->irchandlers->on_public(@_) }); - $self->conn->add_handler('msg' , sub { $self->irchandlers->on_msg(@_) }); - $self->conn->add_handler('mode' , sub { $self->irchandlers->on_mode(@_) }); - $self->conn->add_handler('part' , sub { $self->irchandlers->on_departure(@_) }); - $self->conn->add_handler('join' , sub { $self->irchandlers->on_join(@_) }); - $self->conn->add_handler('kick' , sub { $self->irchandlers->on_kick(@_) }); - $self->conn->add_handler('quit' , sub { $self->irchandlers->on_departure(@_) }); - $self->conn->add_handler('nick' , sub { $self->irchandlers->on_nickchange(@_) }); - $self->conn->add_handler('pong' , sub { $self->lagchecker->on_pong(@_) }); - $self->conn->add_handler('whoisaccount' , sub { $self->antiflood->on_whoisaccount(@_) }); + $self->conn->add_handler([ 251,252,253,254,302,255 ], sub { $self->irchandlers->on_init(@_) }); + $self->conn->add_handler(376 , sub { $self->irchandlers->on_connect(@_) }); + $self->conn->add_handler('disconnect' , sub { $self->irchandlers->on_disconnect(@_) }); + $self->conn->add_handler('notice' , sub { $self->irchandlers->on_notice(@_) }); + $self->conn->add_handler('caction' , sub { $self->irchandlers->on_action(@_) }); + $self->conn->add_handler('public' , sub { $self->irchandlers->on_public(@_) }); + $self->conn->add_handler('msg' , sub { $self->irchandlers->on_msg(@_) }); + $self->conn->add_handler('mode' , sub { $self->irchandlers->on_mode(@_) }); + $self->conn->add_handler('part' , sub { $self->irchandlers->on_departure(@_) }); + $self->conn->add_handler('join' , sub { $self->irchandlers->on_join(@_) }); + $self->conn->add_handler('kick' , sub { $self->irchandlers->on_kick(@_) }); + $self->conn->add_handler('quit' , sub { $self->irchandlers->on_departure(@_) }); + $self->conn->add_handler('nick' , sub { $self->irchandlers->on_nickchange(@_) }); + $self->conn->add_handler('pong' , sub { $self->lagchecker->on_pong(@_) }); + $self->conn->add_handler('whoisaccount' , sub { $self->antiflood->on_whoisaccount(@_) }); $self->conn->add_handler('banlist' , sub { $self->bantracker->on_banlist_entry(@_) }); + $self->conn->add_handler('endofnames' , sub { $self->bantracker->get_banlist(@_) }); # freenode quietlist $self->conn->add_handler(728 , sub { $self->bantracker->on_quietlist_entry(@_) }); - $self->conn->add_handler('endofnames' , sub { $self->bantracker->get_banlist(@_) }); } #main loop @@ -228,7 +239,7 @@ sub do_one_loop { sub start { my $self = shift; - if(not defined $self->{connected} or $self->{connected} == 0) { + if(not $self->{connected}) { $self->connect(); } @@ -247,6 +258,14 @@ sub atexit { $self->{atexit}->execute_all; } +sub change_botnick_trigger { + my ($self, $section, $item, $newvalue) = @_; + + if($self->{connected}) { + $self->conn->nick($newvalue); + } +} + #----------------------------------------------------------------------------------- # Getters/Setters #----------------------------------------------------------------------------------- @@ -310,30 +329,6 @@ sub commands { return $self->{commands}; } -sub botnick { - my $self = shift; - if(@_) { $self->{botnick} = shift; } - return $self->{botnick}; -} - -sub identify_password { - my $self = shift; - if(@_) { $self->{identify_password} = shift; } - return $self->{identify_password}; -} - -sub max_msg_len { - my $self = shift; - if(@_) { $self->{max_msg_len} = shift; } - return $self->{max_msg_len}; -} - -sub module_dir { - my $self = shift; - if(@_) { $self->{module_dir} = shift; } - return $self->{module_dir}; -} - sub ignorelist { my $self = shift; if(@_) { $self->{ignorelist} = shift; } @@ -370,10 +365,4 @@ sub chanops { return $self->{chanops}; } -sub ircserver { - my $self = shift; - if(@_) { $self->{ircserver} = shift; } - return $self->{ircserver}; -} - 1; diff --git a/PBot/Quotegrabs.pm b/PBot/Quotegrabs.pm index e36e9fe0..e0ffa799 100644 --- a/PBot/Quotegrabs.pm +++ b/PBot/Quotegrabs.pm @@ -8,9 +8,6 @@ package PBot::Quotegrabs; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use HTML::Entities; use Time::Duration; use Time::HiRes qw(gettimeofday); diff --git a/PBot/Quotegrabs_Hashtable.pm b/PBot/Quotegrabs_Hashtable.pm index 889f26e2..9b97783b 100644 --- a/PBot/Quotegrabs_Hashtable.pm +++ b/PBot/Quotegrabs_Hashtable.pm @@ -8,9 +8,6 @@ package PBot::Quotegrabs_Hashtable; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use HTML::Entities; use Time::Duration; use Time::HiRes qw(gettimeofday); diff --git a/PBot/Quotegrabs_SQLite.pm b/PBot/Quotegrabs_SQLite.pm index 7abf83df..4f4b596d 100644 --- a/PBot/Quotegrabs_SQLite.pm +++ b/PBot/Quotegrabs_SQLite.pm @@ -8,9 +8,6 @@ package PBot::Quotegrabs_SQLite; use warnings; use strict; -use vars qw($VERSION); -$VERSION = $PBot::PBot::VERSION; - use DBI; use Carp qw(shortmess); diff --git a/PBot/Registerable.pm b/PBot/Registerable.pm index 7e8903b4..87d7f0ab 100644 --- a/PBot/Registerable.pm +++ b/PBot/Registerable.pm @@ -8,9 +8,6 @@ package PBot::Registerable; use warnings; use strict; -use vars qw($VERSION); -$VERSION = '1.0.0'; - use Carp (); sub new { diff --git a/PBot/Registry.pm b/PBot/Registry.pm new file mode 100644 index 00000000..4da64024 --- /dev/null +++ b/PBot/Registry.pm @@ -0,0 +1,156 @@ +# File: Registry.pm +# Author: pragma_ +# +# Purpose: Provides a centralized registry of configuration settings that can +# easily be examined and updated via set/unset commands without restarting. + +package PBot::Registry; + +use warnings; +use strict; + +use Time::HiRes qw(gettimeofday); +use Carp (); + +use PBot::DualIndexHashObject; +use PBot::RegistryCommands; + +sub new { + if(ref($_[1]) eq 'HASH') { + Carp::croak("Options to " . __FILE__ . " should be item/value pairs, not hash reference"); + } + + my ($class, %conf) = @_; + my $self = bless {}, $class; + $self->initialize(%conf); + return $self; +} + +sub initialize { + my ($self, %conf) = @_; + + $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + my $filename = delete $conf{filename}; + + $self->{registry} = PBot::DualIndexHashObject->new(name => 'Registry', filename => $filename, ignore_duplicates => 1); + $self->{triggers} = {}; + + $self->{pbot}->{atexit}->register(sub { $self->save; return; }); + + PBot::RegistryCommands->new(pbot => $self->{pbot}); +} + +sub load { + my $self = shift; + + $self->{pbot}->logger->log("Loading registry from " . $self->{registry}->{filename} . " ...\n"); + + $self->{registry}->load; + + foreach my $section (keys %{ $self->{registry}->hash }) { + foreach my $item (keys %{ $self->{registry}->hash->{$section} }) { + $self->process_trigger($section, $item, $self->{registry}->hash->{$section}->{$item}->{value}); + } + } + + $self->{pbot}->logger->log("Done.\n"); +} + +sub save { + my $self = shift; + $self->{registry}->save; +} + +sub add_default { + my ($self, $type, $section, $item, $value) = @_; + $self->add($type, $section, $item, $value, 1); +} + +sub add { + my $self = shift; + my ($type, $section, $item, $value, $is_default) = @_; + + $type = lc $type; + $section = lc $section; + $item = lc $item; + + $self->{registry}->hash->{$section}->{$item}->{value} = $value; + $self->{registry}->hash->{$section}->{$item}->{type} = $type; + + $self->process_trigger($section, $item, $value) unless $is_default; + $self->save_registry unless $is_default; +} + +sub remove { + my $self = shift; + my ($section, $item) = @_; + + $section = lc $section; + + delete $self->{registry}->hash->{$section}->{$item}; + + if(not scalar keys $self->{registry}->hash->{$section}) { + delete $self->{registry}->hash->{$section}; + } + + $self->save_registry; +} + +sub set { + my ($self, $section, $item, $key, $value) = @_; + + $section = lc $section; + $item = lc $item; + $key = lc $key if defined $key; + + my $oldvalue = $self->get_value($section, $item, 1) if defined $value; + $oldvalue = '' if not defined $oldvalue; + + my $result = $self->{registry}->set($section, $item, $key, $value, 1); + + if(defined $key and $key eq 'value' and defined $value and $oldvalue ne $value) { + $self->process_trigger($section, $item, $value); + } + + return $result; +} + +sub unset { + my ($self, $section, $item, $key) = @_; + + $section = lc $section; + $item = lc $item; + $key = lc $key; + + return $self->{registry}->unset($section, $item, $key); +} + +sub get_value { + my ($self, $section, $item, $as_text) = @_; + + if(exists $self->{registry}->hash->{$section} and exists $self->{registry}->hash->{$section}->{$item}) { + if(not $as_text and $self->{registry}->hash->{$section}->{$item}->{type} eq 'array') { + return split /\s*,\s*/, $self->{registry}->hash->{$section}->{$item}->{value}; + } else { + return $self->{registry}->hash->{$section}->{$item}->{value}; + } + } + return undef; +} + +sub add_trigger { + my ($self, $section, $item, $subref) = @_; + $self->{triggers}->{$section}->{$item} = $subref; +} + +sub process_trigger { + my $self = shift; + my ($section, $item) = @_; + + if(exists $self->{triggers}->{$section} and exists $self->{triggers}->{$section}->{$item}) { + return &{ $self->{triggers}->{$section}->{$item} }(@_); + } + return undef; +} + +1; diff --git a/PBot/RegistryCommands.pm b/PBot/RegistryCommands.pm new file mode 100644 index 00000000..6e85dca9 --- /dev/null +++ b/PBot/RegistryCommands.pm @@ -0,0 +1,240 @@ +# File: RegistryCommands.pm +# Author: pragma_ +# +# Purpose: Commands to introspect and update Registry + +package PBot::RegistryCommands; + +use warnings; +use strict; + +use Carp (); + +sub new { + if(ref($_[1]) eq 'HASH') { + Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference"); + } + + my ($class, %conf) = @_; + my $self = bless {}, $class; + $self->initialize(%conf); + return $self; +} + +sub initialize { + my ($self, %conf) = @_; + + my $pbot = delete $conf{pbot} // Carp::croak("Missing pbot reference to FactoidCommands"); + $self->{pbot} = $pbot; + + $pbot->commands->register(sub { return $self->regadd(@_) }, "regadd", 60); + $pbot->commands->register(sub { return $self->regrem(@_) }, "regrem", 60); + $pbot->commands->register(sub { return $self->regshow(@_) }, "regshow", 0); + $pbot->commands->register(sub { return $self->regset(@_) }, "regset", 60); + $pbot->commands->register(sub { return $self->regunset(@_) }, "regunset", 60); + $pbot->commands->register(sub { return $self->regchange(@_) }, "regchange", 60); + $pbot->commands->register(sub { return $self->regfind(@_) }, "regfind", 0); +} + +sub regset { + my $self = shift; + my ($from, $nick, $user, $host, $arguments) = @_; + my ($section, $item, $key, $value) = split / /, $arguments, 4 if defined $arguments; + + if(not defined $section or not defined $item) { + return "Usage: regset
[key [value]]"; + } + + $key = undef if not length $key; + $value = undef if not length $value; + + return $self->{pbot}->{registry}->set($section, $item, $key, $value); +} + +sub regunset { + my $self = shift; + my ($from, $nick, $user, $host, $arguments) = @_; + my ($section, $item, $key) = split / /, $arguments, 3 if defined $arguments; + + if(not defined $section or not defined $item or not defined $key) { + return "Usage: regunset
" + } + + return $self->{pbot}->{registry}->unset($section, $item, $key); +} + +sub regadd { + my $self = shift; + my ($from, $nick, $user, $host, $arguments) = @_; + my ($section, $item, $value) = split / /, $arguments, 3 if defined $arguments; + + if(not defined $section or not defined $item or not defined $value) { + return "/msg $nick Usage: regadd
"; + } + + $self->{pbot}->{registry}->add('text', $section, $item, $value); + + $self->{pbot}->logger->log("$nick!$user\@$host added registry entry [$section] $item => $value\n"); + return "/msg $nick [$section] $item set to $value"; +} + +sub regrem { + my $self = shift; + my ($from, $nick, $user, $host, $arguments) = @_; + my ($section, $item) = split / /, $arguments if defined $arguments; + + if(not defined $section or not defined $item) { + return "/msg $nick Usage: regrem
"; + } + + if(not exists $self->{pbot}->{registry}->{registry}->hash->{$section}) { + return "/msg $nick No such registry section $section."; + } + + if(not exists $self->{pbot}->{registry}->{registry}->hash->{$section}->{$item}) { + return "/msg $nick No such item $item in section $section."; + } + + $self->{pbot}->logger->log("$nick!$user\@$host removed registry item [$section][$item]\n"); + $self->{pbot}->{registry}->remove($section, $item); + return "/msg $nick Registry item $item removed from section $section."; +} + +sub regshow { + my $self = shift; + my ($from, $nick, $user, $host, $arguments) = @_; + my $registry = $self->{pbot}->{registry}->{registry}->hash; + + my ($section, $item) = split / /, $arguments if defined $arguments; + + if(not defined $section or not defined $item) { + return "Usage: regshow
"; + } + + if(not exists $registry->{$section}) { + return "/msg $nick No such registry section $section."; + } + + if(not exists $registry->{$section}->{$item}) { + return "/msg $nick No such registry item $item in section $section."; + } + + if($registry->{$section}->{$item}->{private}) { + return "/msg $nick [$section] $item is private."; + } + + my $result = "[$section] $item: $registry->{$section}->{$item}->{value}"; + + if($registry->{$section}->{$item}->{type} eq 'array') { + $result .= ' [array]'; + } + + return $result; +} + +sub regfind { + my $self = shift; + my ($from, $nick, $user, $host, $arguments) = @_; + my $registry = $self->{pbot}->{registry}->{registry}->hash; + + if(not defined $arguments) { + return "/msg $nick Usage: regfind [-section section] "; + } + + my $section; + + $section = $1 if $arguments =~ s/-section\s+([^\b\s]+)//i; + + $arguments =~ s/^\s+//; + $arguments =~ s/\s+$//; + $arguments =~ s/\s+/ /g; + + if($arguments eq "") { + return "/msg $nick Usage: regfind [-section section] "; + } + + my ($text, $last_item, $last_section, $i); + $last_section = ""; + $i = 0; + eval { + foreach my $section_key (sort keys %{ $registry }) { + next if defined $section and $section_key !~ /^$section$/i; + foreach my $item_key (sort keys %{ $registry->{$section_key} }) { + next if $registry->{$section_key}->{$item_key}->{private}; + next if $registry->{$section_key}->{$item_key}->{value} !~ /$arguments/i and $item_key !~ /$arguments/i; + + $i++; + + if($section_key ne $last_section) { + $text .= "[$section_key] "; + $last_section = $section_key; + } + $text .= "$item_key "; + $last_item = $item_key; + } + } + }; + + return "/msg $nick $arguments: $@" if $@; + + if($i == 1) { + chop $text; + return "Found one registry entry: [$last_section] $last_item: $registry->{$last_section}->{$last_item}->{value}"; + } else { + return "found $i registry entries: $text" unless $i == 0; + + my $sections = (defined $section ? "section $section" : 'any sections'); + return "No registry entries matching query found in $sections."; + } +} + +sub regchange { + my $self = shift; + my ($from, $nick, $user, $host, $arguments) = @_; + my ($section, $item, $delim, $tochange, $changeto, $modifier); + + if(defined $arguments) { + if($arguments =~ /^([^\s]+) ([^\s]+)\s+s(.)/) { + $section = $1; + $item = $2; + $delim = $3; + } + + if($arguments =~ /$delim(.*?)$delim(.*)$delim(.*)?$/) { + $tochange = $1; + $changeto = $2; + $modifier = $3; + } + } + + if(not defined $section or not defined $item or not defined $changeto) { + return "Usage: regchange
s///"; + } + + my $registry = $self->{pbot}->{registry}->{registry}->hash; + + if(not exists $registry->{$section}) { + return "/msg $nick No such registry section $section."; + } + + if(not exists $registry->{$section}->{$item}) { + return "/msg $nick No such registry item $item in section $section."; + } + + my $ret = eval { + use re::engine::RE2 -strict => 1; + if(not $registry->{$section}->{$item}->{value} =~ s|$tochange|$changeto|) { + $self->{pbot}->logger->log("($from) $nick!$user\@$host: failed to change [$section] $item 's$delim$tochange$delim$changeto$delim$modifier\n"); + return "/msg $nick Change [$section] $item failed."; + } else { + $self->{pbot}->logger->log("($from) $nick!$user\@$host: changed [$section] $item 's/$tochange/$changeto/\n"); + $self->{pbot}->{registry}->process_trigger($section, $item, 'value', $registry->{$section}->{$item}->{value}); + $self->{pbot}->{registry}->save; + return "Changed: [$section] $item set to $registry->{$section}->{$item}->{value}"; + } + }; + return "/msg $nick Change [$section] $item: $@" if $@; + return $ret; +} + +1; diff --git a/PBot/SelectHandler.pm b/PBot/SelectHandler.pm index a3404238..0b13978a 100644 --- a/PBot/SelectHandler.pm +++ b/PBot/SelectHandler.pm @@ -3,9 +3,6 @@ package PBot::SelectHandler; use warnings; use strict; -use vars qw($VERSION); -$VERSION = '1.0.0'; - use IO::Select; use Carp (); diff --git a/PBot/StdinReader.pm b/PBot/StdinReader.pm index 795cdc22..d80702f4 100644 --- a/PBot/StdinReader.pm +++ b/PBot/StdinReader.pm @@ -3,9 +3,6 @@ package PBot::StdinReader; use warnings; use strict; -use vars qw($VERSION); -$VERSION = '1.0.0'; - use POSIX qw(tcgetpgrp getpgrp); # to check whether process is in background or foreground use Carp (); @@ -46,13 +43,13 @@ sub stdin_reader { if($input =~ m/^~([^ ]+)\s+(.*)/) { $from = $1; - $text = "$self->{pbot}->{trigger}$2"; + $text = $self->{pbot}->{registry}->get_value('general', 'trigger') . $2; } else { - $from = "$self->{pbot}->{botnick}!stdin\@localhost"; - $text = "$self->{pbot}->{trigger}$input"; + $from = $self->{pbot}->{registry}->get_value('irc', 'botnick') . "!stdin\@localhost"; + $text = $self->{pbot}->{registry}->get_value('general', 'trigger') . $input; } - return $self->{pbot}->interpreter->process_line($from, $self->{pbot}->{botnick}, "stdin", "localhost", $text); + return $self->{pbot}->interpreter->process_line($from, $self->{pbot}->{registry}->get_value('irc', 'botnick'), "stdin", "localhost", $text); } 1; diff --git a/PBot/Timer.pm b/PBot/Timer.pm index 874c087b..ced52a66 100644 --- a/PBot/Timer.pm +++ b/PBot/Timer.pm @@ -10,9 +10,6 @@ package PBot::Timer; use warnings; use strict; -use vars qw($VERSION); -$VERSION = '1.0.0'; - use Carp (); our $min_timeout = 10; diff --git a/PBot/VERSION.pm b/PBot/VERSION.pm index e9674d9a..678d36be 100644 --- a/PBot/VERSION.pm +++ b/PBot/VERSION.pm @@ -13,8 +13,8 @@ use warnings; # These are set automatically by the build/commit script use constant { BUILD_NAME => "PBot", - BUILD_REVISION => 581, - BUILD_DATE => "2014-05-16", + BUILD_REVISION => 582, + BUILD_DATE => "2014-05-17", }; 1; diff --git a/pbot.pl b/pbot.pl index 7fec4fc6..14ee88e7 100755 --- a/pbot.pl +++ b/pbot.pl @@ -77,8 +77,8 @@ my %config = ( # You shouldn't need to change anything below this line. # ----------------------------------------------------- - # Maximum messages to remember per nick/hostmask - MAX_NICK_MESSAGES => 256, + # Maximum messages to remember per nick/hostmask in message history + MAX_MESSAGES => 256, # Path to data directory data_dir => "$bothome/data", @@ -94,6 +94,9 @@ my %config = ( log_file => "$bothome/log/log", ); +# Location of file containing configuration registry +$config{registry_file} = "$config{config_dir}/registry"; + # Location of file containing bot admin information $config{admins_file} = "$config{config_dir}/admins";