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