use strict; use warnings; package Bot::Net::Script::Bot; use base qw/ App::CLI::Command Class::Accessor::Fast /; use Bot::Net; use File::Copy; use File::Spec; use FindBin; use UNIVERSAL::require; __PACKAGE__->mk_accessors( qw{ bot_class bot_file bot_conf bot_state mixin_classes clone_class clone_conf }); =head1 NAME Bot::Net::Script::Bot - Create a new bot =head1 SYNOPSIS bin/botnet bot --name [ ] =head1 DESCRIPTION This command will create a single bot and automatically generate any additional files such as basic tests, stub configuration file, etc. With no options other than the bot name, this will create a vanilla bot that uses the L mixin and nothing else. The options can further customize this situation. =head1 OPTIONS --name - The name of the bot class to create --mixin - The name of a bot mixin to add to the class. This option may be specified multiple times. --clone - The name of a bot to clone from. If given, the new bot will inherit from the given bot's class and the configuration for that bot will also be used as the basis for the new class =head1 METHODS =head2 options Returns the arguments used by this script. See L. =cut sub options { ( 'name=s' => 'name', 'mixin=s@' => 'mixins', 'clone=s' => 'clone', ); } =head2 run Creates the bot according to the options specified. =cut sub run { my ($self, @args) = @_; defined $self->{name} or die "No bot name given with required --name option.\n"; $self->bot_class( Bot::Net->net_class('Bot', $self->{name}) ); $self->bot_file( File::Spec->catfile( $FindBin::Bin, '..', 'lib', split(/::/, $self->bot_class) ) . '.pm' ); $self->bot_conf( File::Spec->catfile( $FindBin::Bin, '..', 'etc', 'bot', split(/::/, $self->{name}) ) . '.yml' ); $self->bot_state( File::Spec->catfile( $FindBin::Bin, '..', 'var', 'bot', split(/::/, $self->{name}) ) . '.db' ); my @mixins = @{ $self->{mixins} || [] }; my @mixin_classes = ( 'Bot::Net::Bot', map { 'Bot::Net::Mixin::Bot::'.$_ } @mixins ); $self->mixin_classes( \@mixin_classes ); if ($self->{clone}) { $self->clone_class( Bot::Net->net_class('Bot', $self->{clone}) ); $self->clone_config( File::Spec->catfile( $FindBin::Bin, '..', 'etc', 'bot', split(/::/, $self->{clone}) ) . '.yml' ); } $self->_create_bot_module; $self->_create_bot_config; # TODO XXX FIXME Add _create_bot_test() # $self->_create_bot_test; } sub _create_bot_module { my $self = shift; open my $botmod, '>', $self->bot_file or die "Cannot write to @{[$self->bot_file]}: $!"; print "Creating ",$self->bot_file,"...\n"; print $botmod <<"END_OF_BOT_MODULE_START"; use strict; use warnings; package @{[$self->bot_class]}; END_OF_BOT_MODULE_START if ($self->{clone}) { print $botmod <<"END_OF_BOT_MODULE_CLONE"; use base qw/ @{[$self->clone_class]} /; END_OF_BOT_MODULE_CLONE } else { print $botmod <<"END_OF_BOT_MODULE_NEW"; @{[join "\n", map { 'use '.$_.';' } @{ $self->mixin_classes }]} END_OF_BOT_MODULE_NEW } print $botmod <<"END_OF_BOT_MODULE_END"; =head1 NAME @{[$self->bot_class]} - A semi-autonomous agent that does something =head1 SYNOPSIS bin/botnet run --bot $self->{name} =head1 DESCRIPTION A semi-autonomous agent that does something. This documentation needs replacing. =cut 1; END_OF_BOT_MODULE_END } sub _create_bot_config { my $self = shift; if ($self->{clone} and -f $self->clone_conf) { print "Copying ",$self->clone_conf," to ",$self->bot_conf,"...\n"; copy($self->clone_conf, $self->bot_conf); } else { print "Creating ",$self->bot_conf,"...\n"; my @configs; for my $mixin_class (@{ $self->mixin_classes || [] }) { $mixin_class->require; if (my $method = $mixin_class->can('default_configuration')) { push @configs, $method->($mixin_class, $self->bot_class); } } my $bot_conf = Hash::Merge::merge( @configs ); YAML::Syck::DumpFile( $self->bot_conf, $bot_conf ); } } =head1 AUTHORS Andrew Sterling Hanenkamp C<< >> =head1 COPYRIGHT AND LICENSE Copyright 2007 Boomer Consulting, Inc. All Rights Reserved. This program is free software and may be modified and distributed under the same terms as Perl itself. =cut 1;