From 2b33a2468cc6e079ff934085ced019efa9f3618c Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Sat, 24 Aug 2019 19:03:08 -0700 Subject: [PATCH] Add `func` command. Executes built-in bot functions as a command. Very useful! --- PBot/BotAdminCommands.pm | 7 +- PBot/FuncCommand.pm | 145 +++++++++++++++++++++++++++++++++++++++ PBot/PBot.pm | 3 + 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 PBot/FuncCommand.pm diff --git a/PBot/BotAdminCommands.pm b/PBot/BotAdminCommands.pm index e0a359e3..0f5dd8cd 100644 --- a/PBot/BotAdminCommands.pm +++ b/PBot/BotAdminCommands.pm @@ -380,11 +380,16 @@ sub reload { $self->{pbot}->{factoids}->{factoids}->clear; $self->{pbot}->{factoids}->load_factoids; return "Factoids reloaded."; + }, + + 'funcs' => sub { + $self->{pbot}->{func_cmd}->init_funcs; + return "Funcs reloaded."; } ); if (not length $arguments or not exists $reloadables{$arguments}) { - my $usage = 'Usage: refload <'; + my $usage = 'Usage: reload <'; $usage .= join '|', sort keys %reloadables; $usage .= '>'; return $usage; diff --git a/PBot/FuncCommand.pm b/PBot/FuncCommand.pm new file mode 100644 index 00000000..7508b890 --- /dev/null +++ b/PBot/FuncCommand.pm @@ -0,0 +1,145 @@ + +# File: FuncCommand.pm +# Author: pragma_ +# +# Purpose: Special `func` command that executes built-in functions with +# optional arguments. Usage: func [arguments]. +# +# Intended usage is with command-substitution (&{}). For example: +# +# factadd img /call echo https://google.com/search?q=&{func urlencode $args}&tbm=isch +# +# The above would invoke the function 'urlencode' on $args and then replace +# the command-substitution with the result, creating a simple Google Image +# Search factoid command. + +# 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::FuncCommand; + +use warnings; +use strict; + +use feature 'unicode_strings'; + +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) = @_; + + $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to FactoidCommands"); + + $self->{pbot}->{commands}->register(sub { return $self->do_func(@_) }, 'func', 0); + + $self->init_funcs; +} + +sub init_funcs { + my ($self) = @_; + + $self->{funcs} = { + help => { + desc => 'provides help about a func', + usage => 'help [func]', + subref => sub { $self->func_help(@_) } + }, + list => { + desc => 'lists available funcs', + usage => 'list [regex]', + subref => sub { $self->func_list(@_) } + }, + uri_escape => { + desc => 'percent-encode unsafe URI characters', + usage => 'uri_escape ', + subref => sub { $self->func_uri_escape(@_) } + }, + }; +} + +use URI::Escape qw/uri_escape/; + +sub func_uri_escape { + my $self = shift; + my $text = "@_"; + return uri_escape($text); +} + +sub func_help { + my ($self, $func) = @_; + + if (not exists $self->{funcs}->{$func}) { + return "No such func '$func'."; + } + + return "$func: $self->{funcs}->{$func}->{desc}; usage: $self->{funcs}->{$func}->{usage}"; +} + +sub func_list { + my ($self, $regex) = @_; + + $regex = '.*' if not defined $regex; + + my $result = eval { + my $text = ''; + + foreach my $func (sort keys %{$self->{funcs}}) { + if ($func =~ m/$regex/i) { + $text .= "$func: $self->{funcs}->{$func}->{desc}.\n"; + } + } + + if (not length $text) { + if ($regex eq '.*') { + $text = "No funcs yet."; + } else { + $text = "No matching func."; + } + } + + return $text; + }; + + if ($@) { + my $error = $@; + $error =~ s/at PBot.FuncCommand.*$//; + return "Error: $error\n"; + } + + return $result; +} + +sub do_func { + my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_; + + my $func = $self->{pbot}->{interpreter}->shift_arg($stuff->{arglist}); + + if (not defined $func) { + return "Usage: func [arguments]"; + } + + if (not exists $self->{funcs}->{$func}) { + return "[No such func '$func']"; + } + + my @params; + while (my $param = $self->{pbot}->{interpreter}->shift_arg($stuff->{arglist})) { + push @params, $param; + } + + return $self->{funcs}->{$func}->{subref}->(@params); +} + +1; diff --git a/PBot/PBot.pm b/PBot/PBot.pm index 1f20fba0..ab73e3f7 100644 --- a/PBot/PBot.pm +++ b/PBot/PBot.pm @@ -45,6 +45,7 @@ use PBot::Refresher; use PBot::Plugins; use PBot::WebPaste; use PBot::Utils::ParseDate; +use PBot::FuncCommand; sub new { if (ref($_[1]) eq 'HASH') { @@ -71,7 +72,9 @@ sub initialize { $self->{atexit} = PBot::Registerable->new(%conf); $self->{timer} = PBot::Timer->new(timeout => 10, %conf); + $self->{commands} = PBot::Commands->new(pbot => $self, %conf); + $self->{func_cmd} = PBot::FuncCommand->new(pbot => $self, %conf); $self->{refresher} = PBot::Refresher->new(pbot => $self);