Alice
view release on metacpan or search on metacpan
lib/Alice/Role/Commands.pm view on Meta::CPAN
package Alice::Role::Commands;
use Any::Moose 'Role';
use List::MoreUtils qw/none/;
use Try::Tiny;
use Class::Throwable qw/NetworkRequired InvalidNetwork ChannelRequired
InvalidArguments UnknownCommand/;
our %COMMANDS;
my $SRVOPT = qr/\-(\S+)\s*/;
sub commands {
return grep {$_->{eg}} values %COMMANDS;
}
sub irc_command {
my ($self, $req) = @_;
try {
my ($command, $args) = $self->match_irc_command($req->line);
if ($command) {
$self->run_irc_command($command, $req, $args);
}
else {
throw UnknownCommand $req->line ." does not match any known commands. Try /help";
}
}
catch {
$req->reply("$_");
}
}
sub match_irc_command {
my ($self, $line) = @_;
$line = "/say $line" unless substr($line, 0, 1) eq "/";
for my $name (keys %COMMANDS) {
if ($line =~ m{^/$name\b\s*(.*)}) {
my $args = $1;
return ($name, $args);
}
}
}
sub run_irc_command {
my ($self, $name, $req, $args) = @_;
my $command = $COMMANDS{$name};
my $opts = [];
# must be in a channel
my $type = $req->window->type;
if ($command->{window_type} and none {$_ eq $type} @{$command->{window_type}}) {
my $types = join " or ", @{$command->{window_type}};
throw ChannelRequired "Must be in a $types for /$command->{name}.";
}
my $network = $req->window->network;
# determine the network can be overridden
if ($command->{network} and $args =~ s/^$SRVOPT//) {
$network = $1;
}
# command requires a connected network
if ($command->{connection}) {
throw NetworkRequired $command->{eg} unless $network;
my $irc = $self->get_irc($network);
throw InvalidNetwork "The $network network does not exist."
unless $irc;
throw InvalidNetwork "The $network network is not connected"
unless $irc->is_connected;
$req->irc($irc);
}
# gather any options
if (my $opt_re = $command->{opts}) {
if (my (@opts) = ($args =~ /$opt_re/)) {
$opts = \@opts;
}
else {
throw InvalidArguments $command->{eg};
}
}
$command->{cb}->($self, $req, $opts);
}
sub command {
my ($name, $opts) = @_;
if ($opts) {
$COMMANDS{$name} = $opts;
}
return $COMMANDS{$name};
}
command say => {
name => "say",
window_type => [qw/channel privmsg/],
connection => 1,
eg => "/SAY <msg>",
opts => qr{(.+)},
cb => sub {
my ($self, $req, $opts) = @_;
my $msg = $opts->[0];
$self->send_message($req->window, $req->irc->nick, $msg);
$req->irc->send_long_line(PRIVMSG => $req->window->title, $msg);
},
};
command msg => {
name => "msg",
opts => qr{(\S+)\s*(.*)},
eg => "/MSG [-<network>] <nick> [<msg>]",
desc => "Sends a message to a nick.",
connection => 1,
network => 1,
cb => sub {
my ($self, $req, $opts) = @_;
my ($nick, $msg) = @$opts;
my $new_window = $self->find_or_create_window($nick, $req->irc);
$self->broadcast($new_window->join_action);
if ($msg) {
$self->send_message($new_window, $req->nick, $msg);
$req->send_srv(PRIVMSG => $nick, $msg);
}
}
};
command nick => {
name => "nick",
opts => qr{(\S+)},
connection => 1,
network => 1,
eg => "/NICK [-<network>] <new nick>",
desc => "Changes your nick.",
( run in 2.555 seconds using v1.01-cache-2.11-cpan-d8267643d1d )