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