fake_useragent 1.0.0 → 1.0.2

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
  SHA256:
3
- metadata.gz: d3018c646d27e4571aa0a4d066d4dd57b1d1b2c266ae4c3450041d6a3b504cbb
4
- data.tar.gz: 81a2f7ad6a4c9b3354c699ba67bc8e122c27a507aef0f1b1eefb4af189066f82
3
+ metadata.gz: bd933e3718a55c3d9e4f3e8b81ea5e3b2dbbf06828e27771175301aff0a8e3dc
4
+ data.tar.gz: 511210c1c989e109f825c49e6aa36fc7718d2ea9c934b4bad4faf78364e0b569
5
5
  SHA512:
6
- metadata.gz: 6533eb6d2f5978eb780f65f288c0ace51d5965d02f793966c010f896b884c7d7e6140eac04a723b253a803dbb7a35eb2e0159b015071115dfebc40f091d23d7a
7
- data.tar.gz: 68e92e1e0420fc832fcbf8faa0d4167a16108f18f729e59ce1507ea7e78f14e74bdd97feb7729ff058513cf69def3ea91f353297576b03cd1c5f992193249cee
6
+ metadata.gz: 7a528955e7ecfdd44f28b319fee83e4e0b0e5e7af65436ee2aa6b5ee5c1b50d09ac2efea6384b01258213c5eca07076f91e69965cd575f2dad8377cda03a5961
7
+ data.tar.gz: c1b06c2e6a42136fa8821fefbc90b6988a24e4c9e7b931cd529075c284729023dfee2cb6484ee35ba7182e08b3a91d629a724dc4157a1f48758cd168949aa318
data/README.md CHANGED
@@ -3,8 +3,11 @@
3
3
  ## For what?
4
4
  Generates random valid web user agents
5
5
 
6
+ ## Installation
7
+ `gem install fake_useragent`
8
+
6
9
  ## Usage example:
7
- This version just a test. You can use `generate_user_agent` or `generate_navigator_js` from `base.rb`.
10
+ This version just a test. You can use `generate_user_agent`, or `generate_navigator_js`
8
11
 
9
12
  parameters: `os`, `navigator`, `device_type` and `platform` (deprecated)
10
13
  Possible values:
@@ -2,7 +2,7 @@ $:.push File.expand_path("../lib", __FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'fake_useragent'
5
- s.version = '1.0.0'
5
+ s.version = '1.0.2'
6
6
  s.date = '2021-04-17'
7
7
  s.summary = 'FakeUserAgent - simple gem for generating valid web user agents.'
8
8
  s.description = 'Simple gem for generating valid web user agents.'
@@ -1,473 +1,14 @@
1
1
  # frozen_string_literal: true
2
+ require_relative './fake_useragent/core'
2
3
 
3
- require 'time'
4
- require 'json'
5
- require 'pp'
6
-
7
- require_relative './fake_useragent/error'
8
- require_relative './fake_useragent/device'
9
-
10
- private
11
-
12
- DEVICE_TYPE_OS = {
13
- 'desktop' => %w[win mac linux],
14
- 'smartphone' => ['android'],
15
- 'tablet' => ['android']
16
- }.freeze
17
-
18
- OS_DEVICE_TYPE = {
19
- 'win' => ['desktop'],
20
- 'linux' => ['desktop'],
21
- 'mac' => ['desktop'],
22
- 'android' => %w[smartphone tablet]
23
- }.freeze
24
-
25
- DEVICE_TYPE_NAVIGATOR = {
26
- 'desktop' => %w[chrome firefox ie],
27
- 'smartphone' => %w[firefox chrome],
28
- 'tablet' => %w[firefox chrome]
29
- }.freeze
30
-
31
- NAVIGATOR_DEVICE_TYPE = {
32
- 'ie' => ['desktop'],
33
- 'chrome' => %w[desktop smartphone tablet],
34
- 'firefox' => %w[desktop smartphone tablet]
35
- }.freeze
36
-
37
- OS_PLATFORM = {
38
- 'win' => [
39
- 'Windows NT 5.1', # Windows XP
40
- 'Windows NT 6.1', # Windows 7
41
- 'Windows NT 6.2', # Windows 8
42
- 'Windows NT 6.3', # Windows 8.1
43
- 'Windows NT 10.0' # Windows 10
44
- ],
45
- 'mac' => [
46
- 'Macintosh; Intel Mac OS X 10.8',
47
- 'Macintosh; Intel Mac OS X 10.9',
48
- 'Macintosh; Intel Mac OS X 10.10',
49
- 'Macintosh; Intel Mac OS X 10.11',
50
- 'Macintosh; Intel Mac OS X 10.12'
51
- ],
52
- 'linux' => [
53
- 'X11; Linux',
54
- 'X11; Ubuntu; Linux'
55
- ],
56
- 'android' => [
57
- 'Android 4.4', # 2013-10-31
58
- 'Android 4.4.1', # 2013-12-05
59
- 'Android 4.4.2', # 2013-12-09
60
- 'Android 4.4.3', # 2014-06-02
61
- 'Android 4.4.4', # 2014-06-19
62
- 'Android 5.0', # 2014-11-12
63
- 'Android 5.0.1', # 2014-12-02
64
- 'Android 5.0.2', # 2014-12-19
65
- 'Android 5.1', # 2015-03-09
66
- 'Android 5.1.1', # 2015-04-21
67
- 'Android 6.0', # 2015-10-05
68
- 'Android 6.0.1', # 2015-12-07
69
- 'Android 7.0', # 2016-08-22
70
- 'Android 7.1', # 2016-10-04
71
- 'Android 7.1.1' # 2016-12-05
72
- ]
73
- }.freeze
74
-
75
- OS_CPU = {
76
- 'win' => [
77
- '', # 32bit
78
- 'Win64; x64', # 64bit
79
- 'WOW64' # 32bit process on 64bit system
80
- ],
81
- 'linux' => [
82
- 'i686', # 32bit
83
- 'x86_64', # 64bit
84
- 'i686 on x86_64' # 32bit process on 64bit system
85
- ],
86
- 'mac' => [
87
- ''
88
- ],
89
- 'android' => [
90
- 'armv7l', # 32bit
91
- 'armv8l' # 64bit
92
- ]
93
- }.freeze
94
-
95
- OS_NAVIGATOR = {
96
- 'win' => %w[chrome firefox ie],
97
- 'mac' => %w[firefox chrome],
98
- 'linux' => %w[chrome firefox],
99
- 'android' => %w[firefox chrome]
100
- }.freeze
101
-
102
- NAVIGATOR_OS = {
103
- 'chrome' => %w[win linux mac android],
104
- 'firefox' => %w[win linux mac android],
105
- 'ie' => ['win']
106
- }.freeze
107
-
108
- FIREFOX_VERSION = [
109
- ['45.0', Time.new(2016, 3, 8)],
110
- ['46.0', Time.new(2016, 4, 26)],
111
- ['47.0', Time.new(2016, 6, 7)],
112
- ['48.0', Time.new(2016, 8, 2)],
113
- ['49.0', Time.new(2016, 9, 20)],
114
- ['50.0', Time.new(2016, 11, 15)],
115
- ['51.0', Time.new(2017, 1, 24)]
116
- ]
117
-
118
- CHROME_BUILD = '80.0.3987.132
119
- 80.0.3987.149
120
- 80.0.3987.99
121
- 81.0.4044.117
122
- 81.0.4044.138
123
- 83.0.4103.101
124
- 83.0.4103.106
125
- 83.0.4103.96
126
- 84.0.4147.105
127
- 84.0.4147.111
128
- 84.0.4147.125
129
- 84.0.4147.135
130
- 84.0.4147.89
131
- 85.0.4183.101
132
- 85.0.4183.102
133
- 85.0.4183.120
134
- 85.0.4183.121
135
- 85.0.4183.127
136
- 85.0.4183.81
137
- 85.0.4183.83
138
- 86.0.4240.110
139
- 86.0.4240.111
140
- 86.0.4240.114
141
- 86.0.4240.183
142
- 86.0.4240.185
143
- 86.0.4240.75
144
- 86.0.4240.78
145
- 86.0.4240.80
146
- 86.0.4240.96
147
- 86.0.4240.99
148
- '.strip.split(/\n+/)
149
-
150
- IE_VERSION = [
151
- # [numeric ver, string ver, trident ver] # release year
152
- [8, 'MSIE 8.0', '4.0'], # 2009
153
- [9, 'MSIE 9.0', '5.0'], # 2011
154
- [10, 'MSIE 10.0', '6.0'], # 2012
155
- [11, 'MSIE 11.0', '7.0'] # 2013
156
- ].freeze
157
-
158
- MACOSX_CHROME_BUILD_RANGE = {
159
- # https://en.wikipedia.org/wiki/MacOS#Release_history
160
- '10.8' => [0, 8],
161
- '10.9' => [0, 5],
162
- '10.10' => [0, 5],
163
- '10.11' => [0, 6],
164
- '10.12' => [0, 2]
165
- }.freeze
166
-
167
- def user_agent_template(tpl_name, system, app)
168
- case tpl_name
169
- when 'firefox'
170
- "Mozilla/5.0 (#{system['ua_platform']}; rv:#{app['build_version']}) Gecko/#{app['gecko_trail']} Firefox/#{app['build_version']}"
171
- when 'chrome'
172
- "Mozilla/5.0 (#{system['ua_platform']}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/#{app['build_version']} Safari/537.36"
173
- when 'chrome_smartphone'
174
- "Mozilla/5.0 (#{system['ua_platform']}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/#{app['build_version']} Mobile Safari/537.36"
175
- when 'chrome_tablet'
176
- "Mozilla/5.0(#{system['ua_platform']}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/#{app['build_version']} Safari/537.36"
177
- when 'ie_less_11'
178
- "Mozilla/5.0 (compatible; #{app['build_version']}; #{system['ua_platform']}; Trident/#{app['trident_version']})"
179
- when 'ie_11'
180
- "Mozilla/5.0 (#{system['ua_platform']}; Trident/#{app['trident_version']}; rv:11.0) like Gecko"
181
- end
182
- end
183
-
184
- def firefox_build
185
- firefox_ver = FIREFOX_VERSION.sample
186
- build_ver = firefox_ver[0]
187
- date_from = firefox_ver[1].to_i
188
-
189
- begin
190
- idx = FIREFOX_VERSION.index(firefox_ver)
191
- date_to = FIREFOX_VERSION.fetch(idx + 1)[1].to_i
192
- rescue IndexError
193
- date_to = date_from + 86_399
194
- end
195
- sec_range = date_to - date_from
196
- build_rnd_time = Time.at(date_from + rand(sec_range))
197
-
198
- [build_ver, build_rnd_time.strftime('%Y%m%d%H%M%S')]
199
- end
200
-
201
- def chrome_build
202
- CHROME_BUILD.sample
203
- end
204
-
205
- def ie_build
206
- IE_VERSION.sample
207
- end
208
-
209
- def fix_chrome_mac_platform(platform)
210
- ver = platform.split('OS X ')[1]
211
- build_range = 0...MACOSX_CHROME_BUILD_RANGE[ver][1]
212
- build = rand(build_range)
213
- mac_version = "#{ver.sub('.', '_')}_#{build}"
214
-
215
- "Macintosh; Intel Mac OS X #{mac_version}"
216
- end
217
-
218
- def build_system_components(device_type, os_id, navigator_id)
219
- case os_id
220
- when 'win'
221
- platform_version = OS_PLATFORM['win'].sample.to_s
222
- cpu = OS_CPU['win'].sample
223
- platform = if cpu
224
- "#{platform_version}; #{cpu}"
225
- else
226
- platform_version.to_s
227
- end
228
- res = {
229
- 'platform_version' => platform_version,
230
- 'platform' => platform,
231
- 'ua_platform' => platform,
232
- 'os_cpu' => platform
233
- }
234
- when 'linux'
235
- cpu = OS_CPU['linux'].sample
236
- platform_version = OS_PLATFORM['linux'].sample
237
- platform = "#{platform_version}; #{cpu}"
238
- res = {
239
- 'platform_version' => platform_version,
240
- 'platform' => platform,
241
- 'ua_platform' => platform,
242
- 'os_cpu' => "Linux #{cpu}"
243
- }
244
- when 'mac'
245
- cpu = OS_CPU['mac'].sample
246
- platform_version = OS_PLATFORM['mac'].sample
247
- platform = platform_version
248
- platform = fix_chrome_mac_platform(platform) if navigator_id == 'chrome'
249
- res = {
250
- 'platform_version' => platform_version.to_s,
251
- 'platform' => 'MacIntel',
252
- 'ua_platform' => platform.to_s,
253
- 'os_cpu' => "Intel Mac OS X #{platform.split(' ')[-1]}"
254
- }
255
- when 'android'
256
- raise "#{navigator_id} not firefox or chrome" unless %w[firefox chrome].include? navigator_id
257
- raise "#{device_type} not smartphone or tablet" unless %w[smartphone tablet].include? device_type
258
-
259
- platform_version = OS_PLATFORM['android'].sample
260
- case navigator_id
261
- when 'firefox'
262
- case device_type
263
- when 'smartphone'
264
- ua_platform = "#{platform_version}; Mobile"
265
- when 'tablet'
266
- ua_platform = "#{platform_version}; Tablet"
267
- end
268
- when 'chrome'
269
- device_id = SMARTPHONE_DEV_IDS.sample
270
- ua_platform = "Linux; #{platform_version}; #{device_id}"
271
- end
272
- os_cpu = "Linux #{OS_CPU['android'].sample}"
273
- res = {
274
- 'platform_version' => platform_version,
275
- 'ua_platform' => ua_platform,
276
- 'platform' => os_cpu,
277
- 'os_cpu' => os_cpu
278
- }
279
- end
280
- res
281
- end
282
-
283
- def build_app_components(os_id, navigator_id)
284
- case navigator_id
285
- when 'firefox'
286
- build_version, build_id = firefox_build
287
- gecko_trail = if %w[win linux mac].include? os_id
288
- '20100101'
289
- else
290
- build_version
291
- end
292
- res = {
293
- 'name' => 'Netscape',
294
- 'product_sub' => '20100101',
295
- 'vendor' => '',
296
- 'build_version' => build_version,
297
- 'build_id' => build_id,
298
- 'gecko_trail' => gecko_trail
299
- }
300
- when 'chrome'
301
- res = {
302
- 'name' => 'Netscape',
303
- 'product_sub' => '20030107',
304
- 'vendor' => 'Google Inc.',
305
- 'build_version' => chrome_build,
306
- 'build_id' => ''
307
- }
308
- when 'ie'
309
- num_ver, build_version, trident_version = ie_build
310
- app_name = if num_ver >= 11
311
- 'Netscape'
312
- else
313
- 'Microsoft Internet Explorer'
314
- end
315
- res = {
316
- 'name' => app_name,
317
- 'product_sub' => '',
318
- 'vendor' => '',
319
- 'build_version' => build_version.to_s,
320
- 'build_id' => '',
321
- 'trident_version' => trident_version
322
- }
323
- end
324
- res
325
- end
326
-
327
- def option_choices(opt_title, opt_value, default_value, all_choices)
328
- choices = []
329
- if opt_value.instance_of? String
330
- choices = [opt_value]
331
- elsif opt_value.instance_of? Array
332
- choices = opt_value
333
- elsif opt_value.nil?
334
- choices = default_value
335
- else
336
- raise InvalidOption, "Option #{opt_title} has invalid value: #{opt_value}"
337
- end
338
-
339
- choices = all_choices if choices.include? 'all'
340
-
341
- choices.each do |item|
342
- raise InvalidOption, "Choices of option #{opt_title} contains invalid item: #{item}" unless all_choices.include? item
343
- end
344
- choices
345
- end
346
-
347
- def pick_config_ids(device_type, os, navigator)
348
- default_dev_types = if os.nil?
349
- ['desktop']
350
- else
351
- DEVICE_TYPE_OS.keys
352
- end
353
- dev_type_choices = option_choices(
354
- 'device_type',
355
- device_type,
356
- default_dev_types,
357
- DEVICE_TYPE_OS.keys
358
- )
359
- os_choices = option_choices(
360
- 'os',
361
- os,
362
- OS_NAVIGATOR.keys,
363
- OS_NAVIGATOR.keys
364
- )
365
- navigator_choices = option_choices(
366
- 'navigator',
367
- navigator,
368
- NAVIGATOR_OS.keys,
369
- NAVIGATOR_OS.keys
370
- )
371
- variants = []
372
-
373
- prod = dev_type_choices.product(os_choices, navigator_choices)
374
- prod.each do |dev, os_, nav|
375
- if (DEVICE_TYPE_OS[dev].include? os_) && (DEVICE_TYPE_NAVIGATOR[dev].include? nav) && (OS_NAVIGATOR[os_].include? nav)
376
- variants << [dev, os_, nav]
377
- end
378
- end
379
-
380
- raise InvalidOption 'Options device_type, os and navigator conflicts with each other' if variants.nil?
381
-
382
- device_type, os_id, navigator_id = variants.sample
383
-
384
- raise InvalidOption, 'os_id not in OS_PLATFORM' unless OS_PLATFORM.include? os_id
385
- raise InvalidOption, 'navigator_id not in NAVIGATOR_OS' unless NAVIGATOR_OS.include? navigator_id
386
- raise InvalidOption, 'navigator_id not in DEVICE_TYPE_IDS' unless DEVICE_TYPE_OS.include? device_type
387
-
388
- [device_type, os_id, navigator_id]
389
- end
390
-
391
- def choose_ua_template(device_type, navigator_id, app, sys)
392
- tpl_name = navigator_id
393
- case navigator_id
394
- when 'ie'
395
- tpl_name = if app['build_version'] == 'MSIE 11.0'
396
- 'ie_11'
397
- else
398
- 'ie_less_11'
399
- end
400
- when 'chrome'
401
- tpl_name = case device_type
402
- when 'smartphone'
403
- 'chrome_smartphone'
404
- when 'tablet'
405
- 'chrome_tablet'
406
- else
407
- 'chrome'
408
- end
409
- end
410
- user_agent_template(tpl_name, sys, app)
411
- end
412
-
413
- def build_navigator_app_version(os_id, navigator_id, platform_version, user_agent)
414
- if %w[chrome ie].include? navigator_id
415
- raise 'User agent doesn\'t start with "Mozilla/"' unless user_agent.to_s.start_with? 'Mozilla/'
416
-
417
- app_version = user_agent.split('Mozilla/')[1]
418
- elsif navigator_id == 'firefox'
419
- if os_id == 'android'
420
- app_version = "5.0 (#{platform_version})"
421
- else
422
- os_token = {
423
- 'win' => 'Windows',
424
- 'mac' => 'Macintosh',
425
- 'linux' => 'X11'
426
- }[os_id]
427
- app_version = "5.0 (#{os_token})"
428
- end
429
- end
430
- app_version
431
- end
432
-
433
- def generate_navigator(os: nil, navigator: nil, platform: nil, device_type: nil)
434
- unless platform.nil?
435
- os = platform
436
- warn('The `platform` version is deprecated. Use `os` option instead', uplevel: 3)
437
- end
438
- device_type, os_id, navigator_id = pick_config_ids(device_type, os, navigator)
439
- sys = build_system_components(device_type, os_id, navigator_id)
440
- app = build_app_components(os_id, navigator_id)
441
- user_agent = choose_ua_template(device_type, navigator_id, app, sys)
442
- app_version = build_navigator_app_version(
443
- os_id, navigator_id, sys['platform_version'], user_agent
444
- )
445
- {
446
- 'os_id' => os_id,
447
- 'navigator_id' => navigator_id,
448
- 'platform' => sys['platform'],
449
- 'os_cpu' => sys['os_cpu'],
450
- 'build_version' => app['build_version'],
451
- 'build_id' => app['build_id'],
452
- 'app_version' => app_version,
453
- 'app_name' => app['name'],
454
- 'app_code_name' => 'Mozilla',
455
- 'product' => 'Gecko',
456
- 'product_sub' => app['product_sub'],
457
- 'vendor' => app['vendor'],
458
- 'vendor_sub' => '',
459
- 'user_agent' => user_agent
460
- }
461
- end
462
-
463
- public
4
+ public
464
5
 
465
6
  def generate_user_agent(os: nil, navigator: nil, platform: nil, device_type: nil)
466
- generate_navigator(os: os, navigator: navigator, platform: platform, device_type: device_type)['user_agent']
7
+ Core.generate_navigator(os: os, navigator: navigator, platform: platform, device_type: device_type)['user_agent']
467
8
  end
468
9
 
469
10
  def generate_navigator_js(os: nil, navigator: nil, platform: nil, device_type: nil)
470
- config = generate_navigator(os: os, navigator: navigator, platform: platform, device_type: device_type)
11
+ config = Core.generate_navigator(os: os, navigator: navigator, platform: platform, device_type: device_type)
471
12
  {
472
13
  'appCodeName' => config['app_code_name'],
473
14
  'appName' => config['app_name'],
@@ -481,4 +22,4 @@ def generate_navigator_js(os: nil, navigator: nil, platform: nil, device_type: n
481
22
  'vendorSub' => config['vendor_sub'],
482
23
  'buildID' => config['build_id']
483
24
  }
484
- end
25
+ end
@@ -0,0 +1,487 @@
1
+ require 'time'
2
+ require 'json'
3
+ require 'pp'
4
+
5
+ require_relative 'error'
6
+ require_relative 'device'
7
+
8
+ module Core
9
+
10
+ DEVICE_TYPE_OS = {
11
+ 'desktop' => %w[win mac linux],
12
+ 'smartphone' => ['android'],
13
+ 'tablet' => ['android']
14
+ }.freeze
15
+
16
+ OS_DEVICE_TYPE = {
17
+ 'win' => ['desktop'],
18
+ 'linux' => ['desktop'],
19
+ 'mac' => ['desktop'],
20
+ 'android' => %w[smartphone tablet]
21
+ }.freeze
22
+
23
+ DEVICE_TYPE_NAVIGATOR = {
24
+ 'desktop' => %w[chrome firefox ie],
25
+ 'smartphone' => %w[firefox chrome],
26
+ 'tablet' => %w[firefox chrome]
27
+ }.freeze
28
+
29
+ NAVIGATOR_DEVICE_TYPE = {
30
+ 'ie' => ['desktop'],
31
+ 'chrome' => %w[desktop smartphone tablet],
32
+ 'firefox' => %w[desktop smartphone tablet]
33
+ }.freeze
34
+
35
+ OS_PLATFORM = {
36
+ 'win' => [
37
+ 'Windows NT 5.1', # Windows XP
38
+ 'Windows NT 6.1', # Windows 7
39
+ 'Windows NT 6.2', # Windows 8
40
+ 'Windows NT 6.3', # Windows 8.1
41
+ 'Windows NT 10.0' # Windows 10
42
+ ],
43
+ 'mac' => [
44
+ 'Macintosh; Intel Mac OS X 10.8',
45
+ 'Macintosh; Intel Mac OS X 10.9',
46
+ 'Macintosh; Intel Mac OS X 10.10',
47
+ 'Macintosh; Intel Mac OS X 10.11',
48
+ 'Macintosh; Intel Mac OS X 10.12'
49
+ ],
50
+ 'linux' => [
51
+ 'X11; Linux',
52
+ 'X11; Ubuntu; Linux'
53
+ ],
54
+ 'android' => [
55
+ 'Android 4.4', # 2013-10-31
56
+ 'Android 4.4.1', # 2013-12-05
57
+ 'Android 4.4.2', # 2013-12-09
58
+ 'Android 4.4.3', # 2014-06-02
59
+ 'Android 4.4.4', # 2014-06-19
60
+ 'Android 5.0', # 2014-11-12
61
+ 'Android 5.0.1', # 2014-12-02
62
+ 'Android 5.0.2', # 2014-12-19
63
+ 'Android 5.1', # 2015-03-09
64
+ 'Android 5.1.1', # 2015-04-21
65
+ 'Android 6.0', # 2015-10-05
66
+ 'Android 6.0.1', # 2015-12-07
67
+ 'Android 7.0', # 2016-08-22
68
+ 'Android 7.1', # 2016-10-04
69
+ 'Android 7.1.1' # 2016-12-05
70
+ ]
71
+ }.freeze
72
+
73
+ OS_CPU = {
74
+ 'win' => [
75
+ '', # 32bit
76
+ 'Win64; x64', # 64bit
77
+ 'WOW64' # 32bit process on 64bit system
78
+ ],
79
+ 'linux' => [
80
+ 'i686', # 32bit
81
+ 'x86_64', # 64bit
82
+ 'i686 on x86_64' # 32bit process on 64bit system
83
+ ],
84
+ 'mac' => [
85
+ ''
86
+ ],
87
+ 'android' => [
88
+ 'armv7l', # 32bit
89
+ 'armv8l' # 64bit
90
+ ]
91
+ }.freeze
92
+
93
+ OS_NAVIGATOR = {
94
+ 'win' => %w[chrome firefox ie],
95
+ 'mac' => %w[firefox chrome],
96
+ 'linux' => %w[chrome firefox],
97
+ 'android' => %w[firefox chrome]
98
+ }.freeze
99
+
100
+ NAVIGATOR_OS = {
101
+ 'chrome' => %w[win linux mac android],
102
+ 'firefox' => %w[win linux mac android],
103
+ 'ie' => ['win']
104
+ }.freeze
105
+
106
+ FIREFOX_VERSION = [
107
+ ['45.0', Time.new(2016, 3, 8)],
108
+ ['46.0', Time.new(2016, 4, 26)],
109
+ ['47.0', Time.new(2016, 6, 7)],
110
+ ['48.0', Time.new(2016, 8, 2)],
111
+ ['49.0', Time.new(2016, 9, 20)],
112
+ ['50.0', Time.new(2016, 11, 15)],
113
+ ['51.0', Time.new(2017, 1, 24)]
114
+ ]
115
+
116
+ CHROME_BUILD = '80.0.3987.132
117
+ 80.0.3987.149
118
+ 80.0.3987.99
119
+ 81.0.4044.117
120
+ 81.0.4044.138
121
+ 83.0.4103.101
122
+ 83.0.4103.106
123
+ 83.0.4103.96
124
+ 84.0.4147.105
125
+ 84.0.4147.111
126
+ 84.0.4147.125
127
+ 84.0.4147.135
128
+ 84.0.4147.89
129
+ 85.0.4183.101
130
+ 85.0.4183.102
131
+ 85.0.4183.120
132
+ 85.0.4183.121
133
+ 85.0.4183.127
134
+ 85.0.4183.81
135
+ 85.0.4183.83
136
+ 86.0.4240.110
137
+ 86.0.4240.111
138
+ 86.0.4240.114
139
+ 86.0.4240.183
140
+ 86.0.4240.185
141
+ 86.0.4240.75
142
+ 86.0.4240.78
143
+ 86.0.4240.80
144
+ 86.0.4240.96
145
+ 86.0.4240.99
146
+ '.strip.split(/\n+/)
147
+
148
+ IE_VERSION = [
149
+ # [numeric ver, string ver, trident ver] # release year
150
+ [8, 'MSIE 8.0', '4.0'], # 2009
151
+ [9, 'MSIE 9.0', '5.0'], # 2011
152
+ [10, 'MSIE 10.0', '6.0'], # 2012
153
+ [11, 'MSIE 11.0', '7.0'] # 2013
154
+ ].freeze
155
+
156
+ MACOSX_CHROME_BUILD_RANGE = {
157
+ # https://en.wikipedia.org/wiki/MacOS#Release_history
158
+ '10.8' => [0, 8],
159
+ '10.9' => [0, 5],
160
+ '10.10' => [0, 5],
161
+ '10.11' => [0, 6],
162
+ '10.12' => [0, 2]
163
+ }.freeze
164
+
165
+ def self.user_agent_template(tpl_name, system, app)
166
+ case tpl_name
167
+ when 'firefox'
168
+ "Mozilla/5.0 (#{system['ua_platform']}; rv:#{app['build_version']}) Gecko/#{app['gecko_trail']} Firefox/#{app['build_version']}"
169
+ when 'chrome'
170
+ "Mozilla/5.0 (#{system['ua_platform']}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/#{app['build_version']} Safari/537.36"
171
+ when 'chrome_smartphone'
172
+ "Mozilla/5.0 (#{system['ua_platform']}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/#{app['build_version']} Mobile Safari/537.36"
173
+ when 'chrome_tablet'
174
+ "Mozilla/5.0(#{system['ua_platform']}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/#{app['build_version']} Safari/537.36"
175
+ when 'ie_less_11'
176
+ "Mozilla/5.0 (compatible; #{app['build_version']}; #{system['ua_platform']}; Trident/#{app['trident_version']})"
177
+ when 'ie_11'
178
+ "Mozilla/5.0 (#{system['ua_platform']}; Trident/#{app['trident_version']}; rv:11.0) like Gecko"
179
+ end
180
+ end
181
+
182
+ def self.firefox_build
183
+ firefox_ver = FIREFOX_VERSION.sample
184
+ build_ver = firefox_ver[0]
185
+ date_from = firefox_ver[1].to_i
186
+
187
+ begin
188
+ idx = FIREFOX_VERSION.index(firefox_ver)
189
+ date_to = FIREFOX_VERSION.fetch(idx + 1)[1].to_i
190
+ rescue IndexError
191
+ date_to = date_from + 86_399
192
+ end
193
+ sec_range = date_to - date_from
194
+ build_rnd_time = Time.at(date_from + rand(sec_range))
195
+
196
+ [build_ver, build_rnd_time.strftime('%Y%m%d%H%M%S')]
197
+ end
198
+
199
+ def self.chrome_build
200
+ CHROME_BUILD.sample
201
+ end
202
+
203
+ def self.ie_build
204
+ IE_VERSION.sample
205
+ end
206
+
207
+ def self.fix_chrome_mac_platform(platform)
208
+ ver = platform.split('OS X ')[1]
209
+ build_range = 0...MACOSX_CHROME_BUILD_RANGE[ver][1]
210
+ build = rand(build_range)
211
+ mac_version = "#{ver.sub('.', '_')}_#{build}"
212
+
213
+ "Macintosh; Intel Mac OS X #{mac_version}"
214
+ end
215
+
216
+ def self.build_system_components(device_type, os_id, navigator_id)
217
+ case os_id
218
+ when 'win'
219
+ platform_version = OS_PLATFORM['win'].sample.to_s
220
+ cpu = OS_CPU['win'].sample
221
+ platform = if cpu
222
+ "#{platform_version}; #{cpu}"
223
+ else
224
+ platform_version.to_s
225
+ end
226
+ res = {
227
+ 'platform_version' => platform_version,
228
+ 'platform' => platform,
229
+ 'ua_platform' => platform,
230
+ 'os_cpu' => platform
231
+ }
232
+ when 'linux'
233
+ cpu = OS_CPU['linux'].sample
234
+ platform_version = OS_PLATFORM['linux'].sample
235
+ platform = "#{platform_version}; #{cpu}"
236
+ res = {
237
+ 'platform_version' => platform_version,
238
+ 'platform' => platform,
239
+ 'ua_platform' => platform,
240
+ 'os_cpu' => "Linux #{cpu}"
241
+ }
242
+ when 'mac'
243
+ cpu = OS_CPU['mac'].sample
244
+ platform_version = OS_PLATFORM['mac'].sample
245
+ platform = platform_version
246
+ platform = fix_chrome_mac_platform(platform) if navigator_id == 'chrome'
247
+ res = {
248
+ 'platform_version' => platform_version.to_s,
249
+ 'platform' => 'MacIntel',
250
+ 'ua_platform' => platform.to_s,
251
+ 'os_cpu' => "Intel Mac OS X #{platform.split(' ')[-1]}"
252
+ }
253
+ when 'android'
254
+ raise "#{navigator_id} not firefox or chrome" unless %w[firefox chrome].include? navigator_id
255
+ raise "#{device_type} not smartphone or tablet" unless %w[smartphone tablet].include? device_type
256
+
257
+ platform_version = OS_PLATFORM['android'].sample
258
+ case navigator_id
259
+ when 'firefox'
260
+ case device_type
261
+ when 'smartphone'
262
+ ua_platform = "#{platform_version}; Mobile"
263
+ when 'tablet'
264
+ ua_platform = "#{platform_version}; Tablet"
265
+ end
266
+ when 'chrome'
267
+ device_id = SMARTPHONE_DEV_IDS.sample
268
+ ua_platform = "Linux; #{platform_version}; #{device_id}"
269
+ end
270
+ os_cpu = "Linux #{OS_CPU['android'].sample}"
271
+ res = {
272
+ 'platform_version' => platform_version,
273
+ 'ua_platform' => ua_platform,
274
+ 'platform' => os_cpu,
275
+ 'os_cpu' => os_cpu
276
+ }
277
+ end
278
+ res
279
+ end
280
+
281
+ def self.build_app_components(os_id, navigator_id)
282
+ case navigator_id
283
+ when 'firefox'
284
+ build_version, build_id = firefox_build
285
+ gecko_trail = if %w[win linux mac].include? os_id
286
+ '20100101'
287
+ else
288
+ build_version
289
+ end
290
+ res = {
291
+ 'name' => 'Netscape',
292
+ 'product_sub' => '20100101',
293
+ 'vendor' => '',
294
+ 'build_version' => build_version,
295
+ 'build_id' => build_id,
296
+ 'gecko_trail' => gecko_trail
297
+ }
298
+ when 'chrome'
299
+ res = {
300
+ 'name' => 'Netscape',
301
+ 'product_sub' => '20030107',
302
+ 'vendor' => 'Google Inc.',
303
+ 'build_version' => chrome_build,
304
+ 'build_id' => ''
305
+ }
306
+ when 'ie'
307
+ num_ver, build_version, trident_version = ie_build
308
+ app_name = if num_ver >= 11
309
+ 'Netscape'
310
+ else
311
+ 'Microsoft Internet Explorer'
312
+ end
313
+ res = {
314
+ 'name' => app_name,
315
+ 'product_sub' => '',
316
+ 'vendor' => '',
317
+ 'build_version' => build_version.to_s,
318
+ 'build_id' => '',
319
+ 'trident_version' => trident_version
320
+ }
321
+ end
322
+ res
323
+ end
324
+
325
+ def self.option_choices(opt_title, opt_value, default_value, all_choices)
326
+ choices = []
327
+ if opt_value.instance_of? String
328
+ choices = [opt_value]
329
+ elsif opt_value.instance_of? Array
330
+ choices = opt_value
331
+ elsif opt_value.nil?
332
+ choices = default_value
333
+ else
334
+ raise InvalidOption, "Option #{opt_title} has invalid value: #{opt_value}"
335
+ end
336
+
337
+ choices = all_choices if choices.include? 'all'
338
+
339
+ choices.each do |item|
340
+ raise InvalidOption, "Choices of option #{opt_title} contains invalid item: #{item}" unless all_choices.include? item
341
+ end
342
+ choices
343
+ end
344
+
345
+ def self.pick_config_ids(device_type, os, navigator)
346
+ default_dev_types = if os.nil?
347
+ ['desktop']
348
+ else
349
+ DEVICE_TYPE_OS.keys
350
+ end
351
+ dev_type_choices = option_choices(
352
+ 'device_type',
353
+ device_type,
354
+ default_dev_types,
355
+ DEVICE_TYPE_OS.keys
356
+ )
357
+ os_choices = option_choices(
358
+ 'os',
359
+ os,
360
+ OS_NAVIGATOR.keys,
361
+ OS_NAVIGATOR.keys
362
+ )
363
+ navigator_choices = option_choices(
364
+ 'navigator',
365
+ navigator,
366
+ NAVIGATOR_OS.keys,
367
+ NAVIGATOR_OS.keys
368
+ )
369
+ variants = []
370
+
371
+ prod = dev_type_choices.product(os_choices, navigator_choices)
372
+ prod.each do |dev, os_, nav|
373
+ if (DEVICE_TYPE_OS[dev].include? os_) && (DEVICE_TYPE_NAVIGATOR[dev].include? nav) && (OS_NAVIGATOR[os_].include? nav)
374
+ variants << [dev, os_, nav]
375
+ end
376
+ end
377
+
378
+ raise InvalidOption 'Options device_type, os and navigator conflicts with each other' if variants.nil?
379
+
380
+ device_type, os_id, navigator_id = variants.sample
381
+
382
+ raise InvalidOption, 'os_id not in OS_PLATFORM' unless OS_PLATFORM.include? os_id
383
+ raise InvalidOption, 'navigator_id not in NAVIGATOR_OS' unless NAVIGATOR_OS.include? navigator_id
384
+ raise InvalidOption, 'navigator_id not in DEVICE_TYPE_IDS' unless DEVICE_TYPE_OS.include? device_type
385
+
386
+ [device_type, os_id, navigator_id]
387
+ end
388
+
389
+ def self.choose_ua_template(device_type, navigator_id, app, sys)
390
+ tpl_name = navigator_id
391
+ case navigator_id
392
+ when 'ie'
393
+ tpl_name = if app['build_version'] == 'MSIE 11.0'
394
+ 'ie_11'
395
+ else
396
+ 'ie_less_11'
397
+ end
398
+ when 'chrome'
399
+ tpl_name = case device_type
400
+ when 'smartphone'
401
+ 'chrome_smartphone'
402
+ when 'tablet'
403
+ 'chrome_tablet'
404
+ else
405
+ 'chrome'
406
+ end
407
+ end
408
+ user_agent_template(tpl_name, sys, app)
409
+ end
410
+
411
+ def self.build_navigator_app_version(os_id, navigator_id, platform_version, user_agent)
412
+ if %w[chrome ie].include? navigator_id
413
+ raise 'User agent doesn\'t start with "Mozilla/"' unless user_agent.to_s.start_with? 'Mozilla/'
414
+
415
+ app_version = user_agent.split('Mozilla/')[1]
416
+ elsif navigator_id == 'firefox'
417
+ if os_id == 'android'
418
+ app_version = "5.0 (#{platform_version})"
419
+ else
420
+ os_token = {
421
+ 'win' => 'Windows',
422
+ 'mac' => 'Macintosh',
423
+ 'linux' => 'X11'
424
+ }[os_id]
425
+ app_version = "5.0 (#{os_token})"
426
+ end
427
+ end
428
+ app_version
429
+ end
430
+
431
+ def self.generate_navigator(os: nil, navigator: nil, platform: nil, device_type: nil)
432
+ unless platform.nil?
433
+ os = platform
434
+ warn('The `platform` version is deprecated. Use `os` option instead', uplevel: 3)
435
+ end
436
+ device_type, os_id, navigator_id = pick_config_ids(device_type, os, navigator)
437
+ sys = build_system_components(device_type, os_id, navigator_id)
438
+ app = build_app_components(os_id, navigator_id)
439
+ user_agent = choose_ua_template(device_type, navigator_id, app, sys)
440
+ app_version = build_navigator_app_version(
441
+ os_id, navigator_id, sys['platform_version'], user_agent
442
+ )
443
+ {
444
+ 'os_id' => os_id,
445
+ 'navigator_id' => navigator_id,
446
+ 'platform' => sys['platform'],
447
+ 'os_cpu' => sys['os_cpu'],
448
+ 'build_version' => app['build_version'],
449
+ 'build_id' => app['build_id'],
450
+ 'app_version' => app_version,
451
+ 'app_name' => app['name'],
452
+ 'app_code_name' => 'Mozilla',
453
+ 'product' => 'Gecko',
454
+ 'product_sub' => app['product_sub'],
455
+ 'vendor' => app['vendor'],
456
+ 'vendor_sub' => '',
457
+ 'user_agent' => user_agent
458
+ }
459
+ end
460
+
461
+ private_class_method(
462
+ :chrome_build,
463
+ :user_agent_template,
464
+ :build_navigator_app_version,
465
+ :build_app_components,
466
+ :build_system_components,
467
+ :choose_ua_template,
468
+ :pick_config_ids,
469
+ :option_choices,
470
+ :ie_build,
471
+ :firefox_build,
472
+ :fix_chrome_mac_platform
473
+ )
474
+ private_constant(
475
+ :IE_VERSION,
476
+ :DEVICE_TYPE_OS,
477
+ :NAVIGATOR_OS,
478
+ :OS_PLATFORM,
479
+ :OS_NAVIGATOR,
480
+ :DEVICE_TYPE_NAVIGATOR,
481
+ :OS_CPU,
482
+ :MACOSX_CHROME_BUILD_RANGE,
483
+ :CHROME_BUILD,
484
+ :NAVIGATOR_DEVICE_TYPE,
485
+ :OS_DEVICE_TYPE
486
+ )
487
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'minitest/autorun'
4
4
  require './lib/fake_useragent'
5
+ require './lib/fake_useragent/core'
5
6
  require './lib/fake_useragent/error'
6
7
 
7
8
  class TestSuite < Minitest::Test
@@ -94,7 +95,7 @@ class TestSuite < Minitest::Test
94
95
 
95
96
  def test_data_integrity
96
97
  50.times do
97
- navigator = generate_navigator
98
+ navigator = Core.generate_navigator
98
99
  navigator.each_value do |value|
99
100
  assert(value.nil? || (value.instance_of? String))
100
101
  end
@@ -103,22 +104,22 @@ class TestSuite < Minitest::Test
103
104
 
104
105
  def test_feature_platform
105
106
  50.times do
106
- nav = generate_navigator(os: 'win')
107
+ nav = Core.generate_navigator(os: 'win')
107
108
  assert nav['platform'].include? 'Win'
108
- nav = generate_navigator(os: 'linux')
109
+ nav = Core.generate_navigator(os: 'linux')
109
110
  assert nav['platform'].include? 'Linux'
110
- nav = generate_navigator(os: 'mac')
111
+ nav = Core.generate_navigator(os: 'mac')
111
112
  assert nav['platform'].include? 'MacIntel'
112
113
  end
113
114
  end
114
115
 
115
116
  def test_feature_os_cpu
116
117
  10.times do
117
- nav = generate_navigator(os: 'win')
118
+ nav = Core.generate_navigator(os: 'win')
118
119
  assert nav['os_cpu'].include? 'Windows NT'
119
- nav = generate_navigator(os: 'linux')
120
+ nav = Core.generate_navigator(os: 'linux')
120
121
  assert nav['os_cpu'].include? 'Linux'
121
- nav = generate_navigator(os: 'mac')
122
+ nav = Core.generate_navigator(os: 'mac')
122
123
  assert nav['os_cpu'].include? 'Mac OS'
123
124
  end
124
125
  end
@@ -152,22 +153,22 @@ class TestSuite < Minitest::Test
152
153
 
153
154
  def test_build_id_no_firefox
154
155
  50.times do
155
- nav = generate_navigator(navigator: 'chrome')
156
+ nav = Core.generate_navigator(navigator: 'chrome')
156
157
  assert nav['build_id'] == ''
157
- nav = generate_navigator(navigator: 'ie')
158
+ nav = Core.generate_navigator(navigator: 'ie')
158
159
  assert nav['build_id'] == ''
159
160
  end
160
161
  end
161
162
 
162
163
  def test_build_id_firefox
163
- original_ff_version = FIREFOX_VERSION.clone
164
- FIREFOX_VERSION.replace [
164
+ original_ff_version = Core::FIREFOX_VERSION.clone
165
+ Core::FIREFOX_VERSION.replace [
165
166
  ['49.0', Time.new(2016, 9, 20)],
166
167
  ['50.0', Time.new(2016, 11, 15)]
167
168
  ].freeze
168
169
  begin
169
170
  50.times do
170
- nav = generate_navigator(navigator: 'firefox')
171
+ nav = Core.generate_navigator(navigator: 'firefox')
171
172
  assert nav['build_id'].length == 14
172
173
  if nav['user_agent'].include? '50.0'
173
174
  assert nav['build_id'].start_with? '20161115'
@@ -177,7 +178,7 @@ class TestSuite < Minitest::Test
177
178
  end
178
179
  end
179
180
  ensure
180
- FIREFOX_VERSION.replace original_ff_version
181
+ Core::FIREFOX_VERSION.replace original_ff_version
181
182
  end
182
183
  end
183
184
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fake_useragent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bokanov Alexander
@@ -24,6 +24,7 @@ files:
24
24
  - Rakefile
25
25
  - fake_useragent.gemspec
26
26
  - lib/fake_useragent.rb
27
+ - lib/fake_useragent/core.rb
27
28
  - lib/fake_useragent/data/smartphone_dev_ext.json
28
29
  - lib/fake_useragent/data/smartphone_dev_id.json
29
30
  - lib/fake_useragent/data/tablet_dev_ext.json