line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Bot::Backbone::SendPolicy::MinimumRepeatInterval; |
2
|
|
|
|
|
|
|
$Bot::Backbone::SendPolicy::MinimumRepeatInterval::VERSION = '0.161950'; |
3
|
1
|
|
|
1
|
|
741
|
use v5.10; |
|
1
|
|
|
|
|
3
|
|
4
|
1
|
|
|
1
|
|
4
|
use Moose; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
6
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
with 'Bot::Backbone::SendPolicy'; |
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
5545
|
use AnyEvent; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
393
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# ABSTRACT: Prevent any message from being repeated too often |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
has interval => ( |
14
|
|
|
|
|
|
|
is => 'ro', |
15
|
|
|
|
|
|
|
isa => 'Num', |
16
|
|
|
|
|
|
|
required => 1, |
17
|
|
|
|
|
|
|
); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
has queue_length => ( |
21
|
|
|
|
|
|
|
is => 'ro', |
22
|
|
|
|
|
|
|
isa => 'Int', |
23
|
|
|
|
|
|
|
predicate => 'has_queue', |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
has discard => ( |
28
|
|
|
|
|
|
|
is => 'ro', |
29
|
|
|
|
|
|
|
isa => 'Bool', |
30
|
|
|
|
|
|
|
required => 1, |
31
|
|
|
|
|
|
|
default => 0, |
32
|
|
|
|
|
|
|
); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
has lingering_interval => ( |
36
|
|
|
|
|
|
|
is => 'ro', |
37
|
|
|
|
|
|
|
isa => 'Num', |
38
|
|
|
|
|
|
|
predicate => 'has_lingering_interval', |
39
|
|
|
|
|
|
|
); |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
has cache_key => ( |
43
|
|
|
|
|
|
|
is => 'ro', |
44
|
|
|
|
|
|
|
isa => 'CodeRef', |
45
|
|
|
|
|
|
|
required => 1, |
46
|
|
|
|
|
|
|
default => sub { sub { $_[0]->{text} } }, |
47
|
|
|
|
|
|
|
traits => [ 'Code' ], |
48
|
|
|
|
|
|
|
handles => { |
49
|
|
|
|
|
|
|
'get_cache_key' => 'execute', |
50
|
|
|
|
|
|
|
}, |
51
|
|
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
has send_cache => ( |
55
|
|
|
|
|
|
|
is => 'ro', |
56
|
|
|
|
|
|
|
isa => 'HashRef[ArrayRef[Num]]', |
57
|
|
|
|
|
|
|
required => 1, |
58
|
|
|
|
|
|
|
default => sub { +{} }, |
59
|
|
|
|
|
|
|
traits => [ 'Hash' ], |
60
|
|
|
|
|
|
|
handles => { |
61
|
|
|
|
|
|
|
'list_cache_keys' => 'keys', |
62
|
|
|
|
|
|
|
'delete_cache_key' => 'delete', |
63
|
|
|
|
|
|
|
'last_send_times' => 'get', |
64
|
|
|
|
|
|
|
'set_last_send_times' => 'set', |
65
|
|
|
|
|
|
|
'has_cache_key' => 'defined', |
66
|
|
|
|
|
|
|
}, |
67
|
|
|
|
|
|
|
); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub purge_send_cache { |
71
|
299
|
|
|
299
|
1
|
524
|
my $self = shift; |
72
|
|
|
|
|
|
|
|
73
|
299
|
|
|
|
|
6427
|
my $now = AnyEvent->now; |
74
|
299
|
|
|
|
|
14917
|
for my $key ($self->list_cache_keys) { |
75
|
593
|
|
|
|
|
677
|
my ($last_send, $orig_send) = @{ $self->last_send_times($key) }; |
|
593
|
|
|
|
|
27362
|
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Delete if it's been longer than interval since last send |
78
|
593
|
100
|
|
|
|
23507
|
$self->delete_cache_key($key) |
79
|
|
|
|
|
|
|
if $last_send + $self->interval < $now; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub allow_send { |
85
|
299
|
|
|
299
|
1
|
627
|
my ($self, $options) = @_; |
86
|
|
|
|
|
|
|
|
87
|
299
|
|
|
|
|
900
|
$self->purge_send_cache; |
88
|
|
|
|
|
|
|
|
89
|
299
|
|
|
|
|
880
|
my %send = ( allow => 1 ); |
90
|
299
|
|
|
|
|
5766
|
my $now = AnyEvent->now; |
91
|
299
|
|
|
|
|
14951
|
my $key = $self->get_cache_key($options); |
92
|
299
|
|
|
|
|
402
|
my $save = 1; |
93
|
299
|
|
|
|
|
377
|
my $after = 0; |
94
|
299
|
|
|
|
|
532
|
my ($last_send, $orig_send) = ($now, $now); |
95
|
|
|
|
|
|
|
|
96
|
299
|
100
|
|
|
|
14029
|
if ($self->has_cache_key($key)) { |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# If there's already a cache key in place, don't save |
99
|
291
|
|
|
|
|
529
|
$save = 0; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Discard immediately if requested |
102
|
291
|
50
|
|
|
|
11364
|
if ($self->discard) { |
103
|
291
|
|
|
|
|
664
|
$send{allow} = 0; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# Otherwise, determine how long to delay sending |
107
|
|
|
|
|
|
|
else { |
108
|
0
|
|
|
|
|
0
|
($last_send, $orig_send) = @{ $self->last_send_times($key) }; |
|
0
|
|
|
|
|
0
|
|
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# Wait for whatever is left of the interval since the last send |
111
|
0
|
|
|
|
|
0
|
$send{after} = $after = ($last_send + $self->interval) - $now; |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# If we have a lingering interval, we need to modify the send cache |
114
|
0
|
0
|
|
|
|
0
|
if ($self->has_lingering_interval) { |
115
|
0
|
|
|
|
|
0
|
$save = 1; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# The lingering interval has not been passed, so move the last |
118
|
|
|
|
|
|
|
# send date forward |
119
|
0
|
0
|
|
|
|
0
|
if ($now - $orig_send < $self->lingering_interval) { |
120
|
0
|
|
|
|
|
0
|
$last_send = $now + $after; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# The lingering interval has passed, so move it back to the |
124
|
|
|
|
|
|
|
# original, which should guarantee it is purged next cycle |
125
|
|
|
|
|
|
|
else { |
126
|
0
|
|
|
|
|
0
|
$last_send = $orig_send; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# If the number of messages queued is too long, nevermind... |
131
|
0
|
0
|
0
|
|
|
0
|
$send{allow} = 0 |
132
|
|
|
|
|
|
|
if $self->has_queue |
133
|
|
|
|
|
|
|
and $after / $self->interval > $self->queue_length; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
299
|
100
|
|
|
|
1141
|
$self->set_last_send_times($key, [ $last_send, $orig_send ]) if $save; |
138
|
299
|
|
|
|
|
984
|
return \%send; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
__END__ |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=pod |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=encoding UTF-8 |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head1 NAME |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Bot::Backbone::SendPolicy::MinimumRepeatInterval - Prevent any message from being repeated too often |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head1 VERSION |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
version 0.161950 |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head1 SYNOPSIS |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
send_policy dont_repeat_yourself => ( |
160
|
|
|
|
|
|
|
MinimumRepeatInterval => { |
161
|
|
|
|
|
|
|
interval => 5 * 60, |
162
|
|
|
|
|
|
|
discard => 1, |
163
|
|
|
|
|
|
|
linger_interval => 60 * 60, |
164
|
|
|
|
|
|
|
}, |
165
|
|
|
|
|
|
|
); |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=head1 DESCRIPTION |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
This send policy will prevent a particular message text from being sent more frequently than the permitted L</interval>. |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
For example, suppose you have a service which does a Wikipedia lookup each time someone uses a WikiWord and states the link and first sentence from the article. It would be terribly annoying if, during a heated discussion of this article, when the WikiWord were repeated often, if that resulted in the bot posting and re-posting that sentence and link over and over again. With this policy in place, you don't have to worry about that happening. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head2 interval |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
This is the length of time in fractional seconds during which the bot is not permitted to repeat any particular message. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head2 queue_length |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
This is the maximum number of messages that will be queued for later display before the messages will be discarded. If L</discard> is set to false, it is recommended that you set this value to something reasonable. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=head2 discard |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
When set to a true value, any messasge sent too soon will be discarded immediately. The default is false. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 lingering_interval |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
The L</interval> determines how long the bot must wait before sending a duplicate message text. The lingering interval allows the normal interval to be extended with each new attempt to send the duplicate message text. The extension will occur according to the usual C<interval>, but will not be extended being the values set in fractional seconds on the C<lingering_interval>. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
For example, suppose you have interval set to 5 seconds and lingering interval set to 20 seconds. The bot tries to send the message "blah" and then tries again 3 seconds later and then again 6 seconds after the original. Both of these followup attempts will blocked. Assume this continues at 3 second intervals for 60 seconds. All the messages will be blocked except that first message, the message coming at 21 seconds and 42 seconds. |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=head2 cache_key |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
The documentation in this module fudges a little in how this works. It's actually more flexible than it might seem. Normally, this send policy works based upon the actual message text sent by the user. However, in some cases this might not be convenient. In case you want to make the send policy depend on some other aspect of the message other than the message text, just replace the default C<cache_key> with a new subroutine. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
The given subroutine will be passed a single argument, the options hash reference sent to L</allow_send>. It must return a string (i.e., whatever is returned will be stringified). That string will be used as the cache key. |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
This is an advanced feature. If you can't think of a reason why you'd want to use it, you probably don't want to. This is why the rest of the documentation will assumes the message text, but it's really caching according to whatever this little subroutine returns. |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head2 send_cache |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
This is the actual structure used to determine how recently a particular message text was last sent. Each time the send policy is called, it will be purged of any keys that are no longer relevant. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
It should be safe to save this structure using L<JSON> or L<YAML> or L<MongoDB> or L<Storable> or whatever you like and load it again, if you want the bot's C<send_cache> to survive restarts. However, the structure itself should be considered opaque and might change in a future release of L<Bot::Backbone>. It may even be removed altogether in a future release since there are lots of handy caching tools on the CPAN that might be used in place of this manual one. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=head1 METHODS |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head2 purge_send_cache |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
$self->purge_send_cache; |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
This method may go away in a future release depending on the fate of L</send_cache>. In the meantime, however, this method is used clear the C<send_cache> of expired cache keys. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head2 allow_send |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
This applies the send policy to the message. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head1 AUTHOR |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Andrew Sterling Hanenkamp <hanenkamp@cpan.org> |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
This software is copyright (c) 2016 by Qubling Software LLC. |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
228
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=cut |