]> git.mxchange.org Git - friendica-addons.git/blob - twitter/twitter.php
Re-enabling a once stopped mirroring now doesn't flood the timeline with old posts...
[friendica-addons.git] / twitter / twitter.php
1 <?php
2 /**
3  * Name: Twitter Connector
4  * Description: Relay public postings to a connected Twitter account
5  * Version: 1.0.4
6  * Author: Tobias Diekershoff <https://f.diekershoff.de/profile/tobias>
7  * Author: Michael Vogel <https://pirati.ca/profile/heluecht>
8  *
9  * Copyright (c) 2011-2013 Tobias Diekershoff, Michael Vogel
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *    * Redistributions of source code must retain the above copyright notice,
15  *     this list of conditions and the following disclaimer.
16  *    * Redistributions in binary form must reproduce the above
17  *    * copyright notice, this list of conditions and the following disclaimer in
18  *      the documentation and/or other materials provided with the distribution.
19  *    * Neither the name of the <organization> nor the names of its contributors
20  *      may be used to endorse or promote products derived from this software
21  *      without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
31  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35  
36 /*   Twitter Plugin for Friendica
37  *
38  *   Author: Tobias Diekershoff
39  *           tobias.diekershoff@gmx.net
40  *
41  *   License:3-clause BSD license
42  *
43  *   Configuration:
44  *     To use this plugin you need a OAuth Consumer key pair (key & secret)
45  *     you can get it from Twitter at https://twitter.com/apps
46  *
47  *     Register your Friendica site as "Client" application with "Read & Write" access
48  *     we do not need "Twitter as login". When you've registered the app you get the
49  *     OAuth Consumer key and secret pair for your application/site.
50  *
51  *     Add this key pair to your global .htconfig.php or use the admin panel.
52  *
53  *     $a->config['twitter']['consumerkey'] = 'your consumer_key here';
54  *     $a->config['twitter']['consumersecret'] = 'your consumer_secret here';
55  *
56  *     To activate the plugin itself add it to the $a->config['system']['addon']
57  *     setting. After this, your user can configure their Twitter account settings
58  *     from "Settings -> Plugin Settings".
59  *
60  *     Requirements: PHP5, curl [Slinky library]
61  */
62
63 define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes
64
65 function twitter_install() {
66         //  we need some hooks, for the configuration and for sending tweets
67         register_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); 
68         register_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
69         register_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
70         register_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
71         register_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
72         register_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
73         register_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
74         register_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
75         logger("installed twitter");
76 }
77
78
79 function twitter_uninstall() {
80         unregister_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); 
81         unregister_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
82         unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
83         unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
84         unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
85         unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
86         unregister_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
87         unregister_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
88
89         // old setting - remove only
90         unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook');
91         unregister_hook('plugin_settings', 'addon/twitter/twitter.php', 'twitter_settings'); 
92         unregister_hook('plugin_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
93
94 }
95
96 function twitter_follow($a, &$contact) {
97
98         logger("twitter_follow: Check if contact is twitter contact. ".$contact["url"], LOGGER_DEBUG);
99
100         if (!strstr($contact["url"], "://twitter.com") AND !strstr($contact["url"], "@twitter.com"))
101                 return;
102
103         // contact seems to be a twitter contact, so continue
104         $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $contact["url"]);
105         $nickname = str_replace("@twitter.com", "", $nickname);
106
107         $uid = $a->user["uid"];
108
109         $ckey    = get_config('twitter', 'consumerkey');
110         $csecret = get_config('twitter', 'consumersecret');
111         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
112         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
113
114         require_once("addon/twitter/codebird.php");
115
116         $cb = \Codebird\Codebird::getInstance();
117         $cb->setConsumerKey($ckey, $csecret);
118         $cb->setToken($otoken, $osecret);
119
120         $parameters = array();
121         $parameters["screen_name"] = $nickname;
122
123         $user = $cb->friendships_create($parameters);
124
125         twitter_fetchuser($a, $uid, $nickname);
126
127         $r = q("SELECT name,nick,url,addr,batch,notify,poll,request,confirm,poco,photo,priority,network,alias,pubkey
128                 FROM `contact` WHERE `uid` = %d AND `nick` = '%s'",
129                                 intval($uid),
130                                 dbesc($nickname));
131         if (count($r))
132                 $contact["contact"] = $r[0];
133 }
134
135 function twitter_jot_nets(&$a,&$b) {
136         if(! local_user())
137                 return;
138
139         $tw_post = get_pconfig(local_user(),'twitter','post');
140         if(intval($tw_post) == 1) {
141                 $tw_defpost = get_pconfig(local_user(),'twitter','post_by_default');
142                 $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : '');
143                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="twitter_enable"' . $selected . ' value="1" /> ' 
144                         . t('Post to Twitter') . '</div>';
145         }
146 }
147
148 function twitter_settings_post ($a,$post) {
149         if(! local_user())
150                 return;
151         // don't check twitter settings if twitter submit button is not clicked
152         if (!x($_POST,'twitter-submit'))
153                 return;
154
155         if (isset($_POST['twitter-disconnect'])) {
156                 /***
157                  * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
158                  * from the user configuration
159                  */
160                 del_pconfig(local_user(), 'twitter', 'consumerkey');
161                 del_pconfig(local_user(), 'twitter', 'consumersecret');
162                 del_pconfig(local_user(), 'twitter', 'oauthtoken');
163                 del_pconfig(local_user(), 'twitter', 'oauthsecret');
164                 del_pconfig(local_user(), 'twitter', 'post');
165                 del_pconfig(local_user(), 'twitter', 'post_by_default');
166                 del_pconfig(local_user(), 'twitter', 'lastid');
167                 del_pconfig(local_user(), 'twitter', 'mirror_posts');
168                 del_pconfig(local_user(), 'twitter', 'import');
169                 del_pconfig(local_user(), 'twitter', 'create_user');
170                 del_pconfig(local_user(), 'twitter', 'own_id');
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                 //  reload the Addon Settings page, if we don't do it see Bug #42
188                 goaway($a->get_baseurl().'/settings/connectors');
189         } else {
190                 //  if no PIN is supplied in the POST variables, the user has changed the setting
191                 //  to post a tweet for every new __public__ posting to the wall
192                 set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable']));
193                 set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default']));
194                 set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
195                 set_pconfig(local_user(), 'twitter', 'import', intval($_POST['twitter-import']));
196                 set_pconfig(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user']));
197
198                 if (!intval($_POST['twitter-mirror']))
199                         del_pconfig(local_user(),'twitter','lastid');
200
201                 info(t('Twitter settings updated.') . EOL);
202         }}
203 }
204 function twitter_settings(&$a,&$s) {
205         if(! local_user())
206                 return;
207         $a->page['htmlhead'] .= '<link rel="stylesheet"  type="text/css" href="' . $a->get_baseurl() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
208         /***
209          * 1) Check that we have global consumer key & secret
210          * 2) If no OAuthtoken & stuff is present, generate button to get some
211          * 3) Checkbox for "Send public notices (140 chars only)
212          */
213         $ckey    = get_config('twitter', 'consumerkey' );
214         $csecret = get_config('twitter', 'consumersecret' );
215         $otoken  = get_pconfig(local_user(), 'twitter', 'oauthtoken'  );
216         $osecret = get_pconfig(local_user(), 'twitter', 'oauthsecret' );
217         $enabled = get_pconfig(local_user(), 'twitter', 'post');
218         $checked = (($enabled) ? ' checked="checked" ' : '');
219         $defenabled = get_pconfig(local_user(),'twitter','post_by_default');
220         $defchecked = (($defenabled) ? ' checked="checked" ' : '');
221         $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts');
222         $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : '');
223         $importenabled = get_pconfig(local_user(),'twitter','import');
224         $importchecked = (($importenabled) ? ' checked="checked" ' : '');
225         $create_userenabled = get_pconfig(local_user(),'twitter','create_user');
226         $create_userchecked = (($create_userenabled) ? ' checked="checked" ' : '');
227
228         $css = (($enabled) ? '' : '-disabled');
229
230         $s .= '<span id="settings_twitter_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
231         $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
232         $s .= '</span>';
233         $s .= '<div id="settings_twitter_expanded" class="settings-block" style="display: none;">';
234         $s .= '<span class="fakelink" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
235         $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
236         $s .= '</span>';
237
238         if ( (!$ckey) && (!$csecret) ) {
239                 /***
240                  * no global consumer keys
241                  * display warning and skip personal config
242                  */
243                 $s .= '<p>'. t('No consumer key pair for Twitter found. Please contact your site administrator.') .'</p>';
244         } else {
245                 /***
246                  * ok we have a consumer key pair now look into the OAuth stuff
247                  */
248                 if ( (!$otoken) && (!$osecret) ) {
249                         /***
250                          * the user has not yet connected the account to twitter...
251                          * get a temporary OAuth key/secret pair and display a button with
252                          * which the user can request a PIN to connect the account to a
253                          * account at Twitter.
254                          */
255                         require_once('library/twitteroauth.php');
256                         $connection = new TwitterOAuth($ckey, $csecret);
257                         $request_token = $connection->getRequestToken();
258                         $token = $request_token['oauth_token'];
259                         /***
260                          *  make some nice form
261                          */
262                         $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>';
263                         $s .= '<a href="'.$connection->getAuthorizeURL($token).'" target="_twitter"><img src="addon/twitter/lighter.png" alt="'.t('Log in with Twitter').'"></a>';
264                         $s .= '<div id="twitter-pin-wrapper">';
265                         $s .= '<label id="twitter-pin-label" for="twitter-pin">'. t('Copy the PIN from Twitter here') .'</label>';
266                         $s .= '<input id="twitter-pin" type="text" name="twitter-pin" />';
267                         $s .= '<input id="twitter-token" type="hidden" name="twitter-token" value="'.$token.'" />';
268                         $s .= '<input id="twitter-token2" type="hidden" name="twitter-token2" value="'.$request_token['oauth_token_secret'].'" />';
269             $s .= '</div><div class="clear"></div>';
270             $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
271                 } else {
272                         /***
273                          *  we have an OAuth key / secret pair for the user
274                          *  so let's give a chance to disable the postings to Twitter
275                          */
276                         require_once('library/twitteroauth.php');
277                         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
278                         $details = $connection->get('account/verify_credentials');
279                         $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>';
280                         $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>';
281                         if ($a->user['hidewall']) {
282                             $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>';
283                         }
284                         $s .= '<div id="twitter-enable-wrapper">';
285                         $s .= '<label id="twitter-enable-label" for="twitter-checkbox">'. t('Allow posting to Twitter'). '</label>';
286                         $s .= '<input id="twitter-checkbox" type="checkbox" name="twitter-enable" value="1" ' . $checked . '/>';
287                         $s .= '<div class="clear"></div>';
288                         $s .= '<label id="twitter-default-label" for="twitter-default">'. t('Send public postings to Twitter by default') .'</label>';
289                         $s .= '<input id="twitter-default" type="checkbox" name="twitter-default" value="1" ' . $defchecked . '/>';
290                         $s .= '<div class="clear"></div>';
291
292                         $s .= '<label id="twitter-mirror-label" for="twitter-mirror">'.t('Mirror all posts from twitter that are no replies').'</label>';
293                         $s .= '<input id="twitter-mirror" type="checkbox" name="twitter-mirror" value="1" '. $mirrorchecked . '/>';
294                         $s .= '<div class="clear"></div>';
295                         $s .= '</div>';
296
297                         $s .= '<label id="twitter-import-label" for="twitter-import">'.t('Import the remote timeline').'</label>';
298                         $s .= '<input id="twitter-import" type="checkbox" name="twitter-import" value="1" '. $importchecked . '/>';
299                         $s .= '<div class="clear"></div>';
300
301                         $s .= '<label id="twitter-create_user-label" for="twitter-create_user">'.t('Automatically create contacts').'</label>';
302                         $s .= '<input id="twitter-create_user" type="checkbox" name="twitter-create_user" value="1" '. $create_userchecked . '/>';
303                         $s .= '<div class="clear"></div>';
304
305                         $s .= '<div id="twitter-disconnect-wrapper">';
306                         $s .= '<label id="twitter-disconnect-label" for="twitter-disconnect">'. t('Clear OAuth configuration') .'</label>';
307                         $s .= '<input id="twitter-disconnect" type="checkbox" name="twitter-disconnect" value="1" />';
308                         $s .= '</div><div class="clear"></div>';
309                         $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>'; 
310                 }
311         }
312         $s .= '</div><div class="clear"></div>';
313 }
314
315
316 function twitter_post_local(&$a,&$b) {
317
318         if($b['edit'])
319                 return;
320
321         if((local_user()) && (local_user() == $b['uid']) && (! $b['private']) && (! $b['parent']) ) {
322
323                 $twitter_post = intval(get_pconfig(local_user(),'twitter','post'));
324                 $twitter_enable = (($twitter_post && x($_REQUEST,'twitter_enable')) ? intval($_REQUEST['twitter_enable']) : 0);
325
326                 // if API is used, default to the chosen settings
327                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'twitter','post_by_default')))
328                         $twitter_enable = 1;
329
330         if(! $twitter_enable)
331             return;
332
333         if(strlen($b['postopts']))
334             $b['postopts'] .= ',';
335         $b['postopts'] .= 'twitter';
336         }
337 }
338
339 function twitter_action($a, $uid, $pid, $action) {
340
341         $ckey    = get_config('twitter', 'consumerkey');
342         $csecret = get_config('twitter', 'consumersecret');
343         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
344         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
345
346         require_once("addon/twitter/codebird.php");
347
348         $cb = \Codebird\Codebird::getInstance();
349         $cb->setConsumerKey($ckey, $csecret);
350         $cb->setToken($otoken, $osecret);
351
352         $post = array('id' => $pid);
353
354         logger("twitter_action '".$action."' ID: ".$pid." data: " . print_r($post, true), LOGGER_DATA);
355
356         switch ($action) {
357                 case "delete":
358                         $result = $cb->statuses_destroy($post);
359                         break;
360                 case "like":
361                         $result = $cb->favorites_create($post);
362                         break;
363                 case "unlike":
364                         $result = $cb->favorites_destroy($post);
365                         break;
366         }
367         logger("twitter_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
368 }
369
370 function twitter_post_hook(&$a,&$b) {
371
372         /**
373          * Post to Twitter
374          */
375
376         require_once("include/network.php");
377
378         if (!get_pconfig($b["uid"],'twitter','import')) {
379                 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
380                         return;
381         }
382
383         if($b['parent'] != $b['id']) {
384                 logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
385
386                 // Looking if its a reply to a twitter post
387                 if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) {
388                         logger("twitter_post_hook: no twitter post ".$b["parent"]);
389                         return;
390                 }
391
392                 $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
393                         dbesc($b["thr-parent"]),
394                         intval($b["uid"]));
395
396                 if(!count($r)) {
397                         logger("twitter_post_hook: no parent found ".$b["thr-parent"]);
398                         return;
399                 } else {
400                         $iscomment = true;
401                         $orig_post = $r[0];
402                 }
403
404                 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
405                 $nickname = "@[url=".$orig_post["author-link"]."]".$nickname."[/url]";
406
407                 logger("twitter_post_hook: comparing ".$nickname." with ".$b["body"], LOGGER_DEBUG);
408                 if (strpos($b["body"], $nickname) === false)
409                         $b["body"] = $nickname." ".$b["body"];
410
411                 logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA);
412         } else {
413                 $iscomment = false;
414
415                 if($b['private'] OR !strstr($b['postopts'],'twitter'))
416                         return;
417         }
418
419         if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
420                 twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
421
422         if($b['verb'] == ACTIVITY_LIKE) {
423                 logger("twitter_post_hook: parameter 2 ".substr($b["thr-parent"], 9), LOGGER_DEBUG);
424                 if ($b['deleted'])
425                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
426                 else
427                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like");
428                 return;
429         }
430
431         if($b['deleted'] || ($b['created'] !== $b['edited']))
432                 return;
433
434         // if post comes from twitter don't send it back
435         if($b['app'] == "Twitter")
436                 return;
437
438         logger('twitter post invoked');
439
440
441         load_pconfig($b['uid'], 'twitter');
442
443         $ckey    = get_config('twitter', 'consumerkey');
444         $csecret = get_config('twitter', 'consumersecret');
445         $otoken  = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
446         $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
447
448         if($ckey && $csecret && $otoken && $osecret) {
449                 logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
450
451                 // If it's a repeated message from twitter then do a native retweet and exit
452                 if (twitter_is_retweet($a, $b['uid'], $b['body']))
453                         return;
454
455                 require_once('library/twitteroauth.php');
456                 require_once('include/bbcode.php');
457                 $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
458
459                 $max_char = 140;
460                 require_once("include/plaintext.php");
461                 $msgarr = plaintext($a, $b, $max_char, true);
462                 $msg = $msgarr["text"];
463
464                 if (($msg == "") AND isset($msgarr["title"]))
465                         $msg = shortenmsg($msgarr["title"], $max_char - 50);
466
467                 $image = "";
468
469                 if (isset($msgarr["url"]))
470                         $msg .= "\n".$msgarr["url"];
471                 elseif (isset($msgarr["image"]))
472                         $image = $msgarr["image"];
473
474                 // and now tweet it :-)
475                 if(strlen($msg) and ($image != "")) {
476                         $img_str = fetch_url($image);
477
478                         $tempfile = tempnam(get_config("system","temppath"), "cache");
479                         file_put_contents($tempfile, $img_str);
480
481                         // Twitter had changed something so that the old library doesn't work anymore
482                         // so we are using a new library for twitter
483                         // To-Do:
484                         // Switching completely to this library with all functions
485                         require_once("addon/twitter/codebird.php");
486
487                         $cb = \Codebird\Codebird::getInstance();
488                         $cb->setConsumerKey($ckey, $csecret);
489                         $cb->setToken($otoken, $osecret);
490
491                         $post = array('status' => $msg, 'media[]' => $tempfile);
492
493                         if ($iscomment)
494                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
495
496                         $result = $cb->statuses_updateWithMedia($post);
497                         unlink($tempfile);
498
499                         logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG);
500                         if ($result->errors OR $result->error) {
501                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
502
503                                 // Workaround: Remove the picture link so that the post can be reposted without it
504                                 $msg .= " ".$image;
505                                 $image = "";
506                         } elseif ($iscomment) {
507                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
508                                 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
509                                         dbesc("twitter::".$result->id_str),
510                                         dbesc($result->text),
511                                         intval($b['id'])
512                                 );
513                         }
514                 }
515
516                 if(strlen($msg) and ($image == "")) {
517                         $url = 'statuses/update';
518                         $post = array('status' => $msg);
519
520                         if ($iscomment)
521                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
522
523                         $result = $tweet->post($url, $post);
524                         logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
525                         if ($result->errors) {
526                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
527
528                                 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
529                                 if (count($r))
530                                         $a->contact = $r[0]["id"];
531
532                                 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
533                                 require_once('include/queue_fn.php');
534                                 add_to_queue($a->contact,NETWORK_TWITTER,$s);
535                                 notice(t('Twitter post failed. Queued for retry.').EOL);
536                         } elseif ($iscomment) {
537                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
538                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
539                                         dbesc("twitter::".$result->id_str),
540                                         intval($b['id'])
541                                 );
542                                 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
543                                 //      dbesc("twitter::".$result->id_str),
544                                 //      dbesc($result->text),
545                                 //      intval($b['id'])
546                                 //);
547                         }
548                 }
549         }
550 }
551
552 function twitter_plugin_admin_post(&$a){
553         $consumerkey    =       ((x($_POST,'consumerkey'))              ? notags(trim($_POST['consumerkey']))   : '');
554         $consumersecret =       ((x($_POST,'consumersecret'))   ? notags(trim($_POST['consumersecret'])): '');
555         $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
556         set_config('twitter','consumerkey',$consumerkey);
557         set_config('twitter','consumersecret',$consumersecret);
558         set_config('twitter','application_name',$applicationname);
559         info( t('Settings updated.'). EOL );
560 }
561 function twitter_plugin_admin(&$a, &$o){
562         $t = get_markup_template( "admin.tpl", "addon/twitter/" );
563
564         $o = replace_macros($t, array(
565                 '$submit' => t('Save Settings'),
566                                                                 // name, label, value, help, [extra values]
567                 '$consumerkey' => array('consumerkey', t('Consumer key'),  get_config('twitter', 'consumerkey' ), ''),
568                 '$consumersecret' => array('consumersecret', t('Consumer secret'),  get_config('twitter', 'consumersecret' ), ''),
569                 '$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'))
570         ));
571 }
572
573 function twitter_cron($a,$b) {
574         $last = get_config('twitter','last_poll');
575
576         $poll_interval = intval(get_config('twitter','poll_interval'));
577         if(! $poll_interval)
578                 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
579
580         if($last) {
581                 $next = $last + ($poll_interval * 60);
582                 if($next > time()) {
583                         logger('twitter: poll intervall not reached');
584                         return;
585                 }
586         }
587         logger('twitter: cron_start');
588
589         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND()");
590         if(count($r)) {
591                 foreach($r as $rr) {
592                         logger('twitter: fetching for user '.$rr['uid']);
593                         twitter_fetchtimeline($a, $rr['uid']);
594                 }
595         }
596
597
598         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
599         if(count($r)) {
600                 foreach($r as $rr) {
601                         logger('twitter: importing timeline from user '.$rr['uid']);
602                         twitter_fetchhometimeline($a, $rr["uid"]);
603
604 /*
605                         // To-Do
606                         // check for new contacts once a day
607                         $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
608                         if($last_contact_check)
609                                 $next_contact_check = $last_contact_check + 86400;
610                         else
611                                 $next_contact_check = 0;
612
613                         if($next_contact_check <= time()) {
614                                 pumpio_getallusers($a, $rr["uid"]);
615                                 set_pconfig($rr['uid'],'pumpio','contact_check',time());
616                         }
617 */
618
619                 }
620         }
621
622         logger('twitter: cron_end');
623
624         set_config('twitter','last_poll', time());
625 }
626
627 function twitter_fetchtimeline($a, $uid) {
628         $ckey    = get_config('twitter', 'consumerkey');
629         $csecret = get_config('twitter', 'consumersecret');
630         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
631         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
632         $lastid  = get_pconfig($uid, 'twitter', 'lastid');
633
634         $application_name  = get_config('twitter', 'application_name');
635
636         if ($application_name == "")
637                 $application_name = $a->get_hostname();
638
639         $has_picture = false;
640
641         require_once('mod/item.php');
642         require_once('include/items.php');
643
644         require_once('library/twitteroauth.php');
645         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
646
647         $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
648
649         $first_time = ($lastid == "");
650
651         if ($lastid <> "")
652                 $parameters["since_id"] = $lastid;
653
654         $items = $connection->get('statuses/user_timeline', $parameters);
655
656         if (!is_array($items))
657                 return;
658
659         $posts = array_reverse($items);
660
661         if (count($posts)) {
662             foreach ($posts as $post) {
663                 if ($post->id_str > $lastid)
664                         $lastid = $post->id_str;
665
666                 if ($first_time)
667                         continue;
668
669                 if (!strpos($post->source, $application_name)) {
670                         $_SESSION["authenticated"] = true;
671                         $_SESSION["uid"] = $uid;
672
673                         unset($_REQUEST);
674                         $_REQUEST["type"] = "wall";
675                         $_REQUEST["api_source"] = true;
676                         $_REQUEST["profile_uid"] = $uid;
677                         $_REQUEST["source"] = "Twitter";
678
679                         //$_REQUEST["date"] = $post->created_at;
680
681                         $_REQUEST["title"] = "";
682
683                         if (is_object($post->retweeted_status)) {
684
685                                 $_REQUEST['body'] = $post->retweeted_status->text;
686
687                                 // media
688                                 if (is_array($post->retweeted_status->entities->media)) {
689                                         foreach($post->retweeted_status->entities->media AS $media) {
690                                                 switch($media->type) {
691                                                         case 'photo':
692                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
693                                                                 $has_picture = true;
694                                                                 break;
695                                                 }
696                                         }
697                                 }
698
699                                 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $has_picture);
700                                 $_REQUEST['body'] = $converted["body"];
701
702                                 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
703                                         "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
704                                         "' avatar='".$post->retweeted_status->user->profile_image_url_https.
705                                         "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
706                                         $_REQUEST['body'];
707                                 $_REQUEST['body'] .= "[/share]";
708                         } else {
709                                 $_REQUEST["body"] = $post->text;
710
711                                 if (is_array($post->entities->media)) {
712                                         foreach($post->entities->media AS $media) {
713                                                 switch($media->type) {
714                                                         case 'photo':
715                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
716                                                                 $has_picture = true;
717                                                                 break;
718                                                 }
719                                         }
720                                 }
721
722                                 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $has_picture);
723                                 $_REQUEST['body'] = $converted["body"];
724                         }
725
726                         if (is_string($post->place->name))
727                                 $_REQUEST["location"] = $post->place->name;
728
729                         if (is_string($post->place->full_name))
730                                 $_REQUEST["location"] = $post->place->full_name;
731
732                         if (is_array($post->geo->coordinates))
733                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
734
735                         if (is_array($post->coordinates->coordinates))
736                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
737
738                         //print_r($_REQUEST);
739                         logger('twitter: posting for user '.$uid);
740
741 //                      require_once('mod/item.php');
742
743                         item_post($a);
744                 }
745             }
746         }
747         set_pconfig($uid, 'twitter', 'lastid', $lastid);
748 }
749
750 function twitter_queue_hook(&$a,&$b) {
751
752         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
753                 dbesc(NETWORK_TWITTER)
754                 );
755         if(! count($qi))
756                 return;
757
758         require_once('include/queue_fn.php');
759
760         foreach($qi as $x) {
761                 if($x['network'] !== NETWORK_TWITTER)
762                         continue;
763
764                 logger('twitter_queue: run');
765
766                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
767                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
768                         intval($x['cid'])
769                 );
770                 if(! count($r))
771                         continue;
772
773                 $user = $r[0];
774
775                 $ckey    = get_config('twitter', 'consumerkey');
776                 $csecret = get_config('twitter', 'consumersecret');
777                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
778                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
779
780                 $success = false;
781
782                 if ($ckey AND $csecret AND $otoken AND $osecret) {
783
784                         logger('twitter_queue: able to post');
785
786                         $z = unserialize($x['content']);
787
788                         require_once("addon/twitter/codebird.php");
789
790                         $cb = \Codebird\Codebird::getInstance();
791                         $cb->setConsumerKey($ckey, $csecret);
792                         $cb->setToken($otoken, $osecret);
793
794                         if ($z['url'] == "statuses/update")
795                                 $result = $cb->statuses_update($z['post']);
796
797                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
798
799                         if ($result->errors)
800                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
801                         else {
802                                 $success = true;
803                                 remove_queue_item($x['id']);
804                         }
805                 } else
806                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
807
808                 if (!$success) {
809                         logger('twitter_queue: delayed');
810                         update_queue_time($x['id']);
811                 }
812         }
813 }
814
815 function twitter_fetch_contact($uid, $contact, $create_user) {
816
817         // Check if the unique contact is existing
818         // To-Do: only update once a while
819          $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
820                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
821
822         if (count($r) == 0)
823                 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
824                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
825                         dbesc($contact->name),
826                         dbesc($contact->screen_name),
827                         dbesc($contact->profile_image_url_https));
828         else
829                 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
830                         dbesc($contact->name),
831                         dbesc($contact->screen_name),
832                         dbesc($contact->profile_image_url_https),
833                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
834
835         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
836                 intval($uid), dbesc("twitter::".$contact->id_str));
837
838         if(!count($r) AND !$create_user)
839                 return(0);
840
841         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
842                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
843                 return(-1);
844         }
845
846         if(!count($r)) {
847                 // create contact record
848                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
849                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
850                                         `writable`, `blocked`, `readonly`, `pending` )
851                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
852                         intval($uid),
853                         dbesc(datetime_convert()),
854                         dbesc("https://twitter.com/".$contact->screen_name),
855                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
856                         dbesc($contact->screen_name."@twitter.com"),
857                         dbesc("twitter::".$contact->id_str),
858                         dbesc(''),
859                         dbesc("twitter::".$contact->id_str),
860                         dbesc($contact->name),
861                         dbesc($contact->screen_name),
862                         dbesc($contact->profile_image_url_https),
863                         dbesc(NETWORK_TWITTER),
864                         intval(CONTACT_IS_FRIEND),
865                         intval(1),
866                         intval(1)
867                 );
868
869                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
870                         dbesc("twitter::".$contact->id_str),
871                         intval($uid)
872                         );
873
874                 if(! count($r))
875                         return(false);
876
877                 $contact_id  = $r[0]['id'];
878
879                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
880                         intval($uid)
881                 );
882
883                 if($g && intval($g[0]['def_gid'])) {
884                         require_once('include/group.php');
885                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
886                 }
887
888                 require_once("Photo.php");
889
890                 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
891
892                 q("UPDATE `contact` SET `photo` = '%s',
893                                         `thumb` = '%s',
894                                         `micro` = '%s',
895                                         `name-date` = '%s',
896                                         `uri-date` = '%s',
897                                         `avatar-date` = '%s'
898                                 WHERE `id` = %d",
899                         dbesc($photos[0]),
900                         dbesc($photos[1]),
901                         dbesc($photos[2]),
902                         dbesc(datetime_convert()),
903                         dbesc(datetime_convert()),
904                         dbesc(datetime_convert()),
905                         intval($contact_id)
906                 );
907
908         } else {
909                 // update profile photos once every two weeks as we have no notification of when they change.
910
911                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
912                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
913
914                 // check that we have all the photos, this has been known to fail on occasion
915
916                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
917
918                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
919
920                         require_once("Photo.php");
921
922                         $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
923
924                         q("UPDATE `contact` SET `photo` = '%s',
925                                                 `thumb` = '%s',
926                                                 `micro` = '%s',
927                                                 `name-date` = '%s',
928                                                 `uri-date` = '%s',
929                                                 `avatar-date` = '%s',
930                                                 `url` = '%s',
931                                                 `nurl` = '%s',
932                                                 `addr` = '%s',
933                                                 `name` = '%s',
934                                                 `nick` = '%s'
935                                         WHERE `id` = %d",
936                                 dbesc($photos[0]),
937                                 dbesc($photos[1]),
938                                 dbesc($photos[2]),
939                                 dbesc(datetime_convert()),
940                                 dbesc(datetime_convert()),
941                                 dbesc(datetime_convert()),
942                                 dbesc("https://twitter.com/".$contact->screen_name),
943                                 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
944                                 dbesc($contact->screen_name."@twitter.com"),
945                                 dbesc($contact->name),
946                                 dbesc($contact->screen_name),
947                                 intval($r[0]['id'])
948                         );
949                 }
950         }
951
952         return($r[0]["id"]);
953 }
954
955 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
956         $ckey    = get_config('twitter', 'consumerkey');
957         $csecret = get_config('twitter', 'consumersecret');
958         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
959         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
960
961         require_once("addon/twitter/codebird.php");
962
963         $cb = \Codebird\Codebird::getInstance();
964         $cb->setConsumerKey($ckey, $csecret);
965         $cb->setToken($otoken, $osecret);
966
967         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
968                 intval($uid));
969
970         if(count($r)) {
971                 $self = $r[0];
972         } else
973                 return;
974
975         $parameters = array();
976
977         if ($screen_name != "")
978                 $parameters["screen_name"] = $screen_name;
979
980         if ($user_id != "")
981                 $parameters["user_id"] = $user_id;
982
983         // Fetching user data
984         $user = $cb->users_show($parameters);
985
986         if (!is_object($user))
987                 return;
988
989         $contact_id = twitter_fetch_contact($uid, $user, true);
990
991         return $contact_id;
992 }
993
994 function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontincludemedia) {
995         require_once("include/oembed.php");
996         require_once("include/network.php");
997
998         $tags = "";
999
1000         if (isset($item->entities->urls)) {
1001                 $type = "";
1002                 $footerurl = "";
1003                 $footerlink = "";
1004                 $footer = "";
1005
1006                 foreach ($item->entities->urls AS $url) {
1007                         if ($url->url AND $url->expanded_url AND $url->display_url) {
1008
1009                                 $expanded_url = original_url($url->expanded_url);
1010
1011                                 $oembed_data = oembed_fetch_url($expanded_url);
1012
1013                                 // Quickfix: Workaround for URL with "[" and "]" in it
1014                                 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1015                                         $expanded_url = $url->url;
1016
1017                                 if ($type == "")
1018                                         $type = $oembed_data->type;
1019
1020                                 if ($oembed_data->type == "video") {
1021                                         //$body = str_replace($url->url,
1022                                         //              "[video]".$expanded_url."[/video]", $body);
1023                                         //$dontincludemedia = true;
1024                                         $type = $oembed_data->type;
1025                                         $footerurl = $expanded_url;
1026                                         $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1027
1028                                         $body = str_replace($url->url, $footerlink, $body);
1029                                 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1030                                         $body = str_replace($url->url,
1031                                                         "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1032                                                         $body);
1033                                         $dontincludemedia = true;
1034                                 } elseif ($oembed_data->type != "link")
1035                                         $body = str_replace($url->url,
1036                                                         "[url=".$expanded_url."]".$expanded_url."[/url]",
1037                                                         $body);
1038                                 else {
1039                                         $img_str = fetch_url($expanded_url, true, $redirects, 4);
1040
1041                                         $tempfile = tempnam(get_config("system","temppath"), "cache");
1042                                         file_put_contents($tempfile, $img_str);
1043                                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1044                                         unlink($tempfile);
1045
1046                                         if (substr($mime, 0, 6) == "image/") {
1047                                                 $type = "photo";
1048                                                 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1049                                                 $dontincludemedia = true;
1050                                         } else {
1051                                                 $type = $oembed_data->type;
1052                                                 $footerurl = $expanded_url;
1053                                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1054
1055                                                 $body = str_replace($url->url, $footerlink, $body);
1056                                         }
1057                                 }
1058                         }
1059                 }
1060
1061                 if ($footerurl != "")
1062                         $footer = add_page_info($footerurl);
1063
1064                 if (($footerlink != "") AND (trim($footer) != "")) {
1065                         $removedlink = trim(str_replace($footerlink, "", $body));
1066
1067                         if (strstr($body, $removedlink))
1068                                 $body = $removedlink;
1069
1070                         $body .= $footer;
1071                 }
1072
1073                 if ($no_tags)
1074                         return(array("body" => $body, "tags" => ""));
1075
1076                 $tags_arr = array();
1077
1078                 foreach ($item->entities->hashtags AS $hashtag) {
1079                         $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1080                         $tags_arr["#".$hashtag->text] = $url;
1081                         $body = str_replace("#".$hashtag->text, $url, $body);
1082                 }
1083
1084                 foreach ($item->entities->user_mentions AS $mention) {
1085                         $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1086                         $tags_arr["@".$mention->screen_name] = $url;
1087                         $body = str_replace("@".$mention->screen_name, $url, $body);
1088                 }
1089
1090                 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1091                 $tags = get_tags($body);
1092
1093                 if(count($tags)) {
1094                         foreach($tags as $tag) {
1095                                 if (strstr(trim($tag), " "))
1096                                         continue;
1097
1098                                 if(strpos($tag,'#') === 0) {
1099                                         if(strpos($tag,'[url='))
1100                                                 continue;
1101
1102                                         // don't link tags that are already embedded in links
1103
1104                                         if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1105                                                 continue;
1106                                         if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1107                                                 continue;
1108
1109                                         $basetag = str_replace('_',' ',substr($tag,1));
1110                                         $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1111                                         $body = str_replace($tag,$url,$body);
1112                                         $tags_arr["#".$basetag] = $url;
1113                                         continue;
1114                                 } elseif(strpos($tag,'@') === 0) {
1115                                         if(strpos($tag,'[url='))
1116                                                 continue;
1117
1118                                         $basetag = substr($tag,1);
1119                                         $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1120                                         $body = str_replace($tag,$url,$body);
1121                                         $tags_arr["@".$basetag] = $url;
1122                                 }
1123                         }
1124                 }
1125
1126
1127                 $tags = implode($tags_arr, ",");
1128
1129         }
1130         return(array("body" => $body, "tags" => $tags));
1131 }
1132
1133 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1134
1135         $has_picture = false;
1136
1137         $postarray = array();
1138         $postarray['network'] = NETWORK_TWITTER;
1139         $postarray['gravity'] = 0;
1140         $postarray['uid'] = $uid;
1141         $postarray['wall'] = 0;
1142         $postarray['uri'] = "twitter::".$post->id_str;
1143
1144         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1145                         dbesc($postarray['uri']),
1146                         intval($uid)
1147                 );
1148
1149         if (count($r))
1150                 return(array());
1151
1152         $contactid = 0;
1153
1154         if ($post->in_reply_to_status_id_str != "") {
1155
1156                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1157
1158                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1159                                 dbesc($parent),
1160                                 intval($uid)
1161                         );
1162                 if (count($r)) {
1163                         $postarray['thr-parent'] = $r[0]["uri"];
1164                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1165                 } else {
1166                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1167                                         dbesc($parent),
1168                                         intval($uid)
1169                                 );
1170                         if (count($r)) {
1171                                 $postarray['thr-parent'] = $r[0]['uri'];
1172                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1173                         } else {
1174                                 $postarray['thr-parent'] = $postarray['uri'];
1175                                 $postarray['parent-uri'] = $postarray['uri'];
1176                         }
1177                 }
1178
1179                 // Is it me?
1180                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1181
1182                 if ($post->user->id_str == $own_id) {
1183                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1184                                 intval($uid));
1185
1186                         if(count($r)) {
1187                                 $contactid = $r[0]["id"];
1188
1189                                 $postarray['owner-name'] =  $r[0]["name"];
1190                                 $postarray['owner-link'] = $r[0]["url"];
1191                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1192                         } else
1193                                 return(array());
1194                 }
1195         } else
1196                 $postarray['parent-uri'] = $postarray['uri'];
1197
1198         if ($contactid == 0) {
1199                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1200
1201                 $postarray['owner-name'] = $post->user->name;
1202                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1203                 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1204         }
1205
1206         if(($contactid == 0) AND !$only_existing_contact)
1207                 $contactid = $self['id'];
1208         elseif ($contactid <= 0)
1209                 return(array());
1210
1211         $postarray['contact-id'] = $contactid;
1212
1213         $postarray['verb'] = ACTIVITY_POST;
1214         $postarray['author-name'] = $postarray['owner-name'];
1215         $postarray['author-link'] = $postarray['owner-link'];
1216         $postarray['author-avatar'] = $postarray['owner-avatar'];
1217         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1218         $postarray['app'] = strip_tags($post->source);
1219
1220         if ($post->user->protected) {
1221                 $postarray['private'] = 1;
1222                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1223         }
1224
1225         $postarray['body'] = $post->text;
1226
1227         // media
1228         if (is_array($post->entities->media)) {
1229                 foreach($post->entities->media AS $media) {
1230                         switch($media->type) {
1231                                 case 'photo':
1232                                         $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1233                                         $has_picture = true;
1234                                         break;
1235                                 default:
1236                                         $postarray['body'] .= print_r($media, true);
1237                         }
1238                 }
1239         }
1240
1241         $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $has_picture);
1242         $postarray['body'] = $converted["body"];
1243         $postarray['tag'] = $converted["tags"];
1244
1245         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1246         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1247
1248         if (is_string($post->place->name))
1249                 $postarray["location"] = $post->place->name;
1250
1251         if (is_string($post->place->full_name))
1252                 $postarray["location"] = $post->place->full_name;
1253
1254         if (is_array($post->geo->coordinates))
1255                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1256
1257         if (is_array($post->coordinates->coordinates))
1258                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1259
1260         if (is_object($post->retweeted_status)) {
1261
1262                 $postarray['body'] = $post->retweeted_status->text;
1263
1264                 // media
1265                 if (is_array($post->retweeted_status->entities->media)) {
1266                         foreach($post->retweeted_status->entities->media AS $media) {
1267                                 switch($media->type) {
1268                                         case 'photo':
1269                                                 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1270                                                 $has_picture = true;
1271                                                 break;
1272                                         default:
1273                                                 $postarray['body'] .= print_r($media, true);
1274                                 }
1275                         }
1276                 }
1277
1278                 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $has_picture);
1279                 $postarray['body'] = $converted["body"];
1280                 $postarray['tag'] = $converted["tags"];
1281
1282                 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1283
1284                 // Deactivated at the moment, since there are problems with answers to retweets
1285                 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1286                         $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1287                                 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1288                                 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1289                                 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1290                                 $postarray['body'];
1291                         $postarray['body'] .= "[/share]";
1292                 } else {
1293                         // Let retweets look like wall-to-wall posts
1294                         $postarray['author-name'] = $post->retweeted_status->user->name;
1295                         $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1296                         $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1297                         //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1298                         //      $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1299                         //      $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1300                         //}
1301                 }
1302
1303         }
1304         return($postarray);
1305 }
1306
1307 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1308
1309         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1310                         intval($uid)
1311                 );
1312
1313         if(!count($user))
1314                 return;
1315
1316         // Is it me?
1317         if (link_compare($user[0]["url"], $postarray['author-link']))
1318                 return;
1319
1320         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1321                         intval($uid),
1322                         dbesc("twitter::".$own_id)
1323                 );
1324
1325         if(!count($own_user))
1326                 return;
1327
1328         // Is it me from twitter?
1329         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1330                 return;
1331
1332         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1333                         dbesc($postarray['parent-uri']),
1334                         intval($uid)
1335                         );
1336
1337         if(count($myconv)) {
1338
1339                 foreach($myconv as $conv) {
1340                         // now if we find a match, it means we're in this conversation
1341
1342                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1343                                 continue;
1344
1345                         require_once('include/enotify.php');
1346
1347                         $conv_parent = $conv['parent'];
1348
1349                         notification(array(
1350                                 'type'         => NOTIFY_COMMENT,
1351                                 'notify_flags' => $user[0]['notify-flags'],
1352                                 'language'     => $user[0]['language'],
1353                                 'to_name'      => $user[0]['username'],
1354                                 'to_email'     => $user[0]['email'],
1355                                 'uid'          => $user[0]['uid'],
1356                                 'item'         => $postarray,
1357                                 'link'             => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1358                                 'source_name'  => $postarray['author-name'],
1359                                 'source_link'  => $postarray['author-link'],
1360                                 'source_photo' => $postarray['author-avatar'],
1361                                 'verb'         => ACTIVITY_POST,
1362                                 'otype'        => 'item',
1363                                 'parent'       => $conv_parent,
1364                         ));
1365
1366                         // only send one notification
1367                         break;
1368                 }
1369         }
1370 }
1371
1372 function twitter_fetchhometimeline($a, $uid) {
1373         $ckey    = get_config('twitter', 'consumerkey');
1374         $csecret = get_config('twitter', 'consumersecret');
1375         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1376         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1377         $create_user = get_pconfig($uid, 'twitter', 'create_user');
1378
1379         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1380
1381         require_once('library/twitteroauth.php');
1382         require_once('include/items.php');
1383
1384         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1385
1386         $own_contact = twitter_fetch_own_contact($a, $uid);
1387
1388         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1389                 intval($own_contact),
1390                 intval($uid));
1391
1392         if(count($r)) {
1393                 $own_id = $r[0]["nick"];
1394         } else {
1395                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1396                 return;
1397         }
1398
1399         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1400                 intval($uid));
1401
1402         if(count($r)) {
1403                 $self = $r[0];
1404         } else {
1405                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1406                 return;
1407         }
1408
1409         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1410                 intval($uid));
1411         if(!count($u)) {
1412                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1413                 return;
1414         }
1415
1416         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1417         //$parameters["count"] = 200;
1418
1419
1420         // Fetching timeline
1421         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1422
1423         $first_time = ($lastid == "");
1424
1425         if ($lastid <> "")
1426                 $parameters["since_id"] = $lastid;
1427
1428         $items = $connection->get('statuses/home_timeline', $parameters);
1429
1430         if (!is_array($items)) {
1431                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1432                 return;
1433         }
1434
1435         $posts = array_reverse($items);
1436
1437         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1438
1439         if (count($posts)) {
1440                 foreach ($posts as $post) {
1441                         if ($post->id_str > $lastid)
1442                                 $lastid = $post->id_str;
1443
1444                         if ($first_time)
1445                                 continue;
1446
1447                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1448
1449                         if (trim($postarray['body']) == "")
1450                                 continue;
1451
1452                         $item = item_store($postarray);
1453
1454                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1455
1456                         if ($item != 0)
1457                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1458
1459                 }
1460         }
1461         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1462
1463         // Fetching mentions
1464         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1465
1466         $first_time = ($lastid == "");
1467
1468         if ($lastid <> "")
1469                 $parameters["since_id"] = $lastid;
1470
1471         $items = $connection->get('statuses/mentions_timeline', $parameters);
1472
1473         if (!is_array($items)) {
1474                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1475                 return;
1476         }
1477
1478         $posts = array_reverse($items);
1479
1480         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1481
1482         if (count($posts)) {
1483                 foreach ($posts as $post) {
1484                         if ($post->id_str > $lastid)
1485                                 $lastid = $post->id_str;
1486
1487                         if ($first_time)
1488                                 continue;
1489
1490                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1491
1492                         if (trim($postarray['body']) == "")
1493                                 continue;
1494
1495                         $item = item_store($postarray);
1496
1497                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1498
1499                         if ($item == 0) {
1500                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1501                                         dbesc($postarray['uri']),
1502                                         intval($uid)
1503                                 );
1504                                 if (count($r))
1505                                         $item = $r[0]['id'];
1506                         }
1507
1508                         if ($item != 0) {
1509                                 require_once('include/enotify.php');
1510                                 notification(array(
1511                                         'type'         => NOTIFY_TAGSELF,
1512                                         'notify_flags' => $u[0]['notify-flags'],
1513                                         'language'     => $u[0]['language'],
1514                                         'to_name'      => $u[0]['username'],
1515                                         'to_email'     => $u[0]['email'],
1516                                         'uid'          => $u[0]['uid'],
1517                                         'item'         => $postarray,
1518                                         'link'         => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1519                                         'source_name'  => $postarray['author-name'],
1520                                         'source_link'  => $postarray['author-link'],
1521                                         'source_photo' => $postarray['author-avatar'],
1522                                         'verb'         => ACTIVITY_TAG,
1523                                         'otype'        => 'item'
1524                                 ));
1525                         }
1526                 }
1527         }
1528
1529         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1530 }
1531
1532 function twitter_fetch_own_contact($a, $uid) {
1533         $ckey    = get_config('twitter', 'consumerkey');
1534         $csecret = get_config('twitter', 'consumersecret');
1535         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1536         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1537
1538         $own_id = get_pconfig($uid, 'twitter', 'own_id');
1539
1540         $contact_id = 0;
1541
1542         if ($own_id == "") {
1543                 require_once('library/twitteroauth.php');
1544
1545                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1546
1547                 // Fetching user data
1548                 $user = $connection->get('account/verify_credentials');
1549
1550                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1551
1552                 $contact_id = twitter_fetch_contact($uid, $user, true);
1553
1554         } else {
1555                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1556                         intval($uid), dbesc("twitter::".$own_id));
1557                 if(count($r))
1558                         $contact_id = $r[0]["id"];
1559                 else
1560                         del_pconfig($uid, 'twitter', 'own_id');
1561
1562         }
1563
1564         return($contact_id);
1565 }
1566
1567 function twitter_is_retweet($a, $uid, $body) {
1568         $body = trim($body);
1569
1570         // Skip if it isn't a pure repeated messages
1571         // Does it start with a share?
1572         if (strpos($body, "[share") > 0)
1573                 return(false);
1574
1575         // Does it end with a share?
1576         if (strlen($body) > (strrpos($body, "[/share]") + 8))
1577                 return(false);
1578
1579         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1580         // Skip if there is no shared message in there
1581         if ($body == $attributes)
1582                 return(false);
1583
1584         $link = "";
1585         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1586         if ($matches[1] != "")
1587                 $link = $matches[1];
1588
1589         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1590         if ($matches[1] != "")
1591                 $link = $matches[1];
1592
1593         $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1594         if ($id == $link)
1595                 return(false);
1596
1597         logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1598
1599         $ckey    = get_config('twitter', 'consumerkey');
1600         $csecret = get_config('twitter', 'consumersecret');
1601         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1602         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1603
1604         require_once('library/twitteroauth.php');
1605         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1606
1607         $result = $connection->post('statuses/retweet/'.$id);
1608
1609         logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1610
1611         return(!isset($result->errors));
1612 }
1613
1614 ?>