]> git.mxchange.org Git - friendica-addons.git/blob - twitter/twitter.php
Merge pull request #187 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 .= '<img class="connector" src="images/twitter.png" /><h3 class="connector">'. 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 .= '<img class="connector" src="images/twitter.png" /><h3 class="connector">'. 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         require_once('include/items.php');
959
960         require_once('library/twitteroauth.php');
961         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
962
963         $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
964
965         $first_time = ($lastid == "");
966
967         if ($lastid <> "")
968                 $parameters["since_id"] = $lastid;
969
970         $items = $connection->get('statuses/user_timeline', $parameters);
971
972         if (!is_array($items))
973                 return;
974
975         $posts = array_reverse($items);
976
977         if (count($posts)) {
978             foreach ($posts as $post) {
979                 if ($post->id_str > $lastid)
980                         $lastid = $post->id_str;
981
982                 if ($first_time)
983                         continue;
984
985                 if (!strpos($post->source, $application_name)) {
986                         $_SESSION["authenticated"] = true;
987                         $_SESSION["uid"] = $uid;
988
989                         unset($_REQUEST);
990                         $_REQUEST["type"] = "wall";
991                         $_REQUEST["api_source"] = true;
992                         $_REQUEST["profile_uid"] = $uid;
993                         $_REQUEST["source"] = "Twitter";
994
995                         //$_REQUEST["date"] = $post->created_at;
996
997                         $_REQUEST["title"] = "";
998
999                         if (is_object($post->retweeted_status)) {
1000
1001                                 $_REQUEST['body'] = $post->retweeted_status->text;
1002
1003                                 // media
1004                                 if (is_array($post->retweeted_status->entities->media)) {
1005                                         foreach($post->retweeted_status->entities->media AS $media) {
1006                                                 switch($media->type) {
1007                                                         case 'photo':
1008                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1009                                                                 $has_picture = true;
1010                                                                 break;
1011                                                 }
1012                                         }
1013                                 }
1014
1015                                 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $has_picture);
1016                                 $_REQUEST['body'] = $converted["body"];
1017
1018                                 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
1019                                         "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1020                                         "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1021                                         "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1022                                         $_REQUEST['body'];
1023                                 $_REQUEST['body'] .= "[/share]";
1024                         } else {
1025                                 $_REQUEST["body"] = $post->text;
1026
1027                                 if (is_array($post->entities->media)) {
1028                                         foreach($post->entities->media AS $media) {
1029                                                 switch($media->type) {
1030                                                         case 'photo':
1031                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1032                                                                 $has_picture = true;
1033                                                                 break;
1034                                                 }
1035                                         }
1036                                 }
1037
1038                                 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $has_picture);
1039                                 $_REQUEST['body'] = $converted["body"];
1040                         }
1041
1042                         if (is_string($post->place->name))
1043                                 $_REQUEST["location"] = $post->place->name;
1044
1045                         if (is_string($post->place->full_name))
1046                                 $_REQUEST["location"] = $post->place->full_name;
1047
1048                         if (is_array($post->geo->coordinates))
1049                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1050
1051                         if (is_array($post->coordinates->coordinates))
1052                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1053
1054                         //print_r($_REQUEST);
1055                         logger('twitter: posting for user '.$uid);
1056
1057 //                      require_once('mod/item.php');
1058
1059                         item_post($a);
1060                 }
1061             }
1062         }
1063         set_pconfig($uid, 'twitter', 'lastid', $lastid);
1064 }
1065
1066 function twitter_queue_hook(&$a,&$b) {
1067
1068         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
1069                 dbesc(NETWORK_TWITTER)
1070                 );
1071         if(! count($qi))
1072                 return;
1073
1074         require_once('include/queue_fn.php');
1075
1076         foreach($qi as $x) {
1077                 if($x['network'] !== NETWORK_TWITTER)
1078                         continue;
1079
1080                 logger('twitter_queue: run');
1081
1082                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
1083                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1084                         intval($x['cid'])
1085                 );
1086                 if(! count($r))
1087                         continue;
1088
1089                 $user = $r[0];
1090
1091                 $ckey    = get_config('twitter', 'consumerkey');
1092                 $csecret = get_config('twitter', 'consumersecret');
1093                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
1094                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
1095
1096                 $success = false;
1097
1098                 if ($ckey AND $csecret AND $otoken AND $osecret) {
1099
1100                         logger('twitter_queue: able to post');
1101
1102                         $z = unserialize($x['content']);
1103
1104                         require_once("addon/twitter/codebird.php");
1105
1106                         $cb = \Codebird\Codebird::getInstance();
1107                         $cb->setConsumerKey($ckey, $csecret);
1108                         $cb->setToken($otoken, $osecret);
1109
1110                         if ($z['url'] == "statuses/update")
1111                                 $result = $cb->statuses_update($z['post']);
1112
1113                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
1114
1115                         if ($result->errors)
1116                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
1117                         else {
1118                                 $success = true;
1119                                 remove_queue_item($x['id']);
1120                         }
1121                 } else
1122                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
1123
1124                 if (!$success) {
1125                         logger('twitter_queue: delayed');
1126                         update_queue_time($x['id']);
1127                 }
1128         }
1129 }
1130
1131 function twitter_fetch_contact($uid, $contact, $create_user) {
1132
1133         // Check if the unique contact is existing
1134         // To-Do: only update once a while
1135          $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
1136                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1137
1138         if (count($r) == 0)
1139                 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
1140                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1141                         dbesc($contact->name),
1142                         dbesc($contact->screen_name),
1143                         dbesc($contact->profile_image_url_https));
1144         else
1145                 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
1146                         dbesc($contact->name),
1147                         dbesc($contact->screen_name),
1148                         dbesc($contact->profile_image_url_https),
1149                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1150
1151         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1152                 intval($uid), dbesc("twitter::".$contact->id_str));
1153
1154         if(!count($r) AND !$create_user)
1155                 return(0);
1156
1157         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
1158                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
1159                 return(-1);
1160         }
1161
1162         if(!count($r)) {
1163                 // create contact record
1164                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
1165                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
1166                                         `writable`, `blocked`, `readonly`, `pending` )
1167                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
1168                         intval($uid),
1169                         dbesc(datetime_convert()),
1170                         dbesc("https://twitter.com/".$contact->screen_name),
1171                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1172                         dbesc($contact->screen_name."@twitter.com"),
1173                         dbesc("twitter::".$contact->id_str),
1174                         dbesc(''),
1175                         dbesc("twitter::".$contact->id_str),
1176                         dbesc($contact->name),
1177                         dbesc($contact->screen_name),
1178                         dbesc($contact->profile_image_url_https),
1179                         dbesc(NETWORK_TWITTER),
1180                         intval(CONTACT_IS_FRIEND),
1181                         intval(1),
1182                         intval(1)
1183                 );
1184
1185                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
1186                         dbesc("twitter::".$contact->id_str),
1187                         intval($uid)
1188                         );
1189
1190                 if(! count($r))
1191                         return(false);
1192
1193                 $contact_id  = $r[0]['id'];
1194
1195                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
1196                         intval($uid)
1197                 );
1198
1199                 if($g && intval($g[0]['def_gid'])) {
1200                         require_once('include/group.php');
1201                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
1202                 }
1203
1204                 require_once("Photo.php");
1205
1206                 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
1207
1208                 q("UPDATE `contact` SET `photo` = '%s',
1209                                         `thumb` = '%s',
1210                                         `micro` = '%s',
1211                                         `name-date` = '%s',
1212                                         `uri-date` = '%s',
1213                                         `avatar-date` = '%s'
1214                                 WHERE `id` = %d",
1215                         dbesc($photos[0]),
1216                         dbesc($photos[1]),
1217                         dbesc($photos[2]),
1218                         dbesc(datetime_convert()),
1219                         dbesc(datetime_convert()),
1220                         dbesc(datetime_convert()),
1221                         intval($contact_id)
1222                 );
1223
1224         } else {
1225                 // update profile photos once every two weeks as we have no notification of when they change.
1226
1227                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
1228                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
1229
1230                 // check that we have all the photos, this has been known to fail on occasion
1231
1232                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
1233
1234                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
1235
1236                         require_once("Photo.php");
1237
1238                         $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
1239
1240                         q("UPDATE `contact` SET `photo` = '%s',
1241                                                 `thumb` = '%s',
1242                                                 `micro` = '%s',
1243                                                 `name-date` = '%s',
1244                                                 `uri-date` = '%s',
1245                                                 `avatar-date` = '%s',
1246                                                 `url` = '%s',
1247                                                 `nurl` = '%s',
1248                                                 `addr` = '%s',
1249                                                 `name` = '%s',
1250                                                 `nick` = '%s'
1251                                         WHERE `id` = %d",
1252                                 dbesc($photos[0]),
1253                                 dbesc($photos[1]),
1254                                 dbesc($photos[2]),
1255                                 dbesc(datetime_convert()),
1256                                 dbesc(datetime_convert()),
1257                                 dbesc(datetime_convert()),
1258                                 dbesc("https://twitter.com/".$contact->screen_name),
1259                                 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1260                                 dbesc($contact->screen_name."@twitter.com"),
1261                                 dbesc($contact->name),
1262                                 dbesc($contact->screen_name),
1263                                 intval($r[0]['id'])
1264                         );
1265                 }
1266         }
1267
1268         return($r[0]["id"]);
1269 }
1270
1271 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
1272         $ckey    = get_config('twitter', 'consumerkey');
1273         $csecret = get_config('twitter', 'consumersecret');
1274         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1275         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1276
1277         require_once("addon/twitter/codebird.php");
1278
1279         $cb = \Codebird\Codebird::getInstance();
1280         $cb->setConsumerKey($ckey, $csecret);
1281         $cb->setToken($otoken, $osecret);
1282
1283         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1284                 intval($uid));
1285
1286         if(count($r)) {
1287                 $self = $r[0];
1288         } else
1289                 return;
1290
1291         $parameters = array();
1292
1293         if ($screen_name != "")
1294                 $parameters["screen_name"] = $screen_name;
1295
1296         if ($user_id != "")
1297                 $parameters["user_id"] = $user_id;
1298
1299         // Fetching user data
1300         $user = $cb->users_show($parameters);
1301
1302         if (!is_object($user))
1303                 return;
1304
1305         $contact_id = twitter_fetch_contact($uid, $user, true);
1306
1307         return $contact_id;
1308 }
1309
1310 function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontincludemedia) {
1311         require_once("include/oembed.php");
1312         require_once("include/network.php");
1313
1314         $tags = "";
1315
1316         if (isset($item->entities->urls)) {
1317                 $type = "";
1318                 $footerurl = "";
1319                 $footerlink = "";
1320                 $footer = "";
1321
1322                 foreach ($item->entities->urls AS $url) {
1323                         if ($url->url AND $url->expanded_url AND $url->display_url) {
1324
1325                                 $expanded_url = original_url($url->expanded_url);
1326
1327                                 $oembed_data = oembed_fetch_url($expanded_url);
1328
1329                                 // Quickfix: Workaround for URL with "[" and "]" in it
1330                                 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1331                                         $expanded_url = $url->url;
1332
1333                                 if ($type == "")
1334                                         $type = $oembed_data->type;
1335
1336                                 if ($oembed_data->type == "video") {
1337                                         $body = str_replace($url->url,
1338                                                         "[video]".$expanded_url."[/video]", $body);
1339                                         $dontincludemedia = true;
1340                                 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1341                                         $body = str_replace($url->url,
1342                                                         "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1343                                                         $body);
1344                                         $dontincludemedia = true;
1345                                 } elseif ($oembed_data->type != "link")
1346                                         $body = str_replace($url->url,
1347                                                         "[url=".$expanded_url."]".$expanded_url."[/url]",
1348                                                         $body);
1349                                 else {
1350                                         $img_str = fetch_url($expanded_url, true, $redirects, 4);
1351
1352                                         $tempfile = tempnam(get_config("system","temppath"), "cache");
1353                                         file_put_contents($tempfile, $img_str);
1354                                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1355                                         unlink($tempfile);
1356
1357                                         if (substr($mime, 0, 6) == "image/") {
1358                                                 $type = "photo";
1359                                                 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1360                                                 $dontincludemedia = true;
1361                                         } else {
1362                                                 $type = $oembed_data->type;
1363                                                 $footerurl = $expanded_url;
1364                                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1365
1366                                                 $body = str_replace($url->url, $footerlink, $body);
1367                                         }
1368                                 }
1369                         }
1370                 }
1371
1372                 if ($footerurl != "")
1373                         $footer = add_page_info($footerurl);
1374
1375                 if (($footerlink != "") AND (trim($footer) != "")) {
1376                         $removedlink = trim(str_replace($footerlink, "", $body));
1377
1378                         if (strstr($body, $removedlink))
1379                                 $body = $removedlink;
1380
1381                         $body .= $footer;
1382                 }
1383
1384                 if ($no_tags)
1385                         return(array("body" => $body, "tags" => ""));
1386
1387                 $tags_arr = array();
1388
1389                 foreach ($item->entities->hashtags AS $hashtag) {
1390                         $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1391                         $tags_arr["#".$hashtag->text] = $url;
1392                         $body = str_replace("#".$hashtag->text, $url, $body);
1393                 }
1394
1395                 foreach ($item->entities->user_mentions AS $mention) {
1396                         $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1397                         $tags_arr["@".$mention->screen_name] = $url;
1398                         $body = str_replace("@".$mention->screen_name, $url, $body);
1399                 }
1400
1401                 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1402                 $tags = get_tags($body);
1403
1404                 if(count($tags)) {
1405                         foreach($tags as $tag) {
1406                                 if (strstr(trim($tag), " "))
1407                                         continue;
1408
1409                                 if(strpos($tag,'#') === 0) {
1410                                         if(strpos($tag,'[url='))
1411                                                 continue;
1412
1413                                         // don't link tags that are already embedded in links
1414
1415                                         if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1416                                                 continue;
1417                                         if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1418                                                 continue;
1419
1420                                         $basetag = str_replace('_',' ',substr($tag,1));
1421                                         $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1422                                         $body = str_replace($tag,$url,$body);
1423                                         $tags_arr["#".$basetag] = $url;
1424                                         continue;
1425                                 } elseif(strpos($tag,'@') === 0) {
1426                                         if(strpos($tag,'[url='))
1427                                                 continue;
1428
1429                                         $basetag = substr($tag,1);
1430                                         $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1431                                         $body = str_replace($tag,$url,$body);
1432                                         $tags_arr["@".$basetag] = $url;
1433                                 }
1434                         }
1435                 }
1436
1437
1438                 $tags = implode($tags_arr, ",");
1439
1440         }
1441         return(array("body" => $body, "tags" => $tags));
1442 }
1443
1444 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1445
1446         $has_picture = false;
1447
1448         $postarray = array();
1449         $postarray['network'] = NETWORK_TWITTER;
1450         $postarray['gravity'] = 0;
1451         $postarray['uid'] = $uid;
1452         $postarray['wall'] = 0;
1453         $postarray['uri'] = "twitter::".$post->id_str;
1454
1455         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1456                         dbesc($postarray['uri']),
1457                         intval($uid)
1458                 );
1459
1460         if (count($r))
1461                 return(array());
1462
1463         $contactid = 0;
1464
1465         if ($post->in_reply_to_status_id_str != "") {
1466
1467                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1468
1469                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1470                                 dbesc($parent),
1471                                 intval($uid)
1472                         );
1473                 if (count($r)) {
1474                         $postarray['thr-parent'] = $r[0]["uri"];
1475                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1476                 } else {
1477                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1478                                         dbesc($parent),
1479                                         intval($uid)
1480                                 );
1481                         if (count($r)) {
1482                                 $postarray['thr-parent'] = $r[0]['uri'];
1483                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1484                         } else {
1485                                 $postarray['thr-parent'] = $postarray['uri'];
1486                                 $postarray['parent-uri'] = $postarray['uri'];
1487                         }
1488                 }
1489
1490                 // Is it me?
1491                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1492
1493                 if ($post->user->id_str == $own_id) {
1494                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1495                                 intval($uid));
1496
1497                         if(count($r)) {
1498                                 $contactid = $r[0]["id"];
1499
1500                                 $postarray['owner-name'] =  $r[0]["name"];
1501                                 $postarray['owner-link'] = $r[0]["url"];
1502                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1503                         } else
1504                                 return(array());
1505                 }
1506         } else
1507                 $postarray['parent-uri'] = $postarray['uri'];
1508
1509         if ($contactid == 0) {
1510                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1511
1512                 $postarray['owner-name'] = $post->user->name;
1513                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1514                 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1515         }
1516
1517         if(($contactid == 0) AND !$only_existing_contact)
1518                 $contactid = $self['id'];
1519         elseif ($contactid <= 0)
1520                 return(array());
1521
1522         $postarray['contact-id'] = $contactid;
1523
1524         $postarray['verb'] = ACTIVITY_POST;
1525         $postarray['author-name'] = $postarray['owner-name'];
1526         $postarray['author-link'] = $postarray['owner-link'];
1527         $postarray['author-avatar'] = $postarray['owner-avatar'];
1528         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1529         $postarray['app'] = strip_tags($post->source);
1530
1531         if ($post->user->protected) {
1532                 $postarray['private'] = 1;
1533                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1534         }
1535
1536         $postarray['body'] = $post->text;
1537
1538         // media
1539         if (is_array($post->entities->media)) {
1540                 foreach($post->entities->media AS $media) {
1541                         switch($media->type) {
1542                                 case 'photo':
1543                                         $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1544                                         $has_picture = true;
1545                                         break;
1546                                 default:
1547                                         $postarray['body'] .= print_r($media, true);
1548                         }
1549                 }
1550         }
1551
1552         $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $has_picture);
1553         $postarray['body'] = $converted["body"];
1554         $postarray['tag'] = $converted["tags"];
1555
1556         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1557         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1558
1559         if (is_string($post->place->name))
1560                 $postarray["location"] = $post->place->name;
1561
1562         if (is_string($post->place->full_name))
1563                 $postarray["location"] = $post->place->full_name;
1564
1565         if (is_array($post->geo->coordinates))
1566                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1567
1568         if (is_array($post->coordinates->coordinates))
1569                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1570
1571         if (is_object($post->retweeted_status)) {
1572
1573                 $postarray['body'] = $post->retweeted_status->text;
1574
1575                 // media
1576                 if (is_array($post->retweeted_status->entities->media)) {
1577                         foreach($post->retweeted_status->entities->media AS $media) {
1578                                 switch($media->type) {
1579                                         case 'photo':
1580                                                 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1581                                                 $has_picture = true;
1582                                                 break;
1583                                         default:
1584                                                 $postarray['body'] .= print_r($media, true);
1585                                 }
1586                         }
1587                 }
1588
1589                 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $has_picture);
1590                 $postarray['body'] = $converted["body"];
1591                 $postarray['tag'] = $converted["tags"];
1592
1593                 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1594
1595                 // Deactivated at the moment, since there are problems with answers to retweets
1596                 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1597                         $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1598                                 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1599                                 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1600                                 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1601                                 $postarray['body'];
1602                         $postarray['body'] .= "[/share]";
1603                 } else {
1604                         // Let retweets look like wall-to-wall posts
1605                         $postarray['author-name'] = $post->retweeted_status->user->name;
1606                         $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1607                         $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1608                         //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1609                         //      $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1610                         //      $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1611                         //}
1612                 }
1613
1614         }
1615         return($postarray);
1616 }
1617
1618 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1619
1620         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1621                         intval($uid)
1622                 );
1623
1624         if(!count($user))
1625                 return;
1626
1627         // Is it me?
1628         if (link_compare($user[0]["url"], $postarray['author-link']))
1629                 return;
1630
1631         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1632                         intval($uid),
1633                         dbesc("twitter::".$own_id)
1634                 );
1635
1636         if(!count($own_user))
1637                 return;
1638
1639         // Is it me from twitter?
1640         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1641                 return;
1642
1643         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1644                         dbesc($postarray['parent-uri']),
1645                         intval($uid)
1646                         );
1647
1648         if(count($myconv)) {
1649
1650                 foreach($myconv as $conv) {
1651                         // now if we find a match, it means we're in this conversation
1652
1653                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1654                                 continue;
1655
1656                         require_once('include/enotify.php');
1657
1658                         $conv_parent = $conv['parent'];
1659
1660                         notification(array(
1661                                 'type'         => NOTIFY_COMMENT,
1662                                 'notify_flags' => $user[0]['notify-flags'],
1663                                 'language'     => $user[0]['language'],
1664                                 'to_name'      => $user[0]['username'],
1665                                 'to_email'     => $user[0]['email'],
1666                                 'uid'          => $user[0]['uid'],
1667                                 'item'         => $postarray,
1668                                 'link'             => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1669                                 'source_name'  => $postarray['author-name'],
1670                                 'source_link'  => $postarray['author-link'],
1671                                 'source_photo' => $postarray['author-avatar'],
1672                                 'verb'         => ACTIVITY_POST,
1673                                 'otype'        => 'item',
1674                                 'parent'       => $conv_parent,
1675                         ));
1676
1677                         // only send one notification
1678                         break;
1679                 }
1680         }
1681 }
1682
1683 function twitter_fetchhometimeline($a, $uid) {
1684         $ckey    = get_config('twitter', 'consumerkey');
1685         $csecret = get_config('twitter', 'consumersecret');
1686         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1687         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1688         $create_user = get_pconfig($uid, 'twitter', 'create_user');
1689
1690         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1691
1692         require_once('library/twitteroauth.php');
1693         require_once('include/items.php');
1694
1695         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1696
1697         $own_contact = twitter_fetch_own_contact($a, $uid);
1698
1699         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1700                 intval($own_contact),
1701                 intval($uid));
1702
1703         if(count($r)) {
1704                 $own_id = $r[0]["nick"];
1705         } else {
1706                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1707                 return;
1708         }
1709
1710         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1711                 intval($uid));
1712
1713         if(count($r)) {
1714                 $self = $r[0];
1715         } else {
1716                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1717                 return;
1718         }
1719
1720         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1721                 intval($uid));
1722         if(!count($u)) {
1723                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1724                 return;
1725         }
1726
1727         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1728         //$parameters["count"] = 200;
1729
1730
1731         // Fetching timeline
1732         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1733
1734         $first_time = ($lastid == "");
1735
1736         if ($lastid <> "")
1737                 $parameters["since_id"] = $lastid;
1738
1739         $items = $connection->get('statuses/home_timeline', $parameters);
1740
1741         if (!is_array($items)) {
1742                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1743                 return;
1744         }
1745
1746         $posts = array_reverse($items);
1747
1748         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1749
1750         if (count($posts)) {
1751                 foreach ($posts as $post) {
1752                         if ($post->id_str > $lastid)
1753                                 $lastid = $post->id_str;
1754
1755                         if ($first_time)
1756                                 continue;
1757
1758                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1759
1760                         if (trim($postarray['body']) == "")
1761                                 continue;
1762
1763                         $item = item_store($postarray);
1764
1765                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1766
1767                         if ($item != 0)
1768                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1769
1770                 }
1771         }
1772         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1773
1774         // Fetching mentions
1775         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1776
1777         $first_time = ($lastid == "");
1778
1779         if ($lastid <> "")
1780                 $parameters["since_id"] = $lastid;
1781
1782         $items = $connection->get('statuses/mentions_timeline', $parameters);
1783
1784         if (!is_array($items)) {
1785                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1786                 return;
1787         }
1788
1789         $posts = array_reverse($items);
1790
1791         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1792
1793         if (count($posts)) {
1794                 foreach ($posts as $post) {
1795                         if ($post->id_str > $lastid)
1796                                 $lastid = $post->id_str;
1797
1798                         if ($first_time)
1799                                 continue;
1800
1801                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1802
1803                         if (trim($postarray['body']) == "")
1804                                 continue;
1805
1806                         $item = item_store($postarray);
1807
1808                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1809
1810                         if ($item == 0) {
1811                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1812                                         dbesc($postarray['uri']),
1813                                         intval($uid)
1814                                 );
1815                                 if (count($r))
1816                                         $item = $r[0]['id'];
1817                         }
1818
1819                         if ($item != 0) {
1820                                 require_once('include/enotify.php');
1821                                 notification(array(
1822                                         'type'         => NOTIFY_TAGSELF,
1823                                         'notify_flags' => $u[0]['notify-flags'],
1824                                         'language'     => $u[0]['language'],
1825                                         'to_name'      => $u[0]['username'],
1826                                         'to_email'     => $u[0]['email'],
1827                                         'uid'          => $u[0]['uid'],
1828                                         'item'         => $postarray,
1829                                         'link'         => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1830                                         'source_name'  => $postarray['author-name'],
1831                                         'source_link'  => $postarray['author-link'],
1832                                         'source_photo' => $postarray['author-avatar'],
1833                                         'verb'         => ACTIVITY_TAG,
1834                                         'otype'        => 'item'
1835                                 ));
1836                         }
1837                 }
1838         }
1839
1840         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1841 }
1842
1843 function twitter_fetch_own_contact($a, $uid) {
1844         $ckey    = get_config('twitter', 'consumerkey');
1845         $csecret = get_config('twitter', 'consumersecret');
1846         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1847         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1848
1849         $own_id = get_pconfig($uid, 'twitter', 'own_id');
1850
1851         $contact_id = 0;
1852
1853         if ($own_id == "") {
1854                 require_once('library/twitteroauth.php');
1855
1856                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1857
1858                 // Fetching user data
1859                 $user = $connection->get('account/verify_credentials');
1860
1861                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1862
1863                 $contact_id = twitter_fetch_contact($uid, $user, true);
1864
1865         } else {
1866                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1867                         intval($uid), dbesc("twitter::".$own_id));
1868                 if(count($r))
1869                         $contact_id = $r[0]["id"];
1870                 else
1871                         del_pconfig($uid, 'twitter', 'own_id');
1872
1873         }
1874
1875         return($contact_id);
1876 }
1877
1878 function twitter_is_retweet($a, $uid, $body) {
1879         $body = trim($body);
1880
1881         // Skip if it isn't a pure repeated messages
1882         // Does it start with a share?
1883         if (strpos($body, "[share") > 0)
1884                 return(false);
1885
1886         // Does it end with a share?
1887         if (strlen($body) > (strrpos($body, "[/share]") + 8))
1888                 return(false);
1889
1890         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1891         // Skip if there is no shared message in there
1892         if ($body == $attributes)
1893                 return(false);
1894
1895         $link = "";
1896         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1897         if ($matches[1] != "")
1898                 $link = $matches[1];
1899
1900         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1901         if ($matches[1] != "")
1902                 $link = $matches[1];
1903
1904         $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1905         if ($id == $link)
1906                 return(false);
1907
1908         logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1909
1910         $ckey    = get_config('twitter', 'consumerkey');
1911         $csecret = get_config('twitter', 'consumersecret');
1912         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1913         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1914
1915         require_once('library/twitteroauth.php');
1916         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1917
1918         $result = $connection->post('statuses/retweet/'.$id);
1919
1920         logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1921
1922         return(!isset($result->errors));
1923 }
1924
1925 ?>