google_text 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +32 -0
- data/HISTORY.md +4 -0
- data/README.md +6 -1
- data/Rakefile +3 -20
- data/google_text.gemspec +32 -0
- data/lib/google_text/client.rb +3 -3
- data/lib/google_text/configuration.rb +1 -1
- data/lib/google_text/parsers/base.rb +55 -0
- data/lib/google_text/parsers/dashboard.rb +16 -0
- data/lib/google_text/parsers/inbox.rb +75 -0
- data/lib/google_text/parsers/login.rb +20 -0
- data/lib/google_text/version.rb +1 -1
- data/lib/google_text.rb +9 -2
- data/spec/data/dashboard_url.html +494 -0
- data/spec/data/login_url.html +906 -0
- data/spec/data/recent_messages.html +588 -0
- data/spec/google-text/client_spec.rb +6 -9
- data/spec/google-text/parsers/dashboard_spec.rb +14 -0
- data/spec/google-text/parsers/inbox_spec.rb +14 -0
- data/spec/google-text/parsers/login_spec.rb +14 -0
- data/spec/spec_helper.rb +14 -2
- metadata +100 -88
- data/VERSION +0 -1
- data/lib/google_text/parser.rb +0 -107
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
google_text (0.0.2)
|
5
|
+
curb (~> 0.7.6)
|
6
|
+
json
|
7
|
+
nokogiri
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
curb (0.7.15)
|
13
|
+
diff-lcs (1.1.3)
|
14
|
+
fakeweb (1.3.0)
|
15
|
+
json (1.6.1)
|
16
|
+
nokogiri (1.5.0)
|
17
|
+
rspec (2.6.0)
|
18
|
+
rspec-core (~> 2.6.0)
|
19
|
+
rspec-expectations (~> 2.6.0)
|
20
|
+
rspec-mocks (~> 2.6.0)
|
21
|
+
rspec-core (2.6.4)
|
22
|
+
rspec-expectations (2.6.0)
|
23
|
+
diff-lcs (~> 1.1.2)
|
24
|
+
rspec-mocks (2.6.0)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
fakeweb
|
31
|
+
google_text!
|
32
|
+
rspec
|
data/HISTORY.md
CHANGED
data/README.md
CHANGED
@@ -24,6 +24,7 @@ Prerequisites
|
|
24
24
|
|
25
25
|
GoogleText currently relies on the following gems:
|
26
26
|
|
27
|
+
* bundler
|
27
28
|
* nokogiri
|
28
29
|
* curb
|
29
30
|
* json
|
@@ -33,10 +34,14 @@ Installation
|
|
33
34
|
|
34
35
|
1. Get a Google Voice account, if don't already have one.
|
35
36
|
|
36
|
-
2. Install the gem
|
37
|
+
2. Install the gem:
|
37
38
|
|
38
39
|
`gem install google_text`
|
39
40
|
|
41
|
+
3. Install all dependencies using bundler:
|
42
|
+
|
43
|
+
`bundle install`
|
44
|
+
|
40
45
|
3. Configure GoogleText someplace sensible, using the email address and password you use to log on to your Google Voice account. If you are using GoogleText in a Rails application, `config/initializers/google_text.rb` is a good place to put the configuration block:
|
41
46
|
|
42
47
|
|
data/Rakefile
CHANGED
@@ -1,21 +1,4 @@
|
|
1
|
-
require
|
2
|
-
require '
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rspec/core/rake_task'
|
3
3
|
|
4
|
-
|
5
|
-
task :publish do
|
6
|
-
require File.dirname(__FILE__) + '/lib/google_text/version'
|
7
|
-
|
8
|
-
system "git tag v#{GoogleText::Version}"
|
9
|
-
sh "gem build google_text.gemspec"
|
10
|
-
sh "gem push google_text-#{GoogleText::Version}.gem"
|
11
|
-
sh "git push origin master --tags"
|
12
|
-
sh "git clean -fd"
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
desc "Install gem locally"
|
17
|
-
task :install do
|
18
|
-
require File.dirname(__FILE__) + '/lib/google_text/version'
|
19
|
-
sh "gem build google_text.gemspec"
|
20
|
-
sh "sudo gem install ./google_text-#{GoogleText::Version}.gem"
|
21
|
-
end
|
4
|
+
RSpec::Core::RakeTask.new('spec')
|
data/google_text.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'google_text/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "google_text"
|
7
|
+
s.version = GoogleText::VERSION
|
8
|
+
s.summary = "GoogleText is a SMS client library for sending and receiving free SMS through Google Voice."
|
9
|
+
s.description = "GoogleText is a SMS client library for sending and receiving free SMS through Google Voice. Alas, Google Voice does not yet have an API, so GoogleText uses Curl and Nokogiri to scrape and post using Google Voice web URLs."
|
10
|
+
|
11
|
+
s.homepage = "http://github.com/feldpost/google_text"
|
12
|
+
s.email = "sebastian@feldpost.com"
|
13
|
+
s.authors = [ "Sebastian Friedrich" ]
|
14
|
+
|
15
|
+
s.rubyforge_project = "google_text"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency "json"
|
23
|
+
s.add_runtime_dependency "nokogiri"
|
24
|
+
s.add_runtime_dependency "curb", '~>0.7.6'
|
25
|
+
|
26
|
+
s.add_development_dependency('rspec')
|
27
|
+
s.add_development_dependency('fakeweb')
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
|
data/lib/google_text/client.rb
CHANGED
@@ -27,12 +27,12 @@ module GoogleText
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def get_xsrf_token
|
30
|
-
@xsrf_token =
|
30
|
+
@xsrf_token = Parsers::Login.new(login_page).xsrf_token
|
31
31
|
end
|
32
32
|
|
33
33
|
def get_session_id
|
34
34
|
begin
|
35
|
-
@session_id =
|
35
|
+
@session_id = Parsers::Dashboard.new(dashboard_page).session_id
|
36
36
|
rescue
|
37
37
|
raise "Could not retrieve Google Voice Session ID"
|
38
38
|
end
|
@@ -44,7 +44,7 @@ module GoogleText
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def get_messages
|
47
|
-
@messages =
|
47
|
+
@messages = Parsers::Inbox.new(inbox_page).messages
|
48
48
|
end
|
49
49
|
|
50
50
|
def mark_as_read(id)
|
@@ -3,7 +3,7 @@ module GoogleText
|
|
3
3
|
attr_accessor :email, :password, :login_url, :user_agent, :dashboard_url, :inbox_url, :service, :send_url, :mark_as_read_url
|
4
4
|
|
5
5
|
DEFAULTS = {
|
6
|
-
:login_url => 'https://
|
6
|
+
:login_url => 'https://accounts.google.com/ServiceLoginAuth',
|
7
7
|
:dashboard_url => 'https://www.google.com/voice',
|
8
8
|
:mark_as_read_url => 'https://www.google.com/voice/inbox/mark',
|
9
9
|
:inbox_url => 'https://www.google.com/voice/inbox/recent',
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module GoogleText
|
2
|
+
module Parsers
|
3
|
+
class Base
|
4
|
+
attr_accessor :string
|
5
|
+
|
6
|
+
def self.selectors_for(symbol_or_hash)
|
7
|
+
if symbol_or_hash.respond_to?(:keys)
|
8
|
+
@selectors = symbol_or_hash
|
9
|
+
else
|
10
|
+
@selectors[symbol_or_hash]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(string)
|
15
|
+
@string = string
|
16
|
+
process
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def selectors_for(*args)
|
22
|
+
self.class.selectors_for(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def process
|
26
|
+
raise "Not implemented"
|
27
|
+
end
|
28
|
+
|
29
|
+
def html_for(string,selector)
|
30
|
+
selected = string.css(selector)
|
31
|
+
if block_given?
|
32
|
+
selected.each do |item|
|
33
|
+
yield(item)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
selected.inner_html
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_document(string)
|
41
|
+
Nokogiri::HTML::Document.parse(string)
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_fragment(string)
|
45
|
+
Nokogiri::HTML::DocumentFragment.parse(string)
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_string(string)
|
49
|
+
Nokogiri::XML.parse(string)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module GoogleText
|
2
|
+
module Parsers
|
3
|
+
class Dashboard < Base
|
4
|
+
attr_accessor :session_id
|
5
|
+
selectors_for :session_id => 'form#gc-search-form'
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def process
|
10
|
+
section = html_for parse_document(@string), selectors_for(:session_id)
|
11
|
+
@session_id = section.match(/value="(.+)"/)[1]
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module GoogleText
|
2
|
+
module Parsers
|
3
|
+
class Inbox < Base
|
4
|
+
|
5
|
+
attr_accessor :messages
|
6
|
+
|
7
|
+
selectors_for :message_row => 'div.gc-message-sms-row',
|
8
|
+
:message_from => 'span.gc-message-sms-from',
|
9
|
+
:message_time => 'span.gc-message-sms-time',
|
10
|
+
:message_text => 'span.gc-message-sms-text'
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def process
|
15
|
+
@messages = []
|
16
|
+
document = parse_string string
|
17
|
+
html_fragment = parse_fragment document.to_html
|
18
|
+
inbox = json_inbox(document)
|
19
|
+
@messages = parse_messages(inbox['messages'], html_fragment)
|
20
|
+
end
|
21
|
+
|
22
|
+
def json_inbox(document)
|
23
|
+
string = document.css('json').first.to_s.scan(/CDATA\[(.+)\]\]/).flatten
|
24
|
+
JSON.parse(string.first)
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_messages(json_messages, page_fragment)
|
28
|
+
build_messages(json_messages)
|
29
|
+
read_message_details(page_fragment)
|
30
|
+
return @messages
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_messages(json_messages)
|
34
|
+
json_messages.each do |json_message|
|
35
|
+
if json_message[1]['type'].to_i == 2
|
36
|
+
next
|
37
|
+
else
|
38
|
+
@messages << parse_message(json_message)
|
39
|
+
end
|
40
|
+
@messages.sort_by {|message| message.start_time}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_message_details(page_fragment)
|
45
|
+
@messages.each do |message|
|
46
|
+
html_for page_fragment, selectors_for(:message_row) do |row|
|
47
|
+
if html_for(row,selectors_for(:message_from)).strip! =~ /Me:/
|
48
|
+
next
|
49
|
+
elsif html_for(row, selectors_for(:message_time)) =~ Regexp.new(message.display_start_time)
|
50
|
+
message.to = 'Me'
|
51
|
+
message.from = html_for(row, selectors_for(:message_from)).strip!.gsub!(':', '')
|
52
|
+
message.text = html_for row, selectors_for(:message_text)
|
53
|
+
else
|
54
|
+
next
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_message(json_array)
|
61
|
+
message = Message.new
|
62
|
+
message.id = json_array[0]
|
63
|
+
message.start_time = json_array[1]['startTime'].to_i
|
64
|
+
message.read_status = json_array[1]['isRead']
|
65
|
+
message.display_start_time = json_array[1]['displayStartTime']
|
66
|
+
message.relative_start_time = json_array[1]['relativeStartTime']
|
67
|
+
message.display_number = json_array[1]['displayNumber']
|
68
|
+
message.display_start_date_time = json_array[1]['displayStartDateTime']
|
69
|
+
message.labels = json_array[1]['labels']
|
70
|
+
return message
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module GoogleText
|
2
|
+
module Parsers
|
3
|
+
class Login < Base
|
4
|
+
attr_accessor :xsrf_token
|
5
|
+
selectors_for :xsrf_token => 'form#gaia_loginform input'
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def process
|
10
|
+
@xsrf_token = nil
|
11
|
+
html_for parse_fragment(@string), selectors_for(:xsrf_token) do |input|
|
12
|
+
if input.to_s =~ /GALX/
|
13
|
+
@xsrf_token = input.to_s.scan(/value\="(.+?)"/).flatten.pop
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/google_text/version.rb
CHANGED
data/lib/google_text.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
|
1
4
|
require 'curb'
|
2
5
|
require 'nokogiri'
|
3
6
|
require 'json'
|
7
|
+
require "google_text/version"
|
4
8
|
require 'google_text/configuration'
|
5
|
-
require 'google_text/parser'
|
6
9
|
require 'google_text/session'
|
7
10
|
require 'google_text/message'
|
8
11
|
require 'google_text/account'
|
9
|
-
require 'google_text/client'
|
12
|
+
require 'google_text/client'
|
13
|
+
require 'google_text/parsers/base'
|
14
|
+
require 'google_text/parsers/inbox'
|
15
|
+
require 'google_text/parsers/dashboard'
|
16
|
+
require 'google_text/parsers/login'
|