device_detector 1.1.0 → 1.1.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.
@@ -58,7 +58,29 @@ class DeviceDetector
58
58
  end
59
59
 
60
60
  def load_regexes(file_paths)
61
- file_paths.map { |path, full_path| [path, symbolize_keys!(YAML.load_file(full_path))] }
61
+ file_paths.map do |path, full_path|
62
+ object = YAML.load_file(full_path)
63
+ object = rewrite_device_object!(object) if is_device_yml_file?(full_path)
64
+ object = rewrite_vendor_object!(object) if is_vendor_yml_file?(full_path)
65
+
66
+ [path, symbolize_keys!(object)]
67
+ end
68
+ end
69
+
70
+ def is_device_yml_file?(file_path)
71
+ file_path.include?('/regexes/device/')
72
+ end
73
+
74
+ def is_vendor_yml_file?(file_path)
75
+ file_path.include?('/regexes/vendorfragments')
76
+ end
77
+
78
+ def rewrite_vendor_object!(object)
79
+ object.map { |key, values| values.map { |v| { 'regex_name' => key, 'regex' => v } } }.flatten
80
+ end
81
+
82
+ def rewrite_device_object!(object)
83
+ object.map { |key, value| [key, { 'regex_name' => key }.merge!(value)] }.to_h
62
84
  end
63
85
 
64
86
  def symbolize_keys!(object)
@@ -88,8 +110,8 @@ class DeviceDetector
88
110
  Regexp.new('(?:^|[^A-Z0-9\-_]|[^A-Z0-9\-]_|sprd-|MZ-)(?:' + src + ')', Regexp::IGNORECASE)
89
111
  end
90
112
 
91
- def from_cache(key)
92
- DeviceDetector.cache.get_or_set(key) { yield }
113
+ def from_cache(key, &block)
114
+ DeviceDetector.cache.get_or_set(key, &block)
93
115
  end
94
116
  end
95
117
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ class DeviceDetector
6
+ class VendorFragment < Parser
7
+ def name
8
+ vendor_fragment_info
9
+ end
10
+
11
+ private
12
+
13
+ def vendor_fragment_info
14
+ from_cache(['vendor_fragment', self.class.name, user_agent]) do
15
+ return if regex_meta.nil? || regex_meta.empty?
16
+
17
+ regex_meta[:regex_name]
18
+ end
19
+ end
20
+
21
+ def filenames
22
+ ['vendorfragments.yml']
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DeviceDetector
4
- VERSION = '1.1.0'
4
+ VERSION = '1.1.1'
5
5
  end
@@ -15,13 +15,34 @@ require 'device_detector/device'
15
15
  require 'device_detector/os'
16
16
  require 'device_detector/browser'
17
17
  require 'device_detector/client_hint'
18
+ require 'device_detector/vendor_fragment'
18
19
 
19
20
  class DeviceDetector
20
21
  attr_reader :client_hint, :user_agent
21
22
 
22
23
  def initialize(user_agent, headers = nil)
23
24
  @client_hint = ClientHint.new(headers)
24
- @user_agent = user_agent
25
+ utf8_user_agent = encode_user_agent_if_needed(user_agent)
26
+ @user_agent = set_user_agent(utf8_user_agent)
27
+ end
28
+
29
+ # https://github.com/matomo-org/device-detector/blob/c235832dba13961ab0f71b681616baf1aa48de23/Parser/Device/AbstractDeviceParser.php#L1873
30
+ def set_user_agent(user_agent)
31
+ return user_agent if client_hint.model.nil?
32
+
33
+ regex = build_regex('Android 10[.\d]*; K(?: Build/|[;)])')
34
+ return user_agent unless user_agent =~ regex
35
+
36
+ version = client_hint.os_version || '10'
37
+
38
+ user_agent.gsub(regex, "Android #{version}, #{client_hint.model}")
39
+ end
40
+
41
+ def encode_user_agent_if_needed(user_agent)
42
+ return if user_agent.nil?
43
+ return user_agent if user_agent.encoding.name == 'UTF-8'
44
+
45
+ user_agent.encode('utf-8', 'binary', undef: :replace)
25
46
  end
26
47
 
27
48
  def name
@@ -53,13 +74,18 @@ class DeviceDetector
53
74
  end
54
75
 
55
76
  def device_name
77
+ return if fake_ua?
78
+
56
79
  device.name || client_hint.model || fix_for_x_music
57
80
  end
58
81
 
59
82
  def device_brand
83
+ return if fake_ua?
84
+
60
85
  # Assume all devices running iOS / Mac OS are from Apple
61
86
  brand = device.brand
62
87
  brand = 'Apple' if brand.nil? && ['Apple TV', 'iOS', 'Mac'].include?(os_name)
88
+
63
89
  brand
64
90
  end
65
91
 
@@ -206,6 +232,11 @@ class DeviceDetector
206
232
  @os ||= OS.new(user_agent)
207
233
  end
208
234
 
235
+ # https://github.com/matomo-org/device-detector/blob/827a3fab7e38c3274c18d2f5f5bc2a78b7ef4a3a/DeviceDetector.php#L921C5-L921C5
236
+ def fake_ua?
237
+ os_name == 'Android' && device.brand == 'Apple'
238
+ end
239
+
209
240
  # https://github.com/matomo-org/device-detector/blob/be1c9ef486c247dc4886668da5ed0b1c49d90ba8/Parser/Client/Browser.php#L772
210
241
  # Fix mobile browser names e.g. Chrome => Chrome Mobile
211
242
  def mobile_fix?
@@ -218,7 +249,7 @@ class DeviceDetector
218
249
 
219
250
  # Related to issue mentionned in device.rb#1562
220
251
  def fix_for_x_music
221
- user_agent.include?('X-music Ⅲ') ? 'X-Music III' : nil
252
+ user_agent&.include?('X-music Ⅲ') ? 'X-Music III' : nil
222
253
  end
223
254
 
224
255
  def skip_os_version?
data/regexes/bots.yml CHANGED
@@ -13,6 +13,14 @@
13
13
  name: 'Plesk International GmbH'
14
14
  url: 'https://www.plesk.com'
15
15
 
16
+ - regex: 'Cloudflare-Healthchecks'
17
+ name: 'Cloudflare Health Checks'
18
+ category: 'Service Agent'
19
+ url: 'https://developers.cloudflare.com/health-checks/'
20
+ producer:
21
+ name: 'CloudFlare'
22
+ url: 'http://www.cloudflare.com'
23
+
16
24
  - regex: '360Spider'
17
25
  name: '360Spider'
18
26
  category: 'Search bot'
@@ -704,7 +712,7 @@
704
712
  name: 'Visual Meta'
705
713
  url: 'https://www.shopalike.cz/'
706
714
 
707
- - regex: 'AdsBot-Google|Adwords-(DisplayAds|Express|Instant)|Google Web Preview|Google[ -]Publisher[ -]Plugin|Google-(Ads-Conversions|Ads-Qualify|Adwords|AMPHTML|Assess|HotelAdsVerifier|Read-Aloud|Shopping-Quality|Site-Verification|speakr|Stale-Content-Probe|Test|Youtube-Links)|(APIs|DuplexWeb|Feedfetcher|Mediapartners)-Google|Googlebot|Google(?:AdSenseInfeed|AssociationService|Producer)|Google.*/\+/web/snippet'
715
+ - regex: 'AdsBot-Google|Adwords-(DisplayAds|Express|Instant)|Google Web Preview|Google[ -]Publisher[ -]Plugin|Google-(Ads-Conversions|Ads-Qualify|Adwords|AMPHTML|Assess|HotelAdsVerifier|Read-Aloud|Shopping-Quality|Site-Verification|speakr|Stale-Content-Probe|Test|Youtube-Links)|(APIs|DuplexWeb|Feedfetcher|Mediapartners)-Google|Googlebot|Google(?:AdSenseInfeed|AssociationService|Prober|Producer)|Google.*/\+/web/snippet'
708
716
  name: 'Googlebot'
709
717
  category: 'Search bot'
710
718
  url: 'http://www.google.com/bot.html'
@@ -727,6 +735,11 @@
727
735
  name: 'HubSpot Inc.'
728
736
  url: 'https://www.hubspot.com'
729
737
 
738
+ - regex: 'vuhuvBot'
739
+ name: 'Vuhuv Bot'
740
+ category: 'Crawler'
741
+ url: 'http://vuhuv.com/bot.html'
742
+
730
743
  - regex: 'HTTPMon'
731
744
  name: 'HTTPMon'
732
745
  category: 'Site Monitor'
@@ -1020,6 +1033,14 @@
1020
1033
  - regex: 'Octopus [0-9]'
1021
1034
  name: 'Octopus'
1022
1035
 
1036
+ - regex: 'OnlineOrNot.com_bot'
1037
+ name: 'OnlineOrNot Bot'
1038
+ category: 'Site Monitor'
1039
+ url: 'https://onlineornot.com/website-monitoring'
1040
+ producer:
1041
+ name: 'OnlineOrNot'
1042
+ url: 'https://onlineornot.com'
1043
+
1023
1044
  - regex: 'omgili'
1024
1045
  name: 'Omgili bot'
1025
1046
  category: 'Search bot'
@@ -2025,7 +2046,15 @@
2025
2046
  name: 'WooRank sprl'
2026
2047
  url: 'https://www.woorank.com/'
2027
2048
 
2028
- - regex: '(Match|LinkCheck) by Siteimprove.com'
2049
+ - regex: 'by Siteimprove\.com'
2050
+ name: 'Siteimprove'
2051
+ category: 'Search bot'
2052
+ url: 'https://siteimprove.com/'
2053
+ producer:
2054
+ name: 'Siteimprove GmbH'
2055
+ url: 'https://siteimprove.com/'
2056
+
2057
+ - regex: 'Image size by Siteimprove\.com'
2029
2058
  name: 'Siteimprove'
2030
2059
  category: 'Search bot'
2031
2060
  url: 'https://siteimprove.com/'
@@ -2153,6 +2182,14 @@
2153
2182
  name: 'Startpagina B.V.'
2154
2183
  url: 'https://www.startpagina.nl/'
2155
2184
 
2185
+ - regex: 'MoodleBot-Linkchecker'
2186
+ name: 'MoodleBot Linkchecker'
2187
+ category: 'Search bot'
2188
+ url: 'hhttps://docs.moodle.org/en/Usage'
2189
+ producer:
2190
+ name: 'Moodle Pty Ltd'
2191
+ url: 'https://moodle.org/'
2192
+
2156
2193
  - regex: 'GTmetrix'
2157
2194
  name: 'GTmetrix'
2158
2195
  category: 'Crawler'
@@ -2412,6 +2449,13 @@
2412
2449
  producer:
2413
2450
  name: 'Hatena Co., Ltd.'
2414
2451
  url: 'https://www.hatena.ne.jp'
2452
+ - regex: 'Hatena-?Bookmark'
2453
+ name: 'Hatena Bookmark'
2454
+ category: 'Crawler'
2455
+ url: 'https://www.hatena.ne.jp/faq/'
2456
+ producer:
2457
+ name: 'Hatena Co., Ltd.'
2458
+ url: 'https://www.hatena.ne.jp'
2415
2459
 
2416
2460
  - regex: 'RyowlEngine/(\d+)'
2417
2461
  name: 'Ryowl'
@@ -2556,6 +2600,14 @@
2556
2600
  category: 'Security Checker'
2557
2601
  url: 'https://github.com/LeakIX/l9explore'
2558
2602
 
2603
+ - regex: 'l9scan/|^Lkx-(.*)/([\d+.]+)'
2604
+ name: 'LeakIX'
2605
+ category: 'Security Checker'
2606
+ url: 'https://leakix.net/'
2607
+ producer:
2608
+ name: 'BaDaaS SRL'
2609
+ url: 'https://leakix.net/'
2610
+
2559
2611
  - regex: 'MegaIndex.ru/([\d+\.])'
2560
2612
  name: 'MegaIndex'
2561
2613
  category: 'Crawler'
@@ -3047,14 +3099,6 @@
3047
3099
  name: 'New Work SE'
3048
3100
  url: 'https://www.xing.com/'
3049
3101
 
3050
- - regex: '^Lkx-(.*)/([\d+.]+)'
3051
- name: 'LeakIX'
3052
- category: 'Security Checker'
3053
- url: 'https://leakix.net/'
3054
- producer:
3055
- name: 'BaDaaS SRL'
3056
- url: 'https://leakix.net/'
3057
-
3058
3102
  - regex: 'RepoLookoutBot/([\d+.]+)'
3059
3103
  name: 'Repo Lookout'
3060
3104
  category: 'Security Checker'
@@ -3217,6 +3261,124 @@
3217
3261
  category: 'Crawler'
3218
3262
  url: 'https://reqbin.com/curl'
3219
3263
 
3264
+ - regex: 'XoviBot/([\d+.]+)'
3265
+ name: 'XoviBot'
3266
+ category: 'Crawler'
3267
+ url: 'https://www.xovibot.net'
3268
+ producer:
3269
+ name: 'Xovi GmbH'
3270
+ url: 'http://www.xovi.de'
3271
+
3272
+ - regex: 'Overcast/([\d+.]+) Podcast Sync'
3273
+ name: 'Overcast Podcast Sync'
3274
+ category: 'Service Agent'
3275
+ url: 'https://overcast.fm/podcasterinfo'
3276
+
3277
+ - regex: '^Verity/([\d+.]+)'
3278
+ name: 'GumGum Verity'
3279
+ category: 'Service Agent'
3280
+ url: 'https://gumgum.com/verity'
3281
+
3282
+ - regex: 'hackermention'
3283
+ name: 'hackermention'
3284
+ category: 'Feed Reader'
3285
+ url: 'https://github.com/snarfed/hackermention'
3286
+
3287
+ - regex: 'BitSightBot/([\d+.]+)'
3288
+ name: 'BitSight'
3289
+ category: 'Security Checker'
3290
+ url: 'https://www.bitsight.com/'
3291
+ producer:
3292
+ name: 'BitSight Technologies, Inc.'
3293
+ url: 'https://www.bitsight.com/'
3294
+
3295
+ - regex: 'Ezgif/([\d+.]+)'
3296
+ name: 'Ezgif'
3297
+ category: 'Service Agent'
3298
+ url: 'https://ezgif.com/about'
3299
+
3300
+ - regex: 'intelx.io_bot'
3301
+ name: 'Intelligence X'
3302
+ category: 'Crawler'
3303
+ url: 'https://intelx.io/'
3304
+ producer:
3305
+ name: 'Kleissner Investments s.r.o.'
3306
+ url: 'https://intelx.io/'
3307
+
3308
+ - regex: 'FemtosearchBot/([\d+.]+)'
3309
+ name: 'Femtosearch'
3310
+ category: 'Crawler'
3311
+ url: 'http://femtosearch.com/'
3312
+ producer:
3313
+ name: 'Grier Forensics, LLC'
3314
+ url: 'https://www.grierforensics.com/'
3315
+
3316
+ - regex: 'AdsTxtCrawler/([\d+.]+)'
3317
+ name: 'AdsTxtCrawler'
3318
+ category: 'Crawler'
3319
+ url: 'https://github.com/InteractiveAdvertisingBureau/adstxtcrawler'
3320
+ producer:
3321
+ name: 'IAB Technology Laboratory, Inc.'
3322
+ url: 'https://iabtechlab.com/'
3323
+
3324
+ - regex: 'Morningscore'
3325
+ name: 'Morningscore Bot'
3326
+ category: 'Crawler'
3327
+ url: 'https://morningscore.io/'
3328
+ producer:
3329
+ name: 'Morningscore'
3330
+ url: 'https://morningscore.io/'
3331
+
3332
+ - regex: 'Uptime-Kuma/([\d+.]+)'
3333
+ name: 'Uptime-Kuma'
3334
+ category: 'Site Monitor'
3335
+ url: 'https://github.com/louislam/uptime-kuma'
3336
+
3337
+ - regex: 'ChatGPT-User'
3338
+ name: 'ChatGPT'
3339
+ category: 'Crawler'
3340
+ url: 'https://platform.openai.com/docs/plugins/bot'
3341
+ producer:
3342
+ name: 'OpenAI OpCo, LLC'
3343
+ url: 'https://openai.com/'
3344
+
3345
+ - regex: 'BrightEdge Crawler/([\d+.]+)'
3346
+ name: 'BrightEdge'
3347
+ category: 'Crawler'
3348
+ url: 'https://www.brightedge.com/'
3349
+ producer:
3350
+ name: 'BrightEdge Technologies, Inc'
3351
+ url: 'https://www.brightedge.com/'
3352
+
3353
+ - regex: 'sfFeedReader/([\d+.]+)'
3354
+ name: 'sfFeedReader'
3355
+ url: 'https://github.com/diem-project/sfFeed2Plugin'
3356
+ category: 'Feed Fetcher'
3357
+
3358
+ - regex: 'cyberscan.io'
3359
+ name: 'Cyberscan'
3360
+ category: 'Security Checker'
3361
+ url: 'https://www.cyberscan.io/'
3362
+ producer:
3363
+ name: 'DGC Verwaltungs GmbH'
3364
+ url: 'https://dgc.org/'
3365
+
3366
+ - regex: 'deepcrawl\.com'
3367
+ name: 'Lumar'
3368
+ category: 'Crawler'
3369
+ url: 'https://deepcrawl.com/bot'
3370
+ producer:
3371
+ name: 'Lumar'
3372
+ url: 'https://www.lumar.io/'
3373
+
3374
+ - regex: 'RepoLookoutBot'
3375
+ name: 'Repo Lookout'
3376
+ category: 'Crawler'
3377
+ url: 'https://www.repo-lookout.org/'
3378
+ producer:
3379
+ name: 'Crissy Field GmbH'
3380
+ url: 'https://www.crissyfield.de/'
3381
+
3220
3382
  # Generic detections
3221
3383
  - regex: '[a-z0-9\-_]*((?<!cu|power[ _]|m[ _])bot(?![ _]TAB|[ _]?5[0-9]|[ _]Senior|[ _]Junior)|crawler|crawl|checker|archiver|transcoder|spider)([^a-z]|$)'
3222
3384
  name: 'Generic Bot'
@@ -319,7 +319,7 @@
319
319
  default: 'Blink'
320
320
 
321
321
  # Chromium GOST (https://github.com/deemru/chromium-gost)
322
- - regex: 'Chrome/(\d+[\.\d]+).+\(Chromium GOST\)'
322
+ - regex: 'Chrome/(\d+\.[\.\d]+) .*\(Chromium GOST\)'
323
323
  name: 'Chromium GOST'
324
324
  version: '$1'
325
325
  engine:
@@ -349,7 +349,7 @@
349
349
  default: 'Gecko'
350
350
 
351
351
  # CoolBrowser (https://play.google.com/store/apps/details?id=com.easybrowser.browser.coolbrowser)
352
- - regex: 'Chrome/(\d+[\.\d]+).+AgentWeb.+UCBrowser'
352
+ - regex: 'Chrome/(\d+\.[\.\d]+) .*AgentWeb.+UCBrowser'
353
353
  name: 'CoolBrowser'
354
354
  version: '$1'
355
355
  engine:
@@ -387,7 +387,7 @@
387
387
  default: 'Blink'
388
388
 
389
389
  # Qutebrowser (https://qutebrowser.org/)
390
- - regex: 'qutebrowser/(\d+[\.\d]+).+Chrome'
390
+ - regex: 'qutebrowser/(\d+\.[\.\d]+) .*Chrome'
391
391
  name: 'Qutebrowser'
392
392
  version: '$1'
393
393
  engine:
@@ -791,6 +791,13 @@
791
791
  engine:
792
792
  default: 'Blink'
793
793
 
794
+ # Wolvic VR Browser
795
+ - regex: 'Wolvic/(\d+\.[.\d]+)'
796
+ name: 'Wolvic'
797
+ version: '$1'
798
+ engine:
799
+ default: 'Gecko'
800
+
794
801
  # Firefox Reality (https://mixedreality.mozilla.org/firefox-reality/)
795
802
  - regex: 'Mobile VR.+Firefox'
796
803
  name: 'Firefox Reality'
@@ -1168,7 +1175,7 @@
1168
1175
  version: '$1'
1169
1176
  engine:
1170
1177
  default: 'Blink'
1171
- - regex: 'AlohaBrowser(?:/(\d+[\.\d]+))?'
1178
+ - regex: 'AlohaBrowser(?:App)?(?:/(\d+[\.\d]+))?'
1172
1179
  name: 'Aloha Browser'
1173
1180
  version: '$1'
1174
1181
  - regex: 'Aloha/'
@@ -1232,7 +1239,7 @@
1232
1239
  version: '$1'
1233
1240
 
1234
1241
  #Amigo
1235
- - regex: 'Chrome/(\d+[\.\d]+).*MRCHROME'
1242
+ - regex: 'Chrome/(\d+\.[\.\d]+) .*MRCHROME'
1236
1243
  name: 'Amigo'
1237
1244
  version: '$1'
1238
1245
  engine:
@@ -1395,7 +1402,7 @@
1395
1402
  default: 'Gecko'
1396
1403
 
1397
1404
  #Swiftfox
1398
- - regex: 'Firefox/(\d+[\.\d]+).*\(Swiftfox\)'
1405
+ - regex: 'Firefox/(\d+\.[\.\d]+) .*\(Swiftfox\)'
1399
1406
  name: 'Swiftfox'
1400
1407
  version: '$1'
1401
1408
  engine:
@@ -1581,7 +1588,7 @@
1581
1588
  version: '$1'
1582
1589
  engine:
1583
1590
  default: 'Blink'
1584
- - regex: 'Opera/(\d+[\.\d]+).+Opera Mobi'
1591
+ - regex: 'Opera/(\d+\.[\.\d]+) .*Opera Mobi'
1585
1592
  name: 'Opera Mobile'
1586
1593
  version: '$1'
1587
1594
  engine:
@@ -1616,7 +1623,7 @@
1616
1623
  default: 'Presto'
1617
1624
  versions:
1618
1625
  15: 'Blink'
1619
- - regex: '(?:Opera|OPR)[/ ](?:9.80.*Version/)?(\d+[\.\d]+).+Edition Next'
1626
+ - regex: '(?:Opera|OPR)[/ ](?:9.80.*Version/)?(\d+\.[\.\d]+) .*Edition Next'
1620
1627
  name: 'Opera Next'
1621
1628
  version: '$1'
1622
1629
  engine:
@@ -1699,7 +1706,17 @@
1699
1706
  name: 'Baidu Spark'
1700
1707
  version: '$1'
1701
1708
 
1702
- # Yandex Browser Lite
1709
+ # Yandex Browser
1710
+ - regex: 'YaBrowser(?:/(\d+[\.\d]*)) YaApp_iOS'
1711
+ name: 'Yandex Browser'
1712
+ version: '$1'
1713
+ engine:
1714
+ default: 'WebKit'
1715
+ - regex: 'iP(?:hone|ad);.+YaBrowser(?:/(\d+[\.\d]*)) Mobile'
1716
+ name: 'Yandex Browser'
1717
+ version: '$1'
1718
+ engine:
1719
+ default: 'WebKit'
1703
1720
  - regex: 'YaBrowser(?:/(\d+[\.\d]*)) \(lite\)?'
1704
1721
  name: 'Yandex Browser Lite'
1705
1722
  version: '$1'
@@ -1789,7 +1806,7 @@
1789
1806
  default: 'Blink'
1790
1807
 
1791
1808
  # Iron
1792
- - regex: 'Chrome(?:/(\d+[\.\d]+))?.*Iron'
1809
+ - regex: 'Chrome(?:/(\d+\.[\.\d]+) )?.*Iron'
1793
1810
  name: 'Iron'
1794
1811
  version: '$1'
1795
1812
  engine:
@@ -1869,7 +1886,7 @@
1869
1886
  versions:
1870
1887
  28: 'Blink'
1871
1888
 
1872
- - regex: '(?:DDG-Android-|DuckDuckGo/)(\d+[\.\d]*)'
1889
+ - regex: '(?:DDG-Android-|DuckDuckGo/|ddg_android/)(\d+[\.\d]*)'
1873
1890
  name: 'DuckDuckGo Privacy Browser'
1874
1891
  version: '$1'
1875
1892
  engine:
@@ -1957,7 +1974,7 @@
1957
1974
  default: 'Blink'
1958
1975
 
1959
1976
  # Web Explorer
1960
- - regex: 'Web Explorer/(\d+[\.\d]+).*Chrome'
1977
+ - regex: 'Web Explorer/(\d+\.[\.\d]+) .*Chrome'
1961
1978
  name: 'Web Explorer'
1962
1979
  version: '$1'
1963
1980
  engine:
@@ -2244,7 +2261,7 @@
2244
2261
  - regex: '(?:Tizen|SLP) ?Browser(?:/(\d+[\.\d]+))?'
2245
2262
  name: 'Tizen Browser'
2246
2263
  version: '$1'
2247
- - regex: 'Tizen (?:\d+[\.\d]+).+ Version/(\d+[\.\d]+) (?:TV|Mobile|like)'
2264
+ - regex: 'Tizen (?:\d+\.[\.\d]+)[^\.\d].* Version/(\d+[\.\d]+) (?:TV|Mobile|like)'
2248
2265
  name: 'Tizen Browser'
2249
2266
  version: '$1'
2250
2267
  engine:
@@ -2413,7 +2430,7 @@
2413
2430
  version: '$1'
2414
2431
  engine:
2415
2432
  default: 'WebKit'
2416
- - regex: 'GoogleEarth/(\d+[\.\d]+).+client:(?:Plus|Pro)'
2433
+ - regex: 'GoogleEarth/(\d+\.[\.\d]+)[^\.\d].*client:(?:Plus|Pro)'
2417
2434
  name: 'Google Earth Pro'
2418
2435
  version: '$1'
2419
2436
  engine:
@@ -2460,7 +2477,7 @@
2460
2477
  version: '$1'
2461
2478
  engine:
2462
2479
  default: 'Trident'
2463
- - regex: 'MSIE (\d+[\.\d]+).*XBLWP7'
2480
+ - regex: 'MSIE (\d+\.[\.\d]+)[^\.\d].*XBLWP7'
2464
2481
  name: 'IE Mobile'
2465
2482
  version: '$1'
2466
2483
  engine:
@@ -2663,7 +2680,7 @@
2663
2680
  version: '$1'
2664
2681
  engine:
2665
2682
  default: 'WebKit'
2666
- - regex: '(?:Version/(\d+[\.\d]+).*)?Mobile.*Safari/'
2683
+ - regex: '(?:Version/(\d+\.[\.\d]+) .*)?Mobile.*Safari/'
2667
2684
  name: 'Mobile Safari'
2668
2685
  version: '$1'
2669
2686
  engine:
@@ -2673,14 +2690,14 @@
2673
2690
  version: ''
2674
2691
  engine:
2675
2692
  default: 'WebKit'
2676
- - regex: 'Version/(\d+[\.\d]+).*Safari/|(?:Safari|Safari(?:%20)?%E6%B5%8F%E8%A7%88%E5%99%A8)/?\d+'
2693
+ - regex: 'Version/(\d+\.[\.\d]+) .*Safari/|(?:Safari|Safari(?:%20)?%E6%B5%8F%E8%A7%88%E5%99%A8)/?\d+'
2677
2694
  name: 'Safari'
2678
2695
  version: '$1'
2679
2696
  engine:
2680
2697
  default: 'WebKit'
2681
2698
 
2682
2699
  # Dorado
2683
- - regex: 'Dorado WAP-Browser(?:[/ ](\d+[\.\d]+))?'
2700
+ - regex: '(?:\w{1,5}[_ ])?Dorado WAP-Browser(?:[/ ](\d+[\.\d]+))?'
2684
2701
  name: 'Dorado'
2685
2702
  version: '$1'
2686
2703
 
@@ -86,6 +86,7 @@
86
86
  'com.hideitpro.vbrowser': 'vBrowser'
87
87
  'com.cgbrowser.rn': 'CG Browser'
88
88
  'com.azka.browser.anti.blokir': 'Azka Browser'
89
+ 'com.azka.browser': 'Azka Browser'
89
90
  'com.micromaxinfo.browser': 'Mmx Browser'
90
91
  'com.zeesitech.bitchutebrowser': 'Bitchute Browser'
91
92
  'nova.all.video.downloader': 'Nova Video Downloader Pro'
@@ -188,3 +189,7 @@
188
189
  'com.microsoft.emmx': 'Microsoft Edge'
189
190
  'com.explore.web.browser': 'Web Browser & Explorer'
190
191
  'privacy.explorer.fast.safe.browser': 'Privacy Explorer Fast Safe'
192
+ 'app.soundy.browser': 'Soundy Browser'
193
+ 'com.ivvi.browser': 'IVVI Browser'
194
+ 'com.nomone.vrbrowser': 'NOMone VR Browser'
195
+ 'com.opus.browser': 'Opus Browser'
@@ -174,7 +174,7 @@
174
174
  name: 'HTTPie'
175
175
  version: '$1'
176
176
 
177
- - regex: 'rest-client/(\d+[\.\d]+).*ruby'
177
+ - regex: 'rest-client/(\d+\.[\.\d]+) .*ruby'
178
178
  name: 'REST Client for Ruby'
179
179
  version: '$1'
180
180