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