| 1 | package analytics |
| 2 | |
| 3 | import "strings" |
| 4 | |
| 5 | // botMarkers is the vendored user-agent list (spec §3.3): incomplete by |
| 6 | // nature, so the tab shows the bots count rather than pretending. Matched |
| 7 | // case-insensitively as substrings. An empty UA is a bot: no browser sends |
| 8 | // none. |
| 9 | var botMarkers = []string{ |
| 10 | "bot", "crawler", "spider", "slurp", "curl/", "wget/", "python-requests", |
| 11 | "go-http-client", "headlesschrome", "phantomjs", "facebookexternalhit", |
| 12 | "ahrefs", "semrush", "mj12", "dotbot", "petalbot", "bytespider", "gptbot", |
| 13 | "claudebot", "ccbot", "applebot", "yandex", "baiduspider", "duckduckbot", |
| 14 | "linkedinbot", "twitterbot", "discordbot", "telegrambot", "whatsapp", |
| 15 | "pingdom", "uptimerobot", "statuscake", "site24x7", "newrelicpinger", |
| 16 | "lighthouse", "pagespeed", "gtmetrix", "httpclient", "okhttp", "java/", |
| 17 | "libwww", "scrapy", "node-fetch", "axios/", "postmanruntime", "insomnia", |
| 18 | } |
| 19 | |
| 20 | // IsBot reports whether ua looks like an automated client. |
| 21 | func IsBot(ua string) bool { |
| 22 | if ua == "" { |
| 23 | return true |
| 24 | } |
| 25 | l := strings.ToLower(ua) |
| 26 | for _, m := range botMarkers { |
| 27 | if strings.Contains(l, m) { |
| 28 | return true |
| 29 | } |
| 30 | } |
| 31 | return false |
| 32 | } |
| 33 | |