ubr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []