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