fake_useragent 1.0.0 → 1.0.2

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