summaryrefslogtreecommitdiffstats
path: root/lib/IM/Engine/Plugin/Commands.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/IM/Engine/Plugin/Commands.pm')
-rw-r--r--lib/IM/Engine/Plugin/Commands.pm189
1 files changed, 189 insertions, 0 deletions
diff --git a/lib/IM/Engine/Plugin/Commands.pm b/lib/IM/Engine/Plugin/Commands.pm
index 439861b..9a59210 100644
--- a/lib/IM/Engine/Plugin/Commands.pm
+++ b/lib/IM/Engine/Plugin/Commands.pm
@@ -1,5 +1,8 @@
package IM::Engine::Plugin::Commands;
use Moose;
+use Module::Pluggable sub_name => 'commands';
+use List::Util qw(first);
+extends 'IM::Engine::Plugin';
=head1 NAME
@@ -13,6 +16,192 @@ IM::Engine::Plugin::Commands -
=cut
+has namespace => (
+ is => 'ro',
+ isa => 'Str',
+ required => 1,
+ trigger => sub {
+ my $self = shift;
+ my ($path) = @_;
+ $self->search_path(new => $path);
+ },
+);
+
+has exclude_commands => (
+ is => 'ro',
+ isa => 'Str|RegexpRef|ArrayRef[Str|RegexpRef]',
+ trigger => sub {
+ my $self = shift;
+ my ($except) = @_;
+ $self->except($except);
+ },
+);
+
+has only_commands => (
+ is => 'ro',
+ isa => 'Str|RegexpRef|ArrayRef[Str|RegexpRef]',
+ trigger => sub {
+ my $self = shift;
+ my ($only) = @_;
+ $self->only($only);
+ },
+);
+
+has prefix => (
+ is => 'ro',
+ isa => 'Str',
+ default => '!',
+);
+
+# XXX: use mxah here
+has alias => (
+ is => 'ro',
+ isa => 'HashRef[Str]',
+ default => sub { {} },
+);
+
+# XXX: and here
+has _active_commands => (
+ is => 'ro',
+ isa => 'HashRef[IM::Engine::Plugin::Commands::Command]',
+ init_arg => undef,
+ default => sub { {} },
+);
+
+has _last_message => (
+ is => 'rw',
+ isa => 'IM::Engine::Incoming',
+ init_arg => undef,
+);
+
+sub BUILD {
+ my $self = shift;
+ confess "Don't specify an incoming_callback when using " . __PACKAGE__
+ if $self->engine->interface->has_incoming_callback;
+ $self->engine->interface->incoming_callback(
+ sub { $self->incoming(@_) }
+ );
+}
+
+sub incoming {
+ my $self = shift;
+ my ($message) = @_;
+ $self->_last_message($message);
+ my $text = $message->plaintext;
+ my $sender = $message->sender->name;
+ my $prefix = $self->prefix;
+
+ # XXX: rewrite this in terms of Path::Dispatcher
+ return unless $text =~ /^\Q$prefix\E(\w+)(?:\s+(.*))?/;
+ my ($command_name, $action) = (lc($1), $2);
+
+ if ($command_name eq 'cmdlist') { # XXX: make this configurable
+ $self->say(join ' ', map { $self->prefix . $_} $self->command_list);
+ return;
+ }
+
+ if ($command_name eq 'help') {
+ $command_name = $action;
+ $command_name =~ s/^-//;
+ $action = '-help';
+ }
+
+ $command_name = $self->_find_command($command_name);
+ return unless $command_name;
+
+ my $command = $self->_active_commands->{$command_name};
+ if (!defined $command) {
+ my $command_package = $self->_command_package($command_name);
+ eval { Class::MOP::load_class($command_package) };
+ if ($@) {
+ warn $@;
+ $self->say((split /\n/, $@)[0]);
+ return;
+ }
+ $command = $command_package->new(_ime_plugin => $self);
+ $self->_active_commands->{$command_name} = $command;
+ }
+
+ # XXX: commands need to be able to print stuff on their own too
+ #$command->say_cb($args{say_cb});
+
+ if (!$self->_active_commands->{$command_name}->is_active
+ && (!defined($action) || $action !~ /^-/)) {
+ $self->say($command->init($sender)) if $command->can('init');
+ $self->_active_commands->{$command_name}->is_active(1);
+ }
+
+ return unless defined $action;
+
+ if ($action =~ /^-(\w+)\s*(.*)/) {
+ my ($action, $arg) = ($1, $2);
+ if (my $method_meta = $command->meta->get_command($action)) {
+ if ($method_meta->needs_init
+ && !$self->_active_commands->{$command_name}->is_active) {
+ $self->say("$command_name isn't active yet!");
+ return;
+ }
+ my $body = $method_meta->execute($command, $arg,
+ {player => $sender});
+ my @extra_args = $method_meta->meta->does_role('IM::Engine::Plugin::Commands::Trait::Method::Formatted') ? (formatter => $method_meta->formatter) : ();
+ $self->say($body, @extra_args);
+ }
+ else {
+ $self->say("Unknown command $action for command $command_name");
+ return;
+ }
+ }
+ else {
+ # XXX: need better handling for "0", but B::BB doesn't currently
+ # handle that properly either, so
+ # also, this should probably be factored into $say, i think?
+ $self->say($command->default($sender, $action));
+ }
+
+ if (!$command->is_active) {
+ delete $self->_active_commands->{$command_name};
+ }
+
+ return;
+}
+
+sub say {
+ my $self = shift;
+ my ($message, %args) = @_;
+ $message = $args{formatter}->($message) if exists $args{formatter};
+ $self->engine->send_message($self->_last_message->reply($message));
+}
+
+sub command_list {
+ my $self = shift;
+ my $namespace = $self->namespace;
+ return sort map { s/\Q${namespace}:://; lc } $self->commands;
+}
+
+sub is_command {
+ my $self = shift;
+ my ($name) = @_;
+ return (grep { $name eq $_ } $self->command_list) ? 1 : 0;
+}
+
+sub _command_package {
+ my $self = shift;
+ my ($name) = @_;
+ return first { /\Q::$name\E$/i } $self->commands;
+}
+
+sub _find_command {
+ my $self = shift;
+ my ($abbrev) = @_;
+ return $abbrev if $self->is_command($abbrev);
+ return $self->alias->{$abbrev}
+ if exists $self->alias->{$abbrev}
+ && $self->is_command($self->alias->{$abbrev});
+ my @possibilities = grep { /^\Q$abbrev/ } $self->command_list;
+ return $possibilities[0] if @possibilities == 1;
+ return;
+}
+
__PACKAGE__->meta->make_immutable;
no Moose;