Added more Web Metrics
This commit is contained in:
@@ -44,7 +44,7 @@ class admin_plugin_botmon extends AdminPlugin {
|
||||
</nav>';
|
||||
|
||||
if ($this->hasOldLogFiles()) {
|
||||
echo '<div class="info"><strong>Note:</strong> There are old log files that can be deleted. <a href="' . $pluginPath . '/cleanup.php" target="_blank">Click here</a> to run a delete script, or use <em>cron</em> to automatically delete them.</div>';
|
||||
echo '<div class="info"><strong>Note:</strong> There are old log files that can be deleted. <a href="/' . $pluginPath . '/cleanup.php" target="_blank">Click here</a> to run a delete script, or use <em>cron</em> to automatically delete them.</div>';
|
||||
}
|
||||
|
||||
echo '<article role="tabpanel" id="botmon__today"">
|
||||
@@ -64,8 +64,8 @@ class admin_plugin_botmon extends AdminPlugin {
|
||||
<summary>Web metrics</summary>
|
||||
<div class="botmon_overview_grid">
|
||||
<dl id="botmon__today__wm_overview"></dl>
|
||||
<dl></dl>
|
||||
<dl></dl>
|
||||
<dl id="botmon__today__wm_clients"></dl>
|
||||
<dl id="botmon__today__wm_platforms"></dl>
|
||||
<dl></dl>
|
||||
</div>
|
||||
</details>
|
||||
@@ -75,7 +75,7 @@ class admin_plugin_botmon extends AdminPlugin {
|
||||
</details>
|
||||
</div>
|
||||
<footer aria-live="polite">
|
||||
<img src="' . $pluginPath . '/img/spinner.svg" id="botmon__today__busy" width="12" height="12" alt="busy indicator">
|
||||
<img src="/' . $pluginPath . '/img/spinner.svg" id="botmon__today__busy" width="12" height="12" alt="busy indicator">
|
||||
<span id="botmon__today__status">Initialising …</span>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
"id": "oldClient", "desc": "Obsolete browser version",
|
||||
"bot": 40
|
||||
},
|
||||
{"func": "matchesPlatform", "params": ["winold", "macosold","winsrvr"],
|
||||
{"func": "matchesPlatform", "params": ["winold", "macosold"],
|
||||
"id": "oldOS", "desc": "Obsolete platform version",
|
||||
"bot": 40
|
||||
},
|
||||
{"func": "matchesPlatform", "params": ["winsrvr", "bsd"],
|
||||
"id": "serverOS", "desc": "Server OS",
|
||||
"bot": 40
|
||||
},
|
||||
{"func": "smallPageCount", "params": [1],
|
||||
"id": "onePage", "desc": "Visiter viewed only a single page",
|
||||
"bot": 40
|
||||
@@ -59,11 +63,11 @@
|
||||
},
|
||||
{"func": "matchesCountry", "params": ["BR", "CN", "RU", "US", "MX", "SG", "IN", "UY"],
|
||||
"id": "isFrom", "desc": "Location is in a known bot-spamming country.",
|
||||
"bot": 40
|
||||
"bot": 50
|
||||
},
|
||||
{"func": "notFromCountry", "params": ["DE", "AT", "CH", "LI", "LU", "BE"],
|
||||
"id": "notFromHere", "desc": "Location is not in one of the site’s target countries.",
|
||||
"bot": 40
|
||||
"id": "notFromHere", "desc": "Location is not among the site’s main target countries.",
|
||||
"bot": 20
|
||||
}
|
||||
],
|
||||
"ipRanges": [
|
||||
@@ -99,6 +103,7 @@
|
||||
{"from": "189.0.0.0", "to": "189.255.255.254", "label": "South-American ISPs (189.x)"},
|
||||
{"from": "190.0.0.0", "to": "190.255.255.254", "label": "South-American ISPs (190.x)"},
|
||||
{"from": "192.124.170.0", "to": "192.124.182.254", "label": "Relcom [CZ]"},
|
||||
{"from": "195.37.0.0", "to": "195.37.255.255", "label": "DFN [DE]"},
|
||||
{"from": "2001:4800::::::", "to": "2001:4fff:ffff:ffff:ffff:ffff:ffff:ffff", "label": "Rackspace/Google [US]"},
|
||||
{"from": "2001:0ee0::::::", "to": "2001:ee3:ffff:ffff:ffff:ffff:ffff:ffff", "mask": 30, "label": "VNPT [VN]"},
|
||||
{"from": "2600:1f00::::::", "to": "2600:1fff:ffff:ffff:ffff:ffff:ffff:ffff", "label": "Amazon Cloud [US]"},
|
||||
@@ -1,7 +1,7 @@
|
||||
base botmon
|
||||
author Sascha Leib
|
||||
email ad@hominem.com
|
||||
date 2025-09-10
|
||||
date 2025-09-11
|
||||
name Bot Monitoring
|
||||
desc Live monitoring of bot traffic on your DokuWiki instance (under development)
|
||||
url https://www.dokuwiki.org/plugin:botmon
|
||||
|
||||
219
script.js
219
script.js
@@ -482,6 +482,7 @@ BotMon.live = {
|
||||
data: {
|
||||
totalVisits: 0,
|
||||
totalPageViews: 0,
|
||||
humanPageViews: 0,
|
||||
bots: {
|
||||
known: 0,
|
||||
suspected: 0,
|
||||
@@ -535,11 +536,6 @@ BotMon.live = {
|
||||
v._eval = e.rules;
|
||||
v._botVal = e.val;
|
||||
|
||||
// add each page view to IP range information (unless it is already from a known bot IP range):
|
||||
v._pageViews.forEach( pv => {
|
||||
me._addToIPRanges(pv.ip);
|
||||
});
|
||||
|
||||
if (e.isBot) { // likely bots
|
||||
v._type = BM_USERTYPE.LIKELY_BOT;
|
||||
this.data.bots.suspected += v._pageViews.length;
|
||||
@@ -548,13 +544,26 @@ BotMon.live = {
|
||||
v._type = BM_USERTYPE.HUMAN;
|
||||
this.data.bots.human += v._pageViews.length;
|
||||
this.groups.humans.push(v);
|
||||
}
|
||||
// TODO: find suspected bots
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// add to the country lists:
|
||||
me._addToCountries(v.geo, v._country, v._type);
|
||||
// perform actions depending on the visitor type:
|
||||
if (v._type == BM_USERTYPE.KNOWN_BOT || v._type == BM_USERTYPE.LIKELY_BOT) { /* bots only */
|
||||
|
||||
// add bot views to IP range information:
|
||||
v._pageViews.forEach( pv => {
|
||||
me.addToIPRanges(pv.ip);
|
||||
});
|
||||
|
||||
// add to the country lists:
|
||||
me.addToCountries(v.geo, v._country, v._type);
|
||||
|
||||
} else { /* humans only */
|
||||
|
||||
// add browser and platform statistics:
|
||||
me.addBrowserPlatform(v);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
BotMon.live.gui.status.hideBusy('Done.');
|
||||
@@ -572,7 +581,7 @@ BotMon.live = {
|
||||
*
|
||||
* @param {string} ip The IP address to add.
|
||||
*/
|
||||
_addToIPRanges: function(ip) {
|
||||
addToIPRanges: function(ip) {
|
||||
|
||||
// #TODO: handle nestled ranges!
|
||||
const me = BotMon.live.data.analytics;
|
||||
@@ -653,7 +662,7 @@ BotMon.live = {
|
||||
*
|
||||
* @param {string} iso The ISO 3166-1 alpha-2 country code.
|
||||
*/
|
||||
_addToCountries: function(iso, name, type) {
|
||||
addToCountries: function(iso, name, type) {
|
||||
|
||||
const me = BotMon.live.data.analytics;
|
||||
|
||||
@@ -674,7 +683,7 @@ BotMon.live = {
|
||||
arr = me._countries.known_bot;
|
||||
break;
|
||||
default:
|
||||
console.warn(`Unknown user type ${type} in function _addToCountries.`);
|
||||
console.warn(`Unknown user type ${type} in function addToCountries.`);
|
||||
}
|
||||
|
||||
if (arr) {
|
||||
@@ -740,6 +749,130 @@ BotMon.live = {
|
||||
return rList;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
/* browser and platform of human visitors */
|
||||
_browsers: [],
|
||||
_platforms: [],
|
||||
|
||||
addBrowserPlatform: function(visitor) {
|
||||
//console.info('addBrowserPlatform', visitor);
|
||||
|
||||
const me = BotMon.live.data.analytics;
|
||||
|
||||
// add to browsers list:
|
||||
let browserRec = ( visitor._client ? visitor._client : {'id': 'unknown'});
|
||||
if (visitor._client) {
|
||||
let bRec = me._browsers.find( it => it.id == browserRec.id);
|
||||
if (!bRec) {
|
||||
bRec = {
|
||||
'id': browserRec.id,
|
||||
'count': 1
|
||||
};
|
||||
me._browsers.push(bRec);
|
||||
} else {
|
||||
bRec.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// add to platforms list:
|
||||
let platformRec = ( visitor._platform ? visitor._platform : {'id': 'unknown'});
|
||||
if (visitor._platform) {
|
||||
let pRec = me._platforms.find( it => it.id == platformRec.id);
|
||||
if (!pRec) {
|
||||
pRec = {
|
||||
'id': platformRec.id,
|
||||
'count': 1
|
||||
};
|
||||
me._platforms.push(pRec);
|
||||
} else {
|
||||
pRec.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
getTopBrowsers: function(max) {
|
||||
|
||||
const me = BotMon.live.data.analytics;
|
||||
|
||||
me._browsers.sort( (a,b) => b.count - a.count);
|
||||
|
||||
// how many browsers to show:
|
||||
const max2 = ( me._browsers.length >= max ? max-1 : max );
|
||||
|
||||
const rArr = []; // return array
|
||||
let total = 0;
|
||||
const others = {
|
||||
'id': 'other',
|
||||
'name': "Others",
|
||||
'count': 0
|
||||
}
|
||||
for (let i=0; i < me._browsers.length; i++) {
|
||||
if (i < max2) {
|
||||
rArr.push({
|
||||
'id': me._browsers[i].id,
|
||||
'name': BotMon.live.data.clients.getName(me._browsers[i].id),
|
||||
'count': me._browsers[i].count
|
||||
});
|
||||
total += me._browsers[i].count;
|
||||
} else {
|
||||
others.count += me._browsers[i].count;
|
||||
total += me._browsers[i].count;
|
||||
}
|
||||
};
|
||||
|
||||
if (me._browsers.length > (max-1)) {
|
||||
rArr.push(others);
|
||||
}
|
||||
|
||||
// update percentages:
|
||||
rArr.forEach( it => {
|
||||
it.pct = Math.round(it.count * 100 / total);
|
||||
})
|
||||
|
||||
return rArr;
|
||||
},
|
||||
|
||||
getTopPlatforms: function(max) {
|
||||
|
||||
const me = BotMon.live.data.analytics;
|
||||
|
||||
me._platforms.sort( (a,b) => b.count - a.count);
|
||||
// how many browsers to show:
|
||||
const max2 = ( me._platforms.length >= max ? max-1 : max );
|
||||
|
||||
const rArr = []; // return array
|
||||
let total = 0;
|
||||
const others = {
|
||||
'id': 'other',
|
||||
'name': "Others",
|
||||
'count': 0
|
||||
}
|
||||
for (let i=0; i < me._platforms.length; i++) {
|
||||
if (i < max2) {
|
||||
rArr.push({
|
||||
'id': me._platforms[i].id,
|
||||
'name': BotMon.live.data.platforms.getName(me._platforms[i].id),
|
||||
'count': me._platforms[i].count
|
||||
});
|
||||
total += me._platforms[i].count;
|
||||
} else {
|
||||
others.count += me._platforms[i].count;
|
||||
total += me._platforms[i].count;
|
||||
}
|
||||
};
|
||||
|
||||
if (me._platforms.length > (max-1)) {
|
||||
rArr.push(others);
|
||||
}
|
||||
|
||||
// update percentages:
|
||||
rArr.forEach( it => {
|
||||
it.pct = Math.round(it.count * 100 / total);
|
||||
})
|
||||
|
||||
return rArr;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -800,9 +933,9 @@ BotMon.live = {
|
||||
|
||||
// check for unknown bots:
|
||||
if (!botInfo) {
|
||||
const botmatch = agent.match(/[^\s](\w*bot)[\/\s;\),$]/i);
|
||||
const botmatch = agent.match(/([\s\d\w]*bot|[\s\d\w]*crawler|[\s\d\w]*spider)[\/\s;\),\\.$]/i);
|
||||
if(botmatch) {
|
||||
botInfo = {'id': "other", 'n': "Other", "bot": botmatch[0] };
|
||||
botInfo = {'id': ( botmatch[1] || "other_" ), 'n': "Other" + ( botmatch[1] ? " (" + botmatch[1] + ")" : "" ) , "bot": botmatch[1] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,6 +1003,12 @@ BotMon.live = {
|
||||
return match;
|
||||
},
|
||||
|
||||
// return the browser name for a browser ID:
|
||||
getName: function(id) {
|
||||
const it = BotMon.live.data.clients._list.find(client => client.id == id);
|
||||
return it.n;
|
||||
},
|
||||
|
||||
// indicates if the list is loaded and ready to use:
|
||||
_ready: false,
|
||||
|
||||
@@ -929,6 +1068,14 @@ BotMon.live = {
|
||||
return match;
|
||||
},
|
||||
|
||||
// return the platform name for a given ID:
|
||||
getName: function(id) {
|
||||
const it = BotMon.live.data.platforms._list.find( pf => pf.id == id);
|
||||
console.log(it);
|
||||
return ( it ? it.n : 'Unknown' );
|
||||
},
|
||||
|
||||
|
||||
// indicates if the list is loaded and ready to use:
|
||||
_ready: false,
|
||||
|
||||
@@ -944,7 +1091,7 @@ BotMon.live = {
|
||||
|
||||
// Load the list of known bots:
|
||||
BotMon.live.gui.status.showBusy("Loading list of rules …");
|
||||
const url = BotMon._baseDir + 'config/rules.json';
|
||||
const url = BotMon._baseDir + 'config/botmon-config.json';
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
@@ -1413,6 +1560,46 @@ BotMon.live = {
|
||||
}
|
||||
}
|
||||
|
||||
// update the webmetrics clients list:
|
||||
const wmclients = document.getElementById('botmon__today__wm_clients');
|
||||
if (wmclients) {
|
||||
|
||||
wmclients.appendChild(makeElement('dt', {}, "Browsers (humans only)"));
|
||||
|
||||
const clientList = BotMon.live.data.analytics.getTopBrowsers(5);
|
||||
if (clientList) {
|
||||
clientList.forEach( (cInfo) => {
|
||||
const cDd = makeElement('dd');
|
||||
cDd.appendChild(makeElement('span', {'class': 'has_icon client_' + cInfo.id }, ( cInfo.name ? cInfo.name : cInfo.id)));
|
||||
cDd.appendChild(makeElement('span', {
|
||||
'class': 'count',
|
||||
'title': cInfo.count + " page views"
|
||||
}, Math.round(cInfo.pct) + '%'));
|
||||
wmclients.appendChild(cDd);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// update the webmetrics platforms list:
|
||||
const wmplatforms = document.getElementById('botmon__today__wm_platforms');
|
||||
if (wmplatforms) {
|
||||
|
||||
wmplatforms.appendChild(makeElement('dt', {}, "Platforms (humans only)"));
|
||||
|
||||
const pfList = BotMon.live.data.analytics.getTopPlatforms(5);
|
||||
if (pfList) {
|
||||
pfList.forEach( (pInfo) => {
|
||||
const pDd = makeElement('dd');
|
||||
pDd.appendChild(makeElement('span', {'class': 'has_icon client_' + pInfo.id }, ( pInfo.name ? pInfo.name : pInfo.id)));
|
||||
pDd.appendChild(makeElement('span', {
|
||||
'class': 'count',
|
||||
'title': pInfo.count + " page views"
|
||||
}, Math.round(pInfo.pct) + '%'));
|
||||
wmplatforms.appendChild(pDd);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user