diff --git a/action.php b/action.php index 75869d0..46bdcf3 100644 --- a/action.php +++ b/action.php @@ -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 '

'; tpl_pagetitle(); echo "

\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 '

'; tpl_pagetitle(); echo "

\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 ''; + + $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 ''; // 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 ''; - 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 "

Found my IP in range: " . $col[0] . " - " . $col[1] . "

"; - return true; + return true; /* IP whitelisted */ } } } } } - - return false; + return false; /* IP not found in whitelist */ } private function insertCaptchaLoader() { diff --git a/admin.css b/admin.css index 734ee97..d8dad0e 100644 --- a/admin.css +++ b/admin.css @@ -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; } diff --git a/admin.js b/admin.js index 49765e5..0661f9e 100644 --- a/admin.js +++ b/admin.js @@ -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 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 */ diff --git a/captcha.js b/captcha.js index 9f64912..cb79a4e 100644 --- a/captcha.js +++ b/captcha.js @@ -26,7 +26,7 @@ const $BMCaptcha = { // Checkbox: const lbl = document.createElement('label'); - lbl.innerHTML = 'Click to confirm.Checking …Loading …'; + lbl.innerHTML = 'Click to confirm.Checking …Loading …An error occured.'; 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); } }, diff --git a/conf/default.php b/conf/default.php index 48f2cf5..53540b4 100644 --- a/conf/default.php +++ b/conf/default.php @@ -6,5 +6,5 @@ */ $conf['geoiplib'] = 'disabled'; -$conf['useCaptcha'] = 0; -$conf['captchaSeed'] = 'b472719ba5634d378a7d7f9bfc46659f'; +$conf['useCaptcha'] = 'disabled'; +$conf['captchaSeed'] = 'c53bc5f94929451987efa6c768d8856b'; diff --git a/config/default-whitelist.txt b/config/default-whitelist.txt index eaa0688..397720d 100644 --- a/config/default-whitelist.txt +++ b/config/default-whitelist.txt @@ -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 \ No newline at end of file diff --git a/config/known-ipranges.json b/config/known-ipranges.json index 466dddd..4e955cb 100644 --- a/config/known-ipranges.json +++ b/config/known-ipranges.json @@ -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"} ] } \ No newline at end of file diff --git a/img/captcha.png b/img/captcha.png new file mode 100644 index 0000000..6e43e46 Binary files /dev/null and b/img/captcha.png differ diff --git a/img/stages.png b/img/stages.png index 0ac0c9e..4d2621f 100644 Binary files a/img/stages.png and b/img/stages.png differ diff --git a/lang/de/wordlist.txt b/lang/de/wordlist.txt index d756082..f1a1c0f 100644 --- a/lang/de/wordlist.txt +++ b/lang/de/wordlist.txt @@ -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 \ No newline at end of file +welch 1 +Warte 1 diff --git a/style.less b/style.less index c145472..fb29653 100644 --- a/style.less +++ b/style.less @@ -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; } + } } }