From 4a12562946c935266783db9feed5e1f35589842d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 14 Nov 2024 22:51:41 +0700 Subject: [PATCH 1/8] [submodule:extensions] Add new CLI: ELinks, HTTPie --- src/extensions/ua-parser-extensions.js | 4 ++-- src/main/ua-parser.js | 2 +- test/specs/browser-clis.json | 30 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index a6774ad..8734a2f 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -28,8 +28,8 @@ const LIBRARY = 'library'; const CLIs = Object.freeze({ browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] + // wget / curl / Lynx / ELinks / HTTPie + [/(wget|curl|lynx|elinks|httpie)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index c1f07cb..2c5fe6d 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -463,7 +463,7 @@ // Other /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i // Links + /\b(links) \(([\w\.]+)/i // Links ], [NAME, [VERSION, /_/g, '.']], [ /(cobalt)\/([\w\.]+)/i // Cobalt diff --git a/test/specs/browser-clis.json b/test/specs/browser-clis.json index 7d344ff..ce89135 100644 --- a/test/specs/browser-clis.json +++ b/test/specs/browser-clis.json @@ -9,6 +9,36 @@ "type" : "cli" } }, + { + "desc" : "ELinks", + "ua" : "ELinks/0.11.4-3-lite (textmode; Debian; Linux 2.6.26-1-686 i686;", + "expect" : + { + "name" : "ELinks", + "version" : "0.11.4-3-lite", + "type" : "cli" + } + }, + { + "desc" : "ELinks", + "ua" : "ELinks (0.11.3; Linux 2.6.23-hardened-r4 i686; 166x55)", + "expect" : + { + "name" : "ELinks", + "version" : "0.11.3", + "type" : "cli" + } + }, + { + "desc" : "HTTPie", + "ua" : "HTTPie/0.9.9", + "expect" : + { + "name" : "HTTPie", + "version" : "0.9.9", + "type" : "cli" + } + }, { "desc" : "lynx", "ua" : "Lynx 2.8.8dev.3", From 14caf6d204df8f57aad848c0f6886c60c6cc8be3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 15 Nov 2024 17:24:45 +0700 Subject: [PATCH 2/8] [submodule:extensions] Add new library: `java`, `python-urllib`, `python-requests` --- src/extensions/ua-parser-extensions.js | 6 ++-- test/specs/browser-libraries.json | 50 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 8734a2f..628313c 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -293,8 +293,6 @@ const MediaPlayers = Object.freeze({ ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player - /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i, - // Java/urllib/requests/wget/cURL /(lavf)([\d\.]+)/i // Lavf (FFMPEG) ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ @@ -370,8 +368,8 @@ const MediaPlayers = Object.freeze({ const Libraries = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] + // Axios/jsdom/Scrapy/Java/urllib/requests + [/\b(axios|jsdom|scrapy|java|python-urllib|python-requests)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] ] }); diff --git a/test/specs/browser-libraries.json b/test/specs/browser-libraries.json index 8e0eb56..1ebdcdc 100644 --- a/test/specs/browser-libraries.json +++ b/test/specs/browser-libraries.json @@ -1,4 +1,54 @@ [ + { + "desc" : "Axios", + "ua" : "axios/1.7.2", + "expect" : + { + "name" : "axios", + "version" : "1.7.2", + "type" : "library" + } + }, + { + "desc" : "Java", + "ua" : "Java/1.6.0_14", + "expect" : + { + "name" : "Java", + "version" : "1.6.0_14", + "type" : "library" + } + }, + { + "desc" : "jsdom", + "ua" : "Mozilla/5.0 (unknown OS) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/11.12.0", + "expect" : + { + "name" : "jsdom", + "version" : "11.12.0", + "type" : "library" + } + }, + { + "desc" : "Python urllib", + "ua" : "Python-urllib/2.6", + "expect" : + { + "name" : "Python-urllib", + "version" : "2.6", + "type" : "library" + } + }, + { + "desc" : "Python requests", + "ua" : "python-requests/2.32", + "expect" : + { + "name" : "python-requests", + "version" : "2.32", + "type" : "library" + } + }, { "desc" : "Scrapy", "ua" : "Scrapy/1.5.0 (+https://scrapy.org)", From 2181559b01617cbca67f991f8bfb6b983415da4f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 15:28:35 +0700 Subject: [PATCH 3/8] [submodule:extensions] Add new email: Airmail, BlueMail, eMClient, NaverMailApp, Sparrow, Yahoo --- src/extensions/ua-parser-extensions.js | 10 ++- test/specs/browser-emails.json | 90 ++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 628313c..5e836bb 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -42,7 +42,6 @@ const Crawlers = Object.freeze({ [ // AhrefsBot - https://ahrefs.com/robot // Amazonbot - https://developer.amazon.com/amazonbot - // Applebot - http://apple.com/go/applebot // Bingbot - http://www.bing.com/bingbot.htm // CCBot - https://commoncrawl.org/faq // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot @@ -54,7 +53,10 @@ const Crawlers = Object.freeze({ // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SemrushBot - http://www.semrush.com/bot.html - /((?:ahrefs|amazon|apple|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, + /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, + + // Applebot - http://apple.com/go/applebot + /(applebot(?:-extended)?)\/([\w\.]+)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, @@ -195,8 +197,10 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ + [ // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird - [/(evolution|kmail2?|kontact|(?:microsoft |mac)outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] + /(airmail|bluemail|emclient|evolution|foxmail|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|thunderbird|yahoo)(?:m.+ail; |[\/ ])([\w\.]+)/i + ], [NAME, VERSION, [TYPE, EMAIL]] ] }); diff --git a/test/specs/browser-emails.json b/test/specs/browser-emails.json index 883f32b..27fbce5 100644 --- a/test/specs/browser-emails.json +++ b/test/specs/browser-emails.json @@ -1,4 +1,34 @@ [ + { + "desc" : "Airmail", + "ua" : "Airmail 1.0 rv:148 (Macintosh; Mac OS X 10.8.3; en_BE)", + "expect" : + { + "name" : "Airmail", + "version" : "1.0", + "type" : "email" + } + }, + { + "desc" : "BlueMail", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) BlueMail/0.10.31 Chrome/61.0.3163.100 Electron/2.0.18 Safari/537.36", + "expect" : + { + "name" : "BlueMail", + "version" : "0.10.31", + "type" : "email" + } + }, + { + "desc" : "BlueMail", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16A405 BlueMail iOS", + "expect" : + { + "name" : "BlueMail", + "version" : "iOS", + "type" : "email" + } + }, { "desc" : "Evolution", "ua" : "Evolution/3.52.3", @@ -9,6 +39,26 @@ "type" : "email" } }, + { + "desc" : "eM Client", + "ua" : "eMClient/9.2.2157.0", + "expect" : + { + "name" : "eMClient", + "version" : "9.2.2157.0", + "type" : "email" + } + }, + { + "desc" : "Foxmail", + "ua" : "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36 foxmail/7.2.15.80", + "expect" : + { + "name" : "foxmail", + "version" : "7.2.15.80", + "type" : "email" + } + }, { "desc" : "KMail", "ua" : "KMail/4.14.10 (FreeBSD/12.0-CURRENT; KDE/4.14.10; amd64; ; )", @@ -59,6 +109,26 @@ "type" : "email" } }, + { + "desc" : "NaverMailApp", + "ua" : "NaverMailApp/2.1.23 (Android 10; SM-N960N)", + "expect" : + { + "name" : "NaverMailApp", + "version" : "2.1.23", + "type" : "email" + } + }, + { + "desc" : "Sparrow", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Sparrow/1043.1", + "expect" : + { + "name" : "Sparrow", + "version" : "1043.1", + "type" : "email" + } + }, { "desc" : "Thunderbird", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0", @@ -68,5 +138,25 @@ "version" : "78.13.0", "type" : "email" } + }, + { + "desc" : "Yahoo! Mail", + "ua" : "YahooMobile/1.0 (mail; 3.0.5.1311380); (Linux; U; Android 4.0.3; htc_runnymede Build/ICE_CREAM_SANDWICH_MR1);", + "expect" : + { + "name" : "Yahoo", + "version" : "3.0.5.1311380", + "type" : "email" + } + }, + { + "desc" : "Yahoo! Mail", + "ua" : "YahooMobileMail/1.0 (Android Mail; 1.3.10) (supersonic;HTC;PC36100;2.3.5/GRJ90) ", + "expect" : + { + "name" : "Yahoo", + "version" : "1.3.10", + "type" : "email" + } } ] From 2b125c8de45ce77ff735ef78200035cbb8eb733b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 18:40:02 +0700 Subject: [PATCH 4/8] [submodule:extensions] Add new bots: AI2Bot, aiHitBot, anthropic-ai, cohere-ai, Diffbot, ImagesiftBot, magpie-crawler, Omgilibot, Screaming Frog SEO Spider, Seznambot, Teoma, Timpibot, VelenPublicWebCrawler, Vercelbot, Webzio-Extended, YouBot --- src/extensions/ua-parser-extensions.js | 17 ++-- test/specs/browser-crawlers.json | 122 ++++++++++++++++++++++++- test/specs/browser-fetchers.json | 10 ++ 3 files changed, 141 insertions(+), 8 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 5e836bb..bd9d517 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -53,7 +53,8 @@ const Crawlers = Object.freeze({ // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SemrushBot - http://www.semrush.com/bot.html - /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, + // SeznamBot - http://napoveda.seznam.cz/seznambot-intro + /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/([\w\.]+)/i, @@ -62,7 +63,7 @@ const Crawlers = Object.freeze({ /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, // ClaudeBot (Anthropic) - /(claude(?:bot|-web))\/([\w\.]+)/i, + /(claude(?:bot|-web)|anthropic-ai)\/?([\w\.]*)/i, // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, @@ -89,8 +90,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // YisouSpider - /(yisouspider)\/?([\w\.]*)/i + // aiHitBot / Cohere-AI / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|cohere-ai|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -99,13 +100,15 @@ const Crawlers = Object.freeze({ // Google Bots /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, + // AI2Bot - https://allenai.org/crawler // Bytespider // DataForSeoBot - https://dataforseo.com/dataforseo-bot // Huawei AspiegelBot / PetalBot https://aspiegel.com/petalbot + // ImagesiftBot - https://imagesift.com/about // Qihoo 360Spider // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - /(360spider-?(?:image|video)?|bytespider|(?:aspiegel|dataforseo|petal|turnitin)bot|(?=yahoo! )slurp)/i + /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|(?=yahoo! )slurp)/i ], [NAME, [TYPE, CRAWLER]] ] @@ -238,8 +241,8 @@ const Fetchers = Object.freeze({ ], [NAME, VERSION, [TYPE, FETCHER]], - // Google Bots / Snapchat - [/(feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], + // Google Bots / Snapchat / Vercelbot + [/(vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], [NAME, [TYPE, FETCHER]], ] }); diff --git a/test/specs/browser-crawlers.json b/test/specs/browser-crawlers.json index e527740..04a1a35 100644 --- a/test/specs/browser-crawlers.json +++ b/test/specs/browser-crawlers.json @@ -49,6 +49,26 @@ "type" : "crawler" } }, + { + "desc" : "AI2Bot", + "ua" : "Mozilla/5.0 (compatible) AI2Bot (+https://www.allenai.org/crawler)", + "expect" : + { + "name" : "AI2Bot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "aiHitBot", + "ua" : "Mozilla/5.0 (compatible; aiHitBot/2.9; +https://www.aihitdata.com/about)", + "expect" : + { + "name" : "aiHitBot", + "version" : "2.9", + "type" : "crawler" + } + }, { "desc" : "Applebot", "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)", @@ -131,7 +151,7 @@ }, { "desc" : "DataForSEO", - "ua" : "Mozilla/5.0 (compatible; DataForSeoBot; +https://dataforseo.com/dataforseo-bot) ", + "ua" : "Mozilla/5.0 (compatible; DataForSeoBot; +https://dataforseo.com/dataforseo-bot)", "expect" : { "name" : "DataForSeoBot", @@ -139,6 +159,16 @@ "type" : "crawler" } }, + { + "desc" : "Diffbot", + "ua" : "Diffbot/0.1", + "expect" : + { + "name" : "Diffbot", + "version" : "0.1", + "type" : "crawler" + } + }, { "desc" : "Dotbot", "ua" : "Mozilla/5.0 (compatible; DotBot/1.2; +https://opensiteexplorer.org/dotbot; help@moz.com)", @@ -329,6 +359,26 @@ "type" : "crawler" } }, + { + "desc" : "ImagesiftBot", + "ua" : "Mozilla/5.0 (compatible; ImagesiftBot; +imagesift.com)", + "expect" : + { + "name" : "ImagesiftBot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "magpie-crawler", + "ua" : "magpie-crawler/1.1 (robots-txt-checker; +http://www.brandwatch.net)", + "expect" : + { + "name" : "magpie-crawler", + "version" : "1.1", + "type" : "crawler" + } + }, { "desc" : "Meta-ExternalAgent", "ua" : "meta-externalagent/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", @@ -360,6 +410,26 @@ "type" : "crawler" } }, + { + "desc" : "Omgili", + "ua" : "omgili/0.5 +https://omgili.com", + "expect" : + { + "name" : "omgili", + "version" : "0.5", + "type" : "crawler" + } + }, + { + "desc" : "Omgilibot", + "ua" : "omgilibot/0.3 +http://www.omgili.com/Crawler.html", + "expect" : + { + "name" : "omgilibot", + "version" : "0.3", + "type" : "crawler" + } + }, { "desc" : "OpenAI Search", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot", @@ -410,6 +480,36 @@ "type" : "crawler" } }, + { + "desc" : "SeznamBot", + "ua" : "Mozilla/5.0 (compatible; SeznamBot/4.0-RC1; +http://napoveda.seznam.cz/seznambot-intro/)", + "expect" : + { + "name" : "SeznamBot", + "version" : "4.0-RC1", + "type" : "crawler" + } + }, + { + "desc" : "Teoma", + "ua" : "Mozilla/2.0 (compatible; Ask Jeeves/Teoma; +http://sp.ask.com/docs/about/tech_crawling.html)", + "expect" : + { + "name" : "Teoma", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Timpibot", + "ua" : "Timpibot/0.8 (+http://www.timpi.io)", + "expect" : + { + "name" : "Timpibot", + "version" : "0.8", + "type" : "crawler" + } + }, { "desc" : "TurnitinBot", "ua" : "TurnitinBot (https://turnitin.com/robot/crawlerinfo.html)", @@ -420,6 +520,16 @@ "type" : "crawler" } }, + { + "desc" : "VelenPublicWebCrawler", + "ua" : "Mozilla/5.0 (compatible; VelenPublicWebCrawler/1.0; +https://velen.io)", + "expect" : + { + "name" : "VelenPublicWebCrawler", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Yahoo! Japan", "ua" : "Y!J-BRW/1.0 (https://www.yahoo-help.jp/app/answers/detail/p/595/a_id/42716)", @@ -469,5 +579,15 @@ "version" : "undefined", "type" : "crawler" } + }, + { + "desc" : "YouBot", + "ua" : "YouBot (+http://www.you.com)", + "expect" : + { + "name" : "YouBot", + "version" : "undefined", + "type" : "crawler" + } } ] diff --git a/test/specs/browser-fetchers.json b/test/specs/browser-fetchers.json index 94bada0..dfd76f1 100644 --- a/test/specs/browser-fetchers.json +++ b/test/specs/browser-fetchers.json @@ -118,5 +118,15 @@ "version" : "2.0", "type" : "fetcher" } + }, + { + "desc" : "Vercelbot", + "ua" : "Vercelbot (+https://vercel.com)", + "expect" : + { + "name" : "Vercelbot", + "version" : "undefined", + "type" : "fetcher" + } } ] \ No newline at end of file From b1c7dfcc3a0f24a129fae30f7ccde22d271f0552 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 19:22:43 +0700 Subject: [PATCH 5/8] [submodule:helpers] Revert providing extra param in `isAppleSilicon()` and just check for window instead --- src/helpers/ua-parser-helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index abd5803..4a09605 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -15,12 +15,12 @@ const { isStandalonePWA } = require('is-standalone-pwa'); const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; -const isAppleSilicon = (res, useFeatureDetection) => { +const isAppleSilicon = (res) => { if (res.os.is(OS.MACOS)) { if (res.cpu.is(CPU.ARM)) { return true; } - if (useFeatureDetection) { + if (typeof window !== 'undefined') { try { const canvas = document.createElement('canvas'); const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); From 5b375b90d540642a4243a5c1eb24e7f493e21d3c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 21:17:51 +0700 Subject: [PATCH 6/8] [submodule:helpers] Enable directly pass user-agent as an input to `isAppleSilicon()` / `isBot()` / `isChromeFamily()` --- src/helpers/ua-parser-helpers.d.ts | 6 +++--- src/helpers/ua-parser-helpers.js | 17 +++++++++++++---- test/mocha-test-helpers.js | 9 +++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 10398d6..0e61477 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -5,9 +5,9 @@ import { IResult } from "../main/ua-parser"; declare function getDeviceVendor(model: string): string | undefined; -declare function isAppleSilicon(res: IResult, useFeatureDetection?: boolean): boolean; -declare function isBot(res: IResult): boolean; -declare function isChromeFamily(res: IResult): boolean; +declare function isAppleSilicon(resultOrUA: IResult | string): boolean; +declare function isBot(resultOrUA: IResult | string): boolean; +declare function isChromeFamily(resultOrUA: IResult | string): boolean; declare function isElectron(): boolean; declare function isFromEU(): boolean; declare function isFrozenUA(ua: string): boolean; diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 4a09605..f3f19c8 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -9,18 +9,22 @@ const { UAParser } = require('../main/ua-parser'); const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); +const { Bots } = require('../extensions/ua-parser-extensions'); const { isFromEU } = require('detect-europe-js'); const { isFrozenUA } = require('ua-is-frozen'); const { isStandalonePWA } = require('is-standalone-pwa'); +const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value; + const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; -const isAppleSilicon = (res) => { +const isAppleSilicon = (resultOrUA) => { + const res = toResult(resultOrUA); if (res.os.is(OS.MACOS)) { if (res.cpu.is(CPU.ARM)) { return true; } - if (typeof window !== 'undefined') { + if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') { try { const canvas = document.createElement('canvas'); const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); @@ -37,9 +41,14 @@ const isAppleSilicon = (res) => { return false; } -const isBot = (res) => ['cli', 'crawler', 'fetcher', 'library'].includes(res.browser.type); +const isBot = (resultOrUA) => [ + 'cli', + 'crawler', + 'fetcher', + 'library' + ].includes(toResult(resultOrUA, Bots).browser.type); -const isChromeFamily = (res) => res.engine.is(Engine.BLINK); +const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(Engine.BLINK); const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js / electron\//i.test(navigator?.userAgent)); // browser diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index ab61cd9..a2f25cd 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -28,7 +28,9 @@ describe('isAppleSilicon', () => { const macIntel = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0'; assert.equal(isAppleSilicon(UAParser(macIntel)), false); + assert.equal(isAppleSilicon(macIntel), false); assert.equal(isAppleSilicon(UAParser(macARM)), true); + assert.equal(isAppleSilicon(macARM), true); }); }); @@ -46,6 +48,11 @@ describe('isBot', () => { assert.equal(isBot(botParser.setUA(ahrefsBot).getResult()), true); assert.equal(isBot(botParser.setUA(scrapy).getResult()), true); assert.equal(isBot(botParser.setUA(thunderbird).getResult()), false); + + assert.equal(isBot(ahrefsBot), true); + assert.equal(isBot(firefox), false); + assert.equal(isBot(scrapy), true); + assert.equal(isBot(thunderbird), false); }); }); @@ -57,5 +64,7 @@ describe('isChromeFamily', () => { assert.equal(isChromeFamily(UAParser(edge)), true); assert.equal(isChromeFamily(UAParser(firefox)), false); + assert.equal(isChromeFamily(edge), true); + assert.equal(isChromeFamily(firefox), false); }); }); \ No newline at end of file From 70b3003344dd7dc4d815406d955ac48857d89c55 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 22:14:14 +0700 Subject: [PATCH 7/8] [submodule:helpers] Add new method `isAIBot()`: detect AI bots --- README.md | 10 +++- src/extensions/ua-parser-extensions.js | 8 +-- src/helpers/ua-parser-helpers.d.ts | 2 + src/helpers/ua-parser-helpers.js | 72 ++++++++++++++++++++++++++ test/mocha-test-helpers.js | 16 +++++- 5 files changed, 102 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7b0aa5f..aa7e1c0 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,15 @@ see what's new & breaking. ✅ - Extras (Apps, Libs, Emails, Media Players, etc) + AI Bot detection + ❌ + ✅ + ✅ + ✅ + ✅ + + + Extras (Apps, Libs, Emails, Media Players, etc) detection ❌ ✅ ✅ diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index bd9d517..5ed4f75 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -90,8 +90,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Cohere-AI / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot - /((?:aihit|diff|timpi|you)bot|cohere-ai|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -241,8 +241,8 @@ const Fetchers = Object.freeze({ ], [NAME, VERSION, [TYPE, FETCHER]], - // Google Bots / Snapchat / Vercelbot - [/(vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], + // Google Bots / Cohere / Snapchat / Vercelbot + [/(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], [NAME, [TYPE, FETCHER]], ] }); diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 0e61477..4564a23 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -6,6 +6,7 @@ import { IResult } from "../main/ua-parser"; declare function getDeviceVendor(model: string): string | undefined; declare function isAppleSilicon(resultOrUA: IResult | string): boolean; +declare function isAIBot(resultOrUA: IResult | string): boolean; declare function isBot(resultOrUA: IResult | string): boolean; declare function isChromeFamily(resultOrUA: IResult | string): boolean; declare function isElectron(): boolean; @@ -16,6 +17,7 @@ declare function isStandalonePWA(): boolean; export { getDeviceVendor, isAppleSilicon, + isAIBot, isBot, isChromeFamily, isElectron, diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index f3f19c8..17946ee 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -41,6 +41,77 @@ const isAppleSilicon = (resultOrUA) => { return false; } +const isAIBot = (resultOrUA) => [ + + // AI2 + 'ai2bot', + + // Amazon + 'amazonbot', + + // Anthropic + 'anthropic-ai', + 'claude-web', + 'claudebot', + + // Apple + 'applebot', + 'applebot-extended', + + // ByteDance + 'bytespider', + + // Common Crawl + 'ccbot', + + // DataForSeo + 'dataforseobot', + + // Diffbot + 'diffbot', + + // Google + 'googleother', + 'googleother-image', + 'googleother-video', + 'google-extended', + + // Hive AI + 'imagesiftbot', + + // Huawei + 'petalbot', + + // Meta + 'facebookbot', + 'meta-externalagent', + + // OpenAI + 'gptbot', + 'oai-searchbot', + + // Perplexity + 'perplexitybot', + + // Timpi + 'timpibot', + + // Velen.io + 'velenpublicwebcrawler', + + // Webz.io + 'omgili', + 'omgilibot', + 'webzio-extended', + + // You.com + 'youbot', + + // Zyte + 'scrapy' + + ].includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); + const isBot = (resultOrUA) => [ 'cli', 'crawler', @@ -56,6 +127,7 @@ const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || module.exports = { getDeviceVendor, isAppleSilicon, + isAIBot, isBot, isChromeFamily, isElectron, diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index a2f25cd..d6170bc 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,6 +1,6 @@ const assert = require('assert'); const { UAParser } = require('../src/main/ua-parser'); -const { getDeviceVendor, isAppleSilicon, isBot, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); +const { getDeviceVendor, isAppleSilicon, isAIBot, isBot, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); const { Bots, Emails } = require('../src/extensions/ua-parser-extensions'); describe('getDeviceVendor', () => { @@ -34,6 +34,20 @@ describe('isAppleSilicon', () => { }); }); +describe('isAIBot', () => { + it('Can detect AI Bots', () => { + + const claudeBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)'; + const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'; + const searchGPT = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot'; + + assert.equal(isAIBot(UAParser(claudeBot, Bots)), true); + assert.equal(isAIBot(claudeBot), true); + assert.equal(isAIBot(firefox), false); + assert.equal(isAIBot(searchGPT), true); + }); +}); + describe('isBot', () => { it('Can detect Bots', () => { From 38baf844fc3e96f72d9b91e0772c8c10655fd5f7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 18 Nov 2024 07:54:54 +0700 Subject: [PATCH 8/8] Update version to `2.0.0` --- CHANGELOG.md | 14 +++- dist/ua-parser.min.js | 4 +- dist/ua-parser.pack.js | 4 +- package-lock.json | 4 +- package.json | 5 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 2 +- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 37 +++++----- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 91 ++++++++++++++++++++++-- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 6 +- 16 files changed, 141 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e1966d..0be492a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,8 @@ - **`'ua-parser-js/helpers'`**: Provides utility methods to extend detection functionality: - `getDeviceVendor()`: Guesses the device vendor based on its model name - `isAppleSilicon()`: Detects Apple Silicon device properties - - `isBot()`: Checks if the browser is a bot + - `isAIBot()`: Checks if the user-agent is an AI bot + - `isBot()`: Checks if the user-agent is a bot - `isChromeFamily()`: Checks if the browser is Chrome-based (uses Blink engine) — e.g., New Opera, New Edge, Vivaldi, Brave, Arc, etc. - `isElectron()`: Detects if current window is running within Electron - `isFromEU()`: Detects if current browser's timezone is from an EU country @@ -64,6 +65,17 @@ --- +## Version 2.0.0 + +- `ua-parser-js/extensions` submodule: + - Add new CLI: ELinks, HTTPie + - Add new crawler: AI2Bot, aiHitBot, anthropic-ai, Diffbot, ImagesiftBot, magpie-crawler, Omgilibot, Screaming Frog SEO Spider, Seznambot, Teoma, Timpibot, VelenPublicWebCrawler, Webzio-Extended, YouBot + - Add new email: Airmail, BlueMail, eMClient, NaverMailApp, Sparrow, Yahoo + - Add new fetcher: cohere-ai, Vercelbot + - Add new library: java, python-urllib, python-requests +- `ua-parser-js/helpers` submodule: + - Add new method `isAIBot()`: Checks if the user-agent is an AI bot + ## Version 2.0.0-rc.3 - Add support for Headers object diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index bfc76aa..54ff4b6 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-rc.3 +/* UAParser.js v2.0.0 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-rc.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 55b1df1..6f7b1fb 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-rc.3 +/* UAParser.js v2.0.0 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -!function(i,d){"use strict";function e(i){for(var e={},t=0;tC?Ci(i,C):i),this}]]).setUA(r),this}Ui.VERSION="2.0.0-rc.3",Ui.BROWSER=e([m,v,p,f]),Ui.CPU=e([k]),Ui.DEVICE=e([h,g,f,x,y,t,r,o,s]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):li&&(i.UAParser=Ui);var ji,Ei=li&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(ji=new Ui,Ei.ua=ji.getResult(),Ei.ua.get=function(){return ji.getUA()},Ei.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,d){"use strict";function e(i){for(var e={},t=0;tC?Ci(i,C):i),this}]]).setUA(r),this}Ui.VERSION="2.0.0",Ui.BROWSER=e([m,v,p,f]),Ui.CPU=e([k]),Ui.DEVICE=e([h,g,f,x,y,t,r,o,s]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):li&&(i.UAParser=Ui);var ji,Ei=li&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(ji=new Ui,Ei.ua=ji.getResult(),Ei.ua.get=function(){return ji.getUA()},Ei.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 42acf6b..56374f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-rc.3", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-rc.3", + "version": "2.0.0", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index 73f74c8..5a53978 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-rc.3", + "version": "2.0.0", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ @@ -180,7 +180,8 @@ }, "./enums": { "require": "./src/enums/ua-parser-enums.js", - "import": "./src/enums/ua-parser-enums.mjs" + "import": "./src/enums/ua-parser-enums.mjs", + "types": "./src/enums/ua-parser-enums.d.ts" }, "./extensions": { "require": "./src/extensions/ua-parser-extensions.js", diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index ff0156d..ab46296 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-rc.3 +/* Enums for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index dff46cb..c9a2f0f 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-rc.3 +/* Enums for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index b4da549..d1d0a12 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.0 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 5ed4f75..2766271 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-rc.3 +/* Extensions for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 5fc374d..20fbee6 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-rc.3 +/* Extensions for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -32,8 +32,8 @@ const LIBRARY = 'library'; const CLIs = Object.freeze({ browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] + // wget / curl / Lynx / ELinks / HTTPie + [/(wget|curl|lynx|elinks|httpie)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); @@ -46,7 +46,6 @@ const Crawlers = Object.freeze({ [ // AhrefsBot - https://ahrefs.com/robot // Amazonbot - https://developer.amazon.com/amazonbot - // Applebot - http://apple.com/go/applebot // Bingbot - http://www.bing.com/bingbot.htm // CCBot - https://commoncrawl.org/faq // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot @@ -58,13 +57,17 @@ const Crawlers = Object.freeze({ // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SemrushBot - http://www.semrush.com/bot.html - /((?:ahrefs|amazon|apple|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, + // SeznamBot - http://napoveda.seznam.cz/seznambot-intro + /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + + // Applebot - http://apple.com/go/applebot + /(applebot(?:-extended)?)\/([\w\.]+)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, // ClaudeBot (Anthropic) - /(claude(?:bot|-web))\/([\w\.]+)/i, + /(claude(?:bot|-web)|anthropic-ai)\/?([\w\.]*)/i, // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, @@ -91,8 +94,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // YisouSpider - /(yisouspider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -101,13 +104,15 @@ const Crawlers = Object.freeze({ // Google Bots /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, + // AI2Bot - https://allenai.org/crawler // Bytespider // DataForSeoBot - https://dataforseo.com/dataforseo-bot // Huawei AspiegelBot / PetalBot https://aspiegel.com/petalbot + // ImagesiftBot - https://imagesift.com/about // Qihoo 360Spider // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - /(360spider-?(?:image|video)?|bytespider|(?:aspiegel|dataforseo|petal|turnitin)bot|(?=yahoo! )slurp)/i + /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|(?=yahoo! )slurp)/i ], [NAME, [TYPE, CRAWLER]] ] @@ -199,8 +204,10 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ + [ // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird - [/(evolution|kmail2?|kontact|(?:microsoft |mac)outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] + /(airmail|bluemail|emclient|evolution|foxmail|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|thunderbird|yahoo)(?:m.+ail; |[\/ ])([\w\.]+)/i + ], [NAME, VERSION, [TYPE, EMAIL]] ] }); @@ -238,8 +245,8 @@ const Fetchers = Object.freeze({ ], [NAME, VERSION, [TYPE, FETCHER]], - // Google Bots / Snapchat - [/(feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], + // Google Bots / Cohere / Snapchat / Vercelbot + [/(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], [NAME, [TYPE, FETCHER]], ] }); @@ -297,8 +304,6 @@ const MediaPlayers = Object.freeze({ ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player - /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i, - // Java/urllib/requests/wget/cURL /(lavf)([\d\.]+)/i // Lavf (FFMPEG) ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ @@ -374,8 +379,8 @@ const MediaPlayers = Object.freeze({ const Libraries = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] + // Axios/jsdom/Scrapy/Java/urllib/requests + [/\b(axios|jsdom|scrapy|java|python-urllib|python-requests)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] ] }); diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 4564a23..30a2c81 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.0 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 17946ee..4af5175 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-rc.3 +/* Helpers for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index 5e28bf6..c8d1669 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-rc.3 +/* Helpers for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -13,18 +13,22 @@ import { UAParser } from '../main/ua-parser.mjs'; import { CPU, OS, Engine } from '../enums/ua-parser-enums.mjs'; +import { Bots } from '../extensions/ua-parser-extensions.mjs'; import { isFromEU } from 'detect-europe-js'; import { isFrozenUA } from 'ua-is-frozen'; import { isStandalonePWA } from 'is-standalone-pwa'; +const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value; + const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; -const isAppleSilicon = (res, useFeatureDetection) => { +const isAppleSilicon = (resultOrUA) => { + const res = toResult(resultOrUA); if (res.os.is(OS.MACOS)) { if (res.cpu.is(CPU.ARM)) { return true; } - if (useFeatureDetection) { + if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') { try { const canvas = document.createElement('canvas'); const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); @@ -41,9 +45,85 @@ const isAppleSilicon = (res, useFeatureDetection) => { return false; } -const isBot = (res) => ['cli', 'crawler', 'fetcher', 'library'].includes(res.browser.type); +const isAIBot = (resultOrUA) => [ -const isChromeFamily = (res) => res.engine.is(Engine.BLINK); + // AI2 + 'ai2bot', + + // Amazon + 'amazonbot', + + // Anthropic + 'anthropic-ai', + 'claude-web', + 'claudebot', + + // Apple + 'applebot', + 'applebot-extended', + + // ByteDance + 'bytespider', + + // Common Crawl + 'ccbot', + + // DataForSeo + 'dataforseobot', + + // Diffbot + 'diffbot', + + // Google + 'googleother', + 'googleother-image', + 'googleother-video', + 'google-extended', + + // Hive AI + 'imagesiftbot', + + // Huawei + 'petalbot', + + // Meta + 'facebookbot', + 'meta-externalagent', + + // OpenAI + 'gptbot', + 'oai-searchbot', + + // Perplexity + 'perplexitybot', + + // Timpi + 'timpibot', + + // Velen.io + 'velenpublicwebcrawler', + + // Webz.io + 'omgili', + 'omgilibot', + 'webzio-extended', + + // You.com + 'youbot', + + // Zyte + 'scrapy' + + ].includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); + +const isBot = (resultOrUA) => [ + 'cli', + 'crawler', + 'fetcher', + 'library' + ].includes(toResult(resultOrUA, Bots).browser.type); + +const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(Engine.BLINK); const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js / electron\//i.test(navigator?.userAgent)); // browser @@ -51,6 +131,7 @@ const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || export { getDeviceVendor, isAppleSilicon, + isAIBot, isBot, isChromeFamily, isElectron, diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 4e64bf4..150d867 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.0-rc.3 +// Type definitions for UAParser.js v2.0.0 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 2c5fe6d..b6feb1a 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-rc.3 +/* UAParser.js v2.0.0 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-rc.3', + var LIBVERSION = '2.0.0', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 80e1770..09dbce3 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-rc.3 +/* UAParser.js v2.0.0 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-rc.3', + var LIBVERSION = '2.0.0', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -465,7 +465,7 @@ // Other /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i // Links + /\b(links) \(([\w\.]+)/i // Links ], [NAME, [VERSION, /_/g, '.']], [ /(cobalt)\/([\w\.]+)/i // Cobalt