From 92cee555c7937faa4cde22c543ffa1c9844eb81b Mon Sep 17 00:00:00 2001 From: pezcurrel Date: Thu, 1 Dec 2022 05:41:54 +0100 Subject: [PATCH] Removed support to Blacklist table --- web/clitools/crawler.php | 501 ++++++++++++++++++++------------------- 1 file changed, 254 insertions(+), 247 deletions(-) diff --git a/web/clitools/crawler.php b/web/clitools/crawler.php index 87cde27..8e7cfdc 100755 --- a/web/clitools/crawler.php +++ b/web/clitools/crawler.php @@ -20,37 +20,43 @@ const N="\n"; require(__DIR__.'/../site/mustard/include/getfc.php'); -require(__DIR__.'/../site/vendor/autoload.php'); +require(__DIR__.'/lib/vendor/autoload.php'); use LanguageDetection\Language; -if (strtoupper(substr(PHP_OS,0,3))==='WIN') - $iswin=true; -else - $iswin=false; +(strtoupper(substr(PHP_OS,0,3))==='WIN') ? $iswin=true : $iswin=false; $link=false; $jsonf=false; +function eecho($lev,$msg) { + $time=microtime(false); + $time=explode(' ',$time); + $time=date('Y-m-d H:i:s',$time[1]).'.'.substr($time[0],2); + $levs=['Debug', 'Info', 'Warning', 'Error']; + $msg=$time.' '.$levs[$lev].': '.$msg; + if ($lev<2) + echo($msg); + else + fwrite(STDERR,$msg); +} + +function mexit($msg,$code) { + global $link, $jsonf, $lockfp; + if ($link) mysqli_close($link); + if ($jsonf) fclose($jsonf); + if (isset($lockfp) && file_exists($lockfp)) unlink($lockfp); + if ($code!=0) + eecho(3,$msg); + else + eecho(1,$msg); + exit($code); +} + declare(ticks=1); if (function_exists('pcntl_signal')) { function signalHandler($signal) { - global $link, $jsonf, $lockfp; - echo(N.'Sono stato interrotto.'.N); - if ($link) { - echo('La connessione MySQL è aperta, la chiudo.'.N); - mysqli_close($link); - } - if ($jsonf) { - echo('Il file di dump json è aperto, lo chiudo.'.N); - // qui no, altrimenti "riprendi" fa poi casino - //fwrite($jsonf,'"Fine?": true'.N.'}'.N); - fclose($jsonf); - } - if (isset($lockfp) && file_exists($lockfp)) { - echo('Il file di lock esiste, lo elimino.'.N); - unlink($lockfp); - } - exit(2); + echo(N); + mexit('received signal «'.$signal.'», shutting down.'.N,0); } pcntl_signal(SIGTERM,'signalHandler');// Termination ('kill' was called) pcntl_signal(SIGHUP,'signalHandler');// Terminal log-out @@ -59,8 +65,9 @@ if (function_exists('pcntl_signal')) { $opts=array( 'timeout'=>5, - 'deadline'=>60*24*60*60,// se un'istanza non risponde da 60 giorni dichiararla morta - 'ldtoots'=>40,// numero di toots da passare alla funzione di rilevamento automatico della lingua + 'deadline'=>60*24*60*60,// if an instance has not been responding for more than this value of seconds (currently 60 days), declare it dead + 'oldline'=>30*24*60*60,// if an instance has been new for a period longer than this amount (currently 30 days), it's no longer new + 'ldtoots'=>40,// number of toots to check with the automatic language detection function 'setnew'=>true, 'dryrun'=>false, 'jsonfp'=>__DIR__.'/instances.json', @@ -71,41 +78,41 @@ $opts=array( ); $help='crawler.php - DESCRIZIONE - Popola/aggiorna il database di mastostart con i dati che riesce - a recuperare da una lista di istanze composta da quelle già presenti - nel database più quelle di un file specificabile (tipicamente il file - di output di peerscrawl.php). + DESCRIPTION + This script updates mastostart’s database with the data it manages to + retrieve from instances already present in the database plus (optionally) + those listed in a specifiable file (typically the output file from a + peerscrawl.php run). SYNOPSIS - peerscrawl.php [options] + crawler.php [options] OPTIONS -p, --peersfp - Definisce un file da cui caricare la lista delle istanze di cui cercare - di recuperare i dati. Per default non è definito alcun file, quindi - il programma si limita a controllare le istanze già presenti del db. - Nota: questa opzione è ininfluente se il programma viene lanciato - per riprendere un’esecuzione precedente interrotta. - -t, --timeout - Definisce il timeout in secondi di ogni tentativo di connessione. + Sets a file containing a list of instances to consider in addition to those + which are already present in the database. + Note that this option is ignored if the script will recover a previous + unfinished session. + -t, --timeout + Sets the timeout in seconds for every connection attempt. DEFAULT: «'.$opts['timeout'].'» -N, --dontsetnew - Non marca le istanze come nuove, neanche quando lo sono. Può essere utile - per il primo crawl. + If this option is set, the script won’t mark new instances as new. This can + be useful for a first run. -I, --ignorelock - Normalmente, se il suo lockfile esiste, il programma esce con un errore. - Questa opzione fa sì che il lockfile sia ignorato. Attenzione: verifica - che effettivamente il programma non stia già girando prima di usarla. + Normally, if its lockfile exists, the script will exit with an error. + If this option is set, the lockfile existence will be ignored. + Warning: check that the script is actually not running yet before using + this option. -R, --dontrestore - Se sono presenti i file di una sessione precedente non completata - («instances.job» e «currinst.job») ignorali e prosegui (verranno - sovrascritti). + If this option is set and «instances.job» and «currinst.job» files from + a previous unfinished session are present, the script will ignore them + and start a new session. -d, --dryrun - Non scrive nulla nel database. + If this option is set, the script won’t write anything in the database. -j, --jsonwrite - Attiva la scrittura di un file «instances.json» nella stessa directory - di crawler.php, contenente tutti i dati recuperati da tutte le istanze. + If this option is set, the script will write an «instances.json» file + containing all the data it could retrieve from every considered instance. -h, --help - Mostra questo aiuto ed esce. + If this option is set, the script will show this help text and exit. This program comes with ABSOLUTELY NO WARRANTY; for details see the source. This is free software, and you are welcome to redistribute it under @@ -117,14 +124,14 @@ for ($i=1; $i<$argc; $i++) { case '-p': case '--peersfp': if ($i+1>=$argc || !file_exists($argv[$i+1]) || !is_file($argv[$i+1]) || !is_readable($argv[$i+1])) - mexit('L’opzione «'.$argv[$i].'» richiede come parametro un file esistente e leggibile (usa «-h» per vedere la guida).'.N,1); + mexit('option «'.$argv[$i].'» requires an existing and readable file as an argument (use «-h» to read help).'.N,1); $i++; $opts['peersfp']=$argv[$i]; break; case '-t': case '--timeout': if ($i+1>=$argc || preg_match('/^[0-9]+$/',$argv[$i+1])!==1) - mexit('L’opzione «'.$argv[$i].'» richiede un parametro numerico (usa «-h» per vedere la guida).'.N,1); + mexit('option «'.$argv[$i].'» requires a numeric argument (use «-h» to read help).'.N,1); $i++; $opts['timeout']=$argv[$i]+0; break; @@ -150,10 +157,11 @@ for ($i=1; $i<$argc; $i++) { break; case '-h': case '--help': - mexit($help,0); + echo($help); + exit(0); break; default: - mexit('L’opzione «'.$argv[$i].'» è sconosciuta (usa «-h» per vedere la guida).'.N,1); + mexit('option «'.$argv[$i].'» is unknown (use «-h» to read help).'.N,1); break; } } @@ -161,53 +169,41 @@ for ($i=1; $i<$argc; $i++) { use function mysqli_real_escape_string as myesc; -function mexit($msg,$code) { - global $link, $jsonf, $lockfp; - echo($msg); - if ($link) - mysqli_close($link); - if ($jsonf) - fclose($jsonf); - if (isset($lockfp) && file_exists($lockfp)) - unlink($lockfp); - exit($code); -} - $lockfp=__DIR__.'/crawler.lock'; if (file_exists($lockfp) && !$opts['ignorelock']) { - echo('Il file di lock esiste: pare che sia già in corso una sessione; se sei sicur@ che non è così usa «-I» per forzare l’esecuzione.'.N); - exit(2); + eecho(3,'lock file «'.$lockfp.'» exists (if you are sure crawler.php is not already running you can use option «-I» to force execution).'.N); + exit(1); } touch($lockfp); $inifp=__DIR__.'/../conf/mustard.ini'; $iniarr=@parse_ini_file($inifp) - or mexit('Impossibile aprire il file di configurazione «'.$inifp.'»'.N,1); + or mexit('could not open config file «'.$inifp.'»'.N,1); $link=@mysqli_connect($iniarr['db_host'],$iniarr['db_admin_name'],$iniarr['db_admin_password'],$iniarr['db_name'],$iniarr['db_port'],$iniarr['db_socket']) - or mexit('Impossibile connettersi al server MySQL: '.mysqli_connect_error().N,1); + or mexit('could not connect to MySQL server: '.mysqli_connect_error().N,1); mysqli_set_charset($link,'utf8mb4') - or mexit(__LINE__.': '.mysqli_error($link).N,1); + or mexit('could not set «utf8mb4» charset fro MySQL: '.mysqli_error($link).N,1); require(__DIR__.'/../site/mustard/include/tables.php'); $tables=tables($link); //print_r($tables); -$riprendi=false; +$recover=false; $instsjfp=__DIR__.'/instances.job'; $currinstjfp=__DIR__.'/currinst.job'; if (!$opts['dontrestore'] && file_exists($currinstjfp) && file_exists($instsjfp)) { - echo('Pare che ci sia un lavoro in sospeso, provo a riprenderlo...'.N); + eecho(1,'looks like previous session was interrupted, i try to recover it...'.N); $buf=@file($instsjfp,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES) - or mexit('Non ho potuto aprire in lettura il file «'.$instsjfp.'».'.N,1); + or mexit('could not open file «'.$instsjfp.'» for reading.'.N,1); $insts=array(); foreach ($buf as $line) $insts[]=$line; $buf=@file($currinstjfp,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES) - or mexit('Non ho potuto aprire in lettura il file «'.$currinstjfp.'».'.N,1); + or mexit('could not open file «'.$currinstjfp.'» for reading.'.N,1); $buf=explode("\t",$buf[0]); $currinst=array('dom'=>$buf[0], 'i'=>$buf[1], 'qok'=>$buf[2], 'qgood'=>$buf[3]); - $riprendi=true; + $recover=true; } function truncs($str,$tab,$col,$ctx) { @@ -217,7 +213,7 @@ function truncs($str,$tab,$col,$ctx) { $size=$tables[$tab][$col]; $len=mb_strlen($str,'UTF-8'); if ($len>$size) { - notify($ctx.': ho dovuto troncare a '.$size.' caratteri il valore da inserire nella colonna «'.$col.'» della tabella «'.$tab.'» perché troppo lungo ('.$len.' caratteri).',2); + notify($ctx.': had to truncate to '.$size.' characters the value to be inserted into «'.$col.'» column in «'.$tab.'» table because it was too long ('.$len.' characters).',2); $str=mb_substr($str,0,$size-1,'UTF-8').'…'; } return($str); @@ -229,14 +225,14 @@ function truncn($num,$tab,$col,$ctx) { $tab=strtolower($tab); if (is_numeric($num)) { if ($num>$tables[$tab][$col]['max']) { - notify($ctx.': ho dovuto troncare «'.$num.'» al valore massimo «'.$tables[$tab][$col]['max'].'» che può avere nella colonna «'.$col.'» della tabella «'.$tab.'»).',2); + notify($ctx.': had to ceil «'.$num.'» to «'.$tables[$tab][$col]['max'].'», ie the maximum value it can have in column «'.$col.'» of table «'.$tab.'».',2); $num=$tables[$tab][$col]['max']; } elseif ($num<$tables[$tab][$col]['min']) { - notify($ctx.': ho dovuto troncare «'.$num.'» al valore minimo «'.$tables[$tab][$col]['min'].'» che può avere nella colonna «'.$col.'» della tabella «'.$tab.'»).',2); + notify($ctx.': had to floor «'.$num.'» to «'.$tables[$tab][$col]['min'].'», ie the minimum value it can have in column «'.$col.'» of table «'.$tab.'»).',2); $num=$tables[$tab][$col]['min']; } } else { - notify($ctx.': truncn(): mi aspettavo un numero, invece non lo era; ritorno «0».',3); + notify($ctx.': function «truncn»: expecting a number, got something else; returning «0».',3); $num=0; } return($num); @@ -260,67 +256,54 @@ function pgdatetomy($pgdate) { $mtime=$mtime+floatval('0'.$buf[7]); return($mtime); } else { - notify('pgdatetomy: «'.$pgdate.'» non è un formato di data riconosciuto! Ritorno il magico momento attuale.',3); + notify('Function «pgdatetomy»: «'.$pgdate.'» has not a recognized date format; returning current date.',3); return(time()); } } -function blpgdumplinetomy($line) { - $truefalse=array('f'=>0,'t'=>1); - $row=explode("\t",$line); - $row=array('Domain'=>$row[0], - 'CreatedAt'=>pgdatetomy($row[1]), - 'ModifiedAt'=>pgdatetomy($row[2]), - 'Severity'=>$row[3], - 'RejectMedia'=>$truefalse[$row[4]], - 'RejectReports'=>$truefalse[$row[5]], - 'PublicComment'=>$row[6]); - return($row); -} - -if (!$riprendi) { +if (!$recover) { $insts=array(); - echo('Carico le istanze note e vive dal DB e le metto nella lista di quelle da controllare.'.N); $res=mysqli_query($link,'SELECT URI FROM Instances WHERE Dead=0') or mexit(__LINE__.': '.mysqli_error($link).N,3); while($row=mysqli_fetch_assoc($res)) if (!in_array($row['URI'],$insts)) $insts[]=$row['URI']; + eecho(1,'loaded known, alive instances from the database into the list of instances to be checked.'.N); - echo('Creo la lista delle istanze morte.'.N); $res=mysqli_query($link,'SELECT URI FROM Instances WHERE Dead=1') or mexit(__LINE__.': '.mysqli_error($link).N,3); $deadinsts=array(); while($row=mysqli_fetch_assoc($res)) $deadinsts[]=$row['URI']; + eecho(1,'loaded dead instances into the corresponding list.'.N); if (!is_null($opts['peersfp'])) { - echo('Carico le istanze dalla lista «'.$opts['peersfp'].'» e aggiungo alla lista di quelle da controllare quelle che non ci sono già e che non risultano morte.'.N); + eecho(0,'loading other instances to be checked from «'.$opts['peersfp'].'».'.N); $peers=@file($opts['peersfp'],FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES); if ($peers===false) - mexit('Non ho potuto aprire in lettura «'.$opts['peersfp'].'».'.N,1); + mexit('could not open «'.$opts['peersfp'].'» for reading.'.N,1); foreach ($peers as $pdom) { if (!in_array($pdom,$insts)) if (!in_array($pdom,$deadinsts)) if (!willtrunc($pdom,'Instances','URI')) $insts[]=$pdom; else - echo('L’istanza «'.$pdom.'» non sarà considerata perché il suo dominio è troppo lungo per il campo «URI» della tabella «Instances» nel DB.'.N); + eecho(2,'ignoring instance «'.$pdom.'» because its hostname si too long for column «URI» of table «Instances».'.N); else - echo('L’istanza «'.$pdom.'» non sarà considerata perché È MORTA!'.N); + eecho(1,'ignoring instance «'.$pdom.'» because it is dead.'.N); } } sort($insts); // shuffle($insts); - echo('Istanze recuperate: '.count($insts).N); + eecho(1,count($insts).' instances to be checked.'.N); $instsf=@fopen($instsjfp,'w') - or mexit('Non ho potuto aprire in scrittura il file «'.$instsjfp.'».'.N,1); - foreach ($insts as $dom) - fwrite($instsf,$dom.N); + or mexit('could not open «'.$instsjfp.'» for writing.'.N,1); + foreach ($insts as $host) + fwrite($instsf,$host.N); fclose($instsf); } @@ -341,7 +324,7 @@ function b2i($bool,$pre) { else return(0); } else { - notify($pre.'il valore «'.$bool.'» non è booleano, lo assumo come falso e ritorno «0».',3); + notify($pre.'«'.$bool.'» is not a boolean value, returning «0».',3); return(0); } } @@ -376,38 +359,37 @@ function subarimp($glue,$key,&$arr) { function notify($msg,$sev) { global $link, $tables, $iswin, $opts; - echo('NOTIFICAZIÒ: '.strip_tags($msg).N); + eecho(1,'*notification*: '.strip_tags($msg).N); $tab='Notifications'; - if ($iswin) - $tab='notifications'; + if ($iswin) $tab='notifications'; if (!$opts['dryrun']) mysqli_query($link,'INSERT INTO Notifications (ID, Notification, Severity, Microtime, Seen, Deleted) VALUES (NULL, \''.myesc($link,mb_substr($msg,0,$tables[$tab]['Notification'],'UTF-8')).'\', '.$sev.', \''.microtime(true).'\', 0, 0)') or mexit(__LINE__.': '.mysqli_error($link).N,3); } /** */ /** - * Effettua una chiamata alla API di Mastodon. + * Executes a call to Mastodon API. * - * @param string $host L'host da chiamare (e.g.: "mastodon.bida.im") - * @param string $path Il path della API (e.g.: "/api/v1/timelines/public?local=true") - * @return mixed L'oggetto ritornato dalla chiamata, già parsato da json_decode, o NULL se la chiamata fallisce + * @param string $host Host to be called (e.g.: "mastodon.bida.im") + * @param string $path API path (e.g.: "/api/v1/timelines/public?local=true") + * @return mixed An array representing the JSON object as returned by json_decode, or NULL if the call fails */ function get_api($host, $path) { - global $opts; - $buf = @getfc('https://'.$host.$path,$opts['timeout']); - if ($buf['cont']!==false) { - $data = json_decode($buf['cont'], true); - return $data; - } else { - return NULL; - } + global $opts; + $buf = @getfc('https://'.$host.$path,$opts['timeout']); + if ($buf['cont']!==false) { + $data = json_decode($buf['cont'], true); + return $data; + } else { + return NULL; + } } /** - * Torna un elenco di linguaggi riconosciuti nel toot fornito con relativa probabilità. + * Returns a list of known recognized languages, with the related probability, fot the toot that got passed to it * - * @param mixed $toot Il toot da analizzare, come ritornato dalle API - * @return array Mappa tra codice lingua e probabilità che il toot sia in quella lingua. + * @param mixed $toot The toot to be checked, as returned by the API + * @return array Associative array with language and related probability */ function get_toot_languages($toot) { if (is_array($toot) && array_key_exists('language',$toot)) @@ -415,15 +397,15 @@ function get_toot_languages($toot) { else $l = NULL; if($l !== NULL) { - // la lingua è specificata già nel toot: usa quella + // the language is explicitly set in the toot, so use that $langs[$l] = 1; } elseif (array_key_exists('content',$toot)) { - // la lingua non è specificata: deducila + // the language is not explicitly set in the toot, so try and recognize it $text = strip_tags($toot['content']); $ld = new Language; $langs = $ld->detect($text)->bestResults()->close(); } - // raggruppa le lingue derivate, e.g.: "zh" e "zh-CN" + // group derived languages into two-charactes language code (e.g.: "zh-CN" into "zh") $grouped_langs = array(); foreach($langs as $key => $value) { $l = explode("-", $key)[0]; @@ -437,10 +419,10 @@ function get_toot_languages($toot) { } /** - * Date le probabilità di lingua per ogni toot, calcola la media. + * Given the probability of a language for every toot, calculate the average * - * @param array $detected_langs Array di mappe tra lingua e probabilità - * @return array Mappa tra lingua e probabilità + * @param array $detected_langs Array of mappings between language and probability + * @return array Mapping between language and probability */ function summary($detected_langs) { $res = Array(); @@ -459,11 +441,11 @@ function summary($detected_langs) { } /** - * Helper function per usort: compara due array usando il primo elemento. + * Helper function for usort: compares two arrays using the first element * - * @param array $entry1 Primo array da comparare - * @param array $entry2 Secondo array da comparare - * @return number -1, 0 o 1 a seconda che $entry1[0] sia minore, uguale o superiore a $entry2[0] + * @param array $entry1 First array to be compared + * @param array $entry2 Second array to be compared + * @return number -1, 0 o 1 depening on $entry1[0] being less than, equal to or greater than $entry2[0] */ function sort_weights($entry1, $entry2) { $w1 = $entry1[0]; @@ -478,10 +460,10 @@ function sort_weights($entry1, $entry2) { } /** - * Data una mappa di lingue, ritorna una lista di linguaggi considerati probabili. + * Given a language mapping, return a list of probable languages * - * @param array $summary Mappa tra lingue e probabilità - * @return string[] Elenco di lingue considerate probabili + * @param array $summary Map between language and probabilty + * @return string[] List of probable languages */ function get_languages($summary) { $lst = []; @@ -504,10 +486,10 @@ function get_languages($summary) { } /** - * Ritorna una lista di lingue probabili per la data istanza. + * Returns a list of probable languages for the given instance * - * @param string $host Hostname dell'istanza (e.g.: "mastodon.bida.im") - * @return string[] Lista di lingue probabili + * @param string $host Instance’s hostname (e.g.: "mastodon.bida.im") + * @return string[] List of probable languages */ function get_instance_langs($host) { global $opts; @@ -539,9 +521,9 @@ function langs($instid, $uri, $auto) { return($retlangs); } else { if ($auto) - echo('Lingue rilevate: '.implode(', ',$languages).N); + eecho(1,'detected languages: '.implode(', ',$languages).N); else - echo('Lingue dichiarate: '.implode(', ',$languages).N); + eecho(1,'declared languages: '.implode(', ',$languages).N); $pos=0; foreach($languages as $lang) { $res=mysqli_query($link,'SELECT * FROM Languages WHERE Code=\''.myesc($link,$lang).'\'') @@ -600,18 +582,18 @@ require(__DIR__.'/../site/mustard/include/ghs.php'); require(__DIR__.'/../site/mustard/include/ght.php'); /* - * Nodeinfo ('https://'.$dom.'/nodeinfo/2.0.json') è stato aggiunto nella 3.0.0 - * Trends ('https://'.$dom.'/api/v1/trends') è stato aggiunto nella 3.0.0 - * Activity ('https://'.$dom.'/api/v1/instance/activity') è stato aggiunto nella 2.1.2 + * Nodeinfo ('https://'.$host.'/nodeinfo/2.0.json') was added in v3.0.0 + * Trends ('https://'.$host.'/api/v1/trends') was added in v3.0.0 + * Activity ('https://'.$host.'/api/v1/instance/activity') was added in v2.1.2 */ if ($opts['jsonwrite']) { - if ($riprendi) - $mode=array('a','aggiunta'); + if ($recover) + $mode=array('a','append'); else - $mode=array('w','scrittura'); + $mode=array('w','write'); $jsonf=@fopen($opts['jsonfp'],$mode[0]) - or mexit('Non ho potuto aprire in modalità '.$mode[1].' il file di dump delle info json «'.$opts['jsonfp'].'».',1); + or mexit('could not open file «'.$opts['jsonfp'].'» in '.$mode[1].' mode.',1); if ($mode[0]=='w') fwrite($jsonf,'{'.N); } @@ -620,38 +602,38 @@ $cinsts=count($insts); $i=0; $qok=0; $qgood=0; -if ($riprendi) { +if ($recover) { $i=$currinst['i']; $qok=$currinst['qok']; $qgood=$currinst['qgood']; } $beg=$i; while ($i<$cinsts) { - $dom=$insts[$i]; - @file_put_contents($currinstjfp,$dom."\t".$i."\t".$qok."\t".$qgood.N) - or mexit('Non ho potuto aprire in scrittura il file «'.$currinstjfp.'».',1); + $now=time(); + $host=$insts[$i]; + @file_put_contents($currinstjfp,$host."\t".$i."\t".$qok."\t".$qgood.N) + or mexit('could not open «'.$currinstjfp.'» for writing.',1); $i++; $ismast=null; $instans=true; $info=null; - $tela=time()-$tini; - echo('~~~~ '.$dom.' - '.$i.'/'.$cinsts.'; '.$qok.' OK; '.$qgood.' BUONE; '.round(100/$cinsts*$i).'%; tempo trascorso: '.ght($tela,null,0).'; stima tempo rimanente: '.ght($tela/$i*($cinsts-$beg)-$tela,null,0).'; mem.: '.ghs(memory_get_usage(true)).' picco mem.: '.ghs(memory_get_peak_usage(true)).' ~~~~'.N); - if (willtrunc($dom,'Instances','URI')) { - echo('ATTENZIONE: la lunghezza di «'.$dom.'» eccede quella del campo URI della tabella Instances, perciò lo ignoro.'); + $tela=$now-$tini; + eecho(1,'working on «'.$host.'»; '.$i.'/'.$cinsts.'; '.$qok.' ok; '.$qgood.' good; '.round(100/$cinsts*$i).'%; elapsed time: '.ght($tela,null,0).'; estimated remaining time: '.ght($tela/$i*($cinsts-$beg)-$tela,null,0).'; mem.: '.ghs(memory_get_usage(true)).'; mem. peak: '.ghs(memory_get_peak_usage(true)).N); + if (willtrunc($host,'Instances','URI')) { + eecho(2,'ignoring «'.$host.'» because hostname is too long for the «URI» column of «Instances» table.'.N); } else { - echo('Provo a recuperare le informazioni API sull’istanza ... '); - $buf=@getfc('https://'.$dom.'/api/v1/instance',$opts['timeout']); + eecho(0,'trying to fetch instance info from API...'.N); + $buf=@getfc('https://'.$host.'/api/v1/instance',$opts['timeout']); if ($buf['cont']!==false) { - $info=json_decode($buf['cont'],true); + $info=@json_decode($buf['cont'],true); if (is_array($info)) { - echo('OK :-)'.N); - echo('Provo a recuperare le informazioni Nodeinfo sull’istanza ... '); - $buf=@getfc('https://'.$dom.'/nodeinfo/2.0.json',$opts['timeout']); + eecho(1,'got instance info from API :-)'.N); + eecho(0,'trying to fetch instance info from nodeinfo...'.N); + $buf=@getfc('https://'.$host.'/nodeinfo/2.0.json',$opts['timeout']); if ($buf['cont']!==false) { - echo('OK :-)'.N); + eecho(1,'got instance info from nodeinfo :-)'.N); $info['x-nodeinfo']=json_decode($buf['cont'],true); - // teniamo d'occhio le notifiche di cui sotto per includere eventualmente altri derivati di mastodon? - // visti fin qui, verificare cosa sono: epicyon + // we should keep an eye to new software names here, to decide if they are mastodon derivates... if (isset($info['x-nodeinfo']['software']['name']) && !is_null($info['x-nodeinfo']['software']['name'])) { if (preg_match('/^mastodon|corgidon/',$info['x-nodeinfo']['software']['name'])===1) $ismast=true; @@ -660,99 +642,107 @@ while ($i<$cinsts) { if (mysqli_num_rows($res)<1) { if (!$opts['dryrun']) mysqli_query($link,'INSERT INTO Platforms (Name) VALUES (\''.myesc($link,truncs($info['x-nodeinfo']['software']['name'],'Platforms','Name','«'.$info['uri'].'»')).'\')') or mexit(__LINE__.': '.mysqli_error($link).N,3); - notify('«'.$info['uri'].'» utilizza come software «'.$info['x-nodeinfo']['software']['name'].'»; l’ho aggiunto alla tabella delle piattaforme incontrate. Se non si tratta di mastodon o corgidon, che già vengono accettati, sarebbe buona cosa verificare se è una variante di mastodon e quanto è compatibile, per valutare se accettare le istanze che lo utilizzano.',1); + notify('New software found: «'.$info['uri'].'» runs on «'.$info['x-nodeinfo']['software']['name'].'»; i added it to the table of known softwares. It would be good to check whether it is a Mastodon derivate and how compatible it is, to decide whether to consider instances using it as Mastodon instances.',1); } } } else { - echo('ERRORE: '.$buf['emsg'].N); + eecho(2,'could not fetch instance info from nodeinfo: '.$buf['emsg'].N); } if (array_key_exists('version',$info)) { if ($info['version']>='2.1.2') { - echo('Provo a recuperare le informazioni API sull’attività dell’istanza ... '); - $buf=@getfc('https://'.$dom.'/api/v1/instance/activity',$opts['timeout']); + eecho(0,'trying to fetch instance activity info from API...'.N); + $buf=@getfc('https://'.$host.'/api/v1/instance/activity',$opts['timeout']); if ($buf['cont']!==false) { - echo('OK :-)'.N); + eecho(1,'got instance activity info from API :-)'.N); $info['x-activity']=json_decode($buf['cont'],true); } else { - echo('ERRORE: '.$buf['emsg'].N); + eecho(2,'could not fetch instance activity from API: '.$buf['emsg'].N); } } if ($info['version']>='3.0.0') { - echo('Provo a recuperare le informazioni API sui trends dell’istanza ... '); - $buf=@getfc('https://'.$dom.'/api/v1/trends',$opts['timeout']); + eecho(0,'trying to fetch instance trends info from API...'.N); + $buf=@getfc('https://'.$host.'/api/v1/trends',$opts['timeout']); if ($buf['cont']!==false) { - echo('OK :-)'.N); + eecho(1,'got instance trends info from API :-)'.N); $info['x-trends']=json_decode($buf['cont'],true); } else { - echo('ERRORE: '.$buf['emsg'].N); + eecho(2,'could not fetch instance trends from API: '.$buf['emsg'].N); } } } } else { $instans=false; - echo('ERRORE: i dati recuperati non erano un array'.N); + eecho(2,'fetched data were not good JSON.'.N); } } else { $instans=false; - echo('ERRORE: '.$buf['emsg'].N); + eecho(2,'could not fetch instance info from API: '.$buf['emsg'].N); } if (!isset($info['uri']) || preg_match('#^\s*$#',$info['uri'])===1) $instans=false; if (is_array($info) && count($info)>0) { - //echo('Dumpone json di tutte le info recuperate:'.N.json_encode($info,JSON_PRETTY_PRINT).N); + //echo('json dump of all fetched info:'.N.json_encode($info,JSON_PRETTY_PRINT).N); if ($opts['jsonwrite']) - fwrite($jsonf,'"'.$dom.'": '.json_encode($info,JSON_PRETTY_PRINT).','.N); + fwrite($jsonf,'"'.$host.'": '.json_encode($info,JSON_PRETTY_PRINT).','.N); } - $now=time(); if (!$instans) { - // questo è il limbo delle istanze che non rispondono - $res=mysqli_query($link,'SELECT * FROM Instances WHERE URI=\''.myesc($link,$dom).'\'') + // this is the limbo of non-responding instances + $res=mysqli_query($link,'SELECT * FROM Instances WHERE URI=\''.myesc($link,$host).'\'') or mexit(__LINE__.': '.mysqli_error($link).N,3); - if (mysqli_num_rows($res)>0) { - echo('«'.$dom.'» non risponde, ma è presente nel database; aggiorno InstChecks, Instances.LastCheckOk ed eventualmente Instances.Dead.'.N); + $nrows=mysqli_num_rows($res); + if ($nrows==1) { + eecho(1,'«'.$host.'» didn’t respond, but it is present in the database; updating InstChecks, Instances.LastCheckOk and possibly Instances.New and Instances.Dead.'.N); $row=mysqli_fetch_assoc($res); if (!$opts['dryrun']) mysqli_query($link,'INSERT INTO InstChecks (InstID, Time, Status) VALUES ('.$row['ID'].', '.$now.', 0)') or mexit(__LINE__.': '.mysqli_error($link).N,3); if (!$opts['dryrun']) mysqli_query($link,'UPDATE Instances SET LastCheckOk=0 WHERE ID='.$row['ID']) or mexit(__LINE__.': '.mysqli_error($link).N,3); + if ($row['New']==1 && !is_null($row['FirstSeen']) && $now-$row['FirstSeen']>$opts['oldline']) { + eecho(1,'«'.$host.'» is no longer new.'.N); + if (!$opts['dryrun']) mysqli_query($link,'UPDATE Instances SET New=0 WHERE ID='.$row['ID']) + or mexit(__LINE__.': '.mysqli_error($link).N,3); + notify('Instance «'.$row['URI'].'» is no longer new.',1); + } - // vediamo se ha mai risposto e nel caso ritorniamo per primo il momento dell'ultima risposta - $rres=mysqli_query($link,'SELECT Time FROM InstChecks WHERE InstID='.$row['ID'].' AND Status=1 ORDER BY Time DESC') or mexit(__LINE__.': '.mysqli_error($link).N,3); - // se non ha mai risposto ritorniamo per primo il momento del primo check + // we check the last time instance responded, if ever + $rres=mysqli_query($link,'SELECT Time FROM InstChecks WHERE InstID='.$row['ID'].' AND Status=1 ORDER BY Time DESC LIMIT 1') or mexit(__LINE__.': '.mysqli_error($link).N,3); + // if instance never responded we consider the time of first check if (mysqli_num_rows($rres)==0) { - $rres=mysqli_query($link,'SELECT Time FROM InstChecks WHERE InstID='.$row['ID'].' AND Status=0 ORDER BY Time ASC') or mexit(__LINE__.': '.mysqli_error($link).N,3); + $rres=mysqli_query($link,'SELECT Time FROM InstChecks WHERE InstID='.$row['ID'].' AND Status=0 ORDER BY Time ASC LIMIT 1') or mexit(__LINE__.': '.mysqli_error($link).N,3); } if (mysqli_num_rows($rres)>0) { $rrow=mysqli_fetch_assoc($rres); if ($now-$rrow['Time']>$opts['deadline']) { if (!$opts['dryrun']) mysqli_query($link,'UPDATE Instances SET Dead=1 WHERE ID='.$row['ID']) or mexit(__LINE__.': '.mysqli_error($link).N,3); - notify('L’istanza «'.$row['URI'].'» è MORTA!',0); + notify('Instance «'.$row['URI'].'» is dead!',1); } } else { - echo('PAZZESCO! «'.$dom.'» esiste nel database ma non ci sono dati relativi in InstChecks! Rimedio.'.N); + eecho(2,'«'.$host.'» exists in the database but there’s no data about it in InstChecks! I’ll remedy.'.N); if (!$opts['dryrun']) mysqli_query($link,'INSERT INTO InstChecks SET InstID='.$row['ID'].', Time='.$now.', Status=0') or mexit(__LINE__.': '.mysqli_error($link).N,3); } - } else { - echo('«'.$dom.'» non risponde e non è nel database, la aggiungo.'.N); - // "New=0" e nessun FirstSeen (quindi NULL) perché non è nuova e non è vista per la prima volta finché non risponde la prima volta + } elseif ($nrows==0) { + eecho(1,'«'.$host.'» doesn’t respond and is not in the database, adding it.'.N); + // "New=0" and "FirstSeen=NULL" because it's not new and not seen until it responds for the first time if (!$opts['dryrun']) { - mysqli_query($link,'INSERT INTO Instances SET New=0, Good=0, Chosen=0, Visible=0, Noxious=0, URI=\''.myesc($link,$dom).'\', LastCheckOk=0') or mexit(__LINE__.': '.mysqli_error($link).N,3); + mysqli_query($link,'INSERT INTO Instances SET FirstSeen=NULL, New=0, Good=0, Chosen=0, Visible=0, Noxious=0, URI=\''.myesc($link,$host).'\', LastCheckOk=0') or mexit(__LINE__.': '.mysqli_error($link).N,3); $instid=mysqli_insert_id($link); mysqli_query($link,'INSERT INTO InstChecks SET InstID='.$instid.', Time='.$now.', Status=0') or mexit(__LINE__.': '.mysqli_error($link).N,3); } else { $instid=0; } + } else { + eecho(3,'table «Instances» has «'.$nrows.'» entries for instance «'.$host.'»!'.N); + notify('Instance «'.$row['URI'].'» has «'.$nrows.'» entries in «Instances» table!',1); } - } else { - // l'istanza ha risposto occhei... + // instance responded if (is_null($ismast)) { if (!array_key_exists('version',$info)) { - $ismast=null;// ridondante, ma tanto per metterci qualcosa + $ismast=null;// redundant, just to put there something } elseif (array_key_exists('pleroma',$info)) { $ismast=false; } elseif (preg_match('#(compatible|pleroma|pixelfed)#i',$info['version'])==1) { @@ -824,56 +814,65 @@ while ($i<$cinsts) { $whynot=array(); if (is_null($instrow['RegOpen'])) { - $whynot[]='non se ne conosce lo stato delle registrazioni (aperte/chiuse)'; + $whynot[]='we don’t know if it allows registrations'; } elseif ($instrow['RegOpen']==0) { - $whynot[]='ha le registrazioni chiuse'; + $whynot[]='it doesn’t allow registrations'; } if (is_null($instrow['UserCount'])) { - $whynot[]='non se ne conosce il numero di utenti'; + $whynot[]='we don’t know its total users number'; } elseif ($instrow['UserCount']<10 || $instrow['UserCount']>30000) { - $whynot[]='il numero di utenti non è compreso tra 10 e 30.000'; + $whynot[]='total users number is not greater than 10 and less than 30000'; } if (is_null($instrow['DomainCount'])) { - $whynot[]='non se ne conosce il numero di istanze note'; + $whynot[]='we don’t know the number of other instances it knows'; } elseif ($instrow['DomainCount']<500) { - $whynot[]='il numero di istanze note è minore di 500'; + $whynot[]='the number of other instances it knows is less than 500'; } if (!is_null($instrow['ActiveUsersMonth'])) { if ($instrow['ActiveUsersMonth']<10) - $whynot[]='il numero di utenti attivi nell’ultimo mese è minore di 10'; + $whynot[]='the number of active users for the last month is less than 10'; } elseif (!is_null($instrow['StatusCount']) && $instrow['UserCount']>0 && $instrow['StatusCount']/$instrow['UserCount']<10) { - $whynot[]='il numero medio di toots per utente è minore di 10'; + $whynot[]='the average number of toots for user is less than 10'; } else { - $whynot[]='è stato impossibile determinare il numero di utenti attivi nell’ultimo mese o il numero medio di toots per utente'; + $whynot[]='it was impossible to detect the number of active users for the last month or the average number of toots for user'; } if (count($whynot)==0) { $instrow['Good']=1; - echo('Siamo in presenza di un’istanza papabile! :-)'.N); + eecho(1,'this is a suitable instance! :-)'.N); $qgood++; } else { - echo('Siamo in presenza di un’istanza non papabile: '.implode('; ',$whynot).' :-('.N); + eecho(1,'This is not a suitable instance: '.implode('; ',$whynot).' :-('.N); } $res=mysqli_query($link,'SELECT * FROM Instances WHERE URI=\''.myesc($link,$instrow['URI']).'\'') or mexit(__LINE__.': '.mysqli_error($link).N,3); - if (mysqli_num_rows($res)>0) { - echo('«'.$instrow['URI'].'» è già presente nel DB, la aggiorno...'.N); + $nrows=mysqli_num_rows($res); + if ($nrows==1) { + eecho(1,'«'.$instrow['URI'].'» is already present in the database, updating it...'.N); $oldinstrow=mysqli_fetch_assoc($res); $instid=$oldinstrow['ID']; $instrow['ID']=$oldinstrow['ID']; - // se l'istanza già presente nel db ha FirstSeen=NULL significa che è stata aggiunta senza che rispondesse e che questa è la prima volta che risponde, quindi... + // if the instance already present in the db has FirstSeen=NULL, this means this is the first time it responds, so... if (is_null($oldinstrow['FirstSeen'])) { - $instrow['FirstSeen']=time(); + $instrow['FirstSeen']=$now; $instrow['New']=1; } else { $instrow['FirstSeen']=$oldinstrow['FirstSeen']; - $instrow['New']=$oldinstrow['New']; + if ($oldinstrow['New']==1) { + $instrow['New']=1; + if ($now-$oldinstrow['FirstSeen']>$opts['oldline']) { + $instrow['New']=0; + eecho(1,'«'.$row['URI'].'» is no longer new.'.N); + notify('Instance «'.$row['URI'].'» is no longer new.',1); + } + } } + if ($instrow['Good']==1 && $oldinstrow['Good']==0) { - notify('L’istanza «'.$instrow['URI'].'» non era papabile, ma lo è diventata!',1); + notify('Instance «'.$instrow['URI'].'» wasn’t suitable, but it is now!',1); } elseif ($instrow['Good']==0 && $oldinstrow['Good']==1) { - notify('L’istanza «'.$instrow['URI'].'» era papabile, ma non lo è più per i seguenti motivi: '.implode('; ',$whynot),3); + notify('Instance «'.$instrow['URI'].'» was suitable, but it’s no longer for these reasons: '.implode('; ',$whynot),1); } $instrow['Chosen']=$oldinstrow['Chosen']; $instrow['Priority']=$oldinstrow['Priority']; @@ -882,9 +881,9 @@ while ($i<$cinsts) { $instrow['NoxReason']=$oldinstrow['NoxReason']; $instrow['NoxLastModTS']=$oldinstrow['NoxLastModTS']; if ($instrow['ShortDesc']!=$oldinstrow['ShortDesc']) - notify('La «Descrizione breve» dell’istanza «'.$instrow['URI'].'» è cambiata.',2); + notify('«Short description» of instance «'.$instrow['URI'].'» has changed.',1); if ($instrow['LongDesc']!=$oldinstrow['LongDesc']) - notify('La «Descrizione lunga» dell’istanza «'.$instrow['URI'].'» è cambiata.',2); + notify('«Long description» of instance «'.$instrow['URI'].'» has changed.',1); $instrow['OurDesc']=$oldinstrow['OurDesc']; $instrow['OurDescEN']=$oldinstrow['OurDescEN']; $instrow['LocalityID']=$oldinstrow['LocalityID']; @@ -899,7 +898,7 @@ while ($i<$cinsts) { $query.=$field.'=NULL, '; } $query=substr($query,0,-2).' WHERE Instances.ID='.$instrow['ID']; - echo('QUERONA DI UPDATE: «'.$query.'».'.N); + eecho(1,'Update query: «'.$query.'».'.N); if (!$opts['dryrun']) mysqli_query($link,$query) or mexit(__LINE__.': '.mysqli_error($link).N,3); @@ -910,32 +909,36 @@ while ($i<$cinsts) { $oldinstlangs[]=$row; $instlangs=langs($instrow['ID'], $instrow['URI'], false); if ($instlangs!=$oldinstlangs) { - notify('La lista delle lingue utilizzate dichiarate dall’istanza «'.$instrow['URI'].'» è cambiata da «'.subarimp(', ','Code',$oldinstlangs).'» a «'.subarimp(', ','Code',$instlangs).'».',2); - if (!$opts['dryrun']) mysqli_query($link,'DELETE FROM InstLangs WHERE InstID='.$instrow['ID']) - or mexit(__LINE__.': '.mysqli_error($link).N,3); - foreach ($instlangs as $row) { - if (!$opts['dryrun']) mysqli_query($link,'INSERT INTO InstLangs (InstID, LangID, Pos) VALUES ('.$row['InstID'].', '.$row['LangID'].', '.$row['Pos'].')') + notify('The list of languages declared by instance «'.$instrow['URI'].'» has changed from «'.subarimp(', ','Code',$oldinstlangs).'» to «'.subarimp(', ','Code',$instlangs).'».',1); + if (!$opts['dryrun']) { + mysqli_query($link,'DELETE FROM InstLangs WHERE InstID='.$instrow['ID']) or mexit(__LINE__.': '.mysqli_error($link).N,3); - } - } - - if ($instrow['OurLangsLock']==0) { - $instourlangs=langs($instrow['ID'], $instrow['URI'], true); - // se instourlangs è vuoto e instlangs no, imposta instourlangs come instlangs - if (count($instourlangs)==0 && count($instlangs)>0) - $instourlangs=$instlangs; - if (count($instourlangs)>0) { - if (!$opts['dryrun']) mysqli_query($link,'DELETE FROM InstOurLangs WHERE InstID='.$instrow['ID']) - or mexit(__LINE__.': '.mysqli_error($link).N,3); - foreach ($instourlangs as $row) { - if (!$opts['dryrun']) mysqli_query($link,'INSERT INTO InstOurLangs (InstID, OurLangID, Pos) VALUES ('.$row['InstID'].', '.$row['LangID'].', '.$row['Pos'].')') + foreach ($instlangs as $row) { + mysqli_query($link,'INSERT INTO InstLangs (InstID, LangID, Pos) VALUES ('.$row['InstID'].', '.$row['LangID'].', '.$row['Pos'].')') or mexit(__LINE__.': '.mysqli_error($link).N,3); } } } - } else { - echo('«'.$info['uri'].'» non è già presente nel DB, la aggiungo...'.N); + if ($instrow['OurLangsLock']==0) { + $instourlangs=langs($instrow['ID'], $instrow['URI'], true); + // if instourlangs is empty and instlangs is not, set instourlangs as instlangs + if (count($instourlangs)==0 && count($instlangs)>0) + $instourlangs=$instlangs; + if (count($instourlangs)>0) { + if (!$opts['dryrun']) { + mysqli_query($link,'DELETE FROM InstOurLangs WHERE InstID='.$instrow['ID']) + or mexit(__LINE__.': '.mysqli_error($link).N,3); + foreach ($instourlangs as $row) { + mysqli_query($link,'INSERT INTO InstOurLangs (InstID, OurLangID, Pos) VALUES ('.$row['InstID'].', '.$row['LangID'].', '.$row['Pos'].')') + or mexit(__LINE__.': '.mysqli_error($link).N,3); + } + } + } + } + + } elseif ($nrows==0) { + eecho(1,'«'.$info['uri'].'» is not present in the database, adding it...'.N); $instrow['FirstSeen']=$now; if ($opts['setnew']) $instrow['New']=1; @@ -950,7 +953,7 @@ while ($i<$cinsts) { } $values=substr($values,0,-2); $query='INSERT INTO Instances ('.implode(', ',$fields).') VALUES ('.$values.')'; - echo('QUERONA DI INSERT: «'.$query.'»'.N); + eecho(1,'Insert query: «'.$query.'»'.N); if (!$opts['dryrun']) { mysqli_query($link,$query) or mexit(__LINE__.': '.mysqli_error($link).N,3); $instid=mysqli_insert_id($link); @@ -958,7 +961,7 @@ while ($i<$cinsts) { $instid=0; } if ($opts['setnew']) - notify('Ho trovato una nuova istanza: «'.$instrow['URI'].'».',1); + notify('New instance found: «'.$instrow['URI'].'».',1); $instlangs=langs($instid, $instrow['URI'], false); foreach ($instlangs as $row) { @@ -967,17 +970,22 @@ while ($i<$cinsts) { } $instourlangs=langs($instid, $instrow['URI'], true); - // se instourlangs è vuoto e instlangs no, imposta instourlangs come instlangs + // if instourlangs is empty and instlangs is not, set instourlangs as instlangs if (count($instourlangs)==0 && count($instlangs)>0) $instourlangs=$instlangs; - foreach ($instourlangs as $row) { - if (!$opts['dryrun']) mysqli_query($link,'INSERT INTO InstOurLangs (InstID, OurLangID, Pos) VALUES ('.$row['InstID'].', '.$row['LangID'].', '.$row['Pos'].')') - or mexit(__LINE__.': '.mysqli_error($link).N,3); + if (!$opts['dryrun']) { + foreach ($instourlangs as $row) { + mysqli_query($link,'INSERT INTO InstOurLangs (InstID, OurLangID, Pos) VALUES ('.$row['InstID'].', '.$row['LangID'].', '.$row['Pos'].')') + or mexit(__LINE__.': '.mysqli_error($link).N,3); + } } if ($instrow['Good']==1) - notify('La nuova istanza «'.$instrow['URI'].'» è papabile!',1); + notify('New instance «'.$instrow['URI'].'» is suitable!',1); + } else { + eecho(3,'table «Instances» has «'.$nrows.'» entries for instance «'.$host.'»!'.N); + notify('Instance «'.$row['URI'].'» has «'.$nrows.'» entries in «Instances» table!',1); } if (array_key_exists('x-activity',$info) && is_array($info['x-activity'])) { @@ -1027,13 +1035,12 @@ while ($i<$cinsts) { or mexit(__LINE__.': '.mysqli_error($link).N,3); } } - echo(N); } mysqli_close($link); if ($opts['jsonwrite']) { - fwrite($jsonf,'"Fine?": true'.N.'}'.N); + fwrite($jsonf,'"The end?": true'.N.'}'.N); fclose($jsonf); }