File Coverage

blib/lib/WWW/Pusher.pm
Criterion Covered Total %
statement 32 64 50.0
branch 3 24 12.5
condition 5 33 15.1
subroutine 9 12 75.0
pod 4 4 100.0
total 53 137 38.6


line stmt bran cond sub pod time code
1             package WWW::Pusher;
2             {
3             $WWW::Pusher::VERSION = '0.0701';
4             }
5              
6 1     1   755 use warnings;
  1         2  
  1         37  
7 1     1   5 use strict;
  1         2  
  1         30  
8              
9 1     1   27 use 5.008;
  1         11  
  1         36  
10              
11 1     1   6 use JSON;
  1         2  
  1         6  
12 1     1   1115 use URI;
  1         5798  
  1         35  
13 1     1   1245 use LWP::UserAgent;
  1         39572  
  1         35  
14 1     1   10 use Digest::MD5 qw(md5_hex);
  1         2  
  1         65  
15 1     1   870 use Digest::SHA qw(hmac_sha256_hex);
  1         3692  
  1         827  
16              
17             my $pusher_defaults = {
18             host => 'http://api.pusherapp.com',
19             port => 80
20             };
21              
22             =head1 NAME
23              
24             WWW::Pusher - Interface to the Pusher WebSockets API
25              
26             =head1 VERSION
27              
28             version 0.0701
29              
30             =cut
31              
32             =head1 SYNOPSIS
33              
34             use WWW::Pusher;
35              
36             my $pusher = WWW::Pusher->new(
37             auth_key => 'YOUR API KEY',
38             secret => 'YOUR SECRET',
39             app_id => 'YOUR APP ID',
40             channel => 'test_channel' );
41              
42             my $response = $pusher->trigger(event => 'my_event', data => 'Hello, World!');
43             my $sock_auth = $pusher->socket_auth('socket_auth_key');
44              
45             =head1 METHODS
46              
47             =head2 new(auth_key => $auth_key, secret => $secret, app_id => $app_id, channel => $channel_id)
48              
49             Creates a new WWW::Pusher object. All fields excluding the channel are mandatory, however if
50             you do not set the channel name during construction you must specify it when calling any
51             other method.
52              
53             You can optionally specify the host and port keys and override using pusherapp.com's server if you
54             wish. In addtion, setting debug to a true value will return an L response on any request.
55              
56             =cut
57              
58             sub new
59             {
60 1     1 1 794 my ($class, %args) = @_;
61            
62 1 50       5 die 'Pusher auth key must be defined' unless $args{auth_key};
63 1 50       3 die 'Pusher secret must be defined' unless $args{secret};
64 1 50       3 die 'Pusher application ID must be defined' unless $args{app_id};
65              
66 1   33     12 my $self = {
      50        
      50        
      33        
      33        
67             uri => URI->new($args{host} || $pusher_defaults->{host}),
68             lwp => LWP::UserAgent->new,
69             debug => $args{debug} || undef,
70             auth_key => $args{auth_key},
71             app_id => $args{app_id},
72             secret => $args{secret},
73             channel => $args{channel} || '',
74             host => $args{host} || $pusher_defaults->{host},
75             port => $args{port} || $pusher_defaults->{port}
76             };
77              
78 1         11330 $self->{uri}->port($self->{port});
79 1         238 $self->{uri}->path('/apps/'.$self->{app_id}.'/channels/'.$self->{channel}.'/events');
80              
81 1         43 return bless $self;
82              
83             }
84              
85              
86             =head2 trigger(event => $event_name, data => $data, [channel => $channel, socket_id => $socket_id, debug => 1])
87              
88             Send an event to the specified channel. The event name should be a scalar, but data can also be hash/arrayref. There
89             should be no need to JSON encode your data.
90              
91             Returns true on success, or undef on failure. Setting "debug" to a true value will return an L
92             response object.
93              
94             =cut
95              
96             sub trigger
97             {
98 0     0 1   my ($self, %args) = @_;
99              
100 0           my $time = time;
101 0           my $uri = $self->{uri}->clone;
102 0           my $payload = to_json($args{data}, { allow_nonref => 1 });
103              
104 0 0 0       if($args{channel} && $args{channel} ne '')
105             {
106 0           $uri->path('/apps/'.$self->{app_id}.'/channels/'.$args{channel}.'/events');
107             }
108            
109             # The signature needs to have args in an exact order
110 0   0       my $params = [
111             'auth_key' => $self->{auth_key},
112             'auth_timestamp' => $time,
113             'auth_version' => '1.0',
114             'body_md5' => md5_hex($payload),
115             'name' => $args{event},
116             'socket_id' => $args{socket_id} || undef
117             ];
118              
119 0           $uri->query_form(@{$params});
  0            
120 0           my $signature = "POST\n".$uri->path."\n".$uri->query;
121 0           my $auth_signature = hmac_sha256_hex($signature, $self->{secret});
122              
123 0           my $request = HTTP::Request->new('POST', $uri->as_string."&auth_signature=".$auth_signature, ['Content-Type' => 'application/json'], $payload);
124 0           my $response = $self->{lwp}->request($request);
125              
126 0 0 0       if($self->{debug} || $args{debug})
    0 0        
127             {
128 0           return $response;
129             }
130             elsif($response->is_success && $response->content eq "202 ACCEPTED\n")
131             {
132 0           return 1;
133             }
134             else
135             {
136 0           return undef;
137             }
138              
139             }
140              
141             =head2 socket_auth(socket_id => $socket_id, channel => $channel)
142              
143             In order to establish private channels, your end must hand back a checksummed bit of data that browsers will,
144             in turn will pass onto the pusher servers. On success this will return a JSON encoded hashref for you to give
145             back to the client. Specifying the channel is optional only if you did not specify it during construction.
146              
147             =cut
148              
149             sub socket_auth
150             {
151 0     0 1   my($self, %args) = @_;
152              
153 0 0         return undef unless $args{socket_id};
154              
155 0 0 0       my $use_channel = $args{channel} && $args{channel} ne '' ? $args{channel} : $self->{channel};
156              
157 0           my $signature;
158 0 0         if($args{custom_string})
159             {
160 0           $signature = hmac_sha256_hex($args{socket_id}.':'.$use_channel.':'.$args{custom_string}, $self->{secret});
161             }
162             else
163             {
164 0           $signature = hmac_sha256_hex($args{socket_id}.':'.$use_channel, $self->{secret});
165             }
166              
167 0           return encode_json({
168             auth => $self->{'auth_key'}.':'.$signature
169             });
170             }
171              
172             =head2 presence_auth(socket_id => $socket_id, user_id => $user_id, channel => $channel, user=_info => {name => $name, email => $email})
173              
174             Presence signing is exactly like socket ID signing above, only we can include very user-specific data in
175             addition, such as a user ID, name or email. This method generates the signed payload to pass back to Pusher.
176              
177             The socket ID and user ID are mandatory, however both the channel and user info are not. Setting the channel
178             to undef will default to using the channel defined in the WWW::Pusher object.
179              
180             =cut
181              
182             sub presence_auth
183             {
184 0     0 1   my ($self, %args) = @_;
185              
186 0 0 0       return undef unless $args{socket_id} and $args{user_id};
187              
188 0           my $user_data = { user_id => $args{user_id}};
189 0 0         $user_data->{user_info} = { %{$args{user_info}} } if($args{user_info});
  0            
190              
191 0 0 0       my $use_channel = $args{channel} && $args{channel} ne '' ? $args{channel} : $self->{channel};
192            
193 0           return $self->socket_auth(socket_id => $args{socket_id}, channel => $use_channel, custom_string => encode_json($user_data));
194             }
195              
196             =head1 AUTHOR
197              
198             Squeeks, C<< >>
199              
200             JT Smith C<< >>
201              
202             =head1 BUGS
203              
204             Please report bugs to the tracker on GitHub: L
205              
206              
207             =head1 SUPPORT
208              
209             You can find documentation for this module with the perldoc command.
210              
211             perldoc WWW::Pusher
212              
213             More information at: L
214              
215             =head1 SEE ALSO
216              
217             Pusher - L
218              
219             =head1 LICENSE AND COPYRIGHT
220              
221             Copyright 2010 Squeeks.
222              
223             This program is free software; you can redistribute it and/or modify it
224             under the terms of either: the GNU General Public License as published
225             by the Free Software Foundation; or the Artistic License.
226              
227             See http://dev.perl.org/licenses/ for more information.
228              
229             =cut
230              
231             1; # End of WWW::Pusher