ubr 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eb83bfdf9be082662696a13a54aec46d7ca3fa14
4
+ data.tar.gz: 77f3a41027c8c43c543a8ab1fd2d12e1cef8bffa
5
+ SHA512:
6
+ metadata.gz: c326c8c5a5bc3eb4c3fc13a8751e6f6af3f25775fd74ea36df001ce81f1b8bb88aaaa39f2fecf4c04f413eb6939f32ab61425cf440a1010a374845cb6c402446
7
+ data.tar.gz: ecff0e1da0bfee17aec5586c024a7d15cfa9bb23b8f85b6056fbd33ce1a33c0b5e560892c81f53fa36d1c43b2be59e03badc39439cfa79c20710bed82b2ad930
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ubr.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Thai Pangsakulyanont
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,95 @@
1
+ # Ubr
2
+
3
+ A command-line Uber client. Request an Uber from your command line.
4
+
5
+ ## Synopsis and Example
6
+
7
+ ```
8
+ $ from_central_embassy=13.743704,100.546668
9
+ $ to_the_emquatier=13.730656,100.569770
10
+ $ ubr $from_central_embassy $to_the_emquatier
11
+ Searching for available Ubers...
12
+ Selected: uberX
13
+ Getting the ETA...
14
+ Pickup in 4 minutes.
15
+ Estimation:
16
+ - Distance: 2.82 miles
17
+ - Pickup 09:59
18
+ - Dropoff 10:10
19
+ - Price: ฿52-61 (no surge)
20
+ Do you want to request an uberX? (y to confirm): y
21
+ Done! Please check your phone.
22
+ ```
23
+
24
+ ## Disclaimer
25
+
26
+ This app will request an Uber on your behalf, and
27
+ you will pay for any ride initiated by this application.
28
+
29
+ While I strive to make this tool as stable and bug-free as possible,
30
+ I could not make any guarantee that it does not contain a bug that may cause unintended requests to be sent, which may lead to monetary loss.
31
+ Therefore, please this tool at your own risk.
32
+
33
+ ## Installation
34
+
35
+ ```
36
+ gem install ubr
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ To keep the Uber system safe, only an application's developer will be able to
42
+ request a ride until the app is approved by Uber.
43
+ My original `ubr` application has not been approved yet.
44
+ Therefore, you need to [create your own application](https://developer.uber.com/apps).
45
+
46
+ Set these information:
47
+
48
+ - __Callback URL:__ https://dtinth.github.io/ubr/callback.html
49
+ - __Privacy Policy URL:__ https://github.com/dtinth/ubr#privacy-policy
50
+
51
+ As of writing, it is OK to keep the webhook URLs blank.
52
+
53
+ After registering, you should get a __client ID__ and __secret__.
54
+ To login, run this command:
55
+
56
+ ```
57
+ ubr login xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
58
+ ```
59
+
60
+ It should open a browser and let you log in to the application.
61
+ Upon completion, it should redirect you to a page where it says:
62
+
63
+ ```
64
+ ubr authorize 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 'https://dtinth.github.io/ubr/callback.html'
65
+ ```
66
+
67
+ Copy and paste that into the terminal.
68
+ It will ask you to provide the secret key.
69
+ Paste in the secret key and press enter.
70
+
71
+ ```
72
+ Give me your secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
73
+
74
+ Hurray! Your are authorized!
75
+ The token is saved to /Users/username/.ubr
76
+ ```
77
+
78
+ Now, you can request an Uber by running this command:
79
+
80
+ ```
81
+ ubr PICKUP DROPOFF
82
+ ```
83
+
84
+ Where __PICKUP__ and __DROPOFF__ are the coordinates to pick-up and drop-off,
85
+ specified in `LATITUDE,LONGITUDE` format.
86
+ It is useful to put these coordinates as shell variables.
87
+
88
+
89
+ ## Privacy Policy
90
+
91
+ This privacy policy applies only to the authentic copy of `ubr` application.
92
+ We do not keep your personal information.
93
+ Only your `client_id` and the `authorization_code` is sent to GitHub Pages server for the purpose of making logins easier.
94
+ These two pieces of information are then combined with the `secret` key, which you supply in your own computer, to exchange for the `access_token`.
95
+ The `access_token` is then kept on your computer.
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/ubr ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ubr/app'
4
+ require 'ubr/cli'
5
+
6
+ options = Ubr::CLI.parse_options!
7
+
8
+ Ubr::App.run!(options)
@@ -0,0 +1 @@
1
+ require "ubr/version"
@@ -0,0 +1,42 @@
1
+
2
+ require 'rest-client'
3
+ require 'json'
4
+
5
+ module Ubr
6
+ class API
7
+ def initialize
8
+ token = File.read(API.token_path).strip
9
+ @client = RestClient::Resource.new('https://api.uber.com/v1',
10
+ headers: {
11
+ authorization: "Bearer #{token}",
12
+ content_type: 'application/json'
13
+ }
14
+ )
15
+ end
16
+ def get(path, params={})
17
+ parse_response @client[path].get(params: params)
18
+ end
19
+ def post(path, params={})
20
+ parse_response @client[path].post(params.to_json)
21
+ end
22
+ def parse_response(response)
23
+ API.parse_response(response)
24
+ end
25
+ def self.parse_response(response)
26
+ JSON.parse response, symbolize_names: true
27
+ end
28
+ def self.authorize(client_id:, code:, secret:, redirect_uri:)
29
+ client = RestClient::Resource.new('https://login.uber.com/oauth/token')
30
+ parse_response client.post(
31
+ client_secret: secret,
32
+ client_id: client_id,
33
+ grant_type: 'authorization_code',
34
+ redirect_uri: redirect_uri,
35
+ code: code,
36
+ )
37
+ end
38
+ def self.token_path
39
+ File.expand_path('~/.ubr')
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,137 @@
1
+
2
+ require 'ubr/api'
3
+ require 'active_support/all'
4
+
5
+ module Ubr
6
+ class App
7
+
8
+ def self.run!(options)
9
+ new(options).run!
10
+ end
11
+
12
+ def initialize(options)
13
+ @options = options
14
+ @client = API.new
15
+ end
16
+
17
+ def run!
18
+
19
+ print 'Searching for available Ubers...'
20
+ products = get_available_products
21
+ product = pick_or_choose(products,
22
+ title: 'Available Ubers',
23
+ pick: method(:product_preselected?),
24
+ display: method(:product_description),
25
+ )
26
+
27
+ estimate = nil
28
+
29
+ loop do
30
+ print 'Getting the ETA...'
31
+ estimate = get_estimate_for(product: product)
32
+ puts
33
+ print_estimate estimate
34
+ break if confirm? "Do you want to request an #{product[:display_name]}?"
35
+ print_separator
36
+ end
37
+
38
+ surge_confirmation_id = nil
39
+ if estimate[:surge_confirmation_id] and confirm? "Do you accept the surge?"
40
+ surge_confirmation_id = estimate[:surge_confirmation_id]
41
+ end
42
+
43
+ request_uber!(product: product, surge_confirmation_id: surge_confirmation_id)
44
+ puts "Done! Please check your phone."
45
+
46
+ end
47
+
48
+ def get_available_products
49
+ @client.get('/products', @options[:pickup].to_h)[:products]
50
+ end
51
+
52
+ def print_estimate(estimate)
53
+ if estimate[:pickup_estimate]
54
+ puts "Pickup in #{estimate[:pickup_estimate]} minutes."
55
+ puts "Estimation:"
56
+
57
+ distance_estimate = estimate[:trip][:distance_estimate]
58
+ distance_unit = estimate[:trip][:distance_unit].pluralize(distance_estimate)
59
+ puts "- Distance: #{distance_estimate} #{distance_unit}"
60
+
61
+ pickup_time = estimate[:pickup_estimate].minutes.from_now
62
+ puts "- Pickup #{pickup_time.to_s :time}"
63
+
64
+ dropoff_time = pickup_time + estimate[:trip][:duration_estimate].seconds
65
+ puts "- Dropoff #{dropoff_time.to_s :time}"
66
+
67
+ surge_x = if estimate[:price][:surge_multiplier] == 1.0
68
+ 'no'
69
+ else
70
+ "#{estimate[:price][:surge_multiplier]}"
71
+ end
72
+ puts "- Price: #{estimate[:price][:display]} (#{surge_x} surge)"
73
+ else
74
+ puts "None available."
75
+ end
76
+ end
77
+
78
+ def get_estimate_for(product:)
79
+ @client.post('requests/estimate',
80
+ product_id: product[:product_id],
81
+ start_latitude: @options[:pickup].latitude,
82
+ start_longitude: @options[:pickup].longitude,
83
+ end_latitude: @options[:dropoff].latitude,
84
+ end_longitude: @options[:dropoff].longitude,
85
+ )
86
+ end
87
+
88
+ def request_uber!(product:, surge_confirmation_id:)
89
+ @client.post('requests',
90
+ product_id: product[:product_id],
91
+ start_latitude: @options[:pickup].latitude,
92
+ start_longitude: @options[:pickup].longitude,
93
+ end_latitude: @options[:dropoff].latitude,
94
+ end_longitude: @options[:dropoff].longitude,
95
+ surge_confirmation_id: surge_confirmation_id,
96
+ )
97
+ end
98
+
99
+ private
100
+
101
+ def product_preselected?(product)
102
+ @options[:product] and product[:display_name].casecmp(@options[:product]).zero?
103
+ end
104
+
105
+ def product_description(product)
106
+ "#{product[:display_name]}"
107
+ end
108
+
109
+ def pick_or_choose(items, pick:, display:, title:)
110
+ puts
111
+ begin
112
+ items.find(&pick) or begin
113
+ puts "#{title}:"
114
+ items.each_with_index do |item, index|
115
+ puts "- #{index + 1}. #{display[item]}"
116
+ end
117
+ puts
118
+ print "Your choice: "
119
+ items[$stdin.gets.to_i - 1] or raise "Invalid item."
120
+ end
121
+ end.tap do |item|
122
+ puts "Selected: #{display[item]}"
123
+ end
124
+ end
125
+
126
+ def print_separator
127
+ puts "=" * 64
128
+ end
129
+
130
+ def confirm?(message)
131
+ print message
132
+ print " (y to confirm): "
133
+ $stdin.gets.strip.casecmp('y').zero?
134
+ end
135
+
136
+ end
137
+ end
@@ -0,0 +1,85 @@
1
+
2
+ require 'optparse'
3
+ require 'launchy'
4
+ require 'ubr/coordinate'
5
+ require 'ubr/api'
6
+
7
+ module Ubr
8
+ class CLI
9
+ def self.parse_options!
10
+
11
+ options = {}
12
+ argv = ARGV.dup
13
+
14
+ OptionParser.new do |opts|
15
+
16
+ opts.banner = 'Usage: ubr <pickup> <dropoff> [options]'
17
+ opts.separator ''
18
+ opts.separator 'ubr: Request an Uber from the command line.'
19
+ opts.separator ''
20
+ opts.separator 'Options:'
21
+
22
+ opts.on('-h', '--help', 'Displays this help') do
23
+ puts opts
24
+ exit
25
+ end
26
+
27
+ opts.on('-p', '--product NAME', 'Automatically selects a product') do |product|
28
+ options[:product] = product
29
+ end
30
+
31
+ end.parse!(argv)
32
+
33
+ case argv[0]
34
+ when 'login'
35
+ client_id = argv[1] or raise "Client ID needed"
36
+ login!(client_id)
37
+ exit
38
+ when 'authorize'
39
+ client_id = argv[1] or raise "Client ID needed"
40
+ code = argv[2] or raise "Code needed"
41
+ redirect_uri = argv[3] or raise "Redirect URI needed"
42
+ authorize!(client_id, code, redirect_uri)
43
+ exit
44
+ end
45
+
46
+ options[:pickup] = Coordinate.parse(argv.shift) or raise "Pickup location needed!"
47
+ options[:dropoff] = Coordinate.parse(argv.shift) or raise "Dropoff location needed!"
48
+
49
+ raise "Unrecognized args: #{argv.inspect}" unless argv.empty?
50
+
51
+ options
52
+
53
+ end
54
+
55
+ def self.login!(client_id)
56
+ uri = 'https://login.uber.com/oauth/authorize?' + [
57
+ 'response_type=code',
58
+ 'client_id=' + client_id,
59
+ 'state=' + client_id,
60
+ 'scope=request',
61
+ ].join('&')
62
+ puts "Opening login URL: #{uri}"
63
+ Launchy.open(uri)
64
+ end
65
+
66
+ def self.authorize!(client_id, code, redirect_uri, secret=nil)
67
+ unless secret
68
+ print "Give me your secret: "
69
+ secret = $stdin.gets.strip
70
+ end
71
+ result = API.authorize(client_id: client_id, code: code, secret: secret, redirect_uri: redirect_uri)
72
+ token = result[:access_token]
73
+ if token
74
+ filename = API.token_path
75
+ File.write(filename, token)
76
+ File.chmod(0600, filename)
77
+ puts
78
+ puts "Hurray! Your are authorized!"
79
+ puts "The token is saved to #{filename}"
80
+ else
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module Ubr
3
+
4
+ Coordinate = Struct.new(:latitude, :longitude)
5
+
6
+ class << Coordinate
7
+ def parse(text)
8
+ if text =~ /\A(\-?[\d\.]+),(\-?[\d\.]+)\Z/
9
+ Coordinate.new($1.to_f, $2.to_f)
10
+ else
11
+ raise "Coordinate must be in format 'latitude,longitude'; #{text.inspect} given"
12
+ end
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,3 @@
1
+ module Ubr
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ubr/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ubr"
8
+ spec.version = Ubr::VERSION
9
+ spec.authors = ["Thai Pangsakulyanont"]
10
+ spec.email = ["dtinth@spacet.me"]
11
+ spec.summary = %q{Request an Uber from the CLI}
12
+ spec.description = %q{ubr is a command line script that utilizes the Uber API to request a ride for the user.}
13
+ spec.homepage = "https://github.com/dtinth/ubr"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "rest-client"
22
+ spec.add_runtime_dependency "activesupport"
23
+ spec.add_runtime_dependency "launchy"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ubr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Thai Pangsakulyanont
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: launchy
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ description: ubr is a command line script that utilizes the Uber API to request a
84
+ ride for the user.
85
+ email:
86
+ - dtinth@spacet.me
87
+ executables:
88
+ - ubr
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/ubr
98
+ - lib/ubr.rb
99
+ - lib/ubr/api.rb
100
+ - lib/ubr/app.rb
101
+ - lib/ubr/cli.rb
102
+ - lib/ubr/coordinate.rb
103
+ - lib/ubr/version.rb
104
+ - ubr.gemspec
105
+ homepage: https://github.com/dtinth/ubr
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.2.2
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Request an Uber from the CLI
129
+ test_files: []