| POE-Component-IRC documentation | Contained in the POE-Component-IRC distribution. |
POE::Component::IRC::Plugin::PlugMan - A PoCo-IRC plugin that provides plugin management services.
use strict;
use warnings;
use POE qw(Component::IRC::State);
use POE::Component::IRC::Plugin::PlugMan;
my $botowner = 'somebody!*@somehost.com';
my $irc = POE::Component::IRC::State->spawn();
POE::Session->create(
package_states => [
main => [ qw(_start irc_plugin_add) ],
],
);
sub _start {
$irc->yield( register => 'all' );
$irc->plugin_add( 'PlugMan' => POE::Component::IRC::Plugin::PlugMan->new( botowner => $botowner ) );
return;
}
sub irc_plugin_add {
my ($desc, $plugin) = @_[ARG0, ARG1];
if ($desc eq 'PlugMan') {
$plugin->load( 'Connector', 'POE::Component::IRC::Plugin::Connector' );
}
return;
}
POE::Component::IRC::Plugin::PlugMan is a POE::Component::IRC plugin management plugin. It provides support for 'on-the-fly' loading, reloading and unloading of plugin modules, via object methods that you can incorporate into your own code and a handy IRC interface.
newTakes two optional arguments:
'botowner', an IRC mask to match against for people issuing commands via the IRC interface;
'auth_sub', a sub reference which will be called to determine if a user may issue commands via the IRC interface. Overrides 'botowner'. It will be called with three arguments: the IRC component object, the nick!user@host and the channel name as arguments. It should return a true value if the user is authorized, a false one otherwise.
'debug', set to a true value to see when stuff goes wrong;
Not setting 'botowner' or 'auth_sub' effectively disables the IRC interface.
If 'botowner' is specified the plugin checks that it is being loaded into a POE::Component::IRC::State or sub-class and will fail to load otherwise.
Returns a plugin object suitable for feeding to
POE::Component::IRC's plugin_add method.
loadLoads a managed plugin.
Takes two mandatory arguments, a plugin descriptor and a plugin package name. Any other arguments are used as options to the loaded plugin constructor.
$plugin->load( 'Connector', 'POE::Component::IRC::Plugin::Connector', delay, 120 );
Returns true or false depending on whether the load was successfully or not.
unloadUnloads a managed plugin.
Takes one mandatory argument, a plugin descriptor.
$plugin->unload( 'Connector' );
Returns true or false depending on whether the unload was successfully or not.
reloadUnloads and loads a managed plugin, with applicable plugin options.
Takes one mandatory argument, a plugin descriptor.
$plugin->reload( 'Connector' );
loadedTakes no arguments.
$plugin->loaded();
Returns a list of descriptors of managed plugins.
An IRC interface is enabled by specifying a "botowner" mask to
new|/new. Commands may be either invoked via a PRIVMSG directly to
your bot or in a channel by prefixing the command with the nickname of your
bot. One caveat, the parsing of the irc command is very rudimentary (it
merely splits the line on spaces).
plugin_addTakes the same arguments as load|/load.
plugin_delTakes the same arguments as unload|/unload.
plugin_reloadTakes the same arguments as reload|/reload.
plugin_loadedReturns a list of descriptors of managed plugins.
plugin_listReturns a list of descriptors of *all* plugins loaded into the current PoCo-IRC component.
Chris 'BinGOs' Williams
| POE-Component-IRC documentation | Contained in the POE-Component-IRC distribution. |
package POE::Component::IRC::Plugin::PlugMan; BEGIN { $POE::Component::IRC::Plugin::PlugMan::AUTHORITY = 'cpan:HINRIK'; } BEGIN { $POE::Component::IRC::Plugin::PlugMan::VERSION = '6.68'; } use strict; use warnings FATAL => 'all'; use Carp; use IRC::Utils qw( matches_mask parse_user ); use POE::Component::IRC::Plugin qw( :ALL ); BEGIN { # Turn on the debugger's symbol source tracing $^P |= 0x10; # Work around bug in pre-5.8.7 perl where turning on $^P # causes caller() to be confused about eval {}'s in the stack. # (See http://rt.perl.org/rt3/Ticket/Display.html?id=35059 for more info.) eval 'sub DB::sub' if $] < 5.008007; } sub new { my ($package) = shift; croak "$package requires an even number of arguments" if @_ & 1; my %args = @_; $args{ lc $_ } = delete $args{ $_ } for keys %args; return bless \%args, $package; } ########################## # Plugin related methods # ########################## sub PCI_register { my ($self, $irc) = @_; $self->{irc} = $irc; $irc->plugin_register( $self, 'SERVER', qw(public msg) ); $self->{commands} = { PLUGIN_ADD => sub { my ($self, $method, $recipient, @cmd) = @_; my $msg = $self->load(@cmd) ? 'Done.' : 'Nope'; $self->{irc}->yield($method => $recipient => $msg); }, PLUGIN_DEL => sub { my ($self, $method, $recipient, @cmd) = @_; my $msg = $self->unload(@cmd) ? 'Done.' : 'Nope'; $self->{irc}->yield($method => $recipient => $msg); }, PLUGIN_RELOAD => sub { my ($self, $method, $recipient, @cmd) = @_; my $msg = $self->reload(@cmd) ? 'Done.' : 'Nope'; $self->{irc}->yield($method => $recipient => $msg); }, PLUGIN_LIST => sub { my ($self, $method, $recipient, @cmd) = @_; my @aliases = keys %{ $self->{irc}->plugin_list() }; my $msg = @aliases ? 'Plugins [ ' . join(', ', @aliases ) . ' ]' : 'No plugins loaded.'; $self->{irc}->yield($method => $recipient => $msg); }, PLUGIN_LOADED => sub { my ($self, $method, $recipient, @cmd) = @_; my @aliases = $self->loaded(); my $msg = @aliases ? 'Managed Plugins [ ' . join(', ', @aliases ) . ' ]' : 'No managed plugins loaded.'; $self->{irc}->yield($method => $recipient => $msg); }, }; return 1; } sub PCI_unregister { my ($self, $irc) = @_; delete $self->{irc}; return 1; } sub S_public { my ($self, $irc) = splice @_, 0 , 2; my $who = ${ $_[0] }; my $channel = ${ $_[1] }->[0]; my $what = ${ $_[2] }; my $me = $irc->nick_name(); my ($command) = $what =~ m/^\s*\Q$me\E[:,;.!?~]?\s*(.*)$/i; return PCI_EAT_NONE if !$command || !$self->_authed($who, $channel); my (@cmd) = split(/ +/, $command); my $cmd = uc (shift @cmd); if (defined $self->{commands}->{$cmd}) { $self->{commands}->{$cmd}->($self, 'privmsg', $channel, @cmd); } return PCI_EAT_NONE; } sub S_msg { my ($self, $irc) = splice @_, 0 , 2; my $who = ${ $_[0] }; my $nick = parse_user($who); my $channel = ${ $_[1] }->[0]; my $command = ${ $_[2] }; my (@cmd) = split(/ +/,$command); my $cmd = uc (shift @cmd); return PCI_EAT_NONE if !$self->_authed($who, $channel); if (defined $self->{commands}->{$cmd}) { $self->{commands}->{$cmd}->($self, 'notice', $nick, @cmd); } return PCI_EAT_NONE; } ############################### # Plugin manipulation methods # ############################### sub load { my ($self, $desc, $plugin) = splice @_, 0, 3; return if !$desc || !$plugin; my $object; my $module = ref $plugin || $plugin; if (! ref $plugin){ $module .= '.pm' if $module !~ /\.pm$/; $module =~ s/::/\//g; eval "require $plugin"; if ($@) { delete $INC{$module}; $self->_unload_subs($plugin); die "$@\n"; } $object = $plugin->new( @_ ); return if !$object; } else { $object = $plugin; $plugin = ref $object; } my $args = [ @_ ]; $self->{plugins}->{ $desc }->{module} = $module; $self->{plugins}->{ $desc }->{plugin} = $plugin; my $return = $self->{irc}->plugin_add( $desc, $object ); if ( $return ) { # Stash away arguments for use later by _reload. $self->{plugins}->{ $desc }->{args} = $args; } else { # Cleanup delete $self->{plugins}->{ $desc }; } return $return; } sub unload { my ($self, $desc) = splice @_, 0, 2; return if !$desc; my $plugin = $self->{irc}->plugin_del( $desc ); return if !$plugin; my $module = $self->{plugins}->{ $desc }->{module}; my $file = $self->{plugins}->{ $desc }->{plugin}; delete $INC{$module}; delete $self->{plugins}->{ $desc }; $self->_unload_subs($file); return 1; } sub _unload_subs { my $self = shift; my $file = shift || return; for my $sym ( grep { index( $_, "$file:" ) == 0 } keys %DB::sub ) { eval { undef &$sym }; warn "$sym: $@\n" if $@; delete $DB::sub{$sym}; } return 1; } sub reload { my ($self, $desc) = splice @_, 0, 2; return if !defined $desc; my $plugin_state = $self->{plugins}->{ $desc }; return if !$plugin_state; warn "Unloading plugin $desc\n" if $self->{debug}; return if !$self->unload( $desc ); warn "Loading plugin $desc " . $plugin_state->{plugin} . ' [ ' . join(', ',@{ $plugin_state->{args} }) . " ]\n" if $self->{debug}; return if !$self->load( $desc, $plugin_state->{plugin}, @{ $plugin_state->{args} } ); return 1; } sub loaded { my $self = shift; return keys %{ $self->{plugins} }; } sub _authed { my ($self, $who, $chan) = @_; return $self->{auth_sub}->($self->{irc}, $who, $chan) if $self->{auth_sub}; return 1 if matches_mask($self->{botowner}, $who); return; } 1;