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 $s .= '<span id="settings_twitter_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
234 $s .= '<h3>'. t('Twitter') .'</h3>';
236 $s .= '<div id="settings_twitter_expanded" class="settings-block" style="display: none;">';
237 $s .= '<span class="fakelink" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
238 $s .= '<h3>'. t('Twitter') .'</h3>';
241 if ( (!$ckey) && (!$csecret) ) {
243 * no global consumer keys
244 * display warning and skip personal config
246 $s .= '<p>'. t('No consumer key pair for Twitter found. Please contact your site administrator.') .'</p>';
249 * ok we have a consumer key pair now look into the OAuth stuff
251 if ( (!$otoken) && (!$osecret) ) {
253 * the user has not yet connected the account to twitter...
254 * get a temporary OAuth key/secret pair and display a button with
255 * which the user can request a PIN to connect the account to a
256 * account at Twitter.
258 require_once('library/twitteroauth.php');
259 $connection = new TwitterOAuth($ckey, $csecret);
260 $request_token = $connection->getRequestToken();
261 $token = $request_token['oauth_token'];
263 * make some nice form
265 $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>';
266 $s .= '<a href="'.$connection->getAuthorizeURL($token).'" target="_twitter"><img src="addon/twitter/lighter.png" alt="'.t('Log in with Twitter').'"></a>';
267 $s .= '<div id="twitter-pin-wrapper">';
268 $s .= '<label id="twitter-pin-label" for="twitter-pin">'. t('Copy the PIN from Twitter here') .'</label>';
269 $s .= '<input id="twitter-pin" type="text" name="twitter-pin" />';
270 $s .= '<input id="twitter-token" type="hidden" name="twitter-token" value="'.$token.'" />';
271 $s .= '<input id="twitter-token2" type="hidden" name="twitter-token2" value="'.$request_token['oauth_token_secret'].'" />';
272 $s .= '</div><div class="clear"></div>';
273 $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
276 * we have an OAuth key / secret pair for the user
277 * so let's give a chance to disable the postings to Twitter
279 require_once('library/twitteroauth.php');
280 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
281 $details = $connection->get('account/verify_credentials');
282 $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>';
283 $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>';
284 if ($a->user['hidewall']) {
285 $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>';
287 $s .= '<div id="twitter-enable-wrapper">';
288 $s .= '<label id="twitter-enable-label" for="twitter-checkbox">'. t('Allow posting to Twitter'). '</label>';
289 $s .= '<input id="twitter-checkbox" type="checkbox" name="twitter-enable" value="1" ' . $checked . '/>';
290 $s .= '<div class="clear"></div>';
291 $s .= '<label id="twitter-default-label" for="twitter-default">'. t('Send public postings to Twitter by default') .'</label>';
292 $s .= '<input id="twitter-default" type="checkbox" name="twitter-default" value="1" ' . $defchecked . '/>';
293 $s .= '<div class="clear"></div>';
295 $s .= '<label id="twitter-mirror-label" for="twitter-mirror">'.t('Mirror all posts from twitter that are no replies').'</label>';
296 $s .= '<input id="twitter-mirror" type="checkbox" name="twitter-mirror" value="1" '. $mirrorchecked . '/>';
297 $s .= '<div class="clear"></div>';
299 $s .= '<label id="twitter-shortening-label" for="twitter-shortening">'.t('Shortening method that optimizes the tweet').'</label>';
300 $s .= '<input id="twitter-shortening" type="checkbox" name="twitter-shortening" value="1" '. $shorteningchecked . '/>';
301 $s .= '<div class="clear"></div>';
303 $s .= '<label id="twitter-sendtaglinks-label" for="twitter-sendtaglinks">'.t('Send linked #-tags and @-names to Twitter').'</label>';
304 $s .= '<input id="twitter-sendtaglinks" type="checkbox" name="twitter-sendtaglinks" value="1" '. $linkschecked . '/>';
305 $s .= '</div><div class="clear"></div>';
307 $s .= '<label id="twitter-import-label" for="twitter-import">'.t('Import the remote timeline').'</label>';
308 $s .= '<input id="twitter-import" type="checkbox" name="twitter-import" value="1" '. $importchecked . '/>';
309 $s .= '<div class="clear"></div>';
311 $s .= '<label id="twitter-create_user-label" for="twitter-create_user">'.t('Automatically create contacts').'</label>';
312 $s .= '<input id="twitter-create_user" type="checkbox" name="twitter-create_user" value="1" '. $create_userchecked . '/>';
313 $s .= '<div class="clear"></div>';
315 $s .= '<div id="twitter-disconnect-wrapper">';
316 $s .= '<label id="twitter-disconnect-label" for="twitter-disconnect">'. t('Clear OAuth configuration') .'</label>';
317 $s .= '<input id="twitter-disconnect" type="checkbox" name="twitter-disconnect" value="1" />';
318 $s .= '</div><div class="clear"></div>';
319 $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
322 $s .= '</div><div class="clear"></div>';
326 function twitter_post_local(&$a,&$b) {
331 if((local_user()) && (local_user() == $b['uid']) && (! $b['private']) && (! $b['parent']) ) {
333 $twitter_post = intval(get_pconfig(local_user(),'twitter','post'));
334 $twitter_enable = (($twitter_post && x($_REQUEST,'twitter_enable')) ? intval($_REQUEST['twitter_enable']) : 0);
336 // if API is used, default to the chosen settings
337 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'twitter','post_by_default')))
340 if(! $twitter_enable)
343 if(strlen($b['postopts']))
344 $b['postopts'] .= ',';
345 $b['postopts'] .= 'twitter';
349 if (! function_exists('short_link')) {
350 function short_link ($url) {
351 require_once('library/slinky.php');
352 $slinky = new Slinky( $url );
353 $yourls_url = get_config('yourls','url1');
355 $yourls_username = get_config('yourls','username1');
356 $yourls_password = get_config('yourls', 'password1');
357 $yourls_ssl = get_config('yourls', 'ssl1');
358 $yourls = new Slinky_YourLS();
359 $yourls->set( 'username', $yourls_username );
360 $yourls->set( 'password', $yourls_password );
361 $yourls->set( 'ssl', $yourls_ssl );
362 $yourls->set( 'yourls-url', $yourls_url );
363 $slinky->set_cascade( array( $yourls, new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
366 // setup a cascade of shortening services
367 // try to get a short link from these services
368 // in the order ur1.ca, trim, id.gd, tinyurl
369 $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
371 return $slinky->short();
374 function twitter_shortenmsg($b, $shortlink = false) {
375 require_once("include/api.php");
376 require_once("include/bbcode.php");
377 require_once("include/html2plain.php");
381 $b['body'] = bb_CleanPictureLinks($b['body']);
383 // Looking for the first image
384 $cleaned_body = api_clean_plain_items($b['body']);
386 if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$cleaned_body,$matches))
387 $image = $matches[3];
390 if(preg_match("/\[img\](.*?)\[\/img\]/is",$cleaned_body,$matches))
391 $image = $matches[1];
393 $multipleimages = (strpos($cleaned_body, "[img") != strrpos($cleaned_body, "[img"));
395 // When saved into the database the content is sent through htmlspecialchars
396 // That means that we have to decode all image-urls
397 $image = htmlspecialchars_decode($image);
400 if ($b["title"] != "")
401 $body = $b["title"]."\n\n".$body;
403 if (strpos($body, "[bookmark") !== false) {
404 // splitting the text in two parts:
405 // before and after the bookmark
406 $pos = strpos($body, "[bookmark");
407 $body1 = substr($body, 0, $pos);
408 $body2 = substr($body, $pos);
410 // Removing all quotes after the bookmark
411 // they are mostly only the content after the bookmark.
412 $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2);
413 $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2);
414 $body = $body1.$body2;
417 // Add some newlines so that the message could be cut better
418 $body = str_replace(array("[quote", "[bookmark", "[/bookmark]", "[/quote]"),
419 array("\n[quote", "\n[bookmark", "[/bookmark]\n", "[/quote]\n"), $body);
421 // remove the recycle signs and the names since they aren't helpful on twitter
423 $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8');
424 $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
426 $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8');
427 $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
429 // remove the share element
430 //$body = preg_replace("/\[share(.*?)\](.*?)\[\/share\]/ism","\n\n$2\n\n",$body);
432 // At first convert the text to html
433 $html = bbcode(api_clean_plain_items($body), false, false, 2);
435 // Then convert it to plain text
436 $msg = trim(html2plain($html, 0, true));
437 $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
439 // Removing multiple newlines
440 while (strpos($msg, "\n\n\n") !== false)
441 $msg = str_replace("\n\n\n", "\n\n", $msg);
443 // Removing multiple spaces
444 while (strpos($msg, " ") !== false)
445 $msg = str_replace(" ", " ", $msg);
447 $origmsg = trim($msg);
450 $msg = preg_replace('/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', "", $msg);
455 // look for bookmark-bbcode and handle it with priority
456 if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches))
459 $multiplelinks = (strpos($b['body'], "[bookmark") != strrpos($b['body'], "[bookmark"));
461 // If there is no bookmark element then take the first link
463 $links = collecturls($html);
465 foreach($links AS $singlelink) {
466 $img_str = fetch_url($singlelink);
468 $tempfile = tempnam(get_config("system","temppath"), "cache");
469 file_put_contents($tempfile, $img_str);
470 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
473 if (substr($mime, 0, 6) == "image/") {
474 $image = $singlelink;
475 unset($links[$singlelink]);
479 if (sizeof($links) > 0) {
481 $link = current($links);
483 $multiplelinks = (sizeof($links) > 1);
488 $msglink = $b["plink"];
489 else if ($link != "")
491 else if ($multipleimages)
492 $msglink = $b["plink"];
493 else if ($image != "")
496 if (($msglink == "") and strlen($msg) > $max_char)
497 $msglink = $b["plink"];
499 // If the message is short enough then don't modify it.
500 if ((strlen($origmsg) <= $max_char) AND ($msglink == ""))
501 return(array("msg"=>$origmsg, "image"=>""));
503 // If the message is short enough and contains a picture then post the picture as well
504 if ((strlen($origmsg) <= ($max_char - 23)) AND strpos($origmsg, $msglink))
505 return(array("msg"=>$origmsg, "image"=>$image));
507 // If the message is short enough and the link exists in the original message don't modify it as well
508 // -3 because of the bad shortener of twitter
509 if ((strlen($origmsg) <= ($max_char - 3)) AND strpos($origmsg, $msglink))
510 return(array("msg"=>$origmsg, "image"=>""));
512 // Preserve the unshortened link
513 $orig_link = $msglink;
515 // Just replace the message link with a 22 character long string
516 // Twitter calculates with this length
517 if (trim($msglink) <> '')
518 $msglink = "1234567890123456789012";
520 if (strlen(trim($msg." ".$msglink)) > ($max_char)) {
521 $msg = substr($msg, 0, ($max_char) - (strlen($msglink)));
522 $lastchar = substr($msg, -1);
523 $msg = substr($msg, 0, -1);
524 $pos = strrpos($msg, "\n");
526 $msg = substr($msg, 0, $pos);
527 else if ($lastchar != "\n")
528 $msg = substr($msg, 0, -3)."...";
530 // if the post contains a picture and a link then the system tries to cut the post earlier.
531 // So the link and the picture can be posted.
532 if (($image != "") AND ($orig_link != $image)) {
533 $msg2 = substr($msg, 0, ($max_char - 20) - (strlen($msglink)));
534 $lastchar = substr($msg2, -1);
535 $msg2 = substr($msg2, 0, -1);
536 $pos = strrpos($msg2, "\n");
538 $msg = substr($msg2, 0, $pos);
539 else if ($lastchar == "\n")
544 // Removing multiple spaces - again
545 while (strpos($msg, " ") !== false)
546 $msg = str_replace(" ", " ", $msg);
550 // Removing multiple newlines
551 //while (strpos($msg, "\n\n") !== false)
552 // $msg = str_replace("\n\n", "\n", $msg);
554 // Looking if the link points to an image
555 $img_str = fetch_url($orig_link);
557 $tempfile = tempnam(get_config("system","temppath"), "cache");
558 file_put_contents($tempfile, $img_str);
559 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
562 if (($image == $orig_link) OR (substr($mime, 0, 6) == "image/"))
563 return(array("msg"=>$msg, "image"=>$orig_link));
564 else if (($image != $orig_link) AND ($image != "") AND (strlen($msg." ".$msglink) <= ($max_char - 23))) {
566 $orig_link = short_link($orig_link);
568 return(array("msg"=>$msg." ".$orig_link, "image"=>$image));
571 $orig_link = short_link($orig_link);
573 return(array("msg"=>$msg." ".$orig_link, "image"=>""));
577 function twitter_action($a, $uid, $pid, $action) {
579 $ckey = get_config('twitter', 'consumerkey');
580 $csecret = get_config('twitter', 'consumersecret');
581 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
582 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
584 require_once("addon/twitter/codebird.php");
586 $cb = \Codebird\Codebird::getInstance();
587 $cb->setConsumerKey($ckey, $csecret);
588 $cb->setToken($otoken, $osecret);
590 $post = array('id' => $pid);
592 logger("twitter_action '".$action."' ID: ".$pid." data: " . print_r($post, true), LOGGER_DATA);
596 $result = $cb->statuses_destroy($post);
599 $result = $cb->favorites_create($post);
602 $result = $cb->favorites_destroy($post);
605 logger("twitter_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
608 function twitter_post_hook(&$a,&$b) {
614 if (!get_pconfig($b["uid"],'twitter','import')) {
615 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
619 if($b['parent'] != $b['id']) {
620 logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
622 // Looking if its a reply to a twitter post
623 if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) {
624 logger("twitter_post_hook: no twitter post ".$b["parent"]);
628 $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
629 dbesc($b["thr-parent"]),
633 logger("twitter_post_hook: no parent found ".$b["thr-parent"]);
640 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
641 $nickname = "@[url=".$orig_post["author-link"]."]".$nickname."[/url]";
643 logger("twitter_post_hook: comparing ".$nickname." with ".$b["body"], LOGGER_DEBUG);
644 if (strpos($b["body"], $nickname) === false)
645 $b["body"] = $nickname." ".$b["body"];
647 logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA);
651 if($b['private'] OR !strstr($b['postopts'],'twitter'))
655 if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
656 twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
658 if($b['verb'] == ACTIVITY_LIKE) {
659 logger("twitter_post_hook: parameter 2 ".substr($b["thr-parent"], 9), LOGGER_DEBUG);
661 twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
663 twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like");
667 if($b['deleted'] || ($b['created'] !== $b['edited']))
670 // if post comes from twitter don't send it back
671 if($b['app'] == "Twitter")
674 logger('twitter post invoked');
677 load_pconfig($b['uid'], 'twitter');
679 $ckey = get_config('twitter', 'consumerkey');
680 $csecret = get_config('twitter', 'consumersecret');
681 $otoken = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
682 $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
683 $intelligent_shortening = get_pconfig($b['uid'], 'twitter', 'intelligent_shortening');
685 // Global setting overrides this
686 if (get_config('twitter','intelligent_shortening'))
687 $intelligent_shortening = get_config('twitter','intelligent_shortening');
689 if($ckey && $csecret && $otoken && $osecret) {
690 logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
692 // If it's a repeated message from twitter then do a native retweet and exit
693 if (twitter_is_retweet($a, $b['uid'], $b['body']))
696 require_once('library/twitteroauth.php');
697 require_once('include/bbcode.php');
698 $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
700 // in theory max char is 140 but T. uses t.co to make links
701 // longer so we give them 10 characters extra
702 if (!$intelligent_shortening) {
703 $max_char = 130; // max. length for a tweet
704 // we will only work with up to two times the length of the dent
705 // we can later send to Twitter. This way we can "gain" some
706 // information during shortening of potential links but do not
707 // shorten all the links in a 200000 character long essay.
708 if (! $b['title']=='') {
709 $tmp = $b['title'] . ' : '. $b['body'];
710 // $tmp = substr($tmp, 0, 4*$max_char);
712 $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char);
714 // if [url=bla][img]blub.png[/img][/url] get blub.png
715 $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp);
716 // preserve links to images, videos and audios
717 $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp);
718 $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp);
719 $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp);
720 $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp);
721 $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp);
722 $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp);
723 $linksenabled = get_pconfig($b['uid'],'twitter','post_taglinks');
724 // if a #tag is linked, don't send the [url] over to SN
725 // that is, don't send if the option is not set in the
726 // connector settings
727 if ($linksenabled=='0') {
729 $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp);
731 $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp);
733 $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8');
734 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
736 $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8');
737 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
739 $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp);
740 $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp);
741 // find all http or https links in the body of the entry and
742 // apply the shortener if the link is longer then 20 characters
743 if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) {
744 preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls );
745 foreach ($allurls as $url) {
746 foreach ($url as $u) {
748 $sl = short_link($u);
749 $tmp = str_replace( $u, $sl, $tmp );
754 // ok, all the links we want to send out are save, now strip
755 // away the remaining bbcode
756 //$msg = strip_tags(bbcode($tmp, false, false));
757 $msg = bbcode($tmp, false, false, true);
758 $msg = str_replace(array('<br>','<br />'),"\n",$msg);
759 $msg = strip_tags($msg);
761 // quotes not working - let's try this
762 $msg = html_entity_decode($msg);
763 if (( strlen($msg) > $max_char) && $max_char > 0) {
764 $shortlink = short_link( $b['plink'] );
765 // the new message will be shortened such that "... $shortlink"
766 // will fit into the character limit
767 $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4));
768 $msg = str_replace(array('<br>','<br />'),' ',$msg);
769 $e = explode(' ', $msg);
770 // remove the last word from the cut down message to
771 // avoid sending cut words to the MicroBlog
773 $msg = implode(' ', $e);
774 $msg .= '... ' . $shortlink;
780 $msgarr = twitter_shortenmsg($b);
781 $msg = $msgarr["msg"];
782 $image = $msgarr["image"];
785 // and now tweet it :-)
786 if(strlen($msg) and ($image != "")) {
787 $img_str = fetch_url($image);
789 $tempfile = tempnam(get_config("system","temppath"), "cache");
790 file_put_contents($tempfile, $img_str);
792 // Twitter had changed something so that the old library doesn't work anymore
793 // so we are using a new library for twitter
795 // Switching completely to this library with all functions
796 require_once("addon/twitter/codebird.php");
798 $cb = \Codebird\Codebird::getInstance();
799 $cb->setConsumerKey($ckey, $csecret);
800 $cb->setToken($otoken, $osecret);
802 $post = array('status' => $msg, 'media[]' => $tempfile);
805 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
807 $result = $cb->statuses_updateWithMedia($post);
810 logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG);
811 if ($result->errors OR $result->error) {
812 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
814 // Workaround: Remove the picture link so that the post can be reposted without it
817 } elseif ($iscomment) {
818 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
819 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
820 dbesc("twitter::".$result->id_str),
821 dbesc($result->text),
827 if(strlen($msg) and ($image == "")) {
828 $url = 'statuses/update';
829 $post = array('status' => $msg);
832 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
834 $result = $tweet->post($url, $post);
835 logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
836 if ($result->errors) {
837 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
839 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
841 $a->contact = $r[0]["id"];
843 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
844 require_once('include/queue_fn.php');
845 add_to_queue($a->contact,NETWORK_TWITTER,$s);
846 notice(t('Twitter post failed. Queued for retry.').EOL);
847 } elseif ($iscomment) {
848 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
849 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
850 dbesc("twitter::".$result->id_str),
853 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
854 // dbesc("twitter::".$result->id_str),
855 // dbesc($result->text),
863 function twitter_plugin_admin_post(&$a){
864 $consumerkey = ((x($_POST,'consumerkey')) ? notags(trim($_POST['consumerkey'])) : '');
865 $consumersecret = ((x($_POST,'consumersecret')) ? notags(trim($_POST['consumersecret'])): '');
866 $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
867 set_config('twitter','consumerkey',$consumerkey);
868 set_config('twitter','consumersecret',$consumersecret);
869 set_config('twitter','application_name',$applicationname);
870 info( t('Settings updated.'). EOL );
872 function twitter_plugin_admin(&$a, &$o){
873 $t = get_markup_template( "admin.tpl", "addon/twitter/" );
875 $o = replace_macros($t, array(
876 '$submit' => t('Save Settings'),
877 // name, label, value, help, [extra values]
878 '$consumerkey' => array('consumerkey', t('Consumer key'), get_config('twitter', 'consumerkey' ), ''),
879 '$consumersecret' => array('consumersecret', t('Consumer secret'), get_config('twitter', 'consumersecret' ), ''),
880 '$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'))
884 function twitter_cron($a,$b) {
885 $last = get_config('twitter','last_poll');
887 $poll_interval = intval(get_config('twitter','poll_interval'));
889 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
892 $next = $last + ($poll_interval * 60);
894 logger('twitter: poll intervall not reached');
898 logger('twitter: cron_start');
900 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND()");
903 logger('twitter: fetching for user '.$rr['uid']);
904 twitter_fetchtimeline($a, $rr['uid']);
909 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
912 logger('twitter: importing timeline from user '.$rr['uid']);
913 twitter_fetchhometimeline($a, $rr["uid"]);
917 // check for new contacts once a day
918 $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
919 if($last_contact_check)
920 $next_contact_check = $last_contact_check + 86400;
922 $next_contact_check = 0;
924 if($next_contact_check <= time()) {
925 pumpio_getallusers($a, $rr["uid"]);
926 set_pconfig($rr['uid'],'pumpio','contact_check',time());
933 logger('twitter: cron_end');
935 set_config('twitter','last_poll', time());
938 function twitter_fetchtimeline($a, $uid) {
939 $ckey = get_config('twitter', 'consumerkey');
940 $csecret = get_config('twitter', 'consumersecret');
941 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
942 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
943 $lastid = get_pconfig($uid, 'twitter', 'lastid');
945 $application_name = get_config('twitter', 'application_name');
947 if ($application_name == "")
948 $application_name = $a->get_hostname();
950 $has_picture = false;
952 require_once('mod/item.php');
954 require_once('library/twitteroauth.php');
955 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
957 $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
959 $first_time = ($lastid == "");
962 $parameters["since_id"] = $lastid;
964 $items = $connection->get('statuses/user_timeline', $parameters);
966 if (!is_array($items))
969 $posts = array_reverse($items);
972 foreach ($posts as $post) {
973 if ($post->id_str > $lastid)
974 $lastid = $post->id_str;
979 if (!strpos($post->source, $application_name)) {
980 $_SESSION["authenticated"] = true;
981 $_SESSION["uid"] = $uid;
984 $_REQUEST["type"] = "wall";
985 $_REQUEST["api_source"] = true;
986 $_REQUEST["profile_uid"] = $uid;
987 $_REQUEST["source"] = "Twitter";
989 //$_REQUEST["date"] = $post->created_at;
991 $_REQUEST["title"] = "";
993 if (is_object($post->retweeted_status)) {
995 $_REQUEST['body'] = $post->retweeted_status->text;
998 if (is_array($post->retweeted_status->entities->media)) {
999 foreach($post->retweeted_status->entities->media AS $media) {
1000 switch($media->type) {
1002 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1003 $has_picture = true;
1009 //$converted = twitter_convertmsg($a, $_REQUEST['body'], true, $has_picture);
1010 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $has_picture);
1011 $_REQUEST['body'] = $converted["body"];
1013 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
1014 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1015 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1016 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1018 $_REQUEST['body'] .= "[/share]";
1020 $_REQUEST["body"] = $post->text;
1022 if (is_array($post->entities->media)) {
1023 foreach($post->entities->media AS $media) {
1024 switch($media->type) {
1026 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1027 $has_picture = true;
1033 //$converted = twitter_convertmsg($a, $_REQUEST["body"], true, $has_picture);
1034 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $has_picture);
1035 $_REQUEST['body'] = $converted["body"];
1038 if (is_string($post->place->name))
1039 $_REQUEST["location"] = $post->place->name;
1041 if (is_string($post->place->full_name))
1042 $_REQUEST["location"] = $post->place->full_name;
1044 if (is_array($post->geo->coordinates))
1045 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1047 if (is_array($post->coordinates->coordinates))
1048 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1050 //print_r($_REQUEST);
1051 logger('twitter: posting for user '.$uid);
1053 // require_once('mod/item.php');
1059 set_pconfig($uid, 'twitter', 'lastid', $lastid);
1062 function twitter_queue_hook(&$a,&$b) {
1064 $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
1065 dbesc(NETWORK_TWITTER)
1070 require_once('include/queue_fn.php');
1072 foreach($qi as $x) {
1073 if($x['network'] !== NETWORK_TWITTER)
1076 logger('twitter_queue: run');
1078 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
1079 WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1087 $ckey = get_config('twitter', 'consumerkey');
1088 $csecret = get_config('twitter', 'consumersecret');
1089 $otoken = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
1090 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
1094 if ($ckey AND $csecret AND $otoken AND $osecret) {
1096 logger('twitter_queue: able to post');
1098 $z = unserialize($x['content']);
1100 require_once("addon/twitter/codebird.php");
1102 $cb = \Codebird\Codebird::getInstance();
1103 $cb->setConsumerKey($ckey, $csecret);
1104 $cb->setToken($otoken, $osecret);
1106 if ($z['url'] == "statuses/update")
1107 $result = $cb->statuses_update($z['post']);
1109 logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
1111 if ($result->errors)
1112 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
1115 remove_queue_item($x['id']);
1118 logger("twitter_queue: Error getting tokens for user ".$user['uid']);
1121 logger('twitter_queue: delayed');
1122 update_queue_time($x['id']);
1127 function twitter_fetch_contact($uid, $contact, $create_user) {
1129 // Check if the unique contact is existing
1130 // To-Do: only update once a while
1131 $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
1132 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1135 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
1136 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1137 dbesc($contact->name),
1138 dbesc($contact->screen_name),
1139 dbesc($contact->profile_image_url_https));
1141 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
1142 dbesc($contact->name),
1143 dbesc($contact->screen_name),
1144 dbesc($contact->profile_image_url_https),
1145 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1147 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1148 intval($uid), dbesc("twitter::".$contact->id_str));
1150 if(!count($r) AND !$create_user)
1153 if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
1154 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
1159 // create contact record
1160 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
1161 `name`, `nick`, `photo`, `network`, `rel`, `priority`,
1162 `writable`, `blocked`, `readonly`, `pending` )
1163 VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
1165 dbesc(datetime_convert()),
1166 dbesc("https://twitter.com/".$contact->screen_name),
1167 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1168 dbesc($contact->screen_name."@twitter.com"),
1169 dbesc("twitter::".$contact->id_str),
1171 dbesc("twitter::".$contact->id_str),
1172 dbesc($contact->name),
1173 dbesc($contact->screen_name),
1174 dbesc($contact->profile_image_url_https),
1175 dbesc(NETWORK_TWITTER),
1176 intval(CONTACT_IS_FRIEND),
1181 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
1182 dbesc("twitter::".$contact->id_str),
1189 $contact_id = $r[0]['id'];
1191 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
1195 if($g && intval($g[0]['def_gid'])) {
1196 require_once('include/group.php');
1197 group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
1200 require_once("Photo.php");
1202 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
1204 q("UPDATE `contact` SET `photo` = '%s',
1209 `avatar-date` = '%s'
1214 dbesc(datetime_convert()),
1215 dbesc(datetime_convert()),
1216 dbesc(datetime_convert()),
1221 // update profile photos once every two weeks as we have no notification of when they change.
1223 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
1224 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
1226 // check that we have all the photos, this has been known to fail on occasion
1228 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
1230 logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
1232 require_once("Photo.php");
1234 $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
1236 q("UPDATE `contact` SET `photo` = '%s',
1241 `avatar-date` = '%s',
1251 dbesc(datetime_convert()),
1252 dbesc(datetime_convert()),
1253 dbesc(datetime_convert()),
1254 dbesc("https://twitter.com/".$contact->screen_name),
1255 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1256 dbesc($contact->screen_name."@twitter.com"),
1257 dbesc($contact->name),
1258 dbesc($contact->screen_name),
1264 return($r[0]["id"]);
1267 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
1268 $ckey = get_config('twitter', 'consumerkey');
1269 $csecret = get_config('twitter', 'consumersecret');
1270 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
1271 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1273 require_once("addon/twitter/codebird.php");
1275 $cb = \Codebird\Codebird::getInstance();
1276 $cb->setConsumerKey($ckey, $csecret);
1277 $cb->setToken($otoken, $osecret);
1279 $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1287 $parameters = array();
1289 if ($screen_name != "")
1290 $parameters["screen_name"] = $screen_name;
1293 $parameters["user_id"] = $user_id;
1295 // Fetching user data
1296 $user = $cb->users_show($parameters);
1298 if (!is_object($user))
1301 $contact_id = twitter_fetch_contact($uid, $user, true);
1306 function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontincludemedia) {
1307 require_once("include/oembed.php");
1311 if (isset($item->entities->urls)) {
1317 foreach ($item->entities->urls AS $url) {
1318 if ($url->url AND $url->expanded_url AND $url->display_url) {
1320 $expanded_url = twitter_original_url($url->expanded_url);
1322 $oembed_data = oembed_fetch_url($expanded_url);
1324 // Quickfix: Workaround for URL with "[" and "]" in it
1325 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1326 $expanded_url = $url->url;
1329 $type = $oembed_data->type;
1331 if ($oembed_data->type == "video") {
1332 $body = str_replace($url->url,
1333 "[video]".$expanded_url."[/video]", $body);
1334 $dontincludemedia = true;
1335 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1336 $body = str_replace($url->url,
1337 "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1339 $dontincludemedia = true;
1340 } elseif ($oembed_data->type != "link")
1341 $body = str_replace($url->url,
1342 "[url=".$expanded_url."]".$expanded_url."[/url]",
1344 //"[url=".$expanded_url."]".$url->display_url."[/url]",
1346 $img_str = fetch_url($expanded_url, true, $redirects, 4);
1348 $tempfile = tempnam(get_config("system","temppath"), "cache");
1349 file_put_contents($tempfile, $img_str);
1350 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1353 if (substr($mime, 0, 6) == "image/") {
1355 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1356 $dontincludemedia = true;
1358 $type = $oembed_data->type;
1359 $footerurl = $expanded_url;
1360 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1361 //$footerlink = "[url=".$expanded_url."]".$url->display_url."[/url]";
1363 $body = str_replace($url->url, $footerlink, $body);
1369 if ($footerurl != "")
1370 $footer = twitter_siteinfo($footerurl, $dontincludemedia);
1372 if (($footerlink != "") AND (trim($footer) != "")) {
1373 $removedlink = trim(str_replace($footerlink, "", $body));
1375 if (strstr($body, $removedlink))
1376 $body = $removedlink;
1378 $body .= "\n\n[class=type-".$type."]".$footer."[/class]";
1382 return(array("body" => $body, "tags" => ""));
1384 $tags_arr = array();
1386 foreach ($item->entities->hashtags AS $hashtag) {
1387 $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1388 $tags_arr["#".$hashtag->text] = $url;
1389 $body = str_replace("#".$hashtag->text, $url, $body);
1392 foreach ($item->entities->user_mentions AS $mention) {
1393 $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1394 $tags_arr["@".$mention->screen_name] = $url;
1395 $body = str_replace("@".$mention->screen_name, $url, $body);
1398 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1399 $tags = get_tags($body);
1402 foreach($tags as $tag) {
1403 if (strstr(trim($tag), " "))
1406 if(strpos($tag,'#') === 0) {
1407 if(strpos($tag,'[url='))
1410 // don't link tags that are already embedded in links
1412 if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1414 if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1417 $basetag = str_replace('_',' ',substr($tag,1));
1418 $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1419 $body = str_replace($tag,$url,$body);
1420 $tags_arr["#".$basetag] = $url;
1422 } elseif(strpos($tag,'@') === 0) {
1423 if(strpos($tag,'[url='))
1426 $basetag = substr($tag,1);
1427 $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1428 $body = str_replace($tag,$url,$body);
1429 $tags_arr["@".$basetag] = $url;
1435 $tags = implode($tags_arr, ",");
1438 return(array("body" => $body, "tags" => $tags));
1441 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1443 $has_picture = false;
1445 $postarray = array();
1446 $postarray['network'] = NETWORK_TWITTER;
1447 $postarray['gravity'] = 0;
1448 $postarray['uid'] = $uid;
1449 $postarray['wall'] = 0;
1450 $postarray['uri'] = "twitter::".$post->id_str;
1452 $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1453 dbesc($postarray['uri']),
1462 if ($post->in_reply_to_status_id_str != "") {
1464 $parent = "twitter::".$post->in_reply_to_status_id_str;
1466 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1471 $postarray['thr-parent'] = $r[0]["uri"];
1472 $postarray['parent-uri'] = $r[0]["parent-uri"];
1474 $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1479 $postarray['thr-parent'] = $r[0]['uri'];
1480 $postarray['parent-uri'] = $r[0]['parent-uri'];
1482 $postarray['thr-parent'] = $postarray['uri'];
1483 $postarray['parent-uri'] = $postarray['uri'];
1488 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1490 if ($post->user->id_str == $own_id) {
1491 $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1495 $contactid = $r[0]["id"];
1497 $postarray['owner-name'] = $r[0]["name"];
1498 $postarray['owner-link'] = $r[0]["url"];
1499 $postarray['owner-avatar'] = $r[0]["photo"];
1504 $postarray['parent-uri'] = $postarray['uri'];
1506 if ($contactid == 0) {
1507 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1509 $postarray['owner-name'] = $post->user->name;
1510 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1511 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1514 if(($contactid == 0) AND !$only_existing_contact)
1515 $contactid = $self['id'];
1516 elseif ($contactid <= 0)
1519 $postarray['contact-id'] = $contactid;
1521 $postarray['verb'] = ACTIVITY_POST;
1522 $postarray['author-name'] = $postarray['owner-name'];
1523 $postarray['author-link'] = $postarray['owner-link'];
1524 $postarray['author-avatar'] = $postarray['owner-avatar'];
1525 $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1526 $postarray['app'] = strip_tags($post->source);
1528 if ($post->user->protected) {
1529 $postarray['private'] = 1;
1530 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1533 $postarray['body'] = $post->text;
1536 if (is_array($post->entities->media)) {
1537 foreach($post->entities->media AS $media) {
1538 switch($media->type) {
1540 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1541 $has_picture = true;
1544 $postarray['body'] .= print_r($media, true);
1549 //$converted = twitter_convertmsg($a, $postarray['body'], false, $has_picture);
1550 $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $has_picture);
1551 $postarray['body'] = $converted["body"];
1552 $postarray['tag'] = $converted["tags"];
1554 $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1555 $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1557 if (is_string($post->place->name))
1558 $postarray["location"] = $post->place->name;
1560 if (is_string($post->place->full_name))
1561 $postarray["location"] = $post->place->full_name;
1563 if (is_array($post->geo->coordinates))
1564 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1566 if (is_array($post->coordinates->coordinates))
1567 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1569 if (is_object($post->retweeted_status)) {
1571 $postarray['body'] = $post->retweeted_status->text;
1574 if (is_array($post->retweeted_status->entities->media)) {
1575 foreach($post->retweeted_status->entities->media AS $media) {
1576 switch($media->type) {
1578 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1579 $has_picture = true;
1582 $postarray['body'] .= print_r($media, true);
1587 //$converted = twitter_convertmsg($a, $postarray['body'], false, $has_picture);
1588 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $has_picture);
1589 $postarray['body'] = $converted["body"];
1590 $postarray['tag'] = $converted["tags"];
1592 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1594 // Deactivated at the moment, since there are problems with answers to retweets
1595 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1596 $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1597 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1598 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1599 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1601 $postarray['body'] .= "[/share]";
1603 // Let retweets look like wall-to-wall posts
1604 $postarray['author-name'] = $post->retweeted_status->user->name;
1605 $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1606 $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1607 //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1608 // $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1609 // $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1617 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1619 $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1627 if (link_compare($user[0]["url"], $postarray['author-link']))
1630 $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1632 dbesc("twitter::".$own_id)
1635 if(!count($own_user))
1638 // Is it me from twitter?
1639 if (link_compare($own_user[0]["url"], $postarray['author-link']))
1642 $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1643 dbesc($postarray['parent-uri']),
1647 if(count($myconv)) {
1649 foreach($myconv as $conv) {
1650 // now if we find a match, it means we're in this conversation
1652 if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1655 require_once('include/enotify.php');
1657 $conv_parent = $conv['parent'];
1660 'type' => NOTIFY_COMMENT,
1661 'notify_flags' => $user[0]['notify-flags'],
1662 'language' => $user[0]['language'],
1663 'to_name' => $user[0]['username'],
1664 'to_email' => $user[0]['email'],
1665 'uid' => $user[0]['uid'],
1666 'item' => $postarray,
1667 'link' => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1668 'source_name' => $postarray['author-name'],
1669 'source_link' => $postarray['author-link'],
1670 'source_photo' => $postarray['author-avatar'],
1671 'verb' => ACTIVITY_POST,
1673 'parent' => $conv_parent,
1676 // only send one notification
1682 function twitter_fetchhometimeline($a, $uid) {
1683 $ckey = get_config('twitter', 'consumerkey');
1684 $csecret = get_config('twitter', 'consumersecret');
1685 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
1686 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1687 $create_user = get_pconfig($uid, 'twitter', 'create_user');
1689 logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1691 require_once('library/twitteroauth.php');
1692 require_once('include/items.php');
1694 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1696 $own_contact = twitter_fetch_own_contact($a, $uid);
1698 $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1699 intval($own_contact),
1703 $own_id = $r[0]["nick"];
1705 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1709 $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1715 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1719 $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1722 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1726 $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1727 //$parameters["count"] = 200;
1730 // Fetching timeline
1731 $lastid = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1733 $first_time = ($lastid == "");
1736 $parameters["since_id"] = $lastid;
1738 $items = $connection->get('statuses/home_timeline', $parameters);
1740 if (!is_array($items)) {
1741 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1745 $posts = array_reverse($items);
1747 logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1749 if (count($posts)) {
1750 foreach ($posts as $post) {
1751 if ($post->id_str > $lastid)
1752 $lastid = $post->id_str;
1757 $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1759 if (trim($postarray['body']) == "")
1762 $item = item_store($postarray);
1764 logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1767 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1771 set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1773 // Fetching mentions
1774 $lastid = get_pconfig($uid, 'twitter', 'lastmentionid');
1776 $first_time = ($lastid == "");
1779 $parameters["since_id"] = $lastid;
1781 $items = $connection->get('statuses/mentions_timeline', $parameters);
1783 if (!is_array($items)) {
1784 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1788 $posts = array_reverse($items);
1790 logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1792 if (count($posts)) {
1793 foreach ($posts as $post) {
1794 if ($post->id_str > $lastid)
1795 $lastid = $post->id_str;
1800 $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1802 if (trim($postarray['body']) == "")
1805 $item = item_store($postarray);
1807 logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1810 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1811 dbesc($postarray['uri']),
1815 $item = $r[0]['id'];
1819 require_once('include/enotify.php');
1821 'type' => NOTIFY_TAGSELF,
1822 'notify_flags' => $u[0]['notify-flags'],
1823 'language' => $u[0]['language'],
1824 'to_name' => $u[0]['username'],
1825 'to_email' => $u[0]['email'],
1826 'uid' => $u[0]['uid'],
1827 'item' => $postarray,
1828 'link' => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1829 'source_name' => $postarray['author-name'],
1830 'source_link' => $postarray['author-link'],
1831 'source_photo' => $postarray['author-avatar'],
1832 'verb' => ACTIVITY_TAG,
1839 set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1842 function twitter_original_url($url, $depth=1, $fetchbody = false) {
1846 $siteinfo = array();
1848 curl_setopt($ch, CURLOPT_URL, $url);
1849 curl_setopt($ch, CURLOPT_HEADER, 1);
1852 curl_setopt($ch, CURLOPT_NOBODY, 0);
1854 curl_setopt($ch, CURLOPT_NOBODY, 1);
1856 curl_setopt($ch, CURLOPT_TIMEOUT, 10);
1857 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1858 curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0');
1860 $header = curl_exec($ch);
1861 $curl_info = @curl_getinfo($ch);
1862 $http_code = $curl_info['http_code'];
1865 if ((($curl_info['http_code'] == "301") OR ($curl_info['http_code'] == "302"))
1866 AND (($curl_info['redirect_url'] != "") OR ($curl_info['location'] != ""))) {
1867 if ($curl_info['redirect_url'] != "")
1868 return(twitter_original_url($curl_info['redirect_url'], ++$depth, $fetchbody));
1870 return(twitter_original_url($curl_info['location'], ++$depth, $fetchbody));
1873 $pos = strpos($header, "\r\n\r\n");
1876 $body = trim(substr($header, $pos));
1880 if (trim($body) == "")
1881 return(twitter_original_url($url, ++$depth, true));
1883 $doc = new DOMDocument();
1884 @$doc->loadHTML($body);
1886 $xpath = new DomXPath($doc);
1888 $list = $xpath->query("//meta[@content]");
1889 foreach ($list as $node) {
1891 if ($node->attributes->length)
1892 foreach ($node->attributes as $attribute)
1893 $attr[$attribute->name] = $attribute->value;
1895 if (@$attr["http-equiv"] == 'refresh') {
1896 $path = $attr["content"];
1897 $pathinfo = explode(";", $path);
1899 foreach ($pathinfo AS $value)
1900 if (substr(strtolower($value), 0, 4) == "url=")
1901 return(twitter_original_url(substr($value, 4), ++$depth));
1908 function twitter_siteinfo($url, $dontincludemedia) {
1909 require_once("mod/parse_url.php");
1911 // Fetch site infos - but only from the meta data
1912 $data = parseurl_getsiteinfo($url, true);
1914 if ($dontincludemedia)
1915 unset($data["images"]);
1917 if (!is_string($data["text"]) AND (sizeof($data["images"]) == 0) AND ($data["title"] == $url))
1920 if (is_string($data["title"]))
1921 $text .= "[bookmark=".$url."]".trim($data["title"])."[/bookmark]\n";
1923 // Add a spoiler to the extra information
1924 //if ((sizeof($data["images"]) > 0) OR is_string($data["text"]))
1925 // $text .= "[spoiler]";
1927 if (sizeof($data["images"]) > 0) {
1928 $imagedata = $data["images"][0];
1929 //$text .= '[img='.$imagedata["width"].'x'.$imagedata["height"].']'.$imagedata["src"].'[/img]' . "\n";
1930 $text .= '[img]'.$imagedata["src"].'[/img]'."\n";
1933 if (is_string($data["text"]))
1934 $text .= "[quote]".$data["text"]."[/quote]";
1936 //if ((sizeof($data["images"]) > 0) OR is_string($data["text"]))
1937 // $text .= "[/spoiler]";
1943 function twitter_convertmsg($a, $body, $no_tags = false, $dontincludemedia) {
1945 require_once("include/oembed.php");
1947 $links = preg_match_all("/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", $body,$matches,PREG_SET_ORDER);
1954 foreach ($matches AS $match) {
1955 $expanded_url = twitter_original_url($match[2]);
1957 $oembed_data = oembed_fetch_url($expanded_url);
1960 $type = $oembed_data->type;
1962 if ($oembed_data->type != "link")
1963 $body = str_replace($match[2], "\n[url]".$expanded_url."[/url]\n", $body);
1965 $img_str = fetch_url($expanded_url, true, $redirects, 4);
1967 $tempfile = tempnam(get_config("system","temppath"), "cache");
1968 file_put_contents($tempfile, $img_str);
1969 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1972 if (substr($mime, 0, 6) == "image/") {
1974 $body = str_replace($match[2], "[img]".$expanded_url."[/img]", $body);
1975 $dontincludemedia = true;
1977 $type = $oembed_data->type;
1978 $footerurl = $expanded_url;
1979 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1981 $body = str_replace($match[2], $footerlink, $body);
1986 if ($footerurl != "")
1987 $footer = twitter_siteinfo($footerurl, $dontincludemedia);
1989 if (($footerlink != "") AND (trim($footer) != "")) {
1990 $removedlink = trim(str_replace($footerlink, "", $body));
1992 if (strstr($body, $removedlink))
1993 $body = $removedlink;
1995 $body .= "\n\n[class=type-".$type."]".$footer."[/class]";
2000 return(array("body" => $body, "tags" => ""));
2004 $tags = get_tags($body);
2007 foreach($tags as $tag) {
2008 if (strstr(trim($tag), " "))
2011 if(strpos($tag,'#') === 0) {
2012 if(strpos($tag,'[url='))
2015 // don't link tags that are already embedded in links
2017 if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
2019 if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
2022 $basetag = str_replace('_',' ',substr($tag,1));
2023 $body = str_replace($tag,'#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$body);
2024 if(strlen($str_tags))
2026 $str_tags .= '#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]';
2028 } elseif(strpos($tag,'@') === 0) {
2029 $basetag = substr($tag,1);
2030 $body = str_replace($tag,'@[url=https://twitter.com/' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$body);
2036 $cnt = preg_match_all('/@\[url=(.*?)\[\/url\]/ism',$body,$matches,PREG_SET_ORDER);
2038 foreach($matches as $mtch) {
2039 if(strlen($str_tags))
2041 $str_tags .= '@[url=' . $mtch[1] . '[/url]';
2045 return(array("body"=>$body, "tags"=>$str_tags));
2049 function twitter_fetch_own_contact($a, $uid) {
2050 $ckey = get_config('twitter', 'consumerkey');
2051 $csecret = get_config('twitter', 'consumersecret');
2052 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
2053 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
2055 $own_id = get_pconfig($uid, 'twitter', 'own_id');
2059 if ($own_id == "") {
2060 require_once('library/twitteroauth.php');
2062 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
2064 // Fetching user data
2065 $user = $connection->get('account/verify_credentials');
2067 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
2069 $contact_id = twitter_fetch_contact($uid, $user, true);
2072 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
2073 intval($uid), dbesc("twitter::".$own_id));
2075 $contact_id = $r[0]["id"];
2077 del_pconfig($uid, 'twitter', 'own_id');
2081 return($contact_id);
2084 function twitter_is_retweet($a, $uid, $body) {
2085 $body = trim($body);
2087 // Skip if it isn't a pure repeated messages
2088 // Does it start with a share?
2089 if (strpos($body, "[share") > 0)
2092 // Does it end with a share?
2093 if (strlen($body) > (strrpos($body, "[/share]") + 8))
2096 $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
2097 // Skip if there is no shared message in there
2098 if ($body == $attributes)
2102 preg_match("/link='(.*?)'/ism", $attributes, $matches);
2103 if ($matches[1] != "")
2104 $link = $matches[1];
2106 preg_match('/link="(.*?)"/ism', $attributes, $matches);
2107 if ($matches[1] != "")
2108 $link = $matches[1];
2110 $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
2114 logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
2116 $ckey = get_config('twitter', 'consumerkey');
2117 $csecret = get_config('twitter', 'consumersecret');
2118 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
2119 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
2121 require_once('library/twitteroauth.php');
2122 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
2124 $result = $connection->post('statuses/retweet/'.$id);
2126 logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
2128 return(!isset($result->errors));