diff --git a/cleanup.php b/cleanup.php
index 2f68919..ea21193 100644
--- a/cleanup.php
+++ b/cleanup.php
@@ -1,11 +1,12 @@
BotMon Cleanup Script
cogs
\ No newline at end of file
diff --git a/img/firefox.svg b/img/firefox.svg
deleted file mode 100644
index 4b14151..0000000
--- a/img/firefox.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/img/incognito.svg b/img/incognito.svg
deleted file mode 100644
index 52eb818..0000000
--- a/img/incognito.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/img/meta.svg b/img/meta.svg
new file mode 100644
index 0000000..83461fb
--- /dev/null
+++ b/img/meta.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/img/openai.svg b/img/openai.svg
new file mode 100644
index 0000000..89150e2
--- /dev/null
+++ b/img/openai.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/img/qwant.svg b/img/qwant.svg
index 1893307..2c4b9b6 100644
--- a/img/qwant.svg
+++ b/img/qwant.svg
@@ -1,53 +1 @@
-
-
+
\ No newline at end of file
diff --git a/img/safari.svg b/img/safari.svg
deleted file mode 100644
index 8489163..0000000
--- a/img/safari.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/img/scriptrun.svg b/img/scriptrun.svg
new file mode 100644
index 0000000..7a87cbb
--- /dev/null
+++ b/img/scriptrun.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/img/seznam.svg b/img/seznam.svg
new file mode 100644
index 0000000..a95f053
--- /dev/null
+++ b/img/seznam.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/img/winold.svg b/img/winold.svg
deleted file mode 100644
index afdb142..0000000
--- a/img/winold.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/img/yandex.svg b/img/yandex.svg
new file mode 100644
index 0000000..a39c923
--- /dev/null
+++ b/img/yandex.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/script.js b/script.js
index f83b6c8..4d1ab08 100644
--- a/script.js
+++ b/script.js
@@ -1,3 +1,4 @@
+"use strict";
/* DokuWiki BotMon Plugin Script file */
/* 03.09.2025 - 0.1.7 - pre-release */
/* Authors: Sascha Leib */
@@ -209,30 +210,41 @@ BotMon.live = {
},
// register a new visitor (or update if already exists)
- registerVisit: function(dat) {
+ registerVisit: function(dat, type) {
//console.info('registerVisit', dat);
// shortcut to make code more readable:
const model = BotMon.live.data.model;
-
+
// check if it already exists:
let visitor = model.findVisitor(dat.id);
if (!visitor) {
+
+ // is it a known bot?
const bot = BotMon.live.data.bots.match(dat.agent);
+ // override the visitor type?
+ let visitorType = dat.typ;
+ if (bot) visitorType = 'bot';
+
+ // which user id to use:
+ let visitorId = dat.id; // default is the session ID
+ if (bot) visitorId = bot.id; // use bot ID if known bot
+ if (dat.usr !== '') visitorId = 'usr'; // use user ID if known user
+
model._visitors.push(dat);
visitor = dat;
+ visitor.id = visitorId;
visitor._firstSeen = dat.ts;
visitor._lastSeen = dat.ts;
- visitor._isBot = ( bot ? 1.0 : 0.0 ); // likelihood of being a bot; primed to 0% or 100% in case of a known bot
+ visitor._seenBy = [type];
visitor._pageViews = []; // array of page views
visitor._hasReferrer = false; // has at least one referrer
visitor._jsClient = false; // visitor has been seen logged by client js as well
- visitor._client = bot ?? BotMon.live.data.clients.match(dat.agent) ?? null; // client info (browser, bot, etc.)
+ visitor._client = BotMon.live.data.clients.match(dat.agent) ?? null; // client info
+ visitor._bot = bot ?? null; // bot info
visitor._platform = BotMon.live.data.platforms.match(dat.agent); // platform info
-
- // known bots get the bot ID as identifier:
- if (bot) visitor.id = bot.id;
+ visitor._type = visitorType;
}
// find browser
@@ -271,20 +283,25 @@ BotMon.live = {
// shortcut to make code more readable:
const model = BotMon.live.data.model;
- let visitor = model.findVisitor(dat.id);
+ const type = 'log';
+
+ let visitor = BotMon.live.data.model.findVisitor(dat.id);
if (!visitor) {
- visitor = model.registerVisit(dat);
+ visitor = model.registerVisit(dat, type);
+ visitor._seenBy = [type];
}
if (visitor) {
+
+ // prime the "seen by" list:
+ seenBy = ( visitor._seenBy ? ( visitor._seenBy.includes(type) ? visitor._seenBy : [...visitor._seenBy, type] ) : [type] );
+
visitor._lastSeen = dat.ts;
+ visitor._seenBy = seenBy;
visitor._jsClient = true; // seen by client js
- } else {
- console.warn(`No visit with ID ${dat.id}.`);
- return;
}
// find the page view:
- let prereg = model._getVisit(visitor, dat);
+ let prereg = BotMon.live.data.model._getVisit(visitor, dat);
if (prereg) {
// update the page view:
prereg._lastSeen = dat.ts;
@@ -315,11 +332,12 @@ BotMon.live = {
let visitor = model.findVisitor(dat.id);
if (!visitor) {
console.warn(`No visitor with ID ${dat.id}, registering a new one.`);
- visitor = model.registerVisit(dat);
+ visitor = model.registerVisit(dat, 'tck');
}
if (visitor) {
- // update "last seen":
+ // update visitor:
if (visitor._lastSeen < dat.ts) visitor._lastSeen = dat.ts;
+ if (!visitor._seenBy.includes('tck')) visitor._seenBy.push('tck');
// get the page view info:
const pv = model._getVisit(visitor, dat);
@@ -382,6 +400,7 @@ BotMon.live = {
// shortcut to make code more readable:
const model = BotMon.live.data.model;
+ console.log(model._visitors);
// loop over all visitors:
model._visitors.forEach( (v) => {
@@ -391,17 +410,17 @@ BotMon.live = {
this.data.totalPageViews += v._pageViews.length;
// check for typical bot aspects:
- let botScore = v._isBot; // start with the known bot score
+ let botScore = 0;
- if (v._isBot >= 1.0) { // known bots
+ /*if (v._isBot >= 1.0) { // known bots
this.data.bots.known += 1;
this.groups.knownBots.push(v);
- } if (v.usr && v.usr != '') { // known users
+ } if (v.usr && v.usr != '') { // known users */
this.groups.users.push(v);
this.data.bots.users += 1;
- } else {
+ /*} else {
// not a known bot, nor a known user; check other aspects:
// no referrer at all:
@@ -431,7 +450,7 @@ BotMon.live = {
this.data.bots.human += 1;
this.groups.humans.push(v);
}
- }
+ }*/
});
console.log(this.data);
@@ -454,10 +473,9 @@ BotMon.live = {
throw new Error(`${response.status} ${response.statusText}`);
}
- BotMon.live.data.bots._list = await response.json();
- BotMon.live.data.bots._ready = true;
+ this._list = await response.json();
+ this._ready = true;
- // TODO: allow using the bots list...
} catch (error) {
BotMon.live.gui.status.setError("Error while loading the ‘known bots’ file: " + error.message);
} finally {
@@ -467,22 +485,40 @@ BotMon.live = {
},
// returns bot info if the clientId matches a known bot, null otherwise:
- match: function(client) {
- //console.info('BotMon.live.data.bots.match(',client,')');
+ match: function(agent) {
+ //console.info('BotMon.live.data.bots.match(',agent,')');
- if (client) {
- for (let i=0; i {
+ let r = false;
for (let j=0; j 1 ? rxr[1] : -1)
+ }
+ r = true;
+ break;
}
}
- return null; // not found!
- }
+ return r;
+ });
}
+
+ //console.log("botInfo:", botInfo);
+ return botInfo;
},
+
// indicates if the list is loaded and ready to use:
_ready: false,
@@ -516,16 +552,16 @@ BotMon.live = {
},
// returns bot info if the user-agent matches a known bot, null otherwise:
- match: function(cid) {
- //console.info('BotMon.live.data.clients.match(',cid,')');
+ match: function(agent) {
+ //console.info('BotMon.live.data.clients.match(',agent,')');
let match = {"n": "Unknown", "v": -1, "id": null};
- if (cid) {
+ if (agent) {
BotMon.live.data.clients._list.find(client => {
let r = false;
for (let j=0; j 1 ? rxr[1] : -1);
@@ -538,6 +574,7 @@ BotMon.live = {
});
}
+ //console.log(match)
return match;
},
@@ -609,7 +646,7 @@ BotMon.live = {
},
loadLogFile: async function(type, onLoaded = undefined) {
- // console.info('BotMon.live.data.loadLogFile(',type,')');
+ console.info('BotMon.live.data.loadLogFile(',type,')');
let typeName = '';
let columns = [];
@@ -656,19 +693,21 @@ BotMon.live = {
const data = {};
cols.forEach( (colVal,i) => {
colName = columns[i] || `col${i}`;
- const colValue = (colName == 'ts' ? new Date(colVal) : colVal);
+ const colValue = (colName == 'ts' ? new Date(colVal) : colVal.trim());
data[colName] = colValue;
});
// register the visit in the model:
switch(type) {
case 'srv':
- BotMon.live.data.model.registerVisit(data);
+ BotMon.live.data.model.registerVisit(data, type);
break;
case 'log':
+ data.typ = 'js';
BotMon.live.data.model.updateVisit(data);
break;
case 'tck':
+ data.typ = 'js';
BotMon.live.data.model.updateTicks(data);
break;
default:
@@ -856,6 +895,8 @@ BotMon.live = {
// shortcut for neater code:
const make = BotMon.t._makeElement;
+ let ipType = ( data.ip.indexOf(':') >= 0 ? '6' : '4' );
+
const li = make('li'); // root list item
const details = make('details');
const summary = make('summary');
@@ -863,15 +904,29 @@ BotMon.live = {
const span1 = make('span'); /* left-hand group */
- let typeDescr = "Seen by: " + ( data.typ == 'php' ? "Server-only": "Server + Client");
- span1.appendChild(make('span', { /* Type */
- 'class': 'icon type type_' + data.typ,
- 'title': typeDescr
- }, data.typ));
+ if (data._type == 'bot') { /* Bot only */
- span1.appendChild(make('span', { /* ID */
- 'class': 'id'
- }, data.id));
+ span1.appendChild(make('span', { /* Bot */
+ 'class': 'bot bot_' + (data._bot ? data._bot.id : 'unknown'),
+ 'title': "Bot: " + (data._bot ? data._bot.n : 'Unknown')
+ }, (data._bot ? data._bot.n : 'Unknown')));
+
+ } else if (data._type == 'usr') { /* User only */
+
+ span1.appendChild(make('span', { /* User */
+ 'class': 'user' + (data._user ? data._user.id : 'unknown'),
+ 'title': "User: " + data.usr
+ }, data.usr));
+
+ } else { /* others */
+
+ if (data.ip == '127.0.0.1' || data.ip == '::1' ) ipType = '0';
+ span1.appendChild(make('span', { /* IP-Address */
+ 'class': 'ipaddr ip' + ipType,
+ 'title': "IP-Address: " + data.ip
+ }, data.ip));
+
+ }
const platformName = (data._platform ? data._platform.n : 'Unknown');
span1.appendChild(make('span', { /* Platform */
@@ -885,12 +940,7 @@ BotMon.live = {
'title': "Client: " + clientName
}, clientName));
- let ipType = ( data.ip.indexOf(':') >= 0 ? '6' : '4' );
- if (data.ip == '127.0.0.1' || data.ip == '::1' ) ipType = '0';
- span1.appendChild(make('span', { /* IP-Address */
- 'class': 'icon ipaddr ip' + ipType,
- 'title': "IP-Address: " + data.ip
- }, data.ip));
+
summary.appendChild(span1);
const span2 = make('span'); /* right-hand group */
@@ -906,6 +956,12 @@ BotMon.live = {
const dl = make('dl', {'class': 'visitor_details'});
+ if (data._bot) {
+ dl.appendChild(make('dt', {}, "Bot:")); /* bot info */
+ dl.appendChild(make('dd', {'class': 'has_icon bot bot_' + (data._bot ? data._bot.id : 'unknown')},
+ (data._bot ? data._bot.n : 'Unknown')));
+ }
+
dl.appendChild(make('dt', {}, "Client:")); /* client */
dl.appendChild(make('dd', {'class': 'has_icon client_' + (data._client ? data._client.id : 'unknown')},
clientName + ( data._client.v > 0 ? ' (' + data._client.v + ')' : '' ) ));
diff --git a/style.less b/style.less
index a2a6331..85e73e5 100644
--- a/style.less
+++ b/style.less
@@ -95,7 +95,7 @@
}
}
&[open] {
- summary::before {
+ & > summary::before {
transform: rotate(0deg);
}
}
@@ -193,43 +193,52 @@
background-size: 1em;
}
- /* type icons */
- span.type_dw::before, dd.type_dw::before { background-image: url('img/incognito.svg'); }
- span.type_php::before, dd.type_php::before { background-image: url('img/cogs.svg'); }
+ /* bot icons */
+ span.bot::before, dd.bot::before { background-image: url('img/robot.svg') }
+ span.bot_bingbot::before, dd.bot_bingbot::before { background-image: url('img/bing.svg') }
+ span.bot_googlebot::before, dd.bot_googlebot::before,
+ span.bot_googleads::before, dd.bot_googleads::before,
+ span.bot_googleapi::before, dd.bot_googleapi::before { background-image: url('img/google.svg') }
+ span.bot_applebot::before, dd.bot_applebot::before { background-image: url('img/apple.svg') }
+ span.bot_metabots::before, dd.bot_metabots::before { background-image: url('img/meta.svg') }
+ span.bot_yandexbots::before, dd.bot_yandexbots::before { background-image: url('img/yandex.svg') }
+ span.bot_seznambot::before, dd.bot_seznambot::before { background-image: url('img/seznam.svg') }
+
+ /* user info */
+ span.user::before { background-image: url('img/user.svg') }
/* platform icons */
- span.platform_macos::before, dd.platform_macos::before { background-image: url('img/macos.svg'); }
- span.platform_win10::before, dd.platform_win10::before { background-image: url('img/win11.svg'); }
- span.platform_linux::before, dd.platform_linux::before { background-image: url('img/linux.svg'); }
- span.platform_ios::before, dd.platform_ios::before { background-image: url('img/ios.svg'); }
- span.platform_android::before, dd.platform_android::before { background-image: url('img/android.svg'); }
- span.platform_winold::before, dd.platform_winold::before { background-image: url('img/winold.svg'); }
- span.platform_tizen::before, dd.platform_tizen::before { background-image: url('img/tizen.png'); }
- span.platform_hmos::before, dd.platform_hmos::before { background-image: url('img/hmos.svg'); }
- span.platform_chromium::before, dd.platform_chromium::before { background-image: url('img/chromium.svg'); }
-
+ span.platform_macos::before, dd.platform_macos::before { background-image: url('img/apple.svg') }
+ span.platform_win10::before, dd.platform_win10::before { background-image: url('img/win11.svg') }
+ span.platform_linux::before, dd.platform_linux::before { background-image: url('img/linux.svg') }
+ span.platform_ios::before, dd.platform_ios::before { background-image: url('img/ios.svg') }
+ span.platform_android::before, dd.platform_android::before { background-image: url('img/android.svg') }
+ span.platform_winold::before, dd.platform_winold::before { background-image: url('img/winold.png') }
+ span.platform_tizen::before, dd.platform_tizen::before { background-image: url('img/tizen.png') }
+ span.platform_hmos::before, dd.platform_hmos::before { background-image: url('img/hmos.svg') }
+ span.platform_chromium::before, dd.platform_chromium::before { background-image: url('img/chromium.svg') }
/* browser icons */
- span.client_opera::before, dd.client_opera::before { background-image: url('img/opera.svg'); }
- span.client_msie::before, dd.client_msie::before { background-image: url('img/msie.svg'); }
- span.client_brave::before, dd.client_brave::before { background-image: url('img/brave.svg'); }
- span.client_msedge::before, dd.client_msedge::before { background-image: url('img/msedge.svg'); }
- span.client_chrome::before, dd.client_chrome::before { background-image: url('img/chrome.svg'); }
- span.client_chromeold::before, dd.client_chromeold::before { background-image: url('img/chromeold.svg'); }
- span.client_safari::before, dd.client_safari::before { background-image: url('img/safari.svg'); }
- span.client_ddg::before, dd.client_ddg::before { background-image: url('img/ddg.svg'); }
- span.client_firefox::before, dd.client_firefox::before { background-image: url('img/firefox.svg'); }
- span.client_samsung::before, dd.client_samsung::before { background-image: url('img/samsung.svg'); }
- span.client_uc::before, dd.client_uc::before { background-image: url('img/uc.svg'); }
- span.client_huawei::before, dd.client_huawei::before { background-image: url('img/huawei.png'); }
+ span.client_opera::before, dd.client_opera::before { background-image: url('img/opera.svg') }
+ span.client_msie::before, dd.client_msie::before { background-image: url('img/msie.svg') }
+ span.client_brave::before, dd.client_brave::before { background-image: url('img/brave.svg') }
+ span.client_msedge::before, dd.client_msedge::before { background-image: url('img/msedge.svg') }
+ span.client_chrome::before, dd.client_chrome::before { background-image: url('img/chrome.svg') }
+ span.client_chromeold::before, dd.client_chromeold::before { background-image: url('img/chromeold.svg') }
+ span.client_safari::before, dd.client_safari::before { background-image: url('img/safari.png') }
+ span.client_ddg::before, dd.client_ddg::before { background-image: url('img/ddg.svg') }
+ span.client_firefox::before, dd.client_firefox::before { background-image: url('img/firefox.png') }
+ span.client_samsung::before, dd.client_samsung::before { background-image: url('img/samsung.svg') }
+ span.client_uc::before, dd.client_uc::before { background-image: url('img/uc.svg') }
+ span.client_huawei::before, dd.client_huawei::before { background-image: url('img/huawei.png') }
/* ip address type */
- span.ip6::before, dd.ip6::before { background-image: url('img/ip6.svg'); }
- span.ip4::before, dd.ip4::before { background-image: url('img/ip4.svg'); }
- span.ip0::before, dd.ip0::before { background-image: url('img/localhost.svg'); }
+ span.ip6::before, dd.ip6::before { background-image: url('img/ip6.svg') }
+ span.ip4::before, dd.ip4::before { background-image: url('img/ip4.svg') }
+ span.ip0::before, dd.ip0::before { background-image: url('img/localhost.svg') }
/* user agent */
- span.agent::before { background-image: url('img/info.svg'); }
+ span.agent::before { background-image: url('img/info.svg') }
}
@@ -305,6 +314,13 @@
}
}
+ /* visitor lists: */
+ #botmon__today__visitorlists {
+ details ul > li {
+ color: #aeaeae;
+ }
+ }
+
footer {
& {
background-color: #0c0c0d;