File Coverage

blib/lib/POE/Component/OSCAR.pm
Criterion Covered Total %
statement 22 33 66.6
branch n/a
condition n/a
subroutine 8 10 80.0
pod n/a
total 30 43 69.7


line stmt bran cond sub pod time code
1             package POE::Component::OSCAR;
2              
3 1     1   25158 use 5.006;
  1         4  
  1         42  
4 1     1   6 use strict;
  1         2  
  1         41  
5 1     1   5 use vars qw($VERSION);
  1         6  
  1         72  
6 1     1   869 use Filter::Template;
  1         9098  
  1         7  
7 1     1   948 use POE 0.28;
  1         54839  
  1         7  
8              
9             $VERSION = .05;
10              
11             # make life prettier
12             const KERNEL $_[KERNEL]
13             const HEAP $_[HEAP]
14             const SESSION $_[SESSION]
15             const OBJECT $_[OBJECT]
16             const ARGS @_[ARG0..$#_]
17             const SENDER $_[SENDER]
18              
19             # Create an object skeleton to make code in the calling app prettier
20             sub new {
21 0     0     my $class = shift;
22 0           my @args = @_;
23              
24 0           my $self = {
25             session => POE::Session->create(
26             package_states => [
27             OSCARSession => [qw(_start _stop _default queue_im rd_ok wr_ok ex_ok set_callback connection_changed quit)]
28             ],
29             args => [ @args ],
30             )
31             };
32              
33 0           bless $self, $class;
34              
35 0           return $self;
36             }
37              
38             # Pass $oscar->anymethod calls to the anymethod state of the POE
39             # session, which will get picked up by _default and passed to the
40             # Net::OSCAR object
41             sub AUTOLOAD {
42 0     0     my $self = shift;
43              
44 1     1   116905 use vars qw($AUTOLOAD);
  1         3  
  1         25  
45 0           my $state = $AUTOLOAD;
46 0           $state =~ s/.*:://;
47 0           $poe_kernel->post( $self->{session} => $state => @_ );
48             }
49              
50             package OSCARSession;
51              
52 1     1   227 use POE;
  1         2  
  1         6  
53 1     1   838 use Net::OSCAR 0.62;
  0            
  0            
54             use Time::HiRes qw(sleep time);
55              
56             # store filenos so if we get a new one, we can have POE watch it
57             my %filenos = ();
58              
59             sub _start {
60             my %args = ARGS;
61              
62             KERNEL->sig( INT => 'quit' );
63             HEAP->{throttle_time} = delete $args{throttle};
64             HEAP->{im_queue} = [];
65              
66             my $oscar = HEAP->{oscar} = Net::OSCAR->new( %args );
67             # $oscar->loglevel( 10, 1 );
68              
69             $oscar->set_callback_connection_changed( SESSION->callback( 'connection_changed' ) );
70             }
71              
72             sub rd_ok {
73             my ($socket) = ARGS;
74             my $fileno = fileno($socket);
75             return unless $fileno;
76             my $conn = HEAP->{oscar}->findconn($fileno);
77             sleep 0.1;
78             $conn->process_one(1, 0);
79             }
80              
81             sub wr_ok {
82             my ($socket) = ARGS;
83             my $fileno = fileno($socket);
84             return unless $fileno;
85             my $conn = HEAP->{oscar}->findconn($fileno);
86             sleep 0.1;
87             $conn->process_one(0, 1);
88             }
89              
90             sub ex_ok {
91             my ($socket) = ARGS;
92             my $fileno = fileno($socket);
93             return unless $fileno;
94             my $conn = HEAP->{oscar}->findconn($fileno);
95             KERNEL->select($socket); # stop POE from watching the socket
96             $conn->{sockerr} = 1;
97             $conn->disconnect();
98             sleep 0.1;
99             }
100              
101             sub _stop {
102             }
103              
104             sub queue_im {
105             my @send_im_args = ARGS;
106              
107             my $queue = HEAP->{im_queue};
108             if (@send_im_args) {
109             push @$queue, \@send_im_args;
110             }
111              
112             if (@$queue and HEAP->{last_im_sent_time} + HEAP->{throttle_time} < time) {
113             my $args = shift @$queue;
114             eval {
115             HEAP->{oscar}->send_im( @$args );
116             };
117             warn $@ if $@;
118              
119             HEAP->{last_im_sent_time} = time;
120              
121             } else {
122             KERNEL->delay( "queue_im",
123             HEAP->{last_im_sent_time} + HEAP->{throttle_time} - time );
124             return;
125             }
126              
127             if (@$queue) {
128             KERNEL->delay( "queue_im", HEAP->{throttle_time} );
129             }
130             }
131              
132             sub _default {
133             my ($method, $args) = ARGS;
134              
135             if ($method eq 'send_im' and HEAP->{throttle_time}) {
136             KERNEL->yield( "queue_im", @$args );
137             return;
138             }
139              
140             eval {
141             HEAP->{oscar}->$method( @$args );
142             };
143             warn $@ if $@;
144             }
145              
146             sub quit {
147             exit;
148             }
149              
150             sub set_callback {
151             my ($callback, $state) = ARGS;
152              
153             my $method = "set_callback_${callback}";
154             HEAP->{oscar}->$method( SENDER->postback( $state ) );
155             }
156              
157             # Net::OSCAR will send us one of four states when a connection's
158             # state changes: read, write, readwrite, and deleted. Unfortunately,
159             # "connected" is not one of these, so when we see anything other than
160             # a "deleted", we check %filenos to see if POE is already watching it.
161             # If not, we ask POE to watch it.
162             sub connection_changed {
163             my @args = ARGS;
164             my ($oscar_obj, $connection, $state) = @{$args[1]};
165              
166             my $socket = $connection->{socket};
167             if ($state eq 'deleted') {
168             delete $filenos{ fileno($socket) };
169             $poe_kernel->select( $socket );
170             } elsif (!$filenos{ fileno($socket) }) {
171             # Need the line below for faster machines; otherwise some bits seem to get lost
172             # along the way. It's a hack, but it should only get called twice in all (once
173             # upon connection, once upon signon) so for now it should suffice.
174             sleep 0.1;
175             $filenos{ fileno($socket) }++;
176             $poe_kernel->select( $socket, 'rd_ok', 'wr_ok', 'ex_ok' );
177             }
178             }
179              
180             1;
181             __END__