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