UI improvements

And also style changes
This commit is contained in:
Sascha Leib
2025-10-25 15:58:32 +02:00
parent 2c64126222
commit d49ab21345
11 changed files with 261 additions and 129 deletions

View File

@@ -23,17 +23,23 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
global $ACT;
// initialize the session id and type with random data:
$this->sessionId = rand(1000000, 9999999);
$this->sessionType = 'rnd';
// insert header data into the page:
if ($ACT == 'show') {
if ($ACT == 'show' || $ACT == 'edit' || $ACT == 'media') {
$controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertHeader');
// Override the page rendering, if a captcha needs to be displayed:
$controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'showCaptcha');
} else if ($ACT == 'admin' && isset($_REQUEST['page']) && $_REQUEST['page'] == 'botmon') {
$controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertAdminHeader');
}
// Override the page rendering, if a captcha needs to be displayed:
if ($ACT !== 'admin') {
$controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'showCaptcha');
}
}
// also show a captcha before the image preview
$controller->register_hook('TPL_IMG_DISPLAY', 'BEFORE', $this, 'showImageCaptcha');
// write to the log after the page content was displayed:
$controller->register_hook('TPL_CONTENT_DISPLAY', 'AFTER', $this, 'writeServerLog');
@@ -59,14 +65,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
// populate the session id and type:
$this->getSessionInfo();
// is there a user logged in?
$username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) ? $INFO['userinfo']['name'] : '');
// build the tracker code:
$code = "document._botmon = {t0: Date.now(), session: " . json_encode($this->sessionId) . ", seed: " . json_encode($this->getConf('captchaSeed')) . ", ip: " . json_encode($_SERVER['REMOTE_ADDR']) . "};" . NL;
if ($username) {
$code .= DOKU_TAB . DOKU_TAB . 'document._botmon.user = "' . $username . '";'. NL;
}
$code = $this->getBMHeader();
// add the deferred script loader::
$code .= DOKU_TAB . DOKU_TAB . "addEventListener('DOMContentLoaded', function(){" . NL;
@@ -78,6 +78,22 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
$event->data['script'][] = ['_data' => $code];
}
/* create the BM object code for insertion into a script element: */
private function getBMHeader() {
// build the tracker code:
$code = DOKU_TAB . DOKU_TAB . "document._botmon = {t0: Date.now(), session: " . json_encode($this->sessionId) . ", seed: " . json_encode($this->getConf('captchaSeed')) . ", ip: " . json_encode($_SERVER['REMOTE_ADDR']) . "};" . NL;
// is there a user logged in?
$username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) ? $INFO['userinfo']['name'] : '');
if ($username) {
$code .= DOKU_TAB . DOKU_TAB . 'document._botmon.user = "' . $username . '";'. NL;
}
return $code;
}
/**
* Inserts tracking code to the page header
* (only called on 'show' actions)
@@ -120,7 +136,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
substr($conf['lang'],0,2), /* page language */
implode(',', array_unique(array_map( function($it) { return substr(trim($it),0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */
$this->getCountryCode(), /* GeoIP country code */
$this->showCaptcha /* show captcha? */ );
$this->showCaptcha /* show captcha? */
);
//* create the log line */
$filename = __DIR__ .'/logs/' . gmdate('Y-m-d') . '.srv.txt'; /* use GMT date for filename */
@@ -182,37 +199,68 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
$this->sessionId = $_SERVER['REMOTE_ADDR'];
$this->sessionType = 'ip';
}
if (!$this->sessionId) { /* if everything else fails, just us a random ID */
$this->sessionId = rand(1000000, 9999999);
$this->sessionType = 'rand';
}
}
public function showCaptcha(Event $event) {
$useCaptcha = $this->getConf('useCaptcha');
if ($useCaptcha !== 'disabled' && $this->checkCaptchaCookie() && !$this->captchaWhitelisted()) {
$cCode = '-';
if ($useCaptcha !== 'disabled') {
if ($this->captchaWhitelisted()) {
$cCode = 'W'; // whitelisted
} elseif ($this->hasCaptchaCookie()) {
$cCode = 'N'; // user already has a cookie
} else {
$cCode = 'Y'; // show the captcha
$this->showCaptcha = 'Y'; // captcha will be shown.
echo '<h1 class="sectionedit1">'; tpl_pagetitle(); echo "</h1>\n"; // always show the original page title
$event->preventDefault(); // don't show normal content
switch ($useCaptcha) {
case 'blank':
$this->insertBlankBox(); // show dada filler instead of text
break;
case 'dada':
$this->insertDadaFiller(); // show dada filler instead of text
break;
echo '<h1 class="sectionedit1">'; tpl_pagetitle(); echo "</h1>\n"; // always show the original page title
$event->preventDefault(); // don't show normal content
switch ($useCaptcha) {
case 'blank':
$this->insertBlankBox(); // show dada filler instead of text
break;
case 'dada':
$this->insertDadaFiller(); // show dada filler instead of text
break;
}
$this->insertCaptchaLoader(); // and load the captcha
}
$this->insertCaptchaLoader(); // and load the captcha
} else {
$this->showCaptcha = 'N'; // do not show a captcha
}
$this->showCaptcha = $cCode; // store the captcha code for the logfile
}
private function checkCaptchaCookie() {
public function showImageCaptcha(Event $event, $param) {
$useCaptcha = $this->getConf('useCaptcha');
echo '<script>' . $this->getBMHeader($event, $param) . '</script>';
$cCode = '-';
if ($useCaptcha !== 'disabled') {
if ($this->captchaWhitelisted()) {
$cCode = 'W'; // whitelisted
}
elseif ($this->hasCaptchaCookie()) {
$cCode = 'N'; // user already has a cookie
}
else {
$cCode = 'Y'; // show the captcha
echo '<svg width="100%" height="100%" viewBox="0 0 800 400" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M1,1l798,398" style="fill:none;stroke:#f00;stroke-width:1px;"/><path d="M1,399l798,-398" style="fill:none;stroke:#f00;stroke-width:1px;"/><rect x="1" y="1" width="798" height="398" style="fill:none;stroke:#000;stroke-width:1px;"/></svg>'; // placeholder image
$event->preventDefault(); // don't show normal content
// TODO Insert dummy image
$this->insertCaptchaLoader(); // and load the captcha
}
};
$this->showCaptcha = $cCode; // store the captcha code for the logfile
}
private function hasCaptchaCookie() {
$cookieVal = isset($_COOKIE['DWConfirm']) ? $_COOKIE['DWConfirm'] : null;
@@ -223,7 +271,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
//echo '<ul><li>cookie: ' . $cookieVal . '</li><li>expected: ' . $expected . '</li><li>matches: ' .($cookieVal == $expected ? 'true' : 'false') . '</li></ul>';
return $cookieVal !== $expected;
return $cookieVal == $expected;
}
// check if the visitor's IP is on a whitelist:
@@ -251,15 +299,13 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
$to = inet_pton($col[1]);
if ($ip >= $from && $ip <= $to) {
//echo "<p>Found my IP in range: " . $col[0] . " - " . $col[1] . "</p>";
return true;
return true; /* IP whitelisted */
}
}
}
}
}
return false;
return false; /* IP not found in whitelist */
}
private function insertCaptchaLoader() {

View File

@@ -108,6 +108,12 @@
&.cl_operaold::before { background-position-y: -380px }
&.cl_other::before { background-image: url('img/more.svg') }
/* Captcha statuses */
&.captcha::before { background-image: url('img/captcha.png') }
&.cap_Y::before { background-position-y: -20px }
&.cap_YN::before { background-position-y: -40px }
&.cap_W::before { background-position-y: -60px }
/* Country flags */
/* Note: flag images and CSS adapted from: https://github.com/lafeber/world-flags-sprite/ */
&.country::before {
@@ -730,21 +736,28 @@
background-image: url('img/info.svg')
}
/* pageviews */
span.pageviews {
/* pages seen */
span.pageseen, span.pageviews {
border: #999 solid 1px;
padding: 0 2px;
font-size: smaller;
border-radius: .5em;
margin-right: .25em;
}
span.pageviews::before {
span.pageseen::before {
content : '';
display: inline-block;
width: 1.25em; height: 1.25em;
background: transparent url('img/page.svg') center no-repeat;
background-size: 1.25em;
}
span.pageviews::before {
content : '';
display: inline-block;
width: 1.25em; height: 1.25em;
background: transparent url('img/views.svg') center no-repeat;
background-size: 1.25em;
}
}
/* item footer */
@@ -890,7 +903,7 @@
border-top-color: #CCC;
}
}
span.pageviews {
span.pageseen, span.pageviews {
border-color: #555;
}

162
admin.js
View File

@@ -291,7 +291,7 @@ BotMon.live = {
// shortcut to make code more readable:
const model = BotMon.live.data.model;
const timeout = 60 * 60 * 1000; // session timeout: One hour
//const timeout = 60 * 60 * 1000; // session timeout: One hour
if (visitor._type == BM_USERTYPE.KNOWN_BOT) { // known bots match by their bot ID:
@@ -303,13 +303,14 @@ BotMon.live = {
return v;
}
}
} else { // other types match by their DW/PHPIDs:
// loop over all visitors already registered and check for ID matches:
for (let i=0; i<model._visitors.length; i++) {
const v = model._visitors[i];
if ( v.id == visitor.id) { // match the DW/PHP IDs
if ( v.id.trim() !== '' && v.id == visitor.id) { // match the DW/PHP IDs
return v;
}
}
@@ -379,21 +380,35 @@ BotMon.live = {
// check if it already exists:
let visitor = model.findVisitor(nv, type);
if (!visitor) {
visitor = nv;
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 = BotMon.live.data.clients.match(nv.agent) ?? null; // client info
visitor._platform = BotMon.live.data.platforms.match(nv.agent); // platform info
visitor = {...nv, ...{
_seenBy: [type],
_viewCount: 0, // number of page views
_loadCount: 0, // number of page loads (not necessarily views!)
_pageViews: [], // array of page views
_hasReferrer: false, // has at least one referrer
_jsClient: false, // visitor has been seen logged by client js as well
_client: BotMon.live.data.clients.match(nv.agent) ?? null, // client info
_platform: BotMon.live.data.platforms.match(nv.agent), // platform info
_captcha: {'-': 0, 'Y': 0, 'N': 0, 'W':0} // captcha counter
}};
model._visitors.push(visitor);
} else { // update existing
if (visitor._firstSeen > nv.ts) {
visitor._firstSeen = nv.ts;
}
};
// update first and last seen:
if (visitor._firstSeen > nv.ts) {
visitor._firstSeen = nv.ts;
}
if (visitor._lastSeen < nv.ts) {
visitor._lastSeen = nv.ts;
}
// find browser
// update total loads and views (not the same!):
visitor._loadCount += 1;
visitor._viewCount += (nv.captcha == 'Y' ? 0 : 1);
// ...because also a captcha is a "load", but not a "view".
// let's count the captcha statuses as well:
if (nv.captcha) visitor._captcha[nv.captcha] += 1;
// is this visit already registered?
let prereg = model._getPageView(visitor, nv);
@@ -401,14 +416,15 @@ BotMon.live = {
// add new page view:
prereg = model._makePageView(nv, type);
visitor._pageViews.push(prereg);
} else {
// update last seen date
prereg._lastSeen = nv.ts;
// increase view count:
prereg._viewCount += 1;
prereg._tickCount += 1;
}
// update last seen date
prereg._lastSeen = nv.ts;
// increase view count:
prereg._loadCount += (visitor.captcha == 'Y' ? 0 : 1);
//prereg._tickCount += 1;
// update referrer state:
visitor._hasReferrer = visitor._hasReferrer ||
(prereg.ref !== undefined && prereg.ref !== '');
@@ -520,7 +536,8 @@ BotMon.live = {
_lastSeen: data.ts,
_seenBy: [type],
_jsClient: ( type !== BM_LOGTYPE.SERVER),
_viewCount: 1,
_viewCount: 0,
_loadCount: 0,
_tickCount: 0
};
}
@@ -572,19 +589,19 @@ BotMon.live = {
// count visits and page views:
this.data.totalVisits += 1;
this.data.totalPageViews += v._pageViews.length;
this.data.totalPageViews += v._viewCount;
// check for typical bot aspects:
let botScore = 0;
if (v._type == BM_USERTYPE.KNOWN_BOT) { // known bots
this.data.bots.known += v._pageViews.length;
this.data.bots.known += v._viewCount;
this.groups.knownBots.push(v);
} else if (v._type == BM_USERTYPE.KNOWN_USER) { // known users */
this.data.bots.users += v._pageViews.length;
this.data.bots.users += v._viewCount;
this.groups.users.push(v);
} else {
@@ -596,11 +613,11 @@ BotMon.live = {
if (e.isBot) { // likely bots
v._type = BM_USERTYPE.LIKELY_BOT;
this.data.bots.suspected += v._pageViews.length;
this.data.bots.suspected += v._viewCount;
this.groups.suspectedBots.push(v);
} else { // probably humans
v._type = BM_USERTYPE.PROBABLY_HUMAN;
this.data.bots.human += v._pageViews.length;
this.data.bots.human += v._viewCount;
this.groups.humans.push(v);
}
}
@@ -640,7 +657,7 @@ BotMon.live = {
//console.log(BotMon.live.data.analytics.groups.knownBots);
let botsList = BotMon.live.data.analytics.groups.knownBots.toSorted( (a, b) => {
return b._pageViews.length - a._pageViews.length;
return b._viewCount - a._viewCount;
});
const other = {
@@ -659,12 +676,12 @@ BotMon.live = {
rList.push({
id: it._bot.id,
name: (it._bot.n ? it._bot.n : it._bot.id),
count: it._pageViews.length
count: it._viewCount
});
} else {
other.count += it._pageViews.length;
};
total += it._pageViews.length;
total += it._viewCount;
}
};
@@ -997,7 +1014,7 @@ BotMon.live = {
const list = me.groups[type];
list.forEach(it => {
bounces += (it._pageViews.length <= 1 ? 1 : 0);
bounces += (it._viewCount <= 1 ? 1 : 0);
});
return bounces;
@@ -1085,7 +1102,7 @@ BotMon.live = {
}
// add to counter:
ipRec.count += v._pageViews.length;
ipRec.count += v._viewCount;
},
@@ -1520,12 +1537,13 @@ BotMon.live = {
// are there at lest num pages loaded?
smallPageCount: function(visitor, num) {
return (visitor._pageViews.length <= Number(num));
return (visitor._viewCount <= Number(num));
},
// There was no entry in a specific log file for this visitor:
// note that this will also trigger the "noJavaScript" rule:
noRecord: function(visitor, type) {
if (!visitor._seenBy.includes('srv')) return false; // only if 'srv' is also specified!
return !visitor._seenBy.includes(type);
},
@@ -1613,7 +1631,7 @@ BotMon.live = {
// At least x page views were recorded, but they come within less than y seconds
loadSpeed: function(visitor, minItems, maxTime) {
if (visitor._pageViews.length >= minItems) {
if (visitor._viewCount >= minItems) {
//console.log('loadSpeed', visitor._pageViews.length, minItems, maxTime);
const pvArr = visitor._pageViews.map(pv => pv._lastSeen).sort();
@@ -1704,7 +1722,7 @@ BotMon.live = {
switch (type) {
case "srv":
typeName = "Server";
columns = ['ts','ip','pg','id','typ','usr','agent','ref','lang','accept','geo'];
columns = ['ts','ip','pg','id','typ','usr','agent','ref','lang','accept','geo','captcha'];
break;
case "log":
typeName = "Page load";
@@ -2299,20 +2317,11 @@ BotMon.live = {
}, data.id));
}
// seen by icon:
span1.appendChild(make('span', {
'class': 'icon_only seenby sb_' + data._seenBy.join(''),
'title': "Seen by: " + data._seenBy.join('+')
}, data._seenBy.join(', ')));
span1.appendChild(make('span', { /* page views */
'class': 'has_icon pageseen',
'title': data._pageViews.length + " page load(s)"
}, data._pageViews.length));
// country flag:
if (data.geo && data.geo !== 'ZZ') {
span1.appendChild(make('span', {
'class': 'icon_only country ctry_' + data.geo.toLowerCase(),
'data-ctry': data.geo,
'title': "Country: " + ( data._country || "Unknown")
}, ( data._country || "Unknown") ));
}
// referer icons:
if ((data._type == BM_USERTYPE.PROBABLY_HUMAN || data._type == BM_USERTYPE.LIKELY_BOT) && data.ref) {
@@ -2326,15 +2335,26 @@ BotMon.live = {
summary.appendChild(span1);
const span2 = make('span'); /* right-hand group */
span2.appendChild(make('span', { /* first-seen */
'class': 'has_iconfirst-seen',
'title': "First seen: " + data._firstSeen.toLocaleString() + " UTC"
}, BotMon.t._formatTime(data._firstSeen)));
// country flag:
if (data.geo && data.geo !== 'ZZ') {
span2.appendChild(make('span', {
'class': 'icon_only country ctry_' + data.geo.toLowerCase(),
'data-ctry': data.geo,
'title': "Country: " + ( data._country || "Unknown")
}, ( data._country || "Unknown") ));
}
span2.appendChild(make('span', { /* page views */
'class': 'has_icon pageviews',
'title': data._pageViews.length + " page view(s)"
}, data._pageViews.length));
span2.appendChild(make('span', { // seen-by icon:
'class': 'icon_only seenby sb_' + data._seenBy.join(''),
'title': "Seen by: " + data._seenBy.join('+')
}, data._seenBy.join(', ')));
const captchaCode = '' + ( data._captcha['Y'] > 0 ? 'Y' : '' ) + ( data._captcha['N'] > 0 ? 'N' : '' ) + ( data._captcha['W'] > 0 ? 'W' : '' );
if (captchaCode !== '') {
span2.appendChild(make('span', { // captcha status
'class': 'icon_only captcha cap_' + captchaCode,
}, captchaCode));
}
summary.appendChild(span2);
@@ -2414,6 +2434,13 @@ BotMon.live = {
dl.appendChild(make('dd', {'class': 'lastSeen'}, data._lastSeen.toLocaleString()));
}
dl.appendChild(make('dt', {}, "Actions:"));
dl.appendChild(make('dd', {'class': 'views'},
"Page loads: " + data._loadCount.toString() +
( data._captcha['Y'] > 0 || data._captcha['W'] || data._captcha['-'] > 0 ? ", captchas: " + (data._captcha['Y']+data._captcha['W']+data._captcha['-']).toString() : '') +
", views: " + data._viewCount.toString()
));
dl.appendChild(make('dt', {}, "User-Agent:"));
dl.appendChild(make('dd', {'class': 'agent'}, data.agent));
@@ -2441,6 +2468,12 @@ BotMon.live = {
'title': "Country: " + data._country
}, data._country + ' (' + data.geo + ')'));
}
if (data.captcha && data.captcha !=='') {
dl.appendChild(make('dt', {}, "Captcha-status:"));
dl.appendChild(make('dd', {
'class': 'captcha'
}, data.captcha));
}
dl.appendChild(make('dt', {}, "Session ID:"));
dl.appendChild(make('dd', {'class': 'has_icon session typ_' + data.typ}, data.id));
@@ -2528,12 +2561,19 @@ BotMon.live = {
'title': "PageID: " + page.pg
}, page.pg)); /* DW Page ID */
// get the time difference:
row1.appendChild(make('span', {
'class': 'first-seen',
'title': "First visited: " + page._firstSeen.toLocaleString() + " UTC"
}, BotMon.t._formatTime(page._firstSeen)));
const rightGroup = row1.appendChild(make('div')); // right-hand group
// get the time difference:
rightGroup.appendChild(make('span', {
'class': 'first-seen',
'title': "First visited: " + page._firstSeen.toLocaleString() + " UTC"
}, BotMon.t._formatTime(page._firstSeen)));
rightGroup.appendChild(make('span', { /* page loads */
'class': 'has_icon pageviews',
'title': page._viewCount.toString() + " page load(s)"
}, page._viewCount.toString()));
pgLi.appendChild(row1);
/* LINE 2 */

View File

@@ -26,7 +26,7 @@ const $BMCaptcha = {
// Checkbox:
const lbl = document.createElement('label');
lbl.innerHTML = '<span class="confirm">Click to confirm.</span><span class="busy"></span><span class="checking">Checking&nbsp;&hellip;</span><span class="loading">Loading&nbsp;&hellip;</span>';
lbl.innerHTML = '<span class="confirm">Click to confirm.</span><span class="busy"></span><span class="checking">Checking&nbsp;&hellip;</span><span class="loading">Loading&nbsp;&hellip;</span><span class="erricon">&#65533;</span><span class="error">An error occured.</span>';
const cb = document.createElement('input');
cb.setAttribute('type', 'checkbox');
cb.setAttribute('disabled', 'disabled');
@@ -152,26 +152,35 @@ const $BMCaptcha = {
if (e.target.checked) {
//document.getElementById('botmon_captcha_box').close();
const dat = [ // the data to encode
document._botmon.seed || '',
location.hostname,
document._botmon.ip || '0.0.0.0',
(document._botmon.t0 ? new Date(document._botmon.t0) : new Date()).toISOString().substring(0, 10)
];
const hash = $BMCaptcha.digest.hash(dat.join('|'));
try {
var $status = 'loading';
// set the cookie:
document.cookie = "DWConfirm=" + hash + ';path=/;';
// generate the hash:
const dat = [ // the data to encode
document._botmon.seed || '',
location.hostname,
document._botmon.ip || '0.0.0.0',
(new Date()).toISOString().substring(0, 10)
];
const hash = $BMCaptcha.digest.hash(dat.join('|'));
// set the cookie:
document.cookie = "DWConfirm=" + hash + ';path=/;';
} catch (err) {
console.error(err);
$status = 'error';
}
// change the interface:
const dlg = document.getElementById('botmon_captcha_box');
if (dlg) {
dlg.classList.remove('ready');
dlg.classList.add('loading');
dlg.classList.add( $status );
}
// reload the page:
window.location.reload(true);
if ($status !== 'error')window.location.reload(true);
}
},

View File

@@ -6,5 +6,5 @@
*/
$conf['geoiplib'] = 'disabled';
$conf['useCaptcha'] = 0;
$conf['captchaSeed'] = 'b472719ba5634d378a7d7f9bfc46659f';
$conf['useCaptcha'] = 'disabled';
$conf['captchaSeed'] = 'c53bc5f94929451987efa6c768d8856b';

View File

@@ -335,6 +335,12 @@
2001:4860:4801:000c:: 2001:4860:4801:000c:FFFF:FFFF:FFFF:FFFF 64
2001:4860:4801:000f:: 2001:4860:4801:000f:FFFF:FFFF:FFFF:FFFF 64
# PlagAware Bot - from previous visits IPs
157.90.90.163 157.90.90.163 32
2a01:4f8:2190:21a0::2 2a01:4f8:2190:21a0::2 64
# SeznamBot - IPs from: https://o-seznam.cz/napoveda/vyhledavani/en/seznambot-crawler/
77.75.76.0 77.75.79.255 22
2a02:0598:0064:8a00:0000:0000:3100:0000 2a02:0598:0064:8a00:0000:0000:3100:001f 123
2a02:0598:0128:8a00:0000:0000:0b00:0000 2a02:0598:0128:8a00:0000:0000:0b00:001f 123
2a02:0598:0096:8a00:0000:0000:1200:0120 2a02:0598:0096:8a00:0000:0000:1200:013f 123
# localhosts
127.0.0.1 127.255.255.255 8
::1 ::1 128

View File

@@ -1,10 +1,12 @@
{
"groups": [
{"id": "alibaba", "name": "Alibaba"},
{"id": "amazon", "name": "Amazon DS"},
{"id": "amazon", "name": "Amazon"},
{"id": "bezeq", "name": "Bezeq Int."},
{"id": "brasilnet", "name": "BrasilNet"},
{"id": "charter", "name": "Charter Inc."},
{"id": "chinanet", "name": "Chinanet"},
{"id": "cloudflare", "name": "Cloudflare Inc."},
{"id": "cnisp", "name": "China ISP"},
{"id": "cnmob", "name": "China Mobile"},
{"id": "google", "name": "Google LLC"},
@@ -44,7 +46,7 @@
{"from": "52.96.0.0", "to": "52.127.255.254", "m": 11, "g": "microsoft"},
{"from": "54.0.0.0", "to": "54.255.255.254", "m": 8, "g": "amazon"},
{"from": "66.249.64.0", "to": "66.249.95.254", "m": 19, "g": "google"},
{"from": "84.37.35.0", "to": "84.37.255.254", "g": "gtt"},
{"from": "82.80.0.0", "to": "82.81.255.255", "m": 15, "g": "bezeq"},
{"from": "94.74.64.0", "to": "94.74.127.254", "m": 18, "g": "huawei"},
{"from": "91.84.96.0", "to": "91.84.127.254", "m": 19, "g": "vdsina"},
{"from": "101.0.0.0", "to": "101.255.255.254", "m": 8,"g": "chinanet"},
@@ -98,6 +100,7 @@
{"from": "2603:8000::::::", "to": "2603:80ff:ffff:ffff:ffff:ffff:ffff:ffff", "m": 24, "g": "charter"},
{"from": "2607:a400::::::", "to": "2607:a400:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "g": "zenlayer"},
{"from": "2804:::::::", "to": "2804:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "m": 16, "g": "misc_sa"},
{"from": "2a09:bac3::::::", "to": "2a09:bac3:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32,"g": "cloudflare"},
{"from": "2a0a:4cc0::::::", "to": "2a0a:4cc0:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "g": "netcup"}
]
}

BIN
img/captcha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -232,7 +232,7 @@ weiter 13
gleich 13
gute 13
vom 13
Warte 12
warte 12
deinen 12
ins 12
denke 12
@@ -2730,4 +2730,5 @@ zahle 1
brauchten 1
stinkt 1
treiben 1
welch 1
welch 1
Warte 1

View File

@@ -4,11 +4,17 @@ body.botmon_captcha {
color: transparent !important;
text-shadow: 0 0 .25em rgba(0,.0,0,.8);
}
p, h2, h3, h4, h5, h6 {
p, dt, dd, h2, h3, h4, h5, h6, a:link, a:visited {
color: transparent !important;
text-shadow: 0 0 .35em rgba(0,0,0,.5);
user-select: none;
}
svg {
width: auto; height: auto;
-webkit-filter: blur(8px); /* For Safari */
filter: blur(8px);
}
}
#botmon_captcha_box {
& {
@@ -58,6 +64,9 @@ body.botmon_captcha {
background: transparent url('img/busy-light.svg') center no-repeat;
background-size: 24px;
}
span.error, span.erricon {
color: #f96c6c;
}
}
input[type="checkbox"] {
width: 24px; height: 24px;
@@ -74,20 +83,25 @@ body.botmon_captcha {
display: none;
}
&.checking {
input[type="checkbox"], span.confirm, span.loading { display: none;}
input[type="checkbox"], span.confirm, span.loading, span.erricon, span.error { display: none;}
span.busy, span.checking { display: initial; }
label, input[type="checkbox"] { cursor: wait; }
}
&.ready {
input[type="checkbox"], span.confirm { display: initial;}
span.busy, span.checking, span.loading { display: none; }
span.busy, span.checking, span.loading, span.erricon, span.error { display: none; }
label, input[type="checkbox"] { cursor: pointer; }
}
&.loading {
span.busy, span.loading { display: initial; }
input[type="checkbox"], span.confirm, span.checking { display: none;}
input[type="checkbox"], span.confirm, span.checking, span.erricon, span.error { display: none;}
label { cursor: wait; }
}
&.error {
span.erricon, span.error { display: initial; }
input[type="checkbox"], span.confirm, span.checking, span.busy, span.loading { display: none;}
label { cursor: initial; }
}
}
}