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