.
*/
// Logorrh, Verbose, Charliero, Tashugo?
$dodebug=false;
$debug='';
require 'lib/gettlds.php';
$tlds=gettlds('storage/tlds.txt',false);// we don't get them in "regex format" because we need them for js too, so we'll rsort them later
if (!is_null($tlds['warnings'])) $debug.="GetTLDs warning: {$tlds['warnings']}. \n";
rsort($tlds);
$retlds=implode('|',$tlds['tlds']);
$jstlds="const tlds=['".implode("', '",$tlds['tlds'])."'];\n";
$jstldsfp='storage/tlds.js';
if (@file_put_contents($jstldsfp,$jstlds)===false)
dieyoung("Error: could not save «{$jstldsfp}»");
unset($tlds,$jstlds);
require 'lib/mastodon.php';
require 'lib/ckratelimit.php';
require 'lib/getfirstbrowserlang.php';
require 'lib/ght.php';
require 'lib/booltostr.php';
const SNAME='Verbose';
const SVERS='0.3.7';
const SREPO='https://git.lattuga.net/jones/verbose';
const DEFAC=500;
const MINAC=100;
const MAXAC=100000;
const MINCL=40;
const MAXPOSTC=50000;
const MAXREPTOLEN=1500;
const POSTPAUSE=250;// pause between sending split posts (in milliseconds)
const REQSTOPRESV=10;// requests to preserve
const CONFFN='conf.ini';
$sname=SNAME;
$svers=SVERS;
$now=time();
$timeout=5;
$formact=preg_replace('#[^/]+$#','',$_SERVER['REQUEST_URI']);
ini_set('max_execution_time',360);// 6 minutes: default mastodon ratelimits: https://docs.joinmastodon.org/api/rate-limits/
$conf=['link'=>"\n\n[This post was split using ".SREPO.']'];
if (file_exists(CONFFN)) {
$fconf=@parse_ini_file(CONFFN,false,INI_SCANNER_RAW);
if ($fconf===false) {
dieyoung('Error: configuration file «'.CONFFN.'» was found but could not be opened for reading.');
} else {
if (isset($fconf['link']))
$conf['link']="\n\n{$fconf['link']}";
if (isset($fconf['footer']))
$conf['footer']=$fconf['footer'];
if (isset($fconf['webservertimeout'])) {
if (preg_match('#^\d+$#',$fconf['webservertimeout'])===1)
$conf['webservertimeout']=$fconf['webservertimeout']+0;
else
dieyoung('Error: configuration file «'.CONFFN.'»: value «'.$fconf['webservertimeout'].'» is not valid for «webservertimeout».');
} else {
dieyoung('Error: configuration file «'.CONFFN.'» does not define «webservertimeout».');
}
}
unset($fconf);
} else {
dieyoung('Error: configuration file «'.CONFFN.'» was not found.');
}
$debug.='CONF: '.preprint($conf)." \n";
$coopts=[
'expires'=>$now+365*24*60*60,
'path'=>preg_replace('#[^/]+$#','',$_SERVER['REQUEST_URI']),
'domain'=>$_SERVER['SERVER_NAME'],
'secure'=>false,
'httponly'=>false,
'samesite'=>'Lax'// None || Lax || Strict
];
$cooptsx=$coopts;
$cooptsx['expires']=$now-3600;
/*setcookie('verbose_client_id','',$cooptsx);
setcookie('verbose_client_secret','',$cooptsx);
setcookie('verbose_host','',$cooptsx);
setcookie('verbose_token','',$cooptsx);*/
$debug.='COOKIES: '.preprint($_COOKIE);
$debug.='POST: '.preprint($_POST);
$blang=getfirstbrowserlang();
$blang=preg_replace('#^([a-z]+).*#','$1',$blang);
$host=false;
$token=false;
(isset($_SERVER['HTTPS'])) ? $proto='https' : $proto='http';
$rediruri=$proto.'://'.$_SERVER['HTTP_HOST'].preg_replace('#^([^?]*).*#','$1',$_SERVER['REQUEST_URI']);
$authmsgs='';
$scope='read write:statuses';
$provided='you provided';
setcookie('verbose_client_id','',$cooptsx);
setcookie('verbose_client_secret','',$cooptsx);
(isset($_POST['host'])) ? $_POST['host']=trim($_POST['host']) : $_POST['host']='';
(isset($_POST['token'])) ? $_POST['token']=trim($_POST['token']) : $_POST['token']='';
if (isset($_GET['code']) && isset($_COOKIE['verbose_host']) && isset($_COOKIE['verbose_client_id']) && isset($_COOKIE['verbose_client_secret'])) {
$host=$_COOKIE['verbose_host'];
$postcont=['grant_type'=>'authorization_code', 'code'=>$_GET['code'], 'client_id'=>$_COOKIE['verbose_client_id'], 'client_secret'=>$_COOKIE['verbose_client_secret'], 'redirect_uri'=>$rediruri, 'scope'=>$scope];
$res=mastpost($host,null,'/oauth/token',$postcont,$timeout);
if ($res['ok']) {
$res=$res['data'];
$token=$res['access_token'];
setcookie('verbose_token',$token,$coopts);
$authmsgs.="
You successfully authorized {$sname} to use your account on {$host}. \nYour access token is {$token} \nPlease save it somewhere safe, like in a password manager (i suggest KeePassXC), because {$sname} does not save it on the server, but only in a cookie in your browser that lasts one year since your last visit, so it may expire or you may loose it and, without your access token, you would have to repeat the authorization process.
\n";
} else {
$authmsgs.='
Sorry, there was an error trying to authorize you: '.htmlentities($res['error'])."
Warning: if you authorize now you’ll loose your current split session
';
(preg_match('#^\d+$#',$_POST['avchars'])===1) ? $_POST['avchars']+=0 : $_POST['avchars']=DEFAC;
} else {
$_POST['avchars']=DEFAC;
}
if ($_POST['avchars']MAXAC) $_POST['avchars']=MAXAC;
$maxcwprec=$_POST['avchars']-MINCL;
if (!isset($_POST['cw']) || trim($_POST['cw'])=='') $_POST['cw']='';
$cwlen=mb_strlen($_POST['cw'],'UTF-8');
if (!isset($_POST['pre']) || trim($_POST['pre'])=='') $_POST['pre']='';
$prelen=mb_strlen($_POST['pre'],'UTF-8')+2;
$cwprelen=$cwlen+$prelen;
if ($cwprelen>$maxcwprec) $splitmsgs.="
“Content Warning” + “Text to prepend” is {$cwprelen} characters long, that is ".($cwprelen-$maxcwprec)." characters longer than its maximum allowed length ({$maxcwprec} characters, that is {$_POST['avchars']} available characters minus ".MINCL." characters)
\n";
//$_POST['pre']=mb_substr($_POST['pre'],0,$maxprec,'UTF-8');
if (!isset($_POST['post']) || trim($_POST['post'])=='') $_POST['post']='';
$postlen=mb_strlen($_POST['post'],'UTF-8');
if ($postlen>MAXPOSTC) $splitmsgs.="
“Post to split” is {$postlen} characters long, that is ".($postlen-MAXPOSTC)." characters longer than its maximum allowed length, ".MAXPOSTC." characters
\n";
//$_POST['post']=mb_substr($_POST['post'],0,MAXPOSTC,'UTF-8');
$rbsck=[
'cntpos_before'=>'',
'cntpos_after'=>'',
'addref_no'=>'',
'addref_ifav'=>'',
];
if (isset($_POST['cntpos']) && $_POST['cntpos']=='before') {
$cntbef=true;
$rbsck['cntpos_before']=' checked';
} else {
$_POST['cntpos']='after';
$cntbef=false;
$rbsck['cntpos_after']=' checked';
}
if (isset($_POST['addref']) && $_POST['addref']=='no') {
$aliif=false;// "add link if it fits"
$rbsck['addref_no']=' checked';
} else {
$_POST['addref']='ifav';
$aliif=true;
$rbsck['addref_ifav']=' checked';
}
if (!isset($_POST['replyto']) || trim($_POST['replyto'])=='') $_POST['replyto']='';
if (strlen($_POST['replyto'])>MAXREPTOLEN) {
$_POST['replyto']='';
$splitmsgs.='
The “URL of post to reply to” you specified is too long (its maximum allowed length is '.MAXREPTOLEN." characters)
\n";
if ($res['ok']) {
if (isset($res['data']['statuses'][0]['id']))
$reptopost=$res['data']['statuses'][0];
else
$splitmsgs.="
Sorry, {$sname} found no post with the URL you provided as “URL of post to reply to”, please check it and make sure it points to a post you have access to.
\n";
} else {
$splitmsgs.="
Sorry, {$sname} encountered the following error while trying to retrieve the URL you provided as “URL of post to reply to”: ".htmlentities($res['error'])."; please check the URL and make sure it points to a post you have access to.
\n";
}
if (isset($reptopost)) {
if (isset($reptopost['id']))
$reptoid=$reptopost['id'];
if (isset($_POST['setcws']) && $_POST['setcws']=='1' && isset($reptopost['spoiler_text']) && trim($reptopost['spoiler_text'])!='')
$_POST['cw']=$reptopost['spoiler_text'];
if (isset($_POST['setments']) && $_POST['setments']=='1' && isset($reptopost['mentions']))
$_POST['pre']=getpostmentions($reptopost,[$myacc['acct']],'recipients');
if (isset($_POST['setvisib']) && $_POST['setvisib']=='1' && isset($reptopost['visibility']))
$_POST['visib']=$reptopost['visibility'];
if (isset($_POST['setlang']) && $_POST['setlang']=='1' && isset($reptopost['language']))
$_POST['lang']=$reptopost['language'];
}
}
$headmsgs='';
$intro="
Hello, this is a {$sname} instance, it can split your long post considering Mastodon rules: every http(s) link counts for 23 characters and every mention counts only for its username part. Each split post comes with “…” signs where it makes sense and with a counter in “[n/t]” form, where “n” is current post number and “t” is total posts number.
{$sname} doesn’t save anywhere what you write or paste into it, it doesn’t use third parties’ cookies, it sets some cookies of its own only if you choose to connect it to your account, and it can be used even without Javascript.
Sorry, you have to wait ".ght($postwait)." more before {$sname} can post the remaining split post(s).
\n";
$pbtext='Please wait '.ght($postwait).' before posting';
} else {
for ($i=$postsoffset; $i<$postscount; $i++) {
if (time()-$now+1>=$conf['webservertimeout']) {
$postingok=false;
$postsoffset=$i;
$postmsgs.="
{$sname} could not post all the split posts because «{$_SERVER['SERVER_NAME']}» HTTP server was reaching its timeout (".ght($conf['webservertimeout']).'). You can send the remaining split posts '.($i+1)."-{$postscount} below.
\n";
break;
} else {
$post=$posts[$i];
$cont=[
'status'=>$post['post'],
'visibility'=>$_POST['visib'],
'language'=>$_POST['lang']
];
if ($post['cw']!='')
$cont['spoiler_text']=$post['cw'];
if ($i==0 && $reptoid!='')
$cont['in_reply_to_id']=$reptoid;
elseif (isset($lspostid))
$cont['in_reply_to_id']=$lspostid;
if ($i>0 && $_POST['visib']=='public')
$cont['visibility']='unlisted';
//echo ($i+1).preprint($cont)." \n";
$res=mastpost($host,$token,'/api/v1/statuses',$cont,$timeout);
//if ($i==2) $res=['ok'=>false,'error'=>'server exploded'];// test err
//$res=['ok'=>true,'data'=>['id'=>'12345']];// test err
if ($res['ok']) {
$lspostid=$res['data']['id'];
$debug.="lspostid: {$lspostid} \n";
$rls=ckratelimit($res['headers']);
//if ($i==2) $rls=['ok'=>true,'remaining'=>0,'sleep'=>3895];// test err
//$rls=['ok'=>true,'remaining'=>0,'sleep'=>5];// test err
if ($rls['ok'] && $rls['remaining']<=REQSTOPRESV && $i<$postscount-1) {
$postingok=false;
$postwait=$rls['sleep'];
$postwaituntil=time()+$postwait;
$postsoffset=$i+1;
$postmsgs.='
Sending split post '.($i+1)."/{$postscount}, {$sname} reached your {$host}’s account posting rate limit, so it stopped sending; you’ll find the rest of your split posts (those which have not been sent, ".($i+2)."-{$postscount}) below; before posting them, you’ll have to wait ".ght($postwait)." for rate limit reset.
\n";
$pbtext='Please wait '.ght($postwait).' before posting';
break;
}
if ($i<$postscount-1)
usleep(POSTPAUSE*1000);
} else {
$postingok=false;
$postsoffset=$i;
$postmsgs.='
Trying to send split post '.($i+1)."/{$postscount}, {$sname} encountered the following error: «".htmlentities($res['error']).'»; so it stopped sending. You can (re)try sending split posts '.($i+1)."-{$postscount} below.
\n";
break;
}
}
}
if ($postingok) {
$postmsgs.="
{$sname} successfully sent {$i}/{$postscount} split posts :-)
If you don’t want to connect {$sname} to your account, you can just skip to the “Split post” section, but after splitting your long post you’ll have to copy and paste by hand each split post in sequence into Mastodon (i recommend “chain posting”, that is replying to the first post with the second, to the second with the third, and so on).
If you choose instead to connect {$sname} to your account, you’ll be able to post all split posts at once directly from within {$sname}, they will be automatically “chain posted” and, before posting them, you may set their visibility, language, and a post to reply to with the first split post.
To connect {$sname} to your account
if you already have an authentication token you just need to specify your server’s hostname and your token (your current split session will be unaffected);
if you have not an authentication token yet, you first have to authorize {$sname} to access your account by specifying only your server’s hostname, leaving the “Your token” field empty; then you’ll be guided through a very quick and easy one-time authorization process, at the end of which {$sname} will tell you your token for future connections, and will be already connected to your account - but please note that this process will make you loose your current split session.
';
$svers.='-'.rand(10000,999999);
} else {
$debug='';
}
(isset($_POST['act']) && $_POST['act']=='split') ? $splitfh='' : $splitfh=' class="fullheight"';
header('Content-Language: en');
echo "
{$sname}: a post splitter for Mastodon
Elo!
Uz!
Elo!
{$debug}
{$headmsgs}
{$sname}: a post splitter for Mastodon
{$intro}
{$auth}
Split post
{$splitmsgs}
\n
\n";
if ($splitmsgs=='') {
if (isset($_POST['act']) && $_POST['act']=='split' && $_POST['post']!='') {
$linklen=postlength($conf['link']);
($_POST['pre']!='') ? $pre=$_POST['pre']."\n\n" : $pre='';
$posts=splitpost($_POST['post'],$_POST['avchars'],$_POST['cw'],$pre,$cntbef);
$postscount=count($posts);
$errposts=[];
for ($i=0; $i<$postscount; $i++)
if ($posts[$i]['mastlen']>$_POST['avchars']) $errposts[]="{$i}";
$epc=count($errposts);
if ($epc>0) {
if ($epc==1)
echo "
Sorry, the length of one split post (namely {$errposts[0]}) exceeds {$_POST['avchars']} characters.";
else
echo "
Sorry, the length of {$epc} split posts (namely ".implode(', ',$errposts).") exceeds {$_POST['avchars']} characters.";
echo " Please report this bug here, possibly citing the original post text.
\n";
}
if ($postscount>1 && $aliif && $posts[$postscount-1]['mastlen']+$linklen<=$_POST['avchars']) {
$posts[$postscount-1]['post'].=$conf['link'];
$posts[$postscount-1]['mastlen']+=$linklen;
}
}
if ($postscount>0) {
//if ($loggedin) $postmsgs.="