File Coverage

blib/lib/Google/Voice.pm
Criterion Covered Total %
statement 27 61 44.2
branch 0 8 0.0
condition 0 6 0.0
subroutine 9 15 60.0
pod 4 5 80.0
total 40 95 42.1


line stmt bran cond sub pod time code
1             package Google::Voice;
2              
3 1     1   520711 use strict;
  1         2  
  1         42  
4 1     1   5 use warnings;
  1         2  
  1         35  
5              
6 1     1   7 use Mojo::UserAgent;
  1         6  
  1         8  
7 1     1   22 use Mojo::JSON;
  1         2  
  1         53  
8 1     1   6 use IO::Socket::SSL 1.37;
  1         32  
  1         11  
9              
10 1     1   849 use Google::Voice::Feed;
  1         4  
  1         8  
11 1     1   704 use Google::Voice::Call;
  1         3  
  1         10  
12              
13 1     1   40 use Mojo::Base -base;
  1         2  
  1         6  
14              
15             our $VERSION = 0.06;
16              
17             __PACKAGE__->attr([qw/ ua rnr_se /]);
18              
19             sub new {
20 0     0 1   my $self = bless {}, shift;
21              
22 0           $self->ua(Mojo::UserAgent->new);
23              
24 0           return $self;
25             }
26              
27             sub login {
28 0     0 1   my $self = shift;
29 0           my ($user, $pass) = @_;
30 0           my $c = $self->ua;
31              
32 0           $c->max_redirects(6); # Google seems to like redirects everywhere
33              
34             # GALX value
35 0           my $el =
36             $c->get('https://accounts.google.com/ServiceLogin')
37             ->res->dom->at('input[name="GALX"]');
38              
39 0 0         my $galx = $el->attrs->{value} if $el;
40              
41 0           $c->post(
42             'https://accounts.google.com/ServiceLogin',
43             form => { Email => $user,
44             Passwd => $pass,
45             GALX => $galx,
46             }
47             );
48              
49             # rnr_se required for subsequent requests
50 0           $el =
51             $c->get('https://www.google.com/voice#inbox')
52             ->res->dom->at('input[name="_rnr_se"]');
53              
54             # Login not accepted
55 0 0         return unless $el;
56              
57 0           $self->rnr_se($el->attrs->{value});
58              
59 0           return $self;
60             }
61              
62             sub send_sms {
63 0     0 1   my $self = shift;
64 0           my $c = $self->ua;
65 0           my ($phone, $content) = @_;
66              
67 0   0       my $json = $c->post(
68             'https://www.google.com/voice/b/0/sms/send',
69             form => { id => undef,
70             phoneNumber => $phone,
71             text => $content || '',
72             _rnr_se => $self->rnr_se
73             }
74             )->res->json;
75              
76 0 0 0       $@ = $json->{data}->{code} and return unless $json->{ok};
77              
78 0           return $json->{ok};
79             }
80              
81             for my $feed (
82             qw/ all starred spam trash voicemail
83             sms recorded placed received missed /
84             )
85             {
86              
87 1     1   685 no strict 'refs';
  1         2  
  1         468  
88             *{"Google::Voice::${feed}"} = sub {
89 0     0     shift->feed('https://www.google.com/voice/inbox/recent/' . $feed);
90             };
91             }
92              
93             sub feed {
94 0     0 0   my $self = shift;
95 0           my $url = shift;
96              
97 0           my $c = $self->ua;
98              
99             # Multiple conversations
100 0           my $inbox = $c->get($url)->res->dom;
101              
102             # metadata
103 0           my $meta = Mojo::JSON->new->decode($inbox->at('response > json')->text);
104              
105             # content
106 0           my $xml = Mojo::DOM->new->parse($inbox->at('response > html')->text);
107              
108             # Each conversation in a span.gc-message
109 0           return map
110             Google::Voice::Feed->new($_, $meta, $self->rnr_se, $c),
111 0           @{$xml->find('.gc-message')};
112             }
113              
114             sub call {
115 0     0 1   my $self = shift;
116 0           my ($from, $to) = @_;
117              
118 0           my $json = $self->ua->post(
119             'https://www.google.com/voice/call/connect',
120             form => {
121             forwardingNumber => $from,
122             outgoingNumber => $to,
123             phoneType => 1,
124             remember => 0,
125             _rnr_se => $self->rnr_se
126             }
127             )->res->json;
128              
129 0 0 0       $@ = $json->{error} and return unless $json->{ok};
130              
131 0           return Google::Voice::Call->new(@_, $self->rnr_se, $self->ua);
132             }
133              
134             1;
135              
136             =head1 NAME
137              
138             Google::Voice - Easy interface for google voice
139              
140             =head1 DESCRIPTION
141              
142             Easy interface for google voice
143              
144             =head1 USAGE
145              
146             use Google::Voice;
147              
148             my $g = Google::Voice->new->login('username', 'password');
149              
150             # Send sms
151             $g->send_sms(5555555555 => 'Hello friend!');
152              
153             # Error code from google on fail
154             print $@ if !$g->send_sms('invalid phone' => 'text message');
155              
156             # connect call & cancel it
157             my $call = $g->call('+15555555555' => '+14444444444');
158             $call->cancel;
159              
160              
161             # Print all sms conversations
162             foreach my $sms ($g->sms) {
163             print $sms->name;
164             print $_->time, ':', $_->text, "\n" foreach $sms->messages;
165              
166             # Delete conversation
167             $sms->delete;
168             }
169              
170             # loop through voicemail messages
171             foreach my $vm ($g->voicemail) {
172              
173             # Name, number, and transcribed text
174             print $vm->name . "\n";
175             print $vm->meta->{phoneNumber} . "\n";
176             print $vm->text . "\n";
177              
178             # Download mp3
179             $vm->download->move_to($vm->id . '.mp3');
180              
181             # Delete
182             $vm->delete;
183             }
184              
185             =head1 METHODS
186              
187             =head2 new
188              
189             Create object
190              
191             =head2 login
192              
193             Login. Returns object on success, false on failure.
194              
195             =head2 call
196              
197             Connect two phone numbers
198              
199             =head2 send_sms
200              
201             Send SMS message. Returns true/false.
202              
203             =head2 sms
204              
205             List of SMS messages
206              
207             =head2 voicemail
208              
209             List of voicemail messages
210              
211             =head2 recorded
212              
213             List of recorded calls
214              
215             =head2 placed
216              
217             List of placed calls
218              
219             =head2 received
220              
221             List of placed calls
222              
223             =head2 missed
224              
225             List of missed calls
226              
227             =head2 starred
228              
229             List of starred items (call, sms, or voicemail)
230              
231             =head2 spam
232              
233             List of items marked as spam (call, sms, or voicemail)
234              
235             =head2 all
236              
237             List of all items (call, sms, or voicemail)
238              
239             =head1 SEE ALSO
240              
241             L, L
242              
243             =head1 DEVELOPMENT
244              
245             L
246              
247             =head1 AUTHOR
248              
249             Glen Hinkle tempire@cpan.org
250              
251             =head1 CREDITS
252              
253             David Jones
254              
255             Graham Forest
256              
257             =cut