@shware/analytics 8.14.0 → 8.15.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.
- package/dist/bot/generic.cjs +225 -0
- package/dist/bot/generic.cjs.map +1 -0
- package/dist/bot/generic.d.cts +13 -0
- package/dist/bot/generic.d.cts.map +1 -0
- package/dist/bot/generic.d.mts +13 -0
- package/dist/bot/generic.d.mts.map +1 -0
- package/dist/bot/generic.mjs +223 -0
- package/dist/bot/generic.mjs.map +1 -0
- package/dist/bot/index.cjs +1151 -0
- package/dist/bot/index.cjs.map +1 -0
- package/dist/bot/index.d.cts +87 -0
- package/dist/bot/index.d.cts.map +1 -0
- package/dist/bot/index.d.mts +87 -0
- package/dist/bot/index.d.mts.map +1 -0
- package/dist/bot/index.mjs +1146 -0
- package/dist/bot/index.mjs.map +1 -0
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/schema/index.cjs +1 -0
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +6 -0
- package/dist/schema/index.d.cts.map +1 -1
- package/dist/schema/index.d.mts +6 -0
- package/dist/schema/index.d.mts.map +1 -1
- package/dist/schema/index.mjs +1 -0
- package/dist/schema/index.mjs.map +1 -1
- package/dist/track/types.d.cts +2 -0
- package/dist/track/types.d.cts.map +1 -1
- package/dist/track/types.d.mts +2 -0
- package/dist/track/types.d.mts.map +1 -1
- package/dist/web/index.cjs +1 -0
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts.map +1 -1
- package/dist/web/index.d.mts.map +1 -1
- package/dist/web/index.mjs +1 -0
- package/dist/web/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["GENERIC_BOT"],"sources":["../../src/bot/index.ts"],"sourcesContent":["import type { TrackTags } from '../track/types';\nimport { GENERIC_BOT } from './generic';\n\n/**\n * What kind of automated visitor a bot is, as the analytics tools that report bots separately\n * group them (PostHog's traffic categories, Cloudflare's verified bot categories, Matomo's\n * DeviceDetector). Stored next to the name so dashboards can group without re-deriving:\n *\n * - search_crawler: indexes pages for a search engine (Googlebot, Bingbot, Baiduspider)\n * - ai_crawler: collects content to train models (GPTBot, ClaudeBot, Meta-ExternalAgent)\n * - ai_search: indexes pages for an AI search product (OAI-SearchBot, PerplexityBot)\n * - ai_assistant: fetches a page because a person asked an assistant to (ChatGPT-User)\n * - advertising: works for an ad system — reviews landing pages, targets or verifies ads\n * (Meta-ExternalAds, AdsBot-Google, Mediapartners-Google)\n * - link_preview: renders the card of a link shared in a feed or a chat (facebookexternalhit)\n * - seo_tool: SEO, marketing and page-speed tools (AhrefsBot, Lighthouse, HubSpot)\n * - monitoring: uptime checks, synthetics and service callers (Pingdom, Stripe)\n * - headless_browser: a scripted browser (HeadlessChrome, Playwright, a WebDriver session)\n * - http_client: a library or command-line client (curl, python-requests)\n * - no_user_agent: a request without a user agent, which no browser sends\n * - other: automated by every sign, but none of the above\n */\nexport const BOT_CATEGORIES = [\n 'search_crawler',\n 'ai_crawler',\n 'ai_search',\n 'ai_assistant',\n 'advertising',\n 'link_preview',\n 'seo_tool',\n 'monitoring',\n 'headless_browser',\n 'http_client',\n 'no_user_agent',\n 'other',\n] as const;\n\nexport type BotCategory = (typeof BOT_CATEGORIES)[number];\n\n/**\n * The verdict, shaped like the columns a product stores it in (`is_bot`, `bot_name`,\n * `bot_category`). A person is `{ is_bot: false, bot_name: null, bot_category: null }`.\n */\nexport type Bot =\n | { is_bot: true; bot_name: string; bot_category: BotCategory }\n | { is_bot: false; bot_name: null; bot_category: null };\n\nexport const NOT_A_BOT: Bot = { is_bot: false, bot_name: null, bot_category: null };\n\n/**\n * Bots we can name, as `[name, category, pattern]`, tried in order; the first match wins.\n *\n * Where the list comes from (checked 2026-09-30):\n * - bowser's bot parsers (the ~25 named bots its `platform.type === 'bot'` reports), which this\n * replaces.\n * - PostHog's `bot_definitions.py` (web analytics traffic classification), for the bots and\n * categories an analytics product sees most.\n * - ai.robots.txt (`robots.json`), for AI crawlers, search bots, assistants and agents.\n * - The operators' own docs for the big ones: Google's crawler list, OpenAI's and Anthropic's\n * bot pages, Meta's web crawlers, Perplexity, Bing, Apple.\n * - Bots seen in production (Meta-ExternalAds, HubSpot, Bytespider).\n * Every entry was run against the real user agents in crawler-user-agents and PostHog's\n * fixtures, and bowser's browser fixtures were run to check no browser is taken for a bot\n * (Sogou's browser against its spider, Pinterest's in-app browser against its bot); see\n * `index.test.ts` for where to get them.\n *\n * The link above each entry is the operator's page about the bot, else its product page, else\n * its Known Agents profile (knownagents.com, formerly Dark Visitors) when the operator has none.\n *\n * Names are the operators' spelling of their user agent token. Order matters where one token\n * contains another or borrows a name: advertising and SEO tools come before the search engines\n * whose names they carry, and the more specific of Meta's, Anthropic's and Perplexity's tokens\n * come first. The long tail is left to the generic pattern, filed as `other` and named by\n * `nameOfUnknownBot`; add a bot here when it is worth its own category or name in a dashboard.\n */\nexport const NAMED_BOTS = [\n // advertising: before the search crawlers, AdsBot and Mediapartners carry Google's name\n // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers\n ['Meta-ExternalAds', 'advertising', /meta-externalads/i],\n // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers\n ['facebookcatalog', 'advertising', /facebookcatalog/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-special-case-crawlers\n ['AdsBot-Google', 'advertising', /adsbot-google/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-special-case-crawlers\n ['Mediapartners-Google', 'advertising', /mediapartners-google/i],\n // https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0\n ['AdIdxBot', 'advertising', /adidxbot/i],\n // https://support.google.com/adwords/answer/2404197\n ['Google-AdWords', 'advertising', /google-adwords/i],\n // https://support.google.com/google-ads/answer/6095821\n ['Google-Ads-Conversions', 'advertising', /google-ads-conversions/i],\n // https://developers.openai.com/api/docs/bots\n ['OAI-AdsBot', 'advertising', /oai-adsbot/i],\n // https://businesshelp.snapchat.com/s/article/adsbot-crawler\n ['SnapchatAds', 'advertising', /snapchatads/i],\n // https://www.taboola.com\n ['Taboolabot', 'advertising', /taboolabot/i],\n\n // AI assistants and agents: a person asked for this page\n // https://developers.openai.com/api/docs/bots\n ['ChatGPT-User', 'ai_assistant', /chatgpt-user/i],\n // https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler\n ['Claude-User', 'ai_assistant', /claude-user/i],\n // https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler\n ['Claude-Web', 'ai_assistant', /claude-web/i],\n // https://docs.perplexity.ai/guides/bots\n ['Perplexity-User', 'ai_assistant', /perplexity-user/i],\n // https://duckduckgo.com/duckduckgo-help-pages/results/duckassistbot\n ['DuckAssistBot', 'ai_assistant', /duckassistbot/i],\n // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers\n ['Meta-ExternalFetcher', 'ai_assistant', /meta-externalfetcher/i],\n // https://docs.mistral.ai/robots\n ['MistralAI-User', 'ai_assistant', /mistralai-user/i],\n // https://knownagents.com/agents/manus-user\n ['Manus-User', 'ai_assistant', /manus-user/i],\n // https://developer.amazon.com/amazonbot\n ['Amzn-User', 'ai_assistant', /amzn-user/i],\n // https://knownagents.com/agents/amazon-qbusiness\n ['amazon-QBusiness', 'ai_assistant', /amazon-qbusiness/i],\n // https://docs.aws.amazon.com/bedrock-agentcore/\n ['Amazon-Bedrock-AgentCore-Browser', 'ai_assistant', /amazon-bedrock-agentcore/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-user-triggered-fetchers\n ['Google-Agent', 'ai_assistant', /google-?agent/i],\n // https://gemini.google/overview/deep-research/\n ['Gemini-Deep-Research', 'ai_assistant', /gemini-deep-research/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-user-triggered-fetchers\n ['Google-NotebookLM', 'ai_assistant', /google-notebooklm/i],\n // https://www.kimi.ai/policies/kimi-crawlers\n ['Kimi-User', 'ai_assistant', /kimi-(?:user|agent)/i],\n // https://knownagents.com/agents/tongyibot\n ['TongyiBot', 'ai_assistant', /tongyibot/i],\n // https://knownagents.com/agents/yiyanbot\n ['YiyanBot', 'ai_assistant', /yiyanbot/i],\n // https://help.kagi.com/kagi/ai/kagi-ai.html\n ['kagi-fetcher', 'ai_assistant', /kagi-fetcher/i],\n // https://devin.ai\n ['Devin', 'ai_assistant', /\\bdevin\\/\\d/i],\n // https://knownagents.com/agents/novaact\n ['NovaAct', 'ai_assistant', /novaact/i],\n\n // AI search\n // https://developers.openai.com/api/docs/bots\n ['OAI-SearchBot', 'ai_search', /oai-searchbot/i],\n // https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler\n ['Claude-SearchBot', 'ai_search', /claude-searchbot/i],\n // https://docs.perplexity.ai/guides/bots\n ['PerplexityBot', 'ai_search', /perplexitybot/i],\n // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers\n ['Meta-WebIndexer', 'ai_search', /meta-webindexer/i],\n // https://developer.amazon.com/amazonbot\n ['Amazonbot', 'ai_search', /amazonbot/i],\n // https://knownagents.com/agents/youbot\n ['YouBot', 'ai_search', /youbot/i],\n // https://knownagents.com/agents/phindbot\n ['PhindBot', 'ai_search', /phindbot/i],\n // https://developer.amazon.com/amazonbot\n ['Amzn-SearchBot', 'ai_search', /amzn-searchbot/i],\n // https://knownagents.com/agents/azureai-searchbot\n ['AzureAI-SearchBot', 'ai_search', /azureai-searchbot/i],\n // https://www.kimi.ai/policies/kimi-crawlers\n ['Kimi-SearchBot', 'ai_search', /kimi-searchbot/i],\n // https://docs.mistral.ai/robots\n ['MistralAI-Index', 'ai_search', /mistralai-index/i],\n // https://knownagents.com/agents/exasearchbot\n ['ExaSearchBot', 'ai_search', /exasearchbot|\\bexabot/i],\n // https://knownagents.com/agents/tavilybot\n ['TavilyBot', 'ai_search', /tavilybot/i],\n // https://linkup.so/bot\n ['LinkupBot', 'ai_search', /linkupbot/i],\n // https://knownagents.com/agents/iaskspider\n ['iAskBot', 'ai_search', /iask(?:bot|spider)/i],\n ['Andibot', 'ai_search', /andibot/i],\n // https://developers.cloudflare.com/autorag\n ['Cloudflare-AutoRAG', 'ai_search', /cloudflare-autorag/i],\n\n // AI training\n // https://developers.openai.com/api/docs/bots\n ['GPTBot', 'ai_crawler', /gptbot/i],\n // https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler\n ['ClaudeBot', 'ai_crawler', /claudebot/i],\n // https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler\n ['anthropic-ai', 'ai_crawler', /anthropic-ai/i],\n // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers\n ['Meta-ExternalAgent', 'ai_crawler', /meta-externalagent/i],\n // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers\n ['FacebookBot', 'ai_crawler', /facebookbot/i],\n // https://knownagents.com/agents/bytespider\n ['Bytespider', 'ai_crawler', /bytespider/i],\n // https://commoncrawl.org/ccbot\n ['CCBot', 'ai_crawler', /ccbot/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers\n ['Google-CloudVertexBot', 'ai_crawler', /google-cloudvertexbot/i],\n // https://knownagents.com/agents/cohere-training-data-crawler\n ['cohere-ai', 'ai_crawler', /cohere-(?:ai|training-data-crawler)/i],\n // https://www.diffbot.com/docs/crawl/faq/robots-txt\n ['Diffbot', 'ai_crawler', /diffbot/i],\n // https://knownagents.com/agents/webzio-extended\n ['Omgili', 'ai_crawler', /omgili|webzio-extended/i],\n // https://imagesift.com/about\n ['ImagesiftBot', 'ai_crawler', /imagesiftbot/i],\n // https://www.timpi.io\n ['Timpibot', 'ai_crawler', /timpibot/i],\n // https://knownagents.com/agents/ai2bot\n ['AI2Bot', 'ai_crawler', /ai2bot/i],\n // https://knownagents.com/agents/pangubot\n ['PanguBot', 'ai_crawler', /pangubot/i],\n // https://knownagents.com/agents/tiktokspider\n ['TikTokSpider', 'ai_crawler', /tiktokspider/i],\n // https://knownagents.com/agents/deepseekbot\n ['DeepSeekBot', 'ai_crawler', /deepseekbot/i],\n // https://knownagents.com/agents/doubaobot\n ['DoubaoBot', 'ai_crawler', /doubaobot/i],\n // https://knownagents.com/agents/erniebot\n ['ERNIEBot', 'ai_crawler', /erniebot/i],\n // https://knownagents.com/agents/qwenbot\n ['QwenBot', 'ai_crawler', /qwenbot/i],\n // https://www.kimi.ai/policies/kimi-crawlers\n ['KimiBot', 'ai_crawler', /kimibot/i],\n // https://knownagents.com/agents/chatglm-spider\n ['ChatGLM-Spider', 'ai_crawler', /chatglm-spider/i],\n // https://docs.mistral.ai/robots\n ['MistralAI-Training', 'ai_crawler', /mistralai-training/i],\n // https://knownagents.com/agents/yandexadditional\n ['YandexAdditional', 'ai_crawler', /yandexadditional/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers\n ['Google-Extended', 'ai_crawler', /google-extended/i],\n // https://docs.aws.amazon.com/kendra/latest/dg/what-is-kendra.html\n ['Amazon Kendra', 'ai_crawler', /amazon-kendra|kendrabot/i],\n // https://knownagents.com/agents/bedrockbot\n ['bedrockbot', 'ai_crawler', /bedrockbot/i],\n // https://brightdata.com/brightbot\n ['Brightbot', 'ai_crawler', /brightbot/i],\n // https://firecrawl.dev\n ['FirecrawlAgent', 'ai_crawler', /firecrawl/i],\n // https://github.com/unclecode/crawl4ai\n ['Crawl4AI', 'ai_crawler', /crawl4ai/i],\n // https://apify.com/apify/website-content-crawler\n ['ApifyBot', 'ai_crawler', /apify/i],\n // https://github.com/rom1504/img2dataset\n ['img2dataset', 'ai_crawler', /img2dataset/i],\n // https://knownagents.com/agents/laion-huggingface-processor\n ['LAION', 'ai_crawler', /laion/i],\n // https://developers.cloudflare.com/browser-rendering/\n ['Cloudflare Browser Rendering', 'ai_crawler', /cloudflarebrowserrendering/i],\n\n // link previews\n // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers\n ['facebookexternalhit', 'link_preview', /facebookexternalhit/i],\n // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers\n ['Facebot', 'link_preview', /facebot/i],\n // https://developer.x.com/en/docs/x-for-websites/cards/guides/getting-started\n ['Twitterbot', 'link_preview', /twitterbot/i],\n // https://www.linkedin.com/help/linkedin/answer/a521928\n ['LinkedInBot', 'link_preview', /linkedinbot/i],\n // https://api.slack.com/robots\n ['Slackbot', 'link_preview', /slackbot|slack-imgproxy/i],\n // https://discord.com\n ['Discordbot', 'link_preview', /discordbot/i],\n // https://core.telegram.org/bots\n ['TelegramBot', 'link_preview', /telegrambot/i],\n // https://www.whatsapp.com\n ['WhatsApp', 'link_preview', /^whatsapp\\//i],\n // https://www.pinterest.com/bot.html\n ['Pinterestbot', 'link_preview', /pinterestbot|^pinterest\\/0\\./i],\n // https://www.reddit.com\n ['redditbot', 'link_preview', /redditbot/i],\n // https://developers.snap.com/robots\n ['Snap URL Preview', 'link_preview', /snap url preview/i],\n // https://knownagents.com/agents/skypeuripreview\n ['SkypeUriPreview', 'link_preview', /skypeuripreview/i],\n // https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0\n ['BingPreview', 'link_preview', /bingpreview|microsoftpreview/i],\n // https://iframely.com/docs/about\n ['Iframely', 'link_preview', /iframely/i],\n // https://embed.ly\n ['Embedly', 'link_preview', /embedly/i],\n // https://devtalk.kakao.com/t/scrap/33984\n ['KakaoTalk-scrap', 'link_preview', /kakaotalk-scrap/i],\n // https://vk.com/dev/Share\n ['vkShare', 'link_preview', /vkshare/i],\n // https://www.viber.com\n ['Viber', 'link_preview', /^viber\\b|viber-crawler/i],\n // https://github.com/bluesky-social/indigo\n ['Bluesky Cardyb', 'link_preview', /cardyb/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-user-triggered-fetchers\n ['Google-PageRenderer', 'link_preview', /google-pagerenderer/i],\n // https://vercel.com\n ['vercel-screenshot', 'link_preview', /vercel-screenshot/i],\n\n // SEO, marketing and page-speed tools; before the search crawlers, Lighthouse and the\n // Search Console tester borrow Google's name\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers\n ['Google-InspectionTool', 'seo_tool', /google-inspectiontool/i],\n // https://developer.chrome.com/docs/lighthouse\n ['Lighthouse', 'seo_tool', /chrome-lighthouse|google page speed insights/i],\n // https://gtmetrix.com\n ['GTmetrix', 'seo_tool', /gtmetrix/i],\n // https://ahrefs.com/robot\n ['AhrefsBot', 'seo_tool', /ahrefs(?:bot|siteaudit)/i],\n // https://www.semrush.com/bot/\n ['SemrushBot', 'seo_tool', /semrush/i],\n // https://mj12bot.com\n ['MJ12bot', 'seo_tool', /mj12bot/i],\n // https://moz.com/help/moz-procedures/crawlers/dotbot\n ['DotBot', 'seo_tool', /dotbot/i],\n // https://moz.com/help/moz-procedures/crawlers/rogerbot\n ['rogerbot', 'seo_tool', /rogerbot/i],\n // https://www.screamingfrog.co.uk/seo-spider/\n ['Screaming Frog', 'seo_tool', /screaming frog/i],\n // https://serpstatbot.com\n ['SerpstatBot', 'seo_tool', /serpstatbot/i],\n // https://webmeup-crawler.com\n ['BLEXBot', 'seo_tool', /blexbot/i],\n // https://dataforseo.com/dataforseo-bot\n ['DataForSeoBot', 'seo_tool', /dataforseobot/i],\n // https://knownagents.com/agents/hubspot-crawler\n ['HubSpot', 'seo_tool', /hubspot/i],\n // https://search.google.com/structured-data/testing-tool\n ['Google-Structured-Data-Testing-Tool', 'seo_tool', /google-structured-data-testing-tool/i],\n // https://www.webpagetest.org\n ['WebPageTest', 'seo_tool', /\\bptst\\/\\d/i],\n // https://www.dareboost.com\n ['DareBoost', 'seo_tool', /dareboost/i],\n // https://www.exensa.com/crawl\n ['Barkrowler', 'seo_tool', /barkrowler/i],\n // https://seranking.com/backlinks-crawler\n ['SE Ranking', 'seo_tool', /seranking|sebot-wa/i],\n // https://www.brightedge.com\n ['BrightEdge', 'seo_tool', /brightedge/i],\n // https://searchatlas.com\n ['SearchAtlas', 'seo_tool', /searchatlas/i],\n // https://www.siteimprove.com\n ['Siteimprove', 'seo_tool', /siteimprove/i],\n // https://www.dataprovider.com\n ['Dataprovider', 'seo_tool', /dataprovider\\.com/i],\n // https://www.meltwater.com\n ['Meltwater', 'seo_tool', /meltwater/i],\n // https://awario.com/bots.html\n ['Awario', 'seo_tool', /awario/i],\n\n // search engines\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers\n ['Googlebot', 'search_crawler', /googlebot/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers\n ['Storebot-Google', 'search_crawler', /storebot-google/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers\n ['GoogleOther', 'search_crawler', /googleother/i],\n // https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0\n ['Bingbot', 'search_crawler', /bingbot/i],\n // https://support.apple.com/en-us/119829\n ['Applebot', 'search_crawler', /applebot/i],\n // https://www.baidu.com/search/spider.html\n ['Baiduspider', 'search_crawler', /baiduspider/i],\n // https://yandex.com/support/webmaster/robot-workings/check-yandex-robots.html\n ['YandexBot', 'search_crawler', /yandex(?:\\w*bot|images|metrika)/i],\n // https://duckduckgo.com/duckduckgo-help-pages/results/duckduckbot\n ['DuckDuckBot', 'search_crawler', /duckduckbot/i],\n // https://help.yahoo.com/kb/SLN22600.html\n ['Yahoo Slurp', 'search_crawler', /yahoo!?[\\s/]*slurp/i],\n // https://www.sogou.com/docs/help/webmasters.htm#07\n ['Sogou', 'search_crawler', /sogou[\\w\\s-]*spider/i],\n // https://www.so.com/help/help_3_2.html\n ['360Spider', 'search_crawler', /360spider|haosouspider/i],\n // https://knownagents.com/agents/yisouspider\n ['YisouSpider', 'search_crawler', /yisouspider/i],\n // https://webmaster.petalsearch.com/site/petalbot\n ['PetalBot', 'search_crawler', /petalbot/i],\n // https://napoveda.seznam.cz/en/seznambot-intro/\n ['SeznamBot', 'search_crawler', /seznambot/i],\n // https://knownagents.com/agents/yeti\n ['Yeti', 'search_crawler', /\\byeti\\//i],\n // https://help2.line.me/linesearchbot/web/?contentId=50006055&lang=en\n ['Linespider', 'search_crawler', /linespider/i],\n // https://help.qwant.com/bot/\n ['Qwantbot', 'search_crawler', /qwant(?:bot|ify)/i],\n // https://help.coccoc.com/searchengine\n ['coccocbot', 'search_crawler', /coccocbot/i],\n // https://www.mojeek.com/bot.html\n ['MojeekBot', 'search_crawler', /mojeekbot/i],\n // https://search.brave.com/help/brave-search-crawler\n ['Bravebot', 'search_crawler', /bravebot/i],\n // https://bot.seekport.com\n ['SeekportBot', 'search_crawler', /seekport/i],\n // https://mwmbl.org\n ['mwmbl', 'search_crawler', /mwmbl/i],\n // https://archive.org/details/archive.org_bot\n ['Internet Archive', 'search_crawler', /archive\\.org_bot|ia_archiver|archive-it|archiveteam/i],\n\n // monitoring, synthetics, security and consent scanners, service callers\n // https://www.pingdom.com\n ['Pingdom', 'monitoring', /pingdom/i],\n // https://uptimerobot.com\n ['UptimeRobot', 'monitoring', /uptimerobot/i],\n // https://www.statuscake.com\n ['StatusCake', 'monitoring', /statuscake/i],\n // https://www.site24x7.com\n ['Site24x7', 'monitoring', /site24x7/i],\n // https://betterstack.com/docs/uptime/\n ['Better Stack', 'monitoring', /better ?uptime|betterstack/i],\n // https://docs.datadoghq.com/synthetics/\n ['Datadog', 'monitoring', /datadog/i],\n // https://www.checklyhq.com/docs/\n ['Checkly', 'monitoring', /checkly/i],\n // https://docs.newrelic.com/docs/synthetics/\n ['NewRelicPinger', 'monitoring', /newrelicpinger/i],\n // https://github.com/louislam/uptime-kuma\n ['Uptime-Kuma', 'monitoring', /uptime-kuma/i],\n // https://grafana.com/docs/grafana-cloud/testing/synthetic-monitoring/\n ['Grafana Synthetic Monitoring', 'monitoring', /grafanasyntheticmonitoring/i],\n // https://docs.dynatrace.com/docs/observe/digital-experience/synthetic-monitoring\n ['Dynatrace', 'monitoring', /ruxitsynthetic|dynatrace/i],\n // https://docs.splunk.com/observability/en/synthetics/intro-synthetics.html\n ['Splunk', 'monitoring', /splunk/i],\n // https://www.catchpoint.com\n ['Catchpoint', 'monitoring', /catchpoint/i],\n // https://ghostinspector.com\n ['Ghost Inspector', 'monitoring', /ghost inspector/i],\n // https://docs.digitalocean.com/products/uptime/\n ['DigitalOcean Uptime', 'monitoring', /digitalocean uptime/i],\n // https://ohdear.app/docs/faq/what-is-the-oh-dear-checker\n ['OhDear', 'monitoring', /ohdear/i],\n // https://developers.google.com/crawling/docs/crawlers-fetchers/google-special-case-crawlers\n ['Google-Safety', 'monitoring', /google-safety/i],\n // https://www.cookiebot.com\n ['Cookiebot', 'monitoring', /cookiebot/i],\n // https://knownagents.com/agents/onetrust-cmp-scanner\n ['OneTrust', 'monitoring', /onetrust/i],\n // https://www.cookiehub.com\n ['CookieHub', 'monitoring', /cookiehub/i],\n // https://detectify.com/bot/\n ['Detectify', 'monitoring', /detectify/i],\n // https://about.censys.io\n ['CensysInspect', 'monitoring', /censysinspect/i],\n // https://www.bitsight.com\n ['BitSightBot', 'monitoring', /bitsightbot/i],\n // https://github.com/zmap/zgrab2\n ['zgrab', 'monitoring', /zgrab/i],\n // https://nmap.org/book/nse.html\n ['Nmap', 'monitoring', /nmap scripting engine/i],\n // https://docs.stripe.com/webhooks\n ['Stripe', 'monitoring', /stripe\\/1\\.0|stripebot|merchantsecurityscanner/i],\n\n // scripted browsers\n // https://developer.chrome.com/docs/chromium/headless\n ['HeadlessChrome', 'headless_browser', /headlesschrome/i],\n // https://phantomjs.org\n ['PhantomJS', 'headless_browser', /phantomjs/i],\n // https://slimerjs.org\n ['SlimerJS', 'headless_browser', /slimerjs/i],\n // https://playwright.dev\n ['Playwright', 'headless_browser', /playwright/i],\n // https://pptr.dev\n ['Puppeteer', 'headless_browser', /puppeteer/i],\n // https://www.selenium.dev\n ['Selenium', 'headless_browser', /selenium/i],\n // https://www.cypress.io\n ['Cypress', 'headless_browser', /cypress/i],\n // https://lightpanda.io\n ['Lightpanda', 'headless_browser', /lightpanda/i],\n // https://wkhtmltopdf.org\n ['wkhtmltopdf', 'headless_browser', /wkhtmltopdf/i],\n\n // libraries and command-line clients\n // https://curl.se\n ['curl', 'http_client', /^curl\\//i],\n // https://www.gnu.org/software/wget/\n ['Wget', 'http_client', /^wget\\//i],\n // https://requests.readthedocs.io\n ['python-requests', 'http_client', /python-requests/i],\n // https://docs.python.org/3/library/urllib.request.html\n ['python-urllib', 'http_client', /python-urllib/i],\n // https://docs.aiohttp.org\n ['aiohttp', 'http_client', /aiohttp/i],\n // https://www.python-httpx.org\n ['httpx', 'http_client', /python-httpx|^httpx\\//i],\n // https://scrapy.org\n ['Scrapy', 'http_client', /scrapy/i],\n // https://pkg.go.dev/net/http\n ['Go-http-client', 'http_client', /go-http-client/i],\n // https://github.com/square/okhttp\n ['okhttp', 'http_client', /^okhttp\\//i],\n // https://hc.apache.org/httpcomponents-client-5.5.x/\n ['Java', 'http_client', /^java\\/|apache-httpclient/i],\n // https://axios-http.com\n ['axios', 'http_client', /^axios\\//i],\n // https://github.com/node-fetch/node-fetch\n ['node-fetch', 'http_client', /node-fetch/i],\n // https://undici.nodejs.org\n ['undici', 'http_client', /^undici/i],\n // https://www.postman.com\n ['PostmanRuntime', 'http_client', /postmanruntime/i],\n // https://insomnia.rest\n ['insomnia', 'http_client', /^insomnia\\//i],\n // https://metacpan.org/pod/LWP\n ['libwww-perl', 'http_client', /libwww-perl/i],\n // https://docs.guzzlephp.org\n ['Guzzle', 'http_client', /guzzlehttp/i],\n // https://developers.google.com/apps-script/reference/url-fetch\n ['Google-Apps-Script', 'http_client', /google-apps-script/i],\n // https://support.google.com/docs/answer/3093339\n ['GoogleDocs', 'http_client', /googledocs/i],\n] as const satisfies readonly (readonly [string, BotCategory, RegExp])[];\n\nconst botishToken =\n /\\b([\\w.-]*?(?:bot|crawler|spider|scraper|fetcher|agent|preview|checker|monitor)[\\w-]*)\\b/i;\nconst botishWord = /^(?:bot|crawler|spider|scraper|fetcher|agent|preview|checker|monitor)$/i;\nconst compatibleToken = /compatible;\\s*([a-z][\\w.-]*(?: [a-z][\\w.-]*)?)/i;\nconst productTokens = /([a-z][\\w.-]*)\\/\\d/gi;\nconst browserToken =\n /^(?:mozilla|applewebkit|khtml|gecko|chrome|chromium|safari|version|mobile|firefox|edg|edge|opr|msie|trident)$/i;\n\n/**\n * A name for a bot that is not in the named table, from the first of: a token that sounds like\n * one (`Bytespider`, `ABEvalBot`), the name after `compatible;` (`Google-Read-Aloud`), a product\n * token that is not a browser's (`Barkrowler/0.5`), what the generic pattern matched. URLs and\n * email addresses are dropped first, as nearly every crawler links its docs.\n */\nexport function nameOfUnknownBot(userAgent: string, matched: string): string {\n const text = userAgent\n .replace(/\\+?https?:\\/\\/\\S+/gi, ' ')\n .replace(/[\\w.+-]+@[\\w-]+(?:\\.[\\w-]+)+/g, ' ');\n\n const botish = botishToken.exec(text)?.[1];\n if (botish && !botishWord.test(botish)) return botish.slice(0, 64);\n\n const compatible = compatibleToken.exec(text)?.[1];\n if (compatible && !browserToken.test(compatible)) return compatible.slice(0, 64);\n\n for (const [, product] of text.matchAll(productTokens)) {\n if (!browserToken.test(product)) return product.slice(0, 64);\n }\n\n return matched.replace(/^\\W+|\\W+$/g, '').slice(0, 64) || 'unknown';\n}\n\n/**\n * Whether a web visitor is a bot, which one, and what kind. Run on the server when the visitor\n * is created, with the tags it was created with: the user agent is the request's header\n * (`tags.user_agent`, which the server writes), not anything the client says about itself, so\n * the verdict can be recomputed from stored tags when the rules change.\n *\n * Call it for web visitors only: a native app's requests carry an HTTP library's user agent\n * (CFNetwork, okhttp) that would read as a bot, and no crawler runs the app.\n *\n * In order: a bot we can name; a browser driven by WebDriver (`tags.webdriver`, which the SDK\n * sets from `navigator.webdriver`, the one sign a scripted browser with a normal user agent\n * gives); anything the generic pattern calls automated; no user agent at all.\n */\nexport function botOf(tags: TrackTags): Bot {\n const userAgent = typeof tags.user_agent === 'string' ? tags.user_agent.trim() : '';\n\n for (const [name, category, pattern] of NAMED_BOTS) {\n if (pattern.test(userAgent)) return { is_bot: true, bot_name: name, bot_category: category };\n }\n\n const generic = userAgent ? GENERIC_BOT.exec(userAgent) : null;\n const unknownName = generic ? nameOfUnknownBot(userAgent, generic[0]) : null;\n\n // A scripted browser that says so in its user agent takes that name, else it is just WebDriver.\n if (tags.webdriver === true) {\n return { is_bot: true, bot_name: unknownName ?? 'WebDriver', bot_category: 'headless_browser' };\n }\n\n if (unknownName) return { is_bot: true, bot_name: unknownName, bot_category: 'other' };\n\n if (!userAgent)\n return { is_bot: true, bot_name: '(no user agent)', bot_category: 'no_user_agent' };\n\n return NOT_A_BOT;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { TrackTags } from "../track/types.cjs";
|
|
2
|
+
//#region src/bot/index.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* What kind of automated visitor a bot is, as the analytics tools that report bots separately
|
|
5
|
+
* group them (PostHog's traffic categories, Cloudflare's verified bot categories, Matomo's
|
|
6
|
+
* DeviceDetector). Stored next to the name so dashboards can group without re-deriving:
|
|
7
|
+
*
|
|
8
|
+
* - search_crawler: indexes pages for a search engine (Googlebot, Bingbot, Baiduspider)
|
|
9
|
+
* - ai_crawler: collects content to train models (GPTBot, ClaudeBot, Meta-ExternalAgent)
|
|
10
|
+
* - ai_search: indexes pages for an AI search product (OAI-SearchBot, PerplexityBot)
|
|
11
|
+
* - ai_assistant: fetches a page because a person asked an assistant to (ChatGPT-User)
|
|
12
|
+
* - advertising: works for an ad system — reviews landing pages, targets or verifies ads
|
|
13
|
+
* (Meta-ExternalAds, AdsBot-Google, Mediapartners-Google)
|
|
14
|
+
* - link_preview: renders the card of a link shared in a feed or a chat (facebookexternalhit)
|
|
15
|
+
* - seo_tool: SEO, marketing and page-speed tools (AhrefsBot, Lighthouse, HubSpot)
|
|
16
|
+
* - monitoring: uptime checks, synthetics and service callers (Pingdom, Stripe)
|
|
17
|
+
* - headless_browser: a scripted browser (HeadlessChrome, Playwright, a WebDriver session)
|
|
18
|
+
* - http_client: a library or command-line client (curl, python-requests)
|
|
19
|
+
* - no_user_agent: a request without a user agent, which no browser sends
|
|
20
|
+
* - other: automated by every sign, but none of the above
|
|
21
|
+
*/
|
|
22
|
+
export declare const BOT_CATEGORIES: readonly ['search_crawler', 'ai_crawler', 'ai_search', 'ai_assistant', 'advertising', 'link_preview', 'seo_tool', 'monitoring', 'headless_browser', 'http_client', 'no_user_agent', 'other'];
|
|
23
|
+
export type BotCategory = (typeof BOT_CATEGORIES)[number];
|
|
24
|
+
/**
|
|
25
|
+
* The verdict, shaped like the columns a product stores it in (`is_bot`, `bot_name`,
|
|
26
|
+
* `bot_category`). A person is `{ is_bot: false, bot_name: null, bot_category: null }`.
|
|
27
|
+
*/
|
|
28
|
+
export type Bot = {
|
|
29
|
+
is_bot: true;
|
|
30
|
+
bot_name: string;
|
|
31
|
+
bot_category: BotCategory;
|
|
32
|
+
} | {
|
|
33
|
+
is_bot: false;
|
|
34
|
+
bot_name: null;
|
|
35
|
+
bot_category: null;
|
|
36
|
+
};
|
|
37
|
+
export declare const NOT_A_BOT: Bot;
|
|
38
|
+
/**
|
|
39
|
+
* Bots we can name, as `[name, category, pattern]`, tried in order; the first match wins.
|
|
40
|
+
*
|
|
41
|
+
* Where the list comes from (checked 2026-09-30):
|
|
42
|
+
* - bowser's bot parsers (the ~25 named bots its `platform.type === 'bot'` reports), which this
|
|
43
|
+
* replaces.
|
|
44
|
+
* - PostHog's `bot_definitions.py` (web analytics traffic classification), for the bots and
|
|
45
|
+
* categories an analytics product sees most.
|
|
46
|
+
* - ai.robots.txt (`robots.json`), for AI crawlers, search bots, assistants and agents.
|
|
47
|
+
* - The operators' own docs for the big ones: Google's crawler list, OpenAI's and Anthropic's
|
|
48
|
+
* bot pages, Meta's web crawlers, Perplexity, Bing, Apple.
|
|
49
|
+
* - Bots seen in production (Meta-ExternalAds, HubSpot, Bytespider).
|
|
50
|
+
* Every entry was run against the real user agents in crawler-user-agents and PostHog's
|
|
51
|
+
* fixtures, and bowser's browser fixtures were run to check no browser is taken for a bot
|
|
52
|
+
* (Sogou's browser against its spider, Pinterest's in-app browser against its bot); see
|
|
53
|
+
* `index.test.ts` for where to get them.
|
|
54
|
+
*
|
|
55
|
+
* The link above each entry is the operator's page about the bot, else its product page, else
|
|
56
|
+
* its Known Agents profile (knownagents.com, formerly Dark Visitors) when the operator has none.
|
|
57
|
+
*
|
|
58
|
+
* Names are the operators' spelling of their user agent token. Order matters where one token
|
|
59
|
+
* contains another or borrows a name: advertising and SEO tools come before the search engines
|
|
60
|
+
* whose names they carry, and the more specific of Meta's, Anthropic's and Perplexity's tokens
|
|
61
|
+
* come first. The long tail is left to the generic pattern, filed as `other` and named by
|
|
62
|
+
* `nameOfUnknownBot`; add a bot here when it is worth its own category or name in a dashboard.
|
|
63
|
+
*/
|
|
64
|
+
export declare const NAMED_BOTS: readonly [readonly ["Meta-ExternalAds", "advertising", RegExp], readonly ["facebookcatalog", "advertising", RegExp], readonly ["AdsBot-Google", "advertising", RegExp], readonly ["Mediapartners-Google", "advertising", RegExp], readonly ["AdIdxBot", "advertising", RegExp], readonly ["Google-AdWords", "advertising", RegExp], readonly ["Google-Ads-Conversions", "advertising", RegExp], readonly ["OAI-AdsBot", "advertising", RegExp], readonly ["SnapchatAds", "advertising", RegExp], readonly ["Taboolabot", "advertising", RegExp], readonly ["ChatGPT-User", "ai_assistant", RegExp], readonly ["Claude-User", "ai_assistant", RegExp], readonly ["Claude-Web", "ai_assistant", RegExp], readonly ["Perplexity-User", "ai_assistant", RegExp], readonly ["DuckAssistBot", "ai_assistant", RegExp], readonly ["Meta-ExternalFetcher", "ai_assistant", RegExp], readonly ["MistralAI-User", "ai_assistant", RegExp], readonly ["Manus-User", "ai_assistant", RegExp], readonly ["Amzn-User", "ai_assistant", RegExp], readonly ["amazon-QBusiness", "ai_assistant", RegExp], readonly ["Amazon-Bedrock-AgentCore-Browser", "ai_assistant", RegExp], readonly ["Google-Agent", "ai_assistant", RegExp], readonly ["Gemini-Deep-Research", "ai_assistant", RegExp], readonly ["Google-NotebookLM", "ai_assistant", RegExp], readonly ["Kimi-User", "ai_assistant", RegExp], readonly ["TongyiBot", "ai_assistant", RegExp], readonly ["YiyanBot", "ai_assistant", RegExp], readonly ["kagi-fetcher", "ai_assistant", RegExp], readonly ["Devin", "ai_assistant", RegExp], readonly ["NovaAct", "ai_assistant", RegExp], readonly ["OAI-SearchBot", "ai_search", RegExp], readonly ["Claude-SearchBot", "ai_search", RegExp], readonly ["PerplexityBot", "ai_search", RegExp], readonly ["Meta-WebIndexer", "ai_search", RegExp], readonly ["Amazonbot", "ai_search", RegExp], readonly ["YouBot", "ai_search", RegExp], readonly ["PhindBot", "ai_search", RegExp], readonly ["Amzn-SearchBot", "ai_search", RegExp], readonly ["AzureAI-SearchBot", "ai_search", RegExp], readonly ["Kimi-SearchBot", "ai_search", RegExp], readonly ["MistralAI-Index", "ai_search", RegExp], readonly ["ExaSearchBot", "ai_search", RegExp], readonly ["TavilyBot", "ai_search", RegExp], readonly ["LinkupBot", "ai_search", RegExp], readonly ["iAskBot", "ai_search", RegExp], readonly ["Andibot", "ai_search", RegExp], readonly ["Cloudflare-AutoRAG", "ai_search", RegExp], readonly ["GPTBot", "ai_crawler", RegExp], readonly ["ClaudeBot", "ai_crawler", RegExp], readonly ["anthropic-ai", "ai_crawler", RegExp], readonly ["Meta-ExternalAgent", "ai_crawler", RegExp], readonly ["FacebookBot", "ai_crawler", RegExp], readonly ["Bytespider", "ai_crawler", RegExp], readonly ["CCBot", "ai_crawler", RegExp], readonly ["Google-CloudVertexBot", "ai_crawler", RegExp], readonly ["cohere-ai", "ai_crawler", RegExp], readonly ["Diffbot", "ai_crawler", RegExp], readonly ["Omgili", "ai_crawler", RegExp], readonly ["ImagesiftBot", "ai_crawler", RegExp], readonly ["Timpibot", "ai_crawler", RegExp], readonly ["AI2Bot", "ai_crawler", RegExp], readonly ["PanguBot", "ai_crawler", RegExp], readonly ["TikTokSpider", "ai_crawler", RegExp], readonly ["DeepSeekBot", "ai_crawler", RegExp], readonly ["DoubaoBot", "ai_crawler", RegExp], readonly ["ERNIEBot", "ai_crawler", RegExp], readonly ["QwenBot", "ai_crawler", RegExp], readonly ["KimiBot", "ai_crawler", RegExp], readonly ["ChatGLM-Spider", "ai_crawler", RegExp], readonly ["MistralAI-Training", "ai_crawler", RegExp], readonly ["YandexAdditional", "ai_crawler", RegExp], readonly ["Google-Extended", "ai_crawler", RegExp], readonly ["Amazon Kendra", "ai_crawler", RegExp], readonly ["bedrockbot", "ai_crawler", RegExp], readonly ["Brightbot", "ai_crawler", RegExp], readonly ["FirecrawlAgent", "ai_crawler", RegExp], readonly ["Crawl4AI", "ai_crawler", RegExp], readonly ["ApifyBot", "ai_crawler", RegExp], readonly ["img2dataset", "ai_crawler", RegExp], readonly ["LAION", "ai_crawler", RegExp], readonly ["Cloudflare Browser Rendering", "ai_crawler", RegExp], readonly ["facebookexternalhit", "link_preview", RegExp], readonly ["Facebot", "link_preview", RegExp], readonly ["Twitterbot", "link_preview", RegExp], readonly ["LinkedInBot", "link_preview", RegExp], readonly ["Slackbot", "link_preview", RegExp], readonly ["Discordbot", "link_preview", RegExp], readonly ["TelegramBot", "link_preview", RegExp], readonly ["WhatsApp", "link_preview", RegExp], readonly ["Pinterestbot", "link_preview", RegExp], readonly ["redditbot", "link_preview", RegExp], readonly ["Snap URL Preview", "link_preview", RegExp], readonly ["SkypeUriPreview", "link_preview", RegExp], readonly ["BingPreview", "link_preview", RegExp], readonly ["Iframely", "link_preview", RegExp], readonly ["Embedly", "link_preview", RegExp], readonly ["KakaoTalk-scrap", "link_preview", RegExp], readonly ["vkShare", "link_preview", RegExp], readonly ["Viber", "link_preview", RegExp], readonly ["Bluesky Cardyb", "link_preview", RegExp], readonly ["Google-PageRenderer", "link_preview", RegExp], readonly ["vercel-screenshot", "link_preview", RegExp], readonly ["Google-InspectionTool", "seo_tool", RegExp], readonly ["Lighthouse", "seo_tool", RegExp], readonly ["GTmetrix", "seo_tool", RegExp], readonly ["AhrefsBot", "seo_tool", RegExp], readonly ["SemrushBot", "seo_tool", RegExp], readonly ["MJ12bot", "seo_tool", RegExp], readonly ["DotBot", "seo_tool", RegExp], readonly ["rogerbot", "seo_tool", RegExp], readonly ["Screaming Frog", "seo_tool", RegExp], readonly ["SerpstatBot", "seo_tool", RegExp], readonly ["BLEXBot", "seo_tool", RegExp], readonly ["DataForSeoBot", "seo_tool", RegExp], readonly ["HubSpot", "seo_tool", RegExp], readonly ["Google-Structured-Data-Testing-Tool", "seo_tool", RegExp], readonly ["WebPageTest", "seo_tool", RegExp], readonly ["DareBoost", "seo_tool", RegExp], readonly ["Barkrowler", "seo_tool", RegExp], readonly ["SE Ranking", "seo_tool", RegExp], readonly ["BrightEdge", "seo_tool", RegExp], readonly ["SearchAtlas", "seo_tool", RegExp], readonly ["Siteimprove", "seo_tool", RegExp], readonly ["Dataprovider", "seo_tool", RegExp], readonly ["Meltwater", "seo_tool", RegExp], readonly ["Awario", "seo_tool", RegExp], readonly ["Googlebot", "search_crawler", RegExp], readonly ["Storebot-Google", "search_crawler", RegExp], readonly ["GoogleOther", "search_crawler", RegExp], readonly ["Bingbot", "search_crawler", RegExp], readonly ["Applebot", "search_crawler", RegExp], readonly ["Baiduspider", "search_crawler", RegExp], readonly ["YandexBot", "search_crawler", RegExp], readonly ["DuckDuckBot", "search_crawler", RegExp], readonly ["Yahoo Slurp", "search_crawler", RegExp], readonly ["Sogou", "search_crawler", RegExp], readonly ["360Spider", "search_crawler", RegExp], readonly ["YisouSpider", "search_crawler", RegExp], readonly ["PetalBot", "search_crawler", RegExp], readonly ["SeznamBot", "search_crawler", RegExp], readonly ["Yeti", "search_crawler", RegExp], readonly ["Linespider", "search_crawler", RegExp], readonly ["Qwantbot", "search_crawler", RegExp], readonly ["coccocbot", "search_crawler", RegExp], readonly ["MojeekBot", "search_crawler", RegExp], readonly ["Bravebot", "search_crawler", RegExp], readonly ["SeekportBot", "search_crawler", RegExp], readonly ["mwmbl", "search_crawler", RegExp], readonly ["Internet Archive", "search_crawler", RegExp], readonly ["Pingdom", "monitoring", RegExp], readonly ["UptimeRobot", "monitoring", RegExp], readonly ["StatusCake", "monitoring", RegExp], readonly ["Site24x7", "monitoring", RegExp], readonly ["Better Stack", "monitoring", RegExp], readonly ["Datadog", "monitoring", RegExp], readonly ["Checkly", "monitoring", RegExp], readonly ["NewRelicPinger", "monitoring", RegExp], readonly ["Uptime-Kuma", "monitoring", RegExp], readonly ["Grafana Synthetic Monitoring", "monitoring", RegExp], readonly ["Dynatrace", "monitoring", RegExp], readonly ["Splunk", "monitoring", RegExp], readonly ["Catchpoint", "monitoring", RegExp], readonly ["Ghost Inspector", "monitoring", RegExp], readonly ["DigitalOcean Uptime", "monitoring", RegExp], readonly ["OhDear", "monitoring", RegExp], readonly ["Google-Safety", "monitoring", RegExp], readonly ["Cookiebot", "monitoring", RegExp], readonly ["OneTrust", "monitoring", RegExp], readonly ["CookieHub", "monitoring", RegExp], readonly ["Detectify", "monitoring", RegExp], readonly ["CensysInspect", "monitoring", RegExp], readonly ["BitSightBot", "monitoring", RegExp], readonly ["zgrab", "monitoring", RegExp], readonly ["Nmap", "monitoring", RegExp], readonly ["Stripe", "monitoring", RegExp], readonly ["HeadlessChrome", "headless_browser", RegExp], readonly ["PhantomJS", "headless_browser", RegExp], readonly ["SlimerJS", "headless_browser", RegExp], readonly ["Playwright", "headless_browser", RegExp], readonly ["Puppeteer", "headless_browser", RegExp], readonly ["Selenium", "headless_browser", RegExp], readonly ["Cypress", "headless_browser", RegExp], readonly ["Lightpanda", "headless_browser", RegExp], readonly ["wkhtmltopdf", "headless_browser", RegExp], readonly ["curl", "http_client", RegExp], readonly ["Wget", "http_client", RegExp], readonly ["python-requests", "http_client", RegExp], readonly ["python-urllib", "http_client", RegExp], readonly ["aiohttp", "http_client", RegExp], readonly ["httpx", "http_client", RegExp], readonly ["Scrapy", "http_client", RegExp], readonly ["Go-http-client", "http_client", RegExp], readonly ["okhttp", "http_client", RegExp], readonly ["Java", "http_client", RegExp], readonly ["axios", "http_client", RegExp], readonly ["node-fetch", "http_client", RegExp], readonly ["undici", "http_client", RegExp], readonly ["PostmanRuntime", "http_client", RegExp], readonly ["insomnia", "http_client", RegExp], readonly ["libwww-perl", "http_client", RegExp], readonly ["Guzzle", "http_client", RegExp], readonly ["Google-Apps-Script", "http_client", RegExp], readonly ["GoogleDocs", "http_client", RegExp]];
|
|
65
|
+
/**
|
|
66
|
+
* A name for a bot that is not in the named table, from the first of: a token that sounds like
|
|
67
|
+
* one (`Bytespider`, `ABEvalBot`), the name after `compatible;` (`Google-Read-Aloud`), a product
|
|
68
|
+
* token that is not a browser's (`Barkrowler/0.5`), what the generic pattern matched. URLs and
|
|
69
|
+
* email addresses are dropped first, as nearly every crawler links its docs.
|
|
70
|
+
*/
|
|
71
|
+
export declare function nameOfUnknownBot(userAgent: string, matched: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Whether a web visitor is a bot, which one, and what kind. Run on the server when the visitor
|
|
74
|
+
* is created, with the tags it was created with: the user agent is the request's header
|
|
75
|
+
* (`tags.user_agent`, which the server writes), not anything the client says about itself, so
|
|
76
|
+
* the verdict can be recomputed from stored tags when the rules change.
|
|
77
|
+
*
|
|
78
|
+
* Call it for web visitors only: a native app's requests carry an HTTP library's user agent
|
|
79
|
+
* (CFNetwork, okhttp) that would read as a bot, and no crawler runs the app.
|
|
80
|
+
*
|
|
81
|
+
* In order: a bot we can name; a browser driven by WebDriver (`tags.webdriver`, which the SDK
|
|
82
|
+
* sets from `navigator.webdriver`, the one sign a scripted browser with a normal user agent
|
|
83
|
+
* gives); anything the generic pattern calls automated; no user agent at all.
|
|
84
|
+
*/
|
|
85
|
+
export declare function botOf(tags: TrackTags): Bot;
|
|
86
|
+
//#endregion
|
|
87
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/bot/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;qBAsBa;YAeD,sBAAsB;;;;;YAMtB;EACN;EAAc;EAAkB,cAAc;;EAC9C;EAAe;EAAgB;;qBAExB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA4BX,mEAAU,qDAAA,mDAAA,0DAAA,8CAAA,oDAAA,4DAAA,gDAAA,iDAAA,gDAAA,mDAAA,kDAAA,iDAAA,sDAAA,oDAAA,2DAAA,qDAAA,iDAAA,gDAAA,uDAAA,uEAAA,mDAAA,2DAAA,wDAAA,gDAAA,gDAAA,+CAAA,mDAAA,4CAAA,8CAAA,iDAAA,oDAAA,iDAAA,mDAAA,6CAAA,0CAAA,4CAAA,kDAAA,qDAAA,kDAAA,mDAAA,gDAAA,6CAAA,6CAAA,2CAAA,2CAAA,sDAAA,2CAAA,8CAAA,iDAAA,uDAAA,gDAAA,+CAAA,0CAAA,0DAAA,8CAAA,4CAAA,2CAAA,iDAAA,6CAAA,2CAAA,6CAAA,iDAAA,gDAAA,8CAAA,6CAAA,4CAAA,4CAAA,mDAAA,uDAAA,qDAAA,oDAAA,kDAAA,+CAAA,8CAAA,mDAAA,6CAAA,6CAAA,gDAAA,0CAAA,iEAAA,0DAAA,8CAAA,iDAAA,kDAAA,+CAAA,iDAAA,kDAAA,+CAAA,mDAAA,gDAAA,uDAAA,sDAAA,kDAAA,+CAAA,8CAAA,sDAAA,8CAAA,4CAAA,qDAAA,0DAAA,wDAAA,wDAAA,6CAAA,2CAAA,4CAAA,6CAAA,0CAAA,yCAAA,2CAAA,iDAAA,8CAAA,0CAAA,gDAAA,0CAAA,sEAAA,8CAAA,4CAAA,6CAAA,6CAAA,6CAAA,8CAAA,8CAAA,+CAAA,4CAAA,yCAAA,kDAAA,wDAAA,oDAAA,gDAAA,iDAAA,oDAAA,kDAAA,oDAAA,oDAAA,8CAAA,kDAAA,oDAAA,iDAAA,kDAAA,6CAAA,mDAAA,iDAAA,kDAAA,kDAAA,iDAAA,oDAAA,8CAAA,yDAAA,4CAAA,gDAAA,+CAAA,6CAAA,iDAAA,4CAAA,4CAAA,mDAAA,gDAAA,iEAAA,8CAAA,2CAAA,+CAAA,oDAAA,wDAAA,2CAAA,kDAAA,8CAAA,6CAAA,8CAAA,8CAAA,kDAAA,gDAAA,0CAAA,yCAAA,2CAAA,yDAAA,oDAAA,mDAAA,qDAAA,oDAAA,mDAAA,kDAAA,qDAAA,sDAAA,0CAAA,0CAAA,qDAAA,mDAAA,6CAAA,2CAAA,4CAAA,oDAAA,4CAAA,0CAAA,2CAAA,gDAAA,4CAAA,oDAAA,8CAAA,iDAAA,4CAAA,wDAAA,gDAAA;;;;;;;wBA0bP,iBAAiB,mBAAmB;;;;;;;;;;;;;;wBA+BpC,MAAM,MAAM,YAAY"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { TrackTags } from "../track/types.mjs";
|
|
2
|
+
//#region src/bot/index.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* What kind of automated visitor a bot is, as the analytics tools that report bots separately
|
|
5
|
+
* group them (PostHog's traffic categories, Cloudflare's verified bot categories, Matomo's
|
|
6
|
+
* DeviceDetector). Stored next to the name so dashboards can group without re-deriving:
|
|
7
|
+
*
|
|
8
|
+
* - search_crawler: indexes pages for a search engine (Googlebot, Bingbot, Baiduspider)
|
|
9
|
+
* - ai_crawler: collects content to train models (GPTBot, ClaudeBot, Meta-ExternalAgent)
|
|
10
|
+
* - ai_search: indexes pages for an AI search product (OAI-SearchBot, PerplexityBot)
|
|
11
|
+
* - ai_assistant: fetches a page because a person asked an assistant to (ChatGPT-User)
|
|
12
|
+
* - advertising: works for an ad system — reviews landing pages, targets or verifies ads
|
|
13
|
+
* (Meta-ExternalAds, AdsBot-Google, Mediapartners-Google)
|
|
14
|
+
* - link_preview: renders the card of a link shared in a feed or a chat (facebookexternalhit)
|
|
15
|
+
* - seo_tool: SEO, marketing and page-speed tools (AhrefsBot, Lighthouse, HubSpot)
|
|
16
|
+
* - monitoring: uptime checks, synthetics and service callers (Pingdom, Stripe)
|
|
17
|
+
* - headless_browser: a scripted browser (HeadlessChrome, Playwright, a WebDriver session)
|
|
18
|
+
* - http_client: a library or command-line client (curl, python-requests)
|
|
19
|
+
* - no_user_agent: a request without a user agent, which no browser sends
|
|
20
|
+
* - other: automated by every sign, but none of the above
|
|
21
|
+
*/
|
|
22
|
+
export declare const BOT_CATEGORIES: readonly ['search_crawler', 'ai_crawler', 'ai_search', 'ai_assistant', 'advertising', 'link_preview', 'seo_tool', 'monitoring', 'headless_browser', 'http_client', 'no_user_agent', 'other'];
|
|
23
|
+
export type BotCategory = (typeof BOT_CATEGORIES)[number];
|
|
24
|
+
/**
|
|
25
|
+
* The verdict, shaped like the columns a product stores it in (`is_bot`, `bot_name`,
|
|
26
|
+
* `bot_category`). A person is `{ is_bot: false, bot_name: null, bot_category: null }`.
|
|
27
|
+
*/
|
|
28
|
+
export type Bot = {
|
|
29
|
+
is_bot: true;
|
|
30
|
+
bot_name: string;
|
|
31
|
+
bot_category: BotCategory;
|
|
32
|
+
} | {
|
|
33
|
+
is_bot: false;
|
|
34
|
+
bot_name: null;
|
|
35
|
+
bot_category: null;
|
|
36
|
+
};
|
|
37
|
+
export declare const NOT_A_BOT: Bot;
|
|
38
|
+
/**
|
|
39
|
+
* Bots we can name, as `[name, category, pattern]`, tried in order; the first match wins.
|
|
40
|
+
*
|
|
41
|
+
* Where the list comes from (checked 2026-09-30):
|
|
42
|
+
* - bowser's bot parsers (the ~25 named bots its `platform.type === 'bot'` reports), which this
|
|
43
|
+
* replaces.
|
|
44
|
+
* - PostHog's `bot_definitions.py` (web analytics traffic classification), for the bots and
|
|
45
|
+
* categories an analytics product sees most.
|
|
46
|
+
* - ai.robots.txt (`robots.json`), for AI crawlers, search bots, assistants and agents.
|
|
47
|
+
* - The operators' own docs for the big ones: Google's crawler list, OpenAI's and Anthropic's
|
|
48
|
+
* bot pages, Meta's web crawlers, Perplexity, Bing, Apple.
|
|
49
|
+
* - Bots seen in production (Meta-ExternalAds, HubSpot, Bytespider).
|
|
50
|
+
* Every entry was run against the real user agents in crawler-user-agents and PostHog's
|
|
51
|
+
* fixtures, and bowser's browser fixtures were run to check no browser is taken for a bot
|
|
52
|
+
* (Sogou's browser against its spider, Pinterest's in-app browser against its bot); see
|
|
53
|
+
* `index.test.ts` for where to get them.
|
|
54
|
+
*
|
|
55
|
+
* The link above each entry is the operator's page about the bot, else its product page, else
|
|
56
|
+
* its Known Agents profile (knownagents.com, formerly Dark Visitors) when the operator has none.
|
|
57
|
+
*
|
|
58
|
+
* Names are the operators' spelling of their user agent token. Order matters where one token
|
|
59
|
+
* contains another or borrows a name: advertising and SEO tools come before the search engines
|
|
60
|
+
* whose names they carry, and the more specific of Meta's, Anthropic's and Perplexity's tokens
|
|
61
|
+
* come first. The long tail is left to the generic pattern, filed as `other` and named by
|
|
62
|
+
* `nameOfUnknownBot`; add a bot here when it is worth its own category or name in a dashboard.
|
|
63
|
+
*/
|
|
64
|
+
export declare const NAMED_BOTS: readonly [readonly ["Meta-ExternalAds", "advertising", RegExp], readonly ["facebookcatalog", "advertising", RegExp], readonly ["AdsBot-Google", "advertising", RegExp], readonly ["Mediapartners-Google", "advertising", RegExp], readonly ["AdIdxBot", "advertising", RegExp], readonly ["Google-AdWords", "advertising", RegExp], readonly ["Google-Ads-Conversions", "advertising", RegExp], readonly ["OAI-AdsBot", "advertising", RegExp], readonly ["SnapchatAds", "advertising", RegExp], readonly ["Taboolabot", "advertising", RegExp], readonly ["ChatGPT-User", "ai_assistant", RegExp], readonly ["Claude-User", "ai_assistant", RegExp], readonly ["Claude-Web", "ai_assistant", RegExp], readonly ["Perplexity-User", "ai_assistant", RegExp], readonly ["DuckAssistBot", "ai_assistant", RegExp], readonly ["Meta-ExternalFetcher", "ai_assistant", RegExp], readonly ["MistralAI-User", "ai_assistant", RegExp], readonly ["Manus-User", "ai_assistant", RegExp], readonly ["Amzn-User", "ai_assistant", RegExp], readonly ["amazon-QBusiness", "ai_assistant", RegExp], readonly ["Amazon-Bedrock-AgentCore-Browser", "ai_assistant", RegExp], readonly ["Google-Agent", "ai_assistant", RegExp], readonly ["Gemini-Deep-Research", "ai_assistant", RegExp], readonly ["Google-NotebookLM", "ai_assistant", RegExp], readonly ["Kimi-User", "ai_assistant", RegExp], readonly ["TongyiBot", "ai_assistant", RegExp], readonly ["YiyanBot", "ai_assistant", RegExp], readonly ["kagi-fetcher", "ai_assistant", RegExp], readonly ["Devin", "ai_assistant", RegExp], readonly ["NovaAct", "ai_assistant", RegExp], readonly ["OAI-SearchBot", "ai_search", RegExp], readonly ["Claude-SearchBot", "ai_search", RegExp], readonly ["PerplexityBot", "ai_search", RegExp], readonly ["Meta-WebIndexer", "ai_search", RegExp], readonly ["Amazonbot", "ai_search", RegExp], readonly ["YouBot", "ai_search", RegExp], readonly ["PhindBot", "ai_search", RegExp], readonly ["Amzn-SearchBot", "ai_search", RegExp], readonly ["AzureAI-SearchBot", "ai_search", RegExp], readonly ["Kimi-SearchBot", "ai_search", RegExp], readonly ["MistralAI-Index", "ai_search", RegExp], readonly ["ExaSearchBot", "ai_search", RegExp], readonly ["TavilyBot", "ai_search", RegExp], readonly ["LinkupBot", "ai_search", RegExp], readonly ["iAskBot", "ai_search", RegExp], readonly ["Andibot", "ai_search", RegExp], readonly ["Cloudflare-AutoRAG", "ai_search", RegExp], readonly ["GPTBot", "ai_crawler", RegExp], readonly ["ClaudeBot", "ai_crawler", RegExp], readonly ["anthropic-ai", "ai_crawler", RegExp], readonly ["Meta-ExternalAgent", "ai_crawler", RegExp], readonly ["FacebookBot", "ai_crawler", RegExp], readonly ["Bytespider", "ai_crawler", RegExp], readonly ["CCBot", "ai_crawler", RegExp], readonly ["Google-CloudVertexBot", "ai_crawler", RegExp], readonly ["cohere-ai", "ai_crawler", RegExp], readonly ["Diffbot", "ai_crawler", RegExp], readonly ["Omgili", "ai_crawler", RegExp], readonly ["ImagesiftBot", "ai_crawler", RegExp], readonly ["Timpibot", "ai_crawler", RegExp], readonly ["AI2Bot", "ai_crawler", RegExp], readonly ["PanguBot", "ai_crawler", RegExp], readonly ["TikTokSpider", "ai_crawler", RegExp], readonly ["DeepSeekBot", "ai_crawler", RegExp], readonly ["DoubaoBot", "ai_crawler", RegExp], readonly ["ERNIEBot", "ai_crawler", RegExp], readonly ["QwenBot", "ai_crawler", RegExp], readonly ["KimiBot", "ai_crawler", RegExp], readonly ["ChatGLM-Spider", "ai_crawler", RegExp], readonly ["MistralAI-Training", "ai_crawler", RegExp], readonly ["YandexAdditional", "ai_crawler", RegExp], readonly ["Google-Extended", "ai_crawler", RegExp], readonly ["Amazon Kendra", "ai_crawler", RegExp], readonly ["bedrockbot", "ai_crawler", RegExp], readonly ["Brightbot", "ai_crawler", RegExp], readonly ["FirecrawlAgent", "ai_crawler", RegExp], readonly ["Crawl4AI", "ai_crawler", RegExp], readonly ["ApifyBot", "ai_crawler", RegExp], readonly ["img2dataset", "ai_crawler", RegExp], readonly ["LAION", "ai_crawler", RegExp], readonly ["Cloudflare Browser Rendering", "ai_crawler", RegExp], readonly ["facebookexternalhit", "link_preview", RegExp], readonly ["Facebot", "link_preview", RegExp], readonly ["Twitterbot", "link_preview", RegExp], readonly ["LinkedInBot", "link_preview", RegExp], readonly ["Slackbot", "link_preview", RegExp], readonly ["Discordbot", "link_preview", RegExp], readonly ["TelegramBot", "link_preview", RegExp], readonly ["WhatsApp", "link_preview", RegExp], readonly ["Pinterestbot", "link_preview", RegExp], readonly ["redditbot", "link_preview", RegExp], readonly ["Snap URL Preview", "link_preview", RegExp], readonly ["SkypeUriPreview", "link_preview", RegExp], readonly ["BingPreview", "link_preview", RegExp], readonly ["Iframely", "link_preview", RegExp], readonly ["Embedly", "link_preview", RegExp], readonly ["KakaoTalk-scrap", "link_preview", RegExp], readonly ["vkShare", "link_preview", RegExp], readonly ["Viber", "link_preview", RegExp], readonly ["Bluesky Cardyb", "link_preview", RegExp], readonly ["Google-PageRenderer", "link_preview", RegExp], readonly ["vercel-screenshot", "link_preview", RegExp], readonly ["Google-InspectionTool", "seo_tool", RegExp], readonly ["Lighthouse", "seo_tool", RegExp], readonly ["GTmetrix", "seo_tool", RegExp], readonly ["AhrefsBot", "seo_tool", RegExp], readonly ["SemrushBot", "seo_tool", RegExp], readonly ["MJ12bot", "seo_tool", RegExp], readonly ["DotBot", "seo_tool", RegExp], readonly ["rogerbot", "seo_tool", RegExp], readonly ["Screaming Frog", "seo_tool", RegExp], readonly ["SerpstatBot", "seo_tool", RegExp], readonly ["BLEXBot", "seo_tool", RegExp], readonly ["DataForSeoBot", "seo_tool", RegExp], readonly ["HubSpot", "seo_tool", RegExp], readonly ["Google-Structured-Data-Testing-Tool", "seo_tool", RegExp], readonly ["WebPageTest", "seo_tool", RegExp], readonly ["DareBoost", "seo_tool", RegExp], readonly ["Barkrowler", "seo_tool", RegExp], readonly ["SE Ranking", "seo_tool", RegExp], readonly ["BrightEdge", "seo_tool", RegExp], readonly ["SearchAtlas", "seo_tool", RegExp], readonly ["Siteimprove", "seo_tool", RegExp], readonly ["Dataprovider", "seo_tool", RegExp], readonly ["Meltwater", "seo_tool", RegExp], readonly ["Awario", "seo_tool", RegExp], readonly ["Googlebot", "search_crawler", RegExp], readonly ["Storebot-Google", "search_crawler", RegExp], readonly ["GoogleOther", "search_crawler", RegExp], readonly ["Bingbot", "search_crawler", RegExp], readonly ["Applebot", "search_crawler", RegExp], readonly ["Baiduspider", "search_crawler", RegExp], readonly ["YandexBot", "search_crawler", RegExp], readonly ["DuckDuckBot", "search_crawler", RegExp], readonly ["Yahoo Slurp", "search_crawler", RegExp], readonly ["Sogou", "search_crawler", RegExp], readonly ["360Spider", "search_crawler", RegExp], readonly ["YisouSpider", "search_crawler", RegExp], readonly ["PetalBot", "search_crawler", RegExp], readonly ["SeznamBot", "search_crawler", RegExp], readonly ["Yeti", "search_crawler", RegExp], readonly ["Linespider", "search_crawler", RegExp], readonly ["Qwantbot", "search_crawler", RegExp], readonly ["coccocbot", "search_crawler", RegExp], readonly ["MojeekBot", "search_crawler", RegExp], readonly ["Bravebot", "search_crawler", RegExp], readonly ["SeekportBot", "search_crawler", RegExp], readonly ["mwmbl", "search_crawler", RegExp], readonly ["Internet Archive", "search_crawler", RegExp], readonly ["Pingdom", "monitoring", RegExp], readonly ["UptimeRobot", "monitoring", RegExp], readonly ["StatusCake", "monitoring", RegExp], readonly ["Site24x7", "monitoring", RegExp], readonly ["Better Stack", "monitoring", RegExp], readonly ["Datadog", "monitoring", RegExp], readonly ["Checkly", "monitoring", RegExp], readonly ["NewRelicPinger", "monitoring", RegExp], readonly ["Uptime-Kuma", "monitoring", RegExp], readonly ["Grafana Synthetic Monitoring", "monitoring", RegExp], readonly ["Dynatrace", "monitoring", RegExp], readonly ["Splunk", "monitoring", RegExp], readonly ["Catchpoint", "monitoring", RegExp], readonly ["Ghost Inspector", "monitoring", RegExp], readonly ["DigitalOcean Uptime", "monitoring", RegExp], readonly ["OhDear", "monitoring", RegExp], readonly ["Google-Safety", "monitoring", RegExp], readonly ["Cookiebot", "monitoring", RegExp], readonly ["OneTrust", "monitoring", RegExp], readonly ["CookieHub", "monitoring", RegExp], readonly ["Detectify", "monitoring", RegExp], readonly ["CensysInspect", "monitoring", RegExp], readonly ["BitSightBot", "monitoring", RegExp], readonly ["zgrab", "monitoring", RegExp], readonly ["Nmap", "monitoring", RegExp], readonly ["Stripe", "monitoring", RegExp], readonly ["HeadlessChrome", "headless_browser", RegExp], readonly ["PhantomJS", "headless_browser", RegExp], readonly ["SlimerJS", "headless_browser", RegExp], readonly ["Playwright", "headless_browser", RegExp], readonly ["Puppeteer", "headless_browser", RegExp], readonly ["Selenium", "headless_browser", RegExp], readonly ["Cypress", "headless_browser", RegExp], readonly ["Lightpanda", "headless_browser", RegExp], readonly ["wkhtmltopdf", "headless_browser", RegExp], readonly ["curl", "http_client", RegExp], readonly ["Wget", "http_client", RegExp], readonly ["python-requests", "http_client", RegExp], readonly ["python-urllib", "http_client", RegExp], readonly ["aiohttp", "http_client", RegExp], readonly ["httpx", "http_client", RegExp], readonly ["Scrapy", "http_client", RegExp], readonly ["Go-http-client", "http_client", RegExp], readonly ["okhttp", "http_client", RegExp], readonly ["Java", "http_client", RegExp], readonly ["axios", "http_client", RegExp], readonly ["node-fetch", "http_client", RegExp], readonly ["undici", "http_client", RegExp], readonly ["PostmanRuntime", "http_client", RegExp], readonly ["insomnia", "http_client", RegExp], readonly ["libwww-perl", "http_client", RegExp], readonly ["Guzzle", "http_client", RegExp], readonly ["Google-Apps-Script", "http_client", RegExp], readonly ["GoogleDocs", "http_client", RegExp]];
|
|
65
|
+
/**
|
|
66
|
+
* A name for a bot that is not in the named table, from the first of: a token that sounds like
|
|
67
|
+
* one (`Bytespider`, `ABEvalBot`), the name after `compatible;` (`Google-Read-Aloud`), a product
|
|
68
|
+
* token that is not a browser's (`Barkrowler/0.5`), what the generic pattern matched. URLs and
|
|
69
|
+
* email addresses are dropped first, as nearly every crawler links its docs.
|
|
70
|
+
*/
|
|
71
|
+
export declare function nameOfUnknownBot(userAgent: string, matched: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Whether a web visitor is a bot, which one, and what kind. Run on the server when the visitor
|
|
74
|
+
* is created, with the tags it was created with: the user agent is the request's header
|
|
75
|
+
* (`tags.user_agent`, which the server writes), not anything the client says about itself, so
|
|
76
|
+
* the verdict can be recomputed from stored tags when the rules change.
|
|
77
|
+
*
|
|
78
|
+
* Call it for web visitors only: a native app's requests carry an HTTP library's user agent
|
|
79
|
+
* (CFNetwork, okhttp) that would read as a bot, and no crawler runs the app.
|
|
80
|
+
*
|
|
81
|
+
* In order: a bot we can name; a browser driven by WebDriver (`tags.webdriver`, which the SDK
|
|
82
|
+
* sets from `navigator.webdriver`, the one sign a scripted browser with a normal user agent
|
|
83
|
+
* gives); anything the generic pattern calls automated; no user agent at all.
|
|
84
|
+
*/
|
|
85
|
+
export declare function botOf(tags: TrackTags): Bot;
|
|
86
|
+
//#endregion
|
|
87
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/bot/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;qBAsBa;YAeD,sBAAsB;;;;;YAMtB;EACN;EAAc;EAAkB,cAAc;;EAC9C;EAAe;EAAgB;;qBAExB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA4BX,mEAAU,qDAAA,mDAAA,0DAAA,8CAAA,oDAAA,4DAAA,gDAAA,iDAAA,gDAAA,mDAAA,kDAAA,iDAAA,sDAAA,oDAAA,2DAAA,qDAAA,iDAAA,gDAAA,uDAAA,uEAAA,mDAAA,2DAAA,wDAAA,gDAAA,gDAAA,+CAAA,mDAAA,4CAAA,8CAAA,iDAAA,oDAAA,iDAAA,mDAAA,6CAAA,0CAAA,4CAAA,kDAAA,qDAAA,kDAAA,mDAAA,gDAAA,6CAAA,6CAAA,2CAAA,2CAAA,sDAAA,2CAAA,8CAAA,iDAAA,uDAAA,gDAAA,+CAAA,0CAAA,0DAAA,8CAAA,4CAAA,2CAAA,iDAAA,6CAAA,2CAAA,6CAAA,iDAAA,gDAAA,8CAAA,6CAAA,4CAAA,4CAAA,mDAAA,uDAAA,qDAAA,oDAAA,kDAAA,+CAAA,8CAAA,mDAAA,6CAAA,6CAAA,gDAAA,0CAAA,iEAAA,0DAAA,8CAAA,iDAAA,kDAAA,+CAAA,iDAAA,kDAAA,+CAAA,mDAAA,gDAAA,uDAAA,sDAAA,kDAAA,+CAAA,8CAAA,sDAAA,8CAAA,4CAAA,qDAAA,0DAAA,wDAAA,wDAAA,6CAAA,2CAAA,4CAAA,6CAAA,0CAAA,yCAAA,2CAAA,iDAAA,8CAAA,0CAAA,gDAAA,0CAAA,sEAAA,8CAAA,4CAAA,6CAAA,6CAAA,6CAAA,8CAAA,8CAAA,+CAAA,4CAAA,yCAAA,kDAAA,wDAAA,oDAAA,gDAAA,iDAAA,oDAAA,kDAAA,oDAAA,oDAAA,8CAAA,kDAAA,oDAAA,iDAAA,kDAAA,6CAAA,mDAAA,iDAAA,kDAAA,kDAAA,iDAAA,oDAAA,8CAAA,yDAAA,4CAAA,gDAAA,+CAAA,6CAAA,iDAAA,4CAAA,4CAAA,mDAAA,gDAAA,iEAAA,8CAAA,2CAAA,+CAAA,oDAAA,wDAAA,2CAAA,kDAAA,8CAAA,6CAAA,8CAAA,8CAAA,kDAAA,gDAAA,0CAAA,yCAAA,2CAAA,yDAAA,oDAAA,mDAAA,qDAAA,oDAAA,mDAAA,kDAAA,qDAAA,sDAAA,0CAAA,0CAAA,qDAAA,mDAAA,6CAAA,2CAAA,4CAAA,oDAAA,4CAAA,0CAAA,2CAAA,gDAAA,4CAAA,oDAAA,8CAAA,iDAAA,4CAAA,wDAAA,gDAAA;;;;;;;wBA0bP,iBAAiB,mBAAmB;;;;;;;;;;;;;;wBA+BpC,MAAM,MAAM,YAAY"}
|