1
0
Fork 0

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

This commit is contained in:
pezcurrel 2019-12-06 08:28:36 +01:00
parent d2975c46f8
commit 900454889f
2 changed files with 310 additions and 153 deletions

View file

@ -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 lopzione «-, 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 unistanza 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 lunità 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('Lopzione «'.$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: lopzione «'.$key.'» in «'.$inifp.'» è sconosciuta e sarà ignorata.'.N);
}
if (array_key_exists('excludeafter',$opts)) {
$opts['excludeafter']=tosec($opts['excludeafter']);
if ($opts['excludeafter']===false)
mexit('Lopzione «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('Lopzione «'.$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('Lopzione «'.$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('Lopzione «'.$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('Lopzione «'.$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('Lopzione «'.$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);
}
} else {
mexit(N.'Non ho potuto caricare il file delle istanze di partenza «'.$opts['startinstancesfp'].'», muoio.'.N,1);
}
if (count($startinstances)<1)
mexit('Il file delle istanze di partenza «'.$opts['startinstancesfp'].'» non contiene alcuna voce, muoio.'.N,1);
$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);
}
}
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);
}
} else {
echo('NON recupero la lista delle istanze bloccate da "'.$dom.'": la url della stessa non è definita.'.N);
}
ksort($biglist);
echo('Totale istanze note: '.count($biglist).N);
}
ksort($blinstances);
echo(count($blinstances).' istanze bloccate.'.N);
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;
}
}
} else {
echo('ERRORE :-('.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);
}
} 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;
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('e non riproveremo...'.N);
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 {
$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 {
$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 allultima 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 allultima volta)'.N);
?>

View file

@ -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 Mastodons 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 Mastodons 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?). Thats 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. Thats 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">