teecket 0.0.6 → 0.0.7

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.
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