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