File Coverage

blib/lib/POE/Component/IRC/Plugin/AutoJoin.pm
Criterion Covered Total %
statement 97 122 79.5
branch 26 50 52.0
condition n/a
subroutine 15 17 88.2
pod 1 12 8.3
total 139 201 69.1


line stmt bran cond sub pod time code
1             package POE::Component::IRC::Plugin::AutoJoin;
2             our $AUTHORITY = 'cpan:HINRIK';
3             $POE::Component::IRC::Plugin::AutoJoin::VERSION = '6.92';
4 9     9   8273 use strict;
  9         26  
  9         358  
5 9     9   53 use warnings FATAL => 'all';
  9         21  
  9         591  
6 9     9   61 use Carp;
  9         20  
  9         770  
7 9     9   72 use IRC::Utils qw(parse_user lc_irc);
  9         19  
  9         894  
8 9     9   197 use POE::Component::IRC::Plugin qw(:ALL);
  9         43  
  9         17169  
9              
10             sub new {
11 6     6 1 11507 my ($package) = shift;
12 6 50       44 croak "$package requires an even number of arguments" if @_ & 1;
13 6         31 my %self = @_;
14 6         36 return bless \%self, $package;
15             }
16              
17             sub PCI_register {
18 6     6 0 1113 my ($self, $irc) = @_;
19              
20 6 100       70 if (!$self->{Channels}) {
    100          
21 2 100       24 if ($irc->isa('POE::Component::IRC::State')) {
22 1         2 for my $chan (keys %{ $irc->channels() }) {
  1         4  
23 0         0 my $lchan = lc_irc($chan, $irc->isupport('MAPPING'));
24             # note that this will not get the real key on ircu servers
25             # in channels where we don't have ops
26 0 0       0 my $key = $irc->is_channel_mode_set($chan, 'k')
27             ? $irc->channel_key($chan)
28             : ''
29             ;
30              
31 0         0 $self->{Channels}->{$lchan} = $key;
32             }
33             }
34             else {
35 1         5 $self->{Channels} = {};
36             }
37             }
38             elsif (ref $self->{Channels} eq 'ARRAY') {
39 2         6 my %channels;
40 2         5 $channels{lc_irc($_, $irc->isupport('MAPPING'))} = undef for @{ $self->{Channels} };
  2         14  
41 2         36 $self->{Channels} = \%channels;
42             }
43              
44 6         20 $self->{tried_keys} = { };
45 6 100       53 $self->{Rejoin_delay} = 5 if !defined $self->{Rejoin_delay};
46 6 50       37 $self->{NickServ_delay} = 5 if !defined $self->{NickServ_delay};
47 6         39 $irc->plugin_register($self, 'SERVER', qw(001 474 isupport chan_mode join kick part identified));
48 6         506 $irc->plugin_register($self, 'USER', qw(join));
49 6         193 return 1;
50             }
51              
52             sub PCI_unregister {
53 6     6 0 1580 return 1;
54             }
55              
56             sub S_001 {
57 5     5 0 256 my ($self, $irc) = splice @_, 0, 2;
58 5         19 delete $self->{alarm_ids};
59 5         17 return PCI_EAT_NONE;
60             }
61              
62             # we join channels after S_isupport in case the server supports
63             # CAPAB IDENTIFY-MSG, so pocoirc can turn it on before we join channels
64             sub S_isupport {
65 5     5 0 300 my ($self, $irc) = splice @_, 0, 2;
66              
67 5 50       13 if (!grep { $_->isa('POE::Component::IRC::Plugin::NickServID') } values %{ $irc->plugin_list() }) {
  20         503  
  5         73  
68             # we don't have to wait for NickServ, so let's join
69 5         23 while (my ($chan, $key) = each %{ $self->{Channels} }) {
  10         710  
70 5 100       32 $irc->yield(join => $chan => (defined $key ? $key : ()));
71             }
72             }
73             else {
74 0         0 while (my ($chan, $key) = each %{ $self->{Channels} }) {
  0         0  
75 0         0 push @{ $self->{alarm_ids} }, $irc->delay(
76             [join => $chan => (defined $key ? $key : ())],
77             $self->{NickServ_delay},
78 0 0       0 );
79             }
80             }
81 5         28 return PCI_EAT_NONE;
82             }
83              
84             sub S_identified {
85 0     0 0 0 my ($self, $irc) = splice @_, 0, 2;
86              
87 0 0       0 if ($self->{alarm_ids}) {
88 0         0 $irc->delay_remove($_) for @{ $self->{alarm_ids} };
  0         0  
89 0         0 delete $self->{alarm_ids};
90              
91 0         0 while (my ($chan, $key) = each %{ $self->{Channels} }) {
  0         0  
92 0 0       0 $irc->yield(join => $chan => (defined $key ? $key : ()));
93             }
94             }
95 0         0 return PCI_EAT_NONE;
96             }
97              
98             # ERR_BANNEDFROMCHAN
99             sub S_474 {
100 4     4 0 212 my ($self, $irc) = splice @_, 0, 2;
101 4         153 my $chan = ${ $_[2] }->[0];
  4         20  
102 4         22 my $lchan = lc_irc($chan, $irc->isupport('MAPPING'));
103 4 50       87 return PCI_EAT_NONE if !$self->{Retry_when_banned};
104              
105 4         15 my $key = $self->{Channels}{$lchan};
106 4 50       20 $key = $self->{tried_keys}{$lchan} if defined $self->{tried_keys}{$lchan};
107 4 50       40 $irc->delay([join => $chan => (defined $key ? $key : ())], $self->{Retry_when_banned});
108 4         1266 return PCI_EAT_NONE;
109             }
110              
111             sub S_chan_mode {
112 3     3 0 146 my ($self, $irc) = splice @_, 0, 2;
113 3         10 pop @_;
114 3         8 my $chan = ${ $_[1] };
  3         9  
115 3         7 my $mode = ${ $_[2] };
  3         53  
116 3 50       18 my $arg = defined $_[3] ? ${ $_[3] } : '';
  3         7  
117 3         14 my $lchan = lc_irc($chan, $irc->isupport('MAPPING'));
118              
119 3 100       64 $self->{Channels}->{$lchan} = $arg if $mode eq '+k';
120 3 50       13 $self->{Channels}->{$lchan} = '' if $mode eq '-k';
121 3         12 return PCI_EAT_NONE;
122             }
123              
124             sub S_join {
125 11     11 0 518 my ($self, $irc) = splice @_, 0, 2;
126 11         37 my $joiner = parse_user(${ $_[0] });
  11         65  
127 11         206 my $chan = ${ $_[1] };
  11         33  
128 11         68 my $lchan = lc_irc($chan, $irc->isupport('MAPPING'));
129              
130 11 50       186 return PCI_EAT_NONE if $joiner ne $irc->nick_name();
131 11         35 delete $self->{alarm_ids};
132              
133 11 50       46 if (defined $self->{tried_keys}{$lchan}) {
134 11         44 $self->{Channels}->{$lchan} = $self->{tried_keys}{$lchan};
135 11         33 delete $self->{tried_keys}{$lchan};
136             }
137             else {
138 0         0 $self->{Channels}->{$lchan} = '';
139             }
140              
141 11         57 return PCI_EAT_NONE;
142             }
143              
144             sub S_kick {
145 4     4 0 285 my ($self, $irc) = splice @_, 0, 2;
146 4         12 my $chan = ${ $_[1] };
  4         15  
147 4         15 my $victim = ${ $_[2] };
  4         15  
148 4         20 my $lchan = lc_irc($chan, $irc->isupport('MAPPING'));
149              
150 4 50       93 if ($victim eq $irc->nick_name()) {
151 4 50       21 if ($self->{RejoinOnKick}) {
152             $irc->delay([
153             'join',
154             $chan,
155             (defined $self->{Channels}->{$lchan} ? $self->{Channels}->{$lchan} : ())
156 4 50       79 ], $self->{Rejoin_delay});
157             }
158 4         1679 delete $self->{Channels}->{$lchan};
159             }
160 4         26 return PCI_EAT_NONE;
161             }
162              
163             sub S_part {
164 0     0 0 0 my ($self, $irc) = splice @_, 0, 2;
165 0         0 my $parter = parse_user(${ $_[0] });
  0         0  
166 0         0 my $chan = ${ $_[1] };
  0         0  
167 0         0 my $lchan = lc_irc($chan, $irc->isupport('MAPPING'));
168              
169 0 0       0 delete $self->{Channels}->{$lchan} if $parter eq $irc->nick_name();
170 0         0 return PCI_EAT_NONE;
171             }
172              
173             sub U_join {
174 15     15 0 2142 my ($self, $irc) = splice @_, 0, 2;
175 15         36 my (undef, $chan, $key) = split /\s/, ${ $_[0] }, 3;
  15         151  
176 15         120 my $lchan = lc_irc($chan, $irc->isupport('MAPPING'));
177              
178 15 50       339 $self->{tried_keys}->{$lchan} = $key if defined $key;
179 15         63 return PCI_EAT_NONE;
180             }
181              
182             1;
183              
184             =encoding utf8
185              
186             =head1 NAME
187              
188             POE::Component::IRC::Plugin::AutoJoin - A PoCo-IRC plugin which
189             keeps you on your favorite channels
190              
191             =head1 SYNOPSIS
192              
193             use POE qw(Component::IRC::State Component::IRC::Plugin::AutoJoin);
194              
195             my $nickname = 'Chatter';
196             my $server = 'irc.blahblahblah.irc';
197              
198             my %channels = (
199             '#Blah' => '',
200             '#Secret' => 'secret_password',
201             '#Foo' => '',
202             );
203              
204             POE::Session->create(
205             package_states => [
206             main => [ qw(_start irc_join) ],
207             ],
208             );
209              
210             $poe_kernel->run();
211              
212             sub _start {
213             my $irc = POE::Component::IRC::State->spawn(
214             Nick => $nickname,
215             Server => $server,
216             ) or die "Oh noooo! $!";
217              
218             $irc->plugin_add('AutoJoin', POE::Component::IRC::Plugin::AutoJoin->new( Channels => \%channels ));
219             $irc->yield(register => qw(join);
220             $irc->yield(connect => { } );
221             }
222              
223             sub irc_join {
224             my $chan = @_[ARG1];
225             $irc->yield(privmsg => $chan => "hi $channel!");
226             }
227              
228              
229             =head1 DESCRIPTION
230              
231             POE::Component::IRC::Plugin::AutoJoin is a L
232             plugin. If you get disconnected, the plugin will join all the channels you were
233             on the next time it gets connected to the IRC server. It can also rejoin a
234             channel if the IRC component gets kicked from it. It keeps track of channel
235             keys so it will be able to rejoin keyed channels in case of reconnects/kicks.
236              
237             If a L
238             plugin has been added to the IRC component, then AutoJoin will wait for a
239             reply from NickServ before joining channels on connect.
240              
241             This plugin requires the IRC component to be
242             L or a subclass thereof.
243              
244             =head1 METHODS
245              
246             =head2 C
247              
248             Takes the following optional arguments:
249              
250             B<'Channels'>, either an array reference of channel names, or a hash reference
251             keyed on channel name, containing the password for each channel. By default it
252             uses the channels the component is already on if you are using
253             L.
254              
255             B<'RejoinOnKick'>, set this to 1 if you want the plugin to try to rejoin a
256             channel (once) if you get kicked from it. Default is 0.
257              
258             B<'Rejoin_delay'>, the time, in seconds, to wait before rejoining a channel
259             after being kicked (if B<'RejoinOnKick'> is on). Default is 5.
260              
261             B<'Retry_when_banned'>, if you can't join a channel due to a ban, set this
262             to the number of seconds to wait between retries. Default is 0 (disabled).
263              
264             B<'NickServ_delay'>, how long (in seconds) to wait for a reply from NickServ
265             before joining channels. Default is 5.
266              
267             Returns a plugin object suitable for feeding to
268             L's C method.
269              
270             =head1 AUTHOR
271              
272             Hinrik Ern SigurEsson, hinrik.sig@gmail.com
273              
274             =cut