]> git.mxchange.org Git - friendica-addons.git/blob - twitter/twitter.php
The tempory path is now fetched from a new function (that ensures that the value...
[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_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                                 $picture = "";
716
717                                 // media
718                                 if (is_array($post->retweeted_status->entities->media)) {
719                                         foreach($post->retweeted_status->entities->media AS $media) {
720                                                 switch($media->type) {
721                                                         case 'photo':
722                                                                 //$_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
723                                                                 //$has_picture = true;
724                                                                 $_REQUEST['body'] = str_replace($media->url, "", $_REQUEST['body']);
725                                                                 $picture = $media->media_url_https;
726                                                                 break;
727                                                 }
728                                         }
729                                 }
730
731                                 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $picture);
732                                 $_REQUEST['body'] = $converted["body"];
733
734                                 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
735                                         "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
736                                         "' avatar='".$post->retweeted_status->user->profile_image_url_https.
737                                         "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
738                                         $_REQUEST['body'];
739                                 $_REQUEST['body'] .= "[/share]";
740                         } else {
741                                 $_REQUEST["body"] = $post->text;
742
743                                 $picture = "";
744
745                                 if (is_array($post->entities->media)) {
746                                         foreach($post->entities->media AS $media) {
747                                                 switch($media->type) {
748                                                         case 'photo':
749                                                                 //$_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
750                                                                 //$has_picture = true;
751                                                                 $_REQUEST['body'] = str_replace($media->url, "", $_REQUEST['body']);
752                                                                 $picture = $media->media_url_https;
753                                                                 break;
754                                                 }
755                                         }
756                                 }
757
758                                 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $picture);
759                                 $_REQUEST['body'] = $converted["body"];
760                         }
761
762                         if (is_string($post->place->name))
763                                 $_REQUEST["location"] = $post->place->name;
764
765                         if (is_string($post->place->full_name))
766                                 $_REQUEST["location"] = $post->place->full_name;
767
768                         if (is_array($post->geo->coordinates))
769                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
770
771                         if (is_array($post->coordinates->coordinates))
772                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
773
774                         //print_r($_REQUEST);
775                         logger('twitter: posting for user '.$uid);
776
777 //                      require_once('mod/item.php');
778
779                         item_post($a);
780                 }
781             }
782         }
783         set_pconfig($uid, 'twitter', 'lastid', $lastid);
784 }
785
786 function twitter_queue_hook(&$a,&$b) {
787
788         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
789                 dbesc(NETWORK_TWITTER)
790                 );
791         if(! count($qi))
792                 return;
793
794         require_once('include/queue_fn.php');
795
796         foreach($qi as $x) {
797                 if($x['network'] !== NETWORK_TWITTER)
798                         continue;
799
800                 logger('twitter_queue: run');
801
802                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
803                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
804                         intval($x['cid'])
805                 );
806                 if(! count($r))
807                         continue;
808
809                 $user = $r[0];
810
811                 $ckey    = get_config('twitter', 'consumerkey');
812                 $csecret = get_config('twitter', 'consumersecret');
813                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
814                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
815
816                 $success = false;
817
818                 if ($ckey AND $csecret AND $otoken AND $osecret) {
819
820                         logger('twitter_queue: able to post');
821
822                         $z = unserialize($x['content']);
823
824                         require_once("addon/twitter/codebird.php");
825
826                         $cb = \Codebird\Codebird::getInstance();
827                         $cb->setConsumerKey($ckey, $csecret);
828                         $cb->setToken($otoken, $osecret);
829
830                         if ($z['url'] == "statuses/update")
831                                 $result = $cb->statuses_update($z['post']);
832
833                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
834
835                         if ($result->errors)
836                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
837                         else {
838                                 $success = true;
839                                 remove_queue_item($x['id']);
840                         }
841                 } else
842                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
843
844                 if (!$success) {
845                         logger('twitter_queue: delayed');
846                         update_queue_time($x['id']);
847                 }
848         }
849 }
850
851 function twitter_fetch_contact($uid, $contact, $create_user) {
852
853         // Check if the unique contact is existing
854         // To-Do: only update once a while
855         $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
856                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
857
858         if (count($r) == 0)
859                 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
860                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
861                         dbesc($contact->name),
862                         dbesc($contact->screen_name),
863                         dbesc($contact->profile_image_url_https));
864         else
865                 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
866                         dbesc($contact->name),
867                         dbesc($contact->screen_name),
868                         dbesc($contact->profile_image_url_https),
869                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
870
871         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
872                 intval($uid), dbesc("twitter::".$contact->id_str));
873
874         if(!count($r) AND !$create_user)
875                 return(0);
876
877         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
878                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
879                 return(-1);
880         }
881
882         if(!count($r)) {
883                 // create contact record
884                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
885                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
886                                         `writable`, `blocked`, `readonly`, `pending` )
887                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
888                         intval($uid),
889                         dbesc(datetime_convert()),
890                         dbesc("https://twitter.com/".$contact->screen_name),
891                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
892                         dbesc($contact->screen_name."@twitter.com"),
893                         dbesc("twitter::".$contact->id_str),
894                         dbesc(''),
895                         dbesc("twitter::".$contact->id_str),
896                         dbesc($contact->name),
897                         dbesc($contact->screen_name),
898                         dbesc($contact->profile_image_url_https),
899                         dbesc(NETWORK_TWITTER),
900                         intval(CONTACT_IS_FRIEND),
901                         intval(1),
902                         intval(1)
903                 );
904
905                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
906                         dbesc("twitter::".$contact->id_str),
907                         intval($uid)
908                         );
909
910                 if(! count($r))
911                         return(false);
912
913                 $contact_id  = $r[0]['id'];
914
915                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
916                         intval($uid)
917                 );
918
919                 if($g && intval($g[0]['def_gid'])) {
920                         require_once('include/group.php');
921                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
922                 }
923
924                 require_once("Photo.php");
925
926                 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
927
928                 q("UPDATE `contact` SET `photo` = '%s',
929                                         `thumb` = '%s',
930                                         `micro` = '%s',
931                                         `name-date` = '%s',
932                                         `uri-date` = '%s',
933                                         `avatar-date` = '%s'
934                                 WHERE `id` = %d",
935                         dbesc($photos[0]),
936                         dbesc($photos[1]),
937                         dbesc($photos[2]),
938                         dbesc(datetime_convert()),
939                         dbesc(datetime_convert()),
940                         dbesc(datetime_convert()),
941                         intval($contact_id)
942                 );
943
944         } else {
945                 // update profile photos once every two weeks as we have no notification of when they change.
946
947                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
948                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
949
950                 // check that we have all the photos, this has been known to fail on occasion
951
952                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
953
954                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
955
956                         require_once("Photo.php");
957
958                         $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
959
960                         q("UPDATE `contact` SET `photo` = '%s',
961                                                 `thumb` = '%s',
962                                                 `micro` = '%s',
963                                                 `name-date` = '%s',
964                                                 `uri-date` = '%s',
965                                                 `avatar-date` = '%s',
966                                                 `url` = '%s',
967                                                 `nurl` = '%s',
968                                                 `addr` = '%s',
969                                                 `name` = '%s',
970                                                 `nick` = '%s'
971                                         WHERE `id` = %d",
972                                 dbesc($photos[0]),
973                                 dbesc($photos[1]),
974                                 dbesc($photos[2]),
975                                 dbesc(datetime_convert()),
976                                 dbesc(datetime_convert()),
977                                 dbesc(datetime_convert()),
978                                 dbesc("https://twitter.com/".$contact->screen_name),
979                                 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
980                                 dbesc($contact->screen_name."@twitter.com"),
981                                 dbesc($contact->name),
982                                 dbesc($contact->screen_name),
983                                 intval($r[0]['id'])
984                         );
985                 }
986         }
987
988         return($r[0]["id"]);
989 }
990
991 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
992         $ckey    = get_config('twitter', 'consumerkey');
993         $csecret = get_config('twitter', 'consumersecret');
994         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
995         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
996
997         require_once("addon/twitter/codebird.php");
998
999         $cb = \Codebird\Codebird::getInstance();
1000         $cb->setConsumerKey($ckey, $csecret);
1001         $cb->setToken($otoken, $osecret);
1002
1003         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1004                 intval($uid));
1005
1006         if(count($r)) {
1007                 $self = $r[0];
1008         } else
1009                 return;
1010
1011         $parameters = array();
1012
1013         if ($screen_name != "")
1014                 $parameters["screen_name"] = $screen_name;
1015
1016         if ($user_id != "")
1017                 $parameters["user_id"] = $user_id;
1018
1019         // Fetching user data
1020         $user = $cb->users_show($parameters);
1021
1022         if (!is_object($user))
1023                 return;
1024
1025         $contact_id = twitter_fetch_contact($uid, $user, true);
1026
1027         return $contact_id;
1028 }
1029
1030 function twitter_expand_entities($a, $body, $item, $no_tags = false, $picture) {
1031         require_once("include/oembed.php");
1032         require_once("include/network.php");
1033
1034         $tags = "";
1035
1036         if (isset($item->entities->urls)) {
1037                 $type = "";
1038                 $footerurl = "";
1039                 $footerlink = "";
1040                 $footer = "";
1041
1042                 foreach ($item->entities->urls AS $url) {
1043                         if ($url->url AND $url->expanded_url AND $url->display_url) {
1044
1045                                 $expanded_url = original_url($url->expanded_url);
1046
1047                                 $oembed_data = oembed_fetch_url($expanded_url);
1048
1049                                 // Quickfix: Workaround for URL with "[" and "]" in it
1050                                 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1051                                         $expanded_url = $url->url;
1052
1053                                 if ($type == "")
1054                                         $type = $oembed_data->type;
1055
1056                                 if ($oembed_data->type == "video") {
1057                                         //$body = str_replace($url->url,
1058                                         //              "[video]".$expanded_url."[/video]", $body);
1059                                         //$dontincludemedia = true;
1060                                         $type = $oembed_data->type;
1061                                         $footerurl = $expanded_url;
1062                                         $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1063
1064                                         $body = str_replace($url->url, $footerlink, $body);
1065                                 //} elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1066                                 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url)) {
1067                                         $body = str_replace($url->url,
1068                                                         "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1069                                                         $body);
1070                                         //$dontincludemedia = true;
1071                                 } elseif ($oembed_data->type != "link")
1072                                         $body = str_replace($url->url,
1073                                                         "[url=".$expanded_url."]".$expanded_url."[/url]",
1074                                                         $body);
1075                                 else {
1076                                         $img_str = fetch_url($expanded_url, true, $redirects, 4);
1077
1078                                         $tempfile = tempnam(get_temppath(), "cache");
1079                                         file_put_contents($tempfile, $img_str);
1080                                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1081                                         unlink($tempfile);
1082
1083                                         if (substr($mime, 0, 6) == "image/") {
1084                                                 $type = "photo";
1085                                                 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1086                                                 //$dontincludemedia = true;
1087                                         } else {
1088                                                 $type = $oembed_data->type;
1089                                                 $footerurl = $expanded_url;
1090                                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1091
1092                                                 $body = str_replace($url->url, $footerlink, $body);
1093                                         }
1094                                 }
1095                         }
1096                 }
1097
1098                 if ($footerurl != "")
1099                         $footer = add_page_info($footerurl, false, $picture);
1100
1101                 if (($footerlink != "") AND (trim($footer) != "")) {
1102                         $removedlink = trim(str_replace($footerlink, "", $body));
1103
1104                         if (strstr($body, $removedlink))
1105                                 $body = $removedlink;
1106
1107                         $body .= $footer;
1108                 }
1109
1110                 if (($footer == "") AND ($picture != ""))
1111                         $body .= "\n\n[img]".$picture."[/img]\n";
1112
1113                 if ($no_tags)
1114                         return(array("body" => $body, "tags" => ""));
1115
1116                 $tags_arr = array();
1117
1118                 foreach ($item->entities->hashtags AS $hashtag) {
1119                         $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1120                         $tags_arr["#".$hashtag->text] = $url;
1121                         $body = str_replace("#".$hashtag->text, $url, $body);
1122                 }
1123
1124                 foreach ($item->entities->user_mentions AS $mention) {
1125                         $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1126                         $tags_arr["@".$mention->screen_name] = $url;
1127                         $body = str_replace("@".$mention->screen_name, $url, $body);
1128                 }
1129
1130                 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1131                 $tags = get_tags($body);
1132
1133                 if(count($tags)) {
1134                         foreach($tags as $tag) {
1135                                 if (strstr(trim($tag), " "))
1136                                         continue;
1137
1138                                 if(strpos($tag,'#') === 0) {
1139                                         if(strpos($tag,'[url='))
1140                                                 continue;
1141
1142                                         // don't link tags that are already embedded in links
1143
1144                                         if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1145                                                 continue;
1146                                         if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1147                                                 continue;
1148
1149                                         $basetag = str_replace('_',' ',substr($tag,1));
1150                                         $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1151                                         $body = str_replace($tag,$url,$body);
1152                                         $tags_arr["#".$basetag] = $url;
1153                                         continue;
1154                                 } elseif(strpos($tag,'@') === 0) {
1155                                         if(strpos($tag,'[url='))
1156                                                 continue;
1157
1158                                         $basetag = substr($tag,1);
1159                                         $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1160                                         $body = str_replace($tag,$url,$body);
1161                                         $tags_arr["@".$basetag] = $url;
1162                                 }
1163                         }
1164                 }
1165
1166
1167                 $tags = implode($tags_arr, ",");
1168
1169         }
1170         return(array("body" => $body, "tags" => $tags));
1171 }
1172
1173 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1174
1175         $has_picture = false;
1176
1177         $postarray = array();
1178         $postarray['network'] = NETWORK_TWITTER;
1179         $postarray['gravity'] = 0;
1180         $postarray['uid'] = $uid;
1181         $postarray['wall'] = 0;
1182         $postarray['uri'] = "twitter::".$post->id_str;
1183
1184         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1185                         dbesc($postarray['uri']),
1186                         intval($uid)
1187                 );
1188
1189         if (count($r))
1190                 return(array());
1191
1192         $contactid = 0;
1193
1194         if ($post->in_reply_to_status_id_str != "") {
1195
1196                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1197
1198                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1199                                 dbesc($parent),
1200                                 intval($uid)
1201                         );
1202                 if (count($r)) {
1203                         $postarray['thr-parent'] = $r[0]["uri"];
1204                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1205                 } else {
1206                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1207                                         dbesc($parent),
1208                                         intval($uid)
1209                                 );
1210                         if (count($r)) {
1211                                 $postarray['thr-parent'] = $r[0]['uri'];
1212                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1213                         } else {
1214                                 $postarray['thr-parent'] = $postarray['uri'];
1215                                 $postarray['parent-uri'] = $postarray['uri'];
1216                         }
1217                 }
1218
1219                 // Is it me?
1220                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1221
1222                 if ($post->user->id_str == $own_id) {
1223                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1224                                 intval($uid));
1225
1226                         if(count($r)) {
1227                                 $contactid = $r[0]["id"];
1228
1229                                 $postarray['owner-name'] =  $r[0]["name"];
1230                                 $postarray['owner-link'] = $r[0]["url"];
1231                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1232                         } else
1233                                 return(array());
1234                 }
1235         } else
1236                 $postarray['parent-uri'] = $postarray['uri'];
1237
1238         if ($contactid == 0) {
1239                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1240
1241                 $postarray['owner-name'] = $post->user->name;
1242                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1243                 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1244         }
1245
1246         if(($contactid == 0) AND !$only_existing_contact)
1247                 $contactid = $self['id'];
1248         elseif ($contactid <= 0)
1249                 return(array());
1250
1251         $postarray['contact-id'] = $contactid;
1252
1253         $postarray['verb'] = ACTIVITY_POST;
1254         $postarray['author-name'] = $postarray['owner-name'];
1255         $postarray['author-link'] = $postarray['owner-link'];
1256         $postarray['author-avatar'] = $postarray['owner-avatar'];
1257         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1258         $postarray['app'] = strip_tags($post->source);
1259
1260         if ($post->user->protected) {
1261                 $postarray['private'] = 1;
1262                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1263         }
1264
1265         $postarray['body'] = $post->text;
1266
1267         $picture = "";
1268
1269         // media
1270         if (is_array($post->entities->media)) {
1271                 foreach($post->entities->media AS $media) {
1272                         switch($media->type) {
1273                                 case 'photo':
1274                                         //$postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1275                                         //$has_picture = true;
1276                                         $postarray['body'] = str_replace($media->url, "", $postarray['body']);
1277                                         $picture = $media->media_url_https;
1278                                         break;
1279                                 default:
1280                                         $postarray['body'] .= print_r($media, true);
1281                         }
1282                 }
1283         }
1284
1285         $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $picture);
1286         $postarray['body'] = $converted["body"];
1287         $postarray['tag'] = $converted["tags"];
1288
1289         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1290         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1291
1292         if (is_string($post->place->name))
1293                 $postarray["location"] = $post->place->name;
1294
1295         if (is_string($post->place->full_name))
1296                 $postarray["location"] = $post->place->full_name;
1297
1298         if (is_array($post->geo->coordinates))
1299                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1300
1301         if (is_array($post->coordinates->coordinates))
1302                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1303
1304         if (is_object($post->retweeted_status)) {
1305
1306                 $postarray['body'] = $post->retweeted_status->text;
1307
1308                 $picture = "";
1309
1310                 // media
1311                 if (is_array($post->retweeted_status->entities->media)) {
1312                         foreach($post->retweeted_status->entities->media AS $media) {
1313                                 switch($media->type) {
1314                                         case 'photo':
1315                                                 //$postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1316                                                 //$has_picture = true;
1317                                                 $postarray['body'] = str_replace($media->url, "", $postarray['body']);
1318                                                 $picture = $media->media_url_https;
1319                                                 break;
1320                                         default:
1321                                                 $postarray['body'] .= print_r($media, true);
1322                                 }
1323                         }
1324                 }
1325
1326                 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $picture);
1327                 $postarray['body'] = $converted["body"];
1328                 $postarray['tag'] = $converted["tags"];
1329
1330                 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1331
1332                 // Deactivated at the moment, since there are problems with answers to retweets
1333                 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1334                         $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1335                                 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1336                                 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1337                                 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1338                                 $postarray['body'];
1339                         $postarray['body'] .= "[/share]";
1340                 } else {
1341                         // Let retweets look like wall-to-wall posts
1342                         $postarray['author-name'] = $post->retweeted_status->user->name;
1343                         $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1344                         $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1345                         //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1346                         //      $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1347                         //      $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1348                         //}
1349                 }
1350
1351         }
1352         return($postarray);
1353 }
1354
1355 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1356
1357         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1358                         intval($uid)
1359                 );
1360
1361         if(!count($user))
1362                 return;
1363
1364         // Is it me?
1365         if (link_compare($user[0]["url"], $postarray['author-link']))
1366                 return;
1367
1368         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1369                         intval($uid),
1370                         dbesc("twitter::".$own_id)
1371                 );
1372
1373         if(!count($own_user))
1374                 return;
1375
1376         // Is it me from twitter?
1377         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1378                 return;
1379
1380         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1381                         dbesc($postarray['parent-uri']),
1382                         intval($uid)
1383                         );
1384
1385         if(count($myconv)) {
1386
1387                 foreach($myconv as $conv) {
1388                         // now if we find a match, it means we're in this conversation
1389
1390                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1391                                 continue;
1392
1393                         require_once('include/enotify.php');
1394
1395                         $conv_parent = $conv['parent'];
1396
1397                         notification(array(
1398                                 'type'         => NOTIFY_COMMENT,
1399                                 'notify_flags' => $user[0]['notify-flags'],
1400                                 'language'     => $user[0]['language'],
1401                                 'to_name'      => $user[0]['username'],
1402                                 'to_email'     => $user[0]['email'],
1403                                 'uid'          => $user[0]['uid'],
1404                                 'item'         => $postarray,
1405                                 'link'             => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1406                                 'source_name'  => $postarray['author-name'],
1407                                 'source_link'  => $postarray['author-link'],
1408                                 'source_photo' => $postarray['author-avatar'],
1409                                 'verb'         => ACTIVITY_POST,
1410                                 'otype'        => 'item',
1411                                 'parent'       => $conv_parent,
1412                         ));
1413
1414                         // only send one notification
1415                         break;
1416                 }
1417         }
1418 }
1419
1420 function twitter_fetchhometimeline($a, $uid) {
1421         $ckey    = get_config('twitter', 'consumerkey');
1422         $csecret = get_config('twitter', 'consumersecret');
1423         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1424         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1425         $create_user = get_pconfig($uid, 'twitter', 'create_user');
1426
1427         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1428
1429         require_once('library/twitteroauth.php');
1430         require_once('include/items.php');
1431
1432         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1433
1434         $own_contact = twitter_fetch_own_contact($a, $uid);
1435
1436         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1437                 intval($own_contact),
1438                 intval($uid));
1439
1440         if(count($r)) {
1441                 $own_id = $r[0]["nick"];
1442         } else {
1443                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1444                 return;
1445         }
1446
1447         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1448                 intval($uid));
1449
1450         if(count($r)) {
1451                 $self = $r[0];
1452         } else {
1453                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1454                 return;
1455         }
1456
1457         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1458                 intval($uid));
1459         if(!count($u)) {
1460                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1461                 return;
1462         }
1463
1464         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1465         //$parameters["count"] = 200;
1466
1467
1468         // Fetching timeline
1469         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1470
1471         $first_time = ($lastid == "");
1472
1473         if ($lastid <> "")
1474                 $parameters["since_id"] = $lastid;
1475
1476         $items = $connection->get('statuses/home_timeline', $parameters);
1477
1478         if (!is_array($items)) {
1479                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1480                 return;
1481         }
1482
1483         $posts = array_reverse($items);
1484
1485         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1486
1487         if (count($posts)) {
1488                 foreach ($posts as $post) {
1489                         if ($post->id_str > $lastid)
1490                                 $lastid = $post->id_str;
1491
1492                         if ($first_time)
1493                                 continue;
1494
1495                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1496
1497                         if (trim($postarray['body']) == "")
1498                                 continue;
1499
1500                         $item = item_store($postarray);
1501
1502                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1503
1504                         if ($item != 0)
1505                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1506
1507                 }
1508         }
1509         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1510
1511         // Fetching mentions
1512         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1513
1514         $first_time = ($lastid == "");
1515
1516         if ($lastid <> "")
1517                 $parameters["since_id"] = $lastid;
1518
1519         $items = $connection->get('statuses/mentions_timeline', $parameters);
1520
1521         if (!is_array($items)) {
1522                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1523                 return;
1524         }
1525
1526         $posts = array_reverse($items);
1527
1528         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1529
1530         if (count($posts)) {
1531                 foreach ($posts as $post) {
1532                         if ($post->id_str > $lastid)
1533                                 $lastid = $post->id_str;
1534
1535                         if ($first_time)
1536                                 continue;
1537
1538                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1539
1540                         if (trim($postarray['body']) == "")
1541                                 continue;
1542
1543                         $item = item_store($postarray);
1544
1545                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1546
1547                         if ($item == 0) {
1548                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1549                                         dbesc($postarray['uri']),
1550                                         intval($uid)
1551                                 );
1552                                 if (count($r))
1553                                         $item = $r[0]['id'];
1554                         }
1555
1556                         if ($item != 0) {
1557                                 require_once('include/enotify.php');
1558                                 notification(array(
1559                                         'type'         => NOTIFY_TAGSELF,
1560                                         'notify_flags' => $u[0]['notify-flags'],
1561                                         'language'     => $u[0]['language'],
1562                                         'to_name'      => $u[0]['username'],
1563                                         'to_email'     => $u[0]['email'],
1564                                         'uid'          => $u[0]['uid'],
1565                                         'item'         => $postarray,
1566                                         'link'         => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1567                                         'source_name'  => $postarray['author-name'],
1568                                         'source_link'  => $postarray['author-link'],
1569                                         'source_photo' => $postarray['author-avatar'],
1570                                         'verb'         => ACTIVITY_TAG,
1571                                         'otype'        => 'item'
1572                                 ));
1573                         }
1574                 }
1575         }
1576
1577         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1578 }
1579
1580 function twitter_fetch_own_contact($a, $uid) {
1581         $ckey    = get_config('twitter', 'consumerkey');
1582         $csecret = get_config('twitter', 'consumersecret');
1583         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1584         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1585
1586         $own_id = get_pconfig($uid, 'twitter', 'own_id');
1587
1588         $contact_id = 0;
1589
1590         if ($own_id == "") {
1591                 require_once('library/twitteroauth.php');
1592
1593                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1594
1595                 // Fetching user data
1596                 $user = $connection->get('account/verify_credentials');
1597
1598                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1599
1600                 $contact_id = twitter_fetch_contact($uid, $user, true);
1601
1602         } else {
1603                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1604                         intval($uid), dbesc("twitter::".$own_id));
1605                 if(count($r))
1606                         $contact_id = $r[0]["id"];
1607                 else
1608                         del_pconfig($uid, 'twitter', 'own_id');
1609
1610         }
1611
1612         return($contact_id);
1613 }
1614
1615 function twitter_is_retweet($a, $uid, $body) {
1616         $body = trim($body);
1617
1618         // Skip if it isn't a pure repeated messages
1619         // Does it start with a share?
1620         if (strpos($body, "[share") > 0)
1621                 return(false);
1622
1623         // Does it end with a share?
1624         if (strlen($body) > (strrpos($body, "[/share]") + 8))
1625                 return(false);
1626
1627         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1628         // Skip if there is no shared message in there
1629         if ($body == $attributes)
1630                 return(false);
1631
1632         $link = "";
1633         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1634         if ($matches[1] != "")
1635                 $link = $matches[1];
1636
1637         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1638         if ($matches[1] != "")
1639                 $link = $matches[1];
1640
1641         $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1642         if ($id == $link)
1643                 return(false);
1644
1645         logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1646
1647         $ckey    = get_config('twitter', 'consumerkey');
1648         $csecret = get_config('twitter', 'consumersecret');
1649         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1650         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1651
1652         require_once('library/twitteroauth.php');
1653         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1654
1655         $result = $connection->post('statuses/retweet/'.$id);
1656
1657         logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1658
1659         return(!isset($result->errors));
1660 }
1661 ?>