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