package Bot::Cobalt::Timer;
$Bot::Cobalt::Timer::VERSION = '0.017004';
use strictures 2;
use Carp;
use Bot::Cobalt::Common ':types';
use Moo;
## It's possible to pass in a different core.
## (Allows timers to fire against different syndicators if needed)
has core => (
lazy => 1,
is => 'rw',
isa => Object,
builder => sub {
require Bot::Cobalt::Core;
Bot::Cobalt::Core->instance
|| die "Cannot find active Bot::Cobalt::Core instance"
},
);
## May have a timer ID specified at construction for use by
## timer pool managers; if not, creating IDs is up to them.
## (See ::Core::Role::Timers)
has id => (
lazy => 1,
is => 'rw',
isa => Str,
predicate => 'has_id'
);
## 'at' is set regardless of whether delay()/at() is used
## (or 0 if none is ever set)
has at => (
lazy => 1,
is => 'rw',
isa => Num,
builder => sub { 0 },
);
has delay => (
lazy => 1,
is => 'rw',
isa => Num,
predicate => 'has_delay',
clearer => 'clear_delay',
builder => sub { 0 },
trigger => sub {
my ($self, $value) = @_;
$self->at( time() + $value );
},
);
has event => (
lazy => 1,
is => 'rw',
isa => Str,
predicate => 'has_event',
);
has args => (
lazy => 1,
is => 'rw',
isa => ArrayObj,
coerce => 1,
builder => sub { [] },
);
has alias => (
is => 'rw',
isa => Str,
builder => sub { scalar caller },
);
has context => (
lazy => 1,
is => 'rw',
isa => Str,
predicate => 'has_context',
builder => sub { 'Main' },
);
has text => (
lazy => 1,
is => 'rw',
isa => Str,
predicate => 'has_text'
);
has target => (
lazy => 1,
is => 'rw',
isa => Str,
predicate => 'has_target'
);
has type => (
lazy => 1,
is => 'rw',
isa => Str,
builder => sub {
my ($self) = @_;
if ($self->has_context && $self->has_target) {
## Guessing we're a message.
return 'msg'
} else {
## Guessing we're an event.
return 'event'
}
},
coerce => sub {
$_[0] =~ /message|privmsg/i ? 'msg' : lc($_[0]) ;
},
);
sub _process_type {
my ($self) = @_;
## If this is a special type, set up event and args.
my $type = lc($self->type);
if (grep { $_ eq $type } qw/msg message privmsg action/) {
my $ev_name = $type eq 'action' ?
'action' : 'message' ;
$self->event( $ev_name );
my @ev_args = ( $self->context, $self->target, $self->text );
$self->args( \@ev_args );
}
return 1
}
sub is_ready {
my ($self) = @_;
return 1 if $self->at <= time;
return
}
sub execute {
my ($self) = @_;
$self->_process_type;
unless ( $self->event ) {
carp "timer execute called but no event specified";
return
}
unless ( $self->core->can('send_event') ) {
carp "timer execute called but specified core can't send_event";
return
}
my $args = $self->args;
$self->core->send_event( $self->event, @$args );
return 1
}
sub execute_if_ready { execute_ready(@_) }
sub execute_ready {
my ($self) = @_;
return $self->execute if $self->is_ready;
return
}
1;
__END__
=pod
=head1 NAME
Bot::Cobalt::Timer - Cobalt timer objects
=head1 SYNOPSIS
my $timer = Bot::Cobalt::Timer->new(
event => 'my_timed_event',
args => [ $one, $two ],
);
$timer->delay(30);
## Add this instance to Core's TimerPool, for example:
$core->timer_set( $timer );
=head1 DESCRIPTION
A B<Bot::Cobalt::Timer> instance represents a single timed event.
These are usually constructed for use by the L<Bot::Cobalt::Core> TimerPool;
also see L<Bot::Cobalt::Core::Role::Timers/timer_set>.
my $timer = Bot::Cobalt::Timer->new;
By default, timers that are executed will fire against the
L<Bot::Cobalt::Core> singleton; you can pass in a different 'core =>'
specification if needed.
=head1 METHODS
=head2 Timer settings
=head3 at
The absolute time that this timer is supposed to fire (epoch seconds).
This is normally set automatically when L</delay> is called.
(If it is tweaked manually, L</delay> is irrelevant information.)
=head3 delay
The time this timer is supposed to fire expressed in seconds from the
time it was set.
(Sets L</at> to I<time()> + I<delay>)
=head3 event
The name of the event that should be fired via B<send_event> when this
timer is executed.
=head3 args
A L<List::Objects::WithUtils::Array> containing any arguments attached to the
L</event>.
=head3 id
This timer's unique identifier, used as a key in timer pools.
Note that a unique random ID is added when the Timer object is passed to
L<Bot::Cobalt::Core::Role::Timers/timer_set> if no B<id> is explicitly
specified.
=head3 alias
The alias tag attached to this timer. Defaults to C<caller()>
=head3 type
The type of event.
Valid types as of this writing are B<msg>, B<action>, and B<event>.
B<msg> and B<action> types require L</context>, L</text>, and L</target>
attributes be specified.
If no type has been specified for this timer, B<type()> returns our best
guess; for timed events carrying a L</context> and L</target> the
default is B<msg>.
This is used to set up proper event names for special timer types.
=head3 context
B<msg and action timer types only>
The server context for an outgoing B<msg> or B<action>.
See L</type>
=head3 text
B<msg and action timer types only>
The text string to send with an outgoing B<msg> or B<action>.
See L</type>
=head3 target
B<msg and action timer types only>
The target channel or nickname for an outgoing B<msg> or B<action>.
See L</type>
=head2 Execution
A timer object can be instructed to execute as long as it was provided a
proper B<core> object at construction time -- this is normally
L<Bot::Cobalt::Core>, but any class that can B<send_event> will do.
=head3 is_ready
Returns boolean true if the timer is ready to execute; in other words,
if the specified L</at> is reached.
=head3 execute_if_ready
L</execute> the timer if L</is_ready> is true.
=head3 execute
Execute the timer; if our B<core> object can B<send_event>, the timer's
event is broadcast. Otherwise the timer will warn and return.
=head1 AUTHOR
Jon Portnoy <avenj@cobaltirc.org>
=cut