3
.gitignore
vendored
@@ -2,5 +2,6 @@
|
||||
logs/*.log.txt
|
||||
logs/*.srv.txt
|
||||
logs/*.tck.txt
|
||||
config/user-config.json
|
||||
logs/*.captcha.txt
|
||||
config/user-*.*
|
||||
php_errors.log
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# DokuWiki Bot Monitoring Plugin
|
||||
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
|
||||
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
|
||||
|
||||
449
action.php
@@ -13,6 +13,26 @@ use dokuwiki\Logger;
|
||||
|
||||
class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
|
||||
public function __construct() {
|
||||
|
||||
// determine if a captcha should be loaded:
|
||||
$this->showCaptcha = 'Z'; // Captcha unknown
|
||||
|
||||
$useCaptcha = $this->getConf('useCaptcha'); // should we show a captcha?
|
||||
|
||||
if ($useCaptcha !== 'disabled') {
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
|
||||
$this->showCaptcha = 'H'; // Method is HEAD, no need for captcha
|
||||
} elseif ($this->captchaWhitelisted()) {
|
||||
$this->showCaptcha = 'W'; // IP is whitelisted, no captcha
|
||||
} elseif ($this->hasCaptchaCookie()) {
|
||||
$this->showCaptcha = 'N'; // No, user already has a cookie, don't show the captcha
|
||||
} else {
|
||||
$this->showCaptcha = 'Y'; // Yes, show the captcha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback functions
|
||||
*
|
||||
@@ -23,13 +43,23 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
|
||||
global $ACT;
|
||||
|
||||
// populate the session id and type:
|
||||
$this->setSessionInfo();
|
||||
|
||||
// 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, 'insertCaptchaCode');
|
||||
|
||||
} else if ($ACT == 'admin' && isset($_REQUEST['page']) && $_REQUEST['page'] == 'botmon') {
|
||||
$controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertAdminHeader');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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');
|
||||
|
||||
@@ -38,7 +68,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
/* session information */
|
||||
private $sessionId = null;
|
||||
private $sessionType = '';
|
||||
private $ipAddress = null;
|
||||
private $showCaptcha = 'X';
|
||||
|
||||
/**
|
||||
* Inserts tracking code to the page header
|
||||
@@ -51,17 +81,9 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
|
||||
global $INFO;
|
||||
|
||||
// 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) . "'};" . 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;
|
||||
@@ -70,10 +92,25 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
$code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "e.src='".DOKU_BASE."lib/plugins/botmon/client.js';" . NL;
|
||||
$code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(e);" . NL;
|
||||
$code .= DOKU_TAB . DOKU_TAB . "});";
|
||||
|
||||
$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)
|
||||
@@ -87,7 +124,6 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
$event->data['script'][] = ['src' => DOKU_BASE.'lib/plugins/botmon/admin.js', 'defer' => 'defer', '_data' => ''];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes data to the server log.
|
||||
*
|
||||
@@ -107,7 +143,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
|
||||
// create the log array:
|
||||
$logArr = Array(
|
||||
$_SERVER['REMOTE_ADDR'], /*$this->ipAddress, // remote IP */
|
||||
$_SERVER['REMOTE_ADDR'], /* remote IP */
|
||||
$pageId, /* page ID */
|
||||
$this->sessionId, /* Session ID */
|
||||
$this->sessionType, /* session ID type */
|
||||
@@ -116,7 +152,9 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
$_SERVER['HTTP_REFERER'] ?? '', /* HTTP Referrer */
|
||||
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->getCountryCode(), /* GeoIP country code */
|
||||
$this->showCaptcha, /* show captcha? */
|
||||
$_SERVER['REQUEST_METHOD'] ?? '' /* request method */
|
||||
);
|
||||
|
||||
//* create the log line */
|
||||
@@ -140,7 +178,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
|
||||
private function getCountryCode() {
|
||||
|
||||
$country = ( $this->ipAddress == 'localhost' ? 'local' : 'ZZ' ); // default if no geoip is available!
|
||||
$country = ( $_SERVER['REMOTE_ADDR'] == '127.0.0.1' ? 'local' : 'ZZ' ); // default if no geoip is available!
|
||||
|
||||
$lib = $this->getConf('geoiplib'); /* which library to use? (can only be phpgeoip or disabled) */
|
||||
|
||||
@@ -158,10 +196,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
return $country;
|
||||
}
|
||||
|
||||
private function getSessionInfo() {
|
||||
|
||||
$this->ipAddress = $_SERVER['REMOTE_ADDR'] ?? null;
|
||||
if ($this->ipAddress == '127.0.0.1' || $this->ipAddress == '::1') $this->ipAddress = 'localhost';
|
||||
private function setSessionInfo() {
|
||||
|
||||
// what is the session identifier?
|
||||
if (isset($_SESSION)) {
|
||||
@@ -178,13 +213,373 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
|
||||
$this->sessionId = session_id();
|
||||
$this->sessionType = 'php';
|
||||
}
|
||||
if (!$this->sessionId && $this->ipAddress) { /* no PHP session ID, try IP address */
|
||||
$this->sessionId = $this->ipAddress;
|
||||
if (!$this->sessionId) { /* no PHP session ID, try IP address */
|
||||
$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';
|
||||
|
||||
if (!$this->sessionId) { /* if all fails, use random data */
|
||||
$this->sessionId = rand(100000000, 999999999);
|
||||
$this->sessionType = 'rnd';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function insertCaptchaCode(Event $event) {
|
||||
|
||||
$useCaptcha = $this->getConf('useCaptcha'); // which background to show?
|
||||
|
||||
// only if we previously determined that we need a captcha:
|
||||
if ($this->showCaptcha == 'Y') {
|
||||
|
||||
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 'loremipsum':
|
||||
$this->insertLoremIpsum(); // show dada filler instead of text
|
||||
break;
|
||||
case 'dada':
|
||||
$this->insertDadaFiller(); // show dada filler instead of text
|
||||
break;
|
||||
}
|
||||
|
||||
// insert the captcha loader code:
|
||||
echo '<script>' . NL;
|
||||
|
||||
// add the deferred script loader::
|
||||
echo DOKU_TAB . "addEventListener('DOMContentLoaded', function(){" . NL;
|
||||
echo DOKU_TAB . DOKU_TAB . "const cj=document.createElement('script');" . 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 . "document.getElementsByTagName('head')[0].appendChild(cj);" . NL;
|
||||
echo DOKU_TAB . "});" . NL;
|
||||
|
||||
// add the translated strings for the captcha:
|
||||
echo DOKU_TAB . '$BMLocales = {' . NL;
|
||||
echo DOKU_TAB . DOKU_TAB . '"dlgTitle": ' . json_encode($this->getLang('bm_dlgTitle')) . ',' . NL;
|
||||
echo DOKU_TAB . DOKU_TAB . '"dlgSubtitle": ' . json_encode($this->getLang('bm_dlgSubtitle')) . ',' . NL;
|
||||
echo DOKU_TAB . DOKU_TAB . '"dlgConfirm": ' . json_encode($this->getLang('bm_dlgConfirm')) . ',' . NL;
|
||||
echo DOKU_TAB . DOKU_TAB . '"dlgChecking": ' . json_encode($this->getLang('bm_dlgChecking')) . ',' . NL;
|
||||
echo DOKU_TAB . DOKU_TAB . '"dlgLoading": ' . json_encode($this->getLang('bm_dlgLoading')) . ',' . NL;
|
||||
echo DOKU_TAB . DOKU_TAB . '"dlgError": ' . json_encode($this->getLang('bm_dlgError')) . ',' . NL;
|
||||
echo DOKU_TAB . '};' . NL;
|
||||
|
||||
// captcha configuration options
|
||||
echo DOKU_TAB . '$BMConfig = {' . NL;
|
||||
echo DOKU_TAB . DOKU_TAB . '"captchaBypass": ' . json_encode($this->getConf('captchaBypass')) . NL;
|
||||
echo DOKU_TAB . '};' . NL;
|
||||
|
||||
echo '</script>' . NL;
|
||||
|
||||
// insert a warning message for users without JavaScript:
|
||||
echo '<dialog open closedby="any" id="BM__NoJSWarning"><p>' . $this->getLang('bm_noJsWarning') . '</p></dialog>' . NL;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has a valid captcha cookie.
|
||||
*
|
||||
* @return boolean
|
||||
* @access private
|
||||
*
|
||||
**/
|
||||
private function hasCaptchaCookie() {
|
||||
|
||||
$cookieVal = isset($_COOKIE['DWConfirm']) ? $_COOKIE['DWConfirm'] : null;
|
||||
|
||||
$today = substr((new DateTime())->format('c'), 0, 10);
|
||||
|
||||
$raw = $this->getConf('captchaSeed') . ';' . $_SERVER['SERVER_NAME'] . ';' . $_SERVER['REMOTE_ADDR'] . ';' . $today;
|
||||
$expected = hash('sha256', $raw);
|
||||
|
||||
// for debugging: write captcha data to the log:
|
||||
$this->writeCaptchaLog($_SERVER['REMOTE_ADDR'], $cookieVal, $_SERVER['SERVER_NAME'], $expected);
|
||||
|
||||
return $cookieVal == $expected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes data to the captcha log.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function writeCaptchaLog($remote_addr, $cookieVal, $serverName, $expected) {
|
||||
|
||||
global $INFO;
|
||||
|
||||
$logArr = Array(
|
||||
$remote_addr, /* remote IP */
|
||||
$cookieVal, /* cookie value */
|
||||
$this->getConf('captchaSeed'), /* seed */
|
||||
$serverName, /* server name */
|
||||
$expected, /* expected cookie value */
|
||||
($cookieVal == $expected ? 'MATCH' : 'WRONG'), /* cookie matches expected value? */
|
||||
$_SERVER['REQUEST_URI'] /* request URI */
|
||||
);
|
||||
|
||||
//* create the log line */
|
||||
$filename = __DIR__ .'/logs/' . gmdate('Y-m-d') . '.captcha.txt'; /* use GMT date for filename */
|
||||
$logline = gmdate('Y-m-d H:i:s'); /* use GMT time for log entries */
|
||||
foreach ($logArr as $tab) {
|
||||
$logline .= "\t" . $tab;
|
||||
};
|
||||
|
||||
/* write the log line to the file */
|
||||
$logfile = fopen($filename, 'a');
|
||||
if (!$logfile) die();
|
||||
if (fwrite($logfile, $logline . "\n") === false) {
|
||||
fclose($logfile);
|
||||
die();
|
||||
}
|
||||
|
||||
// in case of errors, write the cookie data to the log:
|
||||
if (!$cookieVal) {
|
||||
$logline = print_r($_COOKIE, true);
|
||||
if (fwrite($logfile, $logline . "\n") === false) {
|
||||
fclose($logfile);
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
/* Done. close the file. */
|
||||
fclose($logfile);
|
||||
}
|
||||
|
||||
|
||||
// check if the visitor's IP is on a whitelist:
|
||||
private function captchaWhitelisted() {
|
||||
|
||||
// normalise IP address:
|
||||
$ip = inet_pton($_SERVER['REMOTE_ADDR']);
|
||||
|
||||
// find which file to open:
|
||||
$prefixes = ['user', 'default'];
|
||||
foreach ($prefixes as $pre) {
|
||||
$filename = __DIR__ .'/config/' . $pre . '-whitelist.txt';
|
||||
if (file_exists($filename)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($filename)) {
|
||||
$lines = file($filename, FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($lines as $line) {
|
||||
if (trim($line) !== '' && !str_starts_with($line, '#')) {
|
||||
$col = explode("\t", $line);
|
||||
if (count($col) >= 2) {
|
||||
$from = inet_pton($col[0]);
|
||||
$to = inet_pton($col[1]);
|
||||
|
||||
if ($ip >= $from && $ip <= $to) {
|
||||
return true; /* IP whitelisted */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; /* IP not found in whitelist */
|
||||
}
|
||||
|
||||
// inserts a blank box to ensure there is enough space for the captcha:
|
||||
private function insertLoremIpsum() {
|
||||
|
||||
echo '<div class="level1">' . NL;
|
||||
echo '<p>' . NL . 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'. NL . '</p>' . NL;
|
||||
echo '<p>' . NL . 'At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga.'. NL . '</p>' . NL;
|
||||
echo '</div>' . NL;
|
||||
|
||||
}
|
||||
|
||||
/* Generates a few paragraphs of Dada text to show instead of the article content */
|
||||
private function insertDadaFiller() {
|
||||
|
||||
global $conf;
|
||||
global $TOC;
|
||||
global $ID;
|
||||
|
||||
// list of languages to search for the wordlist
|
||||
$langs = array_unique([$conf['lang'], 'la']);
|
||||
|
||||
// find path to the first available wordlist:
|
||||
foreach ($langs as $lang) {
|
||||
$filename = __DIR__ .'/lang/' . $lang . '/wordlist.txt'; /* language-specific wordlist */
|
||||
if (file_exists($filename)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// load the wordlist file:
|
||||
if (file_exists($filename)) {
|
||||
$words = array();
|
||||
$totalWeight = 0;
|
||||
$lines = file($filename, FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($lines as $line) {
|
||||
$arr = explode("\t", $line);
|
||||
$arr[1] = ( count($arr) > 1 ? (int) trim($arr[1]) : 1 );
|
||||
$totalWeight += (int) $arr[1];
|
||||
array_push($words, $arr);
|
||||
}
|
||||
} else {
|
||||
echo '<script> console.log("Can’t generate filler text: wordlist file not found!"); </script>';
|
||||
return;
|
||||
}
|
||||
|
||||
// If a TOC exists, use it for the headlines:
|
||||
if(is_array($TOC)) {
|
||||
$toc = $TOC;
|
||||
} else {
|
||||
$meta = p_get_metadata($ID, '', METADATA_RENDER_USING_CACHE);
|
||||
//$tocok = (isset($meta['internal']['toc']) ? $meta['internal']['toc'] : $tocok = true);
|
||||
$toc = isset($meta['description']['tableofcontents']) ? $meta['description']['tableofcontents'] : null;
|
||||
}
|
||||
if (!$toc) { // no TOC, generate my own:
|
||||
$hlCount = mt_rand(0, (int) $conf['tocminheads']);
|
||||
$toc = array();
|
||||
for ($i=0; $i<$hlCount; $i++) {
|
||||
array_push($toc, $this->dadaMakeHeadline($words, $totalWeight)); // $toc
|
||||
}
|
||||
}
|
||||
|
||||
// if H1 heading is not in the TOC, add a chappeau section:
|
||||
$chapeauCount = mt_rand(1, 3);
|
||||
if ((int) $conf['toptoclevel'] > 1) {
|
||||
echo "<div class=\"level1\">\n";
|
||||
for ($i=0; $i<$chapeauCount; $i++) {
|
||||
echo $this->dadaMakeParagraph($words, $totalWeight);
|
||||
}
|
||||
echo "</div>\n";
|
||||
}
|
||||
|
||||
// text sections for each sub-headline:
|
||||
foreach ($toc as $hl) {
|
||||
echo $this->dadaMakeSection($words, $totalWeight, $hl);
|
||||
}
|
||||
}
|
||||
|
||||
private function dadaMakeSection($words, $totalWeight, $hl) {
|
||||
|
||||
global $conf;
|
||||
|
||||
// how many paragraphs?
|
||||
$paragraphCount = mt_rand(1, 4);
|
||||
|
||||
// section level
|
||||
$topTocLevel = (int) $conf['toptoclevel'];
|
||||
$secLevel = $hl['level'] + 1;;
|
||||
|
||||
// return value:
|
||||
$sec = "";
|
||||
|
||||
// make a headline:
|
||||
if ($topTocLevel > 1 || $secLevel > 1) {
|
||||
$sec .= "<h{$secLevel} id=\"{$hl['hid']}\">{$hl['title']}</h{$secLevel}>\n";
|
||||
}
|
||||
|
||||
// add the paragraphs:
|
||||
$sec .= "<div class=\"level{$secLevel}\">\n";
|
||||
for ($i=0; $i<$paragraphCount; $i++) {
|
||||
$sec .= $this->dadaMakeParagraph($words, $totalWeight);
|
||||
}
|
||||
$sec .= "</div>\n";
|
||||
|
||||
return $sec;
|
||||
}
|
||||
|
||||
private function dadaMakeHeadline($words, $totalWeight) {
|
||||
|
||||
// how many words to generate?
|
||||
$wordCount = mt_rand(2, 5);
|
||||
|
||||
// function returns an array:
|
||||
$r = Array();
|
||||
|
||||
// generate the headline:
|
||||
$hlArr = array();
|
||||
for ($i=0; $i<$wordCount; $i++) {
|
||||
array_push($hlArr, $this->dadaSelectRandomWord($words, $totalWeight));
|
||||
}
|
||||
|
||||
$r['title'] = ucfirst(implode(' ', $hlArr));
|
||||
|
||||
$r['hid'] = preg_replace('/[^\w\d\-]+/i', '_', strtolower($r['title']));
|
||||
$r['type'] = 'ul'; // always ul!
|
||||
$r['level'] = 1; // always level 1 for now
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
private function dadaMakeParagraph($words, $totalWeight) {
|
||||
|
||||
// how many words to generate?
|
||||
$sentenceCount = mt_rand(2, 5);
|
||||
|
||||
$paragraph = array();
|
||||
for ($i=0; $i<$sentenceCount; $i++) {
|
||||
array_push($paragraph, $this->dadaMakeSentence($words, $totalWeight));
|
||||
}
|
||||
|
||||
return "<p>\n" . implode(' ', $paragraph) . "\n</p>\n";
|
||||
|
||||
}
|
||||
|
||||
private function dadaMakeSentence($words, $totalWeight) {
|
||||
|
||||
// how many words to generate?
|
||||
$wordCount = mt_rand(4, 20);
|
||||
|
||||
// generate the sentence:
|
||||
$sentence = array();
|
||||
for ($i=0; $i<$wordCount; $i++) {
|
||||
array_push($sentence, $this->dadaSelectRandomWord($words, $totalWeight));
|
||||
}
|
||||
|
||||
return ucfirst(implode(' ', $sentence)) . '.';
|
||||
|
||||
}
|
||||
|
||||
private function dadaSelectRandomWord($list, $totalWeight) {
|
||||
|
||||
// get a random selection:
|
||||
$rand = mt_rand(0, $totalWeight);
|
||||
|
||||
// match the selection to the weighted list:
|
||||
$cumulativeWeight = 0;
|
||||
for ($i=0; $i<count($list); $i++) {
|
||||
$cumulativeWeight += $list[$i][1];
|
||||
if ($cumulativeWeight >= $rand) {
|
||||
return $list[$i][0];
|
||||
}
|
||||
}
|
||||
return '***';
|
||||
}
|
||||
|
||||
}
|
||||
162
admin.css
@@ -7,24 +7,29 @@
|
||||
/* icon items */
|
||||
.has_icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.icon_only {
|
||||
display: inline-grid;
|
||||
grid-template-columns: 20px max-content;
|
||||
overflow: hidden;
|
||||
width: 20px;
|
||||
width: 20px; max-width: 20px;
|
||||
}
|
||||
|
||||
.has_icon, .icon_only {
|
||||
& {
|
||||
align-items: center;
|
||||
column-gap: .25em;
|
||||
overflow: hidden;
|
||||
column-gap: .2em;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 20px; height: 20px;
|
||||
width: 20px; min-width: 20px; max-width: 20px;
|
||||
height: 20px;
|
||||
background: transparent none center no-repeat;
|
||||
background-position: 0 0;
|
||||
background-size: 20px;
|
||||
@@ -106,8 +111,22 @@
|
||||
&.cl_ecosia::before { background-position-y: -340px }
|
||||
&.cl_webkit::before { background-position-y: -360px }
|
||||
&.cl_operaold::before { background-position-y: -380px }
|
||||
&.cl_wget::before { background-position-y: -400px }
|
||||
&.cl_python::before { background-position-y: -420px }
|
||||
&.cl_privacybrowser::before { background-position-y: -440px }
|
||||
&.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_N::before, &.cap_YN::before, &.cap_YYN::before { background-position-y: -40px }
|
||||
&.cap_W::before { background-position-y: -60px }
|
||||
&.cap_H::before { background-position-y: -80px }
|
||||
&.cap_X::before, &.cap_undefined::before { background-position-y: -100px }
|
||||
&.cap_YH::before, &.cap_YYH::before { background-position-y: -120px }
|
||||
&.cap_YNH::before { background-position-y: -140px }
|
||||
&.cap_YY::before { background-position-y: -160px }
|
||||
|
||||
/* Country flags */
|
||||
/* Note: flag images and CSS adapted from: https://github.com/lafeber/world-flags-sprite/ */
|
||||
&.country::before {
|
||||
@@ -357,6 +376,7 @@
|
||||
&.typ_php::before { background-position-y: -40px }
|
||||
&.typ_ip::before { background-position-y: -60px }
|
||||
&.typ_usr::before { background-position-y: -80px }
|
||||
&.typ_rnd::before { background-position-y: -100px }
|
||||
|
||||
/* External link icons */
|
||||
&.extlink::before { background-image: url('img/links.png') }
|
||||
@@ -401,14 +421,15 @@
|
||||
.page_icon::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 20px; height: 20px;
|
||||
width: 20px; min-width: 20px; max-width: 20px;
|
||||
height: 20px;
|
||||
background: transparent url('img/page.svg') center no-repeat;
|
||||
background-position: 0 0;
|
||||
background-size: 20px;
|
||||
}
|
||||
|
||||
/* grid layout for the overview: */
|
||||
.botmon_bots_grid, .botmon_webmetrics_grid, .botmon_traffic_grid {
|
||||
.botmon_bots_grid, .botmon_webmetrics_grid, .botmon_traffic_grid, .botmon_captcha_grid {
|
||||
& {
|
||||
display: grid;
|
||||
grid-gap: 0 .33em;
|
||||
@@ -417,7 +438,8 @@
|
||||
dd {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
align-items: center;
|
||||
column-gap: .5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,7 +470,23 @@
|
||||
}
|
||||
}
|
||||
.botmon_traffic_grid {
|
||||
grid-template-columns: 2fr 1fr;
|
||||
& {
|
||||
grid-template-columns: 2fr 1fr;
|
||||
}
|
||||
#botmon__today__wm_pages, #botmon__today__wm_referers {
|
||||
& {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
dd a {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
.botmon_captcha_grid {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
/* The tabs bar */
|
||||
@@ -527,14 +565,15 @@
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
&::marker, &::before {
|
||||
&::marker {
|
||||
content: none;
|
||||
display: none;
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 1.25em; height: 1.25em;
|
||||
width: 1.25em; min-width: 1.25em; max-width: 1.25em;
|
||||
height: 1.25em;
|
||||
background: transparent url('img/chevron.svg') center no-repeat;
|
||||
background-size: 1.25em;
|
||||
transform: rotate(-90deg);
|
||||
@@ -600,8 +639,10 @@
|
||||
border-radius: .5em;
|
||||
}
|
||||
details ul > li > details > summary {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
display: grid;
|
||||
grid-template-columns: 1.25em minmax(calc(100px + 4em), auto) max-content;
|
||||
justify-items: stretch;
|
||||
justify-content: stretch;
|
||||
align-items: center;
|
||||
column-gap: .5em;
|
||||
font-weight: normal;
|
||||
@@ -610,6 +651,7 @@
|
||||
background-color: #F0F0F0;
|
||||
border-bottom: #CCC solid 1px;
|
||||
border-radius: .7em;
|
||||
overflow: auto;
|
||||
}
|
||||
details ul > li > details > summary.noServer {
|
||||
opacity: 67%;
|
||||
@@ -620,7 +662,10 @@
|
||||
column-gap: .25em;
|
||||
}
|
||||
details ul > li > details > summary > span:first-child {
|
||||
flex-grow: 1;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
details ul > li > details > summary > span:last-child {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
details ul > li > details > summary > span > span[title] {
|
||||
cursor: help;
|
||||
@@ -630,8 +675,9 @@
|
||||
& {
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
gap: .25em .5em;
|
||||
border-left: transparent none 0;
|
||||
margin: 0 .5rem .25rem 0;
|
||||
margin: .5rem .5rem .25rem 0;
|
||||
}
|
||||
dt {
|
||||
grid-column: 1;
|
||||
@@ -642,8 +688,6 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
dd.pages {
|
||||
& {
|
||||
}
|
||||
ul {
|
||||
li {
|
||||
& {
|
||||
@@ -656,17 +700,9 @@
|
||||
&:nth-child(odd) {
|
||||
background-color: #DFDFDF;
|
||||
}
|
||||
div.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
white-space: nowrap;
|
||||
line-height: 1.35em;
|
||||
}
|
||||
span {
|
||||
display: inline-block;
|
||||
}
|
||||
/*&.detailled {
|
||||
outline: red dotted 1pt;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
a[hreflang] {
|
||||
@@ -685,6 +721,23 @@
|
||||
padding: 0 1pt;
|
||||
margin-left: .2em;
|
||||
}
|
||||
div.row {
|
||||
& {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
white-space: nowrap;
|
||||
line-height: 1.35em;
|
||||
}
|
||||
& > div {
|
||||
display: inline-flex;
|
||||
column-gap: .4em;
|
||||
}
|
||||
}
|
||||
span {
|
||||
display: inline-block;
|
||||
}
|
||||
span.first-seen {
|
||||
min-width: 4.2em;
|
||||
text-align: right;
|
||||
@@ -693,20 +746,26 @@
|
||||
font-size: smaller;
|
||||
}
|
||||
span.bounce {
|
||||
width: 1.25em; height: 1.25em;
|
||||
width: 1em; height: 1em;
|
||||
overflow: hidden;
|
||||
}
|
||||
span.bounce::before {
|
||||
display: inline-block;
|
||||
content: '';
|
||||
width: 1.25em; height: 1em;
|
||||
width: 1em; height: 1em;
|
||||
background: transparent url('img/bounce.svg') center no-repeat;
|
||||
background-size: .95em;
|
||||
}
|
||||
span.referer {
|
||||
span.referer, span.views, span.ip-address, span.user-agent {
|
||||
font-size: smaller;
|
||||
}
|
||||
span.referer, span.ip-address, span.user-agent {
|
||||
margin-left: .67rem;
|
||||
}
|
||||
span.user-agent {
|
||||
line-break: anywhere;
|
||||
text-wrap-mode: wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -730,21 +789,29 @@
|
||||
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;
|
||||
min-width: fit-content;
|
||||
}
|
||||
span.pageviews::before {
|
||||
span.pageseen::before {
|
||||
content : '';
|
||||
display: inline-block;
|
||||
width: 1.25em; height: 1.25em;
|
||||
width: 1.25em; min-width: 1.25em; max-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 +957,7 @@
|
||||
border-top-color: #CCC;
|
||||
}
|
||||
}
|
||||
span.pageviews {
|
||||
span.pageseen, span.pageviews {
|
||||
border-color: #555;
|
||||
}
|
||||
|
||||
@@ -929,6 +996,23 @@
|
||||
}
|
||||
}
|
||||
/* layout overrides for narrow screens: */
|
||||
@media (max-width: 1024px) {
|
||||
#botmon__admin #botmon__latest {
|
||||
.botmon_traffic_grid {
|
||||
& {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
dt {
|
||||
margin: .5em 0;
|
||||
}
|
||||
dl {
|
||||
border-left: transparent none 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
#botmon__admin #botmon__latest #botmon__today__visitorlists {
|
||||
dl.visitor_details {
|
||||
@@ -952,7 +1036,7 @@
|
||||
|
||||
@media (max-width: 670px) {
|
||||
#botmon__admin #botmon__latest {
|
||||
.botmon_bots_grid, .botmon_webmetrics_grid, .botmon_traffic_grid {
|
||||
.botmon_bots_grid, .botmon_webmetrics_grid, .botmon_captcha_grid {
|
||||
& {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
@@ -966,3 +1050,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 440px) {
|
||||
#botmon__admin #botmon__latest #botmon__today__content details > div { padding: .25em; }
|
||||
#botmon__admin #botmon__latest #botmon__today__visitorlists details ul > li { margin-left: 0; }
|
||||
#botmon__admin #botmon__latest #botmon__today__visitorlists details ul > li > details > summary { column-gap: .1em; }
|
||||
}
|
||||
33
admin.php
@@ -34,6 +34,7 @@ class admin_plugin_botmon extends AdminPlugin {
|
||||
|
||||
// display GeoIP data?
|
||||
$geoIPconf = $this->getConf('geoiplib');
|
||||
$useCaptchaConf = ($this->getConf('useCaptcha') !== 'disabled');
|
||||
|
||||
$hasOldLogFiles = $this->hasOldLogFiles();
|
||||
|
||||
@@ -43,7 +44,7 @@ class admin_plugin_botmon extends AdminPlugin {
|
||||
$pluginPath = $conf['basedir'] . 'lib/plugins/' . $this->getPluginName();
|
||||
|
||||
/* Plugin Headline */
|
||||
echo '<div id="botmon__admin">
|
||||
echo NL . '<div id="botmon__admin">
|
||||
<h1>Bot Monitoring Plugin</h1>
|
||||
<nav id="botmon__tabs">
|
||||
<ul class="tabs" role="tablist">
|
||||
@@ -52,11 +53,18 @@ class admin_plugin_botmon extends AdminPlugin {
|
||||
</ul>
|
||||
</nav>
|
||||
<article role="tabpanel" id="botmon__latest">
|
||||
<script>
|
||||
const BMSettings = {
|
||||
showday: ' . json_encode($this->getConf('showday')) . ',
|
||||
combineNets: ' . json_encode($this->getConf('combineNets')) . ',
|
||||
useCaptcha: ' . json_encode($this->getConf('useCaptcha') !== 'disabled') . '
|
||||
};
|
||||
</script>
|
||||
<h2 class="a11y">Latest data</h2>
|
||||
<header id="botmon__today__title">Loading …</header>
|
||||
<div id="botmon__today__content">
|
||||
<details id="botmon__today__overview" open>
|
||||
<summary>Overview</summary>
|
||||
<summary>Bots overview</summary>
|
||||
<div class="botmon_bots_grid" data-geoip="' . $geoIPconf . '">
|
||||
<dl id="botmon__today__botsvshumans"></dl>
|
||||
<dl id="botmon__botslist"></dl>
|
||||
@@ -79,8 +87,18 @@ class admin_plugin_botmon extends AdminPlugin {
|
||||
<dl id="botmon__today__wm_pages"></dl>
|
||||
<dl id="botmon__today__wm_referers"></dl>
|
||||
</div>
|
||||
</details>
|
||||
<details id="botmon__today__visitors">
|
||||
</details>' . NL;
|
||||
if ($useCaptchaConf) {
|
||||
echo ' <details id="botmon__today__captcha">
|
||||
<summary>Captcha statistics</summary>
|
||||
<div class="botmon_captcha_grid">
|
||||
<dl id="botmon__today__cp_humans"></dl>
|
||||
<dl id="botmon__today__cp_sus"></dl>
|
||||
<dl id="botmon__today__cp_bots"></dl>
|
||||
</div>
|
||||
</details>' . NL;
|
||||
}
|
||||
echo ' <details id="botmon__today__visitors">
|
||||
<summary>Visitor logs</summary>
|
||||
<div id="botmon__today__visitorlists"></div>
|
||||
</details>
|
||||
@@ -92,7 +110,7 @@ class admin_plugin_botmon extends AdminPlugin {
|
||||
</article>
|
||||
<article role="tabpanel" id="botmon__log" hidden>
|
||||
<h2 class="a11y">Process log</h2>
|
||||
<ul id="botmon__loglist">';
|
||||
<ul id="botmon__loglist">' . NL;
|
||||
|
||||
/* proces old logs */
|
||||
if ($hasOldLogFiles) {
|
||||
@@ -101,10 +119,11 @@ class admin_plugin_botmon extends AdminPlugin {
|
||||
|
||||
$helper->cleanup();
|
||||
} else {
|
||||
echo '<li>No files to process.</li>';
|
||||
echo DOKU_TAB . DOKU_TAB . DOKU_TAB . '<li>No files to process.</li>' . NL;
|
||||
}
|
||||
|
||||
echo '</article></div><!-- End of BotMon Admin Tool -->';
|
||||
echo DOKU_TAB . DOKU_TAB . '</ul>' . NL . DOKU_TAB . '</article>' . NL;
|
||||
echo '</div><!-- End of BotMon Admin Tool -->';
|
||||
|
||||
}
|
||||
|
||||
|
||||
237
captcha.js
Normal file
@@ -0,0 +1,237 @@
|
||||
"use strict";
|
||||
/* DokuWiki BotMon Captcha JavaScript */
|
||||
/* 23.10.2025 - 0.1.2 - pre-release */
|
||||
/* Author: Sascha Leib <ad@hominem.info> */
|
||||
|
||||
const $BMCaptcha = {
|
||||
|
||||
init: function() {
|
||||
|
||||
// hide the NoJS warning:
|
||||
document.getElementById('BM__NoJSWarning').close();
|
||||
|
||||
// install the captcha:
|
||||
document.getElementsByTagName('body')[0].classList.add('botmon_captcha');
|
||||
$BMCaptcha._cbDly = 1.5;
|
||||
$BMCaptcha.install()
|
||||
},
|
||||
|
||||
install: function() {
|
||||
|
||||
// localisation helper function:
|
||||
let _loc = function(id, alt) {
|
||||
if ($BMLocales && $BMLocales[id]) return $BMLocales[id];
|
||||
return alt;
|
||||
}
|
||||
|
||||
// find the parent element:
|
||||
let bm_parent = document.getElementsByTagName('body')[0];
|
||||
|
||||
// create the dialog:
|
||||
const dlg = document.createElement('dialog');
|
||||
dlg.setAttribute('closedby', 'none');
|
||||
dlg.setAttribute('open', 'open');
|
||||
dlg.setAttribute('role', 'alertdialog');
|
||||
dlg.setAttribute('aria-labelledby', 'botmon_captcha_title');
|
||||
dlg.classList.add('checking');
|
||||
dlg.id = 'botmon_captcha_box';
|
||||
dlg.innerHTML = '<h2 id="botmon_captcha_title">' + _loc('dlgTitle', 'Title') + '</h2><p>' + _loc('dlgSubtitle', 'Subtitle') + '</p>';
|
||||
|
||||
// Checkbox:
|
||||
const lbl = document.createElement('label');
|
||||
lbl.setAttribute('aria-live', 'assertive');
|
||||
lbl.innerHTML = '<span class="confirm">' + _loc('dlgConfirm', "Confirm.") + '</span>' +
|
||||
'<span class="busy"></span><span class="checking">' + _loc('dlgChecking', "Checking") + '</span>' +
|
||||
'<span class="loading">' + _loc('dlgLoading', "Loading") + '</span>' +
|
||||
'<span class="erricon">�</span><span class="error">' + _loc('dlgError', "Error") + '</span>';
|
||||
const cb = document.createElement('input');
|
||||
cb.setAttribute('type', 'checkbox');
|
||||
cb.setAttribute('disabled', 'disabled');
|
||||
cb.addEventListener('click', $BMCaptcha._cbCallback);
|
||||
lbl.prepend(cb);
|
||||
|
||||
dlg.appendChild(lbl);
|
||||
|
||||
bm_parent.appendChild(dlg);
|
||||
|
||||
// call the delayed callback in a couple of seconds:
|
||||
$BMCaptcha._st = performance.now();
|
||||
setTimeout($BMCaptcha._delayedCallback, $BMCaptcha._cbDly * 1000);
|
||||
},
|
||||
|
||||
/* creates a digest hash */
|
||||
digest: {
|
||||
|
||||
/* simple SHA hash function - adapted from https://geraintluff.github.io/sha256/ */
|
||||
hash: function(ascii) {
|
||||
|
||||
// shortcut:
|
||||
const sha256 = $BMCaptcha.digest.hash;
|
||||
|
||||
// helper function
|
||||
const rightRotate = function(v, a) {
|
||||
return (v>>>a) | (v<<(32 - a));
|
||||
};
|
||||
|
||||
var mathPow = Math.pow;
|
||||
var maxWord = mathPow(2, 32);
|
||||
var lengthProperty = 'length'
|
||||
var i, j;
|
||||
var result = ''
|
||||
|
||||
var words = [];
|
||||
var asciiBitLength = ascii[lengthProperty]*8;
|
||||
|
||||
//* caching results is optional - remove/add slash from front of this line to toggle
|
||||
// Initial hash value: first 32 bits of the fractional parts of the square roots of the first 8 primes
|
||||
// (we actually calculate the first 64, but extra values are just ignored)
|
||||
var hash = sha256.h = sha256.h || [];
|
||||
// Round constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes
|
||||
var k = sha256.k = sha256.k || [];
|
||||
var primeCounter = k[lengthProperty];
|
||||
/*/
|
||||
var hash = [], k = [];
|
||||
var primeCounter = 0;
|
||||
//*/
|
||||
|
||||
var isComposite = {};
|
||||
for (var candidate = 2; primeCounter < 64; candidate++) {
|
||||
if (!isComposite[candidate]) {
|
||||
for (i = 0; i < 313; i += candidate) {
|
||||
isComposite[i] = candidate;
|
||||
}
|
||||
hash[primeCounter] = (mathPow(candidate, .5)*maxWord)|0;
|
||||
k[primeCounter++] = (mathPow(candidate, 1/3)*maxWord)|0;
|
||||
}
|
||||
}
|
||||
|
||||
ascii += '\x80' // Append Ƈ' bit (plus zero padding)
|
||||
while (ascii[lengthProperty]%64 - 56) ascii += '\x00' // More zero padding
|
||||
for (i = 0; i < ascii[lengthProperty]; i++) {
|
||||
j = ascii.charCodeAt(i);
|
||||
if (j>>8) return; // ASCII check: only accept characters in range 0-255
|
||||
words[i>>2] |= j << ((3 - i)%4)*8;
|
||||
}
|
||||
words[words[lengthProperty]] = ((asciiBitLength/maxWord)|0);
|
||||
words[words[lengthProperty]] = (asciiBitLength)
|
||||
|
||||
// process each chunk
|
||||
for (j = 0; j < words[lengthProperty];) {
|
||||
var w = words.slice(j, j += 16); // The message is expanded into 64 words as part of the iteration
|
||||
var oldHash = hash;
|
||||
// This is now the undefinedworking hash", often labelled as variables a...g
|
||||
// (we have to truncate as well, otherwise extra entries at the end accumulate
|
||||
hash = hash.slice(0, 8);
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
var i2 = i + j;
|
||||
// Expand the message into 64 words
|
||||
// Used below if
|
||||
var w15 = w[i - 15], w2 = w[i - 2];
|
||||
|
||||
// Iterate
|
||||
var a = hash[0], e = hash[4];
|
||||
var temp1 = hash[7]
|
||||
+ (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)) // S1
|
||||
+ ((e&hash[5])^((~e)&hash[6])) // ch
|
||||
+ k[i]
|
||||
// Expand the message schedule if needed
|
||||
+ (w[i] = (i < 16) ? w[i] : (
|
||||
w[i - 16]
|
||||
+ (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15>>>3)) // s0
|
||||
+ w[i - 7]
|
||||
+ (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2>>>10)) // s1
|
||||
)|0
|
||||
);
|
||||
// This is only used once, so *could* be moved below, but it only saves 4 bytes and makes things unreadble
|
||||
var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) // S0
|
||||
+ ((a&hash[1])^(a&hash[2])^(hash[1]&hash[2])); // maj
|
||||
|
||||
hash = [(temp1 + temp2)|0].concat(hash); // We don't bother trimming off the extra ones, they're harmless as long as we're truncating when we do the slice()
|
||||
hash[4] = (hash[4] + temp1)|0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
hash[i] = (hash[i] + oldHash[i])|0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (j = 3; j + 1; j--) {
|
||||
var b = (hash[i]>>(j*8))&255;
|
||||
result += ((b < 16) ? 0 : '') + b.toString(16);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
|
||||
_cbCallback: function(e) {
|
||||
if (e.target.checked) {
|
||||
//document.getElementById('botmon_captcha_box').close();
|
||||
|
||||
try {
|
||||
var $status = 'loading';
|
||||
|
||||
// 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)
|
||||
];
|
||||
if (performance.now() - $BMCaptcha._st <= 1500) dat.push(performance.now() - $BMCaptcha._st);
|
||||
|
||||
// set the cookie:
|
||||
document.cookie = "DWConfirm=" + encodeURIComponent($BMCaptcha.digest.hash(dat.join(';'))) + '; path=/; session;';
|
||||
// + (document.location.protocol === 'https:' ? ' secure;' : '');
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
$status = 'error';
|
||||
}
|
||||
|
||||
// change the interface:
|
||||
const dlg = document.getElementById('botmon_captcha_box');
|
||||
if (dlg) {
|
||||
dlg.classList.add( $status );
|
||||
dlg.classList.remove('ready');
|
||||
}
|
||||
|
||||
// reload the page:
|
||||
if ($status !== 'error')window.location.reload(true);
|
||||
}
|
||||
},
|
||||
|
||||
_delayedCallback: function() {
|
||||
const dlg = document.getElementById('botmon_captcha_box');
|
||||
if (dlg) {
|
||||
dlg.classList.add('ready');
|
||||
dlg.classList.remove('checking');
|
||||
|
||||
const input = dlg.getElementsByTagName('input')[0];
|
||||
if (input) {
|
||||
input.removeAttribute('disabled');
|
||||
input.focus();
|
||||
setTimeout($BMCaptcha._autoCheck, 200, input);
|
||||
}
|
||||
}
|
||||
},
|
||||
_cbDly: null,
|
||||
_st: null,
|
||||
|
||||
_autoCheck: function(e) {
|
||||
|
||||
const bypass = ($BMConfig['captchaBypass'] || '').split(',');
|
||||
var action = false;
|
||||
|
||||
if (bypass.indexOf('langmatch') >= 0) { // Languages matching
|
||||
const cntLangs = navigator.languages.map(lang => lang.split('-')[0]);
|
||||
if (cntLangs.indexOf(document.documentElement.lang || 'en') >= 0) action = true;
|
||||
}
|
||||
|
||||
if (action) e.click(); // action!
|
||||
}
|
||||
}
|
||||
// initialise the captcha module:
|
||||
$BMCaptcha.init();
|
||||
@@ -5,4 +5,9 @@
|
||||
* @author Sascha Leib <sascha@leib.be>
|
||||
*/
|
||||
|
||||
$conf['showday'] = 'today';
|
||||
$conf['combineNets'] = true;
|
||||
$conf['geoiplib'] = 'disabled';
|
||||
$conf['useCaptcha'] = 'disabled';
|
||||
$conf['captchaSeed'] = 'c53bc5f94929451987efa6c768d8856b';
|
||||
$conf['captchaBypass'] = '';
|
||||
|
||||
@@ -5,5 +5,21 @@
|
||||
* @author Sascha Leib <sascha@leib.be>
|
||||
*/
|
||||
|
||||
// How to show data in the admin interface:
|
||||
$meta['showday'] = array('multichoice',
|
||||
'_choices' => array ('yesterday', 'today'));
|
||||
|
||||
$meta['combineNets'] = array('onoff');
|
||||
|
||||
// Geolocation settings:
|
||||
$meta['geoiplib'] = array('multichoice',
|
||||
'_choices' => array ('disabled', 'phpgeoip'));
|
||||
|
||||
// Captcha settings:
|
||||
$meta['useCaptcha'] = array('multichoice',
|
||||
'_choices' => array ('disabled', 'loremipsum', 'dada'));
|
||||
|
||||
$meta['captchaSeed'] = array('string');
|
||||
|
||||
$meta['captchaBypass'] = array('multicheckbox',
|
||||
'_choices' => array ('langmatch'), '_other' => 'exists');
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"threshold": 100,
|
||||
"rules": [
|
||||
{"func": "fromKnownBotIP",
|
||||
"id": "botIpRange", "desc": "Common Bot IP range",
|
||||
"bot": 50
|
||||
"id": "knownIpRange", "desc": "From known IP range",
|
||||
"bot": 40
|
||||
},
|
||||
{"func": "matchesClient", "params": ["aol","msie","ffold","chromeold","oldedge","operaold"],
|
||||
"id": "oldClient", "desc": "Obsolete browser version",
|
||||
@@ -41,7 +41,7 @@
|
||||
"id": "susClient", "desc": "Client identifier that is popular with bot networks",
|
||||
"bot": 10
|
||||
},
|
||||
{"func": "matchesClient", "params": ["undici","gohttp"],
|
||||
{"func": "matchesClient", "params": ["undici","gohttp","python","wget","curl"],
|
||||
"id": "botClient", "desc": "Client identifier indicates web crawler",
|
||||
"bot": 100
|
||||
},
|
||||
@@ -57,9 +57,13 @@
|
||||
"id": "impPC", "desc": "Impossible combination of platform and client",
|
||||
"bot": 70
|
||||
},
|
||||
{"func": "loadSpeed", "params": [3, 10],
|
||||
"id": "speedRun", "desc": "Average time between page loads is less than 10 seconds",
|
||||
"bot": 30
|
||||
},
|
||||
{"func": "loadSpeed", "params": [3, 20],
|
||||
"id": "speedRun", "desc": "Average time between page loads is less than 20 seconds",
|
||||
"bot": 60
|
||||
"bot": 30
|
||||
},
|
||||
{"func": "noAcceptLang",
|
||||
"id": "noAcc", "desc": "No “Accept-Language” header",
|
||||
@@ -69,9 +73,13 @@
|
||||
"id": "langMatch", "desc": "Client’s ‘Accept-Language’ header does not match the page language",
|
||||
"bot": 30
|
||||
},
|
||||
{"func": "matchesClient", "params": ["whatsapp","applemsgs","goognblm","tiktok","meta","chatgpt","claude","perplexity"],
|
||||
"id": "previewClient", "desc": "User-triggered bot load (e.g. preview)",
|
||||
"bot": -120
|
||||
{"func": "blockedByCaptcha", "params": [],
|
||||
"id": "blockedByCaptcha", "desc": "Visitor did not solve the captcha",
|
||||
"bot": 50
|
||||
},
|
||||
{"func": "whitelistedByCaptcha", "params": [],
|
||||
"id": "whitelistedByCaptcha", "desc": "Visitor uses a whitelisted IP address",
|
||||
"bot": -30
|
||||
}
|
||||
]
|
||||
}
|
||||
354
config/default-whitelist.txt
Normal file
@@ -0,0 +1,354 @@
|
||||
# Internet Archive Bot Ranges
|
||||
207.241.224.0 207.241.239.255 20
|
||||
207.241.224.0 207.241.224.255 24
|
||||
207.241.231.0 207.241.231.255 24
|
||||
207.241.234.0 207.241.234.255 24
|
||||
207.241.237.0 207.241.237.255 24
|
||||
208.70.24.0 208.70.31.255 21
|
||||
|
||||
# Bing Bot IP ranges - taken from https://www.bing.com/toolbox/bingbot.json
|
||||
157.55.39.0 157.55.39.255 24
|
||||
207.46.13.0 207.46.13.255 24
|
||||
40.77.167.0 40.77.167.255 24
|
||||
13.66.139.0 13.66.139.255 24
|
||||
13.66.144.0 13.66.144.255 24
|
||||
52.167.144.0 52.167.144.255 24
|
||||
13.67.10.16 13.67.10.31 28
|
||||
13.69.66.240 13.69.66.255 28
|
||||
13.71.172.224 13.71.172.239 28
|
||||
139.217.52.0 139.217.52.15 28
|
||||
191.233.204.224 191.233.204.239 28
|
||||
20.36.108.32 20.36.108.47 28
|
||||
20.43.120.16 20.43.120.31 28
|
||||
40.79.131.208 40.79.131.223 28
|
||||
40.79.186.176 40.79.186.191 28
|
||||
52.231.148.0 52.231.148.15 28
|
||||
20.79.107.240 20.79.107.255 28
|
||||
51.105.67.0 51.105.67.15 28
|
||||
20.125.163.80 20.125.163.95 28
|
||||
40.77.188.0 40.77.188.255 22
|
||||
65.55.210.0 65.55.210.255 24
|
||||
199.30.24.0 199.30.24.255 23
|
||||
40.77.202.0 40.77.202.255 24
|
||||
40.77.139.0 40.77.139.127 25
|
||||
20.74.197.0 20.74.197.15 28
|
||||
20.15.133.160 20.15.133.191 27
|
||||
40.77.177.0 40.77.177.255 24
|
||||
40.77.178.0 40.77.178.255 23
|
||||
|
||||
# Google Bot IP ranges - taken from: https://developers.google.com/static/search/apis/ipranges/googlebot.json
|
||||
192.178.4.0 192.178.4.32 27
|
||||
192.178.4.128 192.178.4.159 27
|
||||
192.178.4.160 192.178.4.191 27
|
||||
192.178.4.192 192.178.4.223 27
|
||||
192.178.4.32 192.178.4.63 27
|
||||
192.178.4.64 192.178.4.95 27
|
||||
192.178.4.96 192.178.4.127 27
|
||||
192.178.5.0 192.178.5.32 27
|
||||
192.178.6.0 192.178.6.32 27
|
||||
192.178.6.128 192.178.6.159 27
|
||||
192.178.6.160 192.178.6.191 27
|
||||
192.178.6.192 192.178.6.223 27
|
||||
192.178.6.224 192.178.6.255 27
|
||||
192.178.6.32 192.178.6.63 27
|
||||
192.178.6.64 192.178.6.95 27
|
||||
192.178.6.96 192.178.6.127 27
|
||||
192.178.7.0 192.178.7.32 27
|
||||
192.178.7.128 192.178.7.159 27
|
||||
192.178.7.160 192.178.7.191 27
|
||||
192.178.7.192 192.178.7.223 27
|
||||
192.178.7.224 192.178.7.255 27
|
||||
192.178.7.32 192.178.7.63 27
|
||||
192.178.7.64 192.178.7.95 27
|
||||
192.178.7.96 192.178.7.127 27
|
||||
34.100.182.96 34.100.182.111 28
|
||||
34.101.50.144 34.101.50.159 28
|
||||
34.118.254.0 34.118.254.15 28
|
||||
34.118.66.0 34.118.66.15 28
|
||||
34.126.178.96 34.126.178.111 28
|
||||
34.146.150.144 34.146.150.159 28
|
||||
34.147.110.144 34.147.110.159 28
|
||||
34.151.74.144 34.151.74.159 28
|
||||
34.152.50.64 34.152.50.79 28
|
||||
34.154.114.144 34.154.114.159 28
|
||||
34.155.98.32 34.155.98.47 28
|
||||
34.165.18.176 34.165.18.191 28
|
||||
34.175.160.64 34.175.160.79 28
|
||||
34.176.130.16 34.176.130.31 28
|
||||
34.22.85.0 34.22.85.32 27
|
||||
34.64.82.64 34.64.82.79 28
|
||||
34.65.242.112 34.65.242.127 28
|
||||
34.80.50.80 34.80.50.95 28
|
||||
34.88.194.0 34.88.194.15 28
|
||||
34.89.10.80 34.89.10.95 28
|
||||
34.89.198.80 34.89.198.95 28
|
||||
34.96.162.48 34.96.162.63 28
|
||||
35.247.243.240 35.247.243.255 28
|
||||
66.249.64.0 66.249.64.32 27
|
||||
66.249.64.128 66.249.64.159 27
|
||||
66.249.64.160 66.249.64.191 27
|
||||
66.249.64.192 66.249.64.223 27
|
||||
66.249.64.224 66.249.64.255 27
|
||||
66.249.64.32 66.249.64.63 27
|
||||
66.249.64.64 66.249.64.95 27
|
||||
66.249.64.96 66.249.64.127 27
|
||||
66.249.65.0 66.249.65.32 27
|
||||
66.249.65.128 66.249.65.159 27
|
||||
66.249.65.160 66.249.65.191 27
|
||||
66.249.65.192 66.249.65.223 27
|
||||
66.249.65.224 66.249.65.255 27
|
||||
66.249.65.32 66.249.65.63 27
|
||||
66.249.65.64 66.249.65.95 27
|
||||
66.249.65.96 66.249.65.127 27
|
||||
66.249.66.0 66.249.66.32 27
|
||||
66.249.66.128 66.249.66.159 27
|
||||
66.249.66.160 66.249.66.191 27
|
||||
66.249.66.192 66.249.66.223 27
|
||||
66.249.66.224 66.249.66.255 27
|
||||
66.249.66.32 66.249.66.63 27
|
||||
66.249.66.64 66.249.66.95 27
|
||||
66.249.66.96 66.249.66.127 27
|
||||
66.249.67.0 66.249.67.32 27
|
||||
66.249.67.32 66.249.67.63 27
|
||||
66.249.68.0 66.249.68.32 27
|
||||
66.249.68.128 66.249.68.159 27
|
||||
66.249.68.160 66.249.68.191 27
|
||||
66.249.68.192 66.249.68.223 27
|
||||
66.249.68.32 66.249.68.63 27
|
||||
66.249.68.64 66.249.68.95 27
|
||||
66.249.68.96 66.249.68.127 27
|
||||
66.249.69.0 66.249.69.32 27
|
||||
66.249.69.128 66.249.69.159 27
|
||||
66.249.69.160 66.249.69.191 27
|
||||
66.249.69.192 66.249.69.223 27
|
||||
66.249.69.224 66.249.69.255 27
|
||||
66.249.69.32 66.249.69.63 27
|
||||
66.249.69.64 66.249.69.95 27
|
||||
66.249.69.96 66.249.69.127 27
|
||||
66.249.70.0 66.249.70.32 27
|
||||
66.249.70.128 66.249.70.159 27
|
||||
66.249.70.160 66.249.70.191 27
|
||||
66.249.70.192 66.249.70.223 27
|
||||
66.249.70.224 66.249.70.255 27
|
||||
66.249.70.32 66.249.70.63 27
|
||||
66.249.70.64 66.249.70.95 27
|
||||
66.249.70.96 66.249.70.127 27
|
||||
66.249.71.0 66.249.71.32 27
|
||||
66.249.71.128 66.249.71.159 27
|
||||
66.249.71.160 66.249.71.191 27
|
||||
66.249.71.192 66.249.71.223 27
|
||||
66.249.71.224 66.249.71.255 27
|
||||
66.249.71.32 66.249.71.63 27
|
||||
66.249.71.64 66.249.71.95 27
|
||||
66.249.71.96 66.249.71.127 27
|
||||
66.249.72.0 66.249.72.32 27
|
||||
66.249.72.128 66.249.72.159 27
|
||||
66.249.72.160 66.249.72.191 27
|
||||
66.249.72.192 66.249.72.223 27
|
||||
66.249.72.224 66.249.72.255 27
|
||||
66.249.72.32 66.249.72.63 27
|
||||
66.249.72.64 66.249.72.95 27
|
||||
66.249.73.0 66.249.73.32 27
|
||||
66.249.73.128 66.249.73.159 27
|
||||
66.249.73.160 66.249.73.191 27
|
||||
66.249.73.192 66.249.73.223 27
|
||||
66.249.73.224 66.249.73.255 27
|
||||
66.249.73.32 66.249.73.63 27
|
||||
66.249.73.64 66.249.73.95 27
|
||||
66.249.73.96 66.249.73.127 27
|
||||
66.249.74.0 66.249.74.32 27
|
||||
66.249.74.128 66.249.74.159 27
|
||||
66.249.74.160 66.249.74.191 27
|
||||
66.249.74.192 66.249.74.223 27
|
||||
66.249.74.224 66.249.74.255 27
|
||||
66.249.74.32 66.249.74.63 27
|
||||
66.249.74.64 66.249.74.95 27
|
||||
66.249.74.96 66.249.74.127 27
|
||||
66.249.75.0 66.249.75.32 27
|
||||
66.249.75.128 66.249.75.159 27
|
||||
66.249.75.160 66.249.75.191 27
|
||||
66.249.75.192 66.249.75.223 27
|
||||
66.249.75.224 66.249.75.255 27
|
||||
66.249.75.32 66.249.75.63 27
|
||||
66.249.75.64 66.249.75.95 27
|
||||
66.249.75.96 66.249.75.127 27
|
||||
66.249.76.0 66.249.76.32 27
|
||||
66.249.76.128 66.249.76.159 27
|
||||
66.249.76.160 66.249.76.191 27
|
||||
66.249.76.192 66.249.76.223 27
|
||||
66.249.76.224 66.249.76.255 27
|
||||
66.249.76.32 66.249.76.63 27
|
||||
66.249.76.64 66.249.76.95 27
|
||||
66.249.76.96 66.249.76.127 27
|
||||
66.249.77.0 66.249.77.32 27
|
||||
66.249.77.128 66.249.77.159 27
|
||||
66.249.77.160 66.249.77.191 27
|
||||
66.249.77.192 66.249.77.223 27
|
||||
66.249.77.224 66.249.77.255 27
|
||||
66.249.77.32 66.249.77.63 27
|
||||
66.249.77.64 66.249.77.95 27
|
||||
66.249.77.96 66.249.77.127 27
|
||||
66.249.78.0 66.249.78.32 27
|
||||
66.249.78.128 66.249.78.159 27
|
||||
66.249.78.160 66.249.78.191 27
|
||||
66.249.78.32 66.249.78.63 27
|
||||
66.249.78.64 66.249.78.95 27
|
||||
66.249.78.96 66.249.78.127 27
|
||||
66.249.79.0 66.249.79.32 27
|
||||
66.249.79.128 66.249.79.159 27
|
||||
66.249.79.160 66.249.79.191 27
|
||||
66.249.79.192 66.249.79.223 27
|
||||
66.249.79.224 66.249.79.255 27
|
||||
66.249.79.32 66.249.79.63 27
|
||||
66.249.79.64 66.249.79.95 27
|
||||
2001:4860:4801:0010:: 2001:4860:4801:0010:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0012:: 2001:4860:4801:0012:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0013:: 2001:4860:4801:0013:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0014:: 2001:4860:4801:0014:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0015:: 2001:4860:4801:0015:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0016:: 2001:4860:4801:0016:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0017:: 2001:4860:4801:0017:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0018:: 2001:4860:4801:0018:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0019:: 2001:4860:4801:0019:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:001a:: 2001:4860:4801:001a:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:001b:: 2001:4860:4801:001b:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:001c:: 2001:4860:4801:001c:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:001d:: 2001:4860:4801:001d:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:001e:: 2001:4860:4801:001e:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:001f:: 2001:4860:4801:001f:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0020:: 2001:4860:4801:0020:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0021:: 2001:4860:4801:0021:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0022:: 2001:4860:4801:0022:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0023:: 2001:4860:4801:0023:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0024:: 2001:4860:4801:0024:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0025:: 2001:4860:4801:0025:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0026:: 2001:4860:4801:0026:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0027:: 2001:4860:4801:0027:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0028:: 2001:4860:4801:0028:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0029:: 2001:4860:4801:0029:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0002:: 2001:4860:4801:0002:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:002a:: 2001:4860:4801:002a:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:002b:: 2001:4860:4801:002b:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:002c:: 2001:4860:4801:002c:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:002d:: 2001:4860:4801:002d:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:002e:: 2001:4860:4801:002e:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:002f:: 2001:4860:4801:002f:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0030:: 2001:4860:4801:0030:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0031:: 2001:4860:4801:0031:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0032:: 2001:4860:4801:0032:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0033:: 2001:4860:4801:0033:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0034:: 2001:4860:4801:0034:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0035:: 2001:4860:4801:0035:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0036:: 2001:4860:4801:0036:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0037:: 2001:4860:4801:0037:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0038:: 2001:4860:4801:0038:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0039:: 2001:4860:4801:0039:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:003a:: 2001:4860:4801:003a:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:003b:: 2001:4860:4801:003b:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:003c:: 2001:4860:4801:003c:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:003d:: 2001:4860:4801:003d:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:003e:: 2001:4860:4801:003e:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:003f:: 2001:4860:4801:003f:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0040:: 2001:4860:4801:0040:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0041:: 2001:4860:4801:0041:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0042:: 2001:4860:4801:0042:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0044:: 2001:4860:4801:0044:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0045:: 2001:4860:4801:0045:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0046:: 2001:4860:4801:0046:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0047:: 2001:4860:4801:0047:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0048:: 2001:4860:4801:0048:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0049:: 2001:4860:4801:0049:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:004a:: 2001:4860:4801:004a:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:004b:: 2001:4860:4801:004b:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:004c:: 2001:4860:4801:004c:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:004d:: 2001:4860:4801:004d:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:004e:: 2001:4860:4801:004e:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0050:: 2001:4860:4801:0050:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0051:: 2001:4860:4801:0051:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0052:: 2001:4860:4801:0052:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0053:: 2001:4860:4801:0053:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0054:: 2001:4860:4801:0054:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0055:: 2001:4860:4801:0055:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0056:: 2001:4860:4801:0056:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0057:: 2001:4860:4801:0057:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0058:: 2001:4860:4801:0058:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0060:: 2001:4860:4801:0060:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0061:: 2001:4860:4801:0061:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0062:: 2001:4860:4801:0062:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0063:: 2001:4860:4801:0063:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0064:: 2001:4860:4801:0064:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0065:: 2001:4860:4801:0065:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0066:: 2001:4860:4801:0066:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0067:: 2001:4860:4801:0067:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0068:: 2001:4860:4801:0068:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0069:: 2001:4860:4801:0069:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:006a:: 2001:4860:4801:006a:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:006b:: 2001:4860:4801:006b:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:006c:: 2001:4860:4801:006c:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:006d:: 2001:4860:4801:006d:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:006e:: 2001:4860:4801:006e:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:006f:: 2001:4860:4801:006f:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0070:: 2001:4860:4801:0070:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0071:: 2001:4860:4801:0071:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0072:: 2001:4860:4801:0072:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0073:: 2001:4860:4801:0073:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0074:: 2001:4860:4801:0074:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0075:: 2001:4860:4801:0075:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0076:: 2001:4860:4801:0076:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0077:: 2001:4860:4801:0077:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0078:: 2001:4860:4801:0078:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0079:: 2001:4860:4801:0079:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:007a:: 2001:4860:4801:007a:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:007b:: 2001:4860:4801:007b:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:007c:: 2001:4860:4801:007c:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:007d:: 2001:4860:4801:007d:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0080:: 2001:4860:4801:0080:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0081:: 2001:4860:4801:0081:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0082:: 2001:4860:4801:0082:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0083:: 2001:4860:4801:0083:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0084:: 2001:4860:4801:0084:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0085:: 2001:4860:4801:0085:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0086:: 2001:4860:4801:0086:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0087:: 2001:4860:4801:0087:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0088:: 2001:4860:4801:0088:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0090:: 2001:4860:4801:0090:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0091:: 2001:4860:4801:0091:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0092:: 2001:4860:4801:0092:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0093:: 2001:4860:4801:0093:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0094:: 2001:4860:4801:0094:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0095:: 2001:4860:4801:0095:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0096:: 2001:4860:4801:0096:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:0097:: 2001:4860:4801:0097:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a0:: 2001:4860:4801:00a0:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a1:: 2001:4860:4801:00a1:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a2:: 2001:4860:4801:00a2:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a3:: 2001:4860:4801:00a3:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a4:: 2001:4860:4801:00a4:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a5:: 2001:4860:4801:00a5:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a6:: 2001:4860:4801:00a6:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a7:: 2001:4860:4801:00a7:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a8:: 2001:4860:4801:00a8:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00a9:: 2001:4860:4801:00a9:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00aa:: 2001:4860:4801:00aa:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00ab:: 2001:4860:4801:00ab:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00ac:: 2001:4860:4801:00ac:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00ad:: 2001:4860:4801:00ad:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00ae:: 2001:4860:4801:00ae:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00b0:: 2001:4860:4801:00b0:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00b1:: 2001:4860:4801:00b1:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00b2:: 2001:4860:4801:00b2:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00b3:: 2001:4860:4801:00b3:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00b4:: 2001:4860:4801:00b4:FFFF:FFFF:FFFF:FFFF 64
|
||||
2001:4860:4801:00b5:: 2001:4860:4801:00b5:FFFF:FFFF:FFFF:FFFF 64
|
||||
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
|
||||
|
||||
# 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
|
||||
@@ -7,7 +7,7 @@
|
||||
{"id": "googlebot",
|
||||
"n": "GoogleBot",
|
||||
"r": ["Googlebot"],
|
||||
"rx": ["Googlebot\\/(\\d+\\.\\d+)", "Googlebot-Image\\/(\\d+\\.\\d+)"],
|
||||
"rx": ["Googlebot\\/(\\d+\\.\\d+)", "Googlebot-Image\\/(\\d+\\.\\d+)","\\sGoogleOther(\\-\\w+)?[\\)\\/]"],
|
||||
"url": "http://www.google.com/bot.html"
|
||||
},
|
||||
{"id": "googleads",
|
||||
@@ -22,12 +22,6 @@
|
||||
"rx": ["APIs-Google"],
|
||||
"url": "https://developers.google.com/search/docs/crawling-indexing/google-special-case-crawlers"
|
||||
},
|
||||
{"id": "googleother",
|
||||
"n": "GoogleOther",
|
||||
"r": ["GoogleOther"],
|
||||
"rx": ["\\sGoogleOther(\\-\\w+)?[\\)\\/]"],
|
||||
"url": "https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers#googleother"
|
||||
},
|
||||
{"id": "googinspct",
|
||||
"n": "Google-InspectionTool",
|
||||
"r": ["Google-InspectionTool"],
|
||||
@@ -118,16 +112,16 @@
|
||||
"rx": ["Perplexity\\-User\\/(\\d+\\.\\d+);"],
|
||||
"url": "https://perplexity.ai/perplexitybot"
|
||||
},
|
||||
{"id": "metabots",
|
||||
"n": "Meta/Facebook",
|
||||
"r": ["meta-webindexer","meta-externalads","meta-externalagent"],
|
||||
"rx": ["facebook[cw]\\w+\\/(\\d+\\.\\d+)", "meta-externala\\w+\\/(\\d+\\.\\d+)"],
|
||||
"url": "https://developers.facebook.com/docs/sharing/webmasters/crawler"
|
||||
},
|
||||
{"id": "metauser",
|
||||
"n": "Meta/Facebook User",
|
||||
"r": ["facebookexternalhit","facebookcatalog"],
|
||||
"rx": ["facebookexternalhit\\/(\\d+\\.?\\d*)", "meta-externalfetcher\\/(\\d\\.\\d)", "facebookcatalog\\/(\\d+\\.?\\d*)"],
|
||||
"rx": ["facebookexternalhit\\/(\\d+\\.?\\d*)", "facebookcatalog\\/(\\d\\.?\\d*)"],
|
||||
"url": "https://developers.facebook.com/docs/sharing/webmasters/crawler"
|
||||
},
|
||||
{"id": "metabots",
|
||||
"n": "Meta/Facebook",
|
||||
"r": ["meta-webindexer","meta-externalads","meta-externalagent", "meta-webindexer"],
|
||||
"rx": ["facebook[cw]\\w+\\/(\\d+\\.?\\d*)", "meta\\-[cw]\\w+\\/(\\d+\\.?\\d*)", "meta-externalads\\/(\\d+\\.?\\d*)", "meta-externalagent\\/(\\d+\\.?\\d*)"],
|
||||
"url": "https://developers.facebook.com/docs/sharing/webmasters/crawler"
|
||||
},
|
||||
{"id": "qwant",
|
||||
|
||||
@@ -87,12 +87,24 @@
|
||||
"id": "undici",
|
||||
"rx": ["undici"]
|
||||
},
|
||||
{"n": "GoHTTP-based crawler",
|
||||
{"n": "GoHTTP-based bot",
|
||||
"id": "gohttp",
|
||||
"rx": ["Go\\-http\\-client\\/(\\d+)", "quic\\-go\\-HTTP\\/(\\d+)"]
|
||||
},
|
||||
{"n": "Python URLlib-based crawler",
|
||||
"id": "python",
|
||||
"rx": ["Python\\-?\\w*\\/(\\d+\\.?\\d*)"]
|
||||
},
|
||||
{"n": "AppleWebKit",
|
||||
"id": "webkit",
|
||||
"rx": [ "AppleWebKit\\/(\\d*)\\." ]
|
||||
},
|
||||
{"n": "wget",
|
||||
"id": "wget",
|
||||
"rx": [ "Wget\\/(\\d+\\.?\\d*\\.?\\d*)" ]
|
||||
},
|
||||
{"n": "PrivacyBrowser",
|
||||
"id": "privacybrowser",
|
||||
"rx": [ "PrivacyBrowser\\/(\\d+\\.?\\d*)" ]
|
||||
}
|
||||
]
|
||||
@@ -1,23 +1,25 @@
|
||||
{
|
||||
"groups": [
|
||||
{"id": "alibaba", "name": "Alibaba"},
|
||||
{"id": "amazon", "name": "Amazon DS"},
|
||||
{"id": "brasilnet", "name": "BrasilNet"},
|
||||
{"id": "charter", "name": "Charter Inc."},
|
||||
{"id": "chinanet", "name": "Chinanet"},
|
||||
{"id": "cnisp", "name": "China ISP"},
|
||||
{"id": "alibaba", "name": "Alibaba Network"},
|
||||
{"id": "amazon", "name": "Amazon Data Centres"},
|
||||
{"id": "bezeq", "name": "Bezeq Int."},
|
||||
{"id": "charter", "name": "Charter Inc. Range"},
|
||||
{"id": "chinanet", "name": "ChinaNet"},
|
||||
{"id": "cnisp", "name": "China ISP Range"},
|
||||
{"id": "cnmob", "name": "China Mobile"},
|
||||
{"id": "google", "name": "Google LLC"},
|
||||
{"id": "domtehniki", "name": "Dom Tehniki / WS Telecom"},
|
||||
{"id": "google", "name": "Google LLC Network"},
|
||||
{"id": "hetzner", "name": "Hetzner US"},
|
||||
{"id": "huawei", "name": "Huawei"},
|
||||
{"id": "misc_sa", "name": "Misc. SA ISPs"},
|
||||
{"id": "tencent", "name": "Tencent"},
|
||||
{"id": "huawei", "name": "Huawei Network"},
|
||||
{"id": "netcup", "name": "netcup GmbH"},
|
||||
{"id": "tencent", "name": "Tencent Network"},
|
||||
{"id": "unicom", "name": "China Unicom"},
|
||||
{"id": "vnpt", "name": "Vietnam Telecom"},
|
||||
{"id": "vdsina", "name": "VDSina NL"},
|
||||
{"id": "zenlayer", "name": "Zenlayer"}
|
||||
{"id": "vdsina", "name": "VDSina Network"},
|
||||
{"id": "zenlayer", "name": "Zenlayer Network"}
|
||||
],
|
||||
"ranges": [
|
||||
{"from": "1.92.0.0", "to": "1.95.255.254", "m": 14, "g": "huawei"},
|
||||
{"from": "3.0.0.0", "to": "3.255.255.254", "m": 8, "g": "amazon"},
|
||||
{"from": "5.161.0.0", "to": "5.161.255.255", "m": 16, "g": "hetzner"},
|
||||
{"from": "8.128.0.0", "to": "8.191.255.254", "m": 10, "g": "alibaba"},
|
||||
@@ -32,6 +34,7 @@
|
||||
{"from": "39.64.0.0", "to": "39.95.255.254", "g": "cnmob"},
|
||||
{"from": "43.132.0.0", "to": "43.132.255.254", "m": 16, "g": "tencent"},
|
||||
{"from": "43.133.0.0", "to": "43.133.255.254", "m": 16, "g": "tencent"},
|
||||
{"from": "43.173.128.0", "to": "43.191.255.255", "m": 10, "g": "tencent"},
|
||||
{"from": "44.192.0.0", "to": "44.255.255.254", "m": 16, "g": "tencent"},
|
||||
{"from": "45.0.0.0", "to": "45.255.255.254", "m": 10, "g": "amazon"},
|
||||
{"from": "46.250.160.0", "to": "46.250.191.254", "m": 19, "g": "huawei"},
|
||||
@@ -44,7 +47,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"},
|
||||
@@ -53,41 +56,29 @@
|
||||
{"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": "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": "119.8.0.0", "to": "119.8.255.254", "m": 16, "g": "huawei"},
|
||||
{"from": "119.12.160.0", "to": "119.12.175.255", "m": 20, "g": "huawei"},
|
||||
{"from": "119.13.0.0", "to": "119.13.255.254", "m": 16, "g": "huawei"},
|
||||
{"from": "121.91.168.0", "to": "121.91.175.254", "m": 21, "g": "huawei"},
|
||||
{"from": "122.8.0.0", "to": "122.8.255.254", "g": "cnisp"},
|
||||
{"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": "124.243.128.0", "to": "124.243.191.254", "m": 18, "g": "huawei"},
|
||||
{"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": "136.107.0.0", "to": "136.125.255.254", "m": "+", "g": "google"},
|
||||
{"from": "142.147.128.0", "to": "1142.147.255.254", "m": 17, "g": "w2obj"},
|
||||
{"from": "146.174.128.0", "to": "146.174.191.254", "m": 18, "g": "huawei"},
|
||||
{"from": "149.126.192.0", "to": "149.126.223.255", "m": 19, "g": "domtehniki"},
|
||||
{"from": "149.232.128.0", "to": "149.232.159.254", "m": 19, "g": "huawei"},
|
||||
{"from": "150.40.128.0", "to": "150.40.255.254", "m": 17, "g": "huawei"},
|
||||
{"from": "159.138.0.0", "to": "159.138.225.254", "m": 16, "g": "huawei"},
|
||||
{"from": "162.128.0.0", "to": "162.128.255.254", "m": 16, "g": "zenlayer"},
|
||||
{"from": "166.108.192.0", "to": "166.108.255.254", "m": 18, "g": "huawei"},
|
||||
{"from": "168.232.192.0", "to": "168.232.255.254", "m": 16, "g": "misc_sa"},
|
||||
{"from": "170.0.0.0", "to": "170.3.255.254", "m": 14, "g": "misc_sa"},
|
||||
{"from": "170.80.0.0", "to": "170.83.255.254", "m": 14, "g": "misc_sa"},
|
||||
{"from": "170.254.0.0", "to": "170.254.255.254", "m": 16, "g": "misc_sa"},
|
||||
{"from": "171.224.0.0", "to": "171.239.255.254", "m": 12, "g": "viettel"},
|
||||
{"from": "177.0.0.0", "to": "177.255.255.254", "m": 8, "g": "brasilnet"},
|
||||
{"from": "178.156.128.0", "to": "178.156.255.254", "m": 17, "g": "hetzner"},
|
||||
{"from": "179.0.0.0", "to": "179.255.255.254", "m": 8, "g": "brasilnet"},
|
||||
{"from": "181.0.0.0", "to": "181.255.255.254", "m": 8, "g": "misc_sa"},
|
||||
{"from": "183.87.32.0", "to": "183.87.63.254", "m": 19, "g": "huawei"},
|
||||
{"from": "186.0.0.0", "to": "186.255.255.254", "m": 8, "g": "misc_sa"},
|
||||
{"from": "187.0.0.0", "to": "187.255.255.254", "m": 8, "g": "misc_sa"},
|
||||
{"from": "188.0.0.0", "to": "188.255.255.254", "m": 8, "g": "misc_sa"},
|
||||
{"from": "189.0.0.0", "to": "189.255.255.254", "m": 8, "g": "misc_sa"},
|
||||
{"from": "190.0.0.0", "to": "190.255.255.254", "m": 8, "g": "misc_sa"},
|
||||
{"from": "191.0.0.0", "to": "191.255.255.254", "m": 8, "g": "misc_sa"},
|
||||
{"from": "192.124.170.0", "to": "192.124.182.254", "g": "relcom"},
|
||||
{"from": "200.0.0.0", "to": "200.255.255.254", "m": 8, "g": "misc_sa"},
|
||||
{"from": "201.0.0.0", "to": "201.255.255.254", "m": 8, "g": "misc_sa"},
|
||||
{"from": "212.95.128.0", "to": "212.95.159.254", "m": 19, "g": "asiacell"},
|
||||
{"from": "222.252.0.0", "to": "222.252.255.254", "m": 14, "g": "vnpt"},
|
||||
{"from": "2001:4860::::::", "to": "2001:4860:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "g": "google"},
|
||||
@@ -97,7 +88,6 @@
|
||||
{"from": "2603:6010::::::", "to": "2603:6010:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "g": "charter"},
|
||||
{"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": "2a0a:4cc0::::::", "to": "2a0a:4cc0:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "g": "netcup"}
|
||||
]
|
||||
}
|
||||
BIN
img/addr.png
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.1 KiB |
139
img/busy-light.svg
Normal file
@@ -0,0 +1,139 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
|
||||
<style>.box{fill:#00CADB;transform-origin: 50% 50%}
|
||||
@keyframes box-1 {
|
||||
9% {transform: translate(-12px,0)}
|
||||
18% {transform: translate(0px,0)}
|
||||
27% {transform: translate(0px,0)}
|
||||
36% {transform: translate(12px,0)}
|
||||
45% {transform: translate(12px,12px)}
|
||||
55% {transform: translate(12px,12px)}
|
||||
64% {transform: translate(12px,12px)}
|
||||
73% {transform: translate(12px,0px)}
|
||||
82% {transform: translate(0px,0px)}
|
||||
91% {transform: translate(-12px,0px)}
|
||||
100% {transform: translate(0px,0px)}
|
||||
}
|
||||
.box:nth-child(1){animation: box-1 4s infinite}
|
||||
@keyframes box-2 {
|
||||
9% {transform: translate(0,0)}
|
||||
18% {transform: translate(12px,0)}
|
||||
27% {transform: translate(0px,0)}
|
||||
36% {transform: translate(12px,0)}
|
||||
45% {transform: translate(12px,12px)}
|
||||
55% {transform: translate(12px,12px)}
|
||||
64% {transform: translate(12px,12px)}
|
||||
73% {transform: translate(12px,12px)}
|
||||
82% {transform: translate(0px,12px)}
|
||||
91% {transform: translate(0px,12px)}
|
||||
100% {transform: translate(0px,0px)}
|
||||
}
|
||||
.box:nth-child(2){animation: box-2 4s infinite}
|
||||
@keyframes box-3 {
|
||||
9% {transform: translate(-12px,0)}
|
||||
18% {transform: translate(-12px,0)}
|
||||
27% {transform: translate(0px,0)}
|
||||
36% {transform: translate(-12px,0)}
|
||||
45% {transform: translate(-12px,0)}
|
||||
55% {transform: translate(-12px,0)}
|
||||
64% {transform: translate(-12px,0)}
|
||||
73% {transform: translate(-12px,0)}
|
||||
82% {transform: translate(-12px,-12px)}
|
||||
91% {transform: translate(0px,-12px)}
|
||||
100% {transform: translate(0px,0px)}
|
||||
}
|
||||
.box:nth-child(3) {animation: box-3 4s infinite}
|
||||
@keyframes box-4 {
|
||||
9% {transform: translate(-12px,0)}
|
||||
18% {transform: translate(-12px,0)}
|
||||
27% {transform: translate(-12px,-12px)}
|
||||
36% {transform: translate(0px,-12px)}
|
||||
45% {transform: translate(0px,0px)}
|
||||
55% {transform: translate(0px,-12px)}
|
||||
64% {transform: translate(0px,-12px)}
|
||||
73% {transform: translate(0px,-12px)}
|
||||
82% {transform: translate(-12px,-12px)}
|
||||
91% {transform: translate(-12px,0px)}
|
||||
100% {transform: translate(0px,0px)}
|
||||
}
|
||||
.box:nth-child(4) {animation: box-4 4s infinite}
|
||||
@keyframes box-5 {
|
||||
9% {transform: translate(0,0)}
|
||||
18% {transform: translate(0,0)}
|
||||
27% {transform: translate(0,0)}
|
||||
36% {transform: translate(12px,0)}
|
||||
45% {transform: translate(12px,0)}
|
||||
55% {transform: translate(12px,0)}
|
||||
64% {transform: translate(12px,0)}
|
||||
73% {transform: translate(12px,0)}
|
||||
82% {transform: translate(12px,-12px)}
|
||||
91% {transform: translate(0px,-12px)}
|
||||
100% {transform: translate(0px,0px)}
|
||||
}
|
||||
.box:nth-child(5) {animation: box-5 4s infinite}
|
||||
@keyframes box-6 {
|
||||
9% {transform: translate(0,0)}
|
||||
18% {transform: translate(-12px,0)}
|
||||
27% {transform: translate(-12px,0)}
|
||||
36% {transform: translate(0px,0)}
|
||||
45% {transform: translate(0px,0)}
|
||||
55% {transform: translate(0px,0)}
|
||||
64% {transform: translate(0px,0)}
|
||||
73% {transform: translate(0px,12px)}
|
||||
82% {transform: translate(-12px,12px)}
|
||||
91% {transform: translate(-12px,0px)}
|
||||
100% {transform: translate(0px,0px)}
|
||||
}
|
||||
.box:nth-child(6) {animation: box-6 4s infinite}
|
||||
@keyframes box-7 {
|
||||
9% {transform: translate(12px,0)}
|
||||
18% {transform: translate(12px,0)}
|
||||
27% {transform: translate(12px,0)}
|
||||
36% {transform: translate(0px,0)}
|
||||
45% {transform: translate(0px,-12px)}
|
||||
55% {transform: translate(12px,-12px)}
|
||||
64% {transform: translate(0px,-12px)}
|
||||
73% {transform: translate(0px,-12px)}
|
||||
82% {transform: translate(0px,0px)}
|
||||
91% {transform: translate(12px,0px)}
|
||||
100% {transform: translate(0px,0px)}
|
||||
}
|
||||
.box:nth-child(7) {animation: box-7 4s infinite}
|
||||
@keyframes box-8 {
|
||||
9% {transform: translate(0,0)}
|
||||
18% {transform: translate(-12px,0)}
|
||||
27% {transform: translate(-12px,-12px)}
|
||||
36% {transform: translate(0px,-12px)}
|
||||
45% {transform: translate(0px,-12px)}
|
||||
55% {transform: translate(0px,-12px)}
|
||||
64% {transform: translate(0px,-12px)}
|
||||
73% {transform: translate(0px,-12px)}
|
||||
82% {transform: translate(12px,-12px)}
|
||||
91% {transform: translate(12px,0px)}
|
||||
100% {transform: translate(0px,0px)}
|
||||
}
|
||||
.box:nth-child(8){animation: box-8 4s infinite}
|
||||
@keyframes box-9{
|
||||
9% {transform: translate(-12px,0)}
|
||||
18% {transform: translate(-12px,0)}
|
||||
27% {transform: translate(0px,0)}
|
||||
36% {transform: translate(-12px,0)}
|
||||
45% {transform: translate(0px,0)}
|
||||
55% {transform: translate(0px,0)}
|
||||
64% {transform: translate(-12px,0)}
|
||||
73% {transform: translate(-12px,0)}
|
||||
82% {transform: translate(-24px,0)}
|
||||
91% {transform: translate(-12px,0)}
|
||||
100% {transform: translate(0px,0)}
|
||||
}
|
||||
.box:nth-child(9) {animation: box-9 4s infinite}
|
||||
</style>
|
||||
<g><rect class="box" x="13" y="1" rx="1" width="10" height="10"/>
|
||||
<rect class="box" x="13" y="1" rx="1" width="10" height="10"/>
|
||||
<rect class="box" x="25" y="25" rx="1" width="10" height="10"/>
|
||||
<rect class="box" x="13" y="13" rx="1" width="10" height="10"/>
|
||||
<rect class="box" x="13" y="13" rx="1" width="10" height="10"/>
|
||||
<rect class="box" x="25" y="13" rx="1" width="10" height="10"/>
|
||||
<rect class="box" x="1" y="25" rx="1" width="10" height="10"/>
|
||||
<rect class="box" x="13" y="25" rx="1" width="10" height="10"/>
|
||||
<rect class="box" x="25" y="25" rx="1" width="10" height="10"/></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
BIN
img/captcha.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
img/clients.png
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 26 KiB |
BIN
img/idtyp.png
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
img/stages.png
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 4.1 KiB |
1
img/views.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>eye-outline</title><path d="M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M12,4.5C17,4.5 21.27,7.61 23,12C21.27,16.39 17,19.5 12,19.5C7,19.5 2.73,16.39 1,12C2.73,7.61 7,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C15.76,17.5 19.17,15.36 20.82,12C19.17,8.64 15.76,6.5 12,6.5C8.24,6.5 4.83,8.64 3.18,12Z" style="fill:#777"/></svg>
|
||||
|
After Width: | Height: | Size: 419 B |
15
lang/de/lang.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* German language file for BotMon plugin
|
||||
*
|
||||
* @author Sascha Leib <ad@hominem.info>
|
||||
*/
|
||||
|
||||
// Captcha dialog locale strings:
|
||||
$lang['bm_dlgTitle'] = 'Benutzerüberprüfung';
|
||||
$lang['bm_dlgSubtitle'] = 'Bitte bestätige, dass du kein Bot bist:';
|
||||
$lang['bm_dlgConfirm'] = 'Klicke, um zu bestätigen.';
|
||||
$lang['bm_dlgChecking'] = 'Wird überprüft …';
|
||||
$lang['bm_dlgLoading'] = 'Seite wird geladen …';
|
||||
$lang['bm_dlgError'] = 'Es ist ein Fehler aufgetreten.';
|
||||
$lang['bm_noJsWarning'] = 'Bitte aktivieren Sie JavaScript, um diese Seite anzuzeigen.';
|
||||
2734
lang/de/wordlist.txt
Normal file
15
lang/en/lang.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* English language file for BotMon plugin
|
||||
*
|
||||
* @author Sascha Leib <ad@hominem.info>
|
||||
*/
|
||||
|
||||
// Captcha dialog locale strings:
|
||||
$lang['bm_dlgTitle'] = 'User Check';
|
||||
$lang['bm_dlgSubtitle'] = 'Please confirm you are not a bot:';
|
||||
$lang['bm_dlgConfirm'] = 'Click to confirm.';
|
||||
$lang['bm_dlgChecking'] = 'Checking …';
|
||||
$lang['bm_dlgLoading'] = 'Loading page …';
|
||||
$lang['bm_dlgError'] = 'An error occured.';
|
||||
$lang['bm_noJsWarning'] = 'This page requires JavaScript to be enabled.';
|
||||
@@ -5,6 +5,22 @@
|
||||
* @author Sascha Leib <sascha@leib.be>
|
||||
*/
|
||||
|
||||
$lang['showday'] = 'Which data to show in the “Latest” tab:';
|
||||
$lang['showday_o_yesterday'] = 'Last full day (yesterday)';
|
||||
$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_o_disabled'] = 'Disabled';
|
||||
$lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module';
|
||||
$lang['geoiplib_o_disabled'] = 'Disabled';
|
||||
$lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module';
|
||||
|
||||
$lang['useCaptcha'] = 'Enable Captcha<br><small>(Experimental, read the manual first!)</small>';
|
||||
$lang['useCaptcha_o_disabled'] = 'Disabled';
|
||||
$lang['useCaptcha_o_loremipsum'] = 'Captcha with Lorem ipsum text';
|
||||
$lang['useCaptcha_o_dada'] = 'Captcha with Dada placeholder text';
|
||||
|
||||
$lang['captchaSeed'] = 'Captcha Seed<br><small>(Enter a random string, e.g. <a href="https://www.browserling.com/tools/random-hex" target="_blank">from here</a>)</small>';
|
||||
|
||||
$lang['captchaBypass'] = 'Automatically solve the Captcha, if …<br><small>(Note: this probably does not make much sense for English wikis!)</small>';
|
||||
$lang['captchaBypass_langmatch'] = 'Client and page languages match ';
|
||||
|
||||
3050
lang/en/wordlist.txt
Normal file
9
lang/fr/lang.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* French language file for BotMon plugin
|
||||
*
|
||||
* @author Sascha Leib <ad@hominem.info>
|
||||
*/
|
||||
|
||||
// Captcha dialog locale strings:
|
||||
$lang['bm_noJsWarning'] = 'Veuillez activer JavaScript pour afficher cette page.';
|
||||
3333
lang/fr/wordlist.txt
Normal file
1577
lang/la/wordlist.txt
Normal file
@@ -1,7 +1,7 @@
|
||||
base botmon
|
||||
author Sascha Leib
|
||||
email ad@hominem.com
|
||||
date 2025-10-20
|
||||
date 2025-12-06
|
||||
name Bot Monitoring
|
||||
desc A tool for monitoring and analysing bot traffic to your wiki (under development)
|
||||
desc A tool for monitoring and blocking bot traffic to your wiki (under development)
|
||||
url https://www.dokuwiki.org/plugin:botmon
|
||||
|
||||
165
style.less
@@ -1 +1,164 @@
|
||||
/* This file is no longer in use */
|
||||
body.botmon_captcha {
|
||||
main {
|
||||
h1 {
|
||||
color: transparent !important;
|
||||
text-shadow: 0 0 .25em rgba(0,.0,0,.8);
|
||||
}
|
||||
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 {
|
||||
& {
|
||||
position: fixed;
|
||||
width: 400px;
|
||||
height: 220px;
|
||||
top: ~"calc(50vh - 110px)";
|
||||
left: ~"calc(50vw - 200px)";
|
||||
background: #1C1B22 none;
|
||||
border: #7C7B82 solid 1pt;
|
||||
border-radius: 12px;
|
||||
margin: 0; padding: 18px;
|
||||
font-family: system-ui, sans-serif;
|
||||
box-shadow: .25rem .25rem .5rem rgba(0,0,0,.5);
|
||||
box-sizing: border-box;
|
||||
z-index: 10001;
|
||||
}
|
||||
& * {
|
||||
color: #EDEDF5;
|
||||
margin: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
padding: 0 0 12px 0;
|
||||
}
|
||||
p {
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
padding: 6px 0;
|
||||
}
|
||||
label {
|
||||
& {
|
||||
display: grid;
|
||||
grid-template-columns: 32px auto;
|
||||
column-gap: 8px;
|
||||
align-items: center;
|
||||
padding: 24px;
|
||||
margin: 16px 0 0 0;
|
||||
background-color: #32313A;
|
||||
border: #7C7B82 solid 1px;
|
||||
border-radius: 3px;
|
||||
font-size: 16px;
|
||||
}
|
||||
span.busy {
|
||||
display: inline-block;
|
||||
width: 24px; height: 24px;
|
||||
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;
|
||||
border: 2px solid #00CADB;
|
||||
border-radius: 4px;
|
||||
appearance: none;
|
||||
}
|
||||
input[type="checkbox"]:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 6px rgba(0, 202, 219, .8);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:disabled {
|
||||
display: none;
|
||||
}
|
||||
&.checking {
|
||||
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, 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, 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes delayed-fade-in {
|
||||
0% { opacity: 0; }
|
||||
50% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
// no js warning
|
||||
#BM__NoJSWarning {
|
||||
position: fixed;
|
||||
bottom: calc(50vh - 2.5rem);
|
||||
width: 100%; max-width: fit-content;
|
||||
margin: 0 auto;
|
||||
padding: .25rem 1rem;
|
||||
border-radius: .5rem;
|
||||
border: red solid 2pt;
|
||||
box-shadow: rgba(128, 0, 0, 0.5) .25rem .25rem .5rem;
|
||||
animation: delayed-fade-in 4s forwards;
|
||||
}
|
||||
|
||||
// captcha on smaller screens:
|
||||
@media (max-width: 480px) {
|
||||
body.botmon_captcha #botmon_captcha_box {
|
||||
width: 100vw;
|
||||
height: auto;
|
||||
left: 0; top: 80px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* dark mode overrides */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body.darkmode.botmon_captcha {
|
||||
main {
|
||||
h1 {
|
||||
text-shadow: 0 0 .25em rgba(170,170,170,.75);
|
||||
}
|
||||
p, h2, h3, h4, h5, h6 {
|
||||
text-shadow: 0 0 .35em rgba(170,170,170,.75);
|
||||
}
|
||||
}
|
||||
#botmon_captcha_box {
|
||||
& {
|
||||
background-color: #FFF;
|
||||
border-color: #9F9EA1;
|
||||
box-shadow: .25rem .25rem .5rem rgba(0,0,0,.25);
|
||||
}
|
||||
& * {
|
||||
color: #15141A;
|
||||
}
|
||||
label {
|
||||
background-color: #EEE;
|
||||
border-color: #9F9EA1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||