]> git.mxchange.org Git - friendica-addons.git/blob - twitter/twitter.php
Merge pull request #186 from annando/master
[friendica-addons.git] / twitter / twitter.php
1 <?php
2 /**
3  * Name: Twitter Connector
4  * Description: Relay public postings to a connected Twitter account
5  * Version: 1.0.4
6  * Author: Tobias Diekershoff <https://f.diekershoff.de/profile/tobias>
7  * Author: Michael Vogel <https://pirati.ca/profile/heluecht>
8  *
9  * Copyright (c) 2011-2013 Tobias Diekershoff, Michael Vogel
10  * All rights reserved.
11  *
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.
22  *
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.
33  *
34  */
35  
36 /*   Twitter Plugin for Friendica
37  *
38  *   Author: Tobias Diekershoff
39  *           tobias.diekershoff@gmx.net
40  *
41  *   License:3-clause BSD license
42  *
43  *   Configuration:
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
46  *
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.
50  *
51  *     Add this key pair to your global .htconfig.php or use the admin panel.
52  *
53  *     $a->config['twitter']['consumerkey'] = 'your consumer_key here';
54  *     $a->config['twitter']['consumersecret'] = 'your consumer_secret here';
55  *
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".
59  *
60  *     Requirements: PHP5, curl [Slinky library]
61  */
62
63 define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes
64
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");
76 }
77
78
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');
88
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');
93
94 }
95
96 function twitter_follow($a, &$contact) {
97
98         logger("twitter_follow: Check if contact is twitter contact. ".$contact["url"], LOGGER_DEBUG);
99
100         if (!strstr($contact["url"], "://twitter.com") AND !strstr($contact["url"], "@twitter.com"))
101                 return;
102
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);
106
107         $uid = $a->user["uid"];
108
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');
113
114         require_once("addon/twitter/codebird.php");
115
116         $cb = \Codebird\Codebird::getInstance();
117         $cb->setConsumerKey($ckey, $csecret);
118         $cb->setToken($otoken, $osecret);
119
120         $parameters = array();
121         $parameters["screen_name"] = $nickname;
122
123         $user = $cb->friendships_create($parameters);
124
125         twitter_fetchuser($a, $uid, $nickname);
126
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'",
129                                 intval($uid),
130                                 dbesc($nickname));
131         if (count($r))
132                 $contact["contact"] = $r[0];
133 }
134
135 function twitter_jot_nets(&$a,&$b) {
136         if(! local_user())
137                 return;
138
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>';
145         }
146 }
147
148 function twitter_settings_post ($a,$post) {
149         if(! local_user())
150                 return;
151         // don't check twitter settings if twitter submit button is not clicked
152         if (!x($_POST,'twitter-submit'))
153                 return;
154
155         if (isset($_POST['twitter-disconnect'])) {
156                 /***
157                  * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
158                  * from the user configuration
159                  */
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');
173         } else {
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');
192         } else {
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);
203         }}
204 }
205 function twitter_settings(&$a,&$s) {
206         if(! local_user())
207                 return;
208         $a->page['htmlhead'] .= '<link rel="stylesheet"  type="text/css" href="' . $a->get_baseurl() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
209         /***
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)
213          */
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" ' : '');
232
233         $globalshortening = get_config('twitter','intelligent_shortening');
234
235         $s .= '<span id="settings_twitter_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
236         $s .= '<h3>'. t('Twitter Import/Export/Mirror') .'</h3>';
237         $s .= '</span>';
238         $s .= '<div id="settings_twitter_expanded" class="settings-block" style="display: none;">';
239         $s .= '<span class="fakelink" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
240         $s .= '<h3>'. t('Twitter Import/Export/Mirror') .'</h3>';
241         $s .= '</span>';
242
243         if ( (!$ckey) && (!$csecret) ) {
244                 /***
245                  * no global consumer keys
246                  * display warning and skip personal config
247                  */
248                 $s .= '<p>'. t('No consumer key pair for Twitter found. Please contact your site administrator.') .'</p>';
249         } else {
250                 /***
251                  * ok we have a consumer key pair now look into the OAuth stuff
252                  */
253                 if ( (!$otoken) && (!$osecret) ) {
254                         /***
255                          * the user has not yet connected the account to twitter...
256                          * get a temporary OAuth key/secret pair and display a button with
257                          * which the user can request a PIN to connect the account to a
258                          * account at Twitter.
259                          */
260                         require_once('library/twitteroauth.php');
261                         $connection = new TwitterOAuth($ckey, $csecret);
262                         $request_token = $connection->getRequestToken();
263                         $token = $request_token['oauth_token'];
264                         /***
265                          *  make some nice form
266                          */
267                         $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>';
268                         $s .= '<a href="'.$connection->getAuthorizeURL($token).'" target="_twitter"><img src="addon/twitter/lighter.png" alt="'.t('Log in with Twitter').'"></a>';
269                         $s .= '<div id="twitter-pin-wrapper">';
270                         $s .= '<label id="twitter-pin-label" for="twitter-pin">'. t('Copy the PIN from Twitter here') .'</label>';
271                         $s .= '<input id="twitter-pin" type="text" name="twitter-pin" />';
272                         $s .= '<input id="twitter-token" type="hidden" name="twitter-token" value="'.$token.'" />';
273                         $s .= '<input id="twitter-token2" type="hidden" name="twitter-token2" value="'.$request_token['oauth_token_secret'].'" />';
274             $s .= '</div><div class="clear"></div>';
275             $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
276                 } else {
277                         /***
278                          *  we have an OAuth key / secret pair for the user
279                          *  so let's give a chance to disable the postings to Twitter
280                          */
281                         require_once('library/twitteroauth.php');
282                         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
283                         $details = $connection->get('account/verify_credentials');
284                         $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>';
285                         $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>';
286                         if ($a->user['hidewall']) {
287                             $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>';
288                         }
289                         $s .= '<div id="twitter-enable-wrapper">';
290                         $s .= '<label id="twitter-enable-label" for="twitter-checkbox">'. t('Allow posting to Twitter'). '</label>';
291                         $s .= '<input id="twitter-checkbox" type="checkbox" name="twitter-enable" value="1" ' . $checked . '/>';
292                         $s .= '<div class="clear"></div>';
293                         $s .= '<label id="twitter-default-label" for="twitter-default">'. t('Send public postings to Twitter by default') .'</label>';
294                         $s .= '<input id="twitter-default" type="checkbox" name="twitter-default" value="1" ' . $defchecked . '/>';
295                         $s .= '<div class="clear"></div>';
296
297                         $s .= '<label id="twitter-mirror-label" for="twitter-mirror">'.t('Mirror all posts from twitter that are no replies').'</label>';
298                         $s .= '<input id="twitter-mirror" type="checkbox" name="twitter-mirror" value="1" '. $mirrorchecked . '/>';
299                         $s .= '<div class="clear"></div>';
300
301                         if (!$globalshortening) {
302                                 $s .= '<label id="twitter-shortening-label" for="twitter-shortening">'.t('Shortening method that optimizes the tweet').'</label>';
303                                 $s .= '<input id="twitter-shortening" type="checkbox" name="twitter-shortening" value="1" '. $shorteningchecked . '/>';
304                                 $s .= '<div class="clear"></div>';
305
306                                 $s .= '<label id="twitter-sendtaglinks-label" for="twitter-sendtaglinks">'.t('Send linked #-tags and @-names to Twitter').'</label>';
307                                 $s .= '<input id="twitter-sendtaglinks" type="checkbox" name="twitter-sendtaglinks" value="1" '. $linkschecked . '/>';
308                                 $s .= '<div class="clear"></div>';
309                         }
310                         $s .= '</div>';
311
312                         $s .= '<label id="twitter-import-label" for="twitter-import">'.t('Import the remote timeline').'</label>';
313                         $s .= '<input id="twitter-import" type="checkbox" name="twitter-import" value="1" '. $importchecked . '/>';
314                         $s .= '<div class="clear"></div>';
315
316                         $s .= '<label id="twitter-create_user-label" for="twitter-create_user">'.t('Automatically create contacts').'</label>';
317                         $s .= '<input id="twitter-create_user" type="checkbox" name="twitter-create_user" value="1" '. $create_userchecked . '/>';
318                         $s .= '<div class="clear"></div>';
319
320                         $s .= '<div id="twitter-disconnect-wrapper">';
321                         $s .= '<label id="twitter-disconnect-label" for="twitter-disconnect">'. t('Clear OAuth configuration') .'</label>';
322                         $s .= '<input id="twitter-disconnect" type="checkbox" name="twitter-disconnect" value="1" />';
323                         $s .= '</div><div class="clear"></div>';
324                         $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>'; 
325                 }
326         }
327         $s .= '</div><div class="clear"></div>';
328 }
329
330
331 function twitter_post_local(&$a,&$b) {
332
333         if($b['edit'])
334                 return;
335
336         if((local_user()) && (local_user() == $b['uid']) && (! $b['private']) && (! $b['parent']) ) {
337
338                 $twitter_post = intval(get_pconfig(local_user(),'twitter','post'));
339                 $twitter_enable = (($twitter_post && x($_REQUEST,'twitter_enable')) ? intval($_REQUEST['twitter_enable']) : 0);
340
341                 // if API is used, default to the chosen settings
342                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'twitter','post_by_default')))
343                         $twitter_enable = 1;
344
345         if(! $twitter_enable)
346             return;
347
348         if(strlen($b['postopts']))
349             $b['postopts'] .= ',';
350         $b['postopts'] .= 'twitter';
351         }
352 }
353
354 if (! function_exists('short_link')) {
355 function short_link ($url) {
356     require_once('library/slinky.php');
357     $slinky = new Slinky( $url );
358     $yourls_url = get_config('yourls','url1');
359     if ($yourls_url) {
360             $yourls_username = get_config('yourls','username1');
361             $yourls_password = get_config('yourls', 'password1');
362             $yourls_ssl = get_config('yourls', 'ssl1');
363             $yourls = new Slinky_YourLS();
364             $yourls->set( 'username', $yourls_username );
365             $yourls->set( 'password', $yourls_password );
366             $yourls->set( 'ssl', $yourls_ssl );
367             $yourls->set( 'yourls-url', $yourls_url );
368             $slinky->set_cascade( array( $yourls, new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
369     }
370     else {
371             // setup a cascade of shortening services
372             // try to get a short link from these services
373             // in the order ur1.ca, trim, id.gd, tinyurl
374             $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
375     }
376     return $slinky->short();
377 } };
378
379 function twitter_shortenmsg($b, $shortlink = false) {
380         require_once("include/api.php");
381         require_once("include/bbcode.php");
382         require_once("include/html2plain.php");
383
384         $max_char = 140;
385
386         $b['body'] = bb_CleanPictureLinks($b['body']);
387
388         // Looking for the first image
389         $cleaned_body = api_clean_plain_items($b['body']);
390         $image = '';
391         if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$cleaned_body,$matches))
392                 $image = $matches[3];
393
394         if ($image == '')
395                 if(preg_match("/\[img\](.*?)\[\/img\]/is",$cleaned_body,$matches))
396                         $image = $matches[1];
397
398         $multipleimages = (strpos($cleaned_body, "[img") != strrpos($cleaned_body, "[img"));
399
400         // When saved into the database the content is sent through htmlspecialchars
401         // That means that we have to decode all image-urls
402         $image = htmlspecialchars_decode($image);
403
404         $body = $b["body"];
405         if ($b["title"] != "")
406                 $body = $b["title"]."\n\n".$body;
407
408         if (strpos($body, "[bookmark") !== false) {
409                 // splitting the text in two parts:
410                 // before and after the bookmark
411                 $pos = strpos($body, "[bookmark");
412                 $body1 = substr($body, 0, $pos);
413                 $body2 = substr($body, $pos);
414
415                 // Removing all quotes after the bookmark
416                 // they are mostly only the content after the bookmark.
417                 $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2);
418                 $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2);
419                 $body = $body1.$body2;
420         }
421
422         // Add some newlines so that the message could be cut better
423         $body = str_replace(array("[quote", "[bookmark", "[/bookmark]", "[/quote]"),
424                         array("\n[quote", "\n[bookmark", "[/bookmark]\n", "[/quote]\n"), $body);
425
426         // remove the recycle signs and the names since they aren't helpful on twitter
427         // recycle 1
428         $recycle = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8');
429         $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
430         // recycle 2 (Test)
431         $recycle = html_entity_decode("&#x25CC; ", ENT_QUOTES, 'UTF-8');
432         $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
433
434         // remove the share element
435         //$body = preg_replace("/\[share(.*?)\](.*?)\[\/share\]/ism","\n\n$2\n\n",$body);
436
437         // At first convert the text to html
438         $html = bbcode(api_clean_plain_items($body), false, false, 2);
439
440         // Then convert it to plain text
441         $msg = trim(html2plain($html, 0, true));
442         $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
443
444         // Removing multiple newlines
445         while (strpos($msg, "\n\n\n") !== false)
446                 $msg = str_replace("\n\n\n", "\n\n", $msg);
447
448         // Removing multiple spaces
449         while (strpos($msg, "  ") !== false)
450                 $msg = str_replace("  ", " ", $msg);
451
452         $origmsg = trim($msg);
453
454         // Removing URLs
455         $msg = preg_replace('/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', "", $msg);
456
457         $msg = trim($msg);
458
459         $link = '';
460         // look for bookmark-bbcode and handle it with priority
461         if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches))
462                 $link = $matches[1];
463
464         $multiplelinks = (strpos($b['body'], "[bookmark") != strrpos($b['body'], "[bookmark"));
465
466         // If there is no bookmark element then take the first link
467         if ($link == '') {
468                 $links = collecturls($html);
469
470                 foreach($links AS $singlelink) {
471                         $img_str = fetch_url($singlelink);
472
473                         $tempfile = tempnam(get_config("system","temppath"), "cache");
474                         file_put_contents($tempfile, $img_str);
475                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
476                         unlink($tempfile);
477
478                         if (substr($mime, 0, 6) == "image/") {
479                                 $image = $singlelink;
480                                 unset($links[$singlelink]);
481                         }
482                 }
483
484                 if (sizeof($links) > 0) {
485                         reset($links);
486                         $link = current($links);
487                 }
488                 $multiplelinks = (sizeof($links) > 1);
489         }
490
491         $msglink = "";
492         if ($multiplelinks)
493                 $msglink = $b["plink"];
494         else if ($link != "")
495                 $msglink = $link;
496         else if ($multipleimages)
497                 $msglink = $b["plink"];
498         else if ($image != "")
499                 $msglink = $image;
500
501         if (($msglink == "") and strlen($msg) > $max_char)
502                 $msglink = $b["plink"];
503
504         // If the message is short enough then don't modify it.
505         if ((strlen($origmsg) <= $max_char) AND ($msglink == ""))
506                 return(array("msg"=>$origmsg, "image"=>""));
507
508         // If the message is short enough and contains a picture then post the picture as well
509         if ((strlen($origmsg) <= ($max_char - 23)) AND strpos($origmsg, $msglink))
510                 return(array("msg"=>$origmsg, "image"=>$image));
511
512         // If the message is short enough and the link exists in the original message don't modify it as well
513         // -3 because of the bad shortener of twitter
514         if ((strlen($origmsg) <= ($max_char - 3)) AND strpos($origmsg, $msglink))
515                 return(array("msg"=>$origmsg, "image"=>""));
516
517         // Preserve the unshortened link
518         $orig_link = $msglink;
519
520         // Just replace the message link with a 22 character long string
521         // Twitter calculates with this length
522         if (trim($msglink) <> '')
523                 $msglink = "1234567890123456789012";
524
525         if (strlen(trim($msg." ".$msglink)) > ($max_char)) {
526                 $msg = substr($msg, 0, ($max_char) - (strlen($msglink)));
527                 $lastchar = substr($msg, -1);
528                 $msg = substr($msg, 0, -1);
529                 $pos = strrpos($msg, "\n");
530                 if ($pos > 0)
531                         $msg = substr($msg, 0, $pos);
532                 else if ($lastchar != "\n")
533                         $msg = substr($msg, 0, -3)."...";
534
535                 // if the post contains a picture and a link then the system tries to cut the post earlier.
536                 // So the link and the picture can be posted.
537                 if (($image != "") AND ($orig_link != $image)) {
538                         $msg2 = substr($msg, 0, ($max_char - 20) - (strlen($msglink)));
539                         $lastchar = substr($msg2, -1);
540                         $msg2 = substr($msg2, 0, -1);
541                         $pos = strrpos($msg2, "\n");
542                         if ($pos > 0)
543                                 $msg = substr($msg2, 0, $pos);
544                         else if ($lastchar == "\n")
545                                 $msg = trim($msg2);
546                 }
547
548         }
549         // Removing multiple spaces - again
550         while (strpos($msg, "  ") !== false)
551                 $msg = str_replace("  ", " ", $msg);
552
553         $msg = trim($msg);
554
555         // Removing multiple newlines
556         //while (strpos($msg, "\n\n") !== false)
557         //      $msg = str_replace("\n\n", "\n", $msg);
558
559         // Looking if the link points to an image
560         $img_str = fetch_url($orig_link);
561
562         $tempfile = tempnam(get_config("system","temppath"), "cache");
563         file_put_contents($tempfile, $img_str);
564         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
565         unlink($tempfile);
566
567         if (($image == $orig_link) OR (substr($mime, 0, 6) == "image/"))
568                 return(array("msg"=>$msg, "image"=>$orig_link));
569         else if (($image != $orig_link) AND ($image != "") AND (strlen($msg." ".$msglink) <= ($max_char - 23))) {
570                 if ($shortlink)
571                         $orig_link = short_link($orig_link);
572
573                 return(array("msg"=>$msg." ".$orig_link, "image"=>$image));
574         } else {
575                 if ($shortlink)
576                         $orig_link = short_link($orig_link);
577
578                 return(array("msg"=>$msg." ".$orig_link, "image"=>""));
579         }
580 }
581
582 function twitter_action($a, $uid, $pid, $action) {
583
584         $ckey    = get_config('twitter', 'consumerkey');
585         $csecret = get_config('twitter', 'consumersecret');
586         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
587         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
588
589         require_once("addon/twitter/codebird.php");
590
591         $cb = \Codebird\Codebird::getInstance();
592         $cb->setConsumerKey($ckey, $csecret);
593         $cb->setToken($otoken, $osecret);
594
595         $post = array('id' => $pid);
596
597         logger("twitter_action '".$action."' ID: ".$pid." data: " . print_r($post, true), LOGGER_DATA);
598
599         switch ($action) {
600                 case "delete":
601                         $result = $cb->statuses_destroy($post);
602                         break;
603                 case "like":
604                         $result = $cb->favorites_create($post);
605                         break;
606                 case "unlike":
607                         $result = $cb->favorites_destroy($post);
608                         break;
609         }
610         logger("twitter_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
611 }
612
613 function twitter_post_hook(&$a,&$b) {
614
615         /**
616          * Post to Twitter
617          */
618
619         if (!get_pconfig($b["uid"],'twitter','import')) {
620                 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
621                         return;
622         }
623
624         if($b['parent'] != $b['id']) {
625                 logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
626
627                 // Looking if its a reply to a twitter post
628                 if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) {
629                         logger("twitter_post_hook: no twitter post ".$b["parent"]);
630                         return;
631                 }
632
633                 $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
634                         dbesc($b["thr-parent"]),
635                         intval($b["uid"]));
636
637                 if(!count($r)) {
638                         logger("twitter_post_hook: no parent found ".$b["thr-parent"]);
639                         return;
640                 } else {
641                         $iscomment = true;
642                         $orig_post = $r[0];
643                 }
644
645                 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
646                 $nickname = "@[url=".$orig_post["author-link"]."]".$nickname."[/url]";
647
648                 logger("twitter_post_hook: comparing ".$nickname." with ".$b["body"], LOGGER_DEBUG);
649                 if (strpos($b["body"], $nickname) === false)
650                         $b["body"] = $nickname." ".$b["body"];
651
652                 logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA);
653         } else {
654                 $iscomment = false;
655
656                 if($b['private'] OR !strstr($b['postopts'],'twitter'))
657                         return;
658         }
659
660         if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
661                 twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
662
663         if($b['verb'] == ACTIVITY_LIKE) {
664                 logger("twitter_post_hook: parameter 2 ".substr($b["thr-parent"], 9), LOGGER_DEBUG);
665                 if ($b['deleted'])
666                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
667                 else
668                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like");
669                 return;
670         }
671
672         if($b['deleted'] || ($b['created'] !== $b['edited']))
673                 return;
674
675         // if post comes from twitter don't send it back
676         if($b['app'] == "Twitter")
677                 return;
678
679         logger('twitter post invoked');
680
681
682         load_pconfig($b['uid'], 'twitter');
683
684         $ckey    = get_config('twitter', 'consumerkey');
685         $csecret = get_config('twitter', 'consumersecret');
686         $otoken  = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
687         $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
688         $intelligent_shortening = get_pconfig($b['uid'], 'twitter', 'intelligent_shortening');
689
690         // Global setting overrides this
691         if (get_config('twitter','intelligent_shortening'))
692                 $intelligent_shortening = get_config('twitter','intelligent_shortening');
693
694         if($ckey && $csecret && $otoken && $osecret) {
695                 logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
696
697                 // If it's a repeated message from twitter then do a native retweet and exit
698                 if (twitter_is_retweet($a, $b['uid'], $b['body']))
699                         return;
700
701                 require_once('library/twitteroauth.php');
702                 require_once('include/bbcode.php');
703                 $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
704
705                 // in theory max char is 140 but T. uses t.co to make links 
706                 // longer so we give them 10 characters extra
707                 if (!$intelligent_shortening) {
708                         $max_char = 130; // max. length for a tweet
709                         // we will only work with up to two times the length of the dent 
710                         // we can later send to Twitter. This way we can "gain" some 
711                         // information during shortening of potential links but do not 
712                         // shorten all the links in a 200000 character long essay.
713                         if (! $b['title']=='') {
714                             $tmp = $b['title'] . ' : '. $b['body'];
715         //                    $tmp = substr($tmp, 0, 4*$max_char);
716                         } else {
717                             $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char);
718                         }
719                         // if [url=bla][img]blub.png[/img][/url] get blub.png
720                         $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp);
721                         // preserve links to images, videos and audios
722                         $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp);
723                         $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp);
724                         $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp);
725                         $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp);
726                         $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp);
727                         $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp);
728                         $linksenabled = get_pconfig($b['uid'],'twitter','post_taglinks');
729                         // if a #tag is linked, don't send the [url] over to SN
730                         // that is, don't send if the option is not set in the
731                         // connector settings
732                         if ($linksenabled=='0') {
733                                 // #-tags
734                                 $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp);
735                                 // @-mentions
736                                 $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp);
737                                 // recycle 1
738                                 $recycle = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8');
739                                 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
740                                 // recycle 2 (Test)
741                                 $recycle = html_entity_decode("&#x25CC; ", ENT_QUOTES, 'UTF-8');
742                                 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
743                         }
744                         $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp);
745                         $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp);
746                         // find all http or https links in the body of the entry and
747                         // apply the shortener if the link is longer then 20 characters
748                         if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) {
749                             preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls  );
750                             foreach ($allurls as $url) {
751                                 foreach ($url as $u) {
752                                     if (strlen($u)>20) {
753                                         $sl = short_link($u);
754                                         $tmp = str_replace( $u, $sl, $tmp );
755                                     }
756                                 }
757                             }
758                         }
759                         // ok, all the links we want to send out are save, now strip 
760                         // away the remaining bbcode
761                         //$msg = strip_tags(bbcode($tmp, false, false));
762                         $msg = bbcode($tmp, false, false, true);
763                         $msg = str_replace(array('<br>','<br />'),"\n",$msg);
764                         $msg = strip_tags($msg);
765
766                         // quotes not working - let's try this
767                         $msg = html_entity_decode($msg);
768                         if (( strlen($msg) > $max_char) && $max_char > 0) {
769                                 $shortlink = short_link( $b['plink'] );
770                                 // the new message will be shortened such that "... $shortlink"
771                                 // will fit into the character limit
772                                 $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4));
773                                 $msg = str_replace(array('<br>','<br />'),' ',$msg);
774                                 $e = explode(' ', $msg);
775                                 //  remove the last word from the cut down message to 
776                                 //  avoid sending cut words to the MicroBlog
777                                 array_pop($e);
778                                 $msg = implode(' ', $e);
779                                 $msg .= '... ' . $shortlink;
780                         }
781
782                         $msg = trim($msg);
783                         $image = "";
784                 } else {
785                         $msgarr = twitter_shortenmsg($b);
786                         $msg = $msgarr["msg"];
787                         $image = $msgarr["image"];
788                 }
789
790                 // and now tweet it :-)
791                 if(strlen($msg) and ($image != "")) {
792                         $img_str = fetch_url($image);
793
794                         $tempfile = tempnam(get_config("system","temppath"), "cache");
795                         file_put_contents($tempfile, $img_str);
796
797                         // Twitter had changed something so that the old library doesn't work anymore
798                         // so we are using a new library for twitter
799                         // To-Do:
800                         // Switching completely to this library with all functions
801                         require_once("addon/twitter/codebird.php");
802
803                         $cb = \Codebird\Codebird::getInstance();
804                         $cb->setConsumerKey($ckey, $csecret);
805                         $cb->setToken($otoken, $osecret);
806
807                         $post = array('status' => $msg, 'media[]' => $tempfile);
808
809                         if ($iscomment)
810                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
811
812                         $result = $cb->statuses_updateWithMedia($post);
813                         unlink($tempfile);
814
815                         logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG);
816                         if ($result->errors OR $result->error) {
817                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
818
819                                 // Workaround: Remove the picture link so that the post can be reposted without it
820                                 $msg .= " ".$image;
821                                 $image = "";
822                         } elseif ($iscomment) {
823                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
824                                 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
825                                         dbesc("twitter::".$result->id_str),
826                                         dbesc($result->text),
827                                         intval($b['id'])
828                                 );
829                         }
830                 }
831
832                 if(strlen($msg) and ($image == "")) {
833                         $url = 'statuses/update';
834                         $post = array('status' => $msg);
835
836                         if ($iscomment)
837                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
838
839                         $result = $tweet->post($url, $post);
840                         logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
841                         if ($result->errors) {
842                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
843
844                                 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
845                                 if (count($r))
846                                         $a->contact = $r[0]["id"];
847
848                                 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
849                                 require_once('include/queue_fn.php');
850                                 add_to_queue($a->contact,NETWORK_TWITTER,$s);
851                                 notice(t('Twitter post failed. Queued for retry.').EOL);
852                         } elseif ($iscomment) {
853                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
854                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
855                                         dbesc("twitter::".$result->id_str),
856                                         intval($b['id'])
857                                 );
858                                 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
859                                 //      dbesc("twitter::".$result->id_str),
860                                 //      dbesc($result->text),
861                                 //      intval($b['id'])
862                                 //);
863                         }
864                 }
865         }
866 }
867
868 function twitter_plugin_admin_post(&$a){
869         $consumerkey    =       ((x($_POST,'consumerkey'))              ? notags(trim($_POST['consumerkey']))   : '');
870         $consumersecret =       ((x($_POST,'consumersecret'))   ? notags(trim($_POST['consumersecret'])): '');
871         $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
872         set_config('twitter','consumerkey',$consumerkey);
873         set_config('twitter','consumersecret',$consumersecret);
874         set_config('twitter','application_name',$applicationname);
875         info( t('Settings updated.'). EOL );
876 }
877 function twitter_plugin_admin(&$a, &$o){
878         $t = get_markup_template( "admin.tpl", "addon/twitter/" );
879
880         $o = replace_macros($t, array(
881                 '$submit' => t('Save Settings'),
882                                                                 // name, label, value, help, [extra values]
883                 '$consumerkey' => array('consumerkey', t('Consumer key'),  get_config('twitter', 'consumerkey' ), ''),
884                 '$consumersecret' => array('consumersecret', t('Consumer secret'),  get_config('twitter', 'consumersecret' ), ''),
885                 '$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'))
886         ));
887 }
888
889 function twitter_cron($a,$b) {
890         $last = get_config('twitter','last_poll');
891
892         $poll_interval = intval(get_config('twitter','poll_interval'));
893         if(! $poll_interval)
894                 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
895
896         if($last) {
897                 $next = $last + ($poll_interval * 60);
898                 if($next > time()) {
899                         logger('twitter: poll intervall not reached');
900                         return;
901                 }
902         }
903         logger('twitter: cron_start');
904
905         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND()");
906         if(count($r)) {
907                 foreach($r as $rr) {
908                         logger('twitter: fetching for user '.$rr['uid']);
909                         twitter_fetchtimeline($a, $rr['uid']);
910                 }
911         }
912
913
914         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
915         if(count($r)) {
916                 foreach($r as $rr) {
917                         logger('twitter: importing timeline from user '.$rr['uid']);
918                         twitter_fetchhometimeline($a, $rr["uid"]);
919
920 /*
921                         // To-Do
922                         // check for new contacts once a day
923                         $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
924                         if($last_contact_check)
925                                 $next_contact_check = $last_contact_check + 86400;
926                         else
927                                 $next_contact_check = 0;
928
929                         if($next_contact_check <= time()) {
930                                 pumpio_getallusers($a, $rr["uid"]);
931                                 set_pconfig($rr['uid'],'pumpio','contact_check',time());
932                         }
933 */
934
935                 }
936         }
937
938         logger('twitter: cron_end');
939
940         set_config('twitter','last_poll', time());
941 }
942
943 function twitter_fetchtimeline($a, $uid) {
944         $ckey    = get_config('twitter', 'consumerkey');
945         $csecret = get_config('twitter', 'consumersecret');
946         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
947         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
948         $lastid  = get_pconfig($uid, 'twitter', 'lastid');
949
950         $application_name  = get_config('twitter', 'application_name');
951
952         if ($application_name == "")
953                 $application_name = $a->get_hostname();
954
955         $has_picture = false;
956
957         require_once('mod/item.php');
958
959         require_once('library/twitteroauth.php');
960         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
961
962         $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
963
964         $first_time = ($lastid == "");
965
966         if ($lastid <> "")
967                 $parameters["since_id"] = $lastid;
968
969         $items = $connection->get('statuses/user_timeline', $parameters);
970
971         if (!is_array($items))
972                 return;
973
974         $posts = array_reverse($items);
975
976         if (count($posts)) {
977             foreach ($posts as $post) {
978                 if ($post->id_str > $lastid)
979                         $lastid = $post->id_str;
980
981                 if ($first_time)
982                         continue;
983
984                 if (!strpos($post->source, $application_name)) {
985                         $_SESSION["authenticated"] = true;
986                         $_SESSION["uid"] = $uid;
987
988                         unset($_REQUEST);
989                         $_REQUEST["type"] = "wall";
990                         $_REQUEST["api_source"] = true;
991                         $_REQUEST["profile_uid"] = $uid;
992                         $_REQUEST["source"] = "Twitter";
993
994                         //$_REQUEST["date"] = $post->created_at;
995
996                         $_REQUEST["title"] = "";
997
998                         if (is_object($post->retweeted_status)) {
999
1000                                 $_REQUEST['body'] = $post->retweeted_status->text;
1001
1002                                 // media
1003                                 if (is_array($post->retweeted_status->entities->media)) {
1004                                         foreach($post->retweeted_status->entities->media AS $media) {
1005                                                 switch($media->type) {
1006                                                         case 'photo':
1007                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1008                                                                 $has_picture = true;
1009                                                                 break;
1010                                                 }
1011                                         }
1012                                 }
1013
1014                                 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $has_picture);
1015                                 $_REQUEST['body'] = $converted["body"];
1016
1017                                 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
1018                                         "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1019                                         "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1020                                         "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1021                                         $_REQUEST['body'];
1022                                 $_REQUEST['body'] .= "[/share]";
1023                         } else {
1024                                 $_REQUEST["body"] = $post->text;
1025
1026                                 if (is_array($post->entities->media)) {
1027                                         foreach($post->entities->media AS $media) {
1028                                                 switch($media->type) {
1029                                                         case 'photo':
1030                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1031                                                                 $has_picture = true;
1032                                                                 break;
1033                                                 }
1034                                         }
1035                                 }
1036
1037                                 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $has_picture);
1038                                 $_REQUEST['body'] = $converted["body"];
1039                         }
1040
1041                         if (is_string($post->place->name))
1042                                 $_REQUEST["location"] = $post->place->name;
1043
1044                         if (is_string($post->place->full_name))
1045                                 $_REQUEST["location"] = $post->place->full_name;
1046
1047                         if (is_array($post->geo->coordinates))
1048                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1049
1050                         if (is_array($post->coordinates->coordinates))
1051                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1052
1053                         //print_r($_REQUEST);
1054                         logger('twitter: posting for user '.$uid);
1055
1056 //                      require_once('mod/item.php');
1057
1058                         item_post($a);
1059                 }
1060             }
1061         }
1062         set_pconfig($uid, 'twitter', 'lastid', $lastid);
1063 }
1064
1065 function twitter_queue_hook(&$a,&$b) {
1066
1067         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
1068                 dbesc(NETWORK_TWITTER)
1069                 );
1070         if(! count($qi))
1071                 return;
1072
1073         require_once('include/queue_fn.php');
1074
1075         foreach($qi as $x) {
1076                 if($x['network'] !== NETWORK_TWITTER)
1077                         continue;
1078
1079                 logger('twitter_queue: run');
1080
1081                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
1082                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1083                         intval($x['cid'])
1084                 );
1085                 if(! count($r))
1086                         continue;
1087
1088                 $user = $r[0];
1089
1090                 $ckey    = get_config('twitter', 'consumerkey');
1091                 $csecret = get_config('twitter', 'consumersecret');
1092                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
1093                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
1094
1095                 $success = false;
1096
1097                 if ($ckey AND $csecret AND $otoken AND $osecret) {
1098
1099                         logger('twitter_queue: able to post');
1100
1101                         $z = unserialize($x['content']);
1102
1103                         require_once("addon/twitter/codebird.php");
1104
1105                         $cb = \Codebird\Codebird::getInstance();
1106                         $cb->setConsumerKey($ckey, $csecret);
1107                         $cb->setToken($otoken, $osecret);
1108
1109                         if ($z['url'] == "statuses/update")
1110                                 $result = $cb->statuses_update($z['post']);
1111
1112                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
1113
1114                         if ($result->errors)
1115                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
1116                         else {
1117                                 $success = true;
1118                                 remove_queue_item($x['id']);
1119                         }
1120                 } else
1121                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
1122
1123                 if (!$success) {
1124                         logger('twitter_queue: delayed');
1125                         update_queue_time($x['id']);
1126                 }
1127         }
1128 }
1129
1130 function twitter_fetch_contact($uid, $contact, $create_user) {
1131
1132         // Check if the unique contact is existing
1133         // To-Do: only update once a while
1134          $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
1135                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1136
1137         if (count($r) == 0)
1138                 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
1139                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1140                         dbesc($contact->name),
1141                         dbesc($contact->screen_name),
1142                         dbesc($contact->profile_image_url_https));
1143         else
1144                 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
1145                         dbesc($contact->name),
1146                         dbesc($contact->screen_name),
1147                         dbesc($contact->profile_image_url_https),
1148                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1149
1150         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1151                 intval($uid), dbesc("twitter::".$contact->id_str));
1152
1153         if(!count($r) AND !$create_user)
1154                 return(0);
1155
1156         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
1157                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
1158                 return(-1);
1159         }
1160
1161         if(!count($r)) {
1162                 // create contact record
1163                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
1164                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
1165                                         `writable`, `blocked`, `readonly`, `pending` )
1166                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
1167                         intval($uid),
1168                         dbesc(datetime_convert()),
1169                         dbesc("https://twitter.com/".$contact->screen_name),
1170                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1171                         dbesc($contact->screen_name."@twitter.com"),
1172                         dbesc("twitter::".$contact->id_str),
1173                         dbesc(''),
1174                         dbesc("twitter::".$contact->id_str),
1175                         dbesc($contact->name),
1176                         dbesc($contact->screen_name),
1177                         dbesc($contact->profile_image_url_https),
1178                         dbesc(NETWORK_TWITTER),
1179                         intval(CONTACT_IS_FRIEND),
1180                         intval(1),
1181                         intval(1)
1182                 );
1183
1184                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
1185                         dbesc("twitter::".$contact->id_str),
1186                         intval($uid)
1187                         );
1188
1189                 if(! count($r))
1190                         return(false);
1191
1192                 $contact_id  = $r[0]['id'];
1193
1194                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
1195                         intval($uid)
1196                 );
1197
1198                 if($g && intval($g[0]['def_gid'])) {
1199                         require_once('include/group.php');
1200                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
1201                 }
1202
1203                 require_once("Photo.php");
1204
1205                 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
1206
1207                 q("UPDATE `contact` SET `photo` = '%s',
1208                                         `thumb` = '%s',
1209                                         `micro` = '%s',
1210                                         `name-date` = '%s',
1211                                         `uri-date` = '%s',
1212                                         `avatar-date` = '%s'
1213                                 WHERE `id` = %d",
1214                         dbesc($photos[0]),
1215                         dbesc($photos[1]),
1216                         dbesc($photos[2]),
1217                         dbesc(datetime_convert()),
1218                         dbesc(datetime_convert()),
1219                         dbesc(datetime_convert()),
1220                         intval($contact_id)
1221                 );
1222
1223         } else {
1224                 // update profile photos once every two weeks as we have no notification of when they change.
1225
1226                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
1227                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
1228
1229                 // check that we have all the photos, this has been known to fail on occasion
1230
1231                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
1232
1233                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
1234
1235                         require_once("Photo.php");
1236
1237                         $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
1238
1239                         q("UPDATE `contact` SET `photo` = '%s',
1240                                                 `thumb` = '%s',
1241                                                 `micro` = '%s',
1242                                                 `name-date` = '%s',
1243                                                 `uri-date` = '%s',
1244                                                 `avatar-date` = '%s',
1245                                                 `url` = '%s',
1246                                                 `nurl` = '%s',
1247                                                 `addr` = '%s',
1248                                                 `name` = '%s',
1249                                                 `nick` = '%s'
1250                                         WHERE `id` = %d",
1251                                 dbesc($photos[0]),
1252                                 dbesc($photos[1]),
1253                                 dbesc($photos[2]),
1254                                 dbesc(datetime_convert()),
1255                                 dbesc(datetime_convert()),
1256                                 dbesc(datetime_convert()),
1257                                 dbesc("https://twitter.com/".$contact->screen_name),
1258                                 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1259                                 dbesc($contact->screen_name."@twitter.com"),
1260                                 dbesc($contact->name),
1261                                 dbesc($contact->screen_name),
1262                                 intval($r[0]['id'])
1263                         );
1264                 }
1265         }
1266
1267         return($r[0]["id"]);
1268 }
1269
1270 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
1271         $ckey    = get_config('twitter', 'consumerkey');
1272         $csecret = get_config('twitter', 'consumersecret');
1273         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1274         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1275
1276         require_once("addon/twitter/codebird.php");
1277
1278         $cb = \Codebird\Codebird::getInstance();
1279         $cb->setConsumerKey($ckey, $csecret);
1280         $cb->setToken($otoken, $osecret);
1281
1282         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1283                 intval($uid));
1284
1285         if(count($r)) {
1286                 $self = $r[0];
1287         } else
1288                 return;
1289
1290         $parameters = array();
1291
1292         if ($screen_name != "")
1293                 $parameters["screen_name"] = $screen_name;
1294
1295         if ($user_id != "")
1296                 $parameters["user_id"] = $user_id;
1297
1298         // Fetching user data
1299         $user = $cb->users_show($parameters);
1300
1301         if (!is_object($user))
1302                 return;
1303
1304         $contact_id = twitter_fetch_contact($uid, $user, true);
1305
1306         return $contact_id;
1307 }
1308
1309 function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontincludemedia) {
1310         require_once("include/oembed.php");
1311         require_once("include/network.php");
1312
1313         $tags = "";
1314
1315         if (isset($item->entities->urls)) {
1316                 $type = "";
1317                 $footerurl = "";
1318                 $footerlink = "";
1319                 $footer = "";
1320
1321                 foreach ($item->entities->urls AS $url) {
1322                         if ($url->url AND $url->expanded_url AND $url->display_url) {
1323
1324                                 $expanded_url = original_url($url->expanded_url);
1325
1326                                 $oembed_data = oembed_fetch_url($expanded_url);
1327
1328                                 // Quickfix: Workaround for URL with "[" and "]" in it
1329                                 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1330                                         $expanded_url = $url->url;
1331
1332                                 if ($type == "")
1333                                         $type = $oembed_data->type;
1334
1335                                 if ($oembed_data->type == "video") {
1336                                         $body = str_replace($url->url,
1337                                                         "[video]".$expanded_url."[/video]", $body);
1338                                         $dontincludemedia = true;
1339                                 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1340                                         $body = str_replace($url->url,
1341                                                         "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1342                                                         $body);
1343                                         $dontincludemedia = true;
1344                                 } elseif ($oembed_data->type != "link")
1345                                         $body = str_replace($url->url,
1346                                                         "[url=".$expanded_url."]".$expanded_url."[/url]",
1347                                                         $body);
1348                                 else {
1349                                         $img_str = fetch_url($expanded_url, true, $redirects, 4);
1350
1351                                         $tempfile = tempnam(get_config("system","temppath"), "cache");
1352                                         file_put_contents($tempfile, $img_str);
1353                                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1354                                         unlink($tempfile);
1355
1356                                         if (substr($mime, 0, 6) == "image/") {
1357                                                 $type = "photo";
1358                                                 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1359                                                 $dontincludemedia = true;
1360                                         } else {
1361                                                 $type = $oembed_data->type;
1362                                                 $footerurl = $expanded_url;
1363                                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1364
1365                                                 $body = str_replace($url->url, $footerlink, $body);
1366                                         }
1367                                 }
1368                         }
1369                 }
1370
1371                 if ($footerurl != "")
1372                         $footer = add_page_info($footerurl);
1373
1374                 if (($footerlink != "") AND (trim($footer) != "")) {
1375                         $removedlink = trim(str_replace($footerlink, "", $body));
1376
1377                         if (strstr($body, $removedlink))
1378                                 $body = $removedlink;
1379
1380                         $body .= $footer;
1381                 }
1382
1383                 if ($no_tags)
1384                         return(array("body" => $body, "tags" => ""));
1385
1386                 $tags_arr = array();
1387
1388                 foreach ($item->entities->hashtags AS $hashtag) {
1389                         $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1390                         $tags_arr["#".$hashtag->text] = $url;
1391                         $body = str_replace("#".$hashtag->text, $url, $body);
1392                 }
1393
1394                 foreach ($item->entities->user_mentions AS $mention) {
1395                         $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1396                         $tags_arr["@".$mention->screen_name] = $url;
1397                         $body = str_replace("@".$mention->screen_name, $url, $body);
1398                 }
1399
1400                 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1401                 $tags = get_tags($body);
1402
1403                 if(count($tags)) {
1404                         foreach($tags as $tag) {
1405                                 if (strstr(trim($tag), " "))
1406                                         continue;
1407
1408                                 if(strpos($tag,'#') === 0) {
1409                                         if(strpos($tag,'[url='))
1410                                                 continue;
1411
1412                                         // don't link tags that are already embedded in links
1413
1414                                         if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1415                                                 continue;
1416                                         if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1417                                                 continue;
1418
1419                                         $basetag = str_replace('_',' ',substr($tag,1));
1420                                         $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1421                                         $body = str_replace($tag,$url,$body);
1422                                         $tags_arr["#".$basetag] = $url;
1423                                         continue;
1424                                 } elseif(strpos($tag,'@') === 0) {
1425                                         if(strpos($tag,'[url='))
1426                                                 continue;
1427
1428                                         $basetag = substr($tag,1);
1429                                         $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1430                                         $body = str_replace($tag,$url,$body);
1431                                         $tags_arr["@".$basetag] = $url;
1432                                 }
1433                         }
1434                 }
1435
1436
1437                 $tags = implode($tags_arr, ",");
1438
1439         }
1440         return(array("body" => $body, "tags" => $tags));
1441 }
1442
1443 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1444
1445         $has_picture = false;
1446
1447         $postarray = array();
1448         $postarray['network'] = NETWORK_TWITTER;
1449         $postarray['gravity'] = 0;
1450         $postarray['uid'] = $uid;
1451         $postarray['wall'] = 0;
1452         $postarray['uri'] = "twitter::".$post->id_str;
1453
1454         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1455                         dbesc($postarray['uri']),
1456                         intval($uid)
1457                 );
1458
1459         if (count($r))
1460                 return(array());
1461
1462         $contactid = 0;
1463
1464         if ($post->in_reply_to_status_id_str != "") {
1465
1466                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1467
1468                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1469                                 dbesc($parent),
1470                                 intval($uid)
1471                         );
1472                 if (count($r)) {
1473                         $postarray['thr-parent'] = $r[0]["uri"];
1474                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1475                 } else {
1476                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1477                                         dbesc($parent),
1478                                         intval($uid)
1479                                 );
1480                         if (count($r)) {
1481                                 $postarray['thr-parent'] = $r[0]['uri'];
1482                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1483                         } else {
1484                                 $postarray['thr-parent'] = $postarray['uri'];
1485                                 $postarray['parent-uri'] = $postarray['uri'];
1486                         }
1487                 }
1488
1489                 // Is it me?
1490                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1491
1492                 if ($post->user->id_str == $own_id) {
1493                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1494                                 intval($uid));
1495
1496                         if(count($r)) {
1497                                 $contactid = $r[0]["id"];
1498
1499                                 $postarray['owner-name'] =  $r[0]["name"];
1500                                 $postarray['owner-link'] = $r[0]["url"];
1501                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1502                         } else
1503                                 return(array());
1504                 }
1505         } else
1506                 $postarray['parent-uri'] = $postarray['uri'];
1507
1508         if ($contactid == 0) {
1509                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1510
1511                 $postarray['owner-name'] = $post->user->name;
1512                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1513                 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1514         }
1515
1516         if(($contactid == 0) AND !$only_existing_contact)
1517                 $contactid = $self['id'];
1518         elseif ($contactid <= 0)
1519                 return(array());
1520
1521         $postarray['contact-id'] = $contactid;
1522
1523         $postarray['verb'] = ACTIVITY_POST;
1524         $postarray['author-name'] = $postarray['owner-name'];
1525         $postarray['author-link'] = $postarray['owner-link'];
1526         $postarray['author-avatar'] = $postarray['owner-avatar'];
1527         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1528         $postarray['app'] = strip_tags($post->source);
1529
1530         if ($post->user->protected) {
1531                 $postarray['private'] = 1;
1532                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1533         }
1534
1535         $postarray['body'] = $post->text;
1536
1537         // media
1538         if (is_array($post->entities->media)) {
1539                 foreach($post->entities->media AS $media) {
1540                         switch($media->type) {
1541                                 case 'photo':
1542                                         $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1543                                         $has_picture = true;
1544                                         break;
1545                                 default:
1546                                         $postarray['body'] .= print_r($media, true);
1547                         }
1548                 }
1549         }
1550
1551         $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $has_picture);
1552         $postarray['body'] = $converted["body"];
1553         $postarray['tag'] = $converted["tags"];
1554
1555         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1556         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1557
1558         if (is_string($post->place->name))
1559                 $postarray["location"] = $post->place->name;
1560
1561         if (is_string($post->place->full_name))
1562                 $postarray["location"] = $post->place->full_name;
1563
1564         if (is_array($post->geo->coordinates))
1565                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1566
1567         if (is_array($post->coordinates->coordinates))
1568                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1569
1570         if (is_object($post->retweeted_status)) {
1571
1572                 $postarray['body'] = $post->retweeted_status->text;
1573
1574                 // media
1575                 if (is_array($post->retweeted_status->entities->media)) {
1576                         foreach($post->retweeted_status->entities->media AS $media) {
1577                                 switch($media->type) {
1578                                         case 'photo':
1579                                                 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1580                                                 $has_picture = true;
1581                                                 break;
1582                                         default:
1583                                                 $postarray['body'] .= print_r($media, true);
1584                                 }
1585                         }
1586                 }
1587
1588                 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $has_picture);
1589                 $postarray['body'] = $converted["body"];
1590                 $postarray['tag'] = $converted["tags"];
1591
1592                 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1593
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."']".
1600                                 $postarray['body'];
1601                         $postarray['body'] .= "[/share]";
1602                 } else {
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;
1610                         //}
1611                 }
1612
1613         }
1614         return($postarray);
1615 }
1616
1617 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1618
1619         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1620                         intval($uid)
1621                 );
1622
1623         if(!count($user))
1624                 return;
1625
1626         // Is it me?
1627         if (link_compare($user[0]["url"], $postarray['author-link']))
1628                 return;
1629
1630         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1631                         intval($uid),
1632                         dbesc("twitter::".$own_id)
1633                 );
1634
1635         if(!count($own_user))
1636                 return;
1637
1638         // Is it me from twitter?
1639         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1640                 return;
1641
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']),
1644                         intval($uid)
1645                         );
1646
1647         if(count($myconv)) {
1648
1649                 foreach($myconv as $conv) {
1650                         // now if we find a match, it means we're in this conversation
1651
1652                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1653                                 continue;
1654
1655                         require_once('include/enotify.php');
1656
1657                         $conv_parent = $conv['parent'];
1658
1659                         notification(array(
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,
1672                                 'otype'        => 'item',
1673                                 'parent'       => $conv_parent,
1674                         ));
1675
1676                         // only send one notification
1677                         break;
1678                 }
1679         }
1680 }
1681
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');
1688
1689         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1690
1691         require_once('library/twitteroauth.php');
1692         require_once('include/items.php');
1693
1694         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1695
1696         $own_contact = twitter_fetch_own_contact($a, $uid);
1697
1698         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1699                 intval($own_contact),
1700                 intval($uid));
1701
1702         if(count($r)) {
1703                 $own_id = $r[0]["nick"];
1704         } else {
1705                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1706                 return;
1707         }
1708
1709         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1710                 intval($uid));
1711
1712         if(count($r)) {
1713                 $self = $r[0];
1714         } else {
1715                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1716                 return;
1717         }
1718
1719         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1720                 intval($uid));
1721         if(!count($u)) {
1722                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1723                 return;
1724         }
1725
1726         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1727         //$parameters["count"] = 200;
1728
1729
1730         // Fetching timeline
1731         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1732
1733         $first_time = ($lastid == "");
1734
1735         if ($lastid <> "")
1736                 $parameters["since_id"] = $lastid;
1737
1738         $items = $connection->get('statuses/home_timeline', $parameters);
1739
1740         if (!is_array($items)) {
1741                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1742                 return;
1743         }
1744
1745         $posts = array_reverse($items);
1746
1747         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1748
1749         if (count($posts)) {
1750                 foreach ($posts as $post) {
1751                         if ($post->id_str > $lastid)
1752                                 $lastid = $post->id_str;
1753
1754                         if ($first_time)
1755                                 continue;
1756
1757                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1758
1759                         if (trim($postarray['body']) == "")
1760                                 continue;
1761
1762                         $item = item_store($postarray);
1763
1764                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1765
1766                         if ($item != 0)
1767                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1768
1769                 }
1770         }
1771         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1772
1773         // Fetching mentions
1774         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1775
1776         $first_time = ($lastid == "");
1777
1778         if ($lastid <> "")
1779                 $parameters["since_id"] = $lastid;
1780
1781         $items = $connection->get('statuses/mentions_timeline', $parameters);
1782
1783         if (!is_array($items)) {
1784                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1785                 return;
1786         }
1787
1788         $posts = array_reverse($items);
1789
1790         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1791
1792         if (count($posts)) {
1793                 foreach ($posts as $post) {
1794                         if ($post->id_str > $lastid)
1795                                 $lastid = $post->id_str;
1796
1797                         if ($first_time)
1798                                 continue;
1799
1800                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1801
1802                         if (trim($postarray['body']) == "")
1803                                 continue;
1804
1805                         $item = item_store($postarray);
1806
1807                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1808
1809                         if ($item == 0) {
1810                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1811                                         dbesc($postarray['uri']),
1812                                         intval($uid)
1813                                 );
1814                                 if (count($r))
1815                                         $item = $r[0]['id'];
1816                         }
1817
1818                         if ($item != 0) {
1819                                 require_once('include/enotify.php');
1820                                 notification(array(
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,
1833                                         'otype'        => 'item'
1834                                 ));
1835                         }
1836                 }
1837         }
1838
1839         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1840 }
1841
1842 function twitter_fetch_own_contact($a, $uid) {
1843         $ckey    = get_config('twitter', 'consumerkey');
1844         $csecret = get_config('twitter', 'consumersecret');
1845         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1846         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1847
1848         $own_id = get_pconfig($uid, 'twitter', 'own_id');
1849
1850         $contact_id = 0;
1851
1852         if ($own_id == "") {
1853                 require_once('library/twitteroauth.php');
1854
1855                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1856
1857                 // Fetching user data
1858                 $user = $connection->get('account/verify_credentials');
1859
1860                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1861
1862                 $contact_id = twitter_fetch_contact($uid, $user, true);
1863
1864         } else {
1865                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1866                         intval($uid), dbesc("twitter::".$own_id));
1867                 if(count($r))
1868                         $contact_id = $r[0]["id"];
1869                 else
1870                         del_pconfig($uid, 'twitter', 'own_id');
1871
1872         }
1873
1874         return($contact_id);
1875 }
1876
1877 function twitter_is_retweet($a, $uid, $body) {
1878         $body = trim($body);
1879
1880         // Skip if it isn't a pure repeated messages
1881         // Does it start with a share?
1882         if (strpos($body, "[share") > 0)
1883                 return(false);
1884
1885         // Does it end with a share?
1886         if (strlen($body) > (strrpos($body, "[/share]") + 8))
1887                 return(false);
1888
1889         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1890         // Skip if there is no shared message in there
1891         if ($body == $attributes)
1892                 return(false);
1893
1894         $link = "";
1895         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1896         if ($matches[1] != "")
1897                 $link = $matches[1];
1898
1899         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1900         if ($matches[1] != "")
1901                 $link = $matches[1];
1902
1903         $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1904         if ($id == $link)
1905                 return(false);
1906
1907         logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1908
1909         $ckey    = get_config('twitter', 'consumerkey');
1910         $csecret = get_config('twitter', 'consumersecret');
1911         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1912         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1913
1914         require_once('library/twitteroauth.php');
1915         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1916
1917         $result = $connection->post('statuses/retweet/'.$id);
1918
1919         logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1920
1921         return(!isset($result->errors));
1922 }
1923
1924 ?>