浏览代码

Tanta roba in crawler/crawler.php, due robine in web/home_en.php

pezcurrel 4 年之前
父节点
当前提交
900454889f
共有 2 个文件被更改,包括 308 次插入151 次删除
  1. 305 148
      crawler/crawler.php
  2. 3 3
      web/home_en.php

+ 305 - 148
crawler/crawler.php

@@ -18,39 +18,89 @@
 
 define('N',"\n");
 
-$inifp='crawler.ini';
+$inifp=null;
+
+$opts=array(
+	'excludeafter'=>60*60*24*30,
+	'startinstancesfp'=>null,
+	'loadbiglist'=>true,
+	'onlinecheck'=>true,
+	'timeout'=>5,
+	'biglistfp'=>null,
+	'prodlistfp'=>null
+);
 
 $help='DESCRIZIONE
- Questo script prende una lista di istanze mastodon sorellate, ciascuna
- con (o anche senza, però sarebbe meglio con) una relativa lista di
- istanze da essa bloccate, e genera una lista delle istanze note alle
- istanze sorellate, escludendone quelle bloccate - più, al momento,
- quelle il cui endpoint [istanza]/api/v1/instance non risponde, e quelle
- che hanno chiusa la registrazione di nuovi utenti.
+ Questo script prende una lista di istanze Mastodon («istanze
+ di partenza»), ciascuna con una relativa lista di istanze da essa
+ bloccate (che può anche essere omessa), e genera/aggiorna due liste:
+ una che conterrà i dati di tutte le istanze di partenza e delle istanze
+ ad esse note (comprese quelle bloccate, escluse soltanto quelle che non
+ rispondono da un lasso di tempo impostabile), e una che conterrà solo
+ le istanze non bloccate, che danno possibilità di iscrizione di nuovi
+ utenti, il cui numero di utenti è compreso tra 11 e 30000, che
+ conoscono almeno altre 500 istanze, e la cui media di toot per utente
+ è maggiore o uguale a 10.
 SINTASSI
- crawler.php [opzioni]
+ crawler.php -i <file> | -s <file> -b <file> -p <file> [altre opzioni]
 OPZIONI
- -i, --inifp
-  Imposta il file di configurazione (per default "'.$inifp.'").
-  Tutte le altre opzioni, che siano specificate prima o dopo questa,
-  hanno la precedenza su quelle definite nel file di configurazione.
- -t, --timeout
-  Imposta il timeout delle richieste http.
- -e, --excludeafter
-  Imposta il lasso di tempo (in secondi) dopo il quale un\'istanza che
-  non si identifica (il cui endpoint [istanza]/api/v1/instance non
-  risponde) viene eliminata dal listone di tutte le istanze testate.
- -b, --biglistfp
-  Imposta il file da cui leggere le istanze già testate in passato (se
-  non è specificata l\'opzione "-d", vedi sotto) e in cui scrivere
-  il listone di tutte le istanze testate.
- -p, --prodlistfp
-  Imposta il file in cui scrivere la lista delle istanze occhei.
- -s, --sistersfp
-  Imposta il file da cui leggere le istanze sorelle e le relative liste
-  di istanze sospese-silenziate.
- -d, --dontloadbl
-  Evita di caricare il listone delle istanze già testate in passato.
+ -i, --inifp <file>
+  Imposta un file di configurazione da cui leggere le opzioni.
+  Il formato di questo file è semplice: una opzione per riga in formato
+  <opzione>=<valore>, dove «opzione» è una qualsiasi tra le opzioni
+  descritte qui nel suo formato lungo, tranne «inifp» e «help».
+  Esempio: «startinstancesfp=startinstances.txt».
+  Il file di configurazione può non contenere tutte le opzioni
+  disponibili.
+  Nota bene: tutte le opzioni impostate da riga di comando, che siano
+  specificate prima o dopo questa, hanno la precedenza su quelle
+  definite nel file di configurazione.
+ -s, --startinstancesfp <file>
+  DEVE essere specificata.
+  Imposta il file da cui leggere le istanze di partenza e le relative
+  liste di istanze sospese-silenziate.
+  Il formato del file è questo: per ogni riga:
+  <uri della istanza di partenza>|[uri della relativa lista di istanze
+  bloccate]
+  Ogni riga vuota o che cominci con il carattere «#» sarà ignorata.
+  Il formato del file delle istanze bloccate è questo: per ogni riga:
+  |<uri della istanza bloccata>|<tipo di blocco>(Silenziata|Sospesa)|
+  [riferimento al motivo del blocco.
+  Le prime 4 righe del file saranno ignorate, così come le righe che non
+  corrispondessero al formato di cui sopra.
+  In futuro utilizzeremo un altro formato, per ora ci stiamo adeguando
+  a quello impiegato da mastodon.bida.im per la sua lista di istanze
+  bloccate.
+ -b, --biglistfp <file>
+  DEVE essere specificata.
+  Imposta il file da cui leggere le istanze già testate in passato
+  (se il file esiste e non è specificata l’opzione «-d», vedi sotto)
+  e in cui scrivere tutti i dati recuperabili delle istanze testate.
+ -p, --prodlistfp <file>
+  DEVE essere specificata.
+  Imposta il file da cui leggere (se esiste) e in cui scrivere i dati
+  relativi alle istanze corrispondenti ai criteri di selezione descritti
+  nel paragrafo «DESCRIZIONE».
+ -t, --timeout <secondi>
+  Imposta il timeout delle richieste http(s) in secondi.
+  DEFAULT: '.$opts['timeout'].' secondi.
+ -e, --excludeafter <tempo>
+  Imposta il lasso di tempo dopo il quale un’istanza che non risponde
+  viene eliminata dal listone di tutte le istanze testate.
+  «tempo» deve essere specificato come un numero, seguito eventualmente
+  da un carattere che ne indica l’unità di misura: «s» o nessun
+  carattere per secondi, «m» per minuti, «o» per ore, «g» per giorni,
+  «S» per settimane, «M» per mesi (30 giorni), «A» per anni.
+  DEFAULT: 1 mese.
+ -l, --loadbiglist <si|no>
+  Dice al programma se caricare o meno il listone delle istanze già
+  testate in passato.
+  DEFAULT: «si».
+ -c, --onlinecheck <si|no>
+  Dice al programma se interrogare o meno le istanze note.
+  Se impostato a «no» forza a «si» «loadbiglist» (vedi opzione
+  precedente).
+  DEFAULT: «si».
  -h, --help
   Mostra questo aiuto ed esce.
 
@@ -64,14 +114,36 @@ function mexit($msg,$code) {
 	exit($code);
 }
 
-$opts=array(
-	'excludeafter'=>60*60*24*30,
-	'sistersfp'=>'istanzesorelle',
-	'dontloadbl'=>0,
-	'timeout'=>5,
-	'biglistfp'=>'listona.json',
-	'prodlistfp'=>'listina.json'
-);
+function tosec($str) {
+	if (preg_match('/^([0-9]+)([smogSMA]?)/',$str,$buf)===1) {
+		switch ($buf[2]) {
+			case '':
+			case 's':
+			return($buf[1]);
+			break;
+			case 'm':
+			return($buf[1]*60);
+			break;
+			case 'o':
+			return($buf[1]*60*60);
+			break;
+			case 'g':
+			return($buf[1]*60*60*24);
+			break;
+			case 'S':
+			return($buf[1]*60*60*24*7);
+			break;
+			case 'M':
+			return($buf[1]*60*60*30);
+			break;
+			case 'A':
+			return($buf[1]*60*60*365);
+			break;
+		}
+	} else {
+		return(false);
+	}
+}
 
 for ($i=1; $i<$argc; $i++) {
 	if ($argv[$i]=='-i' || $argv[$i]=='--inifp') {
@@ -79,30 +151,27 @@ for ($i=1; $i<$argc; $i++) {
 			mexit('L’opzione «'.$argv[$i].'» richiede di specificare un file di configurazione (usa «-h» per vedere la guida).'.N,1);
 		$i++;
 		$inifp=$argv[$i];
-		if (!file_exists($inifp) || !is_file($inifp) || !is_readable($inifp))
-			mexit('"'.$inifp.'" non esiste, non è un file o non è leggibile.'.N,1);
 	}
 }
 
-if (file_exists($inifp)) {
+if (!is_null($inifp)) {
 	$buf=@parse_ini_file($inifp);
 	if ($buf!==false) {
 		foreach ($buf as $key=>$val) {
 			if (array_key_exists($key,$opts))
 				$opts[$key]=$val;
+			else
+				echo('Attenzione: l’opzione «'.$key.'» in «'.$inifp.'» è sconosciuta e sarà ignorata.'.N);
+		}
+		if (array_key_exists('excludeafter',$opts)) {
+			$opts['excludeafter']=tosec($opts['excludeafter']);
+			if ($opts['excludeafter']===false)
+				mexit('L’opzione «excludeafter» specificata in «'.$inifp.'» non è in un formato corretto (usa «-h» per vedere la guida).'.N,1);
 		}
 	} else {
-		echo('Attenzione: non ho potuto leggere la configurazione dal file "'.$inifp.'", potrebbe essere non leggibile o corrotto.'.N);
+		mexit('Attenzione: non ho potuto leggere la configurazione dal file «'.$inifp.'».'.N,1);
 	}
 }
-/*$f=@fopen($inifp,'w');
-if ($f!==false) {
-	foreach ($opts as $key=>$val)
-		fwrite($f,$key.'='.$val.N);
-	fclose($f);
-} else {
-	echo('Attenzione: non ho potuto salvare la configurazione nel file "'.$inifp.'".'.N);
-}*/
 
 for ($i=1; $i<$argc; $i++) {
 	if (substr($argv[$i],0,1)=='-') {
@@ -113,10 +182,11 @@ for ($i=1; $i<$argc; $i++) {
 			break;
 			case '-e':
 			case '--excludeafter':
-			if ($i+1>=$argc || preg_match('/^[0-9]+$/',$argv[$i+1])!==1)
-				mexit('L’opzione «'.$argv[$i].'» richiede un parametro numerico intero (usa «-h» per vedere la guida).'.N,1);
+			if ($i+1>=$argc)
 			$i++;
-			$opts['excludeafter']=$argv[$i];
+			$opts['excludeafter']=tosec($argv[$i]);
+			if ($opts['excludeafter']===false)
+				mexit('Opzione «'.$argv[$i].'»: formato non corretto (usa «-h» per vedere la guida).'.N,1);
 			break;
 			case '-t':
 			case '--timeout':
@@ -133,60 +203,66 @@ for ($i=1; $i<$argc; $i++) {
 			$opts['biglistfp']=$argv[$i];
 			break;
 			case '-p':
-			case '--biglistfp':
+			case '--prodlistfp':
 			if ($i+1>=$argc || $argv[$i+1]=='')
 				mexit('L’opzione «'.$argv[$i].'» richiede un parametro di tipo file (usa «-h» per vedere la guida).'.N,1);
 			$i++;
-			$opts['biglistfp']=$argv[$i];
+			$opts['prodlistfp']=$argv[$i];
 			break;
 			case '-s':
-			case '--sistersfp':
+			case '--startinstancesfp':
 			if ($i+1>=$argc || $argv[$i+1]=='')
 				mexit('L’opzione «'.$argv[$i].'» richiede un parametro di tipo file (usa «-h» per vedere la guida).'.N,1);
 			$i++;
-			$opts['sistersfp']=$argv[$i];
+			$opts['startinstancesfp']=$argv[$i];
 			break;
-			case '-d':
-			case '--dontloadbl':
-			$opts['dontloadbl']=1;
+			case '-l':
+			case '--loadbiglist':
+			if ($i+1>=$argc || ($argv[$i+1]!='si' && $argv[$i+1]!='no'))
+				mexit('L’opzione «'.$argv[$i].'» richiede un parametro («si/no») (usa «-h» per vedere la guida).'.N,1);
+			$i++;
+			$opts['loadbiglist']=true;
+			if ($argv[$i]=='no') $opts['loadbiglist']=false;
+			break;
+			case '-c':
+			case '--onlinecheck':
+			if ($i+1>=$argc || ($argv[$i+1]!='si' && $argv[$i+1]!='no'))
+				mexit('L’opzione «'.$argv[$i].'» richiede un parametro («si/no») (usa «-h» per vedere la guida).'.N,1);
+			$i++;
+			$opts['onlinecheck']=true;
+			if ($argv[$i]=='no') $opts['onlinecheck']=false;
 			break;
 			case '-h':
 			case '--help':
 			mexit($help,1);
 			break;
 			default:
-			mexit('Opzione "'.$argv[$i].'" sconosciuta (usa «-h» per vedere la guida).'.N,1);
+			mexit('Opzione «'.$argv[$i].'» sconosciuta (usa «-h» per vedere la guida).'.N,1);
 			break;
 		}
 	} else {
-		mexit('Opzione "'.$argv[$i].'" sconosciuta (usa «-h» per vedere la guida).'.N,1);
+		mexit('Opzione «'.$argv[$i].'» sconosciuta (usa «-h» per vedere la guida).'.N,1);
 	}
 }
 
-$sisters=array();
-echo('Carico il file delle istanze sorelle ("'.$opts['sistersfp'].'") ... ');
-$buf=@file_get_contents($opts['sistersfp']);
-if ($buf!==false) {
-	echo('OK :-)'.N);
-	$buf=explode(N,$buf);
-	foreach ($buf as $val) {
-		if ($val!='' && $val[0]!='#') {
-			$kv=explode('|',$val);
-			if ($kv[1]=='') $kv[1]=NULL;
-			$sisters[$kv[0]]=$kv[1];
-		}
-	}
-} else {
-	mexit(N.'Non ho potuto aprire il file delle istanze sorelle "'.$opts['sistersfp'].'", muoio.'.N,1);
-}
-if (count($sisters)<1)
-	mexit('Il file delle istanze sorelle "'.$opts['sistersfp'].'" non contiene alcuna voce, muoio.'.N,1);
+$buf=null;
+if (is_null($opts['startinstancesfp']))
+	$buf.='- Non hai specificato il file delle istanze di partenza («-s/--startinstancesfp»)'.N;
+if (is_null($opts['biglistfp']))
+	$buf.='- Non hai specificato il file da cui leggere e in cui salvare i dati di tutte le istanze testate («-b/--biglistfp»)'.N;
+if (is_null($opts['prodlistfp']))
+	$buf.='- Non hai specificato il file da cui leggere e in cui salvare i dati di tutte le istanze testate che corrispondono ai criteri di selezione («-p/--prodlistfp»)'.N;
+if (!is_null($buf))
+	mexit('ERRORI'.N.$buf.'Usa «-h/--help» per leggere la guida.'.N,1);
+
+if (!$opts['onlinecheck'])
+	$opts['loadbiglist']=true;
 
 $biglist=array();
 
-if ($opts['dontloadbl']==0) {
+if ($opts['loadbiglist']) {
 	if (file_exists($opts['biglistfp']) && is_file($opts['biglistfp']) && is_readable($opts['biglistfp'])) {
-		echo('Carico la listona pre-esistente ("'.$opts['biglistfp'].'") ... ');
+		echo('Carico la listona pre-esistente («'.$opts['biglistfp'].'») ... ');
 		$buf=@file_get_contents($opts['biglistfp']);
 		if ($buf!==false) {
 			echo('OK :-)'.N);
@@ -197,96 +273,177 @@ if ($opts['dontloadbl']==0) {
 	}
 }
 
-$context=stream_context_create(array('http'=>array('timeout'=>$opts['timeout'])));
-
 $blinstances=array();
-foreach ($sisters as $dom=>$bluri) {
-	if (!is_null($bluri)) {
-		echo('Recupero la lista delle istanze bloccate da "'.$dom.'" ("'.$bluri.'") ... ');
-		$f=@fopen($bluri,'r',false,$context);
-		if ($f!==false) {
-// le prime 4 righe non ci interessano
-			for ($i=0; $i<4; $i++)
-				fgets($f);
-			while (!feof($f)) {
-				$lin=fgets($f);
-				if (preg_match('/^\|([^\|]*)\|([^\|]*)\|([^\|]*)\|$/',$lin,$buf)===1)
-					$blinstances[]=$buf[1];
+
+if ($opts['onlinecheck']) {
+
+	$startinstances=array();
+	echo('Carico il file delle istanze di partenza («'.$opts['startinstancesfp'].'») ... ');
+	$buf=@file_get_contents($opts['startinstancesfp']);
+	if ($buf!==false) {
+		echo('OK :-)'.N);
+		$buf=explode(N,$buf);
+		foreach ($buf as $val) {
+			if ($val!='' && $val[0]!='#') {
+				$kv=explode('|',$val);
+				if ($kv[1]=='') $kv[1]=null;
+				$startinstances[$kv[0]]=$kv[1];
 			}
-			fclose($f);
-			echo('OK :-)'.N);
-		} else {
-			echo('ERRORE :-('.N);
 		}
 	} else {
-		echo('NON recupero la lista delle istanze bloccate da "'.$dom.'": la url della stessa non è definita.'.N);
+		mexit(N.'Non ho potuto caricare il file delle istanze di partenza «'.$opts['startinstancesfp'].'», muoio.'.N,1);
 	}
-}
-ksort($blinstances);
-echo(count($blinstances).' istanze bloccate.'.N);
+	if (count($startinstances)<1)
+		mexit('Il file delle istanze di partenza «'.$opts['startinstancesfp'].'» non contiene alcuna voce, muoio.'.N,1);
 
-foreach ($sisters as $dom=>$bluri) {
-	echo('Recupero la lista delle istanze note a "'.$dom.'" ... ');
-	$buf=@file_get_contents('https://'.$dom.'/api/v1/instance/peers',false,$context);
-	if ($buf!==false) {
-		echo('OK :-)'.N);
-		$peers=json_decode($buf,true);
-		foreach ($peers as $pdom) {
-			if (!in_array($pdom,$blinstances) && !array_key_exists($pdom,$biglist)) {
-				$biglist[$pdom]=NULL;
+	$context=stream_context_create(array('http'=>array('timeout'=>$opts['timeout'])));
+
+	foreach ($startinstances as $dom=>$bluri) {
+		if (!is_null($bluri)) {
+			echo('Recupero la lista delle istanze bloccate da «'.$dom.'» («'.$bluri.'») ... ');
+			$f=@fopen($bluri,'r',false,$context);
+			if ($f!==false) {
+	// le prime 4 righe non ci interessano
+				for ($i=0; $i<4; $i++)
+					fgets($f);
+				while (!feof($f)) {
+					$lin=fgets($f);
+					if (preg_match('/^\|([^\|]*)\|([^\|]*)\|([^\|]*)\|$/',$lin,$buf)===1)
+						$blinstances[]=$buf[1];
+				}
+				fclose($f);
+				echo('OK :-)'.N);
+			} else {
+				echo('ERRORE :-('.N);
 			}
+		} else {
+			echo('NON recupero la lista delle istanze bloccate da «'.$dom.'»: la uri della stessa non è definita.'.N);
 		}
-	} else {
-		echo('ERRORE :-('.N);
 	}
+	ksort($blinstances);
+	echo(count($blinstances).' istanze bloccate.'.N);
+
+	foreach ($startinstances as $dom=>$bluri) {
+		if (!array_key_exists($dom,$biglist))
+			$biglist[$dom]=null;
+		echo('Recupero la lista delle istanze note a «'.$dom.'» ... ');
+		$buf=@file_get_contents('https://'.$dom.'/api/v1/instance/peers',false,$context);
+		if ($buf!==false) {
+			echo('OK :-)'.N);
+			$peers=json_decode($buf,true);
+			foreach ($peers as $pdom) {
+				if (!array_key_exists($pdom,$biglist)) {
+					$biglist[$pdom]=null;
+				}
+			}
+		} else {
+			echo('ERRORE :-('.N);
+		}
+	}
+	ksort($biglist);
+	echo('Totale istanze note: '.count($biglist).N);
+}
+
+$oprodlistc=0;
+$buf=@file_get_contents($opts['prodlistfp']);
+if ($buf!==false) {
+	$prodlist=json_decode($buf,true);
+	$oprodlistc=count($prodlist);
 }
-ksort($biglist);
 
-$prodlist=array();
 $newbiglist=array();
 $i=0;
 $qinst=count($biglist);
 foreach ($biglist as $dom=>$oinfo) {
-	echo('Recupero le informazioni su "'.$dom.'" ('.($i+1).'/'.$qinst.' - '.round(100/$qinst*$i).'%) ... ');
-	$buf=@file_get_contents('https://'.$dom.'/api/v1/instance',false,$context);
-	if ($buf!==false) {
-		echo('OK :-)'.N);
-		$info=json_decode($buf,true);
-		if (!is_null($oinfo) && array_key_exists('cr-checks',$oinfo))
-			$info['cr-checks']=$oinfo['cr-checks'];
-		$info['cr-checks'][]=array('time'=>time(),'ok'=>true);
-		$newbiglist[$dom]=$info;
-		if (array_key_exists('registrations',$info) && $info['registrations']==true
-			&& array_key_exists('stats',$info) && array_key_exists('user_count',$info['stats'] && $info['stats']['user_count']>10 && $info['stats']['user_count']<=30000
-			&& array_key_exists('domain_count',$info['stats'] && $info['stats']['domain_count']>=500
-		) {
-			$prodlist[$dom]=$info;
-			echo('"'.$dom.'" aggiunta alla lista delle istanze ok! :-)'.N);
+	if ($opts['onlinecheck']) {
+		echo('Recupero le informazioni su «'.$dom.'» ('.($i+1).'/'.$qinst.' - '.round(100/$qinst*$i).'%) ... ');
+		$buf=@file_get_contents('https://'.$dom.'/api/v1/instance',false,$context);
+		if ($buf!==false) {
+			echo('OK :-)'.N);
+			$info=json_decode($buf,true);
+			if (!is_null($oinfo) && array_key_exists('cr-checks',$oinfo))
+				$info['cr-checks']=$oinfo['cr-checks'];
+			$info['cr-checks'][]=array('time'=>time(),'ok'=>true);
+			$newbiglist[$dom]=$info;
+		} else {
+			echo('ERRORE :-( ... ');
+			$lastokk=null;
+			if (array_key_exists('cr-checks',$oinfo)) {
+				foreach ($oinfo['cr-checks'] as $key=>$val)
+					if ($val['ok']) $lastokk=$key;
+			}
+			if (is_null($oinfo) || is_null($lastokk) || time()-$oinfo['cr-checks'][$lastokk]['time']<=$opts['excludeafter']) {
+				echo('ma riproveremo...'.N);
+				$oinfo['cr-checks'][]=array('time'=>time(),'ok'=>false);
+				$newbiglist[$dom]=$oinfo;
+			} else {
+				echo('e non riproveremo...'.N);
+				$oinfo=null;
+			}
+			$info=$oinfo;
 		}
+		$i++;
 	} else {
-		echo('ERRORE :-( ... ');
-		if (is_null($oinfo) || time()-$oinfo['cr-checks'][count($oinfo['cr-checks'])-1]['time']<=$opts['excludeafter']) {
-			echo('ma riproveremo...'.N);
-			$oinfo['cr-checks'][]=array('time'=>time(),'ok'=>false);
-			$newbiglist[$dom]=$oinfo;
+		$info=$oinfo;
+	}
+	if (!is_null($info)
+		&& !in_array($dom,$blinstances)
+		&& array_key_exists('registrations',$info) && $info['registrations']==true
+		&& array_key_exists('stats',$info) && array_key_exists('user_count',$info['stats']) && $info['stats']['user_count']>10 && $info['stats']['user_count']<=30000
+		&& array_key_exists('domain_count',$info['stats']) && $info['stats']['domain_count']>=500
+		&& array_key_exists('status_count',$info['stats']) && $info['stats']['status_count']/$info['stats']['user_count']>=10
+/*		&& array_key_exists('contact_account',$info) && array_key_exists('created_at',$info['contact_account'])
+		&& time()-strtotime($info['contact_account']['created_at'])>=6*30*24*60*60*/
+	) {
+		if (array_key_exists($dom,$prodlist)) {
+			$info['new']=false;
+			if (array_key_exists('short_description',$info) && (!array_key_exists('short_description',$prodlist[$dom]) || $prodlist[$dom]['short_description']!=$info['short_description'])) {
+				$info['short_description_changed']=true;
+				$info['prev_short_description']=$prodlist[$dom]['short_description'];
+			} else {
+				$info['short_description_changed']=false;
+			}
+			if ($prodlist[$dom]['description']!=$info['description']) {
+				$info['description_changed']=true;
+				$info['prev_description']=$prodlist[$dom]['description'];
+			} else {
+				$info['description_changed']=false;
+			}
+			echo('«'.$dom.'» era nella lista delle istanze occhei ed è stata AGGIORNATA! :-)'.N);
+			if (array_key_exists('show',$prodlist[$dom]))
+				$info['show']=$prodlist[$dom]['show'];
+			else
+				$info['show']=-1;
 		} else {
-			echo('e non riproveremo...'.N);
+			$info['new']=true;
+			$info['short_description_changed']=false;
+			$info['description_changed']=false;
+			$info['show']=-1;
+			echo('«'.$dom.'» non era nella lista delle istanze occhei ed è stata AGGIUNTA! :-)'.N);
+		}
+		$prodlist[$dom]=$info;
+	} else {
+		if (array_key_exists($dom,$prodlist)) {
+			echo('«'.$dom.'» era nella lista delle istanze occhei ma è stata SCARTATA! :-('.N);
+		} else {
+			echo('«'.$dom.'» non era nella lista delle istanze occhei e NON CI È ENTRATA! :-('.N);
 		}
 	}
-	$i++;
 }
 
-$json=json_encode($newbiglist,JSON_PRETTY_PRINT);
-file_put_contents($opts['biglistfp'],$json);
+if ($opts['onlinecheck']) {
+	$json=json_encode($newbiglist,JSON_PRETTY_PRINT);
+	file_put_contents($opts['biglistfp'],$json);
+	$diff=count($newbiglist)-count($biglist);
+	if ($diff>=0) $diff='+'.$diff;
+	echo('Totale istanze nella listona: '.count($newbiglist).' ('.$diff.' rispetto all’ultima volta)'.N);
+} else {
+	echo('Totale istanze nella listona: '.count($biglist).N);
+}
 $json=json_encode($prodlist,JSON_PRETTY_PRINT);
 file_put_contents($opts['prodlistfp'],$json);
-
-$diff=count($newbiglist)-count($biglist);
-if ($diff<0)
-	$diff='-'.$diff;
-else
-	$diff='+'.$diff;
-echo('Totale istanze nella listona: '.count($newbiglist).' ('.$diff.' rispetto all\'ultima volta)'.N);
-echo('Totale istanze nella listina di quelle occhei: '.count($prodlist).N);
+$diff=count($prodlist)-$oprodlistc;
+if ($diff>=0) $diff='+'.$diff;
+echo('Totale istanze nella listina di quelle occhei: '.count($prodlist).' ('.$diff.' rispetto all’ultima volta)'.N);
 
 ?>

+ 3 - 3
web/home_en.php

@@ -93,7 +93,7 @@
 <h3><a name="Introduction" id="Introduction" class="anchor"></a>Introduction</h3>
 <p>Mastodon is an innovative open-source and self-hostable microblogging platform similar to Twitter or Tumblr. Its development was started in 2016 by Eugen Rochko and since then Mastodon constantly attracted new users and communities looking for a social environment independent from big company logics and censorship.</p>
 <p>Mastodon is not a Twitter clone: by concept, structure and functionalities it is something completely different and interesting!</p>
-<p>This website is an introduction to Mastodon’s basic concepts and features that you should know to fully understand how Mastodon works. It is conceived as a series of independent and menu selectable chapters (just click on the “Guide” link to show the summary menu), but it can also be read as a single text. It also hosts a <a href="instances">list of instances</a> that you can browse and use as a starting point to find the Mastodon instance that best fits your needs.</p>
+<p>This website is an introduction to Mastodon’s basic concepts and features which you should know to fully understand how Mastodon works. It is conceived as a series of independent and menu selectable chapters (just click on the “Guide” link to show the summary menu), but it can also be read as a single text. It also hosts a <a href="instances">list of instances</a> that you can browse and use as a starting point to find the Mastodon instance that best fits your needs.</p>
 </section>
 
 <section class="lev1sect">
@@ -105,7 +105,7 @@
 <p>There is no such thing as <em>a</em> social network called Mastodon! Instead there are thousands of independent social networks called Mastodon Instances.</p>
 <p>Every Instance has its own server, community, rules, admins and moderation.</p>
 <p>From every Mastodon Instance it is possible to interact with users who are on other Instances. It works exactly like e-mail does (you can send an e-mail from Gmail to Yahoo, right?). That’s because all the Instances can be mutually interconnected.</p>
-<p>Every Instance administrator can choose to silence or completely block a user of his or another Instance but also block another Instance as a whole (for example because it has bad moderation and allows trolling).</p>
+<p>Every Instance administrator can choose to silence or to completely block a user of his own or another Instance, but also to block another Instance as a whole (for example because it has bad moderation and allows trolling).</p>
 </section>
 
 <section>
@@ -119,7 +119,7 @@
 <h3><a name="WhoOwnsMastodon" id="WhoOwnsMastodon" class="anchor"></a>Who owns Mastodon?</h3>
 <p>Everybody! Since Mastodon is a Free and Open Source platform, everybody can use, modify and install it on his own server. Moreover the people who developed Mastodon do not own any copyright on it. That’s the basic philosophy of Free and Open Source Software: to share useful software that everybody can use freely and collaborate to improve it.</p>
 <p><img src="<?php echo($prepath); ?>imgs/WhoOwnsMastodon.jpg" class="image" alt="A girl with her arms opened in front of a blue sky. Photo by Jess Hall." title="Photo by Jess Hall."></p>
-<p>Your personal data and content is located on the Instance you have chosen. That means that only the instance admins can have access to it (like on every social platform).</p>
+<p>Your personal data and contents are located on the Instance you have chosen. That means that only the instance admins can have access to it (like on every social platform).</p>
 </section>
 
 <section class="lev1sect">