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