Stats improvements
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,5 +2,5 @@
|
|||||||
logs/*.log.txt
|
logs/*.log.txt
|
||||||
logs/*.srv.txt
|
logs/*.srv.txt
|
||||||
logs/*.tck.txt
|
logs/*.tck.txt
|
||||||
config/user-config.json
|
config/user-*.json
|
||||||
php_errors.log
|
php_errors.log
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
# DokuWiki Bot Monitoring Plugin
|
# DokuWiki Bot Monitoring Plugin
|
||||||
Plugin for live-monitoring your DokuWiki instance for bot activity
|
Plugin for live-monitoring your DokuWiki instance for bot activity
|
||||||
|
|
||||||
IMPORTANT: This is an experimental plugin to investigate bot traffic. This is not a "install and forget" software, but rather requires that you actively look at and manage the log files.
|
IMPORTANT: This is an experimental plugin to investigate bot traffic. This is not a "install and forget" software, but rather requires that you actively look at and manage data for this to be useful.
|
||||||
|
|
||||||
This plugin creates various log files in its own "logs" directory. These files can get quite large, and you should check them actively.
|
In addition to collecting a lot fo information about bot activity on your server, it now also has a simple Captcha function that you can use to block off bots from downloading your precious content. It is however advisable to only activate this after you already have a better understanding of your own site's traffic patterns (both by bots and by humans) to avoid over-blocking legitimate users.
|
||||||
|
|
||||||
Also, these files can get quite large and fill up your server. Make sure to manually delete older files from time to time!
|
For more information, please see the DokuWiki Plugin page at: https://www.dokuwiki.org/plugin:botmon and the documentation found at: https://leib.be/sascha/projects/dokuwiki/botmon/index
|
||||||
|
|
||||||
For more information, please see the DokuWiki Plugin page at: https://www.dokuwiki.org/plugin:botmon
|
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
|||||||
echo DOKU_TAB . DOKU_TAB . "cj.async=true;cj.defer=true;cj.type='text/javascript';" . NL;
|
echo DOKU_TAB . DOKU_TAB . "cj.async=true;cj.defer=true;cj.type='text/javascript';" . NL;
|
||||||
echo DOKU_TAB . DOKU_TAB . "cj.src='".DOKU_BASE."lib/plugins/botmon/captcha.js';" . NL;
|
echo DOKU_TAB . DOKU_TAB . "cj.src='".DOKU_BASE."lib/plugins/botmon/captcha.js';" . NL;
|
||||||
echo DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(cj);" . NL;
|
echo DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(cj);" . NL;
|
||||||
echo DOKU_TAB . "});";
|
echo DOKU_TAB . "});" . NL;
|
||||||
|
|
||||||
// add the translated strings for the captcha:
|
// add the translated strings for the captcha:
|
||||||
echo DOKU_TAB . '$BMLocales = {' . NL;
|
echo DOKU_TAB . '$BMLocales = {' . NL;
|
||||||
|
|||||||
@@ -653,6 +653,7 @@
|
|||||||
& {
|
& {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content auto;
|
grid-template-columns: min-content auto;
|
||||||
|
gap: .25em .5em;
|
||||||
border-left: transparent none 0;
|
border-left: transparent none 0;
|
||||||
margin: 0 .5rem .25rem 0;
|
margin: 0 .5rem .25rem 0;
|
||||||
}
|
}
|
||||||
|
|||||||
255
admin.js
255
admin.js
@@ -161,6 +161,7 @@ const BotMon = {
|
|||||||
|
|
||||||
var bg = b;
|
var bg = b;
|
||||||
var sm = a;
|
var sm = a;
|
||||||
|
if (a == 0 || b == 0) return '—';
|
||||||
if (a > b) {
|
if (a > b) {
|
||||||
var bg = a;
|
var bg = a;
|
||||||
var sm = b;
|
var sm = b;
|
||||||
@@ -291,7 +292,8 @@ BotMon.live = {
|
|||||||
// shortcut to make code more readable:
|
// shortcut to make code more readable:
|
||||||
const model = BotMon.live.data.model;
|
const model = BotMon.live.data.model;
|
||||||
|
|
||||||
//const timeout = 60 * 60 * 1000; // session timeout: One hour
|
// combine Bot networks to one visitor?
|
||||||
|
const combineNets = (BMSettings.hasOwnProperty('combineNets') ? BMSettings['combineNets'] : true);;
|
||||||
|
|
||||||
if (visitor._type == BM_USERTYPE.KNOWN_BOT) { // known bots match by their bot ID:
|
if (visitor._type == BM_USERTYPE.KNOWN_BOT) { // known bots match by their bot ID:
|
||||||
|
|
||||||
@@ -304,6 +306,23 @@ BotMon.live = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (combineNets && visitor.hasOwnProperty('_ipRange')) { // combine with other visits from the same range
|
||||||
|
|
||||||
|
let nonRangeVisitor = null;
|
||||||
|
|
||||||
|
for (let i=0; i<model._visitors.length; i++) {
|
||||||
|
const v = model._visitors[i];
|
||||||
|
|
||||||
|
if ( v.hasOwnProperty('_ipRange') && v._ipRange.g == visitor._ipRange.g ) { // match the IPRange Group IDs
|
||||||
|
return v;
|
||||||
|
} else if ( v.id.trim() !== '' && v.id == visitor.id) { // match the DW/PHP IDs
|
||||||
|
nonRangeVisitor = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no ip range was found, return the non-range visitor instead
|
||||||
|
if (nonRangeVisitor) return nonRangeVisitor;
|
||||||
|
|
||||||
} else { // other types match by their DW/PHPIDs:
|
} else { // other types match by their DW/PHPIDs:
|
||||||
|
|
||||||
// loop over all visitors already registered and check for ID matches:
|
// loop over all visitors already registered and check for ID matches:
|
||||||
@@ -346,6 +365,7 @@ BotMon.live = {
|
|||||||
registerVisit: function(nv, type) {
|
registerVisit: function(nv, type) {
|
||||||
//console.info('registerVisit', nv, type);
|
//console.info('registerVisit', nv, type);
|
||||||
|
|
||||||
|
|
||||||
// shortcut to make code more readable:
|
// shortcut to make code more readable:
|
||||||
const model = BotMon.live.data.model;
|
const model = BotMon.live.data.model;
|
||||||
|
|
||||||
@@ -365,6 +385,12 @@ BotMon.live = {
|
|||||||
if (!nv._firstSeen) nv._firstSeen = nv.ts; // first-seen
|
if (!nv._firstSeen) nv._firstSeen = nv.ts; // first-seen
|
||||||
nv._lastSeen = nv.ts; // last-seen
|
nv._lastSeen = nv.ts; // last-seen
|
||||||
|
|
||||||
|
// known bot IP range?
|
||||||
|
if (nv._type == BM_USERTYPE.UNKNOWN) { // only for unknown visitors
|
||||||
|
const ipInfo = BotMon.live.data.ipRanges.match(nv.ip);
|
||||||
|
if (ipInfo) nv._ipRange = ipInfo;
|
||||||
|
}
|
||||||
|
|
||||||
// country name:
|
// country name:
|
||||||
try {
|
try {
|
||||||
nv._country = ( nv.geo == 'local' ? "localhost" : "Unknown" );
|
nv._country = ( nv.geo == 'local' ? "localhost" : "Unknown" );
|
||||||
@@ -549,9 +575,9 @@ BotMon.live = {
|
|||||||
const cStr = cObj._str();
|
const cStr = cObj._str();
|
||||||
switch (cStr) {
|
switch (cStr) {
|
||||||
case 'Y':
|
case 'Y':
|
||||||
case 'NY': return "Blocked by captcha";
|
case 'NY': return "Blocked.";
|
||||||
case 'YN': return "Captcha solved";
|
case 'YN': return "Solved";
|
||||||
case 'W': return "IP Address whitelisted";
|
case 'W': return "Whitelisted";
|
||||||
case 'H': return "HEAD request, no captcha";
|
case 'H': return "HEAD request, no captcha";
|
||||||
default: return "Undefined: " + cStr;
|
default: return "Undefined: " + cStr;
|
||||||
}
|
}
|
||||||
@@ -570,14 +596,36 @@ BotMon.live = {
|
|||||||
|
|
||||||
// data storage:
|
// data storage:
|
||||||
data: {
|
data: {
|
||||||
totalVisits: 0,
|
visits: {
|
||||||
totalPageViews: 0,
|
bots: 0,
|
||||||
humanPageViews: 0,
|
|
||||||
bots: {
|
|
||||||
known: 0,
|
|
||||||
suspected: 0,
|
suspected: 0,
|
||||||
human: 0,
|
humans: 0,
|
||||||
users: 0
|
users: 0,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
views: {
|
||||||
|
bots: 0,
|
||||||
|
suspected: 0,
|
||||||
|
humans: 0,
|
||||||
|
users: 0,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
loads: {
|
||||||
|
bots: 0,
|
||||||
|
suspected: 0,
|
||||||
|
humans: 0,
|
||||||
|
users: 0,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
captcha: {
|
||||||
|
bots_blocked: 0,
|
||||||
|
bots_passed: 0,
|
||||||
|
bots_whitelisted: 0,
|
||||||
|
humans_blocked: 0,
|
||||||
|
humans_passed: 0,
|
||||||
|
sus_blocked: 0,
|
||||||
|
sus_passed: 0,
|
||||||
|
sus_whitelisted: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -595,6 +643,7 @@ BotMon.live = {
|
|||||||
|
|
||||||
// shortcut to make code more readable:
|
// shortcut to make code more readable:
|
||||||
const model = BotMon.live.data.model;
|
const model = BotMon.live.data.model;
|
||||||
|
const data = BotMon.live.data.analytics.data;
|
||||||
const me = BotMon.live.data.analytics;
|
const me = BotMon.live.data.analytics;
|
||||||
|
|
||||||
BotMon.live.gui.status.showBusy("Analysing data …");
|
BotMon.live.gui.status.showBusy("Analysing data …");
|
||||||
@@ -602,21 +651,35 @@ BotMon.live = {
|
|||||||
// loop over all visitors:
|
// loop over all visitors:
|
||||||
model._visitors.forEach( (v) => {
|
model._visitors.forEach( (v) => {
|
||||||
|
|
||||||
// count visits and page views:
|
const captchaStr = v._captcha._str();
|
||||||
this.data.totalVisits += 1;
|
|
||||||
this.data.totalPageViews += v._viewCount;
|
// count total visits and page views:
|
||||||
|
data.visits.total += 1;
|
||||||
|
data.loads.total += v._loadCount;
|
||||||
|
data.views.total += v._viewCount;
|
||||||
|
|
||||||
// check for typical bot aspects:
|
// check for typical bot aspects:
|
||||||
let botScore = 0;
|
let botScore = 0;
|
||||||
|
|
||||||
if (v._type == BM_USERTYPE.KNOWN_BOT) { // known bots
|
if (v._type == BM_USERTYPE.KNOWN_BOT) { // known bots
|
||||||
|
|
||||||
this.data.bots.known += v._viewCount;
|
data.visits.bots += 1;
|
||||||
|
data.views.bots += v._viewCount;
|
||||||
this.groups.knownBots.push(v);
|
this.groups.knownBots.push(v);
|
||||||
|
|
||||||
|
// captcha counter
|
||||||
|
if (captchaStr == 'Y') {
|
||||||
|
data.captcha.bots_blocked += 1;
|
||||||
|
} else if (captchaStr == 'YN') {
|
||||||
|
data.captcha.bots_passed += 1;
|
||||||
|
} else if (captchaStr == 'W') {
|
||||||
|
data.captcha.bots_whitelisted += 1;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (v._type == BM_USERTYPE.KNOWN_USER) { // known users */
|
} else if (v._type == BM_USERTYPE.KNOWN_USER) { // known users */
|
||||||
|
|
||||||
this.data.bots.users += v._viewCount;
|
data.visits.users += 1;
|
||||||
|
data.views.users += v._viewCount;
|
||||||
this.groups.users.push(v);
|
this.groups.users.push(v);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -627,30 +690,54 @@ BotMon.live = {
|
|||||||
v._botVal = e.val;
|
v._botVal = e.val;
|
||||||
|
|
||||||
if (e.isBot) { // likely bots
|
if (e.isBot) { // likely bots
|
||||||
|
|
||||||
v._type = BM_USERTYPE.LIKELY_BOT;
|
v._type = BM_USERTYPE.LIKELY_BOT;
|
||||||
this.data.bots.suspected += v._viewCount;
|
data.visits.suspected += 1;
|
||||||
|
data.views.suspected += v._viewCount;
|
||||||
this.groups.suspectedBots.push(v);
|
this.groups.suspectedBots.push(v);
|
||||||
|
|
||||||
|
// captcha counter
|
||||||
|
if (captchaStr == 'Y') {
|
||||||
|
data.captcha.sus_blocked += 1;
|
||||||
|
} else if (captchaStr == 'YN') {
|
||||||
|
data.captcha.sus_passed += 1;
|
||||||
|
} else if (captchaStr == 'W') {
|
||||||
|
data.captcha.sus_whitelisted += 1;
|
||||||
|
}
|
||||||
|
|
||||||
} else { // probably humans
|
} else { // probably humans
|
||||||
|
|
||||||
v._type = BM_USERTYPE.PROBABLY_HUMAN;
|
v._type = BM_USERTYPE.PROBABLY_HUMAN;
|
||||||
this.data.bots.human += v._viewCount;
|
data.visits.humans += 1;
|
||||||
|
data.views.humans += v._viewCount;
|
||||||
|
|
||||||
this.groups.humans.push(v);
|
this.groups.humans.push(v);
|
||||||
|
|
||||||
|
// captcha counter
|
||||||
|
if (captchaStr == 'Y') {
|
||||||
|
data.captcha.humans_blocked += 1;
|
||||||
|
} else if (captchaStr == 'YN') {
|
||||||
|
data.captcha.humans_passed += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform actions depending on the visitor type:
|
// perform actions depending on the visitor type:
|
||||||
if (v._type == BM_USERTYPE.KNOWN_BOT ) { /* known bots only */
|
if (v._type == BM_USERTYPE.KNOWN_BOT ) { /* known bots only */
|
||||||
|
|
||||||
|
// no specific actions here.
|
||||||
|
|
||||||
} else if (v._type == BM_USERTYPE.LIKELY_BOT) { /* probable bots only */
|
} else if (v._type == BM_USERTYPE.LIKELY_BOT) { /* probable bots only */
|
||||||
|
|
||||||
// add bot views to IP range information:
|
// add bot views to IP range information:
|
||||||
me.addToIpRanges(v);
|
me.addToIpRanges(v);
|
||||||
|
|
||||||
} else { /* humans only */
|
} else { /* registered users and probable humans */
|
||||||
|
|
||||||
// add browser and platform statistics:
|
// add browser and platform statistics:
|
||||||
me.addBrowserPlatform(v);
|
me.addBrowserPlatform(v);
|
||||||
|
|
||||||
// add
|
// add to referrer and pages lists:
|
||||||
v._pageViews.forEach( pv => {
|
v._pageViews.forEach( pv => {
|
||||||
me.addToRefererList(pv._ref);
|
me.addToRefererList(pv._ref);
|
||||||
me.addToPagesList(pv.pg);
|
me.addToPagesList(pv.pg);
|
||||||
@@ -1064,7 +1151,6 @@ BotMon.live = {
|
|||||||
|
|
||||||
} else { // no known IP range, let's collect necessary information:
|
} else { // no known IP range, let's collect necessary information:
|
||||||
|
|
||||||
|
|
||||||
// collect basic IP address info:
|
// collect basic IP address info:
|
||||||
if (ipType == BM_IPVERSION.IPv6) {
|
if (ipType == BM_IPVERSION.IPv6) {
|
||||||
ipSeg = ipAddr.split(':');
|
ipSeg = ipAddr.split(':');
|
||||||
@@ -1604,14 +1690,7 @@ BotMon.live = {
|
|||||||
fromKnownBotIP: function(visitor) {
|
fromKnownBotIP: function(visitor) {
|
||||||
//console.info('fromKnownBotIP()', visitor.ip);
|
//console.info('fromKnownBotIP()', visitor.ip);
|
||||||
|
|
||||||
const ipInfo = BotMon.live.data.ipRanges.match(visitor.ip);
|
return visitor.hasOwnProperty('_ipRange');
|
||||||
|
|
||||||
if (ipInfo) {
|
|
||||||
visitor._ipInKnownBotRange = true;
|
|
||||||
visitor._ipRange = ipInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ipInfo !== null);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// is the page language mentioned in the client's accepted languages?
|
// is the page language mentioned in the client's accepted languages?
|
||||||
@@ -1910,47 +1989,48 @@ BotMon.live = {
|
|||||||
*/
|
*/
|
||||||
make: function() {
|
make: function() {
|
||||||
|
|
||||||
const data = BotMon.live.data.analytics.data;
|
|
||||||
|
|
||||||
const maxItemsPerList = 5; // how many list items to show?
|
const maxItemsPerList = 5; // how many list items to show?
|
||||||
|
const useCaptcha = BMSettings.useCaptcha || false;
|
||||||
|
|
||||||
const kNoData = '–'; // shown when data is missing
|
const kNoData = '–'; // shown when data is missing
|
||||||
|
const kSeparator = ' / ';
|
||||||
|
|
||||||
// shortcut for neater code:
|
// shortcuts for neater code:
|
||||||
const makeElement = BotMon.t._makeElement;
|
const makeElement = BotMon.t._makeElement;
|
||||||
|
const data = BotMon.live.data.analytics.data;
|
||||||
|
|
||||||
const botsVsHumans = document.getElementById('botmon__today__botsvshumans');
|
const botsVsHumans = document.getElementById('botmon__today__botsvshumans');
|
||||||
if (botsVsHumans) {
|
if (botsVsHumans) {
|
||||||
botsVsHumans.appendChild(makeElement('dt', {}, "Page views"));
|
botsVsHumans.appendChild(makeElement('dt', {}, "Bot statistics"));
|
||||||
|
|
||||||
for (let i = 0; i <= 5; i++) {
|
for (let i = 0; i <= ( useCaptcha ? 5 : 3 ); i++) {
|
||||||
const dd = makeElement('dd');
|
const dd = makeElement('dd');
|
||||||
let title = '';
|
let title = '';
|
||||||
let value = '';
|
let value = '';
|
||||||
switch(i) {
|
switch(i) {
|
||||||
case 0:
|
case 0:
|
||||||
title = "Known bots:";
|
title = "Total (loads / views / visits):";
|
||||||
value = data.bots.known || kNoData;
|
value = (data.loads.total || kNoData) + kSeparator + (data.views.total || kNoData) + kSeparator + (data.visits.total || kNoData);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
title = "Suspected bots:";
|
title = "Known bots (views / visits):";
|
||||||
value = data.bots.suspected || kNoData;
|
value = (data.views.bots || kNoData) + kSeparator + (data.visits.bots || kNoData);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
title = "Probably humans:";
|
title = "Suspected bots (views / visits):";
|
||||||
value = data.bots.human || kNoData;
|
value = (data.visits.suspected || kNoData) + kSeparator + (data.views.suspected || kNoData)
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
title = "Registered users:";
|
title = "Bots-humans ratio (views / visits):";
|
||||||
value = data.bots.users || kNoData;
|
value = BotMon.t._getRatio(data.views.suspected + data.views.bots, data.views.users + data.views.humans, 100) + kSeparator + BotMon.t._getRatio(data.visits.suspected + data.visits.bots, data.visits.users + data.visits.humans, 100);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
title = "Total:";
|
title = "Known bots blocked / passed / whitelisted:";
|
||||||
value = data.totalPageViews || kNoData;
|
value = data.captcha.bots_blocked + kSeparator + data.captcha.bots_passed + kSeparator + data.captcha.bots_whitelisted;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
title = "Bots-humans ratio:";
|
title = "Suspected bots blocked / passed / whitelisted:";
|
||||||
value = BotMon.t._getRatio(data.bots.suspected + data.bots.known, data.bots.users + data.bots.human, 100);
|
value = data.captcha.sus_blocked + kSeparator + data.captcha.sus_passed + kSeparator + data.captcha.sus_whitelisted;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn(`Unknown list type ${i}.`);
|
console.warn(`Unknown list type ${i}.`);
|
||||||
@@ -2007,7 +2087,7 @@ BotMon.live = {
|
|||||||
const wmoverview = document.getElementById('botmon__today__wm_overview');
|
const wmoverview = document.getElementById('botmon__today__wm_overview');
|
||||||
if (wmoverview) {
|
if (wmoverview) {
|
||||||
|
|
||||||
const humanVisits = BotMon.live.data.analytics.groups.users.length + BotMon.live.data.analytics.groups.humans.length;
|
const humanVisits = data.views.total;
|
||||||
const bounceRate = Math.round(100 * (BotMon.live.data.analytics.getBounceCount('users') + BotMon.live.data.analytics.getBounceCount('humans')) / humanVisits);
|
const bounceRate = Math.round(100 * (BotMon.live.data.analytics.getBounceCount('users') + BotMon.live.data.analytics.getBounceCount('humans')) / humanVisits);
|
||||||
|
|
||||||
wmoverview.appendChild(makeElement('dt', {}, "Humans’ metrics"));
|
wmoverview.appendChild(makeElement('dt', {}, "Humans’ metrics"));
|
||||||
@@ -2017,20 +2097,20 @@ BotMon.live = {
|
|||||||
let value = '';
|
let value = '';
|
||||||
switch(i) {
|
switch(i) {
|
||||||
case 0:
|
case 0:
|
||||||
title = "Registered users’ page views:";
|
title = "Registered users (views / visits):";
|
||||||
value = data.bots.users || kNoData;
|
value = (data.views.users || kNoData) + kSeparator + (data.visits.users || kNoData);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
title = "“Probably humans” page views:";
|
title = "Probably humans (views / visits):";
|
||||||
value = data.bots.human || kNoData;
|
value = (data.views.humans || kNoData) + kSeparator + (data.visits.humans || kNoData);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
title = "Total human page views:";
|
title = "Total human page views:";
|
||||||
value = (data.bots.users + data.bots.human) || kNoData;
|
value = (data.views.users + data.views.humans) || kNoData;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
title = "Total human visits:";
|
title = "Total human visits:";
|
||||||
value = humanVisits || kNoData;
|
value = data.views.total || kNoData;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
title = "Humans’ bounce rate:";
|
title = "Humans’ bounce rate:";
|
||||||
@@ -2292,6 +2372,10 @@ BotMon.live = {
|
|||||||
|
|
||||||
const sumClass = ( !data._seenBy || data._seenBy.indexOf(BM_LOGTYPE.SERVER) < 0 ? 'noServer' : 'hasServer');
|
const sumClass = ( !data._seenBy || data._seenBy.indexOf(BM_LOGTYPE.SERVER) < 0 ? 'noServer' : 'hasServer');
|
||||||
|
|
||||||
|
// combine with other networks?
|
||||||
|
const combineNets = (BMSettings.hasOwnProperty('combineNets') ? BMSettings['combineNets'] : true)
|
||||||
|
&& data.hasOwnProperty('_ipRange');
|
||||||
|
|
||||||
const li = make('li'); // root list item
|
const li = make('li'); // root list item
|
||||||
const details = make('details');
|
const details = make('details');
|
||||||
const summary = make('summary', {
|
const summary = make('summary', {
|
||||||
@@ -2301,17 +2385,17 @@ BotMon.live = {
|
|||||||
|
|
||||||
const span1 = make('span'); /* left-hand group */
|
const span1 = make('span'); /* left-hand group */
|
||||||
|
|
||||||
if (data._type !== BM_USERTYPE.KNOWN_BOT) { /* No platform/client for bots */
|
/*if (data._type !== BM_USERTYPE.KNOWN_BOT) { // No platform/client for bots // disabled because no longer relevant
|
||||||
span1.appendChild(make('span', { /* Platform */
|
span1.appendChild(make('span', { // Platform
|
||||||
'class': 'icon_only platform pf_' + (data._platform ? data._platform.id : 'unknown'),
|
'class': 'icon_only platform pf_' + (data._platform ? data._platform.id : 'unknown'),
|
||||||
'title': "Platform: " + platformName
|
'title': "Platform: " + platformName
|
||||||
}, platformName));
|
}, platformName));
|
||||||
|
|
||||||
span1.appendChild(make('span', { /* Client */
|
span1.appendChild(make('span', { // Client
|
||||||
'class': 'icon_only client client cl_' + (data._client ? data._client.id : 'unknown'),
|
'class': 'icon_only client client cl_' + (data._client ? data._client.id : 'unknown'),
|
||||||
'title': "Client: " + clientName
|
'title': "Client: " + clientName
|
||||||
}, clientName));
|
}, clientName));
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// identifier:
|
// identifier:
|
||||||
if (data._type == BM_USERTYPE.KNOWN_BOT) { /* Bot only */
|
if (data._type == BM_USERTYPE.KNOWN_BOT) { /* Bot only */
|
||||||
@@ -2331,16 +2415,22 @@ BotMon.live = {
|
|||||||
|
|
||||||
} else { /* others */
|
} else { /* others */
|
||||||
|
|
||||||
|
if (combineNets) {
|
||||||
|
|
||||||
span1.appendChild(make('span', { // IP-Address
|
const ispName = BotMon.live.data.ipRanges.getOwner( data._ipRange.g ) || data._ipRange.g;
|
||||||
'class': 'has_icon ipaddr ip' + ipType,
|
|
||||||
'title': "IP-Address: " + data.ip
|
|
||||||
}, data.ip));
|
|
||||||
|
|
||||||
/*span1.appendChild(make('span', { // Internal ID
|
span1.appendChild(make('span', { // IP-Address
|
||||||
'class': 'has_icon session typ_' + data.typ,
|
'class': 'has_icon ipaddr ipnet',
|
||||||
'title': "ID: " + data.id
|
'title': "IP-Range: " + data._ipRange.g
|
||||||
}, data.id));*/
|
}, ispName));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
span1.appendChild(make('span', { // IP-Address
|
||||||
|
'class': 'has_icon ipaddr ip' + ipType,
|
||||||
|
'title': "IP-Address: " + data.ip
|
||||||
|
}, data.ip));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span1.appendChild(make('span', { /* page views */
|
span1.appendChild(make('span', { /* page views */
|
||||||
@@ -2362,6 +2452,7 @@ BotMon.live = {
|
|||||||
const span2 = make('span'); /* right-hand group */
|
const span2 = make('span'); /* right-hand group */
|
||||||
|
|
||||||
// country flag:
|
// country flag:
|
||||||
|
if (!combineNets) { // not for combined networks
|
||||||
if (data.geo && data.geo !== 'ZZ') {
|
if (data.geo && data.geo !== 'ZZ') {
|
||||||
span2.appendChild(make('span', {
|
span2.appendChild(make('span', {
|
||||||
'class': 'icon_only country ctry_' + data.geo.toLowerCase(),
|
'class': 'icon_only country ctry_' + data.geo.toLowerCase(),
|
||||||
@@ -2369,21 +2460,22 @@ BotMon.live = {
|
|||||||
'title': "Country: " + ( data._country || "Unknown")
|
'title': "Country: " + ( data._country || "Unknown")
|
||||||
}, ( data._country || "Unknown") ));
|
}, ( data._country || "Unknown") ));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
span2.appendChild(make('span', { // seen-by icon:
|
span2.appendChild(make('span', { // seen-by icon:
|
||||||
'class': 'icon_only seenby sb_' + data._seenBy.join(''),
|
'class': 'icon_only seenby sb_' + data._seenBy.join(''),
|
||||||
'title': "Seen by: " + data._seenBy.join('+')
|
'title': "Seen by: " + data._seenBy.join('+')
|
||||||
}, data._seenBy.join(', ')));
|
}, data._seenBy.join(', ')));
|
||||||
|
|
||||||
// captcha status:
|
// captcha status:
|
||||||
const cCode = ( data._captcha ? data._captcha._str() : '');
|
const cCode = ( data._captcha ? data._captcha._str() : '');
|
||||||
if (cCode !== '') {
|
if (cCode !== '') {
|
||||||
const cTitle = model._makeCaptchaTitle(data._captcha)
|
const cTitle = model._makeCaptchaTitle(data._captcha)
|
||||||
span2.appendChild(make('span', { // captcha status
|
span2.appendChild(make('span', { // captcha status
|
||||||
'class': 'icon_only captcha cap_' + cCode,
|
'class': 'icon_only captcha cap_' + cCode,
|
||||||
'title': "Captcha-status: " + cTitle
|
'title': "Captcha-status: " + cTitle
|
||||||
}, cTitle));
|
}, cTitle));
|
||||||
}
|
}
|
||||||
|
|
||||||
summary.appendChild(span2);
|
summary.appendChild(span2);
|
||||||
|
|
||||||
@@ -2569,6 +2661,13 @@ BotMon.live = {
|
|||||||
dl.appendChild(evalDd);
|
dl.appendChild(evalDd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for debugging only. Disable on production:
|
||||||
|
dl.appendChild(make('dt', {}, "Debug info:"));
|
||||||
|
const dbgDd = make('dd', {'class': 'debug'});
|
||||||
|
dbgDd.innerHTML = '<pre>' + JSON.stringify(data, null, 4) + '</pre>';
|
||||||
|
dl.appendChild(dbgDd);
|
||||||
|
|
||||||
// return the element to add to the UI:
|
// return the element to add to the UI:
|
||||||
return dl;
|
return dl;
|
||||||
},
|
},
|
||||||
|
|||||||
18
admin.php
18
admin.php
@@ -43,7 +43,7 @@ class admin_plugin_botmon extends AdminPlugin {
|
|||||||
$pluginPath = $conf['basedir'] . 'lib/plugins/' . $this->getPluginName();
|
$pluginPath = $conf['basedir'] . 'lib/plugins/' . $this->getPluginName();
|
||||||
|
|
||||||
/* Plugin Headline */
|
/* Plugin Headline */
|
||||||
echo '<div id="botmon__admin">
|
echo NL . '<div id="botmon__admin">
|
||||||
<h1>Bot Monitoring Plugin</h1>
|
<h1>Bot Monitoring Plugin</h1>
|
||||||
<nav id="botmon__tabs">
|
<nav id="botmon__tabs">
|
||||||
<ul class="tabs" role="tablist">
|
<ul class="tabs" role="tablist">
|
||||||
@@ -92,7 +92,7 @@ class admin_plugin_botmon extends AdminPlugin {
|
|||||||
</article>
|
</article>
|
||||||
<article role="tabpanel" id="botmon__log" hidden>
|
<article role="tabpanel" id="botmon__log" hidden>
|
||||||
<h2 class="a11y">Process log</h2>
|
<h2 class="a11y">Process log</h2>
|
||||||
<ul id="botmon__loglist">';
|
<ul id="botmon__loglist">' . NL;
|
||||||
|
|
||||||
/* proces old logs */
|
/* proces old logs */
|
||||||
if ($hasOldLogFiles) {
|
if ($hasOldLogFiles) {
|
||||||
@@ -101,15 +101,17 @@ class admin_plugin_botmon extends AdminPlugin {
|
|||||||
|
|
||||||
$helper->cleanup();
|
$helper->cleanup();
|
||||||
} else {
|
} else {
|
||||||
echo '<li>No files to process.</li>';
|
echo DOKU_TAB . DOKU_TAB . DOKU_TAB . '<li>No files to process.</li>' . NL;
|
||||||
}
|
}
|
||||||
|
|
||||||
echo '</article>' . NL;
|
echo DOKU_TAB . DOKU_TAB . '</ul>' . NL . DOKU_TAB . '</article>' . NL;
|
||||||
echo '<script>
|
echo DOKU_TAB . '<script>
|
||||||
BMSettings = {
|
const BMSettings = {
|
||||||
showday: ' . json_encode($this->getConf('showday')) . '
|
showday: ' . json_encode($this->getConf('showday')) . ',
|
||||||
|
combineNets: ' . json_encode($this->getConf('combineNets')) . ',
|
||||||
|
useCaptcha: ' . json_encode($this->getConf('useCaptcha') !== 'disabled') . '
|
||||||
};
|
};
|
||||||
</script>';
|
</script>' . NL;
|
||||||
echo '</div><!-- End of BotMon Admin Tool -->';
|
echo '</div><!-- End of BotMon Admin Tool -->';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,7 +178,8 @@ const $BMCaptcha = {
|
|||||||
const hash = $BMCaptcha.digest.hash(dat.join('|'));
|
const hash = $BMCaptcha.digest.hash(dat.join('|'));
|
||||||
|
|
||||||
// set the cookie:
|
// set the cookie:
|
||||||
document.cookie = "DWConfirm=" + hash + ';path=/;';
|
document.cookie = "DWConfirm=" + encodeURIComponent(hash) + ';path=/;hostOnly;session;sameSite=strict;'
|
||||||
|
+ (document.location.protocol === 'https:' ? 'secure;' : '');
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
* @author Sascha Leib <sascha@leib.be>
|
* @author Sascha Leib <sascha@leib.be>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$conf['showday'] = 'yesterday';
|
$conf['showday'] = 'today';
|
||||||
|
$conf['combineNets'] = true;
|
||||||
$conf['geoiplib'] = 'disabled';
|
$conf['geoiplib'] = 'disabled';
|
||||||
$conf['useCaptcha'] = 'disabled';
|
$conf['useCaptcha'] = 'disabled';
|
||||||
$conf['captchaSeed'] = 'c53bc5f94929451987efa6c768d8856b';
|
$conf['captchaSeed'] = 'c53bc5f94929451987efa6c768d8856b';
|
||||||
|
|||||||
@@ -8,10 +8,11 @@
|
|||||||
$meta['showday'] = array('multichoice',
|
$meta['showday'] = array('multichoice',
|
||||||
'_choices' => array ('yesterday', 'today'));
|
'_choices' => array ('yesterday', 'today'));
|
||||||
|
|
||||||
|
$meta['combineNets'] = array('onoff');
|
||||||
|
|
||||||
$meta['geoiplib'] = array('multichoice',
|
$meta['geoiplib'] = array('multichoice',
|
||||||
'_choices' => array ('disabled', 'phpgeoip'));
|
'_choices' => array ('disabled', 'phpgeoip'));
|
||||||
|
|
||||||
//$meta['useCaptcha'] = array('onoff');
|
|
||||||
$meta['useCaptcha'] = array('multichoice',
|
$meta['useCaptcha'] = array('multichoice',
|
||||||
'_choices' => array ('disabled', 'loremipsum', 'dada'));
|
'_choices' => array ('disabled', 'loremipsum', 'dada'));
|
||||||
$meta['captchaSeed'] = array('string');
|
$meta['captchaSeed'] = array('string');
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
"bot": 30
|
"bot": 30
|
||||||
},
|
},
|
||||||
{"func": "blockedByCaptcha", "params": [],
|
{"func": "blockedByCaptcha", "params": [],
|
||||||
"id": "blockedByCaptcha", "desc": "Visitor was blocked by captcha",
|
"id": "blockedByCaptcha", "desc": "Visitor did not solve the captcha",
|
||||||
"bot": 20
|
"bot": 20
|
||||||
},
|
},
|
||||||
{"func": "whitelistedByCaptcha", "params": [],
|
{"func": "whitelistedByCaptcha", "params": [],
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"groups": [
|
"groups": [
|
||||||
{"id": "alibaba", "name": "Alibaba"},
|
{"id": "alibaba", "name": "Alibaba Network"},
|
||||||
{"id": "amazon", "name": "Amazon"},
|
{"id": "amazon", "name": "Amazon Data Centres"},
|
||||||
{"id": "bezeq", "name": "Bezeq Int."},
|
{"id": "bezeq", "name": "Bezeq Int."},
|
||||||
{"id": "brasilnet", "name": "BrasilNet"},
|
{"id": "brasilnet", "name": "BrasilNet"},
|
||||||
{"id": "charter", "name": "Charter Inc."},
|
{"id": "charter", "name": "Charter Inc. Range"},
|
||||||
{"id": "chinanet", "name": "Chinanet"},
|
{"id": "chinanet", "name": "ChinaNet"},
|
||||||
{"id": "cloudflare", "name": "Cloudflare Inc."},
|
{"id": "cloudflare", "name": "Cloudflare Network"},
|
||||||
{"id": "cnisp", "name": "China ISP"},
|
{"id": "cnisp", "name": "China ISP Range"},
|
||||||
{"id": "cnmob", "name": "China Mobile"},
|
{"id": "cnmob", "name": "China Mobile"},
|
||||||
{"id": "google", "name": "Google LLC"},
|
{"id": "google", "name": "Google LLC Network"},
|
||||||
{"id": "hetzner", "name": "Hetzner US"},
|
{"id": "hetzner", "name": "Hetzner US"},
|
||||||
{"id": "huawei", "name": "Huawei"},
|
{"id": "huawei", "name": "Huawei Network"},
|
||||||
{"id": "misc_sa", "name": "Misc. SA ISPs"},
|
{"id": "misc_sa", "name": "Misc. SA ISPs"},
|
||||||
{"id": "tencent", "name": "Tencent"},
|
{"id": "tencent", "name": "Tencent Network"},
|
||||||
{"id": "unicom", "name": "China Unicom"},
|
{"id": "unicom", "name": "China Unicom"},
|
||||||
{"id": "vnpt", "name": "Vietnam Telecom"},
|
{"id": "vnpt", "name": "Vietnam Telecom"},
|
||||||
{"id": "vdsina", "name": "VDSina NL"},
|
{"id": "vdsina", "name": "VDSina Network"},
|
||||||
{"id": "zenlayer", "name": "Zenlayer"}
|
{"id": "zenlayer", "name": "Zenlayer Network"}
|
||||||
],
|
],
|
||||||
"ranges": [
|
"ranges": [
|
||||||
{"from": "1.92.0.0", "to": "1.95.255.254", "m": 14, "g": "huawei"},
|
{"from": "1.92.0.0", "to": "1.95.255.254", "m": 14, "g": "huawei"},
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
{"from": "111.119.192.0", "to": "111.119.255.254", "m": 18, "g": "huawei"},
|
{"from": "111.119.192.0", "to": "111.119.255.254", "m": 18, "g": "huawei"},
|
||||||
{"from": "113.160.0.0", "to": "113.191.255.254", "m": 11, "g": "vnpt"},
|
{"from": "113.160.0.0", "to": "113.191.255.254", "m": 11, "g": "vnpt"},
|
||||||
{"from": "114.208.0.0", "to": "114.223.255.254", "m": 12, "g": "unicom"},
|
{"from": "114.208.0.0", "to": "114.223.255.254", "m": 12, "g": "unicom"},
|
||||||
|
{"from": "114.119.0.0", "to": "114.119.255.254", "m": 11, "g": "huawei"},
|
||||||
{"from": "114.224.0.0", "to": "114.255.255.254", "m": 11, "g": "unicom"},
|
{"from": "114.224.0.0", "to": "114.255.255.254", "m": 11, "g": "unicom"},
|
||||||
{"from": "119.8.0.0", "to": "119.8.255.254", "m": 16, "g": "huawei"},
|
{"from": "119.8.0.0", "to": "119.8.255.254", "m": 16, "g": "huawei"},
|
||||||
{"from": "119.13.0.0", "to": "119.13.255.254", "m": 16, "g": "huawei"},
|
{"from": "119.13.0.0", "to": "119.13.255.254", "m": 16, "g": "huawei"},
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
{"from": "122.9.0.0", "to": "122.9.255.254", "m": 16, "g": "huawei"},
|
{"from": "122.9.0.0", "to": "122.9.255.254", "m": 16, "g": "huawei"},
|
||||||
{"from": "123.16.0.0", "to": "123.31.255.254", "m": 12, "g": "vnpt"},
|
{"from": "123.16.0.0", "to": "123.31.255.254", "m": 12, "g": "vnpt"},
|
||||||
{"from": "124.243.128.0", "to": "124.243.191.254", "m": 18, "g": "huawei"},
|
{"from": "124.243.128.0", "to": "124.243.191.254", "m": 18, "g": "huawei"},
|
||||||
|
{"from": "136.107.0.0", "to": "136.125.255.254", "m": "+", "g": "google"},
|
||||||
{"from": "138.59.0.0", "to": "138.59.225.254", "m": 16, "g": "misc_sa"},
|
{"from": "138.59.0.0", "to": "138.59.225.254", "m": 16, "g": "misc_sa"},
|
||||||
{"from": "138.121.0.0", "to": "138.121.225.254", "m": 16, "g": "misc_sa"},
|
{"from": "138.121.0.0", "to": "138.121.225.254", "m": 16, "g": "misc_sa"},
|
||||||
{"from": "142.147.128.0", "to": "1142.147.255.254", "m": 17, "g": "w2obj"},
|
{"from": "142.147.128.0", "to": "1142.147.255.254", "m": 17, "g": "w2obj"},
|
||||||
|
|||||||
BIN
img/addr.png
BIN
img/addr.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
img/captcha.png
BIN
img/captcha.png
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -6,9 +6,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Captcha dialog locale strings:
|
// Captcha dialog locale strings:
|
||||||
$lang['bm_dlgTitle'] = 'Benutzerüberprüfung';
|
$lang['bm_dlgTitle'] = 'Benutzerüberprüfung';
|
||||||
$lang['bm_dlgSubtitle'] = 'Bitte bestätige, dass du kein Bot bist:';
|
$lang['bm_dlgSubtitle'] = 'Bitte bestätige, dass du kein Bot bist:';
|
||||||
$lang['bm_dlgConfirm'] = 'Klicke, um zu bestätigen.';
|
$lang['bm_dlgConfirm'] = 'Klicke, um zu bestätigen.';
|
||||||
$lang['bm_dlgChecking'] = 'Wird überprüft …';
|
$lang['bm_dlgChecking'] = 'Wird überprüft …';
|
||||||
$lang['bm_dlgLoading'] = 'Seite wird geladen …';
|
$lang['bm_dlgLoading'] = 'Seite wird geladen …';
|
||||||
$lang['bm_dlgError'] = 'Es ist ein Fehler aufgetreten.';
|
$lang['bm_dlgError'] = 'Es ist ein Fehler aufgetreten.';
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ $lang['showday'] = 'Which data to show in the “Latest” tab:';
|
|||||||
$lang['showday_o_yesterday'] = 'Last full day (yesterday)';
|
$lang['showday_o_yesterday'] = 'Last full day (yesterday)';
|
||||||
$lang['showday_o_today'] = 'Ongoing logs (today)';
|
$lang['showday_o_today'] = 'Ongoing logs (today)';
|
||||||
|
|
||||||
|
$lang['combineNets'] = 'Combine visits from known IP-ranges into one entry:';
|
||||||
|
|
||||||
$lang['geoiplib'] = 'Add GeoIP Information<br><small>(requires PHP module to be installed)</small>';
|
$lang['geoiplib'] = 'Add GeoIP Information<br><small>(requires PHP module to be installed)</small>';
|
||||||
$lang['geoiplib_o_disabled'] = 'Disabled';
|
$lang['geoiplib_o_disabled'] = 'Disabled';
|
||||||
$lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module';
|
$lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module';
|
||||||
|
|||||||
Reference in New Issue
Block a user