commit 0150d5746732720715abf320f39ffa50643b7ae7 Author: pezcurrel Date: Fri Sep 27 21:20:18 2024 +0200 First commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..2dbf7ad --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +```text +[[[ SYNOPSIS ]]] + + pfaltgall [options] + +[[[ DESCRIPTION ]]] + + This is pfaltgall v0.1, a CLI PHP script that can generate an html file with +a gallery from your Pixelfed profile. + In order to create it, you just need to login to your Pixelfed account and +get an app token (Settings -> Applications -> Create new token), then create +a configuration file for pfaltgall like this (don’t write the «---» lines): + +--- +host=your_instance_host +token=your_token +--- + + For example: + +--- +host=pixelfed.social +token=as7f8a7s0d89f7as97df09a8s7d90f81jkl2h34lkj12h3jkl4 +--- + + Then run pfaltgall with the path of the configuration file you have +created. This will create an «index.html» file that will be ready to be put +where you want (you can also see it locally, obviously). + +[[[ OPTIONS ]]] + + -h, --help + Show this help text and exit. + +[[[ DISCLAIMER AND LICENSE ]]] + + 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 for details. + +``` diff --git a/lib/ckratelimit.php b/lib/ckratelimit.php new file mode 100644 index 0000000..e0843dd --- /dev/null +++ b/lib/ckratelimit.php @@ -0,0 +1,50 @@ +false,'error'=>'no «date» header']; + if (!isset($aaheaders['x-ratelimit-reset'])) return ['ok'=>false,'error'=>'no «x-ratelimit-reset» header']; + if (!isset($aaheaders['x-ratelimit-remaining'])) return ['ok'=>false,'error'=>'no «x-ratelimit-remaining» header']; + if (preg_match('#^\d+$#',$aaheaders['x-ratelimit-remaining'])!==1) return ['ok'=>false,'error'=>'«x-ratelimit-remaining» header is not an integer']; + $remaining=$aaheaders['x-ratelimit-remaining']+0; + $date=@strtotime($aaheaders['date']); + if (!is_int($date)) return ['ok'=>false,'error'=>'«date» header could not be converted to a unix timestamp']; + $reset=@strtotime($aaheaders['x-ratelimit-reset']); + if (!is_int($reset)) return ['ok'=>false,'error'=>'«x-ratelimit-reset» header could not be converted to a unix timestamp']; + // don't do the one on the line below, since it happens lots of times + //if ($reset<$date) return ['ok'=>false,'error'=>'the unix timestamp parsed from «x-ratelimit-reset» header is less than the unix timestamp parsed from «date» header']; + if ($remaining==0) + return ['ok'=>true,'sleep'=>$reset-$date+1,'remaining'=>$remaining]; + else + return ['ok'=>true,'sleep'=>0,'remaining'=>$remaining]; +} + +/* +// test +$context=[ + 'http'=>[ + 'header'=>"Accept: application/json\r\n"; + ] +]; +$context=stream_context_create($context); +while (true) { + $res=@file_get_contents('https://livellosegreto.it/api/v2/instance',false,$context); + echo "{$res}\n"; + print_r($http_response_header); + $rl=ckratelimit($http_response_header); + print_r($rl); + if ($rl['sleep']>0) { + echo 'Reached rate limit, sleeping for '.ght($rl['sleep']).' (until '.date('c',time()+$rl['sleep']).') ...'; + sleep($rl['sleep']); + echo "\n"; + } +} +exit(0);*/ + +?> diff --git a/lib/httpjson.php b/lib/httpjson.php new file mode 100644 index 0000000..170f1bd --- /dev/null +++ b/lib/httpjson.php @@ -0,0 +1,65 @@ +[ + 'timeout'=>$timeout, + 'method'=>$method, + 'user_agent'=>'Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0', + 'header'=>"Accept: {$accept}\r\n", + 'ignore_errors'=>true + ] + ]; + if (!is_null($token)) $context['http']['header'].="Authorization: Bearer {$token}\r\n"; + if (!is_null($postdata)) { + $context['http']['header'].="Content-type: application/x-www-form-urlencoded\r\n"; + $context['http']['content']=http_build_query($postdata); + } + $context=stream_context_create($context); + $headers=[]; + $errors=[]; + $ret=['ok'=>false,'headers'=>[],'content'=>[],'errors'=>null]; + $http_response_header=null; + $res=@file_get_contents($endpoint,false,$context); + if ($res===false) { + $errors[]="could not connect"; + } else { + if (is_array($http_response_header)) { + $httprc=null; + $li=count($http_response_header)-1; + for ($i=$li; $i>=0; $i--) { + array_unshift($headers,$http_response_header[$i]); + if (preg_match('#HTTP/\S+\s+(\d+)#',$http_response_header[$i],$matches)===1) { + $httprc=$matches[1]+0; + break; + } + } + if (is_null($httprc)) + $errors[]="got no HTTP response status code"; + elseif (!in_array($httprc,$okcodes)) + $errors[]="got «{$httprc}» HTTP response status code"; + } else { + $errors[]="«got no headers"; + } + $res=@json_decode($res,true); + if ($res===false) { + $errors[]="got no valid JSON"; + } else { + if (count($errors)>0 && isset($res['error'])) + $errors[]=$res['error']; + $ret['content']=$res; + } + } + if (count($errors)==0) + $ret['ok']=true; + $errors=implode('; ',$errors); + $ret['headers']=$headers; + $ret['errors']=$errors; + return $ret; +} + +?> diff --git a/pfaltgall b/pfaltgall new file mode 100755 index 0000000..87368fc --- /dev/null +++ b/pfaltgall @@ -0,0 +1,417 @@ +#!/usr/bin/php +. +*/ + +$SCRIPTNAME='pfaltgall'; +$SCRIPTVERSION='0.1'; + +require 'lib/ckratelimit.php'; +require 'lib/httpjson.php'; + +$configfp=null; + +$conf=[ + 'host'=>null, + 'token'=>null, + 'accid'=>null +]; + +$help= +"[[[ SYNOPSIS ]]] + + {$SCRIPTNAME} [options] + +[[[ DESCRIPTION ]]] + + This is {$SCRIPTNAME} v{$SCRIPTVERSION}, a CLI PHP script that can generate an html file with +a gallery from your Pixelfed profile. + In order to create it, you just need to login to your Pixelfed account and +get an app token (Settings -> Applications -> Create new token), then create +a configuration file for {$SCRIPTNAME} like this (don’t write the «---» lines): + +--- +host=your_instance_host +token=your_token +--- + + For example: + +--- +host=pixelfed.social +token=as7f8a7s0d89f7as97df09a8s7d90f81jkl2h34lkj12h3jkl4 +--- + + Then run {$SCRIPTNAME} with the path of the configuration file you have +created. This will create an «index.html» file that will be ready to be put +where you want (you can also see it locally, obviously). + +[[[ OPTIONS ]]] + + -h, --help + Show this help text and exit. + +[[[ DISCLAIMER AND LICENSE ]]] + + 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 for details.\n"; + +for ($i=1; $i<$argc; $i++) { + if ($argv[$i]=='-h' || $argv[$i]=='--help') { + echo $help; + exit(0); + } elseif ($argv[$i]=='--make-readme') { + file_put_contents(__DIR__.'/README.md',"```text\n{$help}\n```\n"); + exit(0); + } elseif (is_null($configfp)) { + $configfp=$argv[$i]; + } else { + eecho("Error: «{$argv[$i]}» is not a valid option and the configuration file has already been set to «{$configfp}» (use «-h» or «--help» to read help).\n"); + exit(1); + } +} + +if (is_null($configfp)) { + eecho("Error: you have not specified a config file (use «-h» or «--help» to read help).\n"); + exit(1); +} + +$fconf=@parse_ini_file($configfp); +if ($fconf===false) { + eecho("Error: {$SCRIPTNAME} could not open configuration file «{$configfp}».\n"); + exit(1); +} + +$errors=[]; +if (!array_key_exists('host',$fconf)) + $errors[]="no «host» defined"; +if (!array_key_exists('token',$fconf)) + $errors[]="no «token» defined"; +if (count($errors)>0) { + eecho("Error: {$SCRIPTNAME} has found errors in «{$configfp}» configuration file:\n"); + foreach ($errors as $val) + eecho(" - {$val}\n"); + eecho("Use «-h» or «--help» to read help.\n"); + exit(1); +} +foreach ($conf as $key=>$val) + if (array_key_exists($key,$fconf)) + $conf[$key]=$fconf[$key]; +//print_r($conf); + +$acc=httpjson("https://{$conf['host']}/api/v1/accounts/verify_credentials",null,null,null,null,$conf['token']); +//print_r($res); +if (!$acc['ok']) { + eecho("Error: {$SCRIPTNAME} could not retrieve the account id associated with the given token ({$acc['errors']}).\n"); + exit(2); +} +ckrl($acc['headers']); +$acc=$acc['content']; + +$urls=[]; +$imgs=''; +$i=0; +$ic=0; +do { + $i++; + echo "\rRetrieving chunk {$i}"; + $endpoint="https://{$conf['host']}/api/v1/accounts/{$acc['id']}/statuses?limit=40&only_media=1&exclude_replies=1&exclude_reblogs=1"; + if (isset($max_id)) $endpoint.="&max_id={$max_id}"; + $res=httpjson($endpoint,null,null,null,null,$conf['token']); + //print_r($res); + if (!$res['ok']) { + eecho("\rError: {$SCRIPTNAME} could not retrieve chunk {$i} of statuses ({$res['errors']}).\n"); + exit(2); + } + $count=count($res['content']); + if ($count>0) { + foreach ($res['content'] as $status) { + if (isset($status['created_at']) && preg_match('#^\s+$#',$status['created_at'])!==1) { + $date=strtotime($status['created_at']); + $date=' ['.date('Y/m/d',$date).']'; + } else { + $date=''; + } + if (isset($status['content']) && preg_match('#^\s+$#',$status['content'])!==1) { + $desc=strip_tags($status['content']); + $desc=preg_replace('/#\w+/','',$desc); + $desc=trim($desc); + } else { + $desc=''; + } + if (isset($status['media_attachments']) && is_array($status['media_attachments'])) { + $ca=count($status['media_attachments']); + $ia=0; + foreach ($status['media_attachments'] as $attachment) { + if (isset($attachment['url'])) { + $url=$attachment['url']; + $ia++; + if (isset($attachment['description']) && preg_match('#^\s+$#',$attachment['description'])!==1) + $altdesc=' alt="'.htmlspecialchars(trim($attachment['description']),ENT_QUOTES|ENT_HTML5).'"'; + else + $altdesc=''; + if ($ca>1) + $icnt=" ({$ia}/{$ca})"; + else + $icnt=''; + $imgs.="
{$desc}{$icnt}{$date}
\n"; + $ic++; + } + } + } + } + $max_id=$res['content'][$count-1]['id']; + //echo "count: {$count}; max_id: {$max_id}\n"; + if ($count<40) + break; + } + ckrl($res['headers']); +} while ($count>0); +echo "\n"; + +$title="{$acc['username']}@{$conf['host']}"; +if ($acc['display_name']!='') $title=htmlspecialchars($acc['display_name'],ENT_QUOTES|ENT_HTML5)." ({$title})"; + +$html=' + + +'.$title.' + + + + + + + + + + +
+
+
+

+

'.$title.'

+

'.nl2br($acc['note']).'

+
+
+'.$imgs.' +
+ + +'; + +file_put_contents('index.html',$html); + +exit(0); + + +function eecho($text) { + fwrite(STDERR,$text); +} + +function ckrl($headers) { + $rl=ckratelimit($headers); + if ($rl['ok'] && $rl['sleep']>0) { + echo "\rInfo: reached rate limit, sleeping for {$rl['sleep']} second(s)... "; + sleep($rl['sleep']); + echo "\n"; + } +} + +?>