File Coverage

blib/lib/E2/Ticker.pm
Criterion Covered Total %
statement 45 236 19.0
branch 10 98 10.2
condition 2 6 33.3
subroutine 9 42 21.4
pod 17 18 94.4
total 83 400 20.7


line stmt bran cond sub pod time code
1             # E2::Ticker
2             # Jose M. Weeks
3             # 17 June 2003
4             #
5             # See bottom for pod documentation.
6              
7             package E2::Ticker;
8              
9 2     2   26955 use 5.006;
  2         9  
  2         99  
10 2     2   14 use strict;
  2         4  
  2         77  
11 2     2   12 use warnings;
  2         4  
  2         70  
12 2     2   13 use Carp;
  2         3  
  2         560  
13 2     2   710 use E2::Interface;
  2         12  
  2         12599  
14              
15             our @ISA = ("E2::Interface");
16             our $VERSION = "0.32";
17             our $DEBUG; *DEBUG = *E2::Interface::DEBUG;
18              
19             our %xml_title = (
20             interfaces => "XML Interfaces Ticker",
21             clientversions => "Client Version XML Ticker",
22             messages => "Universal Message XML Ticker",
23             session => "Personal Session XML Ticker",
24             search => "E2 XML Search Interface",
25             scratch => "Scratch Pad XML Ticker",
26             vars => "Raw VARS XML Ticker",
27             timesince => "Time Since XML Ticker",
28             usersearch => "User Search XML Ticker II",
29             heaven => "Node Heaven XML Ticker",
30             bestusers => "Everything's Best Users XML Ticker",
31             coolnodes => "Cool Nodes XML Ticker II",
32             edcools => "Editor Cools XML Ticker",
33             random => "Random Nodes XML Ticker",
34             rooms => "Available Rooms XML Ticker",
35             otherusers => "Other Users XML Ticker II",
36             newwriteups => "New Writeups XML Ticker"
37             );
38              
39             # Prototypes
40              
41             sub new;
42              
43             sub new_writeups;
44             sub other_users;
45             sub random_nodes;
46             sub cool_nodes;
47             sub editor_cools;
48             sub time_since;
49              
50             sub random_nodes_wit;
51             sub time_since_now;
52              
53             # Private
54              
55             sub parse;
56              
57             # Methods
58              
59             sub new {
60 1     1 1 11 my $arg = shift;
61 1   33     9 my $class = ref( $arg ) || $arg;
62 1         12 my $self = $class->SUPER::new();
63              
64 1         7 $self->{xml_interfaces} = \%xml_title;
65              
66 1         3 $self->{ticker_string} = undef;
67            
68 1         2 bless( $self, $class );
69 1         3 return $self;
70             }
71              
72             sub use_string {
73 1 50   1 1 99 my $self = shift or croak "Usage: use_string E2TICKER, STRING";
74 1         2 my $string = shift;
75              
76 1         3 $self->{ticker_string} = $string;
77             }
78              
79             # This method is private (undocumented). It should not be called from
80             # anywhere except internally or in derived classes.
81             #
82             # It takes the following parameters:
83             # E2TICKER - $self
84             # TYPE - Type of ticker to load
85             # HANDLERS - a reference to a hash of twig parsers
86             # LISTREF - a reference to the list we want to return
87             # OPTIONS - set of attr, val pairs used in the POST request
88              
89             sub parse {
90 1 50   1 0 4 croak "Usage: parse E2TICKER, TYPE, HANDLERS, LISTREF, [ OPTIONS ]"
91             if @_ < 4;
92            
93 1         2 my $self = shift;
94 1         2 my $type = shift;
95 1         1 my $handlers = shift;
96 1         2 my $listref = shift;
97              
98 1 50       4 warn "E2::Ticker::parse\n" if $DEBUG > 1;
99 1 50       4 warn "Parsing $type ticker:\n" . Dumper( $handlers )
100             if $DEBUG > 2;
101              
102             # Sanity check
103              
104 1 50 33     7 if( ref $handlers ne 'HASH' || ref $listref ne 'ARRAY' ) {
105 0         0 croak "Usage: parse E2TICKER, TYPE, HANDLERS, LISTREF, " .
106             "[ OPTIONS ]";
107             }
108              
109             # Note: The exception below will only be raised if there are
110             # bugs in e2interface, unless applications are calling
111             # E2::Ticker::parse on their own, which they shouldn't be
112             # doing. This exception is undocumented (it is not
113             # thrown by any documented methods).
114              
115 1         3 my $title = $self->{xml_interfaces}->{$type};
116 1 50       4 if( !$title ) {
117 0         0 croak "Invalid ticker type: $type";
118             }
119              
120             # This is here in case (for some oddball reason) someone wants
121             # to load a ticker from a text string. This is used for the test
122             # cases, and probably for cacheing and so on...
123              
124 1 50       4 if( my $s = $self->{ticker_string} ) {
125 1         1 $self->{ticker_string} = undef;
126 1         9 $self->parse_twig( $s, $handlers );
127 0         0 return @$listref;
128             }
129            
130             # Otherwise, do the normal thing, which is to load the
131             # node from e2.
132              
133             return $self->thread_then(
134             [
135             \&E2::Interface::process_request,
136             $self,
137             node => $title,
138             @_
139             ],
140             sub {
141 0     0   0 $self->parse_twig(shift, $handlers);
142 0         0 return @$listref;
143             }
144 0         0 );
145             }
146              
147             sub load_interfaces {
148 0 0   0 1 0 my $self = shift or croak "Usage: interfaces E2TICKER";
149              
150 0 0       0 warn "E2::Ticker::load_interfaces\n" if $DEBUG > 1;
151            
152             my $handlers = {
153             'this' => sub {
154 0     0   0 (my $a, my $b) = @_;
155 0         0 $self->{xml_interfaces}->{interfaces} =
156             $b->text;
157             },
158             'xmlexport' => sub {
159 0     0   0 (my $a, my $b) = @_;
160 0         0 my $c = $b->{att}->{iface};
161 0         0 $self->{xml_interfaces}->{$c} = $b->text;
162             }
163 0         0 };
164              
165             # Since we're loading a URL instead of a node or node_id,
166             # we're going to have to do this one without the help
167             # of E2::Interface::process_request, and therefore without
168             # E2::Ticker::parse.
169              
170             # If we're working threaded, we have to do it the hard way
171              
172 0 0       0 if( $self->{threads} ) {
173             return thread_then(
174             [
175             \&E2::Interface::start_job,
176             $self,
177             'POST',
178             "http://$self->{domain}/interfaces.xml",
179             $self->{cookie},
180             $self->{agentstring},
181             links_noparse => $self->{links_noparse},
182             ],
183             sub {
184 0     0   0 my $response = shift;
185              
186 0         0 $self->{xml_interfaces} = {};
187            
188 0         0 $self->parse_twig( $response, $handlers );
189            
190 0         0 return 1;
191              
192 0         0 });
193             }
194              
195             # Otherwise, do the same as above, but without passing the work off
196             # to another thread.
197              
198 0         0 my $response = process_request_raw(
199             'POST',
200             "http://$self->{domain}/interfaces.xml",
201             $self->{cookie},
202             $self->{agentstring},
203             links_noparse => $self->{links_noparse},
204             );
205              
206 0         0 $self->cookie( extract_cookie( $response ) );
207              
208 0         0 my $xml = post_process( $response );
209            
210 0         0 $self->{xml_interfaces} = {};
211              
212 0         0 $self->parse_twig( $xml, $handlers );
213              
214 0         0 return 1;
215             }
216              
217             sub new_writeups {
218 1 50   1 1 9 my $self = shift or croak "Usage: new_writeups E2TICKER [, COUNT ]";
219 1         1 my $count = shift;
220              
221 1         2 my %opt;
222              
223             my @writeups;
224              
225 1 50       3 warn "E2::Ticker::new_writeups" if $DEBUG > 1;
226              
227 1 50       3 $opt{count} = $count if $count;
228              
229             my $handlers = {
230             'wu' => sub {
231 0     0   0 (my $a, my $b) = @_;
232 0         0 my $wu = {};
233            
234 0         0 $wu->{type} = $b->{att}->{wrtype};
235              
236 0         0 my $c = $b->first_child('e2link');
237            
238 0         0 $wu->{title} = $c->text;
239 0         0 $wu->{id} = $c->{att}->{node_id};
240              
241 0         0 $c = $b->first_child('author')->first_child('e2link');
242 0         0 $wu->{author} = $c->text;
243 0         0 $wu->{author_id} = $c->{att}->{node_id};
244            
245 0         0 $c = $b->first_child('parent')->first_child('e2link');
246 0         0 $wu->{parent} = $c->text;
247 0         0 $wu->{parent_id} = $c->{att}->{node_id};
248            
249 0         0 push @writeups, $wu;
250             }
251 1         8 };
252              
253 1         6 return $self->parse( 'newwriteups', $handlers, \@writeups, %opt );
254             }
255              
256             sub other_users {
257 0 0   0 1   my $self = shift or croak "Usage: other_users E2TICKER [, ROOM_ID ]";
258 0           my $room = shift;
259              
260 0           my @users;
261            
262 0 0         warn "E2::Ticker::other_users" if $DEBUG > 1;
263              
264 0           my %opt = ( nosort => 1 );
265 0 0         $opt{in_room} = $room if $room;
266            
267             my $handlers = {
268             'user' => sub {
269 0     0     (my $a, my $b) = @_;
270 0           my $user = {};
271            
272 0           $user->{god} = $b->{att}->{e2god};
273 0           $user->{editor} = $b->{att}->{ce};
274 0           $user->{edev} = $b->{att}->{edev};
275 0           $user->{xp} = $b->{att}->{xp};
276 0           $user->{borged} = $b->{att}->{borged};
277              
278 0           my $c = $b->first_child('e2link');
279 0           $user->{name} = $c->text;
280 0           $user->{id} = $c->{att}->{node_id};
281              
282 0 0         if( $c = $b->first_child('room' ) ) {
283 0           $user->{room} = $c->text;
284 0           $user->{room_id} = $c->{att}->{node_id};
285             }
286              
287 0           push @users, $user;
288             }
289 0           };
290              
291 0           return $self->parse( 'otherusers', $handlers, \@users, %opt );
292             }
293              
294             sub random_nodes {
295 0 0   0 1   my $self = shift or croak "Usage: random_nodes E2TICKER";
296              
297 0           my @random;
298              
299 0 0         warn "E2::Ticker::random_nodes" if $DEBUG > 1;
300              
301             my $handlers = {
302             'e2link' => sub {
303 0     0     (my $a, my $b) = @_;
304 0           push @random, {
305             title => $b->text,
306             id => $b->{att}->{node_id}
307             };
308             },
309             'wit' => sub {
310 0     0     (my $a, my $b) = @_;
311 0           $self->{wit} = $b->text;
312             }
313 0           };
314              
315 0           return $self->parse( 'random', $handlers, \@random );
316             }
317              
318             sub cool_nodes {
319 0 0   0 1   my $self = shift or croak "Usage: cool_nodes E2TICKER [, WRITTEN_BY ] [, COOLED_BY ] [, COUNT ] [, OFFSET ]";
320 0           my $written_by = shift;
321 0           my $cooled_by = shift;
322 0           my $count = shift;
323 0           my $offset = shift;
324              
325 0           my @cools;
326              
327 0 0         warn "E2::Ticker::cool_nodes" if $DEBUG > 1;
328              
329 0           my %opt;
330              
331 0 0         $opt{writtenby} = $written_by if $written_by;
332 0 0         $opt{cooledby} = $cooled_by if $cooled_by;
333 0 0         $opt{limit} = $count if $count;
334 0 0         $opt{startat} = $offset if $offset;
335              
336             my $handlers = {
337             'cool' => sub {
338 0     0     (my $a, my $b) = @_;
339 0           my $cool = {};
340 0           my $c = $b->first_child('writeup')->first_child('e2link');
341              
342 0           $cool->{title} = $c->text;
343 0           $cool->{id} = $c->{att}->{node_id};
344              
345 0           $c = $b->first_child('author')->first_child('e2link');
346              
347 0           $cool->{author} = $c->text;
348 0           $cool->{author_id} = $c->{att}->{node_id};
349              
350 0           $c = $b->first_child('cooledby')->first_child('e2link');
351              
352 0           $cool->{cooledby} = $c->text;
353 0           $cool->{cooledby_id} = $c->{att}->{node_id};
354              
355 0           push @cools, $cool;
356             }
357 0           };
358              
359 0           return $self->parse( 'coolnodes', $handlers, \@cools, %opt );
360             }
361              
362             sub editor_cools {
363 0 0   0 1   my $self = shift or croak "Usage: editor_cools E2TICKER [, COUNT ]";
364 0           my $count = shift;
365              
366 0           my @edcools;
367            
368 0 0         warn "E2::Ticker::editor_cools" if $DEBUG > 1;
369              
370 0           my %opt;
371              
372 0 0         $opt{count} = $count if $count;
373              
374             my $handlers = {
375             'edselection' => sub {
376 0     0     (my $a, my $b) = @_;
377 0           my $cool = {};
378 0           my $c = $b->first_child('endorsed');
379              
380 0           $cool->{editor} = $c->text;
381 0           $cool->{editor_id} = $c->{att}->{node_id};
382              
383 0           $c = $b->first_child('e2link');
384              
385 0           $cool->{title} = $c->text;
386 0           $cool->{id} = $c->{att}->{node_id};
387              
388 0           push @edcools, $cool;
389             }
390 0           };
391              
392 0           return $self->parse( 'edcools', $handlers, \@edcools, %opt );
393             }
394              
395             sub time_since {
396 0 0   0 1   my $self = shift or croak "Usage: time_since E2TICKER [, USER1 [, USER2 [, ... ] ] ]";
397 0           my @users = @_;
398 0           my $string = undef;
399              
400 0           my @timesince;
401            
402             my %opt;
403              
404 0 0         warn "E2::Ticker::time_since" if $DEBUG > 1;
405              
406             my $handlers = {
407             'now' => sub {
408 0     0     (my $a, my $b) = @_;
409 0           $self->{now} = $b->text;
410             },
411             'user' => sub {
412 0     0     (my $a, my $b) = @_;
413 0           my $user = {};
414              
415 0           my $c = $b->first_child( 'e2link' );
416              
417 0           $user->{time} = $b->{att}->{lasttime};
418 0           $user->{name} = $c->text;
419 0           $user->{id} = $c->{att}->{node_id};
420              
421 0           push @timesince, $user;
422             }
423 0           };
424              
425             # If they've passed a list of users, determine
426             # whether the list is of usernames or user_ids
427             # and set %opt accordingly.
428              
429 0 0         if( @users ) {
430 0           foreach my $u (@users) {
431 0 0         if( ! int $u ) {
432 0           $string = 1;
433             }
434             }
435              
436 0 0         my $key = $string ? 'node' : 'node_id';
437 0           %opt = ( $key => join ',', @users );
438             }
439              
440 0           return $self->parse( 'timesince', $handlers, \@timesince, %opt );
441             }
442              
443             sub available_rooms {
444 0 0   0 1   my $self = shift or croak "Usage: available_rooms E2TICKER";
445              
446 0           my @rooms = ( { title => 'outside', id => undef } );
447            
448 0 0         warn "E2::Ticker::available_rooms" if $DEBUG > 1;
449              
450             my $handlers = {
451             'outside/e2link' => sub {
452 0     0     (my $a, my $b) = @_;
453 0           $rooms[0] = {
454             title => $b->text,
455             id => $b->{att}->{node_id}
456             };
457             },
458             'roomlist/e2link' => sub {
459 0     0     (my $a, my $b) = @_;
460 0           push @rooms, {
461             title => $b->text,
462             id => $b->{att}->{node_id}
463             };
464             }
465 0           };
466              
467 0           return $self->parse( 'rooms', $handlers, \@rooms );
468             }
469              
470             sub best_users {
471 0 0   0 1   my $self = shift or croak "Usage: best_users E2TICKER [, NOGODS ]";
472 0           my $nogods = shift;
473              
474 0           my @bestusers;
475            
476 0 0         warn "E2::Ticker::best_users" if $DEBUG > 1;
477              
478 0           my %opt;
479 0 0         $opt{ebu_noadmins} = 1 if $nogods;
480              
481             my $handlers = {
482             'bestuser' => sub {
483 0     0     (my $a, my $b) = @_;
484 0           my $exp = $b->first_child( 'experience' );
485 0           my $wri = $b->first_child( 'writeups' );
486 0           my $usr = $b->first_child( 'e2link' );
487 0           my $lvl = $b->first_child( 'level' );
488            
489 0           push @bestusers, {
490             experience => $exp->text,
491             writeups => $wri->text,
492             id => $usr->{att}->{node_id},
493             user => $usr->text,
494             level => $lvl->{att}->{value},
495             level_string => $lvl->text
496             };
497             }
498 0           };
499              
500 0           return $self->parse( 'bestusers', $handlers, \@bestusers, %opt );
501             }
502              
503             sub node_heaven {
504 0 0   0 1   my $self = shift or croak "Usage: node_heaven E2TICKER [, NODE_ID ]";
505 0           my $node_id = shift;
506              
507 0           my @heaven;
508              
509 0 0         warn "E2::Ticker::node_heaven" if $DEBUG > 1;
510              
511 0 0         if( !$self->logged_in ) { return undef; }
  0            
512              
513 0           my %opt;
514              
515 0 0         $opt{visitnode_id} = $node_id if $node_id;
516              
517             my $handlers = {
518             'nodeangel' => sub {
519 0     0     (my $a, my $b) = @_;
520 0           push @heaven, {
521             title => $b->{att}->{title},
522             id => $b->{att}->{node_id},
523             reputation => $b->{att}->{reputation},
524             createtime => $b->{att}->{createtime},
525             text => $b->text
526             };
527             }
528 0           };
529              
530 0           return $self->parse( 'heaven', $handlers, \@heaven, %opt );
531             }
532              
533             sub maintenance_nodes {
534 0 0   0 1   my $self = shift or croak "Usage: maintenance_nodes E2TICKER";
535              
536 0           my @maintenance;
537            
538 0 0         warn "E2::Ticker::maintenance_nodes" if $DEBUG > 1;
539              
540             my $handlers = {
541             'e2link' => sub {
542 0     0     (my $a, my $b) = @_;
543 0           push @maintenance, {
544             title => $b->text,
545             id => $b->{att}->{node_id}
546             };
547             }
548 0           };
549              
550 0           return $self->parse( 'maintenance', $handlers, \@maintenance );
551             }
552              
553             sub raw_vars {
554 0 0   0 1   my $self = shift or croak "Usage: raw_vars E2TICKER";
555              
556 0           my $vars = {};
557            
558 0 0         warn "E2::Ticker::raw_vars" if $DEBUG > 1;
559              
560             # Another method that doesn't return a list. Again, we'll have
561             # to thread_then
562              
563             my $handlers = {
564             'key' => sub {
565 0     0     (my $a, my $b) = @_;
566 0           $vars->{$b->{att}->{name}} = $b->text;
567             }
568 0           };
569              
570             return $self->thread_then(
571             [
572             \&parse,
573             $self,
574             'vars',
575             $handlers,
576             [] # dummy value for array
577             ],
578 0     0     sub { return $vars }
579 0           );
580             }
581              
582             sub interfaces {
583 0 0   0 1   my $self = shift or croak "Usage: interfaces E2TICKER";
584              
585 0           return $self->{xml_interfaces};
586             }
587            
588             sub random_nodes_wit {
589 0 0   0 1   my $self = shift or croak "Usage: random_wit_now E2TICKER";
590 0           return $self->{wit};
591             }
592              
593             sub time_since_now {
594 0 0   0 1   my $self = shift or croak "Usage: time_since_now E2TICKER";
595 0           return $self->{now};
596             }
597              
598             1;
599             __END__