harleytt-gvoice-ruby 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.bundle/config +2 -0
- data/.gitignore +5 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +24 -0
- data/README.rdoc +90 -0
- data/Rakefile +12 -0
- data/VERSION +1 -0
- data/bin/gv-notifier +78 -0
- data/bin/gv-place-call +22 -0
- data/bin/gv-send-sms +16 -0
- data/config/gvoice-ruby-config.yml.sample +11 -0
- data/gvoice-ruby.gemspec +34 -0
- data/lib/gvoice-ruby/call.rb +9 -0
- data/lib/gvoice-ruby/client.rb +277 -0
- data/lib/gvoice-ruby/compatibility.rb +75 -0
- data/lib/gvoice-ruby/config.rb +39 -0
- data/lib/gvoice-ruby/errors.rb +5 -0
- data/lib/gvoice-ruby/inbox_parser.rb +126 -0
- data/lib/gvoice-ruby/sms.rb +9 -0
- data/lib/gvoice-ruby/user.rb +12 -0
- data/lib/gvoice-ruby/version.rb +4 -0
- data/lib/gvoice-ruby/voicemail.rb +10 -0
- data/lib/gvoice-ruby.rb +5 -0
- data/test/client_test.rb +59 -0
- data/test/compatibility_test.rb +47 -0
- data/test/config_test.rb +50 -0
- data/test/fixtures/config_fixture.yml +11 -0
- data/test/fixtures/inbox.json +1 -0
- data/test/fixtures/inbox.yml +206 -0
- data/test/fixtures/inbox_fixture.html +2657 -0
- data/test/fixtures/login_fixture.html +567 -0
- data/test/gvoice-ruby_test.rb +9 -0
- data/test/inbox_parser_test.rb +53 -0
- data/test/test_helper.rb +20 -0
- metadata +223 -0
data/.bundle/config
ADDED
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# A sample Gemfile
|
2
|
+
source "http://rubygems.org"
|
3
|
+
#
|
4
|
+
gem 'curb'
|
5
|
+
gem 'nokogiri'
|
6
|
+
gem 'json'
|
7
|
+
|
8
|
+
# Make sure the correct version of xmpp4r-simple is loaded when
|
9
|
+
# require is called in bin/gv-notifier
|
10
|
+
if RUBY_VERSION < '1.9'
|
11
|
+
gem 'xmpp4r-simple', "= 0.8.8"
|
12
|
+
else
|
13
|
+
gem 'scashin133-xmpp4r-simple', '= 0.8.9'
|
14
|
+
end
|
15
|
+
|
16
|
+
group :test do
|
17
|
+
gem 'shoulda'
|
18
|
+
gem 'mocha'
|
19
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
curb (0.7.8)
|
5
|
+
json (1.4.6)
|
6
|
+
mocha (0.9.8)
|
7
|
+
rake
|
8
|
+
nokogiri (1.4.3.1)
|
9
|
+
rake (0.8.7)
|
10
|
+
scashin133-xmpp4r-simple (0.8.9)
|
11
|
+
xmpp4r (>= 0.3.2)
|
12
|
+
shoulda (2.11.3)
|
13
|
+
xmpp4r (0.5)
|
14
|
+
|
15
|
+
PLATFORMS
|
16
|
+
ruby
|
17
|
+
|
18
|
+
DEPENDENCIES
|
19
|
+
curb
|
20
|
+
json
|
21
|
+
mocha
|
22
|
+
nokogiri
|
23
|
+
scashin133-xmpp4r-simple (= 0.8.9)
|
24
|
+
shoulda
|
data/README.rdoc
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
= gvoice-ruby
|
2
|
+
|
3
|
+
== Introduction
|
4
|
+
|
5
|
+
gvoice-ruby is a library for interacting with Google's Voice service (previously GrandCentral) using ruby. As Google does not yet publish an official API for Google Voice gvoice-ruby uses the ruby libcurl library (Curb) to interact with the web service itself by using the HTTP verbs POST and GET to send and retrieve information to the Google Voice service.
|
6
|
+
|
7
|
+
gvoice-ruby is currently a very preliminary project with limited functionality basically confined to returning arrays of voicemail or sms objects and sending sms messages, or connecting calls. It cannot cancel calls already in progress. It currently works under ruby 1.8.7-p302 and 1.9.2-p0 on my computer running Mac OS X 10.6 (Snow Leopard). It is not guaranteed to work anywhere else and has very few tests.
|
8
|
+
|
9
|
+
However, you should be able to run bundle install after running git clone as outlined below and it should function.
|
10
|
+
|
11
|
+
== Prerequisites
|
12
|
+
|
13
|
+
1.) Ruby 1.8.7 or 1.9.2
|
14
|
+
|
15
|
+
2.) Bundler 1.0 (gem install bundler)
|
16
|
+
|
17
|
+
== Installing
|
18
|
+
|
19
|
+
Install the gem:
|
20
|
+
|
21
|
+
$ gem install gvoice-ruby
|
22
|
+
|
23
|
+
or
|
24
|
+
|
25
|
+
Clone the git repository:
|
26
|
+
|
27
|
+
$ git clone git://github.com/kgautreaux/gvoice-ruby.git
|
28
|
+
|
29
|
+
then:
|
30
|
+
|
31
|
+
$ cd gvoice-ruby
|
32
|
+
|
33
|
+
and:
|
34
|
+
|
35
|
+
$ bundle install
|
36
|
+
|
37
|
+
This should install all the required gems provided you have the right permissions.
|
38
|
+
|
39
|
+
To run the test suite:
|
40
|
+
|
41
|
+
$ bundle exec rake
|
42
|
+
|
43
|
+
== Examples
|
44
|
+
|
45
|
+
Please see the bin/ directory for examples of using the library to accomplish some tasks. You must run bundle exec 'command' in order for the program to work, i.e.
|
46
|
+
|
47
|
+
$ cd gvoice-ruby
|
48
|
+
$ bundle exec bin/gv-notifier
|
49
|
+
|
50
|
+
== Known Issues
|
51
|
+
|
52
|
+
1.) You have to run 'bundle exec bin/"command"' and it must be run in the context of the gvoice-ruby directory.
|
53
|
+
|
54
|
+
2.) It does some wicked monkeypatching to run on both 1.9.2 and 1.8.7 mostly due to the new Date class in Ruby 1.9.2.
|
55
|
+
|
56
|
+
3.) It is technically a library but it doesn't document any sort of API thus far.
|
57
|
+
|
58
|
+
== BUGS
|
59
|
+
|
60
|
+
1.) It has few tests.
|
61
|
+
|
62
|
+
2.) If the Google Voice service changes in any way it will break.
|
63
|
+
|
64
|
+
3.) If the libraries used change it will break.
|
65
|
+
|
66
|
+
4.) It is somewhat tightly coupled to the private post and fetch_page methods (sms and voicemail arrays depend upon these methods).
|
67
|
+
|
68
|
+
5.) It is not RESTful in any way.
|
69
|
+
|
70
|
+
6.) It cannot yet cancel calls very well, but it can initiate them.
|
71
|
+
|
72
|
+
7.) It is my first attempt at writing a library and is not idiomatic ruby.
|
73
|
+
|
74
|
+
8.) It requires rubygems but doesn't set RUBYOPT environment variable.
|
75
|
+
|
76
|
+
9.) It requires the file compatibility.rb which monkeypatches the Array and Symbol classes on Ruby < 1.9 to enable sorting.
|
77
|
+
|
78
|
+
10.) You cannot change any of the Google Voice settings using this library.
|
79
|
+
|
80
|
+
11.) The library loads a specific YAML file that must be in place for anything to work as the script has no logical defaults.
|
81
|
+
|
82
|
+
12.) Minimal to no error handling present. Fail silent...Fail deep.
|
83
|
+
|
84
|
+
13.) The library doesn't post the id of sms messages it sends so they don't get associated with any conversation in Google Voice.
|
85
|
+
|
86
|
+
14.) On Ruby 1.9 and above the library monkeypatches it's own call method to reformat dates.
|
87
|
+
|
88
|
+
== Thanks
|
89
|
+
|
90
|
+
Thanks to Chad Smith for posting the URL for accessing the Google Voice service as listed on http://posttopic.com/topic/google-voice-add-on-development#post-27
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
desc "Run all tests for this project."
|
6
|
+
Rake::TestTask.new(:test) do |test|
|
7
|
+
test.libs << 'lib' << 'test'
|
8
|
+
test.pattern = 'test/**/*_test.rb'
|
9
|
+
test.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :test
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.3
|
data/bin/gv-notifier
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env ruby -W1
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
begin
|
5
|
+
require 'gvoice-ruby'
|
6
|
+
require 'xmpp4r-simple'
|
7
|
+
rescue LoadError
|
8
|
+
warn "\n\nYou must run 'bundle exec #{$0}' to allow loading of correct gems!\n\n"
|
9
|
+
raise
|
10
|
+
end
|
11
|
+
|
12
|
+
config = GvoiceRuby::Configurator.load_config
|
13
|
+
|
14
|
+
# Main
|
15
|
+
jabber_bot = Jabber::Simple.new(config[:bot_name], config[:bot_password])
|
16
|
+
|
17
|
+
voicebox = GvoiceRuby::Client.new
|
18
|
+
|
19
|
+
voicebox.check
|
20
|
+
|
21
|
+
voicebox.missed.each do |call|
|
22
|
+
if DateTime.parse(call.display_start_date_time).yday >= (Time.now.yday - 3)
|
23
|
+
puts "You missed a call from #{call.from} on #{call.display_start_date_time}."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
puts "\n"
|
27
|
+
|
28
|
+
voicebox.received.each do |call|
|
29
|
+
if DateTime.parse(call.display_start_date_time).yday >= (Time.now.yday - 1)
|
30
|
+
puts "You received a call from #{call.from} on #{call.display_start_date_time}."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
puts "\n"
|
34
|
+
|
35
|
+
voicebox.placed.each do |call|
|
36
|
+
if DateTime.parse(call.display_start_date_time).yday >= (Time.now.yday - 3)
|
37
|
+
puts "You placed a call to #{call.from} on #{call.display_start_date_time}."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
puts "\n"
|
41
|
+
|
42
|
+
if voicebox.any_unread?
|
43
|
+
sms_messages = []
|
44
|
+
voicemail_messages = []
|
45
|
+
|
46
|
+
voicebox.smss.each do |t|
|
47
|
+
if t.labels.include?('unread')
|
48
|
+
sms_messages << "#{t.from} says: '#{t.text}' at #{t.display_start_date_time}\n"
|
49
|
+
voicebox.mark_as_read({:id => t.id})
|
50
|
+
voicebox.add_note({:id => t.id, :note => "You were notified via gv-notifier at #{Time.now}."})
|
51
|
+
# voicebox.star({:id => t.id})
|
52
|
+
else
|
53
|
+
next
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
voicebox.voicemails.each do |v|
|
58
|
+
if v.labels.include?('unread')
|
59
|
+
voicemail_messages << "Voicemail from #{v.from} at #{v.display_start_date_time}:\n#{v.transcript}\n"
|
60
|
+
voicebox.mark_as_read({:id => v.id})
|
61
|
+
else
|
62
|
+
next
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
begin
|
67
|
+
[sms_messages, voicemail_messages].each do |thing|
|
68
|
+
thing.each { |m| jabber_bot.deliver(voicebox.user.email, m, :normal);print m }
|
69
|
+
end
|
70
|
+
rescue
|
71
|
+
raise
|
72
|
+
end
|
73
|
+
config[:last_message_start_time] = voicebox.all_messages.last.start_time
|
74
|
+
else
|
75
|
+
print "No unread messages in your Google Voice inbox.\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
GvoiceRuby::Configurator.write_config(config)
|
data/bin/gv-place-call
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby -W1
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
begin
|
5
|
+
require 'gvoice-ruby'
|
6
|
+
rescue LoadError
|
7
|
+
warn "\n\nYou must run 'bundle exec #{$0}' to allow loading of correct gems!\n\n"
|
8
|
+
raise
|
9
|
+
end
|
10
|
+
|
11
|
+
voicebox = GvoiceRuby::Client.new
|
12
|
+
|
13
|
+
# Phone types:
|
14
|
+
# 1) Home
|
15
|
+
# 2) Mobile
|
16
|
+
# 3) Work
|
17
|
+
# 7) Gizmo
|
18
|
+
this_phone = { :outgoing_number => "3088720071", :forwarding_number => "3088721257", :phone_type => 2 }
|
19
|
+
|
20
|
+
puts "Call connected!" if voicebox.call(this_phone)
|
21
|
+
|
22
|
+
puts "Call cancelled!" if voicebox.cancel_call
|
data/bin/gv-send-sms
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby -W1
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
begin
|
5
|
+
require 'gvoice-ruby'
|
6
|
+
rescue LoadError
|
7
|
+
warn "\n\nYou must run 'bundle exec #{$0}' to allow loading of correct gems!\n\n"
|
8
|
+
raise
|
9
|
+
end
|
10
|
+
|
11
|
+
voicebox = GvoiceRuby::Client.new
|
12
|
+
|
13
|
+
txt = {:phone_number => "3088721257", :text => "Testing sms from gvoice-ruby!" }
|
14
|
+
|
15
|
+
puts "Message sent!" if voicebox.send_sms(txt)
|
16
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
---
|
2
|
+
:google_auth_url: https://www.google.com/accounts/ServiceLoginAuth
|
3
|
+
:google_account_password: your_google_account_password
|
4
|
+
:continue_url: https://www.google.com/voice
|
5
|
+
:last_message_start_time: 0
|
6
|
+
:bot_name: your_gtalk_or_jabber_bot_name
|
7
|
+
:google_service: grandcentral
|
8
|
+
:bot_password: your_gtalk_bot_password
|
9
|
+
:google_voice_feed_url: https://www.google.com/voice/inbox/recent
|
10
|
+
:google_account_email: your_google_email_address@gmail.com
|
11
|
+
:logfile: path/to/log/file
|
data/gvoice-ruby.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
3
|
+
require 'gvoice-ruby/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "harleytt-gvoice-ruby"
|
7
|
+
s.version = GvoiceRuby::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Keith Gautreaux", "Roy Kolak", "Jeremy Stephens"]
|
10
|
+
s.email = ["keith.gautreaux@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/kgautreaux/gvoice-ruby"
|
12
|
+
s.summary = "gvoice-ruby is a library for interacting with Google’s Voice service (previously GrandCentral) using ruby."
|
13
|
+
s.description = <<-EOS
|
14
|
+
gvoice-ruby is currently a very preliminary project with limited functionality basically confined to returning arrays of voicemail or sms objects and sending sms messages, or connecting calls. It cannot cancel calls already in progress. It currently works under ruby 1.8.7-p302 and 1.9.2-p0 on my computer running Mac OS X 10.6 (Snow Leopard). It is not guaranteed to work anywhere else and has very few tests.
|
15
|
+
EOS
|
16
|
+
|
17
|
+
s.required_rubygems_version = "~> 1.3.6"
|
18
|
+
# s.rubyforge_project = "gvoice-ruby"
|
19
|
+
|
20
|
+
s.add_dependency "curb", "~> 0.7.8"
|
21
|
+
s.add_dependency "nokogiri", "~> 1.4.3.1"
|
22
|
+
s.add_dependency "json", "~> 1.4.6"
|
23
|
+
|
24
|
+
s.add_development_dependency "bundler", "~> 1.0.0.rc.6"
|
25
|
+
s.add_development_dependency "mocha", "~> 0.9.7"
|
26
|
+
s.add_development_dependency "shoulda", "~> 2.11.3"
|
27
|
+
s.add_development_dependency "rake", "~> 0.8.7"
|
28
|
+
|
29
|
+
s.files = `git ls-files`.split("\n")
|
30
|
+
# s.executables = ["bin/gv-notifier", "bin/gv-place-call", "bin/gv-send-sms"]#`git ls-files`.split("\n").select { |f| f =~ /^bin/ }
|
31
|
+
s.require_paths = ['lib']
|
32
|
+
|
33
|
+
s.post_install_message = "\n\nPlease run 'cd gvoice-ruby;bundle install;bundle exec rake' to run the tests.\n\n"
|
34
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module GvoiceRuby
|
3
|
+
class Call < Struct.new(:id, :start_time, :display_number, :display_start_date_time, :display_start_time, :relative_start_time, :is_read, :starred, :labels, :from, :to)
|
4
|
+
#attr_accessor :id, :start_time, :display_number, :display_start_date_time, :display_start_time, :relative_start_time, :is_read, :labels, :from, :to, :text
|
5
|
+
#
|
6
|
+
#def initialize
|
7
|
+
#end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.unshift(File.dirname(__FILE__))
|
3
|
+
%w[curb nokogiri json sms voicemail call user logger compatibility inbox_parser open-uri errors].each { |lib| require lib }
|
4
|
+
|
5
|
+
module GvoiceRuby
|
6
|
+
class Client
|
7
|
+
include Curl
|
8
|
+
|
9
|
+
attr_accessor :unread_counts, :smss, :voicemails, :user, :all_messages, :calls
|
10
|
+
attr_reader :logger
|
11
|
+
|
12
|
+
def initialize(config = GvoiceRuby::Configurator.load_config)
|
13
|
+
if config[:google_account_email].nil? || config[:google_account_password].nil?
|
14
|
+
raise ArgumentError, "Invalid Google Account username or password provided."
|
15
|
+
else
|
16
|
+
@logger = Logger.new(config.has_key?(:logfile) ? config[:logfile] :
|
17
|
+
File.join(File.dirname(__FILE__), '..', '..', 'log', 'gvoice-ruby.log'))
|
18
|
+
@user = User.new(config[:google_account_email], config[:google_account_password])
|
19
|
+
@any_unread = []
|
20
|
+
@unread_counts = {}
|
21
|
+
@all_messages = []
|
22
|
+
initialize_curb
|
23
|
+
end
|
24
|
+
|
25
|
+
login(config)
|
26
|
+
set_rnr_se_token
|
27
|
+
end
|
28
|
+
|
29
|
+
def any_unread?
|
30
|
+
@any_unread
|
31
|
+
end
|
32
|
+
|
33
|
+
def logged_in?
|
34
|
+
!@curb_instance.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def check(parser = GvoiceRuby::InboxParser.new)
|
38
|
+
inbox = parser.parse_page(fetch_page)
|
39
|
+
|
40
|
+
get_unread_counts(inbox)
|
41
|
+
@smss = parser.parse_sms_messages(inbox['messages'])
|
42
|
+
@voicemails = parser.parse_voicemail_messages(inbox['messages'])
|
43
|
+
@all_messages = smss | voicemails
|
44
|
+
@all_messages.sort_by!(&:start_time)
|
45
|
+
end
|
46
|
+
|
47
|
+
def missed(parser = GvoiceRuby::InboxParser.new)
|
48
|
+
inbox = parser.parse_page(fetch_page('https://www.google.com/voice/inbox/recent/missed/'))
|
49
|
+
parser.parse_calls(inbox['messages'])
|
50
|
+
end
|
51
|
+
|
52
|
+
def received(parser = GvoiceRuby::InboxParser.new)
|
53
|
+
inbox = parser.parse_page(fetch_page('https://www.google.com/voice/inbox/recent/received/'))
|
54
|
+
parser.parse_calls(inbox['messages'])
|
55
|
+
end
|
56
|
+
|
57
|
+
def placed(parser = GvoiceRuby::InboxParser.new)
|
58
|
+
inbox = parser.parse_page(fetch_page('https://www.google.com/voice/inbox/recent/placed/'))
|
59
|
+
parser.parse_calls(inbox['messages'])
|
60
|
+
end
|
61
|
+
|
62
|
+
def send_sms(options, show_details = true)
|
63
|
+
fields = [ PostField.content('phoneNumber', options[:phone_number]),
|
64
|
+
PostField.content('text', options[:text]),
|
65
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
66
|
+
|
67
|
+
options.merge!({ :post_url => "https://www.google.com/voice/sms/send" })
|
68
|
+
|
69
|
+
if show_details
|
70
|
+
logger.info "Sending sms..."
|
71
|
+
logger.info "\tTo: #{options[:phone_number]}"
|
72
|
+
logger.info "\tBody: #{options[:text]}"
|
73
|
+
logger.info "\tSent at: #{Time.now}\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
post(options, fields)
|
77
|
+
end
|
78
|
+
|
79
|
+
def call(options)
|
80
|
+
fields = [ PostField.content('outgoingNumber', options[:outgoing_number]),
|
81
|
+
PostField.content('forwardingNumber', options[:forwarding_number]),
|
82
|
+
PostField.content('phoneType', options[:phone_type] || 2),
|
83
|
+
PostField.content('subscriberNumber', 'undefined'),
|
84
|
+
PostField.content('remember', 0),
|
85
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
86
|
+
|
87
|
+
options.merge!({ :post_url => "https://www.google.com/voice/call/connect" })
|
88
|
+
|
89
|
+
post(options, fields)
|
90
|
+
end
|
91
|
+
|
92
|
+
def cancel_call(options = {})
|
93
|
+
fields = [ PostField.content('outgoingNumber', options[:outgoing_number] || 'undefined'),
|
94
|
+
PostField.content('forwardingNumber', options[:forwarding_number] || 'undefined'),
|
95
|
+
PostField.content('cancelType', 'C2C'),
|
96
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
97
|
+
|
98
|
+
options.merge!({ :post_url => "https://www.google.com/voice/call/cancel" })
|
99
|
+
|
100
|
+
post(options, fields)
|
101
|
+
end
|
102
|
+
|
103
|
+
def archive(options)
|
104
|
+
fields = [ PostField.content('messages', options[:id]),
|
105
|
+
PostField.content('archive', 1),
|
106
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
107
|
+
|
108
|
+
options.merge!({ :post_url => 'https://www.google.com/voice/inbox/mark'})
|
109
|
+
|
110
|
+
post(options, fields)
|
111
|
+
end
|
112
|
+
|
113
|
+
def mark_as_read(options)
|
114
|
+
fields = [ PostField.content('messages', options[:id]),
|
115
|
+
PostField.content('read', 1),
|
116
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
117
|
+
|
118
|
+
options.merge!({ :post_url => 'https://www.google.com/voice/inbox/mark'})
|
119
|
+
|
120
|
+
post(options, fields)
|
121
|
+
end
|
122
|
+
|
123
|
+
def mark_as_unread(options)
|
124
|
+
fields = [ PostField.content('messages', options[:id]),
|
125
|
+
PostField.content('read', 0),
|
126
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
127
|
+
|
128
|
+
options.merge!({ :post_url => 'https://www.google.com/voice/inbox/mark'})
|
129
|
+
|
130
|
+
post(options, fields)
|
131
|
+
end
|
132
|
+
|
133
|
+
def star(options)
|
134
|
+
fields = [ PostField.content('messages', options[:id]),
|
135
|
+
PostField.content('star', 1),
|
136
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
137
|
+
|
138
|
+
options.merge!({ :post_url => 'https://www.google.com/voice/inbox/star'})
|
139
|
+
|
140
|
+
post(options, fields)
|
141
|
+
end
|
142
|
+
|
143
|
+
def unstar(options)
|
144
|
+
fields = [ PostField.content('messages', options[:id]),
|
145
|
+
PostField.content('star', 0),
|
146
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
147
|
+
|
148
|
+
options.merge!({ :post_url => 'https://www.google.com/voice/inbox/star'})
|
149
|
+
|
150
|
+
post(options, fields)
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_note(options)
|
154
|
+
fields = [ PostField.content('id', options[:id]),
|
155
|
+
PostField.content('note', options[:note]),
|
156
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
157
|
+
|
158
|
+
options.merge!({ :post_url => 'https://www.google.com/voice/inbox/savenote'})
|
159
|
+
|
160
|
+
post(options, fields)
|
161
|
+
end
|
162
|
+
|
163
|
+
def delete_note(options)
|
164
|
+
fields = [ PostField.content('id', options[:id]),
|
165
|
+
PostField.content('_rnr_se', @_rnr_se) ]
|
166
|
+
|
167
|
+
options.merge!({ :post_url => 'https://www.google.com/voice/inbox/deletenote'})
|
168
|
+
|
169
|
+
post(options, fields)
|
170
|
+
end
|
171
|
+
|
172
|
+
def logout
|
173
|
+
if logged_in?
|
174
|
+
@curb_instance.url = "https://www.google.com/voice/account/signout"
|
175
|
+
@curb_instance.perform
|
176
|
+
logger.info "FINISHED LOGOUT #{@curb_instance.url}: HTTP #{@curb_instance.response_code}"
|
177
|
+
@curb_instance = nil
|
178
|
+
end
|
179
|
+
self
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
def login(options = {})
|
184
|
+
@curb_instance.url = 'https://www.google.com/accounts/ServiceLoginAuth'
|
185
|
+
@curb_instance.perform
|
186
|
+
# If String#force_encoding doesn't exist we are on Ruby 1.8 and we shouldn't encode anything
|
187
|
+
@curb_instance.body_str.force_encoding("UTF-8") if @curb_instance.body_str.respond_to?(:force_encoding)
|
188
|
+
|
189
|
+
defeat_google_xsrf(@curb_instance.body_str)
|
190
|
+
|
191
|
+
begin
|
192
|
+
fields = [ PostField.content('continue', (options[:continue_url] || 'https://www.google.com/voice')),
|
193
|
+
PostField.content('GALX', @galx),
|
194
|
+
PostField.content('service', (options[:google_service] || 'grandcentral')),
|
195
|
+
PostField.content('Email', options[:google_account_email]),
|
196
|
+
PostField.content('Passwd', options[:google_account_password]) ]
|
197
|
+
rescue ArgumentError
|
198
|
+
warn "Unable to log in."
|
199
|
+
raise GvoiceRuby::LoginFailed
|
200
|
+
end
|
201
|
+
options.merge!({ :post_url => 'https://www.google.com/accounts/ServiceLoginAuth' })
|
202
|
+
|
203
|
+
post(options, fields)
|
204
|
+
end
|
205
|
+
|
206
|
+
def post(options, fields)
|
207
|
+
@curb_instance.url = options[:post_url] #"https://www.google.com/voice/call/connect || https://www.google.com/voice/sms/send"
|
208
|
+
@curb_instance.http_post(fields)
|
209
|
+
|
210
|
+
logger.info "FINISHED POST TO #{options[:post_url]}: HTTP #{@curb_instance.response_code}"
|
211
|
+
return @curb_instance
|
212
|
+
end
|
213
|
+
|
214
|
+
def fetch_page(url = 'https://www.google.com/voice/inbox/recent')
|
215
|
+
|
216
|
+
@curb_instance.url = url #"https://www.google.com/voice/inbox/recent"
|
217
|
+
|
218
|
+
@curb_instance.http_get
|
219
|
+
|
220
|
+
logger.info "FINISHED FETCHING #{url}: HTTP #{@curb_instance.response_code}"
|
221
|
+
return @curb_instance
|
222
|
+
end
|
223
|
+
|
224
|
+
def get_unread_counts(inbox)
|
225
|
+
@unread_counts = inbox['unreadCounts']
|
226
|
+
@any_unread = inbox['unreadCounts']['unread'].to_i != 0
|
227
|
+
logger.info "No unread messages in your Google Voice inbox." unless @any_unread
|
228
|
+
end
|
229
|
+
|
230
|
+
def defeat_google_xsrf(body_string)
|
231
|
+
# defeat Google's XSRF protection
|
232
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(body_string)
|
233
|
+
doc.css('div.loginBox table#gaia_table input').each do |input|
|
234
|
+
if input.to_s =~ /GALX/u
|
235
|
+
@galx = input.to_s.scan(/value\="(.+?)"/u).flatten!.pop
|
236
|
+
else
|
237
|
+
next
|
238
|
+
# raise IOError, 'Cannot fetch galx attribute from Google.'
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def set_rnr_se_token
|
244
|
+
if @curb_instance.response_code == 200 #&& @curb_instance.respond_to?(:perform) # Vestigial?
|
245
|
+
@curb_instance.url = "http://www.google.com/voice"
|
246
|
+
@curb_instance.perform
|
247
|
+
@_rnr_se = extract_rnr_se(@curb_instance.body_str)
|
248
|
+
else
|
249
|
+
raise IOError, "Curb instance was not properly initialized."
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def extract_rnr_se(body_string)
|
254
|
+
begin
|
255
|
+
/value="(.+)"/.match(Nokogiri::HTML::Document.parse(body_string).css('form#gc-search-form').inner_html)
|
256
|
+
return $1
|
257
|
+
rescue IOError
|
258
|
+
raise IOError, "Problem extracting _rnr_se code from page."
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def initialize_curb
|
263
|
+
@curb_instance = Easy.new do |curl|
|
264
|
+
# Google gets mad if you don't fake this...
|
265
|
+
curl.headers["User-Agent"] = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2"
|
266
|
+
# Let's see what happens under the hood
|
267
|
+
# curl.verbose = true
|
268
|
+
|
269
|
+
# Google will redirect us a bit
|
270
|
+
curl.follow_location = true
|
271
|
+
|
272
|
+
# Google will make sure we retain cookies
|
273
|
+
curl.enable_cookies = true
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|