Stats improvements

This commit is contained in:
Sascha Leib
2025-10-27 21:07:05 +01:00
parent 2c8aaf6cac
commit 6b6cd387da
15 changed files with 221 additions and 114 deletions

2
.gitignore vendored
View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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
View File

@@ -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;
}, },

View File

@@ -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 -->';
} }

View File

@@ -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);

View File

@@ -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';

View File

@@ -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');

View File

@@ -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": [],

View File

@@ -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"},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -6,9 +6,9 @@
*/ */
// Captcha dialog locale strings: // Captcha dialog locale strings:
$lang['bm_dlgTitle'] = 'Benutzerüberprüfung'; $lang['bm_dlgTitle'] = 'Benutzer&uuml;berpr&uuml;fung';
$lang['bm_dlgSubtitle'] = 'Bitte bestätige, dass du kein Bot bist:'; $lang['bm_dlgSubtitle'] = 'Bitte best&auml;tige, dass du kein Bot bist:';
$lang['bm_dlgConfirm'] = 'Klicke, um zu bestätigen.'; $lang['bm_dlgConfirm'] = 'Klicke, um zu best&auml;tigen.';
$lang['bm_dlgChecking'] = 'Wird überprüft&nbsp;&hellip;'; $lang['bm_dlgChecking'] = 'Wird &uuml;berpr&uuml;ft&nbsp;&hellip;';
$lang['bm_dlgLoading'] = 'Seite wird geladen&nbsp;&hellip;'; $lang['bm_dlgLoading'] = 'Seite wird geladen&nbsp;&hellip;';
$lang['bm_dlgError'] = 'Es ist ein Fehler aufgetreten.'; $lang['bm_dlgError'] = 'Es ist ein Fehler aufgetreten.';

View File

@@ -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';