Major refactoring: now it usese the JSON feed instead of the RSS one; many minor changes; bumped version to 0.5
This commit is contained in:
parent
15597fa1cc
commit
2458d03010
1 changed files with 181 additions and 186 deletions
367
gancioff
367
gancioff
|
@ -16,12 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
$SNAME='GancioFF';
|
||||
$SNAME='GancioF2F';
|
||||
$ENAME=strtolower($SNAME);
|
||||
$SVERS='0.4.4';
|
||||
$SVERS='0.5';
|
||||
|
||||
require __DIR__.'/lib/ckmkeys.php';
|
||||
require __DIR__.'/lib/gettlds.php';
|
||||
require __DIR__.'/lib/mastodon-postLength.php';
|
||||
require __DIR__.'/lib/mb_ucfirst.php';
|
||||
require __DIR__.'/lib/hashtag.php';
|
||||
require __DIR__.'/lib/html2text.php';
|
||||
require __DIR__.'/lib/curl.php';
|
||||
|
@ -33,16 +35,17 @@ $help=
|
|||
|
||||
[[[ DESCRIPTION ]]]
|
||||
|
||||
This is {$SNAME} v{$SVERS}, a CLI PHP script that can be used to periodically
|
||||
fetch the RSS feed from an instance of Gancio (https://gancio.org) and post
|
||||
its new entries – the events announcements – on the fediverse through a
|
||||
Mastodon account, recording into a state file a reference to each already
|
||||
posted announcement in order to post only new or changed ones on each run.
|
||||
This is {$SNAME} v{$SVERS} («GancioFeed2Fedi»), a CLI PHP script that can
|
||||
be used to periodically fetch the JSON feed from an instance of Gancio
|
||||
(https://gancio.org) and post its new or changed events announcements on the
|
||||
Fediverse through a Mastodon account, recording into a state file a reference
|
||||
to each already posted announcement in order to post only new or changed ones
|
||||
on each run.
|
||||
It can be useful, for example, when the admins of a Gancio instance chose not
|
||||
to use its federation feature because it would be too heavy on its server:
|
||||
in fact, {$SNAME} is a light alternative to federating the Gancio instance,
|
||||
moving from its server to the one running Mastodon the burden of posting each
|
||||
announcement to each fediverse instance hosting at least one follower, and of
|
||||
announcement to each Fediverse instance hosting at least one follower, and of
|
||||
sending them the image a Gancio user can attach to each announcement, because
|
||||
{$SNAME} will fetch it only once and attach it to the Mastodon post; moreover,
|
||||
by default, if an announcement on the Gancio instance fits into a Mastodon
|
||||
|
@ -62,9 +65,9 @@ as an argument on the command line.
|
|||
--- Example configuration file ---
|
||||
# Lines beginnig with a «#» and empty lines will be ignored
|
||||
|
||||
# «feed_url» is required to specify the URL to fetch the RSS feed from.
|
||||
# «feed_hostname» is required to specify the hostname of the Gancio instance.
|
||||
# For example:
|
||||
feed_url = https://gancio.some.domain/feed/rss?show_recurrent=true
|
||||
feed_hostname = gancio.some.domain
|
||||
|
||||
# «fedi_hostname» is required to specify the hostname of the Mastodon instance
|
||||
# you want to post to. For example:
|
||||
|
@ -85,12 +88,12 @@ fedi_token = w6oQ_Ot2LSAm_Q31hrvp0asfl22ip3O4ipYq1kV1ceY
|
|||
# announcements (on every run, {$SNAME} will check this file for entries older
|
||||
# than one year and discard them, to avoid the state file to grow too much).
|
||||
# For example:
|
||||
state_file_absolute_path = /var/local/cache/gancio.some.domain.feed.state
|
||||
state_file_absolute_path = /var/local/cache/gancioff/gancio.some.domain.state
|
||||
|
||||
# «timezone» is required to specify the timezone of the Gancio instance, in
|
||||
# order for {$SNAME} to calculate the correct datetimes. You can list the
|
||||
# supported timezones using option «-T» or «--timezones» (see the related
|
||||
# entry in the «OPTIONS» section. For example:
|
||||
# entry in the «OPTIONS» section). For example:
|
||||
timezone = Europe/Rome
|
||||
|
||||
# «posts_language» is required to specify the ISO 639-1 code for the language
|
||||
|
@ -180,7 +183,7 @@ conditions; see <http://www.gnu.org/licenses/> for details.\n";
|
|||
$confFP=null;
|
||||
|
||||
$conf=[
|
||||
'feed_url'=>['required'=>true, 'default'=>null],
|
||||
'feed_hostname'=>['required'=>true, 'default'=>null],
|
||||
'fedi_hostname'=>['required'=>true, 'default'=>null],
|
||||
'fedi_token'=>['required'=>true, 'default'=>null],
|
||||
'state_file_absolute_path'=>['required'=>true, 'default'=>null],
|
||||
|
@ -324,9 +327,9 @@ if (file_exists($conf['state_file_absolute_path'])) {
|
|||
$i=0;
|
||||
$buff=file($conf['state_file_absolute_path'],FILE_IGNORE_NEW_LINES);
|
||||
foreach ($buff as $key=>$val) {
|
||||
if (preg_match('#^(\d+)\t(\d+)\t(\S+)$#',$val,$matches)===1) {
|
||||
if ($matches[1]+0>=$graceLine)
|
||||
$refs[$matches[3]]=['postdate'=>$matches[1], 'pubdate'=>$matches[2]];
|
||||
if (preg_match('#^(\S+)\t(\S+)\t(\d+)$#',$val,$matches)===1) {// todo: refine the pattern
|
||||
if ($matches[3]+0>=$graceLine)
|
||||
$refs[$matches[1]]=['updatedAt'=>$matches[2], 'postedAt'=>$matches[3]];
|
||||
else
|
||||
$i++;
|
||||
} else {
|
||||
|
@ -336,232 +339,224 @@ if (file_exists($conf['state_file_absolute_path'])) {
|
|||
unset($buff);
|
||||
$fh=fopen($conf['state_file_absolute_path'],'w');
|
||||
foreach ($refs as $key=>$val)
|
||||
fwrite($fh,"{$val['postdate']}\t{$val['pubdate']}\t{$key}\n");
|
||||
fwrite($fh,"{$key}\t{$val['updatedAt']}\t{$val['postedAt']}\n");
|
||||
fclose($fh);
|
||||
vecho($opts['verbose'],'Info: got '.count($refs)." reference(s) to already posted announcement(s) from state file «{$conf['state_file_absolute_path']}»; removed {$i} reference(s) older than one year.\n");
|
||||
} elseif (is_null($opts['do-post']) && !$opts['test']) {
|
||||
dieyoung("State file «{$conf['state_file_absolute_path']}» doesn’t exist yet, so this is probably a first run on feed «{$conf['feed_url']}»; thus, all the announcements {$SNAME} may find in the feed will be considered new and, as a precaution against unintentionally flooding your Mastodon instance’s «Local» timeline, and possibly your followers’ «Home» timelines, you have to explicitly declare whether you want it to post them all, or not, by explicitly setting option «-p» or «--do-post» to «y» («yes») or «n» («no»); mind that in both cases the references to the announcements will be recorded in the state file, so the announcements won’t be posted again on subsequent runs (unless they were changed in the meantime).\n",1);
|
||||
dieyoung("Warning: state file «{$conf['state_file_absolute_path']}» doesn’t exist yet, so this is probably a first run on Gancio instance «{$conf['feed_hostname']}»; thus, all the announcements {$SNAME} may find in the feed will be considered new and, as a precaution against unintentionally flooding your Mastodon instance’s «Local» timeline, and possibly your followers’ «Home» timelines, you have to explicitly declare whether you want it to post them all, or not, by explicitly setting option «-p» or «--do-post» to «y» («yes») or «n» («no»); mind that in both cases the references to the announcements will be recorded in the state file, so the announcements won’t be posted again on subsequent runs (unless they were changed in the meantime).\n",1);
|
||||
} else {
|
||||
vecho($opts['verbose'],"Info: state file «{$conf['state_file_absolute_path']}» was not found.\n");
|
||||
}
|
||||
//print_r($refs);die();
|
||||
if (is_null($opts['do-post']) || $opts['test']) $opts['do-post']=true;
|
||||
|
||||
vecho($opts['verbose'],"Info: trying to fetch feed from «{$conf['feed_url']}».\n");
|
||||
$feed=curl($conf['feed_url'],null,['Accept: application/xml']);
|
||||
if ($feed['content']===false) dieYoung("Error: could not connect to «{$conf['feed_url']}» (error: «{$feed['error']}»).\n",1);
|
||||
if ($feed['httpcode']!='200') dieYoung("Error: «{$conf['feed_url']} returned http code «{$res['httpcode']}».\n",1);
|
||||
$feed=@simplexml_load_string($feed['content'],null,LIBXML_NOCDATA);
|
||||
if ($feed===false) dieYoung("Error: got no valid XML from «{$conf['feed_url']}».\n",1);
|
||||
$url="https://{$conf['feed_hostname']}/feed/json?show_recurrent=true";
|
||||
vecho($opts['verbose'],"Info: trying to fetch JSON feed from «{$url}».\n");
|
||||
$feed=curl($url,null,['Accept: application/json']);
|
||||
if ($feed['content']===false) dieYoung("Error: could not connect to «{$url}» (error: «{$feed['error']}»).\n",1);
|
||||
$feed['content']=@json_decode($feed['content'],true);
|
||||
(!is_null($feed['content']) && isset($feed['content']['error'])) ? $buff=" ({$feed['content']['error']})" : $buff='';
|
||||
if ($feed['httpcode']!='200') dieYoung("Error: «{$url} returned http code «{$res['httpcode']}»{$buff}.\n",1);
|
||||
$feed=$feed['content'];
|
||||
if (is_null($feed)) dieYoung("Error: got no valid JSON from «{$url}».\n",1);
|
||||
//print_r($feed);
|
||||
if (!isset($feed->channel->item) || !is_iterable($feed->channel->item) || !is_countable($feed->channel->item)) dieYoung("Error: feed from «{$conf['feed_url']}» had unexpected format.\n",1);
|
||||
$itemsCount=$feed->channel->item->count();
|
||||
if ($itemsCount==0) exitYoung("Info: feed from «{$conf['feed_url']}» was empty, bye.\n");
|
||||
$buff=['id', 'title', 'slug', 'description', 'multidate', 'start_datetime', 'end_datetime', 'media', 'online_locations', 'updatedAt', 'tags', 'place'];
|
||||
foreach ($feed as $item)
|
||||
if (!ckmkeys($buff,$item)) dieYoung("Error: feed from «{$url}» had unexpected format.\n",1);
|
||||
$itemsCount=count($feed);
|
||||
if ($itemsCount==0) exitYoung("Info: feed from «{$url}» was empty, bye.\n");
|
||||
vecho($opts['verbose'],"Info: got feed with {$itemsCount} announcement(s) from «{$url}».\n");
|
||||
//file_put_contents(__DIR__.'/storage/dump-'.time(),print_r($feed,true));
|
||||
|
||||
date_default_timezone_set($conf['timezone']);
|
||||
//date_default_timezone_set($conf['timezone']);
|
||||
//$dfmt=datefmt_create('it',IntlDateFormatter::FULL,IntlDateFormatter::SHORT,$conf['timezone'],IntlDateFormatter::GREGORIAN,"eeee d MMMM '"._('alle')."' HH:mm");
|
||||
$tsfp="{$conf['state_file_absolute_path']}.tmp";
|
||||
if (!$opts['test'] && ($fh=@fopen($tsfp,'w'))===false) dieYoung("Error: could not open «{$tsfp}» in «write» mode.\n",1);
|
||||
$itemsToPost=0;
|
||||
$goodPostsCount=0;
|
||||
$index=0;
|
||||
//2024-10-27T22:01:28+01:00
|
||||
foreach ($feed->channel->item as $item) {
|
||||
$index++;
|
||||
if (!isset($item->guid) || !isset($item->title) || !isset($item->link) || !isset($item->description) || !isset($item->pubDate)) {
|
||||
fwrite(STDERR,"Warning: announcement #{$index} has unexpected format, skipping.\n");
|
||||
foreach ($feed as $item) {
|
||||
//print_r($item);
|
||||
$now=time();
|
||||
$postUrl="https://{$conf['feed_hostname']}/event/{$item['slug']}";
|
||||
if (!array_key_exists($item['slug'],$refs)) {
|
||||
$state='new';
|
||||
} elseif ($item['updatedAt']!=$refs[$item['slug']]['updatedAt']) {
|
||||
if ($item['start_datetime']>$now || (!is_null($item['end_datetime']) && $item['end_datetime']>$now))
|
||||
$state='changed';
|
||||
else
|
||||
$state='old';
|
||||
} else {
|
||||
//print_r($item);
|
||||
$now=time();
|
||||
$guid=$item->guid->__toString();
|
||||
//$slug=preg_replace('#^.*/(.*)$#','$1',$guid);
|
||||
$pubdate=strtotime($item->pubDate->__toString());
|
||||
(preg_match('#^\[(\d{4,}-\d{2}-\d{2})\] #',$item->title,$matches)===1) ? $evdate=$matches[1] : $evdate=false;
|
||||
$file=null;
|
||||
$imgalt='';
|
||||
if (isset($item->enclosure[0]['url']) && isset($item->enclosure[0]['type']) && isset($item->enclosure[0]['length'])) {
|
||||
$file=['url'=>$item->enclosure[0]['url']->__toString(), 'type'=>$item->enclosure[0]['type']->__toString(), 'length'=>$item->enclosure[0]['length']->__toString()];
|
||||
if (preg_match('#<img [^>]*alt="([^"]+)"#',$item->description->__toString(),$matches)===1) $imgalt=trim($matches[1]);
|
||||
}
|
||||
if ($imgalt=='') $imgalt='Flyer dell’evento';
|
||||
//<h3>Raawwr Beats</h3><strong>Kassel - Werner-Hilpert-Straße 22</strong><br/><small>(samedi, 26 octobre 22:00)</small><br/><img alt="This is the alt-text" src="https://demo.gancio.org/media/fcb4ac7e55cb5a53a4008e7c49200dbd.jpg"/><p></p>
|
||||
$buff=$item->description->__toString();
|
||||
if ($buff=='') {
|
||||
$ptext='';
|
||||
} elseif (preg_match('#^\n?<h3>(.+)</h3><strong>(.+)</strong><br/><small>\((\w+)\W+(\d+)\W+(\w+)\W+(\d+:\d+)\)</small><br/>(.*)$#iuU',$buff,$matches)===1) {
|
||||
//print_r($matches);
|
||||
$matches[1]=hent($matches[1]);
|
||||
$matches[2]=hent($matches[2]);
|
||||
$ptext="{$matches[1]}\n\n".ucfirst($matches[3])." {$matches[4]} {$matches[5]} dalle {$matches[6]} presso {$matches[2]}\n\n".html2text($matches[7]);
|
||||
if ($evdate!==false) $evdate.="T{$matches[6]}:00";
|
||||
$state='old';
|
||||
}
|
||||
// $state='new';
|
||||
if ($state=='old' && !$opts['test']) {
|
||||
if ($opts['do-post'])
|
||||
vecho($opts['verbose'],"Info: won’t try to post status for {$state} announcement «{$postUrl}».\n");
|
||||
else
|
||||
vecho($opts['verbose'],"Info: wouldn’t try to post status for {$state} announcement «{$postUrl}».\n");
|
||||
} else {
|
||||
$itemsToPost++;
|
||||
$postHead="{$item['title']}\n\n";
|
||||
if ($item['multidate']) {
|
||||
$dfmt=datefmt_create($conf['posts_language'],0,0,$conf['timezone'],null,"eeee d MMMM '"._('alle')."' HH:mm");
|
||||
$postHead.=_('Da').' '.datefmt_format($dfmt,$item['start_datetime']).' '._('a').' '.datefmt_format($dfmt,$item['end_datetime']);
|
||||
} else {
|
||||
$ptext=html2text($item->description);
|
||||
$evdate=false;
|
||||
}
|
||||
//echo "evdate: {$evdate}\n";
|
||||
$evdate=strtotime($evdate);
|
||||
//echo "{$now}: ".date('c',$now)." (now)\n{$pubdate}: ".date('c',$pubdate)." (pubdate: {$item->pubDate})\n{$evdate}: ".date('c',$evdate)." (evdate)\n";
|
||||
//exitYoung("Ciao\n");
|
||||
$plink="\n\n".$item->link->__toString();
|
||||
if (isset($item->category) && is_countable($item->category) && is_iterable($item->category) && $item->category->count()>0) {
|
||||
$pcats=[];
|
||||
foreach ($item->category as $val)
|
||||
$pcats[]=hashtag($val->__toString());
|
||||
$pcats="\n\n".implode(' ',$pcats);
|
||||
} else {
|
||||
$pcats='';
|
||||
}
|
||||
if ($opts['test']) {
|
||||
vecho($opts['verbose'],"Info: considering announcement «{$guid}» as new because we are in «test mode»; processing.\n");
|
||||
$state='new';
|
||||
} elseif ($evdate===false) {
|
||||
fwrite(STDERR,"Warning: could not identify the event start datetime in announcement «{$guid}»; skipping.\n");
|
||||
$state='error';
|
||||
} elseif ($evdate<$now) {
|
||||
vecho($opts['verbose'],"Info: announcement «{$guid}» has a start datetime of ".date('c',$evdate).", which is before now, ".date('c',$now)."; skipping.\n");
|
||||
$state='error';
|
||||
} elseif (array_key_exists($guid,$refs)) {
|
||||
if ($pubdate==$refs[$guid]['pubdate']) {
|
||||
vecho($opts['verbose'],"Info: announcement «{$guid}» is not new and has not changed; skipping.\n");
|
||||
$state='old';
|
||||
} else {
|
||||
vecho($opts['verbose'],"Info: announcement «{$guid}» is not new, but it has changed; processing.\n");
|
||||
$state='changed';
|
||||
$itemsToPost++;
|
||||
$ptext="[MODIFICATO]\n\n{$ptext}";
|
||||
$dfmt=datefmt_create($conf['posts_language'],0,0,$conf['timezone'],null,"eeee d MMMM '"._('dalle')."' HH:mm");
|
||||
$postHead.=mb_ucfirst(datefmt_format($dfmt,$item['start_datetime']));
|
||||
if (!is_null($item['end_datetime'])) {
|
||||
$dfmt=datefmt_create($conf['posts_language'],0,0,$conf['timezone'],null,"HH:mm");
|
||||
$postHead.=' '._('alle').' '.datefmt_format($dfmt,$item['end_datetime']);
|
||||
}
|
||||
} else {
|
||||
vecho($opts['verbose'],"Info: announcement «{$guid}» is new; processing.\n");
|
||||
$state='new';
|
||||
$itemsToPost++;
|
||||
}
|
||||
$post="{$ptext}{$plink}{$pcats}";
|
||||
$postHead.=', '._('presso')." {$item['place']['name']}, {$item['place']['address']}";
|
||||
if (is_array($item['online_locations']) && count($item['online_locations'])>0) $postHead.='; '._('e anche online su ').implode(' - ',$item['online_locations']);
|
||||
// if (isset($item['parentId']))// this probably means it's a recurring event, but i see no way to check *when* it is recurring
|
||||
$postBody='';
|
||||
if (!is_null($item['description']) && $item['description']!='' && $item['description']!='<p></p>') $postBody.=html2text($item['description']);
|
||||
if ($postBody!='') $postBody="\n\n{$postBody}";
|
||||
$postLink="\n\n{$postUrl}";
|
||||
$postTags='';
|
||||
if (isset($item['tags']) && is_array($item['tags']) && count($item['tags'])>0) {
|
||||
$buff=[];
|
||||
foreach ($item['tags'] as $val)
|
||||
$buff[]=hashtag($val['tag']);
|
||||
$postTags.=implode(' ',$buff);
|
||||
if ($postTags!='') $postTags="\n\n{$postTags}";
|
||||
}
|
||||
$post="{$postHead}{$postBody}{$postLink}{$postTags}";
|
||||
$postLen=postLength($post,$tldsregex['tlds']);
|
||||
if ($postLen<=$conf['max_post_length'] && !$conf['always_link_gancio_post']) {
|
||||
$plink='';
|
||||
$post="{$ptext}{$pcats}";
|
||||
if (!$conf['always_link_gancio_post'] && $postLen<=$conf['max_post_length']) {
|
||||
$postLink='';
|
||||
$post="{$postHead}{$postBody}{$postTags}";
|
||||
$postLen=postLength($post,$tldsregex['tlds']);
|
||||
}
|
||||
if ($postLen>$conf['max_post_length']) {
|
||||
$pcats='';
|
||||
$post="{$ptext}{$plink}";
|
||||
$postTags='';
|
||||
$post="{$postHead}{$postBody}{$postLink}";
|
||||
$postLen=postLength($post,$tldsregex['tlds']);
|
||||
}
|
||||
while ($postLen>$conf['max_post_length'] && $ptext!='') {
|
||||
$ptext=preg_replace('#\S+\W*$#','',$ptext);
|
||||
$post="{$ptext}[…]{$plink}{$pcats}";
|
||||
while ($postLen>$conf['max_post_length'] && $postBody!='') {
|
||||
$postBody=preg_replace('#\S+\W*$#','',$postBody);
|
||||
$post="{$postHead}{$postBody}[…]{$postLink}{$postTags}";
|
||||
$postLen=postLength($post,$tldsregex['tlds']);
|
||||
}
|
||||
//echo "--- #{$index}: {$guid} ---\n{$post}\n--- (length: ".postLength($post,$tldsregex['tlds']).") ---\n\n";
|
||||
// echo "@@@ {$postUrl}: {$postLen} @@@\n{$post}\n---\n";
|
||||
if ($postLen>$conf['max_post_length']) {
|
||||
fwrite(STDERR,"Warning: could not shorten post for announcement «{$guid}» to make it fit into {$conf['max_post_length']} characters; won’t post.\n");
|
||||
} elseif ($state=='new' || $state=='changed') {
|
||||
if ($opts['do-post']) {
|
||||
$doPost=false;
|
||||
$postData=[];
|
||||
//print_r($file);
|
||||
if (is_null($file)) {
|
||||
vecho($opts['verbose'],"Info: {$state} announcement «{$guid}» has no attachment.\n");
|
||||
$doPost=true;
|
||||
} elseif ($file['length']>$conf['max_image_size']) {
|
||||
fwrite(STDERR,"Warning: the size of attachment «{$file['ulr']}» is greater than image upload max size on «{$conf['fedi_hostname']}»; won’t post status for {$state} announcement «{$guid}».\n");
|
||||
fwrite(STDERR,"Warning: could not shrink post for {$state} announcement «{$postUrl}» into {$conf['max_post_length']} characters; won’t try to post.\n");
|
||||
} elseif (!$opts['do-post'] && !$opts['test']) {
|
||||
vecho($opts['verbose'],"Info: would try to post status for {$state} announcement «{$postUrl}».\n");
|
||||
if ($state=='new' || $state=='changed') $refs[$item['slug']]=['updatedAt'=>$item['updatedAt'], 'postedAt'=>time()];
|
||||
$goodPostsCount++;
|
||||
} else {
|
||||
vecho($opts['verbose'],"Info: trying to post status for {$state} announcement «{$postUrl}».\n");
|
||||
$doPost=false;
|
||||
if (isset($item['media']) && count($item['media'])>0) {
|
||||
vecho($opts['verbose'],"Info: {$state} announcement «{$postUrl}» has an attachment; processing.\n");
|
||||
if ($item['media'][0]['size']>$conf['max_image_size']) {
|
||||
fwrite(STDERR,"Warning: attachment size is greater than «{$conf['fedi_hostname']}» maximum image size; won’t try to post.\n");
|
||||
} else {
|
||||
vecho($opts['verbose'],"Info: {$state} announcement «{$guid}» has an attachment; processing.\n");
|
||||
$res=curl($file['url']);
|
||||
$url="https://{$conf['feed_hostname']}/media/{$item['media'][0]['url']}";
|
||||
$res=curl($url);
|
||||
if ($res['content']===false) {
|
||||
fwrite(STDERR,"Warning: could not connect to «{$file['url']}» (error: «{$res['error']}»); won’t post status for {$state} announcement «{$guid}».\n");
|
||||
fwrite(STDERR,"Warning: could not connect to «{$url}» to fetch attachment: «{$res['error']}»; won’t try to post.\n");
|
||||
} elseif ($res['httpcode']!='200') {
|
||||
fwrite(STDERR,"Warning: «{$file['url']}» returned http code «{$res['httpcode']}»; won’t post status for {$state} announcement «{$guid}».\n");
|
||||
fwrite(STDERR,"Warning: could not fetch attachment «{$url}»: the server returned «{$res['httpcode']}»; won’t try to post.\n");
|
||||
} else {
|
||||
// we don't use CURLStringFile because in php 7.3 it is not available
|
||||
//$pd=['file'=>new CURLStringFile($res['content'],'file',$file['type']), 'description'=>'Flyer dell’evento'];
|
||||
$tfp=__DIR__.'/storage/'.basename($file['url']);
|
||||
$tfp=__DIR__."/storage/{$item['media'][0]['url']}";
|
||||
if (@file_put_contents($tfp,$res['content'])===false) {
|
||||
fwrite(STDERR,"Warning: could not save «{$tfp}»; won’t post status for {$state} announcement «{$guid}».\n");
|
||||
fwrite(STDERR,"Warning: could not save attachment into «{$tfp}»; won’t try to post.\n");
|
||||
} else {
|
||||
$pd=['file'=>curl_file_create($tfp,$file['type'],'file'), 'description'=>$imgalt];
|
||||
$url="https://{$conf['fedi_hostname']}/api/v2/media";
|
||||
$res=curl($url,'/api/v2/media',["Authorization: Bearer {$conf['fedi_token']}", 'Accept: application/json'],$pd);
|
||||
if ($res['content']===false) {
|
||||
fwrite(STDERR,"Warning: could not connect to «{$url}» (error: «{$res['error']}»); won’t post status for {$state} announcement «{$guid}».\n");
|
||||
} elseif (is_null($res['content']=@json_decode($res['content'],true))) {
|
||||
fwrite(STDERR,"Warning: «{$url}» did not return valid JSON; won’t post status for {$state} announcement «{$guid}».\n");
|
||||
} elseif ($res['httpcode']!='200' && $res['httpcode']!='202') {
|
||||
(isset($res['content']['error'])) ? $buff=" (error: «{$res['content']['error']}»)" : $buff='';
|
||||
fwrite(STDERR,"Warning: «{$url}» returned http code «{$res['httpcode']}»{$buff}; won’t post status for {$state} announcement «{$guid}».\n");
|
||||
} elseif (!isset($res['content']['id'])) {
|
||||
fwrite(STDERR,"Warning: no «id» in JSON from «{$url}»; file has not been uploaded successfully; won’t post status for {$state} announcement «{$guid}».\n");
|
||||
if (($type=mime_content_type($tfp))===false) {
|
||||
fwrite(STDERR,"Warning: could not identify the MIME type of «{$tfp}»; won’t try to post.\n");
|
||||
} else {
|
||||
$id=$res['content']['id'];
|
||||
if ($res['httpcode']=='202') {
|
||||
$id=null;
|
||||
$i=1;
|
||||
while ($res['httpcode']!='200' && $i<5) {
|
||||
sleep(2);
|
||||
$url="https://{$conf['fedi_hostname']}/api/v1/media/{$res['id']}";
|
||||
$res=curl($url,'/api/v1/media',["Authorization: Bearer {$conf['fedi_token']}", 'Accept: application/json']);
|
||||
if ($res['content']!==false && $res['httpcode']=='200' && !is_null($res['content']=@json_decode($res,true)) && isset($res['content']['id'])) $id=$res['content']['id'];
|
||||
$i++;
|
||||
$postData=['file'=>curl_file_create($tfp,$type,'file'), 'description'=>$item['media'][0]['name']];
|
||||
$url="https://{$conf['fedi_hostname']}/api/v2/media";
|
||||
$res=curl($url,'/api/v2/media',["Authorization: Bearer {$conf['fedi_token']}", 'Accept: application/json'],$postData);
|
||||
unset($postData);
|
||||
if ($res['content']===false) {
|
||||
fwrite(STDERR,"Warning: could not connect to «{$url}»: «{$res['error']}»; won’t try to post.\n");
|
||||
} elseif (is_null($res['content']=@json_decode($res['content'],true))) {
|
||||
fwrite(STDERR,"Warning: «{$url}» did not return valid JSON; won’t try to post.\n");
|
||||
} elseif ($res['httpcode']!='200' && $res['httpcode']!='202') {
|
||||
(isset($res['content']['error'])) ? $buff=" (error: «{$res['content']['error']}»)" : $buff='';
|
||||
fwrite(STDERR,"Warning: «{$url}» returned http code «{$res['httpcode']}»{$buff}; won’t try to post.\n");
|
||||
} elseif (!isset($res['content']['id'])) {
|
||||
fwrite(STDERR,"Warning: no «id» in JSON from «{$url}»; file has not been uploaded successfully; won’t try to post.\n");
|
||||
} else {
|
||||
$id=$res['content']['id'];
|
||||
if ($res['httpcode']=='202') {
|
||||
$id=null;
|
||||
$i=1;
|
||||
while ($res['httpcode']!='200' && $i<5) {
|
||||
sleep(2);
|
||||
$url="https://{$conf['fedi_hostname']}/api/v1/media/{$res['id']}";
|
||||
$res=curl($url,'/api/v1/media',["Authorization: Bearer {$conf['fedi_token']}", 'Accept: application/json']);
|
||||
if ($res['content']!==false && $res['httpcode']=='200' && !is_null($res['content']=@json_decode($res,true)) && isset($res['content']['id'])) $id=$res['content']['id'];
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
if (!is_null($id)) {
|
||||
vecho($opts['verbose'],"Info: successfully posted attachment for {$state} announcement «{$postUrl}».\n");
|
||||
$postData['media_ids[]']=$id;
|
||||
$doPost=true;
|
||||
} else {
|
||||
fwrite(STDERR,"Warning: server took too long to process file, or could not; won’t try to post.\n");
|
||||
}
|
||||
}
|
||||
if (!is_null($id)) {
|
||||
vecho($opts['verbose'],"Info: successfully posted attachment for {$state} announcement «{$guid}».\n");
|
||||
$postData['media_ids[]']=$id;
|
||||
$doPost=true;
|
||||
} else {
|
||||
fwrite(STDERR,"Warning: server took too long to process file, or could not; won’t post status for {$state} announcement «{$guid}».\n");
|
||||
}
|
||||
}
|
||||
if (@unlink($tfp)===false) fwrite(STDERR,"Warning: could not delete «{$tfp}».\n");
|
||||
if (@unlink($tfp)===false) fwrite(STDERR,"Warning: could not delete temporary attachment file «{$tfp}».\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($doPost) {
|
||||
$postData['status']=$post;
|
||||
$postData['visibility']=$conf['posts_visibility'];
|
||||
$postData['language']=$conf['posts_language'];
|
||||
$url="https://{$conf['fedi_hostname']}/api/v1/statuses";
|
||||
$headers=["Authorization: Bearer {$conf['fedi_token']}", 'Accept: application/json', 'Idempotency-Key: '.md5(implode('-',$postData).time())];
|
||||
$res=curl($url,'/api/v1/statuses',$headers,$postData);
|
||||
if ($res['content']===false) {
|
||||
fwrite(STDERR,"Warning: could not connect to «{$url}» (error: «{$res['error']}»); could not post status for {$state} announcement «{$guid}».\n");
|
||||
} elseif (is_null($res['content']=@json_decode($res['content'],true))) {
|
||||
fwrite(STDERR,"Warning: «{$url}» did not return good JSON; could not post status for {$state} announcement «{$guid}».\n");
|
||||
} elseif ($res['httpcode']!='200') {
|
||||
(isset($res['content']['error'])) ? $buff=" (error: «{$res['content']['error']}»)" : $buff='';
|
||||
fwrite(STDERR,"Warning: «{$url}» returned http code «{$res['httpcode']}»{$buff}; could not post status for {$state} announcement «{$guid}».\n");
|
||||
} elseif (!isset($res['content']['url'])) {
|
||||
fwrite(STDERR,"Warning: JSON from «{$url}» had unexpected format; could not post status for {$state} announcement «{$guid}».\n");
|
||||
} else {
|
||||
vecho($opts['verbose'],"Info: successfully posted status for {$state} announcement «{$guid}» (URL: «{$res['content']['url']}»).\n");
|
||||
//print_r($res['content']);
|
||||
$refs[$guid]=['postdate'=>time(), 'pubdate'=>$pubdate];
|
||||
$goodPostsCount++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vecho($opts['verbose'],"Info: would have tried to post status for {$state} announcement «{$guid}».\n");
|
||||
if ($state=='new' || $state=='changed') $refs[$guid]=['postdate'=>time(), 'pubdate'=>$pubdate];
|
||||
$goodPostsCount++;
|
||||
vecho($opts['verbose'],"Info: {$state} announcement «{$postUrl}» has no attachment.\n");
|
||||
$doPost=true;
|
||||
}
|
||||
if ($doPost) {
|
||||
$postData['status']=$post;
|
||||
$postData['visibility']=$conf['posts_visibility'];
|
||||
$postData['language']=$conf['posts_language'];
|
||||
$url="https://{$conf['fedi_hostname']}/api/v1/statuses";
|
||||
$headers=["Authorization: Bearer {$conf['fedi_token']}", 'Accept: application/json', 'Idempotency-Key: '.md5(implode('-',$postData).time())];
|
||||
$res=curl($url,'/api/v1/statuses',$headers,$postData);
|
||||
if ($res['content']===false) {
|
||||
fwrite(STDERR,"Warning: could not connect to «{$url}»: «{$res['error']}»; could not post status for {$state} announcement «{$postUrl}».\n");
|
||||
} elseif (is_null($res['content']=@json_decode($res['content'],true))) {
|
||||
fwrite(STDERR,"Warning: «{$url}» did not return good JSON; could not post status for {$state} announcement «{$postUrl}».\n");
|
||||
} elseif ($res['httpcode']!='200') {
|
||||
(isset($res['content']['error'])) ? $buff=" (error: «{$res['content']['error']}»)" : $buff='';
|
||||
fwrite(STDERR,"Warning: «{$url}» returned http code «{$res['httpcode']}»{$buff}; could not post status for {$state} announcement «{$postUrl}».\n");
|
||||
} elseif (!isset($res['content']['url'])) {
|
||||
fwrite(STDERR,"Warning: JSON from «{$url}» had unexpected format; could not post status for {$state} announcement «{$postUrl}».\n");
|
||||
} else {
|
||||
vecho($opts['verbose'],"Info: successfully posted status for {$state} announcement «{$postUrl}» (post URL: «{$res['content']['url']}»).\n");
|
||||
//print_r($res['content']);
|
||||
$refs[$item['slug']]=['updatedAt'=>$item['updatedAt'], 'postedAt'=>time()];
|
||||
$goodPostsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$opts['test'] && array_key_exists($guid,$refs)) fwrite($fh,"{$refs[$guid]['postdate']}\t{$refs[$guid]['pubdate']}\t{$guid}\n");
|
||||
}
|
||||
if ($opts['test']) break;// to test a single post
|
||||
if (!$opts['test']) {
|
||||
if (array_key_exists($item['slug'],$refs)) fwrite($fh,"{$item['slug']}\t{$refs[$item['slug']]['updatedAt']}\t{$refs[$item['slug']]['postedAt']}\n");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$opts['test']) {
|
||||
fclose($fh);
|
||||
rename($tsfp,$conf['state_file_absolute_path']);
|
||||
}
|
||||
if (!$opts['test']) {
|
||||
if ($opts['do-post'])
|
||||
vecho($opts['verbose'],"Info: succesfully posted {$goodPostsCount} of {$itemsToPost} new or edited announcement(s) (of {$itemsCount} total announcements in the feed).\n");
|
||||
vecho($opts['verbose'],"Info: succesfully posted {$goodPostsCount} of {$itemsToPost} statuses for new or changed announcement(s) (of {$itemsCount} total announcement(s) in the feed).\n");
|
||||
else
|
||||
vecho($opts['verbose'],"Info: would have tried to post {$itemsToPost} new or changed announcement(s) of {$itemsCount} total announcements in the feed.\n");
|
||||
vecho($opts['verbose'],"Info: would have tried to post {$itemsToPost} statuses for new or changed announcement(s) of {$itemsCount} total announcement(s) in the feed.\n");
|
||||
} elseif ($goodPostsCount==1) {
|
||||
vecho($opts['verbose'],"Info: successfully posted the first of {$itemsCount} total announcements in the feed ({$itemsToPost} are new or changed).\n");
|
||||
vecho($opts['verbose'],"Info: successfully posted status for the first of {$itemsCount} total announcements in the feed.\n");
|
||||
} else {
|
||||
vecho($opts['verbose'],"Info: failed to post the first of {$itemsCount} total announcements in the feed ({$itemsToPost} are new or changed).\n");
|
||||
vecho($opts['verbose'],"Info: failed to post status for the first of {$itemsCount} total announcements in the feed.\n");
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
|
Loading…
Reference in a new issue