File Coverage

blib/lib/SMS/Send/Wadja.pm
Criterion Covered Total %
statement 15 60 25.0
branch 0 26 0.0
condition 0 12 0.0
subroutine 5 9 55.5
pod 3 3 100.0
total 23 110 20.9


line stmt bran cond sub pod time code
1             package SMS::Send::Wadja;
2 1     1   28703 use warnings;
  1         3  
  1         34  
3 1     1   6 use strict;
  1         2  
  1         65  
4 1     1   5 use base 'SMS::Send::Driver';
  1         6  
  1         845  
5 1     1   5461 use HTTP::Request::Common;
  1         43376  
  1         104  
6 1     1   6377 use LWP::UserAgent;
  1         49466  
  1         1020  
7              
8             =head1 NAME
9              
10             SMS::Send::Wadja - Non-regional L driver for the L
11             free global SMS service, using their API.
12              
13             =head1 VERSION
14              
15             Version 1.0
16              
17             =cut
18              
19             our $VERSION = '1.01';
20              
21              
22             =head1 SYNOPSIS
23              
24             use SMS::Send;
25              
26             my $sender = SMS::Send->new('Wadja',
27             _key => 'my-wadja-API-key'
28             );
29              
30             my $sent = $sender->send_sms(
31             to => '+40-722-123456', # recipient
32             text => "Hello, world!", # the text of the message to send
33             _from => 'me@mydomain.com', # optional "from" address
34             );
35              
36             # Did it send to Wadja OK?
37             if ( $sent ) {
38             print "Sent test message\n";
39             } else {
40             print "Test message failed\n";
41             }
42              
43             =head1 DESCRIPTION
44              
45             SMS::Send::Wadja is an L driver for the L free
46             global SMS service, using their API. To apply for an API key, sign up for an
47             account (it requires e-mail confirmation, but no phone number confirmation),
48             then request an API key from L. In
49             theory, Wadja could also be screen scraped so that you could send text messages
50             via the web interface, without having to apply for an API key. However, applying
51             is free, and the Wadja site is JavaScript-heavy and slow, which is why I didn't
52             spend time implementing the screen scraping method.
53              
54             I've seen Wadja deliver text messages successfully to the UK (Vodaphone),
55             Germany (T-Mobile (D1) and Vodafone), Philippines (Global Telecom),
56             Poland (Orange, Polkomtel), Romania (Orange), and Russia (BeeLine), but not to
57             the US (AT&T Wireless), despite the official coverage claim at
58             L. However, Wadja
59             provides a delivery status function (which happens to be currently broken via
60             their API (see L)
61             but works via the web UI).
62              
63             Wadja offers two types of APIs:
64              
65             =over
66              
67             =item * Free SMS API - free, limited to 3 messages per day and 90 characters per
68             message (the remaining characters will be used for ad delivery or Wadja branding)
69              
70             =item * SMS Plus API - requires topping up credit (in EUR), and doesn't deliver
71             ads (thus you get the full 160 characters).
72              
73             =back
74              
75             This module has only been tested with the Free SMS API but will probably work
76             with the SMS Plus API as well.
77              
78             =head1 METHODS
79              
80             =head2 new
81              
82             # Create a new sender using this driver
83             my $sender = SMS::Send->new('Wadja',
84             _key => 'your_wadja_API_key' # required
85             _ua => $your_own_LWP_UserAgent_object # useful if you want to pass proxy parameters
86             );
87              
88             =cut
89              
90             sub new {
91 0     0 1   my $class = shift;
92 0           my $opts = {
93             _send_via => 'api',
94             _api_sms_url => 'http://www.wadja.com/partners/sms/default.aspx',
95             _api_delivery_url => 'http://www.wadja.com/partners/sms/dlr.aspx',
96             @_
97             };
98 0   0       $opts->{_ua} ||= LWP::UserAgent->new(
99             # on 2010-01-26, Wadja changed its API URL and started issuing a '301 Moved Permanently'
100             # response to our 'POST' request. By default, LWP::UserAgent::requests_redirectable excluded 'POST'.
101             requests_redirectable => ['GET', 'HEAD', 'POST']
102             );
103 0           return bless $opts, $class;
104             }
105              
106             =head2 send_sms
107              
108             This method is actually called by L when you call send_sms on it.
109              
110             my $sent = $sender->send_sms(
111             text => 'Messages have a limit of 90 chars',
112             to => '+44-1234567890',
113             _from => 'custom From string' # works only in the SMS Plus API
114             );
115              
116             Unicode messages appear to be handled correctly and are still limited to 90
117             characters.
118              
119             Returns a hashref with the following keys:
120              
121             price
122             batchID - used for tracking the delivery status
123              
124             =cut
125              
126             sub send_sms {
127 0     0 1   my $self = shift;
128 0           my %params = @_;
129 0           my %wadja_response;
130              
131 0 0         my $unicode = $params{text} =~ /[\x{80}-\x{FFFF}]/? 'yes' : 'no';
132 0           my $response = $self->{_ua}->request(POST $self->{_api_sms_url}, [
133             key => $self->{_key},
134             msg => $params{text},
135             to => $params{to},
136             from => $params{_from},
137             # Unicode doesn't work in the Web UI: L
138             # The UI failing prevents us from knowing what the real message length limit is.
139             unicode => $unicode,
140             send => 1, # 0 for getting a price quote
141             ]);
142              
143 0 0 0       if (defined $response and $response->is_success) {
    0          
144 0           $self->{_raw_response} = $response->content;
145 0           while ($self->{_raw_response} =~ / \[ (.*?) : \s* (.*?) \] /gx) {
146 0           $wadja_response{$1} = $2;
147             }
148 0           $self->{_wadja_response} = \%wadja_response;
149              
150 0 0         if ($wadja_response{batchID}) {
151 0           return \%wadja_response;
152             } else {
153             # request went through but message not accepted
154 0 0         if ($wadja_response{ERROR}) {
    0          
155 0           Carp::carp "SMS message not sent -- Wadja ERROR: $wadja_response{ERROR}";
156             } elsif (%wadja_response) {
157             # Wadja did not return a batchID, but there was no error. This indicates success, but is a bug.
158 0           return \%wadja_response;
159             } else {
160 0           Carp::carp q{SMS message not sent -- Wadja returned '} . $self->{_raw_response} . q{'};
161             }
162 0           return 0;
163             }
164             } elsif (defined $response) {
165             # failed LWP request, fatal
166 0           Carp::croak 'Failed to issue HTTP request: ' . $response->status_line;
167             } else {
168 0           Carp::croak 'No HTTP request issued';
169             }
170             }
171              
172              
173             =head2 delivery_status
174              
175             # Get the delivery status of the last message we sent
176             my $status = $sender->delivery_status;
177              
178             # Get the delivery status for an arbitrary message we sent in the past,
179             # based on an anonymous hashref with a 'batchID' key
180             my $sent = $sender->send_sms(...);
181             my $status = $sender->delivery_status($sent);
182            
183             # Or just pass a {batchID => nnn}
184             my $batchID = 2180419;
185             my $status = $sender->delivery_status({batchID => $batchID});
186              
187             if ($status->{error}) {
188             print "Can't check delivery: ", $status->{error}, "\n";
189             } else {
190             print "Delivery status of " . $batchID . ": ", $status->{status}, ' at ', $status->{completed};
191             }
192              
193             If called with no parameters then the most recent message sent out is checked.
194             You can also provide an anoynmous hash with a batchID key set to the value of the
195             batchID returned by a previous L call.
196              
197             Returns a hashref, of which the 'status' key indicates:
198              
199             DELIVRD Delivered to destination
200             ACCEPTD Accepted by network operator
201             EXPIRED Validity period has expired
202             UNDELIV Message is undeliverable
203             UNKNOWN Message is in unknown state
204             REJECTD Message is in rejected state
205              
206             =cut
207              
208             sub _wadja2DateTime {
209 0     0     my ($wadja_date) = @_;
210 0           my ($yy, $mm, $dd, $hh, $min) = $wadja_date =~ /(\d{2})/g;
211 0           return DateTime->new(
212             year => $yy+2000,
213             month => $mm,
214             day => $dd,
215             hour => $hh,
216             minute => $min,
217             time_zone => '+0100', # neither UTC, nor Europe/Athens (unless Wadja's server doesn't follow DST)
218             );
219             }
220              
221             sub delivery_status {
222 0     0 1   require DateTime;
223 0           my $self = shift;
224 0 0 0       my $wadja_response = shift || $self->{_wadja_response}
225             or Carp::croak 'No message available for checking delivery_status';
226 0 0         my $batch_id = $wadja_response->{batchID}
227             or Carp::croak 'No batch ID in the Wadja response';
228 0 0         defined $self->{_ua}
229             or Carp::croak 'UserAgent not initialized';
230              
231 0           my $request_URL = URI->new($self->{_api_delivery_url});
232 0           $request_URL->query_form(
233             key => $self->{_key},
234             bid => $batch_id
235             );
236 0           my $response = $self->{_ua}->get($request_URL);
237              
238 0 0 0       if (defined $response and $response->is_success) {
239             # A response may look like:
240             # [40746057225: id:40012700460178067 sub:001 dlvrd:001 submit date:1001270046 done date:1001270046 stat:DELIVRD err:000 text:First 19 chars of t]
241             # or, if no delivery information is available just yet, like:
242             # [447536736704: ]
243              
244 0 0         my $content = $response->content or return {error => "No response"};
245            
246 0           my ($response_id, $id, $sub, $delivered, $submit_date, $done_date, $status, $error) = $content =~
247             /(\d+): \s+ (?: id: (\S+) \s+ sub: (\S+) \s+ dlvrd: (\S+) \s+ submit\ date: (\S+) \s+ done\ date: (\S+) \s+ stat: (\S+) \s+ err: (\S+) )?/x;
248              
249 0 0         return {error => "Wadja error. Make sure the API key is the same as the one used for batchID $batch_id. Error message was: $content"}
250             if not $response_id;
251 0 0         return {error => "No delivery status available yet. Try again later"}
252             if not $id; # happens if you request delivery status right after sending the SMS
253              
254 0           $submit_date = _wadja2DateTime($submit_date);
255 0           $done_date = _wadja2DateTime($done_date);
256 0           $submit_date->set_time_zone('local');
257 0           $done_date->set_time_zone('local');
258              
259             return {
260 0           delivered => $delivered + 0,
261             submitted => $submit_date,
262             completed => $done_date,
263             status => $status,
264             error => $error + 0
265             }
266             }
267 0           return;
268             }
269              
270             =head1 BUGS AND LIMITATIONS
271              
272             Wadja's API lets you send a text message to multiple recipients at once, by
273             delimiting the phone numbers with commas. However, SMS::Send strips commas from
274             the "to" parameter, which will obviously break things. I filed a bug against
275             SMS::Send at L.
276              
277             The official coverage claim is at L
278             but beware that I could not send text messages successfully to AT&T Wireless in
279             the US despite what the coverage claims.
280              
281             Please report any bugs or feature requests for this module to
282             C, or through the web interface at
283             L. For patches,
284             please send whole files, not diffs.
285              
286             =head1 AUTHOR
287              
288             Dan Dascalescu, L
289              
290             =head1 ACKNOWLEDGEMENTS
291              
292             Thanks to Adam Kennedy Eadamk@cpan.orgE, L for writing
293             SMS::Send. The Wadja API is described at
294             L (as of 2010-01-25, the base
295             URLs are wrong. s/sms/www/g.
296              
297             Many thanks to my friends worldwide for assisting with QA.
298              
299             =head1 LICENSE AND COPYRIGHT
300              
301             Copyright (C) 2009-2010 Dan Dascalescu, L. All rights
302             reserved.
303              
304             This program is free software; you can redistribute it and/or modify it under
305             the same terms as Perl itself.
306              
307             =cut
308              
309             1; # End of SMS::Send::Wadja