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