diff --git a/config/default-config.json b/config/default-config.json index c9d6faf..75bccc5 100644 --- a/config/default-config.json +++ b/config/default-config.json @@ -63,7 +63,7 @@ }, {"func": "clientAccepts", "params": ["zh"], "id": "zhLang", "desc": "Client accepts Chinese language", - "bot": 60 + "bot": 50 }, {"func": "matchesCountry", "params": ["BR", "CN", "RU", "US", "MX", "SG", "IN", "UY"], "id": "isFrom", "desc": "Location is in a known bot-spamming country.", @@ -81,10 +81,11 @@ {"from": "47.200.0.0", "to": "47.203.255.255", "m": 14, "label": "Frontier Communications [US]"}, {"from": "49.0.200.0", "to": "49.0.255.254", "label": "Huawei [SG]"}, {"from": "52.220.0.0", "to": "52.222.255.254", "label": "Amazon Data Services"}, - {"from": "66.249.64.0", "to": "66.249.95.255", "m": 19, "label": "Google LLC [US]"}, + {"from": "66.249.64.0", "to": "66.249.95.255", "m": 19, "label": "Google LLC"}, {"from": "84.37.35.0", "to": "84.37.255.254", "label": "GTT.net [US]"}, {"from": "94.74.64.0", "to": "94.74.127.254", "label": "Huawei [HK]"}, {"from": "101.0.0.0", "to": "101.255.255.254", "label": "ChinaNet [CN]"}, + {"from": "104.196.0.0", "to": "104.199.255.255", "m": 14, "label": "Google LLC"}, {"from": "110.238.80.0", "to": "110.238.127.254", "label": "Huawei [SG]"}, {"from": "111.119.192.0", "to": "111.119.255.254", "label": "Huawei [SG]"}, {"from": "119.0.0.0", "to": "119.207.255.254", "label": "Unicom [CN]"}, @@ -110,7 +111,7 @@ {"from": "2001:4860::::::", "to": "2001:4860:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "label": "Google LLC [US]"}, {"from": "2001:0ee0::::::", "to": "2001:ee3:ffff:ffff:ffff:ffff:ffff:ffff", "m": 30, "label": "VNPT [VN]"}, {"from": "2408:8210::::::", "to": "2408:8210:ffff:ffff:ffff:ffff:ffff:ffff", "m": 30, "label": "China Unicom [CN]"}, - {"from": "2600:1f00::::::", "to": "2600:1fff:ffff:ffff:ffff:ffff:ffff:ffff", "m": "Amazon Cloud [US]"}, + {"from": "2600:1f00::::::", "to": "2600:1fff:ffff:ffff:ffff:ffff:ffff:ffff", "m": "Amazon Cloud"}, {"from": "2603:6010::::::", "to": "2603:6010:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "label": "Charter [US]"}, {"from": "2603:8000::::::", "to": "2603:80ff:ffff:ffff:ffff:ffff:ffff:ffff", "m": 24, "label": "Charter [US]"}, {"from": "2607:a400::::::", "to": "2607:a400:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "label": "Zenlayer Inc. [US]"}, diff --git a/config/known-bots.json b/config/known-bots.json index 811a335..60ca4a5 100644 --- a/config/known-bots.json +++ b/config/known-bots.json @@ -28,12 +28,24 @@ "rx": ["\\sGoogleOther(\\-\\w+)?[\\)\\/]"], "url": "https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers#googleother" }, + {"id": "googinspct", + "n": "Google-InspectionTool", "geo": "US", + "r": ["Google-InspectionTool"], + "rx": ["\\sGoogle-InspectionTool(\\-\\w+)?[\\)\\/]"], + "url": "https://support.google.com/webmasters/answer/9012289" + }, {"id": "applebot", "n": "Applebot", "geo": "US", "r": ["Applebot"], "rx": ["Applebot\\/(\\d+\\.\\d+);"], "url": "http://www.apple.com/go/applebot" }, + {"id": "reddit", + "n": "RedditBot", + "r": ["Applebot"], + "rx": [" redditbot\\/(\\d+\\.\\d+);"], + "url": "http://www.reddit.com/feedback" + }, {"id": "slurp", "n": "Slurp (Yahoo!)", "geo": "US", "r": ["Slurp"], diff --git a/config/known-clients.json b/config/known-clients.json index 67c535d..dda615a 100644 --- a/config/known-clients.json +++ b/config/known-clients.json @@ -3,6 +3,10 @@ "id": "opera", "rx": [ "\\sOpera\\/.*?Version\\/(\\S+)", "Opera\\/(\\S+)" ] }, + {"n": "Firefox on iOS", + "id": "firefox", + "rx": [ "\\sFxiOS\\/(\\d+)\\." ] + }, {"n": "AOL Explorer", "id": "aol", "rx": [ "\\sAOL\\s(\\d+\\.\\d+)\\)", "\\sAOLBUILD\\/(\\S+)", "\\sBukaolshop\\s", "\\.aolapp\\/(\\d+\\.\\d+)\\." ] @@ -74,5 +78,9 @@ {"n": "Firefox", "id": "firefox", "rx": [ "\\sFirefox\\/(\\d\\d\\d)\\." ] + }, + {"n": "AppleWebKit", + "id": "webkit", + "rx": [ "\\sAppleWebKit\\/(\\d\\d\\d)\\." ] } ] diff --git a/img/bots.png b/img/bots.png index 2351c24..087993f 100644 Binary files a/img/bots.png and b/img/bots.png differ diff --git a/img/clients.png b/img/clients.png index b087ff6..50f8a30 100644 Binary files a/img/clients.png and b/img/clients.png differ diff --git a/plugin.info.txt b/plugin.info.txt index 7f678bd..34ccea7 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base botmon author Sascha Leib email ad@hominem.com -date 2025-09-14 +date 2025-09-15 name Bot Monitoring desc A tool for monitoring and analysing bot traffic to your wiki (under development) url https://www.dokuwiki.org/plugin:botmon diff --git a/script.js b/script.js index 54465b5..f34c999 100644 --- a/script.js +++ b/script.js @@ -1847,10 +1847,15 @@ BotMon.live = { summary.appendChild(span1); const span2 = make('span'); /* right-hand group */ - span2.appendChild(make('span', { /* page views */ - 'class': 'has_icon pageviews', - 'title': data._pageViews.length + " page view(s)" - }, data._pageViews.length)); + span2.appendChild(make('span', { /* first-seen */ + 'class': 'has_iconfirst-seen', + 'title': "First seen: " + data._firstSeen.toLocaleString() + " UTC" + }, BotMon.t._formatTime(data._firstSeen))); + + span2.appendChild(make('span', { /* page views */ + 'class': 'has_icon pageviews', + 'title': data._pageViews.length + " page view(s)" + }, data._pageViews.length)); summary.appendChild(span2); @@ -1961,56 +1966,7 @@ BotMon.live = { /* list all page views */ data._pageViews.sort( (a, b) => a._firstSeen - b._firstSeen ); data._pageViews.forEach( (page) => { - //console.log("page:",page); - - const pgLi = make('li'); - - const lGroup = make('span'); // left group: - - lGroup.appendChild(make('span', { - 'data-lang': page.lang, - 'title': "PageID: " + page.pg - }, page.pg)); /* DW Page ID */ - - pgLi.appendChild(lGroup); // end of left group - - const rGroup = make('span'); // right group: - - let visitTimeStr = "Bounce"; - const visitDuration = page._lastSeen.getTime() - page._firstSeen.getTime(); - if (visitDuration > 0) { - visitTimeStr = Math.floor(visitDuration / 1000) + "s"; - } - - /*if (page._ref) { - rGroup.appendChild(make('span', { - 'data-ref': page._ref.host, - 'title': "Referrer: " + page._ref.full - }, page._ref.site)); - } else { - rGroup.appendChild(make('span', { - }, "No referer")); - }*/ - //rGroup.appendChild(make('span', {}, ( page._seenBy ? page._seenBy.join(', ') : '—') + '; ' + page._tickCount)); - - // get the time difference: - const tDiff = BotMon.t._formatTimeDiff(page._firstSeen, page._lastSeen); - if (tDiff) { - rGroup.appendChild(make('span', {'class': 'visit-length', 'title': 'Last seen: ' + page._lastSeen.toLocaleString()}, tDiff)); - } else { - rGroup.appendChild(make('span', { - 'class': 'bounce', - 'title': "Visitor bounced"}, "Bounce")); - } - rGroup.appendChild(make('span', { - 'class': 'first-seen', - 'title': "First visited: " + page._firstSeen.toLocaleString() - }, BotMon.t._formatTime(page._firstSeen))); - - pgLi.appendChild(rGroup); // end of right group - - - pageList.appendChild(pgLi); + pageList.appendChild(BotMon.live.gui.lists._makePageViewItem(page)); }); pagesDd.appendChild(pageList); dl.appendChild(pagesDd); @@ -2063,8 +2019,68 @@ BotMon.live = { } // return the element to add to the UI: return dl; - } + }, + // make a page view item: + _makePageViewItem: function(page) { + console.log("makePageViewItem:",page); + + // shortcut for neater code: + const make = BotMon.t._makeElement; + + // the actual list item: + const pgLi = make('li'); + + const row1 = make('div', {'class': 'row'}); + + row1.appendChild(make('span', { // page id is the left group + 'data-lang': page.lang, + 'title': "PageID: " + page.pg + }, page.pg)); /* DW Page ID */ + + // get the time difference: + row1.appendChild(make('span', { + 'class': 'first-seen', + 'title': "First visited: " + page._firstSeen.toLocaleString() + " UTC" + }, BotMon.t._formatTime(page._firstSeen))); + + pgLi.appendChild(row1); + + /* LINE 2 */ + + const row2 = make('div', {'class': 'row'}); + + // page referrer: + if (page._ref) { + row2.appendChild(make('span', { + 'class': 'referer', + 'title': "Referrer: " + page._ref.href + }, page._ref.hostname)); + } else { + row2.appendChild(make('span', { + 'class': 'referer' + }, "No referer")); + } + + // visit duration: + let visitTimeStr = "Bounce"; + const visitDuration = page._lastSeen.getTime() - page._firstSeen.getTime(); + if (visitDuration > 0) { + visitTimeStr = Math.floor(visitDuration / 1000) + "s"; + } + const tDiff = BotMon.t._formatTimeDiff(page._firstSeen, page._lastSeen); + if (tDiff) { + row2.appendChild(make('span', {'class': 'visit-length', 'title': 'Last seen: ' + page._lastSeen.toLocaleString()}, tDiff)); + } else { + row2.appendChild(make('span', { + 'class': 'bounce', + 'title': "Visitor bounced"}, "Bounce")); + } + + pgLi.appendChild(row2); + + return pgLi; + } } } }; diff --git a/style.less b/style.less index d40963d..1aabda2 100644 --- a/style.less +++ b/style.less @@ -40,7 +40,7 @@ /* Bot icons */ &.bot::before { background-image: url('img/bots.png') } - &.bot_googlebot::before, &.bot_googleads::before, &.bot_googleapi::before, &.bot_googleother::before { background-position-y: -20px } + &.bot_googlebot::before, &.bot_googleads::before, &.bot_googleapi::before, &.bot_googleother::before, &.bot_googinspct::before { background-position-y: -20px } &.bot_bingbot::before { background-position-y: -40px } &.bot_applebot::before { background-position-y: -60px } &.bot_openai::before { background-position-y: -80px } @@ -49,6 +49,7 @@ &.bot_seznambot::before { background-position-y: -140px } &.bot_claude::before { background-position-y: -160px } &.bot_applemsgs::before { background-position-y: -180px } + &.bot_reddit::before { background-position-y: -200px } /* platform icons */ &.platform::before { background-image: url('img/platforms.png') } @@ -86,6 +87,7 @@ &.cl_ffold::before { background-position-y: -300px } &.cl_chromeold::before { background-position-y: -320px } &.cl_ecosia::before { background-position-y: -340px } + &.cl_webkit::before { background-position-y: -360px } /* Country flags */ /* Note: flag images and CSS adapted from: https://github.com/lafeber/world-flags-sprite/ */ @@ -530,9 +532,7 @@ li { & { display: flex; - justify-content: space-between; - align-items: baseline; - white-space: nowrap; + flex-direction: column; line-height: 1.2rem; margin: 0; padding: 0 .25em; @@ -540,6 +540,14 @@ &:nth-child(odd) { background-color: #DFDFDF; } + div.row { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: baseline; + white-space: nowrap; + line-height: 1.35em; + } span { display: inline-block; } @@ -548,6 +556,8 @@ span[data-lang] { overflow: hidden; text-overflow: ellipsis; + padding: 2pt 0 0 2pt; + } span[data-lang]::after { content: attr(data-lang); @@ -561,7 +571,10 @@ } span.first-seen { min-width: 4.2em; - text-align: right;; + text-align: right; + } + span.visit-length { + font-size: smaller; } span.bounce { width: 1.25em; height: 1.25em; @@ -570,9 +583,13 @@ span.bounce::before { display: inline-block; content: ''; - width: 1.25em; height: 1.25em; + width: 1.25em; height: 1em; background: transparent url('img/bounce.svg') center no-repeat; - background-size: 1.25em; + background-size: .95em; + } + span.referer { + font-size: smaller; + margin-left: .67rem; } } }