MastodonHelp/web/clitools/mustool.php

295 lines
11 KiB
PHP
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/php
<?php
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('N',"\n");
require(__DIR__.'/lib/delinstbyid.php');
$levs=['Debug', 'Info', 'Warning', 'Error'];
$opts=array(
'shuffle'=>false,
'updstats'=>false,
'clean'=>false,
'clean_notifs_before_weeks'=>24,
'clean_keep_checks'=>16,
'deleteinstswhere'=>false,
'optimize'=>false
);
$help='mustool.php
DESCRIPTION
mustool.php can do lots of things on Mastodon Helps database.
SYNOPSIS
mustool.php [options] <action> [parameters] ...
ACTIONS
deleteinstswhere <condition[s]>
First, return a list of Instances records matching “condition”, then let
you choose whether you want to delete them and all records referencing them
in other tables.
Example: mustool.php deleteinstswhere "IsMastodon!=1"
shuffle
Randomize instances list (values in «RPos» column).
updstats
Update sites statistics.
clean
Delete records older than '.$opts['clean_notifs_before_weeks'].' weeks from «Notifications» table, and for each
«Instances» record delete all but the most recent '.$opts['clean_keep_checks'].' records from
«InstChecks» table.
optimize
Optimize all the tables in the database.
OPTIONS
-h, --help
Shows this help text and exits.
This program comes with ABSOLUTELY NO WARRANTY; for details see the source.
This is free software, and you are welcome to redistribute it under
certain conditions; see <http://www.gnu.org/licenses/> for details.'.N;
$dosome=false;
for ($i=1; $i<$argc; $i++) {
if ($argv[$i]=='-h' || $argv[$i]=='--help') {
echo($help);
exit(0);
} elseif ($argv[$i]=='deleteinstswhere') {
if ($i==$argc-1) mexit('«'.$argv[$i].'» requires a MySQL condition as an argument (use «-h» to read help).'.N,1);
$i++;
$dosome=true;
$opts['deleteinstswhere']=true;
$opts['deleteinstswhereconds']=$argv[$i];
} elseif ($argv[$i]=='shuffle') {
$dosome=true;
$opts['shuffle']=true;
} elseif ($argv[$i]=='updstats') {
$dosome=true;
$opts['updstats']=true;
} elseif ($argv[$i]=='clean') {
$dosome=true;
$opts['clean']=true;
} elseif ($argv[$i]=='optimize') {
$dosome=true;
$opts['optimize']=true;
} else {
mexit('dont know how to interpret «'.$argv[$i].'» (use «-h» to read help).'.N,1);
}
}
if (!$dosome) mexit('no action was specified (use «-h» to read help).'.N,1);
use function mysqli_real_escape_string as myesc;
$inifp=__DIR__.'/../conf/mustard.ini';
$iniarr=@parse_ini_file($inifp);
if ($iniarr==false) mexit('Could not open configuration file «'.$inifp.'»'.N,1);
try { $link=@mysqli_connect($iniarr['db_host'],$iniarr['db_admin_name'],$iniarr['db_admin_password'],$iniarr['db_name'],$iniarr['db_port'],$iniarr['db_socket']); }
catch (Exception $error) { mexit('could not connect to MySQL server: '.mysqli_connect_error().'.'.N,1,true); }
// for php versions < 8
if ($link===false) mexit('could not connect to MySQL server: '.mysqli_connect_error().'.'.N,1,true);
try { $res=mysqli_set_charset($link,'utf8mb4'); }
catch (Exception $error) { mexit('could not set «utf8mb4» charset for MySQL: '.mysqli_error($link).' ['.mysqli_errno($link).'].'.N,1,true); }
// for php versions < 8
if ($res===false) mexit('could not set MySQL charset: '.mysqli_error($link).' ['.mysqli_errno($link).'].'.N,1,true);
if ($opts['deleteinstswhere']) {
$res=myq($link,'SELECT ID, URI FROM Instances WHERE '.$opts['deleteinstswhereconds']);
$buf=[];
while ($row=mysqli_fetch_assoc($res)) $buf[]=$row;
$cbuf=count($buf);
if ($cbuf) {
foreach ($buf as $row) echo($row['URI'].' (ID='.$row['ID'].')'.N);
echo('Do you really want to delete those '.$cbuf.' record(s)? Enter «YES» to do it, anything else to not do it: ');
$inp=rtrim(fgets(STDIN));
if ($inp=='YES') {
$i=0;
foreach ($buf as $row) {
$i++;
eecho('deleting Instances record with ID = '.$row['ID'].' and URI = «'.$row['URI'].'», and all references to it ('.$i.'/'.$cbuf.', '.round(100/$cbuf*$i,2).'%)'.N,1);
$res=delinstbyid($link,$row['ID'],'eecho',N);
if (!$res) mexit('error trying to delete Instances record with ID='.$row['ID'].'; see the log above for more info.'.N,2);
}
}
} else {
eecho('no Instances records match expression «'.$opts['deleteinstswhereconds'].'».'.N);
}
}
if ($opts['shuffle']) {
eecho('randomizing values in «RPos» column...'.N,1);
$res=myq($link,'SELECT ID FROM Instances');
$i=0;
while ($row=mysqli_fetch_assoc($res)) {
$i++;
$buf[$row['ID']]=$i;
}
shuffle($buf);
foreach ($buf as $key=>$val)
myq($link,'UPDATE Instances SET RPos='.$val.' WHERE ID='.$key);
eecho('done! Affected rows: '.count($buf).'.'.N,1);
}
if ($opts['updstats']) {
$day=24*60*60;
$now=time();
$tdstart=gmmktime(0,0,0,gmdate('n',$now),gmdate('j',$now),gmdate('Y',$now));
//echo('Today started at '.$tdstart.' ('.gmdate('d M Y H:i:s',$tdstart).').'.N);
// this below, if uncommented, populates DESTRUCTIVELY table ZHits for testing purposes
/*myq($link,'DELETE FROM ZHits WHERE TS < '.$tdstart);
myq($link,'DELETE FROM ZStats');
$uids=array(
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
'cccccccccccccccccccccccccccccccc',
'dddddddddddddddddddddddddddddddd',
'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
'ffffffffffffffffffffffffffffffff'
);
$langs=array('ca','de','en','es','fr','gl','it','pt_BR','uk');
$urls=array('home','instances','users','about','links','stats','contribute','404');
for ($i=0; $i<1460; $i++) myq($link,'INSERT INTO ZHits (UID,URL,Lang,TS) VALUES ("'.$uids[rand(0,count($uids)-1)].'","'.$urls[rand(0,count($urls)-1)].'","'.$langs[rand(0,count($langs)-1)].'",'.rand($now-365*24*60*60,$tdstart).')');*/
$res=myq($link,'SELECT * FROM ZHits WHERE TS < '.$tdstart.' ORDER BY TS ASC');
if (mysqli_num_rows($res)>0) {
$row=mysqli_fetch_assoc($res);
$dstart=gmmktime(0,0,0,gmdate('n',$row['TS']),gmdate('j',$row['TS']),gmdate('Y',$row['TS']));
eecho('updating statistics...'.N,1);
} else {
mexit('statistics are already up to date :-)'.N,0);
}
$inserts=0;
while ($dstart<$tdstart) {
//echo('-------- '.gmdate('d M Y H:i:s',$dstart).' ---------'.N);
$inserts++;
$hits=0;
// this line below must be synced with $supplangs in ../site/index.php
$hitslang=array('ca'=>0, 'de'=>0, 'en'=>0, 'es'=>0, 'fr'=>0, 'gl'=>0, 'it'=>0, 'pt_BR'=>0, 'uk'=>0);
// this line below must be synced with the urls we consider, see ../site/index.php
$hitspage=array('home'=>0, 'instances'=>0, 'users'=>0, 'about'=>0, 'links'=>0, 'stats'=>0, 'contribute'=>0, '404'=>0);
$visits=0;
$buf=array();
$res=myq($link,'SELECT * FROM ZHits WHERE TS >= '.$dstart.' AND TS < '.($dstart+$day).' ORDER BY TS ASC');
while ($row=mysqli_fetch_assoc($res)) {
//echo($row['UID'].' '.$row['URL'].' '.$row['Lang'].' '.$row['TS'].N);
$hits++;
$hitslang[$row['Lang']]++;
$hitspage[$row['URL']]++;
if (!in_array($row['UID'],$buf)) {
$buf[]=$row['UID'];
$visits++;
}
}
$buf='';
foreach ($hitslang as $key=>$val) $buf.=$key.':'.$val.';';
$hitslang=substr($buf,0,-1);
$buf='';
foreach ($hitspage as $key=>$val) $buf.=$key.':'.$val.';';
$hitspage=substr($buf,0,-1);
//echo('>>> hits: '.$hits.', hitslang: '.$hitslang.', hitspage: '.$hitspage.', visits: '.$visits.' <<<'.N);
$query='INSERT INTO ZStats (TS, Hits, HitsLang, HitsPage, Visits) VALUES ('.$dstart.', '.$hits.', "'.$hitslang.'", "'.$hitspage.'", '.$visits.')';
//echo($query.N);
myq($link,$query);
$dstart+=$day;
}
myq($link,'DELETE FROM ZHits WHERE TS < '.$tdstart);
eecho('done! Affected rows: '.$inserts.'.'.N,1);
}
if ($opts['clean']) {
$ago=time()-($opts['clean_notifs_before_weeks']*7*24*60*60);
eecho('cleaning records older than '.$opts['clean_notifs_before_weeks'].' weeks from «Notifications» table...'.N,1);
$res=myq($link,'DELETE FROM Notifications WHERE Microtime < '.$ago);
eecho('done! Affected rows: '.mysqli_affected_rows($link).'.'.N,1);
eecho('cleaning records from «InstChecks» table, keeping only the most recent '.$opts['clean_keep_checks'].' for each instance...'.N,1);
$res=myq($link,'SELECT ID FROM Instances WHERE TotChecks > '.$opts['clean_keep_checks']);
$nrows=mysqli_num_rows($res);
$p=0;
$totar=0;
while ($row=mysqli_fetch_assoc($res)) {
$p++;
//echo('Working on instance '.$p.'/'.$nrows.' (ID = '.$row['ID'].')...'.N);
$rres=myq($link,'SELECT Time FROM InstChecks WHERE InstID='.$row['ID'].' ORDER BY Time DESC');
$instchecks=[];
while ($rrow=mysqli_fetch_assoc($rres)) $instchecks[]=$rrow;
if (count($instchecks)>$opts['clean_keep_checks']) {
while(count($instchecks)>$opts['clean_keep_checks']) $bef=array_pop($instchecks);
$bef=$bef['Time'];
myq($link,'DELETE FROM InstChecks WHERE InstID='.$row['ID'].' AND Time<='.$bef);
$ar=mysqli_affected_rows($link);
//echo('Deleted '.$ar.' records from InstChecks table.'.N);
$totar+=$ar;
}/* else {
echo('No InstChecks records to delete.'.N);
}*/
}
eecho('done! Total affected rows: '.$totar.'.'.N,1);
}
if ($opts['optimize']) {
eecho('optimizing all the tables in the database...'.N);
$res=myq($link,'SHOW TABLES');
while ($row=mysqli_fetch_row($res)) {
$rres=myq($link,'OPTIMIZE TABLE '.$row[0]);
$rrow=mysqli_fetch_assoc($rres);
if ($rrow['Msg_type']=='error' || $rrow['Msg_type']=='warning')
eecho(kimplode($rrow).N,2);
}
eecho('done!'.N,1);
}
mysqli_close($link);
exit(0);
// functions
function kimplode(&$arr) {
$buf=[];
foreach ($arr as $key=>$val)
$buf[]=$key.': '.$val;
return(implode('; ',$buf));
}
function myq(&$link,$query) {
try { $res=mysqli_query($link,$query); }
catch (Exception $error) { mexit('Query «'.$query.'» failed: '.$error->getMessage().'.'.N,2); }
// for php versions < 8, which seem to not catch mysql exceptions
if ($res===false) mexit('Query «'.$query.'» failed: '.mysqli_errno($link).': '.mysqli_error($link).'.'.N,2);
return($res);
}
function mexit($msg,$code) {
global $link;
if (isset($link) && $link!==false) mysqli_close($link);
if ($code==0)
eecho($msg,1);
else
eecho($msg,3);
exit($code);
}
function eecho($msg,$lev=1) {
global $levs;
$time=microtime(false);
$time=explode(' ',$time);
$time=date('Y-m-d H:i:s',$time[1]).'.'.substr($time[0],2);
$msg=$time.' '.$levs[$lev].': '.$msg;
if ($lev<2)
echo($msg);
else
fwrite(STDERR,$msg);
}
?>