3 * Name: Twitter Connector
4 * Description: Relay public postings to a connected Twitter account
6 * Author: Tobias Diekershoff <https://f.diekershoff.de/profile/tobias>
7 * Author: Michael Vogel <https://pirati.ca/profile/heluecht>
9 * Copyright (c) 2011-2013 Tobias Diekershoff, Michael Vogel
10 * All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 * * Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 * * Redistributions in binary form must reproduce the above
17 * * copyright notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the distribution.
19 * * Neither the name of the <organization> nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
31 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 /* Twitter Plugin for Friendica
38 * Author: Tobias Diekershoff
39 * tobias.diekershoff@gmx.net
41 * License:3-clause BSD license
44 * To use this plugin you need a OAuth Consumer key pair (key & secret)
45 * you can get it from Twitter at https://twitter.com/apps
47 * Register your Friendica site as "Client" application with "Read & Write" access
48 * we do not need "Twitter as login". When you've registered the app you get the
49 * OAuth Consumer key and secret pair for your application/site.
51 * Add this key pair to your global .htconfig.php or use the admin panel.
53 * $a->config['twitter']['consumerkey'] = 'your consumer_key here';
54 * $a->config['twitter']['consumersecret'] = 'your consumer_secret here';
56 * To activate the plugin itself add it to the $a->config['system']['addon']
57 * setting. After this, your user can configure their Twitter account settings
58 * from "Settings -> Plugin Settings".
60 * Requirements: PHP5, curl [Slinky library]
63 define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes
65 function twitter_install() {
66 // we need some hooks, for the configuration and for sending tweets
67 register_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings');
68 register_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
69 register_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
70 register_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
71 register_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
72 register_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
73 register_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
74 register_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
75 logger("installed twitter");
79 function twitter_uninstall() {
80 unregister_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings');
81 unregister_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
82 unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
83 unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
84 unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
85 unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
86 unregister_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
87 unregister_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
89 // old setting - remove only
90 unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook');
91 unregister_hook('plugin_settings', 'addon/twitter/twitter.php', 'twitter_settings');
92 unregister_hook('plugin_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
96 function twitter_follow($a, &$contact) {
98 logger("twitter_follow: Check if contact is twitter contact. ".$contact["url"], LOGGER_DEBUG);
100 if (!strstr($contact["url"], "://twitter.com") AND !strstr($contact["url"], "@twitter.com"))
103 // contact seems to be a twitter contact, so continue
104 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $contact["url"]);
105 $nickname = str_replace("@twitter.com", "", $nickname);
107 $uid = $a->user["uid"];
109 $ckey = get_config('twitter', 'consumerkey');
110 $csecret = get_config('twitter', 'consumersecret');
111 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
112 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
114 require_once("addon/twitter/codebird.php");
116 $cb = \Codebird\Codebird::getInstance();
117 $cb->setConsumerKey($ckey, $csecret);
118 $cb->setToken($otoken, $osecret);
120 $parameters = array();
121 $parameters["screen_name"] = $nickname;
123 $user = $cb->friendships_create($parameters);
125 twitter_fetchuser($a, $uid, $nickname);
127 $r = q("SELECT name,nick,url,addr,batch,notify,poll,request,confirm,poco,photo,priority,network,alias,pubkey
128 FROM `contact` WHERE `uid` = %d AND `nick` = '%s'",
132 $contact["contact"] = $r[0];
135 function twitter_jot_nets(&$a,&$b) {
139 $tw_post = get_pconfig(local_user(),'twitter','post');
140 if(intval($tw_post) == 1) {
141 $tw_defpost = get_pconfig(local_user(),'twitter','post_by_default');
142 $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : '');
143 $b .= '<div class="profile-jot-net"><input type="checkbox" name="twitter_enable"' . $selected . ' value="1" /> '
144 . t('Post to Twitter') . '</div>';
148 function twitter_settings_post ($a,$post) {
151 // don't check twitter settings if twitter submit button is not clicked
152 if (!x($_POST,'twitter-submit'))
155 if (isset($_POST['twitter-disconnect'])) {
157 * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
158 * from the user configuration
160 del_pconfig(local_user(), 'twitter', 'consumerkey');
161 del_pconfig(local_user(), 'twitter', 'consumersecret');
162 del_pconfig(local_user(), 'twitter', 'oauthtoken');
163 del_pconfig(local_user(), 'twitter', 'oauthsecret');
164 del_pconfig(local_user(), 'twitter', 'post');
165 del_pconfig(local_user(), 'twitter', 'post_by_default');
166 del_pconfig(local_user(), 'twitter', 'post_taglinks');
167 del_pconfig(local_user(), 'twitter', 'lastid');
168 del_pconfig(local_user(), 'twitter', 'mirror_posts');
169 del_pconfig(local_user(), 'twitter', 'intelligent_shortening');
170 del_pconfig(local_user(), 'twitter', 'import');
171 del_pconfig(local_user(), 'twitter', 'create_user');
172 del_pconfig(local_user(), 'twitter', 'own_id');
174 if (isset($_POST['twitter-pin'])) {
175 // if the user supplied us with a PIN from Twitter, let the magic of OAuth happen
176 logger('got a Twitter PIN');
177 require_once('library/twitteroauth.php');
178 $ckey = get_config('twitter', 'consumerkey');
179 $csecret = get_config('twitter', 'consumersecret');
180 // the token and secret for which the PIN was generated were hidden in the settings
181 // form as token and token2, we need a new connection to Twitter using these token
182 // and secret to request a Access Token with the PIN
183 $connection = new TwitterOAuth($ckey, $csecret, $_POST['twitter-token'], $_POST['twitter-token2']);
184 $token = $connection->getAccessToken( $_POST['twitter-pin'] );
185 // ok, now that we have the Access Token, save them in the user config
186 set_pconfig(local_user(),'twitter', 'oauthtoken', $token['oauth_token']);
187 set_pconfig(local_user(),'twitter', 'oauthsecret', $token['oauth_token_secret']);
188 set_pconfig(local_user(),'twitter', 'post', 1);
189 set_pconfig(local_user(),'twitter', 'post_taglinks', 1);
190 // reload the Addon Settings page, if we don't do it see Bug #42
191 goaway($a->get_baseurl().'/settings/connectors');
193 // if no PIN is supplied in the POST variables, the user has changed the setting
194 // to post a tweet for every new __public__ posting to the wall
195 set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable']));
196 set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default']));
197 set_pconfig(local_user(),'twitter','post_taglinks',intval($_POST['twitter-sendtaglinks']));
198 set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
199 set_pconfig(local_user(), 'twitter', 'intelligent_shortening', intval($_POST['twitter-shortening']));
200 set_pconfig(local_user(), 'twitter', 'import', intval($_POST['twitter-import']));
201 set_pconfig(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user']));
202 info( t('Twitter settings updated.') . EOL);
205 function twitter_settings(&$a,&$s) {
208 $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->get_baseurl() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
210 * 1) Check that we have global consumer key & secret
211 * 2) If no OAuthtoken & stuff is present, generate button to get some
212 * 3) Checkbox for "Send public notices (140 chars only)
214 $ckey = get_config('twitter', 'consumerkey' );
215 $csecret = get_config('twitter', 'consumersecret' );
216 $otoken = get_pconfig(local_user(), 'twitter', 'oauthtoken' );
217 $osecret = get_pconfig(local_user(), 'twitter', 'oauthsecret' );
218 $enabled = get_pconfig(local_user(), 'twitter', 'post');
219 $checked = (($enabled) ? ' checked="checked" ' : '');
220 $defenabled = get_pconfig(local_user(),'twitter','post_by_default');
221 $defchecked = (($defenabled) ? ' checked="checked" ' : '');
222 $linksenabled = get_pconfig(local_user(),'twitter','post_taglinks');
223 $linkschecked = (($linksenabled) ? ' checked="checked" ' : '');
224 $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts');
225 $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : '');
226 $shorteningenabled = get_pconfig(local_user(),'twitter','intelligent_shortening');
227 $shorteningchecked = (($shorteningenabled) ? ' checked="checked" ' : '');
228 $importenabled = get_pconfig(local_user(),'twitter','import');
229 $importchecked = (($importenabled) ? ' checked="checked" ' : '');
230 $create_userenabled = get_pconfig(local_user(),'twitter','create_user');
231 $create_userchecked = (($create_userenabled) ? ' checked="checked" ' : '');
233 $css = (($enabled) ? '' : '-disabled');
235 $globalshortening = get_config('twitter','intelligent_shortening');
237 $s .= '<span id="settings_twitter_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
238 $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
240 $s .= '<div id="settings_twitter_expanded" class="settings-block" style="display: none;">';
241 $s .= '<span class="fakelink" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
242 $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
245 if ( (!$ckey) && (!$csecret) ) {
247 * no global consumer keys
248 * display warning and skip personal config
250 $s .= '<p>'. t('No consumer key pair for Twitter found. Please contact your site administrator.') .'</p>';
253 * ok we have a consumer key pair now look into the OAuth stuff
255 if ( (!$otoken) && (!$osecret) ) {
257 * the user has not yet connected the account to twitter...
258 * get a temporary OAuth key/secret pair and display a button with
259 * which the user can request a PIN to connect the account to a
260 * account at Twitter.
262 require_once('library/twitteroauth.php');
263 $connection = new TwitterOAuth($ckey, $csecret);
264 $request_token = $connection->getRequestToken();
265 $token = $request_token['oauth_token'];
267 * make some nice form
269 $s .= '<p>'. t('At this Friendica instance the Twitter plugin was enabled but you have not yet connected your account to your Twitter account. To do so click the button below to get a PIN from Twitter which you have to copy into the input box below and submit the form. Only your <strong>public</strong> posts will be posted to Twitter.') .'</p>';
270 $s .= '<a href="'.$connection->getAuthorizeURL($token).'" target="_twitter"><img src="addon/twitter/lighter.png" alt="'.t('Log in with Twitter').'"></a>';
271 $s .= '<div id="twitter-pin-wrapper">';
272 $s .= '<label id="twitter-pin-label" for="twitter-pin">'. t('Copy the PIN from Twitter here') .'</label>';
273 $s .= '<input id="twitter-pin" type="text" name="twitter-pin" />';
274 $s .= '<input id="twitter-token" type="hidden" name="twitter-token" value="'.$token.'" />';
275 $s .= '<input id="twitter-token2" type="hidden" name="twitter-token2" value="'.$request_token['oauth_token_secret'].'" />';
276 $s .= '</div><div class="clear"></div>';
277 $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
280 * we have an OAuth key / secret pair for the user
281 * so let's give a chance to disable the postings to Twitter
283 require_once('library/twitteroauth.php');
284 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
285 $details = $connection->get('account/verify_credentials');
286 $s .= '<div id="twitter-info" ><img id="twitter-avatar" src="'.$details->profile_image_url.'" /><p id="twitter-info-block">'. t('Currently connected to: ') .'<a href="https://twitter.com/'.$details->screen_name.'" target="_twitter">'.$details->screen_name.'</a><br /><em>'.$details->description.'</em></p></div>';
287 $s .= '<p>'. t('If enabled all your <strong>public</strong> postings can be posted to the associated Twitter account. You can choose to do so by default (here) or for every posting separately in the posting options when writing the entry.') .'</p>';
288 if ($a->user['hidewall']) {
289 $s .= '<p>'. t('<strong>Note</strong>: Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to Twitter will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted.') .'</p>';
291 $s .= '<div id="twitter-enable-wrapper">';
292 $s .= '<label id="twitter-enable-label" for="twitter-checkbox">'. t('Allow posting to Twitter'). '</label>';
293 $s .= '<input id="twitter-checkbox" type="checkbox" name="twitter-enable" value="1" ' . $checked . '/>';
294 $s .= '<div class="clear"></div>';
295 $s .= '<label id="twitter-default-label" for="twitter-default">'. t('Send public postings to Twitter by default') .'</label>';
296 $s .= '<input id="twitter-default" type="checkbox" name="twitter-default" value="1" ' . $defchecked . '/>';
297 $s .= '<div class="clear"></div>';
299 $s .= '<label id="twitter-mirror-label" for="twitter-mirror">'.t('Mirror all posts from twitter that are no replies').'</label>';
300 $s .= '<input id="twitter-mirror" type="checkbox" name="twitter-mirror" value="1" '. $mirrorchecked . '/>';
301 $s .= '<div class="clear"></div>';
303 if (!$globalshortening) {
304 $s .= '<label id="twitter-shortening-label" for="twitter-shortening">'.t('Shortening method that optimizes the tweet').'</label>';
305 $s .= '<input id="twitter-shortening" type="checkbox" name="twitter-shortening" value="1" '. $shorteningchecked . '/>';
306 $s .= '<div class="clear"></div>';
308 $s .= '<label id="twitter-sendtaglinks-label" for="twitter-sendtaglinks">'.t('Send linked #-tags and @-names to Twitter').'</label>';
309 $s .= '<input id="twitter-sendtaglinks" type="checkbox" name="twitter-sendtaglinks" value="1" '. $linkschecked . '/>';
310 $s .= '<div class="clear"></div>';
314 $s .= '<label id="twitter-import-label" for="twitter-import">'.t('Import the remote timeline').'</label>';
315 $s .= '<input id="twitter-import" type="checkbox" name="twitter-import" value="1" '. $importchecked . '/>';
316 $s .= '<div class="clear"></div>';
318 $s .= '<label id="twitter-create_user-label" for="twitter-create_user">'.t('Automatically create contacts').'</label>';
319 $s .= '<input id="twitter-create_user" type="checkbox" name="twitter-create_user" value="1" '. $create_userchecked . '/>';
320 $s .= '<div class="clear"></div>';
322 $s .= '<div id="twitter-disconnect-wrapper">';
323 $s .= '<label id="twitter-disconnect-label" for="twitter-disconnect">'. t('Clear OAuth configuration') .'</label>';
324 $s .= '<input id="twitter-disconnect" type="checkbox" name="twitter-disconnect" value="1" />';
325 $s .= '</div><div class="clear"></div>';
326 $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
329 $s .= '</div><div class="clear"></div>';
333 function twitter_post_local(&$a,&$b) {
338 if((local_user()) && (local_user() == $b['uid']) && (! $b['private']) && (! $b['parent']) ) {
340 $twitter_post = intval(get_pconfig(local_user(),'twitter','post'));
341 $twitter_enable = (($twitter_post && x($_REQUEST,'twitter_enable')) ? intval($_REQUEST['twitter_enable']) : 0);
343 // if API is used, default to the chosen settings
344 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'twitter','post_by_default')))
347 if(! $twitter_enable)
350 if(strlen($b['postopts']))
351 $b['postopts'] .= ',';
352 $b['postopts'] .= 'twitter';
356 if (! function_exists('short_link')) {
357 function short_link ($url) {
358 require_once('library/slinky.php');
359 $slinky = new Slinky( $url );
360 $yourls_url = get_config('yourls','url1');
362 $yourls_username = get_config('yourls','username1');
363 $yourls_password = get_config('yourls', 'password1');
364 $yourls_ssl = get_config('yourls', 'ssl1');
365 $yourls = new Slinky_YourLS();
366 $yourls->set( 'username', $yourls_username );
367 $yourls->set( 'password', $yourls_password );
368 $yourls->set( 'ssl', $yourls_ssl );
369 $yourls->set( 'yourls-url', $yourls_url );
370 $slinky->set_cascade( array( $yourls, new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
373 // setup a cascade of shortening services
374 // try to get a short link from these services
375 // in the order ur1.ca, trim, id.gd, tinyurl
376 $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
378 return $slinky->short();
381 function twitter_shortenmsg($b, $shortlink = false) {
382 require_once("include/api.php");
383 require_once("include/bbcode.php");
384 require_once("include/html2plain.php");
388 $b['body'] = bb_CleanPictureLinks($b['body']);
390 // Looking for the first image
391 $cleaned_body = api_clean_plain_items($b['body']);
393 if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$cleaned_body,$matches))
394 $image = $matches[3];
397 if(preg_match("/\[img\](.*?)\[\/img\]/is",$cleaned_body,$matches))
398 $image = $matches[1];
400 $multipleimages = (strpos($cleaned_body, "[img") != strrpos($cleaned_body, "[img"));
402 // When saved into the database the content is sent through htmlspecialchars
403 // That means that we have to decode all image-urls
404 $image = htmlspecialchars_decode($image);
407 if ($b["title"] != "")
408 $body = $b["title"]."\n\n".$body;
410 if (strpos($body, "[bookmark") !== false) {
411 // splitting the text in two parts:
412 // before and after the bookmark
413 $pos = strpos($body, "[bookmark");
414 $body1 = substr($body, 0, $pos);
415 $body2 = substr($body, $pos);
417 // Removing all quotes after the bookmark
418 // they are mostly only the content after the bookmark.
419 $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2);
420 $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2);
421 $body = $body1.$body2;
424 // Add some newlines so that the message could be cut better
425 $body = str_replace(array("[quote", "[bookmark", "[/bookmark]", "[/quote]"),
426 array("\n[quote", "\n[bookmark", "[/bookmark]\n", "[/quote]\n"), $body);
428 // remove the recycle signs and the names since they aren't helpful on twitter
430 $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8');
431 $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
433 $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8');
434 $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
436 // remove the share element
437 //$body = preg_replace("/\[share(.*?)\](.*?)\[\/share\]/ism","\n\n$2\n\n",$body);
439 // At first convert the text to html
440 $html = bbcode(api_clean_plain_items($body), false, false, 2);
442 // Then convert it to plain text
443 $msg = trim(html2plain($html, 0, true));
444 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
446 // Removing multiple newlines
447 while (strpos($msg, "\n\n\n") !== false)
448 $msg = str_replace("\n\n\n", "\n\n", $msg);
450 // Removing multiple spaces
451 while (strpos($msg, " ") !== false)
452 $msg = str_replace(" ", " ", $msg);
454 $origmsg = trim($msg);
457 $msg = preg_replace('/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', "", $msg);
462 // look for bookmark-bbcode and handle it with priority
463 if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches))
466 $multiplelinks = (strpos($b['body'], "[bookmark") != strrpos($b['body'], "[bookmark"));
468 // If there is no bookmark element then take the first link
470 $links = collecturls($html);
472 foreach($links AS $singlelink) {
473 $img_str = fetch_url($singlelink);
475 $tempfile = tempnam(get_config("system","temppath"), "cache");
476 file_put_contents($tempfile, $img_str);
477 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
480 if (substr($mime, 0, 6) == "image/") {
481 $image = $singlelink;
482 unset($links[$singlelink]);
486 if (sizeof($links) > 0) {
488 $link = current($links);
490 $multiplelinks = (sizeof($links) > 1);
495 $msglink = $b["plink"];
496 else if ($link != "")
498 else if ($multipleimages)
499 $msglink = $b["plink"];
500 else if ($image != "")
503 if (($msglink == "") and strlen($msg) > $max_char)
504 $msglink = $b["plink"];
506 // If the message is short enough then don't modify it.
507 if ((strlen($origmsg) <= $max_char) AND ($msglink == ""))
508 return(array("msg"=>$origmsg, "image"=>""));
510 // If the message is short enough and contains a picture then post the picture as well
511 if ((strlen($origmsg) <= ($max_char - 23)) AND strpos($origmsg, $msglink))
512 return(array("msg"=>$origmsg, "image"=>$image));
514 // If the message is short enough and the link exists in the original message don't modify it as well
515 // -3 because of the bad shortener of twitter
516 if ((strlen($origmsg) <= ($max_char - 3)) AND strpos($origmsg, $msglink))
517 return(array("msg"=>$origmsg, "image"=>""));
519 // Preserve the unshortened link
520 $orig_link = $msglink;
522 // Just replace the message link with a 22 character long string
523 // Twitter calculates with this length
524 if (trim($msglink) <> '')
525 $msglink = "1234567890123456789012";
527 if (strlen(trim($msg." ".$msglink)) > ($max_char)) {
528 $msg = substr($msg, 0, ($max_char) - (strlen($msglink)));
529 $lastchar = substr($msg, -1);
530 $msg = substr($msg, 0, -1);
531 $pos = strrpos($msg, "\n");
533 $msg = substr($msg, 0, $pos);
534 else if ($lastchar != "\n")
535 $msg = substr($msg, 0, -3)."...";
537 // if the post contains a picture and a link then the system tries to cut the post earlier.
538 // So the link and the picture can be posted.
539 if (($image != "") AND ($orig_link != $image)) {
540 $msg2 = substr($msg, 0, ($max_char - 20) - (strlen($msglink)));
541 $lastchar = substr($msg2, -1);
542 $msg2 = substr($msg2, 0, -1);
543 $pos = strrpos($msg2, "\n");
545 $msg = substr($msg2, 0, $pos);
546 else if ($lastchar == "\n")
551 // Removing multiple spaces - again
552 while (strpos($msg, " ") !== false)
553 $msg = str_replace(" ", " ", $msg);
557 // Removing multiple newlines
558 //while (strpos($msg, "\n\n") !== false)
559 // $msg = str_replace("\n\n", "\n", $msg);
561 // Looking if the link points to an image
562 $img_str = fetch_url($orig_link);
564 $tempfile = tempnam(get_config("system","temppath"), "cache");
565 file_put_contents($tempfile, $img_str);
566 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
569 if (($image == $orig_link) OR (substr($mime, 0, 6) == "image/"))
570 return(array("msg"=>$msg, "image"=>$orig_link));
571 else if (($image != $orig_link) AND ($image != "") AND (strlen($msg." ".$msglink) <= ($max_char - 23))) {
573 $orig_link = short_link($orig_link);
575 return(array("msg"=>$msg." ".$orig_link, "image"=>$image));
578 $orig_link = short_link($orig_link);
580 return(array("msg"=>$msg." ".$orig_link, "image"=>""));
584 function twitter_action($a, $uid, $pid, $action) {
586 $ckey = get_config('twitter', 'consumerkey');
587 $csecret = get_config('twitter', 'consumersecret');
588 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
589 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
591 require_once("addon/twitter/codebird.php");
593 $cb = \Codebird\Codebird::getInstance();
594 $cb->setConsumerKey($ckey, $csecret);
595 $cb->setToken($otoken, $osecret);
597 $post = array('id' => $pid);
599 logger("twitter_action '".$action."' ID: ".$pid." data: " . print_r($post, true), LOGGER_DATA);
603 $result = $cb->statuses_destroy($post);
606 $result = $cb->favorites_create($post);
609 $result = $cb->favorites_destroy($post);
612 logger("twitter_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
615 function twitter_post_hook(&$a,&$b) {
621 if (!get_pconfig($b["uid"],'twitter','import')) {
622 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
626 if($b['parent'] != $b['id']) {
627 logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
629 // Looking if its a reply to a twitter post
630 if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) {
631 logger("twitter_post_hook: no twitter post ".$b["parent"]);
635 $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
636 dbesc($b["thr-parent"]),
640 logger("twitter_post_hook: no parent found ".$b["thr-parent"]);
647 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
648 $nickname = "@[url=".$orig_post["author-link"]."]".$nickname."[/url]";
650 logger("twitter_post_hook: comparing ".$nickname." with ".$b["body"], LOGGER_DEBUG);
651 if (strpos($b["body"], $nickname) === false)
652 $b["body"] = $nickname." ".$b["body"];
654 logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA);
658 if($b['private'] OR !strstr($b['postopts'],'twitter'))
662 if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
663 twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
665 if($b['verb'] == ACTIVITY_LIKE) {
666 logger("twitter_post_hook: parameter 2 ".substr($b["thr-parent"], 9), LOGGER_DEBUG);
668 twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
670 twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like");
674 if($b['deleted'] || ($b['created'] !== $b['edited']))
677 // if post comes from twitter don't send it back
678 if($b['app'] == "Twitter")
681 logger('twitter post invoked');
684 load_pconfig($b['uid'], 'twitter');
686 $ckey = get_config('twitter', 'consumerkey');
687 $csecret = get_config('twitter', 'consumersecret');
688 $otoken = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
689 $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
690 $intelligent_shortening = get_pconfig($b['uid'], 'twitter', 'intelligent_shortening');
692 // Global setting overrides this
693 if (get_config('twitter','intelligent_shortening'))
694 $intelligent_shortening = get_config('twitter','intelligent_shortening');
696 if($ckey && $csecret && $otoken && $osecret) {
697 logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
699 // If it's a repeated message from twitter then do a native retweet and exit
700 if (twitter_is_retweet($a, $b['uid'], $b['body']))
703 require_once('library/twitteroauth.php');
704 require_once('include/bbcode.php');
705 $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
707 // in theory max char is 140 but T. uses t.co to make links
708 // longer so we give them 10 characters extra
709 if (!$intelligent_shortening) {
710 $max_char = 130; // max. length for a tweet
711 // we will only work with up to two times the length of the dent
712 // we can later send to Twitter. This way we can "gain" some
713 // information during shortening of potential links but do not
714 // shorten all the links in a 200000 character long essay.
715 if (! $b['title']=='') {
716 $tmp = $b['title'] . ' : '. $b['body'];
717 // $tmp = substr($tmp, 0, 4*$max_char);
719 $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char);
721 // if [url=bla][img]blub.png[/img][/url] get blub.png
722 $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp);
723 // preserve links to images, videos and audios
724 $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp);
725 $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp);
726 $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp);
727 $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp);
728 $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp);
729 $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp);
730 $linksenabled = get_pconfig($b['uid'],'twitter','post_taglinks');
731 // if a #tag is linked, don't send the [url] over to SN
732 // that is, don't send if the option is not set in the
733 // connector settings
734 if ($linksenabled=='0') {
736 $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp);
738 $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp);
740 $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8');
741 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
743 $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8');
744 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
746 $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp);
747 $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp);
748 // find all http or https links in the body of the entry and
749 // apply the shortener if the link is longer then 20 characters
750 if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) {
751 preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls );
752 foreach ($allurls as $url) {
753 foreach ($url as $u) {
755 $sl = short_link($u);
756 $tmp = str_replace( $u, $sl, $tmp );
761 // ok, all the links we want to send out are save, now strip
762 // away the remaining bbcode
763 //$msg = strip_tags(bbcode($tmp, false, false));
764 $msg = bbcode($tmp, false, false, true);
765 $msg = str_replace(array('<br>','<br />'),"\n",$msg);
766 $msg = strip_tags($msg);
768 // quotes not working - let's try this
769 $msg = html_entity_decode($msg);
770 if (( strlen($msg) > $max_char) && $max_char > 0) {
771 $shortlink = short_link( $b['plink'] );
772 // the new message will be shortened such that "... $shortlink"
773 // will fit into the character limit
774 $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4));
775 $msg = str_replace(array('<br>','<br />'),' ',$msg);
776 $e = explode(' ', $msg);
777 // remove the last word from the cut down message to
778 // avoid sending cut words to the MicroBlog
780 $msg = implode(' ', $e);
781 $msg .= '... ' . $shortlink;
787 $msgarr = twitter_shortenmsg($b);
788 $msg = $msgarr["msg"];
789 $image = $msgarr["image"];
792 // and now tweet it :-)
793 if(strlen($msg) and ($image != "")) {
794 $img_str = fetch_url($image);
796 $tempfile = tempnam(get_config("system","temppath"), "cache");
797 file_put_contents($tempfile, $img_str);
799 // Twitter had changed something so that the old library doesn't work anymore
800 // so we are using a new library for twitter
802 // Switching completely to this library with all functions
803 require_once("addon/twitter/codebird.php");
805 $cb = \Codebird\Codebird::getInstance();
806 $cb->setConsumerKey($ckey, $csecret);
807 $cb->setToken($otoken, $osecret);
809 $post = array('status' => $msg, 'media[]' => $tempfile);
812 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
814 $result = $cb->statuses_updateWithMedia($post);
817 logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG);
818 if ($result->errors OR $result->error) {
819 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
821 // Workaround: Remove the picture link so that the post can be reposted without it
824 } elseif ($iscomment) {
825 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
826 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
827 dbesc("twitter::".$result->id_str),
828 dbesc($result->text),
834 if(strlen($msg) and ($image == "")) {
835 $url = 'statuses/update';
836 $post = array('status' => $msg);
839 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
841 $result = $tweet->post($url, $post);
842 logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
843 if ($result->errors) {
844 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
846 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
848 $a->contact = $r[0]["id"];
850 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
851 require_once('include/queue_fn.php');
852 add_to_queue($a->contact,NETWORK_TWITTER,$s);
853 notice(t('Twitter post failed. Queued for retry.').EOL);
854 } elseif ($iscomment) {
855 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
856 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
857 dbesc("twitter::".$result->id_str),
860 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
861 // dbesc("twitter::".$result->id_str),
862 // dbesc($result->text),
870 function twitter_plugin_admin_post(&$a){
871 $consumerkey = ((x($_POST,'consumerkey')) ? notags(trim($_POST['consumerkey'])) : '');
872 $consumersecret = ((x($_POST,'consumersecret')) ? notags(trim($_POST['consumersecret'])): '');
873 $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
874 set_config('twitter','consumerkey',$consumerkey);
875 set_config('twitter','consumersecret',$consumersecret);
876 set_config('twitter','application_name',$applicationname);
877 info( t('Settings updated.'). EOL );
879 function twitter_plugin_admin(&$a, &$o){
880 $t = get_markup_template( "admin.tpl", "addon/twitter/" );
882 $o = replace_macros($t, array(
883 '$submit' => t('Save Settings'),
884 // name, label, value, help, [extra values]
885 '$consumerkey' => array('consumerkey', t('Consumer key'), get_config('twitter', 'consumerkey' ), ''),
886 '$consumersecret' => array('consumersecret', t('Consumer secret'), get_config('twitter', 'consumersecret' ), ''),
887 '$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'))
891 function twitter_cron($a,$b) {
892 $last = get_config('twitter','last_poll');
894 $poll_interval = intval(get_config('twitter','poll_interval'));
896 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
899 $next = $last + ($poll_interval * 60);
901 logger('twitter: poll intervall not reached');
905 logger('twitter: cron_start');
907 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND()");
910 logger('twitter: fetching for user '.$rr['uid']);
911 twitter_fetchtimeline($a, $rr['uid']);
916 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
919 logger('twitter: importing timeline from user '.$rr['uid']);
920 twitter_fetchhometimeline($a, $rr["uid"]);
924 // check for new contacts once a day
925 $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
926 if($last_contact_check)
927 $next_contact_check = $last_contact_check + 86400;
929 $next_contact_check = 0;
931 if($next_contact_check <= time()) {
932 pumpio_getallusers($a, $rr["uid"]);
933 set_pconfig($rr['uid'],'pumpio','contact_check',time());
940 logger('twitter: cron_end');
942 set_config('twitter','last_poll', time());
945 function twitter_fetchtimeline($a, $uid) {
946 $ckey = get_config('twitter', 'consumerkey');
947 $csecret = get_config('twitter', 'consumersecret');
948 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
949 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
950 $lastid = get_pconfig($uid, 'twitter', 'lastid');
952 $application_name = get_config('twitter', 'application_name');
954 if ($application_name == "")
955 $application_name = $a->get_hostname();
957 $has_picture = false;
959 require_once('mod/item.php');
960 require_once('include/items.php');
962 require_once('library/twitteroauth.php');
963 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
965 $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
967 $first_time = ($lastid == "");
970 $parameters["since_id"] = $lastid;
972 $items = $connection->get('statuses/user_timeline', $parameters);
974 if (!is_array($items))
977 $posts = array_reverse($items);
980 foreach ($posts as $post) {
981 if ($post->id_str > $lastid)
982 $lastid = $post->id_str;
987 if (!strpos($post->source, $application_name)) {
988 $_SESSION["authenticated"] = true;
989 $_SESSION["uid"] = $uid;
992 $_REQUEST["type"] = "wall";
993 $_REQUEST["api_source"] = true;
994 $_REQUEST["profile_uid"] = $uid;
995 $_REQUEST["source"] = "Twitter";
997 //$_REQUEST["date"] = $post->created_at;
999 $_REQUEST["title"] = "";
1001 if (is_object($post->retweeted_status)) {
1003 $_REQUEST['body'] = $post->retweeted_status->text;
1006 if (is_array($post->retweeted_status->entities->media)) {
1007 foreach($post->retweeted_status->entities->media AS $media) {
1008 switch($media->type) {
1010 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1011 $has_picture = true;
1017 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $has_picture);
1018 $_REQUEST['body'] = $converted["body"];
1020 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
1021 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1022 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1023 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1025 $_REQUEST['body'] .= "[/share]";
1027 $_REQUEST["body"] = $post->text;
1029 if (is_array($post->entities->media)) {
1030 foreach($post->entities->media AS $media) {
1031 switch($media->type) {
1033 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1034 $has_picture = true;
1040 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $has_picture);
1041 $_REQUEST['body'] = $converted["body"];
1044 if (is_string($post->place->name))
1045 $_REQUEST["location"] = $post->place->name;
1047 if (is_string($post->place->full_name))
1048 $_REQUEST["location"] = $post->place->full_name;
1050 if (is_array($post->geo->coordinates))
1051 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1053 if (is_array($post->coordinates->coordinates))
1054 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1056 //print_r($_REQUEST);
1057 logger('twitter: posting for user '.$uid);
1059 // require_once('mod/item.php');
1065 set_pconfig($uid, 'twitter', 'lastid', $lastid);
1068 function twitter_queue_hook(&$a,&$b) {
1070 $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
1071 dbesc(NETWORK_TWITTER)
1076 require_once('include/queue_fn.php');
1078 foreach($qi as $x) {
1079 if($x['network'] !== NETWORK_TWITTER)
1082 logger('twitter_queue: run');
1084 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
1085 WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1093 $ckey = get_config('twitter', 'consumerkey');
1094 $csecret = get_config('twitter', 'consumersecret');
1095 $otoken = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
1096 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
1100 if ($ckey AND $csecret AND $otoken AND $osecret) {
1102 logger('twitter_queue: able to post');
1104 $z = unserialize($x['content']);
1106 require_once("addon/twitter/codebird.php");
1108 $cb = \Codebird\Codebird::getInstance();
1109 $cb->setConsumerKey($ckey, $csecret);
1110 $cb->setToken($otoken, $osecret);
1112 if ($z['url'] == "statuses/update")
1113 $result = $cb->statuses_update($z['post']);
1115 logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
1117 if ($result->errors)
1118 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
1121 remove_queue_item($x['id']);
1124 logger("twitter_queue: Error getting tokens for user ".$user['uid']);
1127 logger('twitter_queue: delayed');
1128 update_queue_time($x['id']);
1133 function twitter_fetch_contact($uid, $contact, $create_user) {
1135 // Check if the unique contact is existing
1136 // To-Do: only update once a while
1137 $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
1138 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1141 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
1142 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1143 dbesc($contact->name),
1144 dbesc($contact->screen_name),
1145 dbesc($contact->profile_image_url_https));
1147 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
1148 dbesc($contact->name),
1149 dbesc($contact->screen_name),
1150 dbesc($contact->profile_image_url_https),
1151 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1153 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1154 intval($uid), dbesc("twitter::".$contact->id_str));
1156 if(!count($r) AND !$create_user)
1159 if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
1160 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
1165 // create contact record
1166 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
1167 `name`, `nick`, `photo`, `network`, `rel`, `priority`,
1168 `writable`, `blocked`, `readonly`, `pending` )
1169 VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
1171 dbesc(datetime_convert()),
1172 dbesc("https://twitter.com/".$contact->screen_name),
1173 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1174 dbesc($contact->screen_name."@twitter.com"),
1175 dbesc("twitter::".$contact->id_str),
1177 dbesc("twitter::".$contact->id_str),
1178 dbesc($contact->name),
1179 dbesc($contact->screen_name),
1180 dbesc($contact->profile_image_url_https),
1181 dbesc(NETWORK_TWITTER),
1182 intval(CONTACT_IS_FRIEND),
1187 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
1188 dbesc("twitter::".$contact->id_str),
1195 $contact_id = $r[0]['id'];
1197 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
1201 if($g && intval($g[0]['def_gid'])) {
1202 require_once('include/group.php');
1203 group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
1206 require_once("Photo.php");
1208 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
1210 q("UPDATE `contact` SET `photo` = '%s',
1215 `avatar-date` = '%s'
1220 dbesc(datetime_convert()),
1221 dbesc(datetime_convert()),
1222 dbesc(datetime_convert()),
1227 // update profile photos once every two weeks as we have no notification of when they change.
1229 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
1230 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
1232 // check that we have all the photos, this has been known to fail on occasion
1234 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
1236 logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
1238 require_once("Photo.php");
1240 $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
1242 q("UPDATE `contact` SET `photo` = '%s',
1247 `avatar-date` = '%s',
1257 dbesc(datetime_convert()),
1258 dbesc(datetime_convert()),
1259 dbesc(datetime_convert()),
1260 dbesc("https://twitter.com/".$contact->screen_name),
1261 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1262 dbesc($contact->screen_name."@twitter.com"),
1263 dbesc($contact->name),
1264 dbesc($contact->screen_name),
1270 return($r[0]["id"]);
1273 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
1274 $ckey = get_config('twitter', 'consumerkey');
1275 $csecret = get_config('twitter', 'consumersecret');
1276 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
1277 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1279 require_once("addon/twitter/codebird.php");
1281 $cb = \Codebird\Codebird::getInstance();
1282 $cb->setConsumerKey($ckey, $csecret);
1283 $cb->setToken($otoken, $osecret);
1285 $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1293 $parameters = array();
1295 if ($screen_name != "")
1296 $parameters["screen_name"] = $screen_name;
1299 $parameters["user_id"] = $user_id;
1301 // Fetching user data
1302 $user = $cb->users_show($parameters);
1304 if (!is_object($user))
1307 $contact_id = twitter_fetch_contact($uid, $user, true);
1312 function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontincludemedia) {
1313 require_once("include/oembed.php");
1314 require_once("include/network.php");
1318 if (isset($item->entities->urls)) {
1324 foreach ($item->entities->urls AS $url) {
1325 if ($url->url AND $url->expanded_url AND $url->display_url) {
1327 $expanded_url = original_url($url->expanded_url);
1329 $oembed_data = oembed_fetch_url($expanded_url);
1331 // Quickfix: Workaround for URL with "[" and "]" in it
1332 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1333 $expanded_url = $url->url;
1336 $type = $oembed_data->type;
1338 if ($oembed_data->type == "video") {
1339 $body = str_replace($url->url,
1340 "[video]".$expanded_url."[/video]", $body);
1341 $dontincludemedia = true;
1342 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1343 $body = str_replace($url->url,
1344 "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1346 $dontincludemedia = true;
1347 } elseif ($oembed_data->type != "link")
1348 $body = str_replace($url->url,
1349 "[url=".$expanded_url."]".$expanded_url."[/url]",
1352 $img_str = fetch_url($expanded_url, true, $redirects, 4);
1354 $tempfile = tempnam(get_config("system","temppath"), "cache");
1355 file_put_contents($tempfile, $img_str);
1356 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1359 if (substr($mime, 0, 6) == "image/") {
1361 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1362 $dontincludemedia = true;
1364 $type = $oembed_data->type;
1365 $footerurl = $expanded_url;
1366 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1368 $body = str_replace($url->url, $footerlink, $body);
1374 if ($footerurl != "")
1375 $footer = add_page_info($footerurl);
1377 if (($footerlink != "") AND (trim($footer) != "")) {
1378 $removedlink = trim(str_replace($footerlink, "", $body));
1380 if (strstr($body, $removedlink))
1381 $body = $removedlink;
1387 return(array("body" => $body, "tags" => ""));
1389 $tags_arr = array();
1391 foreach ($item->entities->hashtags AS $hashtag) {
1392 $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1393 $tags_arr["#".$hashtag->text] = $url;
1394 $body = str_replace("#".$hashtag->text, $url, $body);
1397 foreach ($item->entities->user_mentions AS $mention) {
1398 $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1399 $tags_arr["@".$mention->screen_name] = $url;
1400 $body = str_replace("@".$mention->screen_name, $url, $body);
1403 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1404 $tags = get_tags($body);
1407 foreach($tags as $tag) {
1408 if (strstr(trim($tag), " "))
1411 if(strpos($tag,'#') === 0) {
1412 if(strpos($tag,'[url='))
1415 // don't link tags that are already embedded in links
1417 if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1419 if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1422 $basetag = str_replace('_',' ',substr($tag,1));
1423 $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1424 $body = str_replace($tag,$url,$body);
1425 $tags_arr["#".$basetag] = $url;
1427 } elseif(strpos($tag,'@') === 0) {
1428 if(strpos($tag,'[url='))
1431 $basetag = substr($tag,1);
1432 $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1433 $body = str_replace($tag,$url,$body);
1434 $tags_arr["@".$basetag] = $url;
1440 $tags = implode($tags_arr, ",");
1443 return(array("body" => $body, "tags" => $tags));
1446 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1448 $has_picture = false;
1450 $postarray = array();
1451 $postarray['network'] = NETWORK_TWITTER;
1452 $postarray['gravity'] = 0;
1453 $postarray['uid'] = $uid;
1454 $postarray['wall'] = 0;
1455 $postarray['uri'] = "twitter::".$post->id_str;
1457 $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1458 dbesc($postarray['uri']),
1467 if ($post->in_reply_to_status_id_str != "") {
1469 $parent = "twitter::".$post->in_reply_to_status_id_str;
1471 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1476 $postarray['thr-parent'] = $r[0]["uri"];
1477 $postarray['parent-uri'] = $r[0]["parent-uri"];
1479 $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1484 $postarray['thr-parent'] = $r[0]['uri'];
1485 $postarray['parent-uri'] = $r[0]['parent-uri'];
1487 $postarray['thr-parent'] = $postarray['uri'];
1488 $postarray['parent-uri'] = $postarray['uri'];
1493 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1495 if ($post->user->id_str == $own_id) {
1496 $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1500 $contactid = $r[0]["id"];
1502 $postarray['owner-name'] = $r[0]["name"];
1503 $postarray['owner-link'] = $r[0]["url"];
1504 $postarray['owner-avatar'] = $r[0]["photo"];
1509 $postarray['parent-uri'] = $postarray['uri'];
1511 if ($contactid == 0) {
1512 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1514 $postarray['owner-name'] = $post->user->name;
1515 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1516 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1519 if(($contactid == 0) AND !$only_existing_contact)
1520 $contactid = $self['id'];
1521 elseif ($contactid <= 0)
1524 $postarray['contact-id'] = $contactid;
1526 $postarray['verb'] = ACTIVITY_POST;
1527 $postarray['author-name'] = $postarray['owner-name'];
1528 $postarray['author-link'] = $postarray['owner-link'];
1529 $postarray['author-avatar'] = $postarray['owner-avatar'];
1530 $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1531 $postarray['app'] = strip_tags($post->source);
1533 if ($post->user->protected) {
1534 $postarray['private'] = 1;
1535 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1538 $postarray['body'] = $post->text;
1541 if (is_array($post->entities->media)) {
1542 foreach($post->entities->media AS $media) {
1543 switch($media->type) {
1545 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1546 $has_picture = true;
1549 $postarray['body'] .= print_r($media, true);
1554 $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $has_picture);
1555 $postarray['body'] = $converted["body"];
1556 $postarray['tag'] = $converted["tags"];
1558 $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1559 $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1561 if (is_string($post->place->name))
1562 $postarray["location"] = $post->place->name;
1564 if (is_string($post->place->full_name))
1565 $postarray["location"] = $post->place->full_name;
1567 if (is_array($post->geo->coordinates))
1568 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1570 if (is_array($post->coordinates->coordinates))
1571 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1573 if (is_object($post->retweeted_status)) {
1575 $postarray['body'] = $post->retweeted_status->text;
1578 if (is_array($post->retweeted_status->entities->media)) {
1579 foreach($post->retweeted_status->entities->media AS $media) {
1580 switch($media->type) {
1582 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1583 $has_picture = true;
1586 $postarray['body'] .= print_r($media, true);
1591 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $has_picture);
1592 $postarray['body'] = $converted["body"];
1593 $postarray['tag'] = $converted["tags"];
1595 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1597 // Deactivated at the moment, since there are problems with answers to retweets
1598 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1599 $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1600 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1601 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1602 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1604 $postarray['body'] .= "[/share]";
1606 // Let retweets look like wall-to-wall posts
1607 $postarray['author-name'] = $post->retweeted_status->user->name;
1608 $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1609 $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1610 //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1611 // $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1612 // $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1620 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1622 $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1630 if (link_compare($user[0]["url"], $postarray['author-link']))
1633 $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1635 dbesc("twitter::".$own_id)
1638 if(!count($own_user))
1641 // Is it me from twitter?
1642 if (link_compare($own_user[0]["url"], $postarray['author-link']))
1645 $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1646 dbesc($postarray['parent-uri']),
1650 if(count($myconv)) {
1652 foreach($myconv as $conv) {
1653 // now if we find a match, it means we're in this conversation
1655 if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1658 require_once('include/enotify.php');
1660 $conv_parent = $conv['parent'];
1663 'type' => NOTIFY_COMMENT,
1664 'notify_flags' => $user[0]['notify-flags'],
1665 'language' => $user[0]['language'],
1666 'to_name' => $user[0]['username'],
1667 'to_email' => $user[0]['email'],
1668 'uid' => $user[0]['uid'],
1669 'item' => $postarray,
1670 'link' => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1671 'source_name' => $postarray['author-name'],
1672 'source_link' => $postarray['author-link'],
1673 'source_photo' => $postarray['author-avatar'],
1674 'verb' => ACTIVITY_POST,
1676 'parent' => $conv_parent,
1679 // only send one notification
1685 function twitter_fetchhometimeline($a, $uid) {
1686 $ckey = get_config('twitter', 'consumerkey');
1687 $csecret = get_config('twitter', 'consumersecret');
1688 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
1689 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1690 $create_user = get_pconfig($uid, 'twitter', 'create_user');
1692 logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1694 require_once('library/twitteroauth.php');
1695 require_once('include/items.php');
1697 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1699 $own_contact = twitter_fetch_own_contact($a, $uid);
1701 $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1702 intval($own_contact),
1706 $own_id = $r[0]["nick"];
1708 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1712 $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1718 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1722 $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1725 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1729 $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1730 //$parameters["count"] = 200;
1733 // Fetching timeline
1734 $lastid = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1736 $first_time = ($lastid == "");
1739 $parameters["since_id"] = $lastid;
1741 $items = $connection->get('statuses/home_timeline', $parameters);
1743 if (!is_array($items)) {
1744 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1748 $posts = array_reverse($items);
1750 logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1752 if (count($posts)) {
1753 foreach ($posts as $post) {
1754 if ($post->id_str > $lastid)
1755 $lastid = $post->id_str;
1760 $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1762 if (trim($postarray['body']) == "")
1765 $item = item_store($postarray);
1767 logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1770 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1774 set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1776 // Fetching mentions
1777 $lastid = get_pconfig($uid, 'twitter', 'lastmentionid');
1779 $first_time = ($lastid == "");
1782 $parameters["since_id"] = $lastid;
1784 $items = $connection->get('statuses/mentions_timeline', $parameters);
1786 if (!is_array($items)) {
1787 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1791 $posts = array_reverse($items);
1793 logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1795 if (count($posts)) {
1796 foreach ($posts as $post) {
1797 if ($post->id_str > $lastid)
1798 $lastid = $post->id_str;
1803 $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1805 if (trim($postarray['body']) == "")
1808 $item = item_store($postarray);
1810 logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1813 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1814 dbesc($postarray['uri']),
1818 $item = $r[0]['id'];
1822 require_once('include/enotify.php');
1824 'type' => NOTIFY_TAGSELF,
1825 'notify_flags' => $u[0]['notify-flags'],
1826 'language' => $u[0]['language'],
1827 'to_name' => $u[0]['username'],
1828 'to_email' => $u[0]['email'],
1829 'uid' => $u[0]['uid'],
1830 'item' => $postarray,
1831 'link' => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1832 'source_name' => $postarray['author-name'],
1833 'source_link' => $postarray['author-link'],
1834 'source_photo' => $postarray['author-avatar'],
1835 'verb' => ACTIVITY_TAG,
1842 set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1845 function twitter_fetch_own_contact($a, $uid) {
1846 $ckey = get_config('twitter', 'consumerkey');
1847 $csecret = get_config('twitter', 'consumersecret');
1848 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
1849 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1851 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1855 if ($own_id == "") {
1856 require_once('library/twitteroauth.php');
1858 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1860 // Fetching user data
1861 $user = $connection->get('account/verify_credentials');
1863 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1865 $contact_id = twitter_fetch_contact($uid, $user, true);
1868 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1869 intval($uid), dbesc("twitter::".$own_id));
1871 $contact_id = $r[0]["id"];
1873 del_pconfig($uid, 'twitter', 'own_id');
1877 return($contact_id);
1880 function twitter_is_retweet($a, $uid, $body) {
1881 $body = trim($body);
1883 // Skip if it isn't a pure repeated messages
1884 // Does it start with a share?
1885 if (strpos($body, "[share") > 0)
1888 // Does it end with a share?
1889 if (strlen($body) > (strrpos($body, "[/share]") + 8))
1892 $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1893 // Skip if there is no shared message in there
1894 if ($body == $attributes)
1898 preg_match("/link='(.*?)'/ism", $attributes, $matches);
1899 if ($matches[1] != "")
1900 $link = $matches[1];
1902 preg_match('/link="(.*?)"/ism', $attributes, $matches);
1903 if ($matches[1] != "")
1904 $link = $matches[1];
1906 $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1910 logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1912 $ckey = get_config('twitter', 'consumerkey');
1913 $csecret = get_config('twitter', 'consumersecret');
1914 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
1915 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1917 require_once('library/twitteroauth.php');
1918 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1920 $result = $connection->post('statuses/retweet/'.$id);
1922 logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1924 return(!isset($result->errors));