teecket 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3479ec9a0e55ad444516dfb35ab55f332e2bdd46
4
- data.tar.gz: fa4da71811908b15c8f007c7372ad9487dc06a26
3
+ metadata.gz: cc28311468f921a143f0d0075e3376e1d189734d
4
+ data.tar.gz: 6bdee2c7f682a6af7530fd8d9f85b6f9a382ba0e
5
5
  SHA512:
6
- metadata.gz: 1a44d6f4ec825a4d4f2baf8a26e6d865033850256138feb49d48c7a76847372ab17fa966898dc77cd2b760cc881e38851fc7fcd6c2f5074cbd87e4199a027527
7
- data.tar.gz: 6c27263ecc36e673abeeb44c871ad7c2659c8c2db03f2692c485b613b7b45263a77cbfa1f93a43e4a419c01d660f94474b7b04d8a0468cb1cb556412ae9f688a
6
+ metadata.gz: 11324f1c829189ff9d6ef4527c82dd0de7cb619f70e66f7a3813676c8d0c051953ae24b07b17d31dbc24fb6298cef62540dbb97442f09bf06f0881d0d8a77722
7
+ data.tar.gz: 0c2eb3a6a541d58a4bcd4a210e806df2ac6a50277442aefa3d37717609e7375ca7a1e0def89f29255451636a15ecd8a09187f3bc93d144da41a706b9cd3e1820
data/README.md CHANGED
@@ -3,6 +3,9 @@
3
3
  Teecket is a gem that will search from AirAsia, Firefly, Malaysia Airlines and
4
4
  Malindo Air from the comfort of your command line.
5
5
 
6
+ [![Code
7
+ Climate](https://codeclimate.com/github/amree/teecket/badges/gpa.svg)](https://codeclimate.com/github/amree/teecket)
8
+
6
9
  ## Installation
7
10
 
8
11
  At the command prompt, install it using `gem`:
data/bin/teecket CHANGED
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'teecket'
4
- require 'teecket/printer'
3
+ require "teecket"
4
+ require "teecket/printer"
5
5
 
6
6
  if ARGV.count == 3
7
- output = Teecket.search({ from: ARGV[0], to: ARGV[1], date: ARGV[2]})
7
+ output = Teecket.search(from: ARGV[0], to: ARGV[1], date: ARGV[2])
8
8
  puts Printer.table(output)
9
9
  else
10
- puts 'Example: teecket KBR KUL 30-07-2015'
10
+ puts "Example: teecket KBR KUL 30-07-2015"
11
11
  end
12
-
data/lib/teecket.rb CHANGED
@@ -1,25 +1,44 @@
1
- require 'date'
1
+ require "date"
2
+ require "thwait"
2
3
 
3
- require 'teecket/flight'
4
- require 'teecket/air_asia'
5
- require 'teecket/malaysia_airlines'
6
- require 'teecket/malindo_air'
7
- require 'teecket/firefly'
4
+ require "teecket/page_requester"
5
+ require "teecket/selectors/air_asia"
6
+ require "teecket/selectors/malaysia_airlines"
7
+ require "teecket/selectors/malindo_air"
8
+ require "teecket/selectors/firefly"
9
+ require "teecket/flight"
10
+ require "teecket/air_asia"
11
+ require "teecket/malaysia_airlines"
12
+ require "teecket/malindo_air"
13
+ require "teecket/firefly"
8
14
 
9
15
  class Teecket
10
16
  def self.search(params)
11
- airasia = AirAsia.new({ from: params[:from], to: params[:to], date: params[:date] })
12
- airasia.get
17
+ flights = [
18
+ "AirAsia",
19
+ "Firefly",
20
+ "MalaysiaAirlines",
21
+ "MalindoAir"
22
+ ]
13
23
 
14
- firefly = Firefly.new({ from: params[:from], to: params[:to], date: params[:date] })
15
- firefly.get
24
+ results = []
25
+ threads = []
26
+ flights.each do |flight|
27
+ threads << Thread.new do
28
+ klass = Object.const_get(flight)
16
29
 
17
- mas = MalaysiaAirlines.new({ from: params[:from], to: params[:to], date: params[:date] })
18
- mas.get
30
+ scrapper = klass.new(from: params[:from],
31
+ to: params[:to],
32
+ date: params[:date])
19
33
 
20
- malindo = MalindoAir.new({ from: params[:from], to: params[:to], date: params[:date] })
21
- malindo.get
34
+ scrapper.search
22
35
 
23
- airasia.fares + mas.fares + malindo.fares + firefly.fares
36
+ results = results + scrapper.fares
37
+ end
38
+ end
39
+
40
+ ThreadsWait.all_waits(*threads)
41
+
42
+ results
24
43
  end
25
44
  end
@@ -1,45 +1,65 @@
1
1
  class AirAsia < Flight
2
- def get
3
- uri = URI('https://argon.airasia.com/api/7.0/search')
2
+ include PageRequester
3
+ include Selectors::AirAsia
4
+
5
+ attr_accessor :res
6
+
7
+ def search
8
+ get
9
+ process
10
+ rescue StandardError
11
+ end
4
12
 
5
- http = Net::HTTP.new(uri.host, uri.port)
6
- http.use_ssl = true
7
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
13
+ private
14
+
15
+ def get
16
+ uri = URI("https://argon.airasia.com/api/7.0/search")
8
17
 
9
18
  req = Net::HTTP::Post.new(uri.path)
10
- req.body = "type=classic&origin=#{from}&destination=#{to}&depart=#{date}&return=&passenger-count=1&child-count=0&infant-count=0&currency=MYR&days=1"
11
-
12
- res = http.request(req)
13
-
14
- result = JSON.parse(res.body)
15
-
16
- if result['session-id']
17
- begin
18
- result['depart'][date]['details']['low-fare'].each do |rs|
19
- depart_at = DateTime.parse(rs['segments'][0]['departure-datetime'])
20
- arrive_at = DateTime.parse(rs['segments'][0]['arrival-datetime'])
21
- fare = rs['total']['adult']
22
- flight_number = rs['segments'][0]['flight-number']
23
- origin = rs['segments'][0]['origincode']
24
- destination = rs['segments'][0]['destinationcode']
25
- transit = 'NO'
26
-
27
- depart_at = depart_at.strftime('%I:%M %p')
28
- arrive_at = arrive_at.strftime('%I:%M %p')
29
- fare = sprintf("%.2f", fare)
30
- flight_number = flight_number.gsub(/ /, '')
31
-
32
- fares << { flight_name: 'AirAsia',
33
- flight_number: flight_number,
34
- transit: transit,
35
- origin: origin,
36
- destination: destination,
37
- depart_at: depart_at,
38
- arrive_at: arrive_at,
39
- fare: fare }
40
- end
41
- rescue StandardError
42
- end
19
+ req.body = URI.encode_www_form([
20
+ ["type", "classic"],
21
+ ["origin", from],
22
+ ["destination", to],
23
+ ["depart", date],
24
+ ["return", ""],
25
+ ["passenger-count", 1],
26
+ ["child-count", 0],
27
+ ["infant-count", 0],
28
+ ["currency", "MYR"],
29
+ ["days", 1]
30
+ ])
31
+
32
+ self.res = request(uri, req)
33
+ end
34
+
35
+ def process
36
+ json = JSON.parse(res.body)
37
+
38
+ flights(json).each do |flight|
39
+ depart_at = depart_at_selector(flight)
40
+ arrive_at = arrive_at_selector(flight)
41
+ fare = fare_selector(flight)
42
+ flight_number = flight_number_selector(flight)
43
+ origin = origin_selector(flight)
44
+ destination = destination_selector(flight)
45
+ transit = "NO"
46
+
47
+ add_to_fares(flight_name: "AirAsia",
48
+ flight_number: flight_number,
49
+ transit: transit,
50
+ origin: origin,
51
+ destination: destination,
52
+ depart_at: depart_at,
53
+ arrive_at: arrive_at,
54
+ fare: fare)
55
+ end
56
+ end
57
+
58
+ def flights(result)
59
+ if result["session-id"]
60
+ result["depart"][date]["details"]["low-fare"]
61
+ else
62
+ []
43
63
  end
44
64
  end
45
65
  end
@@ -1,77 +1,86 @@
1
- require 'nokogiri'
1
+ require "nokogiri"
2
2
 
3
3
  class Firefly < Flight
4
- def get
5
-
6
- uri = URI('https://m.fireflyz.com.my/')
7
-
8
- http = Net::HTTP.new(uri.host, uri.port)
9
- http.use_ssl = true
10
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
11
-
12
- req = Net::HTTP::Get.new(uri.path)
4
+ include PageRequester
5
+ include Selectors::Firefly
13
6
 
14
- res = http.request(req)
7
+ attr_accessor :res, :cookie
15
8
 
16
- cookie = res['Set-Cookie']
17
-
18
- uri = URI('https://m.fireflyz.com.my/Search')
19
-
20
- http = Net::HTTP.new(uri.host, uri.port)
21
- http.use_ssl = true
22
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
23
-
24
- req = Net::HTTP::Get.new(uri.path, initheader = { 'Cookie' => cookie})
9
+ def search
10
+ get
11
+ process
12
+ rescue StandardError
13
+ end
25
14
 
26
- cookie = res['Set-Cookie']
15
+ private
27
16
 
28
- uri = URI('https://m.fireflyz.com.my/Search')
17
+ def get
18
+ get_main_page
19
+ get_search_page
20
+ get_result_page
21
+ end
29
22
 
30
- http = Net::HTTP.new(uri.host, uri.port)
31
- http.use_ssl = true
32
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
23
+ def get_main_page
24
+ uri = URI("https://m.fireflyz.com.my/")
33
25
 
34
- req = Net::HTTP::Post.new(uri.path, initheader = { 'Cookie' => cookie})
35
- req.body = "type=2&departure_station=#{from}&arrival_station=#{to}&departure_date=#{date}&return_date=30%2F06%2F2015&adult=1&infant=0"
26
+ req = Net::HTTP::Get.new(uri.path)
36
27
 
37
- res = http.request(req)
28
+ self.res = request(uri, req)
38
29
 
39
- if res['location']
40
- uri = URI(res['location'])
30
+ self.cookie = res["Set-Cookie"]
31
+ end
41
32
 
42
- http = Net::HTTP.new(uri.host, uri.port)
43
- http.use_ssl = true
44
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
33
+ def get_search_page
34
+ uri = URI("https://m.fireflyz.com.my/Search")
35
+
36
+ req = Net::HTTP::Post.new(uri.path, "Cookie" => cookie)
37
+ req.body = URI.encode_www_form([
38
+ ["type", 2],
39
+ ["return_date", date],
40
+ ["adult", 1],
41
+ ["infant", 0],
42
+ ["departure_station", from],
43
+ ["arrival_station", to],
44
+ ["departure_date", date]
45
+ ])
46
+
47
+ self.res = request(uri, req)
48
+ end
45
49
 
46
- req = Net::HTTP::Get.new(uri.path, initheader = { 'Cookie' => cookie})
50
+ def get_result_page
51
+ if res["location"]
52
+ uri = URI(res["location"])
47
53
 
48
- res = http.request(req)
54
+ req = Net::HTTP::Get.new(uri.path, "Cookie" => cookie)
49
55
 
50
- doc = Nokogiri::HTML(res.body)
56
+ self.res = request(uri, req)
57
+ end
58
+ end
51
59
 
52
- doc.css('div.market1').each_with_index do |elem, i|
53
- depart_at = doc.css('div.market1')[i].css('div.visible-xs').css('table')[1].css('td')[0].text.strip
54
- arrive_at = doc.css('div.market1')[i].css('div.visible-xs').css('table')[1].css('td')[1].text.strip
55
- fare = doc.css('div.market1')[i].css('div.visible-xs > div').text.strip
56
- flight_number = doc.css('div.market1')[i].css('div.visible-xs').css('table')[0].text.strip
57
- origin = doc.css('div.market1')[i]['onclick'].scan(/~[A-Z]{3}~/)[0].gsub('~', '')
58
- destination = doc.css('div.market1')[i]['onclick'].scan(/~[A-Z]{3}~/)[1].gsub('~', '')
59
- transit = 'NO'
60
+ def process
61
+ html = Nokogiri::HTML(res.body)
60
62
 
61
- depart_at = DateTime.parse("#{date} #{depart_at.gsub(/\t/, '').match(/^(.*?)(AM|PM)/).to_s}").strftime('%I:%M %p')
62
- arrive_at = DateTime.parse("#{date} #{arrive_at.gsub(/\t/, '').match(/^(.*?)(AM|PM)/).to_s}").strftime('%I:%M %p')
63
- fare = fare.gsub(/ MYR/, '')
64
- flight_number = flight_number.gsub(/ /, '').gsub(/FLIGHTNO\./, '')
63
+ flights(html).each do |flight|
64
+ depart_at = depart_at_selector(flight)
65
+ arrive_at = arrive_at_selector(flight)
66
+ fare = fare_selector(flight)
67
+ flight_number = flight_number_selector(flight)
68
+ origin = origin_selector(flight)
69
+ destination = destination_selector(flight)
70
+ transit = "NO"
65
71
 
66
- fares << { flight_name: 'Firefly',
72
+ add_to_fares(flight_name: "Firefly",
67
73
  flight_number: flight_number,
68
74
  transit: transit,
69
75
  origin: origin,
70
76
  destination: destination,
71
77
  depart_at: depart_at,
72
78
  arrive_at: arrive_at,
73
- fare: fare }
74
- end
79
+ fare: fare)
75
80
  end
76
81
  end
82
+
83
+ def flights(data)
84
+ data.css("div.market1")
85
+ end
77
86
  end
@@ -1,15 +1,32 @@
1
- require 'net/http'
2
- require 'openssl'
3
- require 'json'
1
+ require "net/http"
2
+ require "openssl"
3
+ require "json"
4
4
 
5
5
  class Flight
6
- attr_reader :from, :to, :date, :fares
6
+ attr_accessor :from, :to, :date, :fares
7
7
 
8
8
  def initialize(params)
9
- @from = params[:from]
10
- @to = params[:to]
11
- @date = params[:date]
9
+ @from = params[:from]
10
+ @to = params[:to]
11
+ @date = params[:date]
12
12
 
13
13
  @fares = []
14
14
  end
15
+
16
+ def search
17
+ raise NotImplementedError
18
+ end
19
+
20
+ private
21
+
22
+ def add_to_fares(params)
23
+ fares << { flight_name: params[:flight_name],
24
+ flight_number: params[:flight_number],
25
+ transit: params[:transit],
26
+ origin: params[:origin],
27
+ destination: params[:destination],
28
+ depart_at: params[:depart_at],
29
+ arrive_at: params[:arrive_at],
30
+ fare: params[:fare] }
31
+ end
15
32
  end
@@ -1,53 +1,92 @@
1
1
  class MalaysiaAirlines < Flight
2
+ include PageRequester
3
+ include Selectors::MalaysiaAirlines
4
+
5
+ attr_accessor :res
6
+
7
+ def search
8
+ get
9
+ process
10
+ rescue StandardError
11
+ end
12
+
13
+ private
14
+
2
15
  def get
3
16
  new_date = DateTime.parse(date)
4
- new_date = new_date.strftime('%Y-%m-%d')
5
-
6
- uri = URI("https://flymh.mobi/TravelAPI/travelapi/shop/1/mh/#{from}/#{to}/1/0/0/Economy/#{new_date}/")
7
-
8
- http = Net::HTTP.new(uri.host, uri.port)
9
- http.use_ssl = true
10
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
11
-
12
- req = Net::HTTP::Get.new(uri.path, initHeader = { 'X-apiKey' => '52e6d6d613d3a3e825ac02253fe6b5a4'})
13
-
14
- res = http.request(req)
15
- res.body.gsub!(/^fn\(/, '')
16
- res.body.gsub!(/\)/, '')
17
-
18
- result = JSON.parse(res.body)
19
-
20
- if result['success']
21
- result['outboundOptions'].each do |rs|
22
- fare = rs['fareDetails']['totalTripFare']
23
- origin = rs['flights'][0]['departureAirport']['code']
24
- depart_at = rs['flights'][0]['depScheduled']
25
-
26
- if rs['flights'].count > 1
27
- transit = 'YES'
28
- arrive_at = rs['flights'][rs['flights'].count - 1]['arrScheduled']
29
- flight_number = rs['flights'].map { |arr| arr['operatingAirline'] + arr['flightNumber'] }.join(' + ')
30
- destination = rs['flights'][rs['flights'].count - 1]['arrivalAirport']['code']
31
- else
32
- transit = 'NO'
33
- arrive_at = rs['flights'][0]['arrScheduled']
34
- flight_number = rs['flights'][0]['marketingAirline'] + rs['flights'][0]['flightNumber']
35
- destination = rs['flights'][0]['arrivalAirport']['code']
36
- end
37
-
38
- depart_at = DateTime.parse(depart_at).strftime('%I:%M %p')
39
- arrive_at = DateTime.parse(arrive_at).strftime('%I:%M %p')
40
- fare = sprintf("%.2f", fare)
41
-
42
- fares << { flight_name: 'Malaysia Airlines',
43
- flight_number: flight_number,
44
- transit: transit,
45
- origin: origin,
46
- destination: destination,
47
- depart_at: depart_at,
48
- arrive_at: arrive_at,
49
- fare: fare }
50
- end
17
+ new_date = new_date.strftime("%Y-%m-%d")
18
+
19
+ key = "52e6d6d613d3a3e825ac02253fe6b5a4"
20
+ url = "https://flymh.mobi/TravelAPI/travelapi/shop/1/mh/" <<
21
+ "#{from}/#{to}/1/0/0/Economy/#{new_date}/"
22
+
23
+ uri = URI(url)
24
+
25
+ req = Net::HTTP::Get.new(uri.path, "X-apiKey" => key)
26
+
27
+ self.res = request(uri, req)
28
+ end
29
+
30
+ def process
31
+ json = JSON.parse res.body.gsub(/^fn\(|\)/, "")
32
+
33
+ flights(json).each do |flight|
34
+ params = if flight["flights"].count > 1
35
+ process_for_transit(flight)
36
+ else
37
+ process_for_non_transit(flight)
38
+ end
39
+
40
+ process_for_all(flight, params)
51
41
  end
52
42
  end
43
+
44
+ def process_for_transit(flight)
45
+ trips = flight["flights"]
46
+
47
+ transit = "YES"
48
+ arrive_at = arrive_at_selector(trips[trips.count - 1])
49
+ flight_number = flight_number_selector(trips, true)
50
+ destination = destination_selector(trips[trips.count - 1], true)
51
+
52
+ { transit: transit,
53
+ arrive_at: arrive_at,
54
+ flight_number: flight_number,
55
+ destination: destination }
56
+ end
57
+
58
+ def process_for_non_transit(flight)
59
+ trips = flight["flights"]
60
+
61
+ transit = "NO"
62
+ arrive_at = arrive_at_selector(trips[0])
63
+ flight_number = flight_number_selector(trips[0], false)
64
+ destination = destination_selector(trips[0], false)
65
+
66
+ { transit: transit,
67
+ arrive_at: arrive_at,
68
+ flight_number: flight_number,
69
+ destination: destination }
70
+ end
71
+
72
+ def process_for_all(flight, params)
73
+ trips = flight["flights"]
74
+
75
+ fare = fare_selector(flight)
76
+ origin = origin_selector(trips[0])
77
+ depart_at = depart_at_selector(trips[0])
78
+
79
+ add_to_fares(flight_name: "Malaysia Airlines",
80
+ flight_number: params[:flight_number],
81
+ transit: params[:transit],
82
+ origin: origin,
83
+ destination: params[:destination],
84
+ depart_at: depart_at,
85
+ arrive_at: params[:arrive_at],
86
+ fare: fare)
87
+ end
88
+
89
+ def flights(result)
90
+ result["outboundOptions"]
91
+ end
53
92
  end
@@ -1,69 +1,123 @@
1
1
  class MalindoAir < Flight
2
- def get
3
- new_date = DateTime.parse(date)
4
- new_date = new_date.strftime('%Q')
2
+ include PageRequester
3
+ include Selectors::MalindoAir
4
+
5
+ attr_accessor :res
6
+
7
+ def search
8
+ get
9
+ process
10
+ rescue StandardError
11
+ end
12
+
13
+ private
5
14
 
6
- uri = URI('https://mobileapi.malindoair.com/GQWCF_FlightEngine/GQDPMobileBookingService.svc/InitializeGQService')
15
+ def prepare
16
+ self.date = DateTime.parse(date).strftime("%Q")
17
+ self.from = from.upcase
18
+ self.to = to.upcase
19
+ end
7
20
 
8
- http = Net::HTTP.new(uri.host, uri.port)
9
- http.use_ssl = true
10
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
21
+ def get
22
+ get_initialization_page
23
+ get_result_page
24
+ end
11
25
 
12
- req = Net::HTTP::Post.new(uri.path, initheader = { 'Content-Type' => 'application/json' })
13
- req.body = '{"B2BID":"0","UserLoginId":"0","CustomerUserID":91,"Language":"en-GB","isearchType":"15"}'
26
+ def get_initialization_page
27
+ prepare
14
28
 
15
- res = http.request(req)
16
- key = res['wscContext']
29
+ url = "https://mobileapi.malindoair.com/GQWCF_FlightEngine/" <<
30
+ "GQDPMobileBookingService.svc/InitializeGQService"
17
31
 
18
- if key
19
- uri = URI('https://mobileapi.malindoair.com/GQWCF_FlightEngine/GQDPMobileBookingService.svc/SearchAirlineFlights')
32
+ uri = URI(url)
20
33
 
21
- http = Net::HTTP.new(uri.host, uri.port)
22
- http.use_ssl = true
23
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
34
+ req = Net::HTTP::Post.new(uri.path, "Content-Type" => "application/json")
35
+ req.body = '{"B2BID":"0","UserLoginId":"0","CustomerUserID":91,' <<
36
+ '"Language":"en-GB","isearchType":"15"}'
24
37
 
25
- req = Net::HTTP::Post.new(uri.path, initheader = { 'Content-Type' => 'application/json',
26
- 'WscContext' => key })
38
+ self.res = request(uri, req)
39
+ end
27
40
 
28
- payload = "{\"sd\":{\"Adults\":1,\"AirlineCode\":\"\",\"ArrivalCity\":\"#{to}\",\"ArrivalCityName\":null,\"BookingClass\":null,\"CabinClass\":0,\"ChildAge\":[],\"Children\":0,\"CustomerId\":0,\"CustomerType\":0,\"CustomerUserId\":91,\"DepartureCity\":\"#{from}\",\"DepartureCityName\":null,\"DepartureDate\":\"/Date(#{new_date})/\",\"DepartureDateGap\":0,\"DirectFlightsOnly\":false,\"Infants\":0,\"IsPackageUpsell\":false,\"JourneyType\":1,\"ReturnDate\":\"/Date(-2208988800000)/\",\"ReturnDateGap\":0,\"SearchOption\":1},\"fsc\":\"0\"}"
41
+ def get_result_page
42
+ key = res["wscContext"]
29
43
 
30
- req.body = payload
44
+ url = "https://mobileapi.malindoair.com/GQWCF_FlightEngine/" <<
45
+ "GQDPMobileBookingService.svc/SearchAirlineFlights"
31
46
 
32
- res = http.request(req)
47
+ uri = URI(url)
33
48
 
34
- result = JSON.parse(res.body)
49
+ req = Net::HTTP::Post.new(uri.path,
50
+ "Content-Type" => "application/json",
51
+ "WscContext" => key)
35
52
 
36
- if result['SearchAirlineFlightsResult']
37
- result['SearchAirlineFlightsResult'].each do |rs|
53
+ req.body = payload
38
54
 
39
- if rs['SegmentInformation'].count > 1
40
- transit = 'YES'
41
- arrive_at = rs['SegmentInformation'][rs['SegmentInformation'].count - 1]['ArrivalDate']
42
- else
43
- transit = 'NO'
44
- arrive_at = rs['ArrivalDate']
45
- end
55
+ self.res = request(uri, req)
56
+ end
46
57
 
47
- depart_at = rs['DepartureDate']
48
- fare = rs['FlightAmount']
49
- origin = rs['DepCity']
50
- destination = rs['ArrCity']
51
- flight_number = rs['SegmentInformation'].map { |arr| arr['MACode'] + arr['FlightNo'] }.join(' + ')
58
+ def process
59
+ json = JSON.parse(res.body)
52
60
 
53
- depart_at = DateTime.strptime(depart_at.gsub(/^\/Date\(|\)\//, ''), '%Q').strftime('%I:%M %p')
54
- arrive_at = DateTime.strptime(arrive_at.gsub(/^\/Date\(|\)\//, ''), '%Q').strftime('%I:%M %p')
55
- fare = sprintf("%.2f", fare)
61
+ flights(json).each do |flight|
62
+ params = if trips(flight).count > 1
63
+ process_for_transit(flight)
64
+ else
65
+ process_for_non_transit(flight)
66
+ end
56
67
 
57
- fares << { flight_name: 'Malindo Air',
58
- flight_number: flight_number,
59
- transit: transit,
60
- origin: origin,
61
- destination: destination,
62
- depart_at: depart_at,
63
- arrive_at: arrive_at,
64
- fare: fare }
65
- end
66
- end
68
+ process_for_all(flight, params)
67
69
  end
68
70
  end
71
+
72
+ def trips(flight)
73
+ flight["SegmentInformation"]
74
+ end
75
+
76
+ def process_for_transit(flight)
77
+ trips = trips(flight)
78
+ arrive_at = arrive_at_selector(trips[trips.size - 1]["ArrivalDate"])
79
+
80
+ { transit: "YES", arrive_at: arrive_at }
81
+ end
82
+
83
+ def process_for_non_transit(flight)
84
+ arrive_at = arrive_at_selector(flight["ArrivalDate"])
85
+
86
+ { transit: "NO", arrive_at: arrive_at }
87
+ end
88
+
89
+ def process_for_all(flight, params)
90
+ depart_at = depart_at_selector(flight)
91
+ fare = fare_selector(flight)
92
+ origin = origin_selector(flight)
93
+ destination = destination_selector(flight)
94
+ flight_number = flight_number_selector(flight)
95
+
96
+ add_to_fares(flight_name: "Malindo Air",
97
+ flight_number: flight_number,
98
+ transit: params[:transit],
99
+ origin: origin,
100
+ destination: destination,
101
+ depart_at: depart_at,
102
+ arrive_at: params[:arrive_at],
103
+ fare: fare)
104
+ end
105
+
106
+ def payload
107
+ "{\"sd\":{\"Adults\":1,\"AirlineCode\":\"\"," <<
108
+ "\"ArrivalCity\":\"#{to}\",\"ArrivalCityName\":null," <<
109
+ "\"BookingClass\":null,\"CabinClass\":0,\"ChildAge\":[]," <<
110
+ "\"Children\":0,\"CustomerId\":0,\"CustomerType\":0," <<
111
+ "\"CustomerUserId\":91,\"DepartureCity\":\"#{from}\"," <<
112
+ "\"DepartureCityName\":null," <<
113
+ "\"DepartureDate\":\"/Date(#{date})/\"," <<
114
+ "\"DepartureDateGap\":0,\"DirectFlightsOnly\":false," <<
115
+ "\"Infants\":0,\"IsPackageUpsell\":false,\"JourneyType\":1," <<
116
+ "\"ReturnDate\":\"/Date(-2208988800000)/\"," <<
117
+ "\"ReturnDateGap\":0,\"SearchOption\":1},\"fsc\":\"0\"}"
118
+ end
119
+
120
+ def flights(result)
121
+ result["SearchAirlineFlightsResult"]
122
+ end
69
123
  end
@@ -0,0 +1,10 @@
1
+ module PageRequester
2
+ def request(uri, req)
3
+ http = Net::HTTP.new(uri.host, uri.port)
4
+
5
+ http.use_ssl = true
6
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
7
+
8
+ http.request(req)
9
+ end
10
+ end
@@ -1,12 +1,20 @@
1
- require 'terminal-table'
1
+ require "terminal-table"
2
2
 
3
3
  class Printer
4
4
  def self.table(rows = [])
5
- headings = ['Flight', 'Flight #', 'Transit', 'Origin', 'Destination', 'Depart', 'Arrive', 'Fare (RM)']
5
+ headings = [
6
+ "Flight",
7
+ "Flight #",
8
+ "Transit",
9
+ "Origin",
10
+ "Destination",
11
+ "Depart",
12
+ "Arrive",
13
+ "Fare (RM)"]
6
14
 
7
- rows.each { |row| row[:fare] = row[:fare].rjust(9, ' ') }
15
+ rows.each { |row| row[:fare] = row[:fare].rjust(9, " ") }
8
16
 
9
- rows.map! { |row| row.values }
17
+ rows.map!(&:values)
10
18
 
11
19
  Terminal::Table.new(headings: headings, rows: rows)
12
20
  end
@@ -0,0 +1,39 @@
1
+ module Selectors
2
+ module AirAsia
3
+ def depart_at_selector(flight)
4
+ depart_arrivate_at_formatter(flight["segments"][0]["departure-datetime"])
5
+ end
6
+
7
+ def arrive_at_selector(flight)
8
+ depart_arrivate_at_formatter(flight["segments"][0]["arrival-datetime"])
9
+ end
10
+
11
+ def fare_selector(flight)
12
+ fare_formatter(flight["total"]["adult"])
13
+ end
14
+
15
+ def flight_number_selector(flight)
16
+ flight_number_formatter(flight["segments"][0]["flight-number"])
17
+ end
18
+
19
+ def origin_selector(flight)
20
+ flight["segments"][0]["origincode"]
21
+ end
22
+
23
+ def destination_selector(flight)
24
+ flight["segments"][0]["destinationcode"]
25
+ end
26
+
27
+ def depart_arrivate_at_formatter(datetime)
28
+ DateTime.parse(datetime).strftime("%I:%M %p")
29
+ end
30
+
31
+ def fare_formatter(fare)
32
+ sprintf("%.2f", fare)
33
+ end
34
+
35
+ def flight_number_formatter(flight_number)
36
+ flight_number.gsub(/ /, "")
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,61 @@
1
+ module Selectors
2
+ module Firefly
3
+ def depart_at_selector(flight)
4
+ value = flight
5
+ .css("div.visible-xs")
6
+ .css("table")[1]
7
+ .css("td")[0]
8
+
9
+ depart_arrive_at_formatter(value)
10
+ end
11
+
12
+ def arrive_at_selector(flight)
13
+ value = flight
14
+ .css("div.visible-xs")
15
+ .css("table")[1]
16
+ .css("td")[1]
17
+
18
+ depart_arrive_at_formatter(value)
19
+ end
20
+
21
+ def fare_selector(flight)
22
+ value = flight
23
+ .css("div.visible-xs > div")
24
+
25
+ fare_formatter(value)
26
+ end
27
+
28
+ def flight_number_selector(flight)
29
+ value = flight
30
+ .css("div.visible-xs")
31
+ .css("table")[0]
32
+
33
+ flight_number_formatter(value)
34
+ end
35
+
36
+ def origin_selector(flight)
37
+ flight["onclick"]
38
+ .scan(/~[A-Z]{3}~/)[0]
39
+ .gsub("~", "")
40
+ end
41
+
42
+ def destination_selector(flight)
43
+ flight["onclick"]
44
+ .scan(/~[A-Z]{3}~/)[1]
45
+ .gsub("~", "")
46
+ end
47
+
48
+ def depart_arrive_at_formatter(datetime)
49
+ datetime = datetime.text.strip.gsub(/\t/, "").match(/^(.*?)(AM|PM)/)[0]
50
+ DateTime.parse("#{date} #{datetime}").strftime("%I:%M %p")
51
+ end
52
+
53
+ def fare_formatter(fare)
54
+ fare.text.strip.gsub(/ MYR/, "")
55
+ end
56
+
57
+ def flight_number_formatter(flight_number)
58
+ flight_number.text.strip.gsub(/ /, "").gsub(/FLIGHTNO\./, "")
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,45 @@
1
+ module Selectors
2
+ module MalaysiaAirlines
3
+ def depart_at_selector(flight)
4
+ depart_arrive_at_formatter(flight["depScheduled"])
5
+ end
6
+
7
+ def arrive_at_selector(flight)
8
+ depart_arrive_at_formatter(flight["arrScheduled"])
9
+ end
10
+
11
+ def fare_selector(flight)
12
+ fare_formatter(flight["fareDetails"]["totalTripFare"])
13
+ end
14
+
15
+ def flight_number_selector(flight, transit)
16
+ if transit
17
+ flight.map do |arr|
18
+ arr["operatingAirline"] + arr["flightNumber"]
19
+ end.join(" + ")
20
+ else
21
+ flight["marketingAirline"] + flight["flightNumber"]
22
+ end
23
+ end
24
+
25
+ def origin_selector(flight)
26
+ flight["departureAirport"]["code"]
27
+ end
28
+
29
+ def destination_selector(flight, transit)
30
+ if transit
31
+ flight["arrivalAirport"]["code"]
32
+ else
33
+ flight["arrivalAirport"]["code"]
34
+ end
35
+ end
36
+
37
+ def depart_arrive_at_formatter(datetime)
38
+ DateTime.parse(datetime).strftime("%I:%M %p")
39
+ end
40
+
41
+ def fare_formatter(fare)
42
+ sprintf("%.2f", fare)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,40 @@
1
+ module Selectors
2
+ module MalindoAir
3
+ def depart_at_selector(flight)
4
+ depart_arrive_at_formatter(flight["DepartureDate"])
5
+ end
6
+
7
+ def arrive_at_selector(flight)
8
+ depart_arrive_at_formatter(flight)
9
+ end
10
+
11
+ def fare_selector(flight)
12
+ fare_formatter(flight["FlightAmount"])
13
+ end
14
+
15
+ def flight_number_selector(flight)
16
+ flight["SegmentInformation"].map do |trip|
17
+ trip["MACode"] + trip["FlightNo"]
18
+ end.join(" + ")
19
+ end
20
+
21
+ def origin_selector(flight)
22
+ flight["DepCity"]
23
+ end
24
+
25
+ def destination_selector(elem)
26
+ elem["ArrCity"]
27
+ end
28
+
29
+ def depart_arrive_at_formatter(datetime)
30
+ DateTime
31
+ .strptime(datetime.gsub(%r(^\/Date\(|\)\/), ""), "%Q")
32
+ .to_time
33
+ .strftime("%I:%M %p")
34
+ end
35
+
36
+ def fare_formatter(fare)
37
+ sprintf("%.2f", fare)
38
+ end
39
+ end
40
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: teecket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amree Zaid
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-11 00:00:00.000000000 Z
11
+ date: 2015-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: terminal-table
@@ -70,7 +70,9 @@ dependencies:
70
70
  - - ">="
71
71
  - !ruby/object:Gem::Version
72
72
  version: 5.0.0
73
- description: Search ticket's fare for all major airlines in Malaysia at once
73
+ description: |-
74
+ Search ticket's fare for all
75
+ major airlines in Malaysia at once
74
76
  email: mohd.amree@gmail.com
75
77
  executables:
76
78
  - teecket
@@ -85,7 +87,12 @@ files:
85
87
  - lib/teecket/flight.rb
86
88
  - lib/teecket/malaysia_airlines.rb
87
89
  - lib/teecket/malindo_air.rb
90
+ - lib/teecket/page_requester.rb
88
91
  - lib/teecket/printer.rb
92
+ - lib/teecket/selectors/air_asia.rb
93
+ - lib/teecket/selectors/firefly.rb
94
+ - lib/teecket/selectors/malaysia_airlines.rb
95
+ - lib/teecket/selectors/malindo_air.rb
89
96
  homepage: https://github.com/amree/teecket
90
97
  licenses:
91
98
  - MIT