browser 2.5.2 → 5.3.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.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE.md +1 -1
- data/.github/workflows/tests.yml +57 -0
- data/.gitignore +1 -0
- data/.prettierignore +1 -0
- data/.rubocop.yml +22 -107
- data/CHANGELOG.md +192 -11
- data/FUNDING.yml +3 -0
- data/Gemfile +2 -0
- data/README.md +139 -89
- data/Rakefile +11 -3
- data/bot_exceptions.yml +2 -0
- data/bots.yml +302 -247
- data/browser.gemspec +12 -7
- data/gemfiles/rails6_0.gemfile +6 -0
- data/gemfiles/rails6_1.gemfile +6 -0
- data/lib/browser/accept_language.rb +12 -9
- data/lib/browser/action_controller.rb +1 -3
- data/lib/browser/aliases.rb +5 -5
- data/lib/browser/alipay.rb +2 -2
- data/lib/browser/base.rb +101 -19
- data/lib/browser/blackberry.rb +3 -3
- data/lib/browser/bot/empty_user_agent_matcher.rb +11 -0
- data/lib/browser/bot/keyword_matcher.rb +11 -0
- data/lib/browser/bot/known_bots_matcher.rb +11 -0
- data/lib/browser/bot.rb +39 -27
- data/lib/browser/browser.rb +65 -54
- data/lib/browser/chrome.rb +18 -5
- data/lib/browser/detect_version.rb +5 -5
- data/lib/browser/device/android.rb +20 -0
- data/lib/browser/device/blackberry_playbook.rb +1 -1
- data/lib/browser/device/ipad.rb +1 -1
- data/lib/browser/device/iphone.rb +1 -1
- data/lib/browser/device/ipod_touch.rb +1 -1
- data/lib/browser/device/kindle.rb +1 -1
- data/lib/browser/device/kindle_fire.rb +1 -1
- data/lib/browser/device/playstation3.rb +1 -1
- data/lib/browser/device/playstation4.rb +1 -1
- data/lib/browser/device/psp.rb +1 -1
- data/lib/browser/device/psvita.rb +1 -1
- data/lib/browser/device/samsung.rb +33 -0
- data/lib/browser/device/surface.rb +2 -4
- data/lib/browser/device/switch.rb +19 -0
- data/lib/browser/device/tv.rb +1 -1
- data/lib/browser/device/unknown.rb +1 -1
- data/lib/browser/device/wii.rb +1 -1
- data/lib/browser/device/wiiu.rb +1 -1
- data/lib/browser/device/xbox_360.rb +1 -1
- data/lib/browser/device/xbox_one.rb +1 -1
- data/lib/browser/device.rb +47 -30
- data/lib/browser/duck_duck_go.rb +22 -0
- data/lib/browser/edge.rb +6 -2
- data/lib/browser/electron.rb +2 -2
- data/lib/browser/facebook.rb +4 -2
- data/lib/browser/firefox.rb +2 -2
- data/lib/browser/google_search_app.rb +21 -0
- data/lib/browser/huawei_browser.rb +21 -0
- data/lib/browser/instagram.rb +21 -0
- data/lib/browser/internet_explorer.rb +9 -10
- data/lib/browser/maxthon.rb +21 -0
- data/lib/browser/meta/base.rb +0 -1
- data/lib/browser/meta/generic_browser.rb +1 -3
- data/lib/browser/meta.rb +12 -13
- data/lib/browser/micro_messenger.rb +2 -2
- data/lib/browser/middleware/context/additions.rb +1 -1
- data/lib/browser/middleware.rb +4 -3
- data/lib/browser/miui_browser.rb +21 -0
- data/lib/browser/nokia.rb +2 -2
- data/lib/browser/opera.rb +2 -2
- data/lib/browser/otter.rb +2 -2
- data/lib/browser/phantom_js.rb +2 -2
- data/lib/browser/platform/adobe_air.rb +2 -2
- data/lib/browser/platform/android.rb +1 -1
- data/lib/browser/platform/base.rb +3 -2
- data/lib/browser/platform/blackberry.rb +2 -2
- data/lib/browser/platform/chrome_os.rb +1 -1
- data/lib/browser/platform/firefox_os.rb +1 -1
- data/lib/browser/platform/ios.rb +17 -4
- data/lib/browser/platform/kai_os.rb +23 -0
- data/lib/browser/platform/linux.rb +1 -1
- data/lib/browser/platform/mac.rb +5 -3
- data/lib/browser/platform/{other.rb → unknown.rb} +3 -3
- data/lib/browser/platform/windows.rb +2 -2
- data/lib/browser/platform/windows_mobile.rb +1 -1
- data/lib/browser/platform/windows_phone.rb +1 -1
- data/lib/browser/platform.rb +37 -28
- data/lib/browser/qq.rb +5 -5
- data/lib/browser/rails.rb +11 -5
- data/lib/browser/safari.rb +19 -4
- data/lib/browser/samsung_browser.rb +21 -0
- data/lib/browser/snapchat.rb +21 -0
- data/lib/browser/sougou_browser.rb +24 -0
- data/lib/browser/sputnik.rb +21 -0
- data/lib/browser/uc_browser.rb +2 -2
- data/lib/browser/{generic.rb → unknown.rb} +6 -8
- data/lib/browser/version.rb +1 -1
- data/lib/browser/weibo.rb +2 -2
- data/lib/browser/yandex.rb +21 -0
- data/lib/browser.rb +2 -2
- data/samsung.yml +138 -0
- data/search_engines.yml +2 -2
- data/test/browser_test.rb +42 -10
- data/test/rails_test.rb +10 -0
- data/test/sample_app.rb +14 -0
- data/test/test_helper.rb +9 -1
- data/test/ua.yml +147 -109
- data/test/ua_bots.yml +98 -45
- data/test/ua_search_engines.yml +7 -6
- data/test/unit/accept_language_test.rb +24 -0
- data/test/unit/adobe_air_test.rb +1 -1
- data/test/unit/alipay_test.rb +6 -0
- data/test/unit/blackberry_test.rb +0 -6
- data/test/unit/bots_test.rb +37 -27
- data/test/unit/chrome_test.rb +8 -19
- data/test/unit/console_test.rb +2 -2
- data/test/unit/device_test.rb +60 -3
- data/test/unit/duck_duck_go_test.rb +37 -0
- data/test/unit/edge_test.rb +49 -5
- data/test/unit/facebook_test.rb +20 -0
- data/test/unit/firefox_test.rb +0 -3
- data/test/unit/google_search_app_test.rb +54 -0
- data/test/unit/huawei_browser_test.rb +25 -0
- data/test/unit/instagram_test.rb +30 -0
- data/test/unit/internet_explorer_test.rb +0 -12
- data/test/unit/ios_test.rb +7 -5
- data/test/unit/kai_os_test.rb +31 -0
- data/test/unit/kindle_test.rb +0 -2
- data/test/unit/maxthon_test.rb +25 -0
- data/test/unit/meta_test.rb +10 -2
- data/test/unit/micro_messenger_test.rb +21 -6
- data/test/unit/miui_browser_test.rb +25 -0
- data/test/unit/opera_test.rb +1 -2
- data/test/unit/platform_test.rb +34 -8
- data/test/unit/qq_test.rb +12 -0
- data/test/unit/safari_test.rb +12 -7
- data/test/unit/samsung_browser_test.rb +23 -0
- data/test/unit/snapchat_test.rb +40 -0
- data/test/unit/sougou_browser_test.rb +41 -0
- data/test/unit/sputnik_test.rb +22 -0
- data/test/unit/yandex_test.rb +37 -0
- metadata +83 -26
- data/.bundle/config +0 -2
- data/.travis.yml +0 -16
- data/bin/rake +0 -17
- data/gemfiles/rails4.gemfile +0 -4
- data/lib/browser/meta/modern.rb +0 -11
|
@@ -14,8 +14,7 @@ module Browser
|
|
|
14
14
|
.map {|string| string.squeeze(" ").strip }
|
|
15
15
|
.map {|part| new(part) }
|
|
16
16
|
.reject {|al| al.quality.zero? }
|
|
17
|
-
.sort_by
|
|
18
|
-
.reverse
|
|
17
|
+
.sort_by.with_index {|al, idx| [-al.quality, idx] }
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
attr_reader :part
|
|
@@ -35,26 +34,30 @@ module Browser
|
|
|
35
34
|
def code
|
|
36
35
|
@code ||= begin
|
|
37
36
|
code = part[/\A([^-;]+)/, 1]
|
|
38
|
-
code
|
|
37
|
+
code&.downcase
|
|
39
38
|
end
|
|
40
39
|
end
|
|
41
40
|
|
|
42
41
|
def region
|
|
43
42
|
@region ||= begin
|
|
44
43
|
region = part[/\A(?:.*?)-([^;-]+)/, 1]
|
|
45
|
-
region
|
|
44
|
+
region&.upcase
|
|
46
45
|
end
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
def quality
|
|
50
|
-
@quality ||=
|
|
49
|
+
@quality ||= begin
|
|
50
|
+
Float(quality_value || 1.0)
|
|
51
|
+
rescue ArgumentError
|
|
52
|
+
0.1
|
|
53
|
+
end
|
|
51
54
|
end
|
|
52
55
|
|
|
53
|
-
private
|
|
54
|
-
|
|
55
|
-
def quality_value
|
|
56
|
+
private def quality_value
|
|
56
57
|
qvalue = part[/;q=([\d.]+)/, 1]
|
|
57
|
-
qvalue
|
|
58
|
+
qvalue = /\A0\.0?\z/.match?(qvalue) ? "0.0" : qvalue
|
|
59
|
+
qvalue = qvalue.squeeze(".") if qvalue
|
|
60
|
+
qvalue
|
|
58
61
|
end
|
|
59
62
|
end
|
|
60
63
|
end
|
data/lib/browser/aliases.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative "../browser"
|
|
4
4
|
require "forwardable"
|
|
5
5
|
|
|
6
6
|
module Browser
|
|
@@ -15,10 +15,10 @@ module Browser
|
|
|
15
15
|
|
|
16
16
|
DEVICE_ALIASES = %w[
|
|
17
17
|
blackberry_playbook? console? ipad? iphone? ipod_touch? kindle?
|
|
18
|
-
kindle_fire? mobile? nintendo?
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
xbox_one?
|
|
18
|
+
kindle_fire? mobile? nintendo? nintendo_switch? nintendo_wii?
|
|
19
|
+
nintendo_wiiu? playbook? playstation3? playstation4? playstation?
|
|
20
|
+
playstation_vita? ps3? ps4? psp? psp_vita? silk? surface? tablet? tv?
|
|
21
|
+
vita? wii? wiiu? xbox? xbox_360? xbox_one?
|
|
22
22
|
].freeze
|
|
23
23
|
|
|
24
24
|
def self.included(target)
|
data/lib/browser/alipay.rb
CHANGED
data/lib/browser/base.rb
CHANGED
|
@@ -6,12 +6,11 @@ module Browser
|
|
|
6
6
|
|
|
7
7
|
attr_reader :ua
|
|
8
8
|
|
|
9
|
-
# Return an array with all preferred languages that this browser accepts.
|
|
10
|
-
attr_reader :accept_language
|
|
11
|
-
|
|
12
9
|
def initialize(ua, accept_language: nil)
|
|
10
|
+
validate_size(:user_agent, ua.to_s)
|
|
11
|
+
|
|
13
12
|
@ua = ua
|
|
14
|
-
@
|
|
13
|
+
@accept_language_raw = accept_language.to_s
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
# Return a meta info about this browser.
|
|
@@ -19,6 +18,14 @@ module Browser
|
|
|
19
18
|
Meta.get(self)
|
|
20
19
|
end
|
|
21
20
|
|
|
21
|
+
# Return an array with all preferred languages that this browser accepts.
|
|
22
|
+
def accept_language
|
|
23
|
+
@accept_language ||= begin
|
|
24
|
+
validate_size(:accept_language, @accept_language_raw)
|
|
25
|
+
AcceptLanguage.parse(@accept_language_raw)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
22
29
|
alias_method :to_a, :meta
|
|
23
30
|
|
|
24
31
|
# Return meta representation as string.
|
|
@@ -50,11 +57,6 @@ module Browser
|
|
|
50
57
|
@device ||= Device.new(ua)
|
|
51
58
|
end
|
|
52
59
|
|
|
53
|
-
# Return true if browser is modern (Webkit, Firefox 17+, IE9+, Opera 12+).
|
|
54
|
-
def modern?
|
|
55
|
-
Browser.modern_rules.any? {|rule| rule === self } # rubocop:disable Metrics/LineLength, Style/CaseEquality
|
|
56
|
-
end
|
|
57
|
-
|
|
58
60
|
# Detect if browser is Microsoft Internet Explorer.
|
|
59
61
|
def ie?(expected_version = nil)
|
|
60
62
|
InternetExplorer.new(ua).match? &&
|
|
@@ -78,6 +80,18 @@ module Browser
|
|
|
78
80
|
"0"
|
|
79
81
|
end
|
|
80
82
|
|
|
83
|
+
# Detect if browser is Instagram.
|
|
84
|
+
def instagram?(expected_version = nil)
|
|
85
|
+
Instagram.new(ua).match? &&
|
|
86
|
+
detect_version?(full_version, expected_version)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Detect if browser is Snapchat.
|
|
90
|
+
def snapchat?(expected_version = nil)
|
|
91
|
+
Snapchat.new(ua).match? &&
|
|
92
|
+
detect_version?(full_version, expected_version)
|
|
93
|
+
end
|
|
94
|
+
|
|
81
95
|
# Detect if browser if Facebook.
|
|
82
96
|
def facebook?(expected_version = nil)
|
|
83
97
|
Facebook.new(ua).match? &&
|
|
@@ -92,19 +106,20 @@ module Browser
|
|
|
92
106
|
|
|
93
107
|
# Detect if browser is WebKit-based.
|
|
94
108
|
def webkit?(expected_version = nil)
|
|
95
|
-
ua
|
|
96
|
-
!edge? &&
|
|
109
|
+
ua.match?(/AppleWebKit/i) &&
|
|
110
|
+
(!edge? || Edge.new(ua).chrome_based?) &&
|
|
97
111
|
detect_version?(webkit_full_version, expected_version)
|
|
98
112
|
end
|
|
99
113
|
|
|
100
114
|
# Detect if browser is QuickTime
|
|
101
115
|
def quicktime?(expected_version = nil)
|
|
102
|
-
ua
|
|
116
|
+
ua.match?(/QuickTime/i) && detect_version?(full_version, expected_version)
|
|
103
117
|
end
|
|
104
118
|
|
|
105
119
|
# Detect if browser is Apple CoreMedia.
|
|
106
120
|
def core_media?(expected_version = nil)
|
|
107
|
-
ua
|
|
121
|
+
ua.include?("CoreMedia") && detect_version?(full_version,
|
|
122
|
+
expected_version)
|
|
108
123
|
end
|
|
109
124
|
|
|
110
125
|
# Detect if browser is PhantomJS
|
|
@@ -115,11 +130,11 @@ module Browser
|
|
|
115
130
|
|
|
116
131
|
# Detect if browser is Safari.
|
|
117
132
|
def safari?(expected_version = nil)
|
|
118
|
-
Safari.new(ua).match? && detect_version?(
|
|
133
|
+
Safari.new(ua).match? && detect_version?(full_version, expected_version)
|
|
119
134
|
end
|
|
120
135
|
|
|
121
136
|
def safari_webapp_mode?
|
|
122
|
-
(device.ipad? || device.iphone?) && ua
|
|
137
|
+
(device.ipad? || device.iphone?) && ua.include?("AppleWebKit")
|
|
123
138
|
end
|
|
124
139
|
|
|
125
140
|
# Detect if browser is Firefox.
|
|
@@ -137,10 +152,16 @@ module Browser
|
|
|
137
152
|
Opera.new(ua).match? && detect_version?(full_version, expected_version)
|
|
138
153
|
end
|
|
139
154
|
|
|
155
|
+
# Detect if browser is Sputnik.
|
|
156
|
+
def sputnik?(expected_version = nil)
|
|
157
|
+
Sputnik.new(ua).match? && detect_version?(full_version, expected_version)
|
|
158
|
+
end
|
|
159
|
+
|
|
140
160
|
# Detect if browser is Yandex.
|
|
141
161
|
def yandex?(expected_version = nil)
|
|
142
|
-
ua
|
|
162
|
+
Yandex.new(ua).match? && detect_version?(full_version, expected_version)
|
|
143
163
|
end
|
|
164
|
+
alias_method :yandex_browser?, :yandex?
|
|
144
165
|
|
|
145
166
|
# Detect if browser is UCBrowser.
|
|
146
167
|
def uc_browser?(expected_version = nil)
|
|
@@ -171,15 +192,65 @@ module Browser
|
|
|
171
192
|
|
|
172
193
|
# Detect if browser is Opera Mini.
|
|
173
194
|
def opera_mini?(expected_version = nil)
|
|
174
|
-
ua
|
|
195
|
+
ua.include?("Opera Mini") && detect_version?(full_version,
|
|
196
|
+
expected_version)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Detect if browser is DuckDuckGo.
|
|
200
|
+
def duck_duck_go?(expected_version = nil)
|
|
201
|
+
ua.include?("DuckDuckGo") && detect_version?(full_version,
|
|
202
|
+
expected_version)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Detect if browser is Samsung.
|
|
206
|
+
def samsung_browser?(expected_version = nil)
|
|
207
|
+
ua.include?("SamsungBrowser") && detect_version?(full_version,
|
|
208
|
+
expected_version)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Detect if browser is Huawei.
|
|
212
|
+
def huawei_browser?(expected_version = nil)
|
|
213
|
+
HuaweiBrowser.new(ua).match? &&
|
|
214
|
+
detect_version?(full_version, expected_version)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Detect if browser is Xiaomi Miui.
|
|
218
|
+
def miui_browser?(expected_version = nil)
|
|
219
|
+
MiuiBrowser.new(ua).match? &&
|
|
220
|
+
detect_version?(full_version, expected_version)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Detect if browser is Maxthon.
|
|
224
|
+
def maxthon?(expected_version = nil)
|
|
225
|
+
Maxthon.new(ua).match? && detect_version?(full_version, expected_version)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Detect if browser is QQ.
|
|
229
|
+
def qq?(expected_version = nil)
|
|
230
|
+
QQ.new(ua).match? && detect_version?(full_version, expected_version)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Detect if browser is Sougou.
|
|
234
|
+
def sougou_browser?(expected_version = nil)
|
|
235
|
+
SougouBrowser.new(ua).match? &&
|
|
236
|
+
detect_version?(full_version, expected_version)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Detect if browser is Google Search App
|
|
240
|
+
def google_search_app?(expected_version = nil)
|
|
241
|
+
ua.include?("GSA") && detect_version?(full_version, expected_version)
|
|
175
242
|
end
|
|
176
243
|
|
|
177
244
|
def webkit_full_version
|
|
178
|
-
ua[%r
|
|
245
|
+
ua[%r{AppleWebKit/([\d.]+)}, 1] || "0.0"
|
|
179
246
|
end
|
|
180
247
|
|
|
181
248
|
def known?
|
|
182
|
-
|
|
249
|
+
!unknown?
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def unknown?
|
|
253
|
+
id == :unknown_browser
|
|
183
254
|
end
|
|
184
255
|
|
|
185
256
|
# Detect if browser is a proxy browser.
|
|
@@ -191,5 +262,16 @@ module Browser
|
|
|
191
262
|
def electron?(expected_version = nil)
|
|
192
263
|
Electron.new(ua).match? && detect_version?(full_version, expected_version)
|
|
193
264
|
end
|
|
265
|
+
|
|
266
|
+
private def validate_size(subject, input)
|
|
267
|
+
actual_bytesize = input.bytesize
|
|
268
|
+
size_limit = Browser.public_send("#{subject}_size_limit")
|
|
269
|
+
|
|
270
|
+
return if actual_bytesize < size_limit
|
|
271
|
+
|
|
272
|
+
raise Error,
|
|
273
|
+
"#{subject} cannot be larger than #{size_limit} bytes; " \
|
|
274
|
+
"actual size is #{actual_bytesize} bytes"
|
|
275
|
+
end
|
|
194
276
|
end
|
|
195
277
|
end
|
data/lib/browser/blackberry.rb
CHANGED
|
@@ -11,13 +11,13 @@ module Browser
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def full_version
|
|
14
|
-
ua[%r
|
|
15
|
-
ua[%r
|
|
14
|
+
ua[%r{BlackBerry[\da-z]+/([\d.]+)}, 1] ||
|
|
15
|
+
ua[%r{Version/([\d.]+)}, 1] ||
|
|
16
16
|
"0.0"
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def match?
|
|
20
|
-
ua
|
|
20
|
+
ua.match?(/BlackBerry|BB10/)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
data/lib/browser/bot.rb
CHANGED
|
@@ -2,64 +2,76 @@
|
|
|
2
2
|
|
|
3
3
|
module Browser
|
|
4
4
|
class Bot
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
GENERIC_NAME = "Generic Bot"
|
|
6
|
+
|
|
7
|
+
def self.matchers
|
|
8
|
+
@matchers ||= default_matchers
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.default_matchers
|
|
12
|
+
[
|
|
13
|
+
EmptyUserAgentMatcher,
|
|
14
|
+
KnownBotsMatcher,
|
|
15
|
+
KeywordMatcher
|
|
16
|
+
]
|
|
7
17
|
end
|
|
8
18
|
|
|
9
|
-
def self.
|
|
10
|
-
|
|
19
|
+
def self.load_yaml(path)
|
|
20
|
+
YAML.load_file(Browser.root.join(path))
|
|
11
21
|
end
|
|
12
22
|
|
|
13
23
|
def self.bots
|
|
14
|
-
@bots ||=
|
|
24
|
+
@bots ||= load_yaml("bots.yml")
|
|
15
25
|
end
|
|
16
26
|
|
|
17
27
|
def self.bot_exceptions
|
|
18
|
-
@bot_exceptions ||=
|
|
19
|
-
.load_file(Browser.root.join("bot_exceptions.yml"))
|
|
28
|
+
@bot_exceptions ||= load_yaml("bot_exceptions.yml")
|
|
20
29
|
end
|
|
21
30
|
|
|
22
31
|
def self.search_engines
|
|
23
|
-
@search_engines ||=
|
|
24
|
-
|
|
32
|
+
@search_engines ||= load_yaml("search_engines.yml")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.why?(ua)
|
|
36
|
+
ua = ua.downcase.strip
|
|
37
|
+
browser = Browser.new(ua)
|
|
38
|
+
matchers.find {|matcher| matcher.call(ua, browser) }
|
|
25
39
|
end
|
|
26
40
|
|
|
27
|
-
attr_reader :ua
|
|
41
|
+
attr_reader :ua, :browser
|
|
28
42
|
|
|
29
43
|
def initialize(ua)
|
|
30
|
-
@ua = ua
|
|
44
|
+
@ua = ua.downcase.strip
|
|
45
|
+
@browser = Browser.new(@ua)
|
|
31
46
|
end
|
|
32
47
|
|
|
33
48
|
def bot?
|
|
34
|
-
|
|
49
|
+
!bot_exception? && detect_bot?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def why?
|
|
53
|
+
self.class.matchers.find {|matcher| matcher.call(ua, self) }
|
|
35
54
|
end
|
|
36
55
|
|
|
37
56
|
def search_engine?
|
|
38
|
-
self.class.search_engines.any? {|key, _|
|
|
57
|
+
self.class.search_engines.any? {|key, _| ua.include?(key) }
|
|
39
58
|
end
|
|
40
59
|
|
|
41
60
|
def name
|
|
42
61
|
return unless bot?
|
|
43
|
-
return "Generic Bot" if bot_with_empty_ua?
|
|
44
|
-
self.class.bots.find {|key, _| downcased_ua.include?(key) }.last
|
|
45
|
-
end
|
|
46
62
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def bot_with_empty_ua?
|
|
50
|
-
self.class.detect_empty_ua? && ua.strip == ""
|
|
63
|
+
self.class.bots.find {|key, _| ua.include?(key) }&.last || GENERIC_NAME
|
|
51
64
|
end
|
|
52
65
|
|
|
53
|
-
def bot_exception?
|
|
54
|
-
self.class.bot_exceptions.any? {|key|
|
|
66
|
+
private def bot_exception?
|
|
67
|
+
self.class.bot_exceptions.any? {|key| ua.include?(key) }
|
|
55
68
|
end
|
|
56
69
|
|
|
57
|
-
def detect_bot?
|
|
58
|
-
self.class.
|
|
70
|
+
private def detect_bot?
|
|
71
|
+
self.class.matchers.any? {|matcher| matcher.call(ua, browser) }
|
|
59
72
|
end
|
|
60
73
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
end
|
|
74
|
+
private :ua
|
|
75
|
+
private :browser
|
|
64
76
|
end
|
|
65
77
|
end
|
data/lib/browser/browser.rb
CHANGED
|
@@ -4,43 +4,66 @@ require "set"
|
|
|
4
4
|
require "yaml"
|
|
5
5
|
require "pathname"
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
7
|
+
require_relative "version"
|
|
8
|
+
require_relative "detect_version"
|
|
9
|
+
require_relative "accept_language"
|
|
10
|
+
require_relative "base"
|
|
11
|
+
require_relative "safari"
|
|
12
|
+
require_relative "chrome"
|
|
13
|
+
require_relative "internet_explorer"
|
|
14
|
+
require_relative "firefox"
|
|
15
|
+
require_relative "edge"
|
|
16
|
+
require_relative "opera"
|
|
17
|
+
require_relative "blackberry"
|
|
18
|
+
require_relative "unknown"
|
|
19
|
+
require_relative "phantom_js"
|
|
20
|
+
require_relative "uc_browser"
|
|
21
|
+
require_relative "nokia"
|
|
22
|
+
require_relative "micro_messenger"
|
|
23
|
+
require_relative "weibo"
|
|
24
|
+
require_relative "qq"
|
|
25
|
+
require_relative "alipay"
|
|
26
|
+
require_relative "electron"
|
|
27
|
+
require_relative "facebook"
|
|
28
|
+
require_relative "otter"
|
|
29
|
+
require_relative "instagram"
|
|
30
|
+
require_relative "yandex"
|
|
31
|
+
require_relative "sputnik"
|
|
32
|
+
require_relative "snapchat"
|
|
33
|
+
require_relative "duck_duck_go"
|
|
34
|
+
require_relative "samsung_browser"
|
|
35
|
+
require_relative "huawei_browser"
|
|
36
|
+
require_relative "miui_browser"
|
|
37
|
+
require_relative "maxthon"
|
|
38
|
+
require_relative "sougou_browser"
|
|
39
|
+
require_relative "google_search_app"
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
require_relative "bot"
|
|
42
|
+
require_relative "bot/empty_user_agent_matcher"
|
|
43
|
+
require_relative "bot/keyword_matcher"
|
|
44
|
+
require_relative "bot/known_bots_matcher"
|
|
32
45
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
require_relative "middleware"
|
|
47
|
+
require_relative "platform"
|
|
48
|
+
require_relative "device"
|
|
49
|
+
require_relative "meta"
|
|
36
50
|
|
|
37
51
|
module Browser
|
|
38
|
-
EMPTY_STRING = ""
|
|
52
|
+
EMPTY_STRING = ""
|
|
53
|
+
|
|
54
|
+
Error = Class.new(StandardError)
|
|
39
55
|
|
|
40
56
|
def self.root
|
|
41
|
-
@root ||= Pathname.new(File.expand_path("
|
|
57
|
+
@root ||= Pathname.new(File.expand_path("../..", __dir__))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class << self
|
|
61
|
+
attr_accessor :user_agent_size_limit, :accept_language_size_limit
|
|
42
62
|
end
|
|
43
63
|
|
|
64
|
+
self.user_agent_size_limit = 2048
|
|
65
|
+
self.accept_language_size_limit = 2048
|
|
66
|
+
|
|
44
67
|
# Hold the list of browser matchers.
|
|
45
68
|
# Order is important.
|
|
46
69
|
def self.matchers
|
|
@@ -55,40 +78,28 @@ module Browser
|
|
|
55
78
|
Firefox,
|
|
56
79
|
Otter,
|
|
57
80
|
Facebook, # must be placed before Chrome and Safari
|
|
81
|
+
Instagram, # must be placed before Chrome and Safari
|
|
82
|
+
Snapchat, # must be placed before Chrome and Safari
|
|
58
83
|
Weibo, # must be placed before Chrome and Safari
|
|
84
|
+
MicroMessenger, # must be placed before QQ
|
|
59
85
|
QQ, # must be placed before Chrome and Safari
|
|
60
86
|
Alipay, # must be placed before Chrome and Safari
|
|
61
87
|
Electron, # must be placed before Chrome and Safari
|
|
88
|
+
Yandex, # must be placed before Chrome and Safari
|
|
89
|
+
Sputnik, # must be placed before Chrome and Safari
|
|
90
|
+
DuckDuckGo, # must be placed before Chrome and Safari
|
|
91
|
+
SamsungBrowser, # must be placed before Chrome and Safari
|
|
92
|
+
HuaweiBrowser, # must be placed before Chrome and Safari
|
|
93
|
+
MiuiBrowser, # must be placed before Chrome and Safari
|
|
94
|
+
Maxthon, # must be placed before Chrome and Safari
|
|
95
|
+
SougouBrowser, # must be placed before Chrome and Safari
|
|
96
|
+
GoogleSearchApp, # must be placed before Chrome and Safari
|
|
62
97
|
Chrome,
|
|
63
98
|
Safari,
|
|
64
|
-
|
|
65
|
-
Generic
|
|
99
|
+
Unknown
|
|
66
100
|
]
|
|
67
101
|
end
|
|
68
102
|
|
|
69
|
-
# Define the rules which define a modern browser.
|
|
70
|
-
# A rule must be a proc/lambda or any object that implements the method
|
|
71
|
-
# === and accepts the browser object.
|
|
72
|
-
#
|
|
73
|
-
# To redefine all rules, clear the existing rules before adding your own.
|
|
74
|
-
#
|
|
75
|
-
# # Only Chrome Canary is considered modern.
|
|
76
|
-
# Browser.modern_rules.clear
|
|
77
|
-
# Browser.modern_rules << -> b { b.chrome? && b.version >= "37" }
|
|
78
|
-
#
|
|
79
|
-
def self.modern_rules
|
|
80
|
-
@modern_rules ||= []
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
modern_rules.tap do |rules|
|
|
84
|
-
rules << ->(b) { b.webkit? }
|
|
85
|
-
rules << ->(b) { b.firefox? && b.version.to_i >= 17 }
|
|
86
|
-
rules << ->(b) { b.ie? && b.version.to_i >= 9 && !b.compatibility_view? }
|
|
87
|
-
rules << ->(b) { b.edge? && !b.compatibility_view? }
|
|
88
|
-
rules << ->(b) { b.opera? && b.version.to_i >= 12 }
|
|
89
|
-
rules << ->(b) { b.firefox? && b.device.tablet? && b.platform.android? && b.version.to_i >= 14 } # rubocop:disable Metrics/LineLength
|
|
90
|
-
end
|
|
91
|
-
|
|
92
103
|
def self.new(user_agent, **kwargs)
|
|
93
104
|
matchers
|
|
94
105
|
.map {|klass| klass.new(user_agent || EMPTY_STRING, **kwargs) }
|
data/lib/browser/chrome.rb
CHANGED
|
@@ -12,15 +12,28 @@ module Browser
|
|
|
12
12
|
|
|
13
13
|
def full_version
|
|
14
14
|
# Each regex on its own line to enforce precedence.
|
|
15
|
-
ua[%r
|
|
16
|
-
ua[%r
|
|
17
|
-
ua[%r
|
|
18
|
-
ua[%r
|
|
15
|
+
ua[%r{Chrome/([\d.]+)}, 1] ||
|
|
16
|
+
ua[%r{CriOS/([\d.]+)}, 1] ||
|
|
17
|
+
ua[%r{Safari/([\d.]+)}, 1] ||
|
|
18
|
+
ua[%r{AppleWebKit/([\d.]+)}, 1] ||
|
|
19
19
|
"0.0"
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def match?
|
|
23
|
-
ua
|
|
23
|
+
ua.match?(/Chrome|CriOS/) &&
|
|
24
|
+
!ua.match?(/PhantomJS|FxiOS|ArchiveBot/) &&
|
|
25
|
+
!opera? &&
|
|
26
|
+
!edge? &&
|
|
27
|
+
!duck_duck_go? &&
|
|
28
|
+
!yandex? &&
|
|
29
|
+
!sputnik? &&
|
|
30
|
+
!samsung_browser? &&
|
|
31
|
+
!huawei_browser? &&
|
|
32
|
+
!miui_browser? &&
|
|
33
|
+
!maxthon? &&
|
|
34
|
+
!qq? &&
|
|
35
|
+
!sougou_browser? &&
|
|
36
|
+
!google_search_app?
|
|
24
37
|
end
|
|
25
38
|
end
|
|
26
39
|
end
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Browser
|
|
4
4
|
module DetectVersion
|
|
5
|
-
private
|
|
6
|
-
|
|
7
|
-
def detect_version?(actual_version, expected_version)
|
|
5
|
+
private def detect_version?(actual_version, expected_version)
|
|
8
6
|
return true unless expected_version
|
|
9
7
|
return false if expected_version && !actual_version
|
|
10
8
|
|
|
@@ -13,10 +11,12 @@ module Browser
|
|
|
13
11
|
|
|
14
12
|
Gem::Requirement.create(expected_version)
|
|
15
13
|
.satisfied_by?(Gem::Version.create(actual_version))
|
|
14
|
+
rescue ArgumentError
|
|
15
|
+
false
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def parse_version(version)
|
|
19
|
-
version.
|
|
18
|
+
private def parse_version(version)
|
|
19
|
+
version.is_a?(Numeric) ? version.to_s : version
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
end
|