user_agent_parser 2.4.1 → 2.5.0
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.
Potentially problematic release.
This version of user_agent_parser might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/bin/user_agent_parser +1 -1
- data/lib/user_agent_parser/cli.rb +3 -3
- data/lib/user_agent_parser/device.rb +8 -0
- data/lib/user_agent_parser/operating_system.rb +8 -3
- data/lib/user_agent_parser/parser.rb +74 -67
- data/lib/user_agent_parser/user_agent.rb +9 -0
- data/lib/user_agent_parser/version.rb +13 -2
- data/vendor/uap-core/regexes.yaml +274 -36
- metadata +5 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bd4b7259791194e0dfdd38e5a9fe5f0316f055eb94d21578fa542a8913c6834b
|
4
|
+
data.tar.gz: 1682d5ac3c9ff1e10b896846f215e7c487d83a58ded86ebbed073213156699f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0306cab9fe8e6213f339a061a5683cea62de9a8f06ea5c96e756da014d0cfcffb90d82ff8be66da7c83726fe18ac0161f6eea817e4c211a1ebcb632538bbb38
|
7
|
+
data.tar.gz: be503613c609542a66750d169d0f7a52caa36baa12aeca16739984476edc50a58479d7e537cc3b572c5845d4f13ecbaf5f642953b2ac0b3bf72b532ed110d770
|
data/bin/user_agent_parser
CHANGED
@@ -20,7 +20,7 @@ module UserAgentParser
|
|
20
20
|
minor
|
21
21
|
elsif @options[:os]
|
22
22
|
@user_agent.os.to_s
|
23
|
-
elsif format = @options[:format]
|
23
|
+
elsif (format = @options[:format])
|
24
24
|
format
|
25
25
|
.gsub('%f', @user_agent.family)
|
26
26
|
.gsub('%n', @user_agent.name)
|
@@ -47,8 +47,8 @@ module UserAgentParser
|
|
47
47
|
@version ||= @user_agent.version
|
48
48
|
end
|
49
49
|
|
50
|
-
def with_version
|
51
|
-
|
50
|
+
def with_version
|
51
|
+
yield(version) if version
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -13,9 +13,7 @@ module UserAgentParser
|
|
13
13
|
|
14
14
|
def to_s
|
15
15
|
string = family
|
16
|
-
unless version.nil?
|
17
|
-
string += " #{version}"
|
18
|
-
end
|
16
|
+
string += " #{version}" unless version.nil?
|
19
17
|
string
|
20
18
|
end
|
21
19
|
|
@@ -30,5 +28,12 @@ module UserAgentParser
|
|
30
28
|
end
|
31
29
|
|
32
30
|
alias == eql?
|
31
|
+
|
32
|
+
def to_h
|
33
|
+
{
|
34
|
+
version: version.to_h,
|
35
|
+
family: family
|
36
|
+
}
|
37
|
+
end
|
33
38
|
end
|
34
39
|
end
|
@@ -3,8 +3,25 @@
|
|
3
3
|
require 'yaml'
|
4
4
|
|
5
5
|
module UserAgentParser
|
6
|
-
|
7
6
|
class Parser
|
7
|
+
FAMILY_REPLACEMENT_KEYS = %w[
|
8
|
+
family_replacement
|
9
|
+
v1_replacement
|
10
|
+
v2_replacement
|
11
|
+
v3_replacement
|
12
|
+
v4_replacement
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
OS_REPLACEMENT_KEYS = %w[
|
16
|
+
os_replacement
|
17
|
+
os_v1_replacement
|
18
|
+
os_v2_replacement
|
19
|
+
os_v3_replacement
|
20
|
+
os_v4_replacement
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
private_constant :FAMILY_REPLACEMENT_KEYS, :OS_REPLACEMENT_KEYS
|
24
|
+
|
8
25
|
attr_reader :patterns_path
|
9
26
|
|
10
27
|
def initialize(options = {})
|
@@ -18,15 +35,15 @@ module UserAgentParser
|
|
18
35
|
parse_ua(user_agent, os, device)
|
19
36
|
end
|
20
37
|
|
21
|
-
|
38
|
+
private
|
22
39
|
|
23
40
|
def load_patterns(path)
|
24
41
|
yml = YAML.load_file(path)
|
25
42
|
|
26
43
|
# Parse all the regexs
|
27
|
-
yml.each_pair do |
|
44
|
+
yml.each_pair do |_type, patterns|
|
28
45
|
patterns.each do |pattern|
|
29
|
-
pattern[
|
46
|
+
pattern[:regex] = Regexp.new(pattern['regex'], pattern['regex_flag'] == 'i')
|
30
47
|
end
|
31
48
|
end
|
32
49
|
|
@@ -63,69 +80,36 @@ module UserAgentParser
|
|
63
80
|
end
|
64
81
|
end
|
65
82
|
|
66
|
-
|
67
|
-
patterns
|
68
|
-
|
69
|
-
|
83
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
|
84
|
+
def first_pattern_match(patterns, value)
|
85
|
+
patterns.each do |pattern|
|
86
|
+
if pattern[:regex].match?(value)
|
87
|
+
return [pattern, pattern[:regex].match(value)]
|
88
|
+
end
|
70
89
|
end
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
else
|
93
|
+
def first_pattern_match(patterns, value)
|
94
|
+
patterns.each do |pattern|
|
95
|
+
if (match = pattern[:regex].match(value))
|
96
|
+
return [pattern, match]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
nil
|
71
100
|
end
|
72
|
-
nil
|
73
101
|
end
|
74
102
|
|
75
103
|
def user_agent_from_pattern_match(pattern, match, os = nil, device = nil)
|
76
|
-
family,
|
77
|
-
|
78
|
-
if pattern['family_replacement']
|
79
|
-
family = pattern['family_replacement'].sub('$1', family || '')
|
80
|
-
end
|
81
|
-
|
82
|
-
if pattern['v1_replacement']
|
83
|
-
v1 = pattern['v1_replacement'].sub('$2', v1 || '')
|
84
|
-
end
|
85
|
-
|
86
|
-
if pattern['v2_replacement']
|
87
|
-
v2 = pattern['v2_replacement'].sub('$3', v2 || '')
|
88
|
-
end
|
89
|
-
|
90
|
-
if pattern['v3_replacement']
|
91
|
-
v3 = pattern['v3_replacement'].sub('$4', v3 || '')
|
92
|
-
end
|
104
|
+
family, *versions = from_pattern_match(FAMILY_REPLACEMENT_KEYS, pattern, match)
|
93
105
|
|
94
|
-
|
95
|
-
v4 = pattern['v4_replacement'].sub('$5', v4 || '')
|
96
|
-
end
|
97
|
-
|
98
|
-
version = version_from_segments(v1, v2, v3, v4)
|
99
|
-
|
100
|
-
UserAgent.new(family, version, os, device)
|
106
|
+
UserAgent.new(family, version_from_segments(*versions), os, device)
|
101
107
|
end
|
102
108
|
|
103
109
|
def os_from_pattern_match(pattern, match)
|
104
|
-
os,
|
105
|
-
|
106
|
-
if pattern['os_replacement']
|
107
|
-
os = pattern['os_replacement'].sub('$1', os || '')
|
108
|
-
end
|
109
|
-
|
110
|
-
if pattern['os_v1_replacement']
|
111
|
-
v1 = pattern['os_v1_replacement'].sub('$2', v1 || '')
|
112
|
-
end
|
113
|
-
|
114
|
-
if pattern['os_v2_replacement']
|
115
|
-
v2 = pattern['os_v2_replacement'].sub('$3', v2 || '')
|
116
|
-
end
|
117
|
-
|
118
|
-
if pattern['os_v3_replacement']
|
119
|
-
v3 = pattern['os_v3_replacement'].sub('$4', v3 || '')
|
120
|
-
end
|
121
|
-
|
122
|
-
if pattern['os_v4_replacement']
|
123
|
-
v4 = pattern['os_v4_replacement'].sub('$5', v4 || '')
|
124
|
-
end
|
110
|
+
os, *versions = from_pattern_match(OS_REPLACEMENT_KEYS, pattern, match)
|
125
111
|
|
126
|
-
|
127
|
-
|
128
|
-
OperatingSystem.new(os, version)
|
112
|
+
OperatingSystem.new(os, version_from_segments(*versions))
|
129
113
|
end
|
130
114
|
|
131
115
|
def device_from_pattern_match(pattern, match)
|
@@ -135,26 +119,49 @@ module UserAgentParser
|
|
135
119
|
|
136
120
|
if pattern['device_replacement']
|
137
121
|
family = pattern['device_replacement']
|
138
|
-
match.each_with_index { |m,i| family = family.sub("$#{i}", m) }
|
122
|
+
match.each_with_index { |m, i| family = family.sub("$#{i}", m) }
|
139
123
|
end
|
140
|
-
if pattern[
|
141
|
-
model = pattern[
|
142
|
-
match.each_with_index { |m,i| model = model.sub("$#{i}", m) }
|
124
|
+
if pattern['model_replacement']
|
125
|
+
model = pattern['model_replacement']
|
126
|
+
match.each_with_index { |m, i| model = model.sub("$#{i}", m) }
|
143
127
|
end
|
144
|
-
if pattern[
|
145
|
-
brand = pattern[
|
146
|
-
match.each_with_index { |m,i| brand = brand.sub("$#{i}", m) }
|
128
|
+
if pattern['brand_replacement']
|
129
|
+
brand = pattern['brand_replacement']
|
130
|
+
match.each_with_index { |m, i| brand = brand.sub("$#{i}", m) }
|
147
131
|
brand.strip!
|
148
132
|
end
|
149
133
|
|
150
|
-
model
|
134
|
+
model&.strip!
|
151
135
|
|
152
136
|
Device.new(family.strip, model, brand)
|
153
137
|
end
|
154
138
|
|
139
|
+
# Maps replacement keys to their values
|
140
|
+
def from_pattern_match(keys, pattern, match)
|
141
|
+
keys.each_with_index.map do |key, idx|
|
142
|
+
# Check if there is any replacement specified
|
143
|
+
if pattern[key]
|
144
|
+
interpolate(pattern[key], match)
|
145
|
+
else
|
146
|
+
# No replacement defined, just return correct match group
|
147
|
+
match[idx + 1]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Interpolates a string with data from matches if specified
|
153
|
+
def interpolate(replacement, match)
|
154
|
+
group_idx = replacement.index('$')
|
155
|
+
return replacement if group_idx.nil?
|
156
|
+
|
157
|
+
group_nbr = replacement[group_idx + 1]
|
158
|
+
replacement.sub("$#{group_nbr}", match[group_nbr.to_i])
|
159
|
+
end
|
160
|
+
|
155
161
|
def version_from_segments(*segments)
|
156
|
-
|
157
|
-
|
162
|
+
return if segments.all?(&:nil?)
|
163
|
+
|
164
|
+
Version.new(*segments)
|
158
165
|
end
|
159
166
|
end
|
160
167
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module UserAgentParser
|
4
4
|
class Version
|
5
|
-
|
6
5
|
# Private: Regex used to split string version string into major, minor,
|
7
6
|
# patch, and patch_minor.
|
8
7
|
SEGMENTS_REGEX = /\d+\-\d+|\d+[a-zA-Z]+$|\d+|[A-Za-z][0-9A-Za-z-]*$/
|
@@ -11,10 +10,12 @@ module UserAgentParser
|
|
11
10
|
alias to_s version
|
12
11
|
|
13
12
|
def initialize(*args)
|
13
|
+
# If only one string argument is given, assume a complete version string
|
14
|
+
# and attempt to parse it
|
14
15
|
if args.length == 1 && args.first.is_a?(String)
|
15
16
|
@version = args.first.to_s.strip
|
16
17
|
else
|
17
|
-
@segments = args.map(&:to_s).map(&:strip)
|
18
|
+
@segments = args.compact.map(&:to_s).map(&:strip)
|
18
19
|
@version = segments.join('.')
|
19
20
|
end
|
20
21
|
end
|
@@ -50,5 +51,15 @@ module UserAgentParser
|
|
50
51
|
def segments
|
51
52
|
@segments ||= version.scan(SEGMENTS_REGEX)
|
52
53
|
end
|
54
|
+
|
55
|
+
def to_h
|
56
|
+
{
|
57
|
+
version: version,
|
58
|
+
major: major,
|
59
|
+
minor: minor,
|
60
|
+
patch: patch,
|
61
|
+
patch_minor: patch_minor
|
62
|
+
}
|
63
|
+
end
|
53
64
|
end
|
54
65
|
end
|
@@ -29,7 +29,7 @@ user_agent_parsers:
|
|
29
29
|
# Pingdom
|
30
30
|
- regex: '(Pingdom.com_bot_version_)(\d+)\.(\d+)'
|
31
31
|
family_replacement: 'PingdomBot'
|
32
|
-
|
32
|
+
# 'Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PingdomTMS/0.8.5 Safari/534.34'
|
33
33
|
- regex: '(PingdomTMS)/(\d+)\.(\d+)\.(\d+)'
|
34
34
|
family_replacement: 'PingdomBot'
|
35
35
|
|
@@ -37,6 +37,13 @@ user_agent_parsers:
|
|
37
37
|
- regex: '(NewRelicPinger)/(\d+)\.(\d+)'
|
38
38
|
family_replacement: 'NewRelicPingerBot'
|
39
39
|
|
40
|
+
# Tableau
|
41
|
+
- regex: '(Tableau)/(\d+)\.(\d+)'
|
42
|
+
family_replacement: 'Tableau'
|
43
|
+
|
44
|
+
# Salesforce
|
45
|
+
- regex: '(Salesforce)(?:.)\/(\d+)\.(\d?)'
|
46
|
+
|
40
47
|
#StatusCake
|
41
48
|
- regex: '(\(StatusCake\))'
|
42
49
|
family_replacement: 'StatusCakeBot'
|
@@ -53,6 +60,10 @@ user_agent_parsers:
|
|
53
60
|
- regex: 'via ggpht.com GoogleImageProxy'
|
54
61
|
family_replacement: 'GmailImageProxy'
|
55
62
|
|
63
|
+
# Yahoo
|
64
|
+
- regex: 'YahooMailProxy; https://help.yahoo.com/kb/yahoo-mail-proxy-SLN28749.html'
|
65
|
+
family_replacement: 'YahooMailProxy'
|
66
|
+
|
56
67
|
# Twitter
|
57
68
|
- regex: '(Twitterbot)/(\d+)\.(\d+)'
|
58
69
|
family_replacement: 'TwitterBot'
|
@@ -66,15 +77,22 @@ user_agent_parsers:
|
|
66
77
|
- regex: '(MSIE) (\d+)\.(\d+)([a-z]\d?)?;.* MSIECrawler'
|
67
78
|
family_replacement: 'MSIECrawler'
|
68
79
|
|
80
|
+
# DAVdroid
|
81
|
+
- regex: '(DAVdroid)/(\d+)\.(\d+)(?:\.(\d+))?'
|
82
|
+
|
69
83
|
# Downloader ...
|
70
|
-
- regex: '(Google-HTTP-Java-Client|Apache-HttpClient|http%20client|Python-urllib|HttpMonitor|TLSProber|WinHTTP|JNLP|okhttp)(?:[ /](\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
|
84
|
+
- regex: '(Google-HTTP-Java-Client|Apache-HttpClient|Go-http-client|scalaj-http|http%20client|Python-urllib|HttpMonitor|TLSProber|WinHTTP|JNLP|okhttp|aihttp|reqwest)(?:[ /](\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
|
71
85
|
|
72
86
|
# Pinterestbot
|
73
87
|
- regex: '(Pinterest(?:bot)?)/(\d+)(?:\.(\d+)(?:\.(\d+))?)?[;\s\(]+\+https://www.pinterest.com/bot.html'
|
74
88
|
family_replacement: 'Pinterestbot'
|
75
89
|
|
76
90
|
# Bots
|
77
|
-
- regex: '(1470\.net crawler|50\.nu|8bo Crawler Bot|Aboundex|Accoona-[A-z]+-Agent|AdsBot-Google(?:-[a-z]+)?|altavista|AppEngine-Google|archive.*?\.org_bot|archiver|Ask Jeeves|[Bb]ai[Dd]u[Ss]pider(?:-[A-Za-z]+)*|bingbot|BingPreview|blitzbot|BlogBridge|Bloglovin|BoardReader(?: [A-Za-z]+)*|boitho.com-dc|BotSeer|\b\w*favicon\w*\b|\bYeti(?:-[a-z]+)?|Catchpoint(?: bot)?|[Cc]harlotte|Checklinks|clumboot|Comodo HTTP\(S\) Crawler|Comodo-Webinspector-Crawler|ConveraCrawler|CRAWL-E|CrawlConvera|Daumoa(?:-feedfetcher)?|Feed Seeker Bot|Feedbin|findlinks|Flamingo_SearchEngine|FollowSite Bot|furlbot|Genieo|gigabot|GomezAgent|gonzo1|(?:[a-zA-Z]+-)?Googlebot(?:-[a-zA-Z]+)?|Google SketchUp|grub-client|gsa-crawler|heritrix|HiddenMarket|holmes|HooWWWer|htdig|ia_archiver|ICC-Crawler|Icarus6j|ichiro(?:/mobile)?|IconSurf|IlTrovatore(?:-Setaccio)?|InfuzApp|Innovazion Crawler|InternetArchive|IP2[a-z]+Bot|jbot\b|KaloogaBot|Kraken|Kurzor|larbin|LEIA|LesnikBot|Linguee Bot|LinkAider|LinkedInBot|Lite Bot|Llaut|lycos|Mail\.RU_Bot|masscan|masidani_bot|Mediapartners-Google|Microsoft .*? Bot|mogimogi|mozDex|MJ12bot|msnbot(?:-media *)?|msrbot|Mtps Feed Aggregation System|netresearch|Netvibes|NewsGator[^/]*|^NING|Nutch[^/]*|Nymesis|ObjectsSearch|Orbiter|OOZBOT|PagePeeker|PagesInventory|PaxleFramework|Peeplo Screenshot Bot|PlantyNet_WebRobot|Pompos|Qwantify|Read%20Later|Reaper|RedCarpet|Retreiver|Riddler|Rival IQ|scooter|Scrapy|Scrubby|searchsight|seekbot|semanticdiscovery|Simpy|SimplePie|SEOstats|SimpleRSS|SiteCon|Slackbot-LinkExpanding|Slack-ImgProxy|Slurp|snappy|Speedy Spider|Squrl Java|Stringer|TheUsefulbot|ThumbShotsBot|Thumbshots\.ru|Tiny Tiny RSS|TwitterBot|WhatsApp|URL2PNG|Vagabondo|VoilaBot|^vortex|Votay bot|^voyager|WASALive.Bot|Web-sniffer|WebThumb|WeSEE:[A-z]+|WhatWeb|WIRE|WordPress|Wotbox|www\.almaden\.ibm\.com|Xenu(?:.s)? Link Sleuth|Xerka [A-z]+Bot|yacy(?:bot)?|Yahoo[a-z]*Seeker|Yahoo! Slurp|Yandex\w+|YodaoBot(?:-[A-z]+)?|YottaaMonitor|Yowedo|^Zao|^Zao-Crawler|ZeBot_www\.ze\.bz|ZooShot|ZyBorg)(?:[ /]v?(\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
|
91
|
+
- regex: '(1470\.net crawler|50\.nu|8bo Crawler Bot|Aboundex|Accoona-[A-z]+-Agent|AdsBot-Google(?:-[a-z]+)?|altavista|AppEngine-Google|archive.*?\.org_bot|archiver|Ask Jeeves|[Bb]ai[Dd]u[Ss]pider(?:-[A-Za-z]+)*|bingbot|BingPreview|blitzbot|BlogBridge|Bloglovin|BoardReader(?: [A-Za-z]+)*|boitho.com-dc|BotSeer|BUbiNG|\b\w*favicon\w*\b|\bYeti(?:-[a-z]+)?|Catchpoint(?: bot)?|[Cc]harlotte|Checklinks|clumboot|Comodo HTTP\(S\) Crawler|Comodo-Webinspector-Crawler|ConveraCrawler|CRAWL-E|CrawlConvera|Daumoa(?:-feedfetcher)?|Feed Seeker Bot|Feedbin|findlinks|Flamingo_SearchEngine|FollowSite Bot|furlbot|Genieo|gigabot|GomezAgent|gonzo1|(?:[a-zA-Z]+-)?Googlebot(?:-[a-zA-Z]+)?|Google SketchUp|grub-client|gsa-crawler|heritrix|HiddenMarket|holmes|HooWWWer|htdig|ia_archiver|ICC-Crawler|Icarus6j|ichiro(?:/mobile)?|IconSurf|IlTrovatore(?:-Setaccio)?|InfuzApp|Innovazion Crawler|InternetArchive|IP2[a-z]+Bot|jbot\b|KaloogaBot|Kraken|Kurzor|larbin|LEIA|LesnikBot|Linguee Bot|LinkAider|LinkedInBot|Lite Bot|Llaut|lycos|Mail\.RU_Bot|masscan|masidani_bot|Mediapartners-Google|Microsoft .*? Bot|mogimogi|mozDex|MJ12bot|msnbot(?:-media *)?|msrbot|Mtps Feed Aggregation System|netresearch|Netvibes|NewsGator[^/]*|^NING|Nutch[^/]*|Nymesis|ObjectsSearch|Orbiter|OOZBOT|PagePeeker|PagesInventory|PaxleFramework|Peeplo Screenshot Bot|PlantyNet_WebRobot|Pompos|Qwantify|Read%20Later|Reaper|RedCarpet|Retreiver|Riddler|Rival IQ|scooter|Scrapy|Scrubby|searchsight|seekbot|semanticdiscovery|SemrushBot|Simpy|SimplePie|SEOstats|SimpleRSS|SiteCon|Slackbot-LinkExpanding|Slack-ImgProxy|Slurp|snappy|Speedy Spider|Squrl Java|Stringer|TheUsefulbot|ThumbShotsBot|Thumbshots\.ru|Tiny Tiny RSS|TwitterBot|WhatsApp|URL2PNG|Vagabondo|VoilaBot|^vortex|Votay bot|^voyager|WASALive.Bot|Web-sniffer|WebThumb|WeSEE:[A-z]+|WhatWeb|WIRE|WordPress|Wotbox|www\.almaden\.ibm\.com|Xenu(?:.s)? Link Sleuth|Xerka [A-z]+Bot|yacy(?:bot)?|Yahoo[a-z]*Seeker|Yahoo! Slurp|Yandex\w+|YodaoBot(?:-[A-z]+)?|YottaaMonitor|Yowedo|^Zao|^Zao-Crawler|ZeBot_www\.ze\.bz|ZooShot|ZyBorg)(?:[ /]v?(\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
|
92
|
+
|
93
|
+
# AWS S3 Clients
|
94
|
+
# must come before "Bots General matcher" to catch "boto"/"boto3" before "bot"
|
95
|
+
- regex: '\b(Boto3?|JetS3t|aws-(?:cli|sdk-(?:cpp|go|java|nodejs|ruby2?))|s3fs)/(\d+)\.(\d+)(?:\.(\d+))?'
|
78
96
|
|
79
97
|
# Bots General matcher 'name/0.0'
|
80
98
|
- regex: '(?:\/[A-Za-z0-9\.]+)? *([A-Za-z0-9 \-_\!\[\]:]*(?:[Aa]rchiver|[Ii]ndexer|[Ss]craper|[Bb]ot|[Ss]pider|[Cc]rawl[a-z]*))/(\d+)(?:\.(\d+)(?:\.(\d+))?)?'
|
@@ -88,8 +106,8 @@ user_agent_parsers:
|
|
88
106
|
# See os_parsers if you want to target a specific TV
|
89
107
|
- regex: '(HbbTV)/(\d+)\.(\d+)\.(\d+) \('
|
90
108
|
|
91
|
-
# must go before Firefox to catch Chimera/SeaMonkey/Camino
|
92
|
-
- regex: '(Chimera|SeaMonkey|Camino)/(\d+)\.(\d+)\.?([ab]?\d+[a-z]*)?'
|
109
|
+
# must go before Firefox to catch Chimera/SeaMonkey/Camino/Waterfox
|
110
|
+
- regex: '(Chimera|SeaMonkey|Camino|Waterfox)/(\d+)\.(\d+)\.?([ab]?\d+[a-z]*)?'
|
93
111
|
|
94
112
|
# Social Networks
|
95
113
|
# Facebook
|
@@ -98,6 +116,18 @@ user_agent_parsers:
|
|
98
116
|
# Pinterest
|
99
117
|
- regex: '\[(Pinterest)/[^\]]+\]'
|
100
118
|
- regex: '(Pinterest)(?: for Android(?: Tablet)?)?/(\d+)(?:\.(\d+)(?:\.(\d+))?)?'
|
119
|
+
# Instagram app
|
120
|
+
- regex: 'Mozilla.*Mobile.*(Instagram).(\d+)\.(\d+)\.(\d+)'
|
121
|
+
# Flipbaord app
|
122
|
+
- regex: 'Mozilla.*Mobile.*(Flipboard).(\d+)\.(\d+)\.(\d+)'
|
123
|
+
# Flipbaord-briefing app
|
124
|
+
- regex: 'Mozilla.*Mobile.*(Flipboard-Briefing).(\d+)\.(\d+)\.(\d+)'
|
125
|
+
# Onefootball app
|
126
|
+
- regex: 'Mozilla.*Mobile.*(Onefootball)\/Android.(\d+)\.(\d+)\.(\d+)'
|
127
|
+
|
128
|
+
# Basilisk
|
129
|
+
- regex: '(Firefox)/(\d+)\.(\d+) Basilisk/(\d+)'
|
130
|
+
family_replacement: 'Basilisk'
|
101
131
|
|
102
132
|
# Pale Moon
|
103
133
|
- regex: '(PaleMoon)/(\d+)\.(\d+)\.?(\d+)?'
|
@@ -263,7 +293,7 @@ user_agent_parsers:
|
|
263
293
|
|
264
294
|
# Seznam.cz browser (based on WebKit)
|
265
295
|
- regex: '(SznProhlizec)/(\d+)\.(\d+)(?:\.(\d+))?'
|
266
|
-
family_replacement: 'Seznam
|
296
|
+
family_replacement: 'Seznam prohlížeč'
|
267
297
|
|
268
298
|
# Coc Coc browser, based on Chrome (used in Vietnam)
|
269
299
|
- regex: '(coc_coc_browser)/(\d+)\.(\d+)(?:\.(\d+))?'
|
@@ -283,6 +313,8 @@ user_agent_parsers:
|
|
283
313
|
- regex: '(Crosswalk)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
|
284
314
|
|
285
315
|
# Chrome Mobile
|
316
|
+
- regex: 'Version/.+(Chrome)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
|
317
|
+
family_replacement: 'Chrome Mobile WebView'
|
286
318
|
- regex: '; wv\).+(Chrome)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
|
287
319
|
family_replacement: 'Chrome Mobile WebView'
|
288
320
|
- regex: '(CrMo)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
|
@@ -335,6 +367,17 @@ user_agent_parsers:
|
|
335
367
|
# Podcast catcher Applications using iTunes
|
336
368
|
- regex: '(PodCruncher|Downcast)[ /]?(\d+)\.?(\d+)?\.?(\d+)?\.?(\d+)?'
|
337
369
|
|
370
|
+
# Box Notes https://www.box.com/resources/downloads
|
371
|
+
# Must be before Electron
|
372
|
+
- regex: ' (BoxNotes)/(\d+)\.(\d+)\.(\d+)'
|
373
|
+
|
374
|
+
# Whale
|
375
|
+
- regex: '(Whale)/(\d+)\.(\d+)\.(\d+)\.(\d+) Mobile(?:[ /]|$)'
|
376
|
+
family_replacement: 'Whale'
|
377
|
+
|
378
|
+
- regex: '(Whale)/(\d+)\.(\d+)\.(\d+)'
|
379
|
+
family_replacement: 'Whale'
|
380
|
+
|
338
381
|
#### END SPECIAL CASES TOP ####
|
339
382
|
|
340
383
|
#### MAIN CASES - this catches > 50% of all browsers ####
|
@@ -350,7 +393,7 @@ user_agent_parsers:
|
|
350
393
|
family_replacement: 'HipChat Desktop Client'
|
351
394
|
|
352
395
|
# Browser/major_version.minor_version.beta_version
|
353
|
-
- regex: '\b(MobileIron|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Epiphany|Shiira|Sunrise|Spotify|Flock|Netscape|Lunascape|WebPilot|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\.Browser|Bunjalloo|Google Earth|Raven for Mac|Openwave|MacOutlook|Electron)/(\d+)\.(\d+)\.(\d+)'
|
396
|
+
- regex: '\b(MobileIron|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Epiphany|Shiira|Sunrise|Spotify|Flock|Netscape|Lunascape|WebPilot|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\.Browser|Bunjalloo|Google Earth|Raven for Mac|Openwave|MacOutlook|Electron|OktaMobile)/(\d+)\.(\d+)\.(\d+)'
|
354
397
|
|
355
398
|
# Outlook 2007
|
356
399
|
- regex: 'Microsoft Office Outlook 12\.\d+\.\d+|MSOffice 12'
|
@@ -415,15 +458,19 @@ user_agent_parsers:
|
|
415
458
|
|
416
459
|
# Headless Chrome
|
417
460
|
# https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
|
418
|
-
|
419
|
-
|
420
|
-
|
461
|
+
- regex: '(HeadlessChrome)(?:/(\d+)\.(\d+)\.(\d+))?'
|
462
|
+
|
463
|
+
# Evolution Mail CardDav/CalDav integration
|
464
|
+
- regex: '(Evolution)/(\d+)\.(\d+)\.(\d+\.\d+)'
|
465
|
+
|
466
|
+
# Roundcube Mail CardDav plugin
|
467
|
+
- regex: '(RCM CardDAV plugin)/(\d+)\.(\d+)\.(\d+(?:-dev)?)'
|
421
468
|
|
422
469
|
# Browser/major_version.minor_version
|
423
470
|
- regex: '(bingbot|Bolt|AdobeAIR|Jasmine|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|NetFront|Netfront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|iTunes|MacAppStore|NetNewsWire|Space Bison|Stainless|Orca|Dolfin|BOLT|Minimo|Tizen Browser|Polaris|Abrowser|Planetweb|ICE Browser|mDolphin|qutebrowser|Otter|QupZilla|MailBar|kmail2|YahooMobileMail|ExchangeWebServices|ExchangeServicesClient|Dragon|Outlook-iOS-Android)/(\d+)\.(\d+)(?:\.(\d+))?'
|
424
471
|
|
425
472
|
# Chrome/Chromium/major_version.minor_version
|
426
|
-
- regex: '(Chromium|Chrome)/(\d+)\.(\d+)(?:\.(\d+))?'
|
473
|
+
- regex: '(Chromium|Chrome)/(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?'
|
427
474
|
|
428
475
|
##########
|
429
476
|
# IE Mobile needs to happen before Android to catch cases such as:
|
@@ -463,7 +510,7 @@ user_agent_parsers:
|
|
463
510
|
family_replacement: 'FancyMusic'
|
464
511
|
- regex: 'EspnDownloadManager'
|
465
512
|
family_replacement: 'ESPN'
|
466
|
-
- regex: '(ESPN) Radio (\d+)\.(\d+)\.?(\d+)? ?
|
513
|
+
- regex: '(ESPN) Radio (\d+)\.(\d+)\.?(\d+)? ?(?:rv:(\d+))? '
|
467
514
|
- regex: '(podracer|jPodder) v ?(\d+)\.(\d+)\.?(\d+)?'
|
468
515
|
- regex: '(ZDM)/(\d+)\.(\d+)[; ]?'
|
469
516
|
- regex: '(Zune|BeyondPod) (\d+)\.?(\d+)?[\);]'
|
@@ -521,6 +568,15 @@ user_agent_parsers:
|
|
521
568
|
- regex: '(MSIE) (\d+)\.(\d+).*XBLWP7'
|
522
569
|
family_replacement: 'IE Large Screen'
|
523
570
|
|
571
|
+
# Nextcloud desktop sync client
|
572
|
+
- regex: '(Nextcloud)'
|
573
|
+
|
574
|
+
# Generic mirall client
|
575
|
+
- regex: '(mirall)/(\d+)\.(\d+)\.(\d+)'
|
576
|
+
|
577
|
+
# Nextcloud/Owncloud android client
|
578
|
+
- regex: '(ownCloud-android)/(\d+)\.(\d+)\.(\d+)'
|
579
|
+
family_replacement: 'Owncloud'
|
524
580
|
|
525
581
|
#### END MAIN CASES ####
|
526
582
|
|
@@ -552,6 +608,8 @@ user_agent_parsers:
|
|
552
608
|
# @note: iOS / OSX Applications
|
553
609
|
- regex: '(iPod|iPhone|iPad).+Version/(\d+)\.(\d+)(?:\.(\d+))?.*[ +]Safari'
|
554
610
|
family_replacement: 'Mobile Safari'
|
611
|
+
- regex: '(iPod|iPod touch|iPhone|iPad);.*CPU.*OS[ +](\d+)_(\d+)(?:_(\d+))?.* AppleNews\/\d+\.\d+\.\d+?'
|
612
|
+
family_replacement: 'Mobile Safari UI/WKWebView'
|
555
613
|
- regex: '(iPod|iPhone|iPad).+Version/(\d+)\.(\d+)(?:\.(\d+))?'
|
556
614
|
family_replacement: 'Mobile Safari UI/WKWebView'
|
557
615
|
- regex: '(iPod|iPod touch|iPhone|iPad);.*CPU.*OS[ +](\d+)_(\d+)(?:_(\d+))?.*Mobile.*[ +]Safari'
|
@@ -563,6 +621,11 @@ user_agent_parsers:
|
|
563
621
|
- regex: '(iPod|iPhone|iPad)'
|
564
622
|
family_replacement: 'Mobile Safari UI/WKWebView'
|
565
623
|
|
624
|
+
##########################
|
625
|
+
# Outlook on iOS >= 2.62.0
|
626
|
+
##########################
|
627
|
+
- regex: '(Outlook-iOS)/\d+\.\d+\.prod\.iphone \((\d+)\.(\d+)\.(\d+)\)'
|
628
|
+
|
566
629
|
- regex: '(AvantGo) (\d+).(\d+)'
|
567
630
|
|
568
631
|
- regex: '(OneBrowser)/(\d+).(\d+)'
|
@@ -653,8 +716,21 @@ user_agent_parsers:
|
|
653
716
|
- regex: '(Phantom)/V(\d+)\.(\d+)'
|
654
717
|
family_replacement: 'Phantom Browser'
|
655
718
|
|
656
|
-
- regex: 'Trident
|
719
|
+
- regex: '(Trident)/(7|8)\.(0)'
|
720
|
+
family_replacement: 'IE'
|
721
|
+
v1_replacement: '11'
|
722
|
+
|
723
|
+
- regex: '(Trident)/(6)\.(0)'
|
724
|
+
family_replacement: 'IE'
|
725
|
+
v1_replacement: '10'
|
726
|
+
|
727
|
+
- regex: '(Trident)/(5)\.(0)'
|
657
728
|
family_replacement: 'IE'
|
729
|
+
v1_replacement: '9'
|
730
|
+
|
731
|
+
- regex: '(Trident)/(4)\.(0)'
|
732
|
+
family_replacement: 'IE'
|
733
|
+
v1_replacement: '8'
|
658
734
|
|
659
735
|
# Espial
|
660
736
|
- regex: '(Espial)/(\d+)(?:\.(\d+))?(?:\.(\d+))?'
|
@@ -678,10 +754,16 @@ user_agent_parsers:
|
|
678
754
|
family_replacement: 'Python Requests'
|
679
755
|
|
680
756
|
# headless user-agents
|
681
|
-
- regex: '\b(Windows-Update-Agent|Microsoft-CryptoAPI|SophosUpdateManager|SophosAgent|Debian APT-HTTP|Ubuntu APT-HTTP|libcurl-agent|libwww-perl|urlgrabber|curl|Wget|OpenBSD ftp|jupdate)(?:[ /](\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
|
757
|
+
- regex: '\b(Windows-Update-Agent|Microsoft-CryptoAPI|SophosUpdateManager|SophosAgent|Debian APT-HTTP|Ubuntu APT-HTTP|libcurl-agent|libwww-perl|urlgrabber|curl|PycURL|Wget|aria2|Axel|OpenBSD ftp|lftp|jupdate|insomnia)(?:[ /](\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
|
682
758
|
|
683
759
|
- regex: '(Java)[/ ]{0,1}\d+\.(\d+)\.(\d+)[_-]*([a-zA-Z0-9]+)*'
|
684
760
|
|
761
|
+
# Cloud Storage Clients
|
762
|
+
- regex: '^(Cyberduck)/(\d+)\.(\d+)\.(\d+)(?:\.\d+)?'
|
763
|
+
- regex: '^(S3 Browser) (\d+)-(\d+)-(\d+)(?:\s*http://s3browser\.com)?'
|
764
|
+
# rclone - rsync for cloud storage - https://rclone.org/
|
765
|
+
- regex: '^(rclone)/v(\d+)\.(\d+)'
|
766
|
+
|
685
767
|
# Roku Digital-Video-Players https://www.roku.com/
|
686
768
|
- regex: '^(Roku)/DVP-(\d+)\.(\d+)'
|
687
769
|
|
@@ -689,6 +771,8 @@ user_agent_parsers:
|
|
689
771
|
- regex: '(Kurio)\/(\d+)\.(\d+)\.(\d+)'
|
690
772
|
family_replacement: 'Kurio App'
|
691
773
|
|
774
|
+
# Box Drive and Box Sync https://www.box.com/resources/downloads
|
775
|
+
- regex: '^(Box(?: Sync)?)/(\d+)\.(\d+)\.(\d+)'
|
692
776
|
|
693
777
|
os_parsers:
|
694
778
|
##########
|
@@ -767,7 +851,7 @@ os_parsers:
|
|
767
851
|
# Android
|
768
852
|
# can actually detect rooted android os. do we care?
|
769
853
|
##########
|
770
|
-
- regex: '(Android)[ \-/](\d+)
|
854
|
+
- regex: '(Android)[ \-/](\d+)\.?(\d+)?(?:[.\-]([a-z0-9]+))?'
|
771
855
|
|
772
856
|
- regex: '(Android) Donut'
|
773
857
|
os_v1_replacement: '1'
|
@@ -799,6 +883,10 @@ os_parsers:
|
|
799
883
|
- regex: '^(JUC).*; ?U; ?(?:Android)?(\d+)\.(\d+)(?:[\.\-]([a-z0-9]+))?'
|
800
884
|
os_replacement: 'Android'
|
801
885
|
|
886
|
+
# Salesforce
|
887
|
+
- regex: '(android)\s(?:mobile\/)(\d+)\.(\d+)\.?(\d+)?'
|
888
|
+
os_replacement: 'Android'
|
889
|
+
|
802
890
|
##########
|
803
891
|
# Kindle Android
|
804
892
|
##########
|
@@ -824,51 +912,75 @@ os_parsers:
|
|
824
912
|
os_replacement: 'Windows Mobile'
|
825
913
|
|
826
914
|
- regex: '(Windows (?:NT 5\.2|NT 5\.1))'
|
827
|
-
os_replacement: 'Windows
|
915
|
+
os_replacement: 'Windows'
|
916
|
+
os_v1_replacement: 'XP'
|
828
917
|
|
829
918
|
- regex: '(Windows NT 6\.1)'
|
830
|
-
os_replacement: 'Windows
|
919
|
+
os_replacement: 'Windows'
|
920
|
+
os_v1_replacement: '7'
|
831
921
|
|
832
922
|
- regex: '(Windows NT 6\.0)'
|
833
|
-
os_replacement: 'Windows
|
923
|
+
os_replacement: 'Windows'
|
924
|
+
os_v1_replacement: 'Vista'
|
834
925
|
|
835
926
|
- regex: '(Win 9x 4\.90)'
|
836
|
-
os_replacement: 'Windows
|
927
|
+
os_replacement: 'Windows'
|
928
|
+
os_v1_replacement: 'ME'
|
837
929
|
|
838
930
|
- regex: '(Windows 98|Windows XP|Windows ME|Windows 95|Windows CE|Windows 7|Windows NT 4\.0|Windows Vista|Windows 2000|Windows 3.1)'
|
839
931
|
|
840
932
|
- regex: '(Windows NT 6\.2; ARM;)'
|
841
|
-
os_replacement: 'Windows
|
933
|
+
os_replacement: 'Windows'
|
934
|
+
os_v1_replacement: 'RT'
|
935
|
+
|
842
936
|
- regex: '(Windows NT 6\.2)'
|
843
|
-
os_replacement: 'Windows
|
937
|
+
os_replacement: 'Windows'
|
938
|
+
os_v1_replacement: '8'
|
844
939
|
|
845
940
|
- regex: '(Windows NT 6\.3; ARM;)'
|
846
|
-
os_replacement: 'Windows
|
941
|
+
os_replacement: 'Windows'
|
942
|
+
os_v1_replacement: 'RT 8.1'
|
943
|
+
|
847
944
|
- regex: '(Windows NT 6\.3)'
|
848
|
-
os_replacement: 'Windows
|
945
|
+
os_replacement: 'Windows'
|
946
|
+
os_v1_replacement: '8.1'
|
849
947
|
|
850
948
|
- regex: '(Windows NT 6\.4)'
|
851
|
-
os_replacement: 'Windows
|
949
|
+
os_replacement: 'Windows'
|
950
|
+
os_v1_replacement: '10'
|
951
|
+
|
852
952
|
- regex: '(Windows NT 10\.0)'
|
853
|
-
os_replacement: 'Windows
|
953
|
+
os_replacement: 'Windows'
|
954
|
+
os_v1_replacement: '10'
|
854
955
|
|
855
956
|
- regex: '(Windows NT 5\.0)'
|
856
|
-
os_replacement: 'Windows
|
957
|
+
os_replacement: 'Windows'
|
958
|
+
os_v1_replacement: '2000'
|
857
959
|
|
858
960
|
- regex: '(WinNT4.0)'
|
859
|
-
os_replacement: 'Windows
|
961
|
+
os_replacement: 'Windows'
|
962
|
+
os_v1_replacement: 'NT 4.0'
|
860
963
|
|
861
964
|
- regex: '(Windows ?CE)'
|
862
|
-
os_replacement: 'Windows
|
965
|
+
os_replacement: 'Windows'
|
966
|
+
os_v1_replacement: 'CE'
|
863
967
|
|
864
968
|
- regex: 'Win ?(95|98|3.1|NT|ME|2000)'
|
865
|
-
os_replacement: 'Windows
|
969
|
+
os_replacement: 'Windows'
|
970
|
+
os_v1_replacement: '$1'
|
866
971
|
|
867
972
|
- regex: 'Win16'
|
868
|
-
os_replacement: 'Windows
|
973
|
+
os_replacement: 'Windows'
|
974
|
+
os_v1_replacement: '3.1'
|
869
975
|
|
870
976
|
- regex: 'Win32'
|
871
|
-
os_replacement: 'Windows
|
977
|
+
os_replacement: 'Windows'
|
978
|
+
os_v1_replacement: '95'
|
979
|
+
|
980
|
+
# Box apps (Drive, Sync, Notes) on Windows https://www.box.com/resources/downloads
|
981
|
+
- regex: '^Box.*Windows/([\d.]+);'
|
982
|
+
os_replacement: 'Windows'
|
983
|
+
os_v1_replacement: '$1'
|
872
984
|
|
873
985
|
##########
|
874
986
|
# Tizen OS from Samsung
|
@@ -883,6 +995,11 @@ os_parsers:
|
|
883
995
|
##########
|
884
996
|
- regex: '((?:Mac[ +]?|; )OS[ +]X)[\s+/](?:(\d+)[_.](\d+)(?:[_.](\d+))?|Mach-O)'
|
885
997
|
os_replacement: 'Mac OS X'
|
998
|
+
- regex: '(\w+\s+Mac OS X\s+\w+\s+(\d+).(\d+).(\d+).*)'
|
999
|
+
os_replacement: 'Mac OS X'
|
1000
|
+
os_v1_replacement: '$2'
|
1001
|
+
os_v2_replacement: '$3'
|
1002
|
+
os_v3_replacement: '$4'
|
886
1003
|
# Leopard
|
887
1004
|
- regex: ' (Dar)(win)/(9).(\d+).*\((?:i386|x86_64|Power Macintosh)\)'
|
888
1005
|
os_replacement: 'Mac OS X'
|
@@ -920,6 +1037,10 @@ os_parsers:
|
|
920
1037
|
# ios devices spoof (mac os x), so including intel/ppc prefixes
|
921
1038
|
- regex: '(?:PPC|Intel) (Mac OS X)'
|
922
1039
|
|
1040
|
+
# Box Drive and Box Sync on Mac OS X use OSX version numbers, not Darwin
|
1041
|
+
- regex: '^Box.*;(Darwin)/(10)\.(1\d)(?:\.(\d+))?'
|
1042
|
+
os_replacement: 'Mac OS X'
|
1043
|
+
|
923
1044
|
##########
|
924
1045
|
# iOS
|
925
1046
|
# http://en.wikipedia.org/wiki/IOS_version_history
|
@@ -979,9 +1100,29 @@ os_parsers:
|
|
979
1100
|
os_replacement: 'Mac OS X'
|
980
1101
|
os_v1_replacement: '10'
|
981
1102
|
os_v2_replacement: '11'
|
1103
|
+
- regex: 'CFNetwork/7.* Darwin/15\.4\.\d+'
|
1104
|
+
os_replacement: 'iOS'
|
1105
|
+
os_v1_replacement: '9'
|
1106
|
+
os_v2_replacement: '3'
|
1107
|
+
os_v3_replacement: '1'
|
1108
|
+
- regex: 'CFNetwork/7.* Darwin/15\.5\.\d+'
|
1109
|
+
os_replacement: 'iOS'
|
1110
|
+
os_v1_replacement: '9'
|
1111
|
+
os_v2_replacement: '3'
|
1112
|
+
os_v3_replacement: '2'
|
1113
|
+
- regex: 'CFNetwork/7.* Darwin/15\.6\.\d+'
|
1114
|
+
os_replacement: 'iOS'
|
1115
|
+
os_v1_replacement: '9'
|
1116
|
+
os_v2_replacement: '3'
|
1117
|
+
os_v3_replacement: '5'
|
982
1118
|
- regex: '(CF)(Network)/758\.(\d)'
|
983
1119
|
os_replacement: 'iOS'
|
984
1120
|
os_v1_replacement: '9'
|
1121
|
+
- regex: 'CFNetwork/808\.3 Darwin/16\.3\.\d+'
|
1122
|
+
os_replacement: 'iOS'
|
1123
|
+
os_v1_replacement: '10'
|
1124
|
+
os_v2_replacement: '2'
|
1125
|
+
os_v3_replacement: '1'
|
985
1126
|
- regex: '(CF)(Network)/808\.(\d)'
|
986
1127
|
os_replacement: 'iOS'
|
987
1128
|
os_v1_replacement: '10'
|
@@ -990,6 +1131,10 @@ os_parsers:
|
|
990
1131
|
# CFNetwork macOS Apps (must be before CFNetwork iOS Apps
|
991
1132
|
# @ref: https://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history
|
992
1133
|
##########
|
1134
|
+
- regex: 'CFNetwork/.* Darwin/17\.\d+.*\(x86_64\)'
|
1135
|
+
os_replacement: 'Mac OS X'
|
1136
|
+
os_v1_replacement: '10'
|
1137
|
+
os_v2_replacement: '13'
|
993
1138
|
- regex: 'CFNetwork/.* Darwin/16\.\d+.*\(x86_64\)'
|
994
1139
|
os_replacement: 'Mac OS X'
|
995
1140
|
os_v1_replacement: '10'
|
@@ -1025,14 +1170,76 @@ os_parsers:
|
|
1025
1170
|
os_replacement: 'iOS'
|
1026
1171
|
os_v1_replacement: '9'
|
1027
1172
|
os_v2_replacement: '0'
|
1173
|
+
- regex: 'CFNetwork/8.* Darwin/16\.5\.\d+'
|
1174
|
+
os_replacement: 'iOS'
|
1175
|
+
os_v1_replacement: '10'
|
1176
|
+
os_v2_replacement: '3'
|
1177
|
+
- regex: 'CFNetwork/8.* Darwin/16\.6\.\d+'
|
1178
|
+
os_replacement: 'iOS'
|
1179
|
+
os_v1_replacement: '10'
|
1180
|
+
os_v2_replacement: '3'
|
1181
|
+
os_v3_replacement: '2'
|
1182
|
+
- regex: 'CFNetwork/8.* Darwin/16\.7\.\d+'
|
1183
|
+
os_replacement: 'iOS'
|
1184
|
+
os_v1_replacement: '10'
|
1185
|
+
os_v2_replacement: '3'
|
1186
|
+
os_v3_replacement: '3'
|
1028
1187
|
- regex: 'CFNetwork/8.* Darwin/(16)\.\d+'
|
1029
1188
|
os_replacement: 'iOS'
|
1030
1189
|
os_v1_replacement: '10'
|
1190
|
+
- regex: 'CFNetwork/8.* Darwin/17\.0\.\d+'
|
1191
|
+
os_replacement: 'iOS'
|
1192
|
+
os_v1_replacement: '11'
|
1193
|
+
os_v2_replacement: '0'
|
1194
|
+
- regex: 'CFNetwork/8.* Darwin/17\.2\.\d+'
|
1195
|
+
os_replacement: 'iOS'
|
1196
|
+
os_v1_replacement: '11'
|
1197
|
+
os_v2_replacement: '1'
|
1198
|
+
- regex: 'CFNetwork/8.* Darwin/17\.3\.\d+'
|
1199
|
+
os_replacement: 'iOS'
|
1200
|
+
os_v1_replacement: '11'
|
1201
|
+
os_v2_replacement: '2'
|
1202
|
+
- regex: 'CFNetwork/8.* Darwin/17\.4\.\d+'
|
1203
|
+
os_replacement: 'iOS'
|
1204
|
+
os_v1_replacement: '11'
|
1205
|
+
os_v2_replacement: '2'
|
1206
|
+
os_v3_replacement: '6'
|
1207
|
+
- regex: 'CFNetwork/8.* Darwin/17\.5\.\d+'
|
1208
|
+
os_replacement: 'iOS'
|
1209
|
+
os_v1_replacement: '11'
|
1210
|
+
os_v2_replacement: '3'
|
1211
|
+
- regex: 'CFNetwork/9.* Darwin/17\.6\.\d+'
|
1212
|
+
os_replacement: 'iOS'
|
1213
|
+
os_v1_replacement: '11'
|
1214
|
+
os_v2_replacement: '4'
|
1215
|
+
- regex: 'CFNetwork/9.* Darwin/17\.7\.\d+'
|
1216
|
+
os_replacement: 'iOS'
|
1217
|
+
os_v1_replacement: '11'
|
1218
|
+
os_v2_replacement: '4'
|
1219
|
+
os_v3_replacement: '1'
|
1220
|
+
- regex: 'CFNetwork/8.* Darwin/(17)\.\d+'
|
1221
|
+
os_replacement: 'iOS'
|
1222
|
+
os_v1_replacement: '11'
|
1223
|
+
- regex: 'CFNetwork/9.* Darwin/18\.0\.\d+'
|
1224
|
+
os_replacement: 'iOS'
|
1225
|
+
os_v1_replacement: '12'
|
1226
|
+
os_v2_replacement: '0'
|
1227
|
+
- regex: 'CFNetwork/9.* Darwin/(18)\.\d+'
|
1228
|
+
os_replacement: 'iOS'
|
1229
|
+
os_v1_replacement: '12'
|
1230
|
+
- regex: 'CFNetwork/.* Darwin/'
|
1231
|
+
os_replacement: 'iOS'
|
1232
|
+
|
1031
1233
|
# iOS Apps
|
1032
1234
|
- regex: '\b(iOS[ /]|iOS; |iPhone(?:/| v|[ _]OS[/,]|; | OS : |\d,\d/|\d,\d; )|iPad/)(\d{1,2})[_\.](\d{1,2})(?:[_\.](\d+))?'
|
1033
1235
|
os_replacement: 'iOS'
|
1034
1236
|
- regex: '\((iOS);'
|
1035
1237
|
|
1238
|
+
##########################
|
1239
|
+
# Outlook on iOS >= 2.62.0
|
1240
|
+
##########################
|
1241
|
+
- regex: 'Outlook-(iOS)/\d+\.\d+\.prod\.iphone'
|
1242
|
+
|
1036
1243
|
##########
|
1037
1244
|
# Apple TV
|
1038
1245
|
##########
|
@@ -1189,6 +1396,13 @@ os_parsers:
|
|
1189
1396
|
- regex: '(Linux)(?:[ /](\d+)\.(\d+)(?:\.(\d+))?)?'
|
1190
1397
|
- regex: 'SunOS'
|
1191
1398
|
os_replacement: 'Solaris'
|
1399
|
+
# Wget/x.x.x (linux-gnu)
|
1400
|
+
- regex: '\(linux-gnu\)'
|
1401
|
+
os_replacement: 'Linux'
|
1402
|
+
- regex: '\(x86_64-redhat-linux-gnu\)'
|
1403
|
+
os_replacement: 'Red Hat'
|
1404
|
+
- regex: '\((freebsd)(\d+)\.(\d+)\)'
|
1405
|
+
os_replacement: 'FreeBSD'
|
1192
1406
|
|
1193
1407
|
# Roku Digital-Video-Players https://www.roku.com/
|
1194
1408
|
- regex: '^(Roku)/DVP-(\d+)\.(\d+)'
|
@@ -2098,7 +2312,7 @@ device_parsers:
|
|
2098
2312
|
device_replacement: '$1'
|
2099
2313
|
brand_replacement: 'Google'
|
2100
2314
|
model_replacement: '$1'
|
2101
|
-
- regex: '; *(Pixel
|
2315
|
+
- regex: '; *(Pixel.*) Build'
|
2102
2316
|
device_replacement: '$1'
|
2103
2317
|
brand_replacement: 'Google'
|
2104
2318
|
model_replacement: '$1'
|
@@ -2374,7 +2588,7 @@ device_parsers:
|
|
2374
2588
|
device_replacement: '$1 $2'
|
2375
2589
|
brand_replacement: 'iBall'
|
2376
2590
|
model_replacement: '$1 $2'
|
2377
|
-
- regex: '; *(IBall)(?:[ _]([^;/]+)
|
2591
|
+
- regex: '; *(IBall)(?:[ _]([^;/]+))? Build'
|
2378
2592
|
regex_flag: 'i'
|
2379
2593
|
device_replacement: '$1 $2'
|
2380
2594
|
brand_replacement: 'iBall'
|
@@ -3449,6 +3663,10 @@ device_parsers:
|
|
3449
3663
|
device_replacement: '$1'
|
3450
3664
|
brand_replacement: 'RCA'
|
3451
3665
|
model_replacement: '$1'
|
3666
|
+
- regex: '; RCA (\w+) Build/'
|
3667
|
+
device_replacement: 'RCA $1'
|
3668
|
+
brand_replacement: 'RCA'
|
3669
|
+
model_replacement: '$1'
|
3452
3670
|
|
3453
3671
|
#########
|
3454
3672
|
# Rockchip
|
@@ -4088,7 +4306,7 @@ device_parsers:
|
|
4088
4306
|
# XiaoMi
|
4089
4307
|
# @ref: http://www.xiaomi.com/event/buyphone
|
4090
4308
|
#########
|
4091
|
-
- regex: '; *((MI|HM|MI-ONE|Redmi)[ -](NOTE |Note )?[^;/]*) (Build|MIUI)/'
|
4309
|
+
- regex: '; *((Mi|MI|HM|MI-ONE|Redmi)[ -](NOTE |Note )?[^;/]*) (Build|MIUI)/'
|
4092
4310
|
device_replacement: 'XiaoMi $1'
|
4093
4311
|
brand_replacement: 'XiaoMi'
|
4094
4312
|
model_replacement: '$1'
|
@@ -4550,6 +4768,18 @@ device_parsers:
|
|
4550
4768
|
device_replacement: '$1'
|
4551
4769
|
brand_replacement: 'Apple'
|
4552
4770
|
model_replacement: '$1'
|
4771
|
+
- regex: '(Apple Watch)(?:;| Simulator;)'
|
4772
|
+
device_replacement: '$1'
|
4773
|
+
brand_replacement: 'Apple'
|
4774
|
+
model_replacement: '$1'
|
4775
|
+
- regex: '(HomePod)(?:;| Simulator;)'
|
4776
|
+
device_replacement: '$1'
|
4777
|
+
brand_replacement: 'Apple'
|
4778
|
+
model_replacement: '$1'
|
4779
|
+
- regex: 'iPhone'
|
4780
|
+
device_replacement: 'iPhone'
|
4781
|
+
brand_replacement: 'Apple'
|
4782
|
+
model_replacement: 'iPhone'
|
4553
4783
|
# @note: desktop applications show device info
|
4554
4784
|
- regex: 'CFNetwork/.* Darwin/\d.*\(((?:Mac|iMac|PowerMac|PowerBook)[^\d]*)(\d+)(?:,|%2C)(\d+)'
|
4555
4785
|
device_replacement: '$1$2,$3'
|
@@ -4567,6 +4797,14 @@ device_parsers:
|
|
4567
4797
|
brand_replacement: 'Apple'
|
4568
4798
|
model_replacement: 'iOS-Device'
|
4569
4799
|
|
4800
|
+
##########################
|
4801
|
+
# Outlook on iOS >= 2.62.0
|
4802
|
+
##########################
|
4803
|
+
- regex: 'Outlook-(iOS)/\d+\.\d+\.prod\.iphone'
|
4804
|
+
brand_replacement: 'Apple'
|
4805
|
+
device_replacement: 'iPhone'
|
4806
|
+
model_replacement: 'iPhone'
|
4807
|
+
|
4570
4808
|
##########
|
4571
4809
|
# Acer
|
4572
4810
|
##########
|
@@ -4717,7 +4955,7 @@ device_parsers:
|
|
4717
4955
|
##########
|
4718
4956
|
# LGE NetCast TV
|
4719
4957
|
##########
|
4720
|
-
- regex: 'LGE; (?:Media\/)?([^;]*);[^;]*;[^;]*;?\); "?LG NetCast(\.TV|\.Media
|
4958
|
+
- regex: 'LGE; (?:Media\/)?([^;]*);[^;]*;[^;]*;?\); "?LG NetCast(\.TV|\.Media)?-\d+'
|
4721
4959
|
device_replacement: 'NetCast$2'
|
4722
4960
|
brand_replacement: 'LG'
|
4723
4961
|
model_replacement: '$1'
|
@@ -4988,7 +5226,7 @@ device_parsers:
|
|
4988
5226
|
##########
|
4989
5227
|
# Spiders (this is hack...)
|
4990
5228
|
##########
|
4991
|
-
- regex: '(bot|zao|borg|DBot|oegp|silk|Xenu|zeal|^NING|CCBot|crawl|htdig|lycos|slurp|teoma|voila|yahoo|Sogou|CiBra|Nutch|^Java/|^JNLP/|Daumoa|Genieo|ichiro|larbin|pompos|Scrapy|snappy|speedy|spider|msnbot|msrbot|vortex|^vortex|crawler|favicon|indexer|Riddler|scooter|scraper|scrubby|WhatWeb|WinHTTP|bingbot|BingPreview|openbot|gigabot|furlbot|polybot|seekbot|^voyager|archiver|Icarus6j|mogimogi|Netvibes|blitzbot|altavista|charlotte|findlinks|Retreiver|TLSProber|WordPress|SeznamBot|ProoXiBot|wsr\-agent|Squrl Java|EtaoSpider|PaperLiBot|SputnikBot|A6\-Indexer|netresearch|searchsight|baiduspider|YisouSpider|ICC\-Crawler|http%20client|Python-urllib|dataparksearch|converacrawler|Screaming Frog|AppEngine-Google|YahooCacheSystem|fast\-webcrawler|Sogou Pic Spider|semanticdiscovery|Innovazion Crawler|facebookexternalhit|Google.*/\+/web/snippet|Google-HTTP-Java-Client|BlogBridge|IlTrovatore-Setaccio|InternetArchive|GomezAgent|WebThumbnail|heritrix|NewsGator|PagePeeker|Reaper|ZooShot|holmes|NL-Crawler|Pingdom|StatusCake|WhatsApp|masscan|Google Web Preview|Qwantify)'
|
5229
|
+
- regex: '(bot|BUbiNG|zao|borg|DBot|oegp|silk|Xenu|zeal|^NING|CCBot|crawl|htdig|lycos|slurp|teoma|voila|yahoo|Sogou|CiBra|Nutch|^Java/|^JNLP/|Daumoa|Daum|Genieo|ichiro|larbin|pompos|Scrapy|snappy|speedy|spider|msnbot|msrbot|vortex|^vortex|crawler|favicon|indexer|Riddler|scooter|scraper|scrubby|WhatWeb|WinHTTP|bingbot|BingPreview|openbot|gigabot|furlbot|polybot|seekbot|^voyager|archiver|Icarus6j|mogimogi|Netvibes|blitzbot|altavista|charlotte|findlinks|Retreiver|TLSProber|WordPress|SeznamBot|ProoXiBot|wsr\-agent|Squrl Java|EtaoSpider|PaperLiBot|SputnikBot|A6\-Indexer|netresearch|searchsight|baiduspider|YisouSpider|ICC\-Crawler|http%20client|Python-urllib|dataparksearch|converacrawler|Screaming Frog|AppEngine-Google|YahooCacheSystem|fast\-webcrawler|Sogou Pic Spider|semanticdiscovery|Innovazion Crawler|facebookexternalhit|Google.*/\+/web/snippet|Google-HTTP-Java-Client|BlogBridge|IlTrovatore-Setaccio|InternetArchive|GomezAgent|WebThumbnail|heritrix|NewsGator|PagePeeker|Reaper|ZooShot|holmes|NL-Crawler|Pingdom|StatusCake|WhatsApp|masscan|Google Web Preview|Qwantify|Yeti)'
|
4992
5230
|
regex_flag: 'i'
|
4993
5231
|
device_replacement: 'Spider'
|
4994
5232
|
brand_replacement: 'Spider'
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: user_agent_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Lucas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: coveralls
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - '='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 0.8.21
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - '='
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 0.8.21
|
11
|
+
date: 2018-10-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
27
13
|
description: A simple, comprehensive Ruby gem for parsing user agent strings with
|
28
14
|
the help of BrowserScope's UA database
|
29
15
|
email: t@toolmantim.com
|
@@ -54,7 +40,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
40
|
requirements:
|
55
41
|
- - ">="
|
56
42
|
- !ruby/object:Gem::Version
|
57
|
-
version:
|
43
|
+
version: '2.3'
|
58
44
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
45
|
requirements:
|
60
46
|
- - ">="
|
@@ -62,7 +48,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
48
|
version: '0'
|
63
49
|
requirements: []
|
64
50
|
rubyforge_project:
|
65
|
-
rubygems_version: 2.
|
51
|
+
rubygems_version: 2.7.4
|
66
52
|
signing_key:
|
67
53
|
specification_version: 4
|
68
54
|
summary: A simple, comprehensive Ruby gem for parsing user agent strings with the
|