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