File Coverage

blib/lib/SMS/Send/UK/Kapow.pm
Criterion Covered Total %
statement 27 112 24.1
branch 0 44 0.0
condition 0 74 0.0
subroutine 9 13 69.2
pod 4 4 100.0
total 40 247 16.1


line stmt bran cond sub pod time code
1             package SMS::Send::UK::Kapow;
2              
3 1     1   58286 use warnings;
  1         3  
  1         32  
4 1     1   5 use strict;
  1         2  
  1         35  
5 1     1   5 use Carp;
  1         13  
  1         77  
6              
7             =head1 NAME
8              
9             SMS::Send::UK::Kapow - SMS::Send driver for the Kapow.co.uk website
10              
11             =cut
12              
13 1     1   885 use SMS::Send::Driver ();
  1         327  
  1         21  
14 1     1   6 use base 'SMS::Send::Driver';
  1         2  
  1         99  
15 1     1   802 use version;
  1         3194  
  1         6  
16             our $VERSION = qv('0.06');
17 1     1   966 use URI::Escape;
  1         1525  
  1         120  
18              
19             =head1 SYNOPSIS
20              
21             use SMS::Send;
22              
23             my $sender = SMS::Send->new('UK::Kapow',
24             _login => 'my-kapow-username', # normally required, see below (synonymous with _user)
25             _password => 'my-kapow-password', # normally required, see below
26             _send_via => 'http', # optional, can be http, https or email, default is http
27             _http_method => 'get', # optional, the http method to use for http & https.
28             # get or post, default post.
29             _email_via => 'sendmail', # optional, for use when 'email' is used. can be
30             # 'sendmail' or 'smtp'
31             _url => 'http://foo.com/done' # optional url to call after sending, for http and https
32             _from => 'me@mydomain.com', # optional, for use when 'email' is used in send_via
33             _from_id => 'my-kapow-id', # optional message originator, if enabled for your account
34             _route => '840101', # optional shortcode for premium sms reverse billing
35             _wait => 0, # optional, supply a defined but false value to stop the
36             # module from trying to confirm delivery. default true.
37             );
38              
39             my $sent = $sender->send_sms(
40             to => '447712345678', # the recipient phone number
41             text => "Hello, world!", # the text of the message to send
42             _url => 'http://foo.com/done123', # optional url per message to call after sending
43             # (for http and https)
44             _from => 'me@mydomain.com', # optional from address per message (for email)
45             _from_id => 'my-kapow-id', # optional message originator per message
46             _route => '840101', # optional shortcode per message for reverse billing
47             _wait => 0, # optional per message control of delivery checks.
48             );
49              
50             # Did it send to Kapow ok?
51             if ( $sent ) {
52             print "Sent test message\n";
53             } else {
54             print "Test message failed\n";
55             }
56              
57             # What's the delivery status of the last message we sent? (available for http & https methods)
58             my $status = $sender->delivery_status;
59              
60             # What's the delivery status for an arbitrary message we sent in the past?
61             my $status = $sender->delivery_status($sent);
62              
63             =head1 DESCRIPTION
64              
65             L is a L driver that delivers messages
66             via the L website.
67              
68             Messages for any country can be sent through this interface, although
69             Kapow is generally aimed at users in the UK.
70              
71             This driver is based on the Kapow implementation document available at
72             L
73              
74             =head2 Preparing to Use This Driver
75              
76             You need to create an account at http://www.kapow.co.uk to be able to
77             use this driver. You will also need to purchase some SMS credits for
78             your account. Other optional services such getting a custom 'from-id'
79             or setting trusted sender email addresses can be configured in your
80             Kapow account and used through this module.
81              
82             =head1 METHODS
83              
84             =head2 new
85              
86             # Create a new sender using this driver
87             my $sender = SMS::Send->new('UK::Kapow',
88             _login => 'username', # normally required but see below
89             _password => 'password', # normally required but see below
90             );
91              
92             In most cases you should provide your Kapow username and password.
93              
94             If, however, you have set your Kapow account to allow any SMS requests
95             received via email from a certain trusted email address then you do
96             not need to set your username and password. In that case you may need
97             to provide a '_from' parameter here or in the send_sms method unless
98             your user account's email address on your system is the same as the
99             trusted one registered with your Kapow account.
100              
101             =over
102              
103             =item _login
104              
105             The C<_login> param should be your kapow.co.uk username. The C<_user>
106             parameter can be used instead.
107              
108             =item _password
109              
110             The C<_password> param should be your kapow.co.uk password.
111              
112             =item _send_via
113              
114             The C<_send_via> param controls how your SMS messages are sent to
115             Kapow. The default method is http, which issues an http request from
116             your server to Kapow's. Other options are 'https' and 'email'.
117              
118             The preferred methods are 'http' or 'https' because they allow the
119             system to track the delivery statuses of individual messages. If your
120             server cannot issue http/s requests then you can set this to 'email'
121             and it will instead send an email message to Kapow to issue the SMS
122             message. Note that using email means you lose the delivery tracking
123             features of the http/s methods.
124              
125             =item _http_method
126              
127             The C<_http_method> param is relevant when using http or https to send
128             your messages and specifies whether to use 'get' or 'post' methods
129             when issuing the request to Kapow. This defaults to 'post' but can be
130             changed to 'get' if desired.
131              
132             =item _email_via
133              
134             The C<_email_via> param is relevant when using email to send the
135             messages. Valid values for this param are 'sendmail' and 'smtp'. The
136             default is 'sendmail' except on Windows, where the default is 'smtp'.
137              
138             =item _url
139              
140             The C<_url> param is relevant when using http or https to send your
141             messages. It provides an arbitrary callback URL which the Kapow server
142             should call once the message has been successfully delivered. You can
143             override this per-message in order to use unique URLs which relate to
144             specific messages.
145              
146             =item _from
147              
148             The C<_from> param is relevant when using email to send your messages
149             and it should contain the email address to us as the 'from' address on
150             the emails sent to Kapow. The default (if using email) is to use your
151             user account's default email address.
152              
153             =item _from_id
154              
155             The C<_from_id> param is relevant for users who use Kapow's 'from-id'
156             feature to control the sender information that the recipient sees. If
157             you have purchased this feature from Kapow for your account then you
158             can control it using this parameter.
159              
160             =item _route
161              
162             The C<_route> param is for users who have purchased Kapow's Premium
163             SMS service to reverse-bill recipients for sending an SMS message. If
164             you have purchased this feature from Kapow for your account then you
165             can control it using this parameter.
166              
167             =item _wait
168              
169             The C<_wait> param is relevant when using http or https to send your
170             messages. The SMS::Send specification dictates that if a message can
171             have its delivery checked & confirmed then the driver module should do
172             that automatically at the time the message is sent. This means that if
173             you use http/s to send your messages then the system will wait for up
174             to 10 seconds trying to validate that the message has gone. If you want
175             to override this behaviour then supply a defined-but-false value for
176             this param, such as 0 or the empty string.
177              
178             Note that if you do suppress the automatic checking as described above
179             then you can still check the state of delivery of each message using
180             the delivery_status() method.
181              
182             =back
183              
184             Returns a new C object, or dies on error.
185              
186             =cut
187              
188 1     1   1105 use LWP::UserAgent;
  1         147180  
  1         34  
189 1     1   1387 use MIME::Lite;
  1         65325  
  1         1437  
190              
191             my $IS_WINDOWS = ($^O eq 'MSWin32');
192              
193             sub new
194             {
195 0     0 1   my $class = shift;
196 0           my %opts = (_send_via => 'http', _http_method => 'post',
197             _wait => 1,
198             @_);
199              
200 0           $opts{ua} = LWP::UserAgent->new;
201              
202 0   0       return bless \%opts, ref($class) || $class;
203             }
204              
205             =head2 send_sms
206              
207             # Send an SMS message
208             my $sent = $sender->send_sms(
209             to => '447712345678', # phone number to which to send the message
210             text => "Hello, world!", # content of the message
211             );
212              
213             =over
214              
215             =item _to
216              
217             The required C<_to> param should contain the phone number for the
218             recipient. See the SMS::Send documentation for acceptable formats.
219              
220             =item _text
221              
222             The required C<_text> param should contain the text content that you
223             wish to send. Normally this is limited to 160 characters though that
224             restriction is NOT enforced by this module in order to take advantage
225             of multiple-part messages ability available on some phones.
226              
227             Any newlines or carriage returns present are converted to spaces.
228              
229             =item _url
230              
231             The C<_url> param is optional and can be used to provide a callback
232             URL specific to this message. See above for a complete description.
233              
234             =item _from
235              
236             The C<_from> param is optional and can be used to provide an email
237             from address specific to this message. See above for a complete
238             description.
239              
240             =item _from_id
241              
242             The C<_from_id> param is optional and can be used to provide a mesage
243             originator specific to this message. See above for a complete
244             description.
245              
246             =item _route
247              
248             The C<_route> param is optional and can be used to provide a reverse
249             billing shortcode specific to this message. See above for a complete
250             description.
251              
252             =item _wait
253              
254             The C<_wait> param is optional and can be used to suppress the automatic
255             delivery confirmation stage on a per-message basis. See above for a
256             complete description.
257              
258             =back
259              
260             =cut
261              
262             sub send_sms
263             {
264 0     0 1   my $self = shift;
265 0           my %opts = (to => undef, text => undef, @_);
266              
267 0 0 0       if (not (defined($opts{to}) and defined($opts{text})))
268             {
269 0           Carp::croak "You must provide a 'to' number and 'text' content";
270             }
271              
272 0           $opts{to} =~ s/^\+//s;
273 0           $opts{text} =~ tr/\r\n/ /s;
274              
275 0 0         if (substr($self->{_send_via},0,4) eq 'http')
    0          
276             {
277 0 0 0       if (not (defined($self->{_login} || $self->{_user})
      0        
278             and
279             defined($self->{_password})))
280             {
281 0           Carp::croak "To send messages using http/s you must provide a Kapow username and password";
282             }
283 0   0       my $protocol = $self->{_send_via} || 'http';
284 0   0       my $request_method = $self->{_http_method} || 'post';
285              
286             my $response = do
287 0           {
288 0           my $r = undef;
289              
290 0   0       my %params = (username => $self->{_login} || $self->{_user} || "",
      0        
      0        
      0        
      0        
      0        
      0        
291             password => $self->{_password} || "",
292             mobile => $opts{to} || "",
293             sms => $opts{text} || "",
294             from_id => $opts{_from_id} || $self->{_from_id} || undef,
295             route => $opts{_route} || $self->{_route} || undef,
296             url => $opts{_url} || $self->{_url} || undef,
297             returnid => 'TRUE', # enable message delivery tracking
298             );
299            
300 0 0         if ($request_method eq 'get')
    0          
301             {
302 0           $r = $self->{ua}->get(sprintf("%s://www.kapow.co.uk/scripts/sendsms.php?%s",
303             $protocol,
304 0           join('&', map {sprintf("%s=%s", $_, uri_escape($params{$_}))}
305 0           grep {defined($params{$_})} keys %params)));
306             }
307             elsif ($request_method eq 'post')
308             {
309 0           $r = $self->{ua}->post("$protocol://www.kapow.co.uk/scripts/sendsms.php",
310 0           {map {$_ => $params{$_}} grep {defined $params{$_}} keys %params});
  0            
311             }
312 0           $r;
313             };
314 0 0 0       if (defined($response) and $response->is_success)
    0          
315             {
316 0           my $reply = $response->content;
317              
318 0           $self->{_raw_response} = $reply;
319              
320 0           my ($word,$num_credits,$unique_id) = split /\s+/, $reply;
321              
322 0 0         if ($word eq 'OK') # message accepted
323             {
324 0           $self->{_new_credits_balance} = $num_credits;
325 0           $self->{_msg_delivery_id} = $unique_id;
326 0           $self->{_sent_at} = time();
327              
328             # check delivery status, unless disabled
329 0           my $check_delivery_status = 1; # default true
330              
331 0 0 0       if (defined($opts{_wait}) and ! $opts{_wait}) { undef $check_delivery_status }
  0 0 0        
332 0           elsif (defined($self->{_wait}) and ! $self->{_wait}) { undef $check_delivery_status }
333              
334 0 0         if ($check_delivery_status)
335             {
336             wait_for_success:
337 0           for (1..10)
338             {
339 0           my $reply = $self->delivery_status;
340            
341 0 0 0       if ($reply and substr($reply,0,1) ne 'N')
342             {
343 0           $self->{_delivered_at} = time();
344 0           last wait_for_success;
345             }
346 0 0         sleep 1 if $_ < 10; # wait and try again
347             }
348             }
349            
350 0           return $unique_id;
351             }
352             else # request went through but message not accepted
353             {
354 0           Carp::carp "SMS message not sent -- Kapow returned '$word'";
355 0           return;
356             }
357             }
358             elsif (defined($response)) # failed lwp request, fatal
359             {
360 0           Carp::croak "Failed to issue HTTP request: " . $response->status_line;
361             }
362             else # perhaps a strange request method. we probably never made any request
363             {
364 0           Carp::croak "No HTTP request issued -- please ensure '_http_method' is set to either 'get' or 'post'";
365             }
366             }
367             elsif ($self->{_send_via} eq 'email')
368             {
369 0   0       my $send_method = $self->{_email_via} || ($IS_WINDOWS ? 'smtp' : 'sendmail');
370              
371 0   0       my $sms = $opts{text} || "";
372 0   0       my $mobile = $opts{to} || "";
373              
374 0           my %options = (To => "$opts{to}\@kapow.co.uk",
375             Subject => $sms, Type => 'TEXT',
376             Data => "", );
377              
378 0 0 0       if (my $username = $self->{_login} || $self->{_user}
      0        
379             and
380             my $password = $self->{_password})
381             {
382 0           $options{Data} = "$username\n$password\n";
383             }
384 0 0 0       if (my $from = $opts{_from} || $self->{_from})
385             {
386 0           $options{From} = $from;
387             }
388 0           my $message_as_string = "";
389              
390             eval
391 0           {
392 0           my $msg = MIME::Lite->new(%options);
393 0           $message_as_string = $msg->as_string;
394 0           $msg->send($send_method);
395             };
396 0 0         if ($@)
397             {
398 0           Carp::carp $@;
399 0           return;
400             }
401 0           else { $self->{_sent_at} = time(); return $message_as_string } # send and forget
  0            
402             }
403 0           return;
404             }
405              
406             =head2 delivery_status
407              
408             # What's the delivery status of the last message we sent? (available for http & https methods)
409             my $status = $sender->delivery_status;
410              
411             # What's the delivery status for an arbitrary message we sent in the past? (pass it the return value of the send_sms method)
412             my $status = $sender->delivery_status($sent);
413              
414             For messages sent via http/s you can check the delivery status of the
415             message by calling this method. If called with no parameters then the
416             most recent message sent out is checked. You can also provide the
417             return value of the send_sms method as a parameter to check the
418             delivery status for other messages.
419              
420             The module will use the same http or https setting that the sender
421             object used to send the message (i.e. the _send_via param) as well as
422             the same get/post setting.
423              
424             Messages sent by email cannot be checked for their delivery status.
425              
426             =cut
427              
428             sub delivery_status
429             {
430 0     0 1   my $self = shift;
431 0 0 0       my $unique_id = shift || $self->{_msg_delivery_id}
432             or Carp::croak "No message available for checking delivery_status";
433              
434 0 0         if (not defined $self->{ua})
435             {
436 0           Carp::croak "No user-agent available for checking delivery_status";
437             }
438              
439 0   0       my $protocol = $self->{_send_via} || 'http';
440 0   0       my $get_or_post = $self->{_http_method} || 'get';
441 0           my $response = undef;
442            
443 0 0         if ($get_or_post eq 'get')
    0          
444             {
445 0   0       $response = $self->{ua}->get(sprintf("%s://www.kapow.co.uk/scripts/chk_status.php?username=%s&returnid=%s",
446             $protocol, uri_escape($self->{_login} || $self->{_user}),
447             uri_escape($unique_id)));
448             }
449             elsif ($get_or_post eq 'post')
450             {
451 0   0       $response = $self->{ua}->post("$protocol://www.kapow.co.uk/scripts/chk_status.php",
452             { username => $self->{_login} || $self->{_user},
453             returnid => $unique_id });
454             }
455 0           else { Carp::croak "Unknown _http_method" }
456            
457 0 0 0       if (defined($response) and $response->is_success)
458             {
459 0           return $response->content;
460             }
461 0           return;
462             }
463              
464             =head2 send_status
465              
466             The send_status method is an alias for delivery_status, provided for
467             backward-compatibility.
468              
469             =cut
470              
471             sub send_status
472             {
473 0     0 1   my $self = shift;
474 0           return $self->delivery_status(@_);
475             }
476              
477             =head1 AUTHOR
478              
479             Jeremy Jones, C<< >>
480              
481             =head1 BUGS
482              
483             Please report any bugs or feature requests to C, or through
484             the web interface at L.
485              
486             =head1 SUPPORT
487              
488             You can find documentation for this module with the perldoc command.
489              
490             perldoc SMS::Send::UK::Kapow
491              
492              
493             =head1 ACKNOWLEDGEMENTS
494              
495             Adam Kennedy's SMS::Send module and Andrew Moore's
496             SMS::Send::US::Ipipi module were useful for writing this one.
497              
498             =head1 COPYRIGHT & LICENSE
499              
500             Copyright 2009 Jeremy Jones, all rights reserved.
501              
502             This program is free software; you can redistribute it and/or modify it
503             under the same terms as Perl itself.
504              
505             =cut
506              
507             1; # End of SMS::Send::UK::Kapow