File Coverage

blib/lib/Bot/JabberBot.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Bot::JabberBot;
2              
3 1     1   6850 use strict;
  1         2  
  1         50  
4              
5             =head1 NAME
6              
7             Bot::JabberBot - simple jabber bot base class
8              
9             =head1 SYNOPSIS
10              
11             use Bot::JabberBot;
12             Bot::JabberBot->new( server => 'jabber.earth.li',
13             port => 5222, # (default)
14             nick => 'jabberbot',
15             password => 'foo',
16             resource => 'foo')->run();
17            
18             =cut
19              
20 1     1   1540 use Jabber::Connection;
  0            
  0            
21             use Jabber::NodeFactory;
22             use Class::MethodMaker new_hash_init => 'new', get_set => [ qw{ server port nick password resource name username session session_length roster }];
23              
24             our $VERSION = '0.02';
25              
26             sub connect {
27             my $self = shift;
28             return $self->c;
29             }
30              
31             sub c {
32             my $self = shift;
33             return $self->{c} if $self->{c};
34             my $server = $self->server || 'localhost';
35             my $port = $self->port || '5222';
36             $self->{c} = Jabber::Connection->new(server => $server.':'.$port,
37             log => 1);
38             print "Logging in to $server:$port...\n";
39             return $self->{c};
40             }
41              
42             sub nf {
43             my $self = shift;
44             return $self->{nf} if $self->{nf};
45             $self->{nf} = Jabber::NodeFactory->new(fromstr => 1);
46             }
47              
48             sub run {
49             my $self = shift;
50             my $c = $self->connect;
51             die "oops: ".$c->lastError unless $c->connect();
52              
53             $c->register_handler('message',sub { return $self->message(@_) });
54             $c->register_handler('presence',sub { return $self->presence(@_) });
55             $c->register_handler('iq',sub { return $self->handle_iq(@_) });
56              
57             $c->auth($self->nick,$self->password,$self->resource);
58             $c->send('');
59             $self->request_roster;
60             $c->start;
61             }
62              
63             sub stop {
64             my $self = shift;
65             print "Exiting...\n";
66             $self->c->disconnect();
67             exit(0);
68             }
69              
70             sub message {
71             my ($self,$in) = @_;
72             my $said;
73              
74             $said->{body} = $in->getTag('body')->data;
75             $said->{who} = $in->attr('from');
76              
77             my $reply = $self->said($said);
78              
79             if ($reply) {
80             my $response;
81             if (ref $reply eq 'HASH') {
82             $response = $reply->{body};
83             }
84             else { $response = $reply; }
85              
86             $self->say({ who => $said->{who},
87             body => $response,
88             type => $in->attr('type')});
89            
90             }
91             }
92              
93             sub said {
94             # override
95             }
96              
97             sub say {
98             my ($self,$say) = @_;
99             my $out = $self->nf->newNodeFromStr(''.$say->{body}.'');
100             $out->attr('to',$say->{who});
101             my $type = $say->{type} || 'chat';
102             $out->attr('type',$type);
103             $self->c->send($out);
104             }
105              
106             sub presence {
107             my ($self,$in) = @_;
108            
109             my $type = $in->attr('type');
110             if ($type eq 'subscribe') {
111             my $message = "";
112             my $node = $self->nf->newNodeFromStr($message);
113             $self->c->send($node);
114             $message = "I would like to add you to my roster.";
115             my $node = $self->nf->newNodeFromStr($message);
116             $self->c->send($node);
117             my $roster = $self->roster;
118             push @{$roster}, $in->attr('from');
119             $self->roster($roster);
120             }
121             }
122            
123             sub handle_iq {
124             my ($self,$in) = @_;
125              
126             my $type = $in->attr('id');
127             if ($type =~ m/roster_1/) {
128             my @roster;
129             my $query = $in->getTag('query');
130             my @items = $query->getTag('item');
131             foreach (@items) {
132             if ($_->attr('jid') =~ m/\@/) {
133             push @roster, $_->attr('jid');
134             }
135             }
136             $self->roster(\@roster);
137             }
138             }
139              
140             sub update_session {
141             my ($self,$said) = @_;
142             my $session = $self->session;
143             my $dialogue = $session->{$said->{who}} || [ ];
144             my $session_length = $self->session_length || '8';
145             if (scalar(@{$dialogue}) > 8) {
146             pop @{$dialogue};
147             }
148             push @{$dialogue}, $said->{body};
149             $session->{$said->{who}} = $dialogue;
150             $self->session($session);
151             }
152              
153             sub request_roster {
154             my ($self) = @_;
155             my $request = $self->nf->newNodeFromStr('');
156             $self->c->send($request);
157             }
158              
159             =head1 DESCRIPTION
160              
161             a very simple Jabber bot base class, which shares interface with the Bot::BasicBot
162             class for IRC bots. this allows me to take Bot::BasicBot subclasses and replace the
163             base class with
164              
165             use base qw( Bot::JabberBot );
166              
167             and they Just Work. also provides some jabber-specific features; the bot requests
168             the Roster of jabberids whose presence it wants to know about; and when it it sent a
169             jabber subscription request, it automatically accepts it and adds the requester to
170             its roster.
171              
172             =head1 METHODS
173              
174             new(%args);
175             Creates a new instance of the class. Name value pairs may be
176             passed which will have the same effect as calling the method of that name
177             with the value supplied.
178              
179             run();
180             Runs the bot. Hands the control over to the Jabber::Connection object
181              
182             said({ who => 'test@jabber.org', body => 'foo'})
183            
184             This is the main method that you'll want to override in your sub-
185             class - it's the one called by default whenever someone sends a message.
186             You'll be passed a reference to a hash that contains these arguments:
187              
188             { who => [jabberid of message sender],
189             body => [body text of message }
190              
191             You should return what you want to say. This can either be a sim-
192             ple string or a hashref that contains values that are compatible with say
193             (just changing the body and returning the structure you were passed works
194             very well.)
195              
196             Returning undef will cause nothing to be said.
197              
198             say({who => 'test@jabber.org', body => 'bar'})
199            
200             Say something to someone.
201              
202             roster();
203            
204             Returns an array ref of jabberids whose presence is registered with the bot.
205              
206             session();
207              
208             A session get-set is provided to store per-user session information.
209             Nothing is put in here by default.
210              
211             =head1 BOT JABBER ACCOUNTS
212              
213             To use a Bot::JabberBot you must register an account for it with a jabber
214             server through a regular client, and set up transports to other IM accounts
215             in this way. i thought of doing this automatically, but decided it would
216             be spammy and might lead to bot abuse.
217              
218             =head1 AUTHOR
219              
220             Jo Walsh Ejo@london.pm.orgE
221              
222             =head1 CREDITS
223            
224             Simon Kent - maintainer of Bot::BasicBot
225             Mark Fowler - original author of Bot::BasicBot
226             DJ Adams - author of Jabber::Connection
227             Tom Hukins - patched 0.02
228             everyone on #bots and #pants
229              
230             =head1 SEE ALSO
231              
232             Bot::BasicBot
233             Jabber::Connection
234             Jabber::NodeFactory
235              
236             =cut
237              
238             1;