device_detector 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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