russian_post 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ - jruby-19mode
data/Gemfile.lock CHANGED
@@ -4,21 +4,40 @@ PATH
4
4
  russian_post (0.1.0)
5
5
  chunky_png (~> 1.2)
6
6
  excon (~> 0.20)
7
+ mechanize (~> 2.6.0)
7
8
  nokogiri (~> 1.5)
8
9
 
9
10
  GEM
10
11
  remote: https://rubygems.org/
11
12
  specs:
12
13
  addressable (2.3.4)
13
- chunky_png (1.2.7)
14
+ chunky_png (1.2.8)
14
15
  crack (0.3.2)
16
+ domain_name (0.5.11)
17
+ unf (>= 0.0.5, < 1.0.0)
15
18
  excon (0.20.1)
19
+ mechanize (2.6.0)
20
+ domain_name (~> 0.5, >= 0.5.1)
21
+ mime-types (~> 1.17, >= 1.17.2)
22
+ net-http-digest_auth (~> 1.1, >= 1.1.1)
23
+ net-http-persistent (~> 2.5, >= 2.5.2)
24
+ nokogiri (~> 1.4)
25
+ ntlm-http (~> 0.1, >= 0.1.1)
26
+ webrobots (>= 0.0.9, < 0.2)
27
+ mime-types (1.23)
28
+ net-http-digest_auth (1.3)
29
+ net-http-persistent (2.8)
16
30
  nokogiri (1.5.9)
31
+ ntlm-http (0.1.1)
17
32
  rake (10.0.3)
33
+ unf (0.1.1)
34
+ unf_ext
35
+ unf_ext (0.0.6)
18
36
  vcr (2.4.0)
19
37
  webmock (1.11.0)
20
38
  addressable (>= 2.2.7)
21
39
  crack (>= 0.3.2)
40
+ webrobots (0.1.1)
22
41
 
23
42
  PLATFORMS
24
43
  ruby
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Russian Post Tools
2
2
 
3
3
  [![Code Climate](https://codeclimate.com/github/t3hk0d3/russian_post.png)](https://codeclimate.com/github/t3hk0d3/russian_post)
4
+ [![Build Status](https://travis-ci.org/t3hk0d3/russian_post.png?branch=master)](https://travis-ci.org/t3hk0d3/russian_post)
5
+ [![Dependency Status](https://gemnasium.com/t3hk0d3/russian_post.png)](https://gemnasium.com/t3hk0d3/russian_post)
4
6
 
5
7
  ## Installation
6
8
 
@@ -38,7 +38,6 @@ module RussianPost
38
38
  end
39
39
 
40
40
  def recognize!
41
-
42
41
  captcha_image = grayscale(image)
43
42
 
44
43
  results = {}
@@ -63,7 +62,8 @@ module RussianPost
63
62
  end
64
63
 
65
64
  def prepare_text(results)
66
- results.map(&:character).join('')
65
+ captcha_text = results.map(&:character).join('')
66
+ captcha_text.size == 5 ? captcha_text : raise("Unable to recognize captcha")
67
67
  end
68
68
 
69
69
  def grayscale(captcha_image)
@@ -96,4 +96,4 @@ module RussianPost
96
96
 
97
97
  end
98
98
 
99
- end
99
+ end
@@ -1,114 +1,77 @@
1
- require 'excon'
2
- require 'nokogiri'
1
+ require 'mechanize'
3
2
 
4
3
  require 'time'
5
- require 'cgi'
6
4
 
7
5
  module RussianPost
8
6
 
9
7
  class Tracking
10
8
 
11
- TRACKING_PAGE = 'http://www.russianpost.ru/rp/servise/ru/home/postuslug/trackingpo'
9
+ attr_reader :barcode, :agent
12
10
 
11
+ TRACKING_PAGE = 'http://www.russianpost.ru/rp/servise/ru/home/postuslug/trackingpo'
12
+ COLUMNS = [:type, :date, :zip_code, :location, :message, :weight, :declared_cost,
13
+ :delivery_cash, :destination_zip_code, :destination_location]
14
+
13
15
  def initialize(tracking_code)
14
- @code = tracking_code.strip
16
+ @barcode = tracking_code.strip.upcase
17
+ @agent = Mechanize.new
15
18
  end
16
19
 
17
20
  def track
18
- # fetch form
19
- response = fetch(TRACKING_PAGE)
20
-
21
- tracking_form = response.body
22
-
23
- tracking_params = {}
24
-
25
- tracking_form.scan(/\<input ([^\>]+)\>/) do |result|
26
- param = Hash[result.first.scan(/(name|value)=(?:"|')([^\"\']+)(?:"|')/)]
27
-
28
- tracking_params[param['name']] = param['value']
29
- end
30
-
31
- action_path = if tracking_form =~ /\<form .* action=\"([^\"]+)\"/
32
- $1
33
- else
34
- raise "Unable to extract form path"
35
- end
36
-
37
- captcha_url = if tracking_form =~ /<img id='captchaImage' src='([^\']+)'/
38
- $1
39
- else
40
- raise "Unable to extract captcha image url"
41
- end
42
-
43
- captcha = RussianPost::Captcha.for_url(captcha_url)
44
-
45
- raise "Unable to recognize captcha" unless captcha.valid?
46
-
47
- tracking_params['BarCode'] = @code
48
- tracking_params['InputedCaptchaCode'] = captcha.text
49
- tracking_params['searchsign'] = '1'
50
-
51
- cookies = Hash[response.headers['Set-Cookie'].scan(/([^\=\;]+)=([^\;]+)[^\,]*,*/).map { |name, value| [name.strip, value.strip] }]
52
- cookies.delete("path")
53
- cookies = cookies.map { |name, value| "#{name}=#{value}"}.join("; ")
54
-
55
- request_data = encode_params(tracking_params)
56
-
57
- response = Excon.post('http://www.russianpost.ru' + action_path,
58
- :headers => {'Cookie' => cookies,
59
- 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',
60
- 'Referer' => 'http://www.russianpost.ru/resp_engine.aspx?Path=rp/servise/ru/home/postuslug/trackingpo',\
61
- 'Origin' => 'http://www.russianpost.ru',
62
- 'Content-Type' => 'application/x-www-form-urlencoded',
63
- 'Content-Length' => request_data.size
64
- },
65
- :body => request_data)
21
+ initial_page = fetch_initial_page
22
+ tracking_page = fetch_tracking_data(initial_page)
23
+ parse_tracking_table(tracking_table(tracking_page))
24
+ end
66
25
 
67
- if response.body =~ /<table class="pagetext">(.+)<\/table>/
68
- doc = Nokogiri::HTML::Document.parse(response.body)
26
+ private
69
27
 
70
- columns = [:type, :date, :zip_code, :location, :message, :weight, :declared_cost, :delivery_cash, :destination_zip_code, :destination_location]
28
+ def solve_captcha(page)
29
+ RussianPost::Captcha.for_url(get_captcha_url(page)).text
30
+ end
71
31
 
72
- rows = []
73
- doc.css('table.pagetext tr').each do |row|
74
- data = row.css('td').map(&:text).map { |text| !['-', ''].include?(text) ? text : nil }
32
+ def get_captcha_url(page)
33
+ page.images.last.src
34
+ end
75
35
 
76
- next if data.empty?
36
+ def tracking_table(page)
37
+ table = page.search(".pagetext tbody tr")
38
+ table ? table : raise("No tracking table found")
39
+ end
77
40
 
78
- hash = Hash[columns.zip(data)]
79
- hash[:date] = Time.parse(hash[:date])
41
+ def parse_tracking_table(table)
42
+ table.map { |e| parse_row(e) }
43
+ end
80
44
 
81
- rows << hash
82
- end
45
+ def parse_row(row)
46
+ data = get_row_data(row)
47
+ data[1] = Time.parse("#{data[1]} +04:00")
83
48
 
84
- return rows
85
- else
86
- raise "No tracks table in response"
87
- end
49
+ Hash[COLUMNS.zip(data)]
88
50
  end
89
51
 
90
- private
91
-
92
- def encode_params(params)
93
- params.map do |name, value|
94
- "#{CGI.escape(name.to_s)}=#{CGI.escape(value.to_s)}"
95
- end.join('&')
52
+ def get_row_data(row)
53
+ row.css('td').map { |td| td.text unless ['-', ''].include?(td.text) }
96
54
  end
97
55
 
98
- def fetch(url)
99
- response = Excon.get(url)
100
- if response.body =~ /<input id=\"key\" name=\"key\" value=\"([0-9]+)\"\/>/ # tough security huh
101
- response = Excon.post(url, body: "key=#{$1}")
102
- end
56
+ def fetch_initial_page
57
+ page = agent.get(TRACKING_PAGE)
58
+ bypass_security(page) or page
59
+ end
103
60
 
104
- if response.body.include?("window.location.replace(window.location.toString())") # hehe
105
- puts "foo"
106
- response = fetch(url)
61
+ def bypass_security(page)
62
+ if page.form.has_field?("key")
63
+ page.form.submit
64
+ elsif page.body.include?("window.location.replace(window.location.toString())")
65
+ fetch_initial_page
107
66
  end
108
-
109
- response
110
67
  end
111
68
 
69
+ def fetch_tracking_data(page)
70
+ page.form.set_fields(
71
+ 'BarCode' => barcode,
72
+ 'InputedCaptchaCode' => solve_captcha(page),
73
+ 'searchsign' => '1')
74
+ page.form.submit
75
+ end
112
76
  end
113
-
114
- end
77
+ end
@@ -1,3 +1,3 @@
1
1
  module RussianPost
2
- VERSION = "0.1.0"
3
- end
2
+ VERSION = "0.1.1"
3
+ end
data/russian_post.gemspec CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency "excon", "~> 0.20"
22
22
  spec.add_dependency "chunky_png", "~> 1.2"
23
23
  spec.add_dependency "nokogiri", "~> 1.5"
24
+ spec.add_dependency "mechanize", "~> 2.6.0"
24
25
 
25
26
  spec.add_development_dependency "bundler", "~> 1.3"
26
27
  spec.add_development_dependency "rake"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: russian_post
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-25 00:00:00.000000000 Z
12
+ date: 2013-04-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: excon
@@ -59,6 +59,22 @@ dependencies:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
61
  version: '1.5'
62
+ - !ruby/object:Gem::Dependency
63
+ name: mechanize
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 2.6.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 2.6.0
62
78
  - !ruby/object:Gem::Dependency
63
79
  name: bundler
64
80
  requirement: !ruby/object:Gem::Requirement
@@ -132,6 +148,7 @@ extensions: []
132
148
  extra_rdoc_files: []
133
149
  files:
134
150
  - .gitignore
151
+ - .travis.yml
135
152
  - Gemfile
136
153
  - Gemfile.lock
137
154
  - README.md