File Coverage

blib/lib/Bot/IRC/X/Reminder.pm
Criterion Covered Total %
statement 20 71 28.1
branch 0 46 0.0
condition 0 8 0.0
subroutine 6 9 66.6
pod 0 1 0.0
total 26 135 19.2


line stmt bran cond sub pod time code
1             # ABSTRACT: Bot::IRC plugin for scheduling reminders
2              
3             use 5.014;
4 1     1   399409 use exact;
  1         11  
5 1     1   10  
  1         2  
  1         9  
6             use DateTime;
7 1     1   1558 use DateTime::Duration;
  1         375818  
  1         43  
8 1     1   7 use Time::Crontab;
  1         2  
  1         19  
9 1     1   4  
  1         3  
  1         1317  
10             our $VERSION = '1.07'; # VERSION
11              
12             my ($bot) = @_;
13             $bot->load('Store');
14 1     1 0 5235  
15 1         5 $bot->hook(
16             {
17             to_me => 1,
18             text => qr/
19             ^remind\s+(?<target>\S+)\s+(?<type>at|in|every)\s+
20             (?<expr>
21             (?:[\d\*\/\,\-]+\s+){4}[\d\*\/\,\-]+|
22             \d{1,2}:\d{2}\s*[ap]m?|
23             (?:\d+:)+\d+|
24             \d{3,4}
25             )
26             \s+(?<text>.+)
27             /ix,
28             },
29             sub {
30             my ( $bot, $in, $m ) = @_;
31              
32 0     0   0 my $target = lc( $m->{target} );
33             $target = $in->{nick} if ( $target eq 'me' );
34 0         0  
35 0 0       0 my ( $expr, $lapse ) = ( '', 0 );
36             if ( $m->{expr} =~ /^(\d{1,2}):(\d{2})\s*([ap])m?$/i ) {
37 0         0 $expr = join( ' ', $2, ( ( lc $3 eq 'a' ) ? $1 + 12 : $1 ), '* * *' );
38 0 0       0 }
    0          
    0          
39 0 0       0 elsif ( $m->{expr} =~ /^(\d{1,2})(\d{2})$/ ) {
40             $expr = "$2 $1 * * *";
41             }
42 0         0 elsif ( $m->{expr} =~ /^(?:\d+:)*\d+$/ ) {
43             my @parts = split( /:/, $m->{expr} );
44             shift(@parts) while ( @parts > 6 );
45 0         0 unshift( @parts, 0 ) while ( @parts < 6 );
46 0         0  
47 0         0 $lapse = DateTime->now->add_duration(
48             DateTime::Duration->new(
49             map { $_ => shift @parts } qw( years months weeks days hours minutes )
50             )
51 0         0 )->epoch - time();
  0         0  
52             }
53             else {
54             $expr = $m->{expr};
55             }
56 0         0  
57             my @reminders = @{ $bot->store->get('reminders') || [] };
58             push( @reminders, {
59 0 0       0 author => lc( $in->{nick} ),
  0         0  
60             target => $target,
61             repeat => ( ( lc( $m->{type} ) eq 'every' ) ? 1 : 0 ),
62             text => $m->{text},
63             expr => $expr,
64             time => ( ($lapse) ? time() + $lapse : undef ),
65 0 0       0 lapse => $lapse,
    0          
66             } );
67             $bot->store->set( 'reminders' => \@reminders );
68              
69 0         0 $bot->reply_to('OK.');
70             },
71 0         0 );
72              
73 1         880 $bot->tick(
74             '* * * * *',
75             sub {
76             my ($bot) = @_;
77             my @reminders = @{ $bot->store->get('reminders') || [] };
78 0     0   0 return unless (@reminders);
79 0 0       0 my $reminders_changed = 0;
  0         0  
80 0 0       0  
81 0         0 @reminders = grep { defined } map {
82             $bot->msg( $_->{target}, $_->{text} );
83 0         0 $_->{time} += $_->{lapse} if ( $_->{time} );
84 0         0 $reminders_changed = 1 unless ( $_->{repeat} );
85 0 0       0 ( $_->{repeat} ) ? $_ : undef;
86 0 0       0 }
87 0 0       0 grep {
88             $_->{time} and $_->{time} <= time() or
89             $_->{expr} and Time::Crontab->new( $_->{expr} )->match( time() )
90 0         0 } @reminders;
91 0 0 0     0  
      0        
92             $bot->store->set( 'reminders' => \@reminders ) if ($reminders_changed);
93             },
94 0 0       0 );
95              
96 1         25 $bot->hook(
97             {
98             to_me => 1,
99             text => qr/^reminders\s+(?<command>list|forget)\s+(?<scope>mine|all)\b/i,
100             },
101             sub {
102             my ( $bot, $in, $m ) = @_;
103             my @reminders = @{ $bot->store->get('reminders') || [] };
104 0     0   0  
105 0 0       0 if ( lc( $m->{command} ) eq 'list' ) {
  0         0  
106             if ( lc( $m->{scope} ) eq 'mine' ) {
107 0 0       0 my $me = lc( $in->{nick} );
108 0 0       0 @reminders = grep { $_->{author} eq $me } @reminders;
109 0         0 }
110 0         0 $bot->reply_to(
  0         0  
111             'I have no reminders ' . ( ( lc( $m->{scope} ) eq 'mine' ) ? 'from you ' : '' ) . 'on file.'
112             ) unless (@reminders);
113 0 0       0  
    0          
114             for ( my $i = 0; $i < @reminders; $i++ ) {
115             $bot->reply_to(
116 0         0 ( $reminders[$i]->{expr} || scalar( localtime( $reminders[$i]->{time} ) ) ) . ' ' .
117             ( ( $reminders[$i]->{repeat} ) ? '(repeating) ' : '' ) .
118             'to ' . $reminders[$i]->{target} .
119             ': ' . $reminders[$i]->{text}
120             );
121             sleep 1 if ( $i + 1 < @reminders );
122 0 0 0     0 }
123 0 0       0 }
124             else {
125             if ( lc( $m->{scope} ) eq 'mine' ) {
126             my $me = lc( $in->{nick} );
127 0 0       0 @reminders = grep { $_->{author} ne $me } @reminders;
128 0         0 }
129 0         0 else {
  0         0  
130             @reminders = ();
131             }
132 0         0  
133             $bot->store->set( 'reminders' => \@reminders );
134             $bot->reply_to('OK.');
135 0         0 }
136 0         0  
137             return 1;
138             },
139 0         0 );
140              
141 1         944 $bot->helps( reminder =>
142             'Set reminders for things. ' .
143 1         23 'Usage: <bot nick> remind <nick> <every|at|in> <time expr> <reminder text>. ' .
144             'See also: https://metacpan.org/pod/Bot::IRC::X::Reminder.'
145             );
146             }
147              
148             1;
149              
150              
151             =pod
152              
153             =encoding UTF-8
154              
155             =head1 NAME
156              
157             Bot::IRC::X::Reminder - Bot::IRC plugin for scheduling reminders
158              
159             =head1 VERSION
160              
161             version 1.07
162              
163             =for markdown [![test](https://github.com/gryphonshafer/Bot-IRC-X-Reminder/workflows/test/badge.svg)](https://github.com/gryphonshafer/Bot-IRC-X-Reminder/actions?query=workflow%3Atest)
164             [![codecov](https://codecov.io/gh/gryphonshafer/Bot-IRC-X-Reminder/graph/badge.svg)](https://codecov.io/gh/gryphonshafer/Bot-IRC-X-Reminder)
165              
166             =head1 SYNOPSIS
167              
168             use Bot::IRC;
169              
170             Bot::IRC->new(
171             connect => { server => 'irc.perl.org' },
172             plugins => ['Reminder'],
173             )->run;
174              
175             =head1 DESCRIPTION
176              
177             This L<Bot::IRC> plugin is for scheduling reminders. You can ask the bot to
178             remind someone about something at some future time. If the nick who needs to
179             be reminded isn't online at the time of the reminder, the reminder isn't issued.
180              
181             The general format is:
182              
183             <bot nick> remind <nick|channel> <every|at|in> <time expr> <reminder text>
184              
185             If you specify a "nick" of "me", then the bot will remind your nick.
186              
187             The "every|at|in" is the type of reminder. Each type of reminder requires a
188             slightly different time expression.
189              
190             =head2 at
191              
192             The "at" reminder type requires a time expression in the form of a clock time
193             or a crontab-looking expression. Clock time expressions are in the form
194             C<\d{1,2}:\d{2}\s*[ap]m?> for "normal human time" and C<\d{3,4}> for military
195             time.
196              
197             bot remind me at 2:30p This is to remind you of your dentist appointment.
198             bot remind hoser at 1620 Hey hoser, it's 4:20 PM now.
199             bot remind #team at 0530 Time for someone on the team to make coffee.
200              
201             Crontab-looking expressions are in the C<* * * * *> form.
202              
203             bot remind me at 30 5 * * 1-5 Good morning! It's a great day to code Perl.
204              
205             Once an "at" reminder type triggers, it's done and won't repeat.
206              
207             =head2 in
208              
209             The "in" reminder type requires a number of minutes in the future for when the
210             reminder should happen. In addition to minutes, you can specify hours, days,
211             weeks, or whatever.
212              
213             bot remind me in 30 It has been half-an-hour since you set this reminder.
214             bot remind me in 2:30 It has been 2 hours and 30 minutes.
215             bot remind me in 3:0:0 It has been 3 days.
216             bot remind me in 1:2:0:0 It has been 1 week and 2 days.
217              
218             Once an "in" reminder type triggers, it's done and won't repeat.
219              
220             =head2 every
221              
222             The "every" reminder type is exactly like the "at" reminder type except that
223             the reminder repeatedly triggers when the time expression matches.
224              
225             bot remind me every 30 5 * * 1-5 Another great day to code Perl.
226              
227             =head1 HELPER FUNCTIONS
228              
229             There are a couple of helper functions you can call as well.
230              
231             =head2 list reminders
232              
233             You can list all of your reminders or all reminders from anyone.
234              
235             bot reminders list mine
236             bot reminders list all
237              
238             =head2 forget reminders
239              
240             You can tell the bot to forget all of your reminders or all reminders from
241             everyone.
242              
243             bot reminders forget mine
244             bot reminders forget all
245              
246             =head1 SEE ALSO
247              
248             You can look for additional information at:
249              
250             =over 4
251              
252             =item *
253              
254             L<Bot::IRC>
255              
256             =item *
257              
258             L<GitHub|https://github.com/gryphonshafer/Bot-IRC-X-Reminder>
259              
260             =item *
261              
262             L<MetaCPAN|https://metacpan.org/pod/Bot::IRC::X::Reminder>
263              
264             =item *
265              
266             L<GitHub Actions|https://github.com/gryphonshafer/Bot-IRC-X-Reminder/actions>
267              
268             =item *
269              
270             L<Codecov|https://codecov.io/gh/gryphonshafer/Bot-IRC-X-Reminder>
271              
272             =item *
273              
274             L<CPANTS|http://cpants.cpanauthors.org/dist/Bot-IRC-X-Reminder>
275              
276             =item *
277              
278             L<CPAN Testers|http://www.cpantesters.org/distro/T/Bot-IRC-X-Reminder.html>
279              
280             =back
281              
282             =for Pod::Coverage init
283              
284             =head1 AUTHOR
285              
286             Gryphon Shafer <gryphon@cpan.org>
287              
288             =head1 COPYRIGHT AND LICENSE
289              
290             This software is Copyright (c) 2016-2050 by Gryphon Shafer.
291              
292             This is free software, licensed under:
293              
294             The Artistic License 2.0 (GPL Compatible)
295              
296             =cut