line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Bot::Backbone::Bot; |
2
|
|
|
|
|
|
|
$Bot::Backbone::Bot::VERSION = '0.161950'; |
3
|
4
|
|
|
4
|
|
5325
|
use v5.10; |
|
4
|
|
|
|
|
12
|
|
4
|
4
|
|
|
4
|
|
18
|
use Moose; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
23
|
|
5
|
|
|
|
|
|
|
|
6
|
4
|
|
|
4
|
|
16873
|
use Bot::Backbone::Types qw( EventLoop ServiceList ); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
35
|
|
7
|
4
|
|
|
4
|
|
6736
|
use POE qw( Loop::AnyEvent ); |
|
4
|
|
|
|
|
94010
|
|
|
4
|
|
|
|
|
27
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# ABSTRACT: Provides backbone services to your bot |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
has event_loop => ( |
13
|
|
|
|
|
|
|
is => 'ro', |
14
|
|
|
|
|
|
|
isa => EventLoop, |
15
|
|
|
|
|
|
|
required => 1, |
16
|
|
|
|
|
|
|
default => 'POE::Kernel', |
17
|
|
|
|
|
|
|
); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
has services => ( |
21
|
|
|
|
|
|
|
is => 'ro', |
22
|
|
|
|
|
|
|
isa => ServiceList, |
23
|
|
|
|
|
|
|
required => 1, |
24
|
|
|
|
|
|
|
default => sub { +{} }, |
25
|
|
|
|
|
|
|
traits => [ 'Hash' ], |
26
|
|
|
|
|
|
|
handles => { |
27
|
|
|
|
|
|
|
add_service => 'set', |
28
|
|
|
|
|
|
|
service_names => 'keys', |
29
|
|
|
|
|
|
|
list_services => 'values', |
30
|
|
|
|
|
|
|
destroy_services => 'clear', |
31
|
|
|
|
|
|
|
has_service => 'defined', |
32
|
|
|
|
|
|
|
get_service => 'get', |
33
|
|
|
|
|
|
|
}, |
34
|
|
|
|
|
|
|
); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
has initialized_services => ( |
38
|
|
|
|
|
|
|
is => 'ro', |
39
|
|
|
|
|
|
|
isa => 'HashRef[Bool]', |
40
|
|
|
|
|
|
|
required => 1, |
41
|
|
|
|
|
|
|
default => sub { +{} }, |
42
|
|
|
|
|
|
|
); |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
|
45
|
0
|
|
|
0
|
1
|
0
|
sub bot { $_[0] } |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub _ordered_services { |
49
|
10
|
|
|
10
|
|
16
|
my $self = shift; |
50
|
|
|
|
|
|
|
|
51
|
10
|
|
|
|
|
17
|
my %forest; |
52
|
10
|
|
|
|
|
24
|
for my $pair ($self->meta->services_kv) { |
53
|
24
|
|
|
|
|
34
|
my ($name, $config) = @$pair; |
54
|
|
|
|
|
|
|
|
55
|
24
|
50
|
|
|
|
105
|
if ($config->{service}->does('Bot::Backbone::Service::Role::ChatConsumer')) { |
56
|
0
|
|
|
|
|
0
|
$forest{ $name } = $config->{chat}; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
else { |
59
|
24
|
|
|
|
|
4371
|
$forest{ $name } = undef; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
10
|
|
|
|
|
22
|
my @names; |
64
|
10
|
|
|
|
|
24
|
for my $name (keys %forest) { |
65
|
24
|
50
|
|
|
|
29
|
if (defined $forest{ $name }) { |
66
|
0
|
|
|
|
|
0
|
my $depth = 1; |
67
|
0
|
|
|
|
|
0
|
my $next_name = $forest{ $name }; |
68
|
0
|
|
|
|
|
0
|
while (defined($next_name = $forest{ $next_name })) { |
69
|
0
|
|
|
|
|
0
|
$depth++; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
0
|
|
|
|
|
0
|
$forest{ $name } = $depth; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
else { |
75
|
24
|
|
|
|
|
27
|
$forest{ $name } = 0; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
10
|
|
|
|
|
33
|
return sort { $forest{ $a } <=> $forest{ $b } } keys %forest; |
|
20
|
|
|
|
|
34
|
|
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub construct_services { |
83
|
5
|
|
|
5
|
1
|
360
|
my $self = shift; |
84
|
|
|
|
|
|
|
|
85
|
5
|
|
|
|
|
21
|
my $my_name = $self->meta->name; |
86
|
|
|
|
|
|
|
|
87
|
5
|
|
|
|
|
121
|
my @names = $self->_ordered_services; |
88
|
|
|
|
|
|
|
|
89
|
5
|
|
|
|
|
13
|
for my $name ($self->_ordered_services) { |
90
|
12
|
|
|
|
|
32
|
my $service_config = $self->meta->services->{$name}; |
91
|
12
|
100
|
|
|
|
272
|
next if defined $self->services->{$name}; |
92
|
|
|
|
|
|
|
|
93
|
9
|
|
|
|
|
12
|
my $class_name = $service_config->{service}; |
94
|
9
|
|
|
|
|
164
|
my $service = $class_name->new( |
95
|
|
|
|
|
|
|
%$service_config, |
96
|
|
|
|
|
|
|
name => $name, |
97
|
|
|
|
|
|
|
bot => $self, |
98
|
|
|
|
|
|
|
); |
99
|
|
|
|
|
|
|
|
100
|
9
|
|
|
|
|
298
|
$self->add_service($name, $service); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub initialize_services { |
106
|
4
|
|
|
4
|
1
|
8
|
my $self = shift; |
107
|
|
|
|
|
|
|
|
108
|
4
|
|
|
|
|
156
|
for my $name ($self->service_names) { |
109
|
9
|
50
|
|
|
|
558
|
next if $self->initialized_services->{ $name }; |
110
|
|
|
|
|
|
|
|
111
|
9
|
|
|
|
|
212
|
$self->initialized_services->{ $name }++; |
112
|
|
|
|
|
|
|
|
113
|
9
|
|
|
|
|
269
|
my $service = $self->get_service($name); |
114
|
9
|
|
|
|
|
40
|
$service->initialize; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub run { |
120
|
4
|
|
|
4
|
1
|
1186
|
my $self = shift; |
121
|
|
|
|
|
|
|
|
122
|
4
|
|
|
|
|
22
|
$self->construct_services; |
123
|
4
|
|
|
|
|
26
|
$self->initialize_services; |
124
|
|
|
|
|
|
|
|
125
|
4
|
|
|
|
|
137
|
$self->event_loop->run; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub shutdown { |
130
|
1
|
|
|
1
|
1
|
905175
|
my $self = shift; |
131
|
|
|
|
|
|
|
|
132
|
1
|
|
|
|
|
62
|
$_->shutdown for ($self->list_services); |
133
|
1
|
|
|
|
|
50
|
$self->destroy_services; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
__END__ |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=pod |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=encoding UTF-8 |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=head1 NAME |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Bot::Backbone::Bot - Provides backbone services to your bot |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head1 VERSION |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
version 0.161950 |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head1 SYNOPSIS |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
my $bot = My::Bot->new; |
156
|
|
|
|
|
|
|
$bot->run; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=head1 DESCRIPTION |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
When you use L<Bot::Backbone> in your code, you get a bot implementing this |
161
|
|
|
|
|
|
|
role. It provides tools for constructing, executing, and shutting down services. |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=head2 event_loop |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
Bots do all their work using an event loop. Usually, this is either L<POE> or |
168
|
|
|
|
|
|
|
L<AnyEvent>. Fortunately, these event loops tend to work well together in case |
169
|
|
|
|
|
|
|
you need both. Just in case you need specialized startup for your bot's event |
170
|
|
|
|
|
|
|
loop, though, this is attribute is provided to allow the event loop startup to |
171
|
|
|
|
|
|
|
be customized. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
This is an object or class on which you may call a C<run> with no arguments. It |
174
|
|
|
|
|
|
|
will be called to start the event loop. By default, this is just |
175
|
|
|
|
|
|
|
"L<POE::Kernel>". It is expected that this method will block until the bot is |
176
|
|
|
|
|
|
|
shutdown. |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=head2 services |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
This is a hash of constructed services used by this bot. There should be a key |
181
|
|
|
|
|
|
|
in this hash matching every key in the same attribute in |
182
|
|
|
|
|
|
|
L<Bot::Backbone::Meta::Class>, once L</run> has been called. |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=head2 initialized_services |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
This is a set containing the names of all the services that have been |
187
|
|
|
|
|
|
|
constructed and initialized. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head1 METHODS |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=head2 bot |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
Returns itself. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=head2 construct_services |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
$bot->construct_services; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
This method iterates through the service configurations of the meta class and constructs each service from that configuration. |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
You may run this prior to L</run> to construct your services prior to running. Normally, though, this method is called within L</run>. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head2 initialize_services |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
$bot->initialize_services; |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
If more services are added to the bot later, this method may be called to initialize services after the new services have been constructed. |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head2 run |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
$bot->run; |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
This starts your bot running. It constructs the services if they have not yet been constructed. Then, it initializes each service. Finally, it starts the L<POE> event loop. This last part really isn't it's business and might go away in the future. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
This method will not return until the event loop terminates. The usual way to do this is to call L</shutdown>. |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=head2 shutdown |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
$bot->shutdown; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
You may call this at any time while your bot is running to shutdown all the services. This notifies each service that it should shutdown (i.e., finish or terminate any pending jobs in the event loop). It then clears the L</services> hash, which should cause all services to be destroyed. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head1 CAVEATS |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
This thing sort of kind of needs L<POE> to be any kind of useful. However, L<POE> seems to have weird drawbacks. I have some planned work-arounds for this being an explicit and required dependency, but it's there for now. |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
Second, if you use the Jabber chat service, you need L<AnyEvent>. Mostly, L<AnyEvent> and L<POE> seem to get along, but it's slow and I've found that timers, in particular, just plain don't work quite right. |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=head1 AUTHOR |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
Andrew Sterling Hanenkamp <hanenkamp@cpan.org> |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
This software is copyright (c) 2016 by Qubling Software LLC. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
238
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=cut |