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