From d815fa6d0e5316faabeb7c326f23706894e58a0b Mon Sep 17 00:00:00 2001 From: rajchales-monito Date: Thu, 30 Jul 2026 13:46:39 +0200 Subject: [PATCH] Improve visitor bot filtering --- server.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/server.js b/server.js index d23eb15..5761863 100644 --- a/server.js +++ b/server.js @@ -1835,7 +1835,16 @@ function currentVisitors() { function formatVisitorSession(row, thresholds = adminPresenceSettings()) { const history = parseJson(row.browsing_history_json, []); const device = parseJson(row.device_json, {}); - const bot = botInfo(device.userAgent); + const pageCountValue = new Set(history.map((item) => item.url).filter(Boolean)).size; + const sessionSecondsValue = sessionSeconds(row.browsing_history_json, row.first_seen_at); + const conversationCountValue = row.visitor_conversation_count || 0; + const bot = botInfo(device.userAgent, { + pageCount: pageCountValue, + visitCount: Math.max(1, history.length), + conversationCount: conversationCountValue, + sessionSeconds: sessionSecondsValue, + referrer: row.referrer + }); const countryCode = row.country_code || countryFromTimezone(row.timezone) || countryFromLanguage(row.language); const presence = visitorPresence(row, thresholds); return { @@ -1849,9 +1858,9 @@ function formatVisitorSession(row, thresholds = adminPresenceSettings()) { currentUrl: row.current_url, referrer: row.referrer, browsingHistory: history, - pageCount: new Set(history.map((item) => item.url).filter(Boolean)).size, + pageCount: pageCountValue, visitCount: Math.max(1, history.length), - conversationCount: row.visitor_conversation_count || 0, + conversationCount: conversationCountValue, lastConversationId: row.last_conversation_id, isBot: bot.isBot, botReason: bot.reason, @@ -1860,7 +1869,7 @@ function formatVisitorSession(row, thresholds = adminPresenceSettings()) { timezone: row.timezone, countryCode, countryFlag: countryCode ? flagEmoji(countryCode) : "", - sessionSeconds: sessionSeconds(row.browsing_history_json, row.first_seen_at), + sessionSeconds: sessionSecondsValue, firstSeenAt: row.first_seen_at, lastActivityAt: row.last_activity_at, lastSeenAt: row.last_seen_at @@ -2245,19 +2254,30 @@ function countryFromLanguage(language) { return match ? match[1].toUpperCase() : null; } -function botInfo(userAgent) { +function botInfo(userAgent, activity = {}) { const ua = String(userAgent || ""); const patterns = [ "googlebot", "bingbot", "slurp", "duckduckbot", "baiduspider", "yandexbot", "ahrefsbot", "semrushbot", "mj12bot", "dotbot", "petalbot", "bytespider", "facebookexternalhit", "twitterbot", "linkedinbot", "slackbot", "discordbot", "whatsapp", "telegrambot", "preview", "crawler", "spider", "headlesschrome", - "pingdom", "uptimerobot", "curl", "wget", "python-requests", "axios", "httpclient" + "phantomjs", "playwright", "puppeteer", "selenium", "cypress", "chrome-lighthouse", + "pagespeed", "lighthouse", "pingdom", "uptimerobot", "curl", "wget", + "python-requests", "aiohttp", "go-http-client", "axios", "httpclient", "java/", + "okhttp", "libwww-perl", "scrapy", "httpx", "node-fetch", "undici" ]; const lower = ua.toLowerCase(); const match = patterns.find((pattern) => lower.includes(pattern)); if (match) return { isBot: true, reason: match }; if (/\bbot\b/i.test(ua)) return { isBot: true, reason: "bot" }; + if (!ua.trim()) return { isBot: true, reason: "empty ua" }; + if (activity.conversationCount > 0) return { isBot: false, reason: "" }; + const pageCount = Number(activity.pageCount || 0); + const visitCount = Number(activity.visitCount || 0); + const sessionSecondsValue = Number(activity.sessionSeconds || 0); + if (pageCount >= 25 && sessionSecondsValue <= 180) return { isBot: true, reason: "fast crawler" }; + if (pageCount >= 40) return { isBot: true, reason: "many pages" }; + if (visitCount >= 60 && !activity.referrer) return { isBot: true, reason: "no-ref crawler" }; return { isBot: false, reason: "" }; }