X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=twitter%2Ftwitter.php;h=7a4c491aeda8035519323d641f9a8ef58bfcf9ff;hb=e226b74b7e4c572befcb73d6f7bc46917c2270d0;hp=82d059ee2d28954e240130489784a1c38a8bbd80;hpb=0fb5631f9bfa909f46a500feae23f22f11895846;p=friendica-addons.git diff --git a/twitter/twitter.php b/twitter/twitter.php index 82d059ee..7a4c491a 100755 --- a/twitter/twitter.php +++ b/twitter/twitter.php @@ -3,10 +3,36 @@ * Name: Twitter Connector * Description: Relay public postings to a connected Twitter account * Version: 1.0.4 - * Author: Tobias Diekershoff + * Author: Tobias Diekershoff + * Author: Michael Vogel + * + * Copyright (c) 2011-2013 Tobias Diekershoff, Michael Vogel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * * copyright notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * */ - - + /* Twitter Plugin for Friendica * * Author: Tobias Diekershoff @@ -32,10 +58,10 @@ * from "Settings -> Plugin Settings". * * Requirements: PHP5, curl [Slinky library] - * - * Documentation: http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twitter_Plugin */ +define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes + function twitter_install() { // we need some hooks, for the configuration and for sending tweets register_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); @@ -43,6 +69,7 @@ function twitter_install() { register_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local'); register_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook'); register_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets'); + register_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron'); logger("installed twitter"); } @@ -53,6 +80,7 @@ function twitter_uninstall() { unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local'); unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook'); unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets'); + unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron'); // old setting - remove only unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook'); @@ -70,10 +98,8 @@ function twitter_jot_nets(&$a,&$b) { $tw_defpost = get_pconfig(local_user(),'twitter','post_by_default'); $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : ''); $b .= '
' - . t('Post to Twitter') . '
'; + . t('Post to Twitter') . ''; } - - } function twitter_settings_post ($a,$post) { @@ -87,20 +113,23 @@ function twitter_settings_post ($a,$post) { * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair * from the user configuration */ - del_pconfig( local_user(), 'twitter', 'consumerkey' ); - del_pconfig( local_user(), 'twitter', 'consumersecret' ); - del_pconfig( local_user(), 'twitter', 'oauthtoken' ); - del_pconfig( local_user(), 'twitter', 'oauthsecret' ); - del_pconfig( local_user(), 'twitter', 'post' ); - del_pconfig( local_user(), 'twitter', 'post_by_default' ); - del_pconfig( local_user(), 'twitter', 'post_taglinks'); + del_pconfig(local_user(), 'twitter', 'consumerkey'); + del_pconfig(local_user(), 'twitter', 'consumersecret'); + del_pconfig(local_user(), 'twitter', 'oauthtoken'); + del_pconfig(local_user(), 'twitter', 'oauthsecret'); + del_pconfig(local_user(), 'twitter', 'post'); + del_pconfig(local_user(), 'twitter', 'post_by_default'); + del_pconfig(local_user(), 'twitter', 'post_taglinks'); + del_pconfig(local_user(), 'twitter', 'lastid'); + del_pconfig(local_user(), 'twitter', 'mirror_posts'); + del_pconfig(local_user(), 'twitter', 'intelligent_shortening'); } else { if (isset($_POST['twitter-pin'])) { // if the user supplied us with a PIN from Twitter, let the magic of OAuth happen logger('got a Twitter PIN'); require_once('library/twitteroauth.php'); - $ckey = get_config('twitter', 'consumerkey' ); - $csecret = get_config('twitter', 'consumersecret' ); + $ckey = get_config('twitter', 'consumerkey'); + $csecret = get_config('twitter', 'consumersecret'); // the token and secret for which the PIN was generated were hidden in the settings // form as token and token2, we need a new connection to Twitter using these token // and secret to request a Access Token with the PIN @@ -119,6 +148,8 @@ function twitter_settings_post ($a,$post) { set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable'])); set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default'])); set_pconfig(local_user(),'twitter','post_taglinks',intval($_POST['twitter-sendtaglinks'])); + set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror'])); + set_pconfig(local_user(), 'twitter', 'intelligent_shortening', intval($_POST['twitter-shortening'])); info( t('Twitter settings updated.') . EOL); }} } @@ -141,6 +172,10 @@ function twitter_settings(&$a,&$s) { $defchecked = (($defenabled) ? ' checked="checked" ' : ''); $linksenabled = get_pconfig(local_user(),'twitter','post_taglinks'); $linkschecked = (($linksenabled) ? ' checked="checked" ' : ''); + $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts'); + $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : ''); + $shorteningenabled = get_pconfig(local_user(),'twitter','intelligent_shortening'); + $shorteningchecked = (($shorteningenabled) ? ' checked="checked" ' : ''); $s .= '
'; $s .= '

'. t('Twitter Posting Settings') .'

'; @@ -198,6 +233,15 @@ function twitter_settings(&$a,&$s) { $s .= ''; $s .= ''; $s .= '
'; + + $s .= ''; + $s .= ''; + $s .= '
'; + + $s .= ''; + $s .= ''; + $s .= '
'; + $s .= ''; $s .= ''; $s .= '
'; @@ -286,6 +330,24 @@ function twitter_shortenmsg($b) { if ($b["title"] != "") $body = $b["title"]."\n\n".$body; + if (strpos($body, "[bookmark") !== false) { + // splitting the text in two parts: + // before and after the bookmark + $pos = strpos($body, "[bookmark"); + $body1 = substr($body, 0, $pos); + $body2 = substr($body, $pos); + + // Removing all quotes after the bookmark + // they are mostly only the content after the bookmark. + $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2); + $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2); + $body = $body1.$body2; + } + + // Add some newlines so that the message could be cut better + $body = str_replace(array("[quote", "[bookmark", "[/bookmark]", "[/quote]"), + array("\n[quote", "\n[bookmark", "[/bookmark]\n", "[/quote]\n"), $body); + // remove the recycle signs and the names since they aren't helpful on twitter // recycle 1 $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8'); @@ -294,8 +356,11 @@ function twitter_shortenmsg($b) { $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8'); $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body); + // remove the share element + //$body = preg_replace("/\[share(.*?)\](.*?)\[\/share\]/ism","\n\n$2\n\n",$body); + // At first convert the text to html - $html = bbcode($body, false, false); + $html = bbcode($body, false, false, 2); // Then convert it to plain text //$msg = trim($b['title']." \n\n".html2plain($html, 0, true)); @@ -310,6 +375,11 @@ function twitter_shortenmsg($b) { while (strpos($msg, " ") !== false) $msg = str_replace(" ", " ", $msg); + $origmsg = $msg; + + // Removing URLs + $msg = preg_replace('/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', "", $msg); + $msg = trim($msg); $link = ''; @@ -322,6 +392,21 @@ function twitter_shortenmsg($b) { // If there is no bookmark element then take the first link if ($link == '') { $links = collecturls($html); + + foreach($links AS $singlelink) { + $img_str = fetch_url($singlelink); + + $tempfile = tempnam(get_config("system","temppath"), "cache"); + file_put_contents($tempfile, $img_str); + $mime = image_type_to_mime_type(exif_imagetype($tempfile)); + unlink($tempfile); + + if (substr($mime, 0, 6) == "image/") { + $image = $singlelink; + unset($links[$singlelink]); + } + } + if (sizeof($links) > 0) { reset($links); $link = current($links); @@ -342,26 +427,81 @@ function twitter_shortenmsg($b) { if (($msglink == "") and strlen($msg) > $max_char) $msglink = $b["plink"]; - if (strlen($msglink) > 20) - $msglink = short_link($msglink); + // If the message is short enough then don't modify it. + if ((strlen(trim($origmsg)) <= $max_char) AND ($msglink == "")) + return(array("msg"=>trim($origmsg), "image"=>"")); + + // If the message is short enough and contains a picture then post the picture as well + if ((strlen(trim($origmsg)) <= ($max_char - 20)) AND strpos($origmsg, $msglink)) + return(array("msg"=>trim($origmsg), "image"=>$image)); + + // If the message is short enough and the link exists in the original message don't modify it as well + // -3 because of the bad shortener of twitter + if ((strlen(trim($origmsg)) <= ($max_char - 3)) AND strpos($origmsg, $msglink)) + return(array("msg"=>trim($origmsg), "image"=>"")); - if (strlen(trim($msg." ".$msglink)) > $max_char) { - $msg = substr($msg, 0, $max_char - (strlen($msglink))); + // Preserve the unshortened link + $orig_link = $msglink; + + //if (strlen($msglink) > 20) + // $msglink = short_link($msglink); + // + //if (strlen(trim($msg." ".$msglink)) > ($max_char - 3)) { + // $msg = substr($msg, 0, ($max_char - 3) - (strlen($msglink))); + + // Just replace the message link with a 15 character long string + // Twitter shortens it anyway to this length + if (trim($msglink) <> '') + $msglink = "123456789012345"; + + if (strlen(trim($msg." ".$msglink)) > ($max_char)) { + $msg = substr($msg, 0, ($max_char) - (strlen($msglink))); $lastchar = substr($msg, -1); $msg = substr($msg, 0, -1); $pos = strrpos($msg, "\n"); if ($pos > 0) - $msg = substr($msg, 0, $pos-1); + $msg = substr($msg, 0, $pos); else if ($lastchar != "\n") $msg = substr($msg, 0, -3)."..."; + + // if the post contains a picture and a link then the system tries to cut the post earlier. + // So the link and the picture can be posted. + if (($image != "") AND ($orig_link != $image)) { + $msg2 = substr($msg, 0, ($max_char - 20) - (strlen($msglink))); + $lastchar = substr($msg2, -1); + $msg2 = substr($msg2, 0, -1); + $pos = strrpos($msg2, "\n"); + if ($pos > 0) + $msg = substr($msg2, 0, $pos); + else if ($lastchar == "\n") + $msg = trim($msg2); + } + } - $msg = str_replace("\n", " ", $msg); + //$msg = str_replace("\n", " ", $msg); // Removing multiple spaces - again while (strpos($msg, " ") !== false) $msg = str_replace(" ", " ", $msg); - return(trim($msg." ".$msglink)); + // Removing multiple newlines + //while (strpos($msg, "\n\n") !== false) + // $msg = str_replace("\n\n", "\n", $msg); + + // Looking if the link points to an image + $img_str = fetch_url($orig_link); + + $tempfile = tempnam(get_config("system","temppath"), "cache"); + file_put_contents($tempfile, $img_str); + $mime = image_type_to_mime_type(exif_imagetype($tempfile)); + unlink($tempfile); + + if (($image == $orig_link) OR (substr($mime, 0, 6) == "image/")) + return(array("msg"=>trim($msg), "image"=>$orig_link)); + else if (($image != $orig_link) AND ($image != "") AND (strlen($msg."\n".$msglink) <= ($max_char - 20))) + return(array("msg"=>trim($msg."\n".$orig_link), "image"=>$image)); + else + return(array("msg"=>trim($msg."\n".$orig_link), "image"=>"")); } function twitter_post_hook(&$a,&$b) { @@ -379,15 +519,24 @@ function twitter_post_hook(&$a,&$b) { if($b['parent'] != $b['id']) return; + // if post comes from twitter don't send it back + if($b['app'] == "Twitter") + return; + logger('twitter post invoked'); load_pconfig($b['uid'], 'twitter'); - $ckey = get_config('twitter', 'consumerkey' ); - $csecret = get_config('twitter', 'consumersecret' ); - $otoken = get_pconfig($b['uid'], 'twitter', 'oauthtoken' ); - $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret' ); + $ckey = get_config('twitter', 'consumerkey'); + $csecret = get_config('twitter', 'consumersecret'); + $otoken = get_pconfig($b['uid'], 'twitter', 'oauthtoken'); + $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret'); + $intelligent_shortening = get_pconfig($b['uid'], 'twitter', 'intelligent_shortening'); + + // Global setting overrides this + if (get_config('twitter','intelligent_shortening')) + $intelligent_shortening = get_config('twitter','intelligent_shortening'); if($ckey && $csecret && $otoken && $osecret) { logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG); @@ -397,9 +546,6 @@ function twitter_post_hook(&$a,&$b) { $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret); // in theory max char is 140 but T. uses t.co to make links // longer so we give them 10 characters extra - - $intelligent_shortening = get_config('twitter','intelligent_shortening'); - if (!$intelligent_shortening) { $max_char = 130; // max. length for a tweet // we will only work with up to two times the length of the dent @@ -455,7 +601,7 @@ function twitter_post_hook(&$a,&$b) { // ok, all the links we want to send out are save, now strip // away the remaining bbcode //$msg = strip_tags(bbcode($tmp, false, false)); - $msg = bbcode($tmp, false, false); + $msg = bbcode($tmp, false, false, true); $msg = str_replace(array('
','
'),"\n",$msg); $msg = strip_tags($msg); @@ -477,15 +623,36 @@ function twitter_post_hook(&$a,&$b) { $msg = trim($msg); } else - $msg = twitter_shortenmsg($b); + $msgarr = twitter_shortenmsg($b); + $msg = $msgarr["msg"]; + $image = $msgarr["image"]; // and now tweet it :-) - if(strlen($msg)) { + if(strlen($msg) and ($image != "")) { + $img_str = fetch_url($image); + + $tempfile = tempnam(get_config("system","temppath"), "cache"); + file_put_contents($tempfile, $img_str); + $mime = image_type_to_mime_type(exif_imagetype($tempfile)); + unlink($tempfile); + + $filename = "upload"; + + $result = $tweet->post('statuses/update_with_media', array('media[]' => "{$img_str};type=".$mime.";filename={$filename}" , 'status' => $msg)); + + logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG); + if ($result->errors OR $result->error) { + logger('Send to Twitter failed: "' . $result->errors . '"'); + // Workaround: Remove the picture link so that the post can be reposted without it + $image = ""; + } + } + + if(strlen($msg) and ($image == "")) { $result = $tweet->post('statuses/update', array('status' => $msg)); logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG); - if ($result->error) { - logger('Send to Twitter failed: "' . $result->error . '"'); - } + if ($result->errors OR $result->error) + logger('Send to Twitter failed: "' . $result->errors . '"'); } } } @@ -493,16 +660,125 @@ function twitter_post_hook(&$a,&$b) { function twitter_plugin_admin_post(&$a){ $consumerkey = ((x($_POST,'consumerkey')) ? notags(trim($_POST['consumerkey'])) : ''); $consumersecret = ((x($_POST,'consumersecret')) ? notags(trim($_POST['consumersecret'])): ''); + $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):''); set_config('twitter','consumerkey',$consumerkey); set_config('twitter','consumersecret',$consumersecret); + set_config('twitter','application_name',$applicationname); info( t('Settings updated.'). EOL ); } function twitter_plugin_admin(&$a, &$o){ - $t = file_get_contents( dirname(__file__). "/admin.tpl" ); + $t = get_markup_template( "admin.tpl", "addon/twitter/" ); + $o = replace_macros($t, array( '$submit' => t('Submit'), // name, label, value, help, [extra values] '$consumerkey' => array('consumerkey', t('Consumer key'), get_config('twitter', 'consumerkey' ), ''), - '$consumersecret' => array('consumersecret', t('Consumer secret'), get_config('twitter', 'consumersecret' ), '') + '$consumersecret' => array('consumersecret', t('Consumer secret'), get_config('twitter', 'consumersecret' ), ''), + '$applicationname' => array('applicationname', t('Name of the Twitter Application'), get_config('twitter','application_name'),t('set this to avoid mirroring postings from ~friendica back to ~friendica')) )); } + +function twitter_cron($a,$b) { + $last = get_config('twitter','last_poll'); + + $poll_interval = intval(get_config('twitter','poll_interval')); + if(! $poll_interval) + $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL; + + if($last) { + $next = $last + ($poll_interval * 60); + if($next > time()) { + logger('twitter: poll intervall not reached'); + return; + } + } + logger('twitter: cron_start'); + + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND() "); + if(count($r)) { + foreach($r as $rr) { + logger('twitter: fetching for user '.$rr['uid']); + twitter_fetchtimeline($a, $rr['uid']); + } + } + + logger('twitter: cron_end'); + + set_config('twitter','last_poll', time()); +} + +function twitter_fetchtimeline($a, $uid) { + $ckey = get_config('twitter', 'consumerkey'); + $csecret = get_config('twitter', 'consumersecret'); + $otoken = get_pconfig($uid, 'twitter', 'oauthtoken'); + $osecret = get_pconfig($uid, 'twitter', 'oauthsecret'); + $lastid = get_pconfig($uid, 'twitter', 'lastid'); + + $application_name = get_config('twitter', 'application_name'); + + if ($application_name == "") + $application_name = $a->get_hostname(); + + require_once('library/twitteroauth.php'); + $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret); + + $parameters = array("exclude_replies" => true, "trim_user" => true, "contributor_details" => false, "include_rts" => false); + + $first_time = ($lastid == ""); + + if ($lastid <> "") + $parameters["since_id"] = $lastid; + + $items = $connection->get('statuses/user_timeline', $parameters); + + if (!is_array($items)) + return; + + $posts = array_reverse($items); + + if (count($posts)) { + foreach ($posts as $post) { + if ($post->id_str > $lastid) + $lastid = $post->id_str; + + if ($first_time) + continue; + + if (!strpos($post->source, $application_name)) { + $_SESSION["authenticated"] = true; + $_SESSION["uid"] = $uid; + + unset($_REQUEST); + $_REQUEST["type"] = "wall"; + $_REQUEST["api_source"] = true; + $_REQUEST["profile_uid"] = $uid; + $_REQUEST["source"] = "Twitter"; + + //$_REQUEST["date"] = $post->created_at; + + $_REQUEST["title"] = ""; + + $_REQUEST["body"] = $post->text; + if (is_string($post->place->name)) + $_REQUEST["location"] = $post->place->name; + + if (is_string($post->place->full_name)) + $_REQUEST["location"] = $post->place->full_name; + + if (is_array($post->geo->coordinates)) + $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1]; + + if (is_array($post->coordinates->coordinates)) + $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0]; + + //print_r($_REQUEST); + logger('twitter: posting for user '.$uid); + + require_once('mod/item.php'); + item_post($a); + + } + } + } + set_pconfig($uid, 'twitter', 'lastid', $lastid); +}