pbot/PBot/PBot.pm

515 lines
19 KiB
Perl
Raw Normal View History

# File: PBot.pm
# Author: pragma_
#
# Purpose: IRC Bot (3rd generation)
License project under MPL2 This patch adds the file LICENSE which is the verbatim copy of the Mozilla Public License Version 2.0 as retreived from https://www.mozilla.org/media/MPL/2.0/index.815ca599c9df.txt on 2017-03-05. This patch also places license headers for the MPL2 type A variant of the license header in the following files: PBot/AntiFlood.pm PBot/BanTracker.pm PBot/BlackList.pm PBot/BotAdminCommands.pm PBot/BotAdmins.pm PBot/ChanOpCommands.pm PBot/ChanOps.pm PBot/Channels.pm PBot/Commands.pm PBot/DualIndexHashObject.pm PBot/EventDispatcher.pm PBot/FactoidCommands.pm PBot/FactoidModuleLauncher.pm PBot/Factoids.pm PBot/HashObject.pm PBot/IRCHandlers.pm PBot/IgnoreList.pm PBot/IgnoreListCommands.pm PBot/Interpreter.pm PBot/LagChecker.pm PBot/Logger.pm PBot/MessageHistory.pm PBot/MessageHistory_SQLite.pm PBot/NickList.pm PBot/PBot.pm PBot/Plugins.pm PBot/Plugins/AntiAway.pm PBot/Plugins/AntiKickAutoRejoin.pm PBot/Plugins/AntiRepeat.pm PBot/Plugins/AntiTwitter.pm PBot/Plugins/AutoRejoin.pm PBot/Plugins/Counter.pm PBot/Plugins/Quotegrabs.pm PBot/Plugins/Quotegrabs/Quotegrabs_Hashtable.pm PBot/Plugins/Quotegrabs/Quotegrabs_SQLite.pm PBot/Plugins/UrlTitles.pm PBot/Plugins/_Example.pm PBot/Refresher.pm PBot/Registerable.pm PBot/Registry.pm PBot/RegistryCommands.pm PBot/SQLiteLogger.pm PBot/SQLiteLoggerLayer.pm PBot/SelectHandler.pm PBot/StdinReader.pm PBot/Timer.pm PBot/Utils/ParseDate.pm PBot/VERSION.pm build/update-version.pl modules/acronym.pl modules/ago.pl modules/c11std.pl modules/c2english.pl modules/c2english/CGrammar.pm modules/c2english/c2eng.pl modules/c99std.pl modules/cdecl.pl modules/cfaq.pl modules/cjeopardy/IRCColors.pm modules/cjeopardy/QStatskeeper.pm modules/cjeopardy/Scorekeeper.pm modules/cjeopardy/cjeopardy.pl modules/cjeopardy/cjeopardy_answer.pl modules/cjeopardy/cjeopardy_filter.pl modules/cjeopardy/cjeopardy_hint.pl modules/cjeopardy/cjeopardy_qstats.pl modules/cjeopardy/cjeopardy_scores.pl modules/cjeopardy/cjeopardy_show.pl modules/codepad.pl modules/compiler_block.pl modules/compiler_client.pl modules/compiler_vm/Diff.pm modules/compiler_vm/cc modules/compiler_vm/compiler_client.pl modules/compiler_vm/compiler_server.pl modules/compiler_vm/compiler_server_vbox_win32.pl modules/compiler_vm/compiler_server_watchdog.pl modules/compiler_vm/compiler_vm_client.pl modules/compiler_vm/compiler_vm_server.pl modules/compiler_vm/compiler_watchdog.pl modules/compiler_vm/languages/_c_base.pm modules/compiler_vm/languages/_default.pm modules/compiler_vm/languages/bash.pm modules/compiler_vm/languages/bc.pm modules/compiler_vm/languages/bf.pm modules/compiler_vm/languages/c11.pm modules/compiler_vm/languages/c89.pm modules/compiler_vm/languages/c99.pm modules/compiler_vm/languages/clang.pm modules/compiler_vm/languages/clang11.pm modules/compiler_vm/languages/clang89.pm modules/compiler_vm/languages/clang99.pm modules/compiler_vm/languages/clangpp.pm modules/compiler_vm/languages/clisp.pm modules/compiler_vm/languages/cpp.pm modules/compiler_vm/languages/freebasic.pm modules/compiler_vm/languages/go.pm modules/compiler_vm/languages/haskell.pm modules/compiler_vm/languages/java.pm modules/compiler_vm/languages/javascript.pm modules/compiler_vm/languages/ksh.pm modules/compiler_vm/languages/lua.pm modules/compiler_vm/languages/perl.pm modules/compiler_vm/languages/python.pm modules/compiler_vm/languages/python3.pm modules/compiler_vm/languages/qbasic.pm modules/compiler_vm/languages/scheme.pm modules/compiler_vm/languages/server/_c_base.pm modules/compiler_vm/languages/server/_default.pm modules/compiler_vm/languages/server/c11.pm modules/compiler_vm/languages/server/c89.pm modules/compiler_vm/languages/server/c99.pm modules/compiler_vm/languages/server/clang.pm modules/compiler_vm/languages/server/clang11.pm modules/compiler_vm/languages/server/clang89.pm modules/compiler_vm/languages/server/clang99.pm modules/compiler_vm/languages/server/cpp.pm modules/compiler_vm/languages/server/freebasic.pm modules/compiler_vm/languages/server/haskell.pm modules/compiler_vm/languages/server/java.pm modules/compiler_vm/languages/server/qbasic.pm modules/compiler_vm/languages/server/tendra.pm modules/compiler_vm/languages/sh.pm modules/compiler_vm/languages/tendra.pm modules/compliment modules/cstd.pl modules/define.pl modules/dice_roll.pl modules/excuse.sh modules/expand_macros.pl modules/fnord.pl modules/funnyish_quote.pl modules/g.pl modules/gdefine.pl modules/gen_cfacts.pl modules/gencstd.pl modules/get_title.pl modules/getcfact.pl modules/google.pl modules/gspy.pl modules/gtop10.pl modules/gtop15.pl modules/headlines.pl modules/horoscope modules/horrorscope modules/ideone.pl modules/insult.pl modules/love_quote.pl modules/man.pl modules/map.pl modules/math.pl modules/prototype.pl modules/qalc.pl modules/random_quote.pl modules/seen.pl modules/urban modules/weather.pl modules/wikipedia.pl pbot.pl pbot.sh It is highly recommended that this list of files is reviewed to ensure that all files are the copyright of the sole maintainer of the repository. If any files with license headers contain the intellectual property of anyone else, it is recommended that a request is made to revise this patch or that the explicit permission of the co-author is gained to allow for the license of the work to be changed. I (Tomasz Kramkowski), the contributor, take no responsibility for any legal action taken against the maintainer of this repository for incorrectly claiming copyright to any work not owned by the maintainer of this repository.
2017-03-05 22:33:31 +01:00
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
package PBot::PBot;
use strict; use warnings;
2019-07-11 03:40:53 +02:00
use feature 'unicode_strings';
# unbuffer stdout
STDOUT->autoflush(1);
use Carp ();
use PBot::Logger;
use PBot::VERSION;
use PBot::HashObject;
use PBot::DualIndexHashObject;
use PBot::DualIndexSQLiteObject;
use PBot::Registry;
use PBot::Capabilities;
use PBot::SelectHandler;
use PBot::StdinReader;
use PBot::IRC;
use PBot::EventDispatcher;
use PBot::IRCHandlers;
use PBot::Channels;
use PBot::BanList;
use PBot::NickList;
use PBot::LagChecker;
use PBot::MessageHistory;
use PBot::AntiFlood;
2018-08-06 07:41:08 +02:00
use PBot::AntiSpam;
use PBot::Interpreter;
use PBot::Commands;
use PBot::ChanOps;
2019-06-26 18:34:19 +02:00
use PBot::Factoids;
use PBot::Users;
use PBot::IgnoreList;
use PBot::BlackList;
use PBot::Timer;
use PBot::Refresher;
use PBot::WebPaste;
use PBot::Utils::ParseDate;
use PBot::Plugins;
use PBot::Functions;
use PBot::Modules;
use PBot::ProcessManager;
2020-04-21 02:53:32 +02:00
use PBot::Updater;
sub new {
2020-02-15 23:38:32 +01:00
my ($proto, %conf) = @_;
my $class = ref($proto) || $proto;
my $self = bless {}, $class;
$self->initialize(%conf);
return $self;
}
sub initialize {
2020-02-15 23:38:32 +01:00
my ($self, %conf) = @_;
$self->{startup_timestamp} = time;
2020-04-21 02:53:32 +02:00
my $data_dir = $conf{data_dir};
my $module_dir = $conf{module_dir};
my $plugin_dir = $conf{plugin_dir};
my $update_dir = $conf{update_dir};
2020-02-15 23:38:32 +01:00
# process command-line arguments
2020-02-15 23:38:32 +01:00
foreach my $arg (@ARGV) {
2020-04-21 02:53:32 +02:00
if ($arg =~ m/^-?(?:general\.)?((?:data|module|plugin|update)_dir)=(.*)$/) {
# check command-line arguments for directory overrides
2020-04-21 02:53:32 +02:00
my $override = $1;
my $value = $2;
$value =~ s/[\\\/]$//; # strip trailing directory separator
2020-04-21 02:53:32 +02:00
$data_dir = $value if $override eq 'data_dir';
$module_dir = $value if $override eq 'module_dir';
$plugin_dir = $value if $override eq 'plugin_dir';
$update_dir = $value if $override eq 'update_dir';
} else {
# check command-line arguments for registry overrides
my ($item, $value) = split /=/, $arg, 2;
if (not defined $item or not defined $value) {
print STDERR "Fatal error: unknown argument `$arg`; arguments must be in the form of `section.key=value` (e.g.: irc.botnick=newnick)\n";
exit;
}
my ($section, $key) = split /\./, $item, 2;
if (not defined $section or not defined $key) {
print STDERR "Fatal error: bad argument `$arg`; registry entries must be in the form of section.key (e.g.: irc.botnick)\n";
exit;
}
$section =~ s/^-//; # remove a leading - to allow arguments like -irc.botnick due to habitual use of -args
$self->{overrides}->{"$section.$key"} = $value;
2020-02-15 23:38:32 +01:00
}
}
2020-04-20 19:53:35 +02:00
# make sure the data directory exists
2020-02-15 23:38:32 +01:00
if (not -d $data_dir) {
2020-04-20 19:53:35 +02:00
print STDERR "Data directory ($data_dir) does not exist; aborting...\n";
2020-02-15 23:38:32 +01:00
exit;
}
# let modules register signal handlers
$self->{atexit} = PBot::Registerable->new(%conf, pbot => $self);
$self->register_signal_handlers;
2020-04-20 19:53:35 +02:00
# create logger
$self->{logger} = PBot::Logger->new(pbot => $self, filename => "$data_dir/log/log", %conf);
# make sure the rest of the environment is sane
2020-02-15 23:38:32 +01:00
if (not -d $module_dir) {
$self->{logger}->log("Modules directory ($module_dir) does not exist; aborting...\n");
exit;
}
2020-02-15 23:38:32 +01:00
if (not -d $plugin_dir) {
$self->{logger}->log("Plugins directory ($plugin_dir) does not exist; aborting...\n");
exit;
}
2020-04-21 02:53:32 +02:00
if (not -d $update_dir) {
$self->{logger}->log("Updates directory ($update_dir) does not exist; aborting...\n");
2020-04-20 19:53:35 +02:00
exit;
}
2020-04-21 02:53:32 +02:00
$self->{updater} = PBot::Updater->new(pbot => $self, data_dir => $data_dir, update_dir => $update_dir);
2020-04-20 19:53:35 +02:00
2020-04-21 02:53:32 +02:00
# update any data files to new locations/formats
if ($self->{updater}->update) {
$self->{logger}->log("Update failed.\n");
2020-04-20 19:53:35 +02:00
exit 0;
}
# create capabilities so commands can add new capabilities
2020-02-15 23:38:32 +01:00
$self->{capabilities} = PBot::Capabilities->new(pbot => $self, filename => "$data_dir/capabilities", %conf);
2020-04-20 19:53:35 +02:00
# create commands so the modules can register new commands
2020-02-15 23:38:32 +01:00
$self->{commands} = PBot::Commands->new(pbot => $self, filename => "$data_dir/commands", %conf);
# add some commands
$self->{commands}->register(sub { $self->cmd_list(@_) }, "list");
$self->{commands}->register(sub { $self->cmd_die(@_) }, "die", 1);
$self->{commands}->register(sub { $self->cmd_export(@_) }, "export", 1);
$self->{commands}->register(sub { $self->cmd_reload(@_) }, "reload", 1);
$self->{commands}->register(sub { $self->cmd_eval(@_) }, "eval", 1);
$self->{commands}->register(sub { $self->cmd_sl(@_) }, "sl", 1);
2020-02-15 23:38:32 +01:00
# add 'cap' capability command
$self->{commands}->register(sub { $self->{capabilities}->cmd_cap(@_) }, "cap");
2020-02-15 23:38:32 +01:00
# prepare the version
$self->{version} = PBot::VERSION->new(pbot => $self, %conf);
$self->{logger}->log($self->{version}->version . "\n");
$self->{logger}->log("Args: @ARGV\n") if @ARGV;
$self->{logger}->log("module_dir: $module_dir\n");
$self->{logger}->log("plugin_dir: $plugin_dir\n");
2020-04-20 19:53:35 +02:00
$self->{logger}->log("data_dir: $data_dir\n");
2020-04-21 02:53:32 +02:00
$self->{logger}->log("update_dir: $update_dir\n");
2020-04-20 19:53:35 +02:00
2020-02-15 23:38:32 +01:00
$self->{timer} = PBot::Timer->new(pbot => $self, timeout => 10, name => 'PBot Timer', %conf);
2020-02-15 23:38:32 +01:00
$self->{modules} = PBot::Modules->new(pbot => $self, %conf);
$self->{functions} = PBot::Functions->new(pbot => $self, %conf);
$self->{refresher} = PBot::Refresher->new(pbot => $self);
# create registry and set some defaults
$self->{registry} = PBot::Registry->new(pbot => $self, filename => "$data_dir/registry", %conf);
2020-04-21 02:53:32 +02:00
$self->{registry}->add_default('text', 'general', 'data_dir', $data_dir);
$self->{registry}->add_default('text', 'general', 'module_dir', $module_dir);
$self->{registry}->add_default('text', 'general', 'plugin_dir', $plugin_dir);
$self->{registry}->add_default('text', 'general', 'update_dir', $update_dir);
2020-04-20 19:53:35 +02:00
$self->{registry}->add_default('text', 'general', 'trigger', $conf{trigger} // '!');
2020-02-15 23:38:32 +01:00
$self->{registry}->add_default('text', 'irc', 'debug', $conf{irc_debug} // 0);
$self->{registry}->add_default('text', 'irc', 'show_motd', $conf{show_motd} // 1);
$self->{registry}->add_default('text', 'irc', 'max_msg_len', $conf{max_msg_len} // 425);
$self->{registry}->add_default('text', 'irc', 'server', $conf{server} // "irc.freenode.net");
$self->{registry}->add_default('text', 'irc', 'port', $conf{port} // 6667);
$self->{registry}->add_default('text', 'irc', 'SSL', $conf{SSL} // 0);
$self->{registry}->add_default('text', 'irc', 'SSL_ca_file', $conf{SSL_ca_file} // 'none');
$self->{registry}->add_default('text', 'irc', 'SSL_ca_path', $conf{SSL_ca_path} // 'none');
$self->{registry}->add_default('text', 'irc', 'botnick', $conf{botnick} // "");
$self->{registry}->add_default('text', 'irc', 'username', $conf{username} // "pbot3");
$self->{registry}->add_default('text', 'irc', 'realname', $conf{realname} // "https://github.com/pragma-/pbot");
$self->{registry}->add_default('text', 'irc', 'identify_password', $conf{identify_password} // '');
$self->{registry}->add_default('text', 'irc', 'log_default_handler', 1);
$self->{registry}->set_default('irc', 'SSL_ca_file', 'private', 1);
$self->{registry}->set_default('irc', 'SSL_ca_path', 'private', 1);
$self->{registry}->set_default('irc', 'identify_password', 'private', 1);
# load existing registry entries from file (if exists) to overwrite defaults
if (-e $self->{registry}->{registry}->{filename}) { $self->{registry}->load; }
# update important paths
2020-04-21 02:53:32 +02:00
$self->{registry}->set('general', 'data_dir', 'value', $data_dir, 0, 1);
$self->{registry}->set('general', 'module_dir', 'value', $module_dir, 0, 1);
$self->{registry}->set('general', 'plugin_dir', 'value', $plugin_dir, 0, 1);
$self->{registry}->set('general', 'update_dir', 'value', $update_dir, 0, 1);
2020-02-15 23:38:32 +01:00
# override registry entries with command-line arguments, if any
foreach my $override (keys %{$self->{overrides}}) {
my ($section, $key) = split /\./, $override;
my $value = $self->{overrides}->{$override};
$self->{logger}->log("Overriding $section.$key to $value\n");
$self->{registry}->set($section, $key, 'value', $value, 0, 1);
}
# registry triggers fire when value changes
$self->{registry}->add_trigger('irc', 'botnick', sub { $self->change_botnick_trigger(@_) });
$self->{registry}->add_trigger('irc', 'debug', sub { $self->irc_debug_trigger(@_) });
# ensure user has attempted to configure the bot
if (not length $self->{registry}->get_value('irc', 'botnick')) {
$self->{logger}->log("Fatal error: IRC nickname not defined; please set registry key irc.botnick in $data_dir/registry to continue.\n");
exit;
}
$self->{event_dispatcher} = PBot::EventDispatcher->new(pbot => $self, %conf);
$self->{process_manager} = PBot::ProcessManager->new(pbot => $self, %conf);
$self->{irchandlers} = PBot::IRCHandlers->new(pbot => $self, %conf);
$self->{select_handler} = PBot::SelectHandler->new(pbot => $self, %conf);
$self->{users} = PBot::Users->new(pbot => $self, filename => "$data_dir/users", %conf);
$self->{stdin_reader} = PBot::StdinReader->new(pbot => $self, %conf);
$self->{lagchecker} = PBot::LagChecker->new(pbot => $self, %conf);
$self->{messagehistory} = PBot::MessageHistory->new(pbot => $self, filename => "$data_dir/message_history.sqlite3", %conf);
$self->{antiflood} = PBot::AntiFlood->new(pbot => $self, %conf);
$self->{antispam} = PBot::AntiSpam->new(pbot => $self, %conf);
$self->{ignorelist} = PBot::IgnoreList->new(pbot => $self, filename => "$data_dir/ignorelist", %conf);
$self->{blacklist} = PBot::BlackList->new(pbot => $self, filename => "$data_dir/blacklist", %conf);
$self->{irc} = PBot::IRC->new();
$self->{channels} = PBot::Channels->new(pbot => $self, filename => "$data_dir/channels", %conf);
$self->{chanops} = PBot::ChanOps->new(pbot => $self, %conf);
$self->{banlist} = PBot::BanList->new(pbot => $self, %conf);
2020-02-15 23:38:32 +01:00
$self->{nicklist} = PBot::NickList->new(pbot => $self, %conf);
$self->{webpaste} = PBot::WebPaste->new(pbot => $self, %conf);
$self->{parsedate} = PBot::Utils::ParseDate->new(pbot => $self, %conf);
$self->{interpreter} = PBot::Interpreter->new(pbot => $self, %conf);
$self->{interpreter}->register(sub { $self->{commands}->interpreter(@_) });
$self->{interpreter}->register(sub { $self->{factoids}->interpreter(@_) });
$self->{factoids} = PBot::Factoids->new(pbot => $self, filename => "$data_dir/factoids.sqlite3", %conf);
2020-02-15 23:38:32 +01:00
$self->{plugins} = PBot::Plugins->new(pbot => $self, %conf);
# load available plugins
$self->{plugins}->autoload(%conf);
# give botowner all capabilities
$self->{capabilities}->rebuild_botowner_capabilities();
# flush all pending save events to disk at exit
$self->{atexit}->register(sub {
2020-05-30 05:48:53 +02:00
$self->{timer}->execute_and_dequeue_event('save *');
return;
}
);
}
sub random_nick {
2020-02-15 23:38:32 +01:00
my ($self, $length) = @_;
$length //= 9;
my @chars = ("A" .. "Z", "a" .. "z", "0" .. "9");
my $nick = $chars[rand @chars - 10]; # nicks cannot start with a digit
$nick .= $chars[rand @chars] for 1 .. $length;
return $nick;
}
# TODO: add disconnect subroutine
sub connect {
2020-02-15 23:38:32 +01:00
my ($self, $server) = @_;
return if $ENV{PBOT_LOCAL};
2020-02-15 23:38:32 +01:00
if ($self->{connected}) {
# TODO: disconnect, clean-up, etc
}
$server = $self->{registry}->get_value('irc', 'server') if not defined $server;
$self->{logger}->log("Connecting to $server ...\n");
while (
not $self->{conn} = $self->{irc}->newconn(
Nick => $self->{registry}->get_value('irc', 'randomize_nick') ? $self->random_nick : $self->{registry}->get_value('irc', 'botnick'),
Username => $self->{registry}->get_value('irc', 'username'),
Ircname => $self->{registry}->get_value('irc', 'realname'),
Server => $server,
Pacing => 1,
UTF8 => 1,
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')
)
)
{
$self->{logger}->log("$0: Can't connect to $server:" . $self->{registry}->get_value('irc', 'port') . ". Retrying in 15 seconds...\n");
sleep 15;
}
$self->{connected} = 1;
# start timer once connected
$self->{timer}->start;
# set up handlers for the IRC engine
$self->{conn}->add_default_handler(sub { $self->{irchandlers}->default_handler(@_) }, 1);
$self->{conn}->add_handler([251, 252, 253, 254, 255, 302], sub { $self->{irchandlers}->on_init(@_) });
# ignore these events
$self->{conn}->add_handler(
[
'whoisserver',
'whoiscountry',
'whoischannels',
'whoisidle',
'motdstart',
'endofmotd',
'away',
],
sub { }
);
}
#main loop
sub do_one_loop {
2020-02-15 23:38:32 +01:00
my $self = shift;
$self->{irc}->do_one_loop() if $self->{connected};
$self->{select_handler}->do_select;
}
sub start {
2020-02-15 23:38:32 +01:00
my $self = shift;
while (1) {
$self->connect if not $self->{connected};
$self->do_one_loop;
2020-02-15 23:38:32 +01:00
}
}
sub register_signal_handlers {
2020-02-15 23:38:32 +01:00
my $self = shift;
$SIG{INT} = sub { $self->atexit; exit 0; };
}
sub atexit {
2020-02-15 23:38:32 +01:00
my $self = shift;
$self->{atexit}->execute_all;
alarm 0;
}
sub irc_debug_trigger {
2020-02-15 23:38:32 +01:00
my ($self, $section, $item, $newvalue) = @_;
$self->{irc}->debug($newvalue);
$self->{conn}->debug($newvalue) if $self->{connected};
}
sub change_botnick_trigger {
2020-02-15 23:38:32 +01:00
my ($self, $section, $item, $newvalue) = @_;
$self->{conn}->nick($newvalue) if $self->{connected};
}
sub cmd_list {
my ($self, $context) = @_;
2020-02-15 23:38:32 +01:00
my $text;
my $usage = "Usage: list <modules|commands>";
return $usage if not length $context->{arguments};
2020-02-15 23:38:32 +01:00
if ($context->{arguments} =~ /^modules$/i) {
2020-02-15 23:38:32 +01:00
$text = "Loaded modules: ";
foreach my $channel (sort $self->{factoids}->{factoids}->get_keys) {
foreach my $command (sort $self->{factoids}->{factoids}->get_keys($channel)) {
next if $command eq '_name';
if ($self->{factoids}->{factoids}->get_data($channel, $command, 'type') eq 'module') {
$text .= $self->{factoids}->{factoids}->get_data($channel, $command, '_name') . ' ';
}
}
}
2020-02-15 23:38:32 +01:00
return $text;
}
2020-02-15 23:38:32 +01:00
if ($context->{arguments} =~ /^commands$/i) {
2020-02-15 23:38:32 +01:00
$text = "Registered commands: ";
foreach my $command (sort { $a->{name} cmp $b->{name} } @{$self->{commands}->{handlers}}) {
if ($command->{requires_cap}) { $text .= "+$command->{name} "; }
else { $text .= "$command->{name} "; }
}
return $text;
}
2020-02-15 23:38:32 +01:00
return $usage;
}
sub cmd_sl {
my ($self, $context) = @_;
return "Usage: sl <ircd command>" if not length $context->{arguments};
$self->{conn}->sl($context->{arguments});
return "/msg $context->{nick} sl: command sent. See log for result.";
}
sub cmd_die {
my ($self, $context) = @_;
$self->{logger}->log("$context->{hostmask} made me exit.\n");
$self->{conn}->privmsg($context->{from}, "Good-bye.") if $context->{from} ne 'stdin@pbot';
$self->{conn}->quit("Departure requested.") if defined $self->{conn};
$self->atexit();
2020-02-15 23:38:32 +01:00
exit 0;
}
sub cmd_export {
my ($self, $context) = @_;
return "Usage: export <factoids>" if not length $context->{arguments};
if ($context->{arguments} =~ /^factoids$/i) { return $self->{factoids}->export_factoids; }
}
sub cmd_eval {
my ($self, $context) = @_;
$self->{logger}->log("eval: $context->{from} $context->{hostmask} evaluating `$context->{arguments}`\n");
2020-02-15 23:38:32 +01:00
my $ret = '';
my $result = eval $context->{arguments};
2020-02-15 23:38:32 +01:00
if ($@) {
if (length $result) { $ret .= "[Error: $@] "; }
else { $ret .= "Error: $@"; }
$ret =~ s/ at \(eval \d+\) line 1.//;
}
2020-02-15 23:38:32 +01:00
$result = 'Undefined.' if not defined $result;
$result = 'No output.' if not length $result;
return "/say $ret $result";
}
sub cmd_reload {
my ($self, $context) = @_;
2020-02-15 23:38:32 +01:00
my %reloadables = (
'capabilities' => sub {
$self->{capabilities}->{caps}->load;
return "Capabilities reloaded.";
},
'commands' => sub {
$self->{commands}->{metadata}->load;
return "Commands metadata reloaded.";
},
'blacklist' => sub {
2020-06-02 06:48:17 +02:00
$self->{blacklist}->clear_blacklist;
2020-02-15 23:38:32 +01:00
$self->{blacklist}->load_blacklist;
return "Blacklist reloaded.";
},
'ban-exemptions' => sub {
$self->{antiflood}->{'ban-exemptions'}->load;
return "Ban exemptions reloaded.";
},
'ignores' => sub {
$self->{ignorelist}->{ignorelist}->load;
2020-02-15 23:38:32 +01:00
return "Ignore list reloaded.";
},
'users' => sub {
$self->{users}->load;
return "Users reloaded.";
},
'channels' => sub {
$self->{channels}->{channels}->load;
return "Channels reloaded.";
},
'banlist' => sub {
$self->{timer}->dequeue_event('unban #.*');
$self->{timer}->dequeue_event('unmute #.*');
$self->{banlist}->{banlist}->load;
$self->{banlist}->{quietlist}->load;
2020-05-02 11:38:39 +02:00
$self->{banlist}->enqueue_timeouts($self->{banlist}->{banlist}, 'b');
$self->{banlist}->enqueue_timeouts($self->{banlist}->{quietlist}, 'q');
return "Ban list reloaded.";
2020-02-15 23:38:32 +01:00
},
'registry' => sub {
$self->{registry}->load;
return "Registry reloaded.";
},
'factoids' => sub {
$self->{factoids}->load_factoids;
return "Factoids reloaded.";
}
);
if (not length $context->{arguments} or not exists $reloadables{$context->{arguments}}) {
2020-02-15 23:38:32 +01:00
my $usage = 'Usage: reload <';
$usage .= join '|', sort keys %reloadables;
$usage .= '>';
return $usage;
}
return $reloadables{$context->{arguments}}();
}
1;