* @copyright: Florian Buenzli, 2010
* @licence: GNU/GPL
* @download: http://floern.com/software/tripleflap
* @language: de-ch
* @charset: UTF-8
*/
header('Content-Type: text/html');
define('TRIPLEFLAP', 'TripleFlap/1.2 (@http://'.$_SERVER['SERVER_NAME'].'/; +http://floern.com/software/tripleflap)');
// Feed-ID auslesen
$twitterAccount = !empty($_POST['tuac']) ? $_POST['tuac'] : die('invalid Twitter-account');
// ungültige Zeichen im Namen löschen
$twitterAccount = preg_replace('#[^a-zA-Z0-9_]#', '', $twitterAccount);
// Cache-Datei
$tweetCache = 'tweetcache.'.$twitterAccount.'.tmp';
// Dauer des Cachings in Sekunden
$cacheMaxAge = 15*60;
// prüfen, ob Cache noch aktuell
if(file_exists($tweetCache) && filemtime($tweetCache)>=(time()-$cacheMaxAge)){
$newestTweet = file_get_contents($tweetCache);
if($newestTweet===false){
// Fehler beim Lesen der Cache-Datei
die('Error: could not read Tweet from cache file');
}
else{
// Inhalt ausgeben & fertig
die($newestTweet);
}
}
// Feed-URL
$feedUrl = 'http://twitter.com/status/user_timeline/'.$twitterAccount.'.rss';
// Feed mit cURL auslesen
if(function_exists('curl_init')){
$req = curl_init();
curl_setopt($req, CURLOPT_URL, $feedUrl);
curl_setopt($req, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($req, CURLOPT_USERAGENT, TRIPLEFLAP);
curl_setopt($req, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($req, CURLOPT_MAXREDIRS, 2);
curl_setopt($req, CURLOPT_HEADER, false);
curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
$feedContent = curl_exec($req);
if(curl_errno($req)!=0) die('Error: cURL#'.curl_errno($req).': '.curl_error($req));
if(curl_getinfo($req, CURLINFO_HTTP_CODE)!=200) die('Error: could not read Twitter-feed, HTTP-Code '.curl_getinfo($req, CURLINFO_HTTP_CODE));
curl_close($req);
}
// sonst Feed mit file_get_contents auslesen
elseif(function_exists('file_get_contents') && (int)ini_get('allow_url_fopen')==1){
$feedContent = file_get_contents($feedUrl);
if(!$feedContent) die('Error: could not read Twitter-feed [2]');
}
// sonst Fehler
else{
die('Error: PHP is not able to access external content');
}
// RSS-Datei auslesen
if(function_exists('simplexml_load_string')){
$rss = simplexml_load_string($feedContent);
if(!$rss) die('Error: invalid RSS-Feed');
$newestTweet = 'there is no tweet';
$newestTweetDate = -1;
// neuster Tweet ermitteln
foreach($rss->channel->item as $tweet){
if(($pubdate=strtotime($tweet->pubDate)) > $newestTweetDate){
$newestTweet = trim($tweet->title);
$newestTweetDate = $pubdate;
}
}
// Entity-Fehler von Twitter ausbügeln // htmlspecialchars() ohne < und >
$newestTweet = preg_replace('#&(?!(lt;|gt;))#', '&', $newestTweet);
// Multi-Byte-Zeichen als Entites codieren
$newestTweet = htmlallentities($newestTweet);
}
else{
$newestTweet = preg_match('#([^<]+)#s', $feedContent, $newestTweetTmp) ? trim($newestTweetTmp[1]) : 'there is no tweet [2]';
// Bug-Fix bzgl. bestimmter Sonderzeichen (u.a. ist '<' als '<' statt '<' codiert)
$newestTweet = preg_replace('~&(lt|gt|#\d{3,5});~', '&$1;', $newestTweet);
}
// Username wegschneiden
$newestTweet = preg_replace('#^[^:]+:\s#s', '', $newestTweet, 1);
// Links verlinken
$newestTweet = preg_replace_callback('#https?://[^/\s]{4,}(/[^\s]*)?#', 'findUrl', $newestTweet);
// @username verlinken
$newestTweet = preg_replace('#(^|[^a-zA-Z0-9\_])@([a-zA-Z0-9\_]+)#s', '$1@$2', $newestTweet);
// lange Wörter mit (soft hyphen) trennen
$newestTweet = preg_replace('#(^|>)(([^<]*[[:punct:][:space:]]|)[^[:punct:][:space:]]{15})(?=[^[:punct:][:space:]]{5})#us', '$1$2', $newestTweet);
// Zeilenumbrüche einfügen
$newestTweet = preg_replace('#(^|[^\s])\s*\n\s*($|[^\s])#s', '$1
$2', trim($newestTweet));
// Status-Text ausgeben
echo $newestTweet;
// Cache-Datei aktualisieren
if(!file_put_contents($tweetCache, $newestTweet)) die("\n".'Error: Could not write cache-file');
// Callback-Funktion zur URL-Verlinkung
function findUrl($u){
$url = htmlspecialchars_decode($u[0]);
$afterUrl = ''; // Zeichenkette am Ende der URL, die nicht zur URL gehört
while(preg_match('#[[:punct:]]$#', $url, $found)){
$chr = $found[0]; // letztes Zeichen
if($chr==='.' || $chr===',' || $chr==='!' || $chr==='?' || $chr===':' || $chr===';' || $chr==='"' || $chr==="'" || $chr==='>'){
// Ein Satzzeichen, das nicht zur URL gehört
$afterUrl = $chr.$afterUrl;
$url = substr($url, 0, -1);
}
elseif($chr===')' && strpos($url, '(')!==false || $chr===']' && strpos($url, '[')!==false || $chr==='}' && strpos($url, '{')!==false)
break; // Klammer gehört nur zur URL, wenn auch öffnende Klammer vorkommt.
elseif($chr===')' || $chr===']' || $chr==='}'){
// .. Klammer gehört nicht zur URL
$afterUrl = $chr.$afterUrl;
$url = substr($url, 0, -1);
}
elseif($chr==='(' || $chr==='[' || $chr==='{'){
// öffnende Klammer am Ende gehört nicht zur URL
$afterUrl = $chr.$afterUrl;
$url = substr($url, 0, -1);
}
else
break; // Zeichen gehört zur URL
}
// Ziel-URL auslesen (z.B. bei Kurz-URLs)
$targeturl = htmlspecialchars(getTarget($url));
// URL mit HTML-Code zurückgeben
return ''.preg_replace('#([^ \-]{22})(?=[^ \-]{8})#', '$1', $url).''.$afterUrl;
}
// Abfrage des Location-Headers
function getTarget($url){
if(!function_exists('curl_init')) return $url;
$req = curl_init();
curl_setopt($req, CURLOPT_URL, $url);
curl_setopt($req, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($req, CURLOPT_USERAGENT, TRIPLEFLAP);
curl_setopt($req, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($req, CURLOPT_HEADER, true);
curl_setopt($req, CURLOPT_NOBODY, true);
curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
$resp = curl_exec($req);
curl_close($req);
return preg_match('#\sLocation: (https?://[^\s]{4,})#is', $resp, $location) ? $location[1] : $url;
}
// Alle Multibyte-Zeichen als Entity darstellen
function htmlallentities($str){
$res = '';
$strlen = strlen($str);
for($i=0; $i<$strlen; $i++){
$byte = ord($str[$i]);
if($byte < 128) // 1-byte char
$res .= $str[$i];
elseif($byte < 192); // invalid utf8
elseif($byte < 224) // 2-byte char
$res .= ''.((63&$byte)*64 + (63&ord($str[++$i]))).';';
elseif($byte < 240) // 3-byte char
$res .= ''.((15&$byte)*4096 + (63&ord($str[++$i]))*64 + (63&ord($str[++$i]))).';';
elseif($byte < 248) // 4-byte char
$res .= ''.((15&$byte)*262144 + (63&ord($str[++$i]))*4096 + (63&ord($str[++$i]))*64 + (63&ord($str[++$i]))).';';
}
return $res;
}
?>