1fdcb89ca773d59cede3b2f70978ff3a9442abee
[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 // -----------------
555                         $max_char = 280;
556                         require_once("include/plaintext.php");
557                         $msgarr = plaintext($a, $b, $max_char, true, 8);
558                         $msg = $msgarr["text"];
559
560                         if (($msg == "") && isset($msgarr["title"]))
561                                 $msg = shortenmsg($msgarr["title"], $max_char - 50);
562
563                         if (isset($msgarr["url"]))
564                                 $msg .= "\n".$msgarr["url"];
565 // -----------------
566                         $url = 'statuses/update';
567                         $post = array('status' => $msg, 'weighted_character_count' => 'true');
568
569                         if ($iscomment)
570                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
571
572                         $result = $tweet->post($url, $post);
573                         logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
574
575                         if ($result->source)
576                                 set_config("twitter", "application_name", strip_tags($result->source));
577
578                         if ($result->errors) {
579                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
580
581                                 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
582                                 if (count($r))
583                                         $a->contact = $r[0]["id"];
584
585                                 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
586                                 require_once('include/queue_fn.php');
587                                 add_to_queue($a->contact,NETWORK_TWITTER,$s);
588                                 notice(t('Twitter post failed. Queued for retry.').EOL);
589                         } elseif ($iscomment) {
590                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
591                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
592                                         dbesc("twitter::".$result->id_str),
593                                         intval($b['id'])
594                                 );
595                                 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
596                                 //      dbesc("twitter::".$result->id_str),
597                                 //      dbesc($result->text),
598                                 //      intval($b['id'])
599                                 //);
600                         }
601                 }
602         }
603 }
604
605 function twitter_plugin_admin_post(&$a){
606         $consumerkey    =       ((x($_POST,'consumerkey'))              ? notags(trim($_POST['consumerkey']))   : '');
607         $consumersecret =       ((x($_POST,'consumersecret'))   ? notags(trim($_POST['consumersecret'])): '');
608         $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
609         set_config('twitter','consumerkey',$consumerkey);
610         set_config('twitter','consumersecret',$consumersecret);
611         //set_config('twitter','application_name',$applicationname);
612         info( t('Settings updated.'). EOL );
613 }
614 function twitter_plugin_admin(&$a, &$o){
615         $t = get_markup_template( "admin.tpl", "addon/twitter/" );
616
617         $o = replace_macros($t, array(
618                 '$submit' => t('Save Settings'),
619                                                                 // name, label, value, help, [extra values]
620                 '$consumerkey' => array('consumerkey', t('Consumer key'),  get_config('twitter', 'consumerkey' ), ''),
621                 '$consumersecret' => array('consumersecret', t('Consumer secret'),  get_config('twitter', 'consumersecret' ), ''),
622                 //'$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'))
623         ));
624 }
625
626 function twitter_cron($a,$b) {
627         $last = get_config('twitter','last_poll');
628
629         $poll_interval = intval(get_config('twitter','poll_interval'));
630         if(! $poll_interval)
631                 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
632
633         if($last) {
634                 $next = $last + ($poll_interval * 60);
635                 if($next > time()) {
636                         logger('twitter: poll intervall not reached');
637                         return;
638                 }
639         }
640         logger('twitter: cron_start');
641
642         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1'");
643         if(count($r)) {
644                 foreach($r as $rr) {
645                         logger('twitter: fetching for user '.$rr['uid']);
646                         proc_run(PRIORITY_MEDIUM, "addon/twitter/twitter_sync.php", 1, (int)$rr['uid']);
647                 }
648         }
649
650         $abandon_days = intval(get_config('system','account_abandon_days'));
651         if ($abandon_days < 1)
652                 $abandon_days = 0;
653
654         $abandon_limit = date("Y-m-d H:i:s", time() - $abandon_days * 86400);
655
656         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1'");
657         if(count($r)) {
658                 foreach($r as $rr) {
659                         if ($abandon_days != 0) {
660                                 $user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit);
661                                 if (!count($user)) {
662                                         logger('abandoned account: timeline from user '.$rr['uid'].' will not be imported');
663                                         continue;
664                                 }
665                         }
666
667                         logger('twitter: importing timeline from user '.$rr['uid']);
668                         proc_run(PRIORITY_MEDIUM, "addon/twitter/twitter_sync.php", 2, (int)$rr['uid']);
669 /*
670                         // To-Do
671                         // check for new contacts once a day
672                         $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
673                         if($last_contact_check)
674                                 $next_contact_check = $last_contact_check + 86400;
675                         else
676                                 $next_contact_check = 0;
677
678                         if($next_contact_check <= time()) {
679                                 pumpio_getallusers($a, $rr["uid"]);
680                                 set_pconfig($rr['uid'],'pumpio','contact_check',time());
681                         }
682 */
683
684                 }
685         }
686
687         logger('twitter: cron_end');
688
689         set_config('twitter','last_poll', time());
690 }
691
692 function twitter_expire($a,$b) {
693
694         $days = get_config('twitter', 'expire');
695
696         if ($days == 0)
697                 return;
698
699         if (method_exists('dba', 'delete')) {
700                 $r = dba::select('item', array('id'), array('deleted' => true, 'network' => NETWORK_TWITTER));
701                 while ($row = dba::fetch($r)) {
702                         dba::delete('item', array('id' => $row['id']));
703                 }
704                 dba::close($r);
705         } else {
706                 $r = q("DELETE FROM `item` WHERE `deleted` AND `network` = '%s'", dbesc(NETWORK_TWITTER));
707         }
708
709         require_once("include/items.php");
710
711         logger('twitter_expire: expire_start');
712
713         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
714         if(count($r)) {
715                 foreach($r as $rr) {
716                         logger('twitter_expire: user '.$rr['uid']);
717                         item_expire($rr['uid'], $days, NETWORK_TWITTER, true);
718                 }
719         }
720
721         logger('twitter_expire: expire_end');
722 }
723
724 function twitter_prepare_body(&$a,&$b) {
725         if ($b["item"]["network"] != NETWORK_TWITTER)
726                 return;
727
728         if ($b["preview"]) {
729                 $max_char = 140;
730                 require_once("include/plaintext.php");
731                 $item = $b["item"];
732                 $item["plink"] = $a->get_baseurl()."/display/".$a->user["nickname"]."/".$item["parent"];
733
734                 $r = q("SELECT `author-link` FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
735                         dbesc($item["thr-parent"]),
736                         intval(local_user()));
737
738                 if(count($r)) {
739                         $orig_post = $r[0];
740
741                         $nicknameplain = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
742                         $nickname = "@[url=".$orig_post["author-link"]."]".$nicknameplain."[/url]";
743                         $nicknameplain = "@".$nicknameplain;
744
745                         if ((strpos($item["body"], $nickname) === false) && (strpos($item["body"], $nicknameplain) === false))
746                                 $item["body"] = $nickname." ".$item["body"];
747                 }
748
749
750                 $msgarr = plaintext($a, $item, $max_char, true, 8);
751                 $msg = $msgarr["text"];
752
753                 if (isset($msgarr["url"]) && ($msgarr["type"] != "photo"))
754                         $msg .= " ".$msgarr["url"];
755
756                 if (isset($msgarr["image"]))
757                         $msg .= " ".$msgarr["image"];
758
759                 $b['html'] = nl2br(htmlspecialchars($msg));
760         }
761 }
762
763 /**
764  * @brief Build the item array for the mirrored post
765  *
766  * @param object $a Application class
767  * @param integer $uid User id
768  * @param object $post Twitter object with the post
769  *
770  * @return array item data to be posted
771  */
772 function twitter_do_mirrorpost($a, $uid, $post) {
773         $datarray["type"] = "wall";
774         $datarray["api_source"] = true;
775         $datarray["profile_uid"] = $uid;
776         $datarray["extid"] = NETWORK_TWITTER;
777         $datarray['message_id'] = item_new_uri($a->get_hostname(), $uid, NETWORK_TWITTER.":".$post->id);
778         $datarray['object'] = json_encode($post);
779         $datarray["title"] = "";
780
781         if (is_object($post->retweeted_status)) {
782                 // We don't support nested shares, so we mustn't show quotes as shares on retweets
783                 $item = twitter_createpost($a, $uid, $post->retweeted_status, array('id' => 0), false, false, true);
784
785                 $datarray['body'] = "\n".share_header($item['author-name'], $item['author-link'], $item['author-avatar'], "",
786                                         $item['created'], $item['plink']);
787
788                 $datarray['body'] .= $item['body'].'[/share]';
789         } else {
790                 $item = twitter_createpost($a, $uid, $post, array('id' => 0), false, false, false);
791
792                 $datarray['body'] = $item['body'];
793         }
794
795         $datarray["source"] = $item['app'];
796         $datarray["verb"] = $item['verb'];
797
798         if (isset($item["location"])) {
799                 $datarray["location"] = $item["location"];
800         }
801
802         if (isset($item["coord"])) {
803                 $datarray["coord"] = $item["coord"];
804         }
805
806         return $datarray;
807 }
808
809 function twitter_fetchtimeline($a, $uid) {
810         $ckey    = get_config('twitter', 'consumerkey');
811         $csecret = get_config('twitter', 'consumersecret');
812         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
813         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
814         $lastid  = get_pconfig($uid, 'twitter', 'lastid');
815
816         $application_name  = get_config('twitter', 'application_name');
817
818         if ($application_name == "")
819                 $application_name = $a->get_hostname();
820
821         $has_picture = false;
822
823         require_once('mod/item.php');
824         require_once('include/items.php');
825         require_once('mod/share.php');
826
827         require_once('library/twitteroauth.php');
828         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
829
830         $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true, "tweet_mode" => "extended");
831
832         $first_time = ($lastid == "");
833
834         if ($lastid <> "")
835                 $parameters["since_id"] = $lastid;
836
837         $items = $connection->get('statuses/user_timeline', $parameters);
838
839         if (!is_array($items))
840                 return;
841
842         $posts = array_reverse($items);
843
844         if (count($posts)) {
845             foreach ($posts as $post) {
846                 if ($post->id_str > $lastid) {
847                         $lastid = $post->id_str;
848                         set_pconfig($uid, 'twitter', 'lastid', $lastid);
849                 }
850
851                 if ($first_time)
852                         continue;
853
854                 if (!stristr($post->source, $application_name)) {
855
856                         $_SESSION["authenticated"] = true;
857                         $_SESSION["uid"] = $uid;
858
859                         $_REQUEST = twitter_do_mirrorpost($a, $uid, $post);
860
861                         logger('twitter: posting for user '.$uid);
862
863                         item_post($a);
864                 }
865             }
866         }
867         set_pconfig($uid, 'twitter', 'lastid', $lastid);
868 }
869
870 function twitter_queue_hook(&$a,&$b) {
871
872         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
873                 dbesc(NETWORK_TWITTER)
874                 );
875         if(! count($qi))
876                 return;
877
878         require_once('include/queue_fn.php');
879
880         foreach($qi as $x) {
881                 if($x['network'] !== NETWORK_TWITTER)
882                         continue;
883
884                 logger('twitter_queue: run');
885
886                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
887                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
888                         intval($x['cid'])
889                 );
890                 if(! count($r))
891                         continue;
892
893                 $user = $r[0];
894
895                 $ckey    = get_config('twitter', 'consumerkey');
896                 $csecret = get_config('twitter', 'consumersecret');
897                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
898                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
899
900                 $success = false;
901
902                 if ($ckey && $csecret && $otoken && $osecret) {
903
904                         logger('twitter_queue: able to post');
905
906                         $z = unserialize($x['content']);
907
908                         require_once("addon/twitter/codebird.php");
909
910                         $cb = \Codebird\Codebird::getInstance();
911                         $cb->setConsumerKey($ckey, $csecret);
912                         $cb->setToken($otoken, $osecret);
913
914                         if ($z['url'] == "statuses/update")
915                                 $result = $cb->statuses_update($z['post']);
916
917                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
918
919                         if ($result->errors)
920                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
921                         else {
922                                 $success = true;
923                                 remove_queue_item($x['id']);
924                         }
925                 } else
926                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
927
928                 if (!$success) {
929                         logger('twitter_queue: delayed');
930                         update_queue_time($x['id']);
931                 }
932         }
933 }
934
935 function twitter_fix_avatar($avatar) {
936         require_once("include/Photo.php");
937
938         $new_avatar = str_replace("_normal.", ".", $avatar);
939
940         $info = get_photo_info($new_avatar);
941         if (!$info)
942                 $new_avatar = $avatar;
943
944         return $new_avatar;
945 }
946
947 function twitter_fetch_contact($uid, $contact, $create_user) {
948
949         if ($contact->id_str == "")
950                 return(-1);
951
952         $avatar = twitter_fix_avatar($contact->profile_image_url_https);
953
954         update_gcontact(array("url" => "https://twitter.com/".$contact->screen_name,
955                         "network" => NETWORK_TWITTER, "photo" => $avatar,  "hide" => true,
956                         "name" => $contact->name, "nick" => $contact->screen_name,
957                         "location" => $contact->location, "about" => $contact->description,
958                         "addr" => $contact->screen_name."@twitter.com", "generation" => 2));
959
960         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
961                 intval($uid), dbesc("twitter::".$contact->id_str));
962
963         if(!count($r) && !$create_user)
964                 return(0);
965
966         if (count($r) && ($r[0]["readonly"] || $r[0]["blocked"])) {
967                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
968                 return(-1);
969         }
970
971         if(!count($r)) {
972                 // create contact record
973                 q("INSERT INTO `contact` (`uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
974                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
975                                         `location`, `about`, `writable`, `blocked`, `readonly`, `pending`)
976                                         VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', %d, 0, 0, 0)",
977                         intval($uid),
978                         dbesc(datetime_convert()),
979                         dbesc("https://twitter.com/".$contact->screen_name),
980                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
981                         dbesc($contact->screen_name."@twitter.com"),
982                         dbesc("twitter::".$contact->id_str),
983                         dbesc(''),
984                         dbesc("twitter::".$contact->id_str),
985                         dbesc($contact->name),
986                         dbesc($contact->screen_name),
987                         dbesc($avatar),
988                         dbesc(NETWORK_TWITTER),
989                         intval(CONTACT_IS_FRIEND),
990                         intval(1),
991                         dbesc($contact->location),
992                         dbesc($contact->description),
993                         intval(1)
994                 );
995
996                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
997                         dbesc("twitter::".$contact->id_str),
998                         intval($uid)
999                         );
1000
1001                 if(! count($r))
1002                         return(false);
1003
1004                 $contact_id  = $r[0]['id'];
1005
1006                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
1007                         intval($uid)
1008                 );
1009
1010                 if($g && intval($g[0]['def_gid'])) {
1011                         require_once('include/group.php');
1012                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
1013                 }
1014
1015                 require_once("Photo.php");
1016
1017                 $photos = import_profile_photo($avatar, $uid, $contact_id, true);
1018
1019                 if ($photos) {
1020                         q("UPDATE `contact` SET `photo` = '%s',
1021                                                 `thumb` = '%s',
1022                                                 `micro` = '%s',
1023                                                 `name-date` = '%s',
1024                                                 `uri-date` = '%s',
1025                                                         `avatar-date` = '%s'
1026                                         WHERE `id` = %d",
1027                                 dbesc($photos[0]),
1028                                 dbesc($photos[1]),
1029                                 dbesc($photos[2]),
1030                                 dbesc(datetime_convert()),
1031                                 dbesc(datetime_convert()),
1032                                 dbesc(datetime_convert()),
1033                                 intval($contact_id)
1034                         );
1035                 }
1036         } else {
1037                 // update profile photos once every two weeks as we have no notification of when they change.
1038
1039                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
1040                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
1041
1042                 // check that we have all the photos, this has been known to fail on occasion
1043
1044                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
1045
1046                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
1047
1048                         require_once("Photo.php");
1049
1050                         $photos = import_profile_photo($avatar, $uid, $r[0]['id'], true);
1051
1052                         if ($photos) {
1053                                 q("UPDATE `contact` SET `photo` = '%s',
1054                                                         `thumb` = '%s',
1055                                                         `micro` = '%s',
1056                                                         `name-date` = '%s',
1057                                                         `uri-date` = '%s',
1058                                                         `avatar-date` = '%s',
1059                                                         `url` = '%s',
1060                                                         `nurl` = '%s',
1061                                                         `addr` = '%s',
1062                                                         `name` = '%s',
1063                                                         `nick` = '%s',
1064                                                         `location` = '%s',
1065                                                         `about` = '%s'
1066                                                 WHERE `id` = %d",
1067                                         dbesc($photos[0]),
1068                                         dbesc($photos[1]),
1069                                         dbesc($photos[2]),
1070                                         dbesc(datetime_convert()),
1071                                         dbesc(datetime_convert()),
1072                                         dbesc(datetime_convert()),
1073                                         dbesc("https://twitter.com/".$contact->screen_name),
1074                                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1075                                         dbesc($contact->screen_name."@twitter.com"),
1076                                         dbesc($contact->name),
1077                                         dbesc($contact->screen_name),
1078                                         dbesc($contact->location),
1079                                         dbesc($contact->description),
1080                                         intval($r[0]['id'])
1081                                 );
1082                         }
1083                 }
1084         }
1085
1086         return($r[0]["id"]);
1087 }
1088
1089 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
1090         $ckey    = get_config('twitter', 'consumerkey');
1091         $csecret = get_config('twitter', 'consumersecret');
1092         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1093         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1094
1095         require_once("addon/twitter/codebird.php");
1096
1097         $cb = \Codebird\Codebird::getInstance();
1098         $cb->setConsumerKey($ckey, $csecret);
1099         $cb->setToken($otoken, $osecret);
1100
1101         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1102                 intval($uid));
1103
1104         if(count($r)) {
1105                 $self = $r[0];
1106         } else
1107                 return;
1108
1109         $parameters = array();
1110
1111         if ($screen_name != "")
1112                 $parameters["screen_name"] = $screen_name;
1113
1114         if ($user_id != "")
1115                 $parameters["user_id"] = $user_id;
1116
1117         // Fetching user data
1118         $user = $cb->users_show($parameters);
1119
1120         if (!is_object($user))
1121                 return;
1122
1123         $contact_id = twitter_fetch_contact($uid, $user, true);
1124
1125         return $contact_id;
1126 }
1127
1128 function twitter_expand_entities($a, $body, $item, $no_tags = false, $picture) {
1129         require_once("include/oembed.php");
1130         require_once("include/network.php");
1131
1132         $tags = "";
1133
1134         $plain = $body;
1135
1136         if (isset($item->entities->urls)) {
1137                 $type = "";
1138                 $footerurl = "";
1139                 $footerlink = "";
1140                 $footer = "";
1141
1142                 foreach ($item->entities->urls AS $url) {
1143
1144                         $plain = str_replace($url->url, '', $plain);
1145
1146                         if ($url->url && $url->expanded_url && $url->display_url) {
1147
1148                                 $expanded_url = original_url($url->expanded_url);
1149
1150                                 $oembed_data = oembed_fetch_url($expanded_url);
1151
1152                                 // Quickfix: Workaround for URL with "[" and "]" in it
1153                                 if (strpos($expanded_url, "[") || strpos($expanded_url, "]"))
1154                                         $expanded_url = $url->url;
1155
1156                                 if ($type == "")
1157                                         $type = $oembed_data->type;
1158
1159                                 if ($oembed_data->type == "video") {
1160                                         //$body = str_replace($url->url,
1161                                         //              "[video]".$expanded_url."[/video]", $body);
1162                                         //$dontincludemedia = true;
1163                                         $type = $oembed_data->type;
1164                                         $footerurl = $expanded_url;
1165                                         $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1166
1167                                         $body = str_replace($url->url, $footerlink, $body);
1168                                 //} elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1169                                 } elseif (($oembed_data->type == "photo") && isset($oembed_data->url)) {
1170                                         $body = str_replace($url->url,
1171                                                         "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1172                                                         $body);
1173                                         //$dontincludemedia = true;
1174                                 } elseif ($oembed_data->type != "link")
1175                                         $body = str_replace($url->url,
1176                                                         "[url=".$expanded_url."]".$expanded_url."[/url]",
1177                                                         $body);
1178                                 else {
1179                                         $img_str = fetch_url($expanded_url, true, $redirects, 4);
1180
1181                                         $tempfile = tempnam(get_temppath(), "cache");
1182                                         file_put_contents($tempfile, $img_str);
1183                                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1184                                         unlink($tempfile);
1185
1186                                         if (substr($mime, 0, 6) == "image/") {
1187                                                 $type = "photo";
1188                                                 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1189                                                 //$dontincludemedia = true;
1190                                         } else {
1191                                                 $type = $oembed_data->type;
1192                                                 $footerurl = $expanded_url;
1193                                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1194
1195                                                 $body = str_replace($url->url, $footerlink, $body);
1196                                         }
1197                                 }
1198                         }
1199                 }
1200
1201                 if ($footerurl != "")
1202                         $footer = add_page_info($footerurl, false, $picture);
1203
1204                 if (($footerlink != "") && (trim($footer) != "")) {
1205                         $removedlink = trim(str_replace($footerlink, "", $body));
1206
1207                         if (($removedlink == "") || strstr($body, $removedlink))
1208                                 $body = $removedlink;
1209
1210                         $body .= $footer;
1211                 }
1212
1213                 if (($footer == "") && ($picture != ""))
1214                         $body .= "\n\n[img]".$picture."[/img]\n";
1215                 elseif (($footer == "") && ($picture == ""))
1216                         $body = add_page_info_to_body($body);
1217
1218                 if ($no_tags)
1219                         return array("body" => $body, "tags" => "", "plain" => $plain);
1220
1221                 $tags_arr = array();
1222
1223                 foreach ($item->entities->hashtags AS $hashtag) {
1224                         $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1225                         $tags_arr["#".$hashtag->text] = $url;
1226                         $body = str_replace("#".$hashtag->text, $url, $body);
1227                 }
1228
1229                 foreach ($item->entities->user_mentions AS $mention) {
1230                         $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1231                         $tags_arr["@".$mention->screen_name] = $url;
1232                         $body = str_replace("@".$mention->screen_name, $url, $body);
1233                 }
1234
1235                 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1236                 $tags = get_tags($body);
1237
1238                 if(count($tags)) {
1239                         foreach($tags as $tag) {
1240                                 if (strstr(trim($tag), " "))
1241                                         continue;
1242
1243                                 if(strpos($tag,'#') === 0) {
1244                                         if(strpos($tag,'[url='))
1245                                                 continue;
1246
1247                                         // don't link tags that are already embedded in links
1248
1249                                         if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1250                                                 continue;
1251                                         if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1252                                                 continue;
1253
1254                                         $basetag = str_replace('_',' ',substr($tag,1));
1255                                         $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1256                                         $body = str_replace($tag,$url,$body);
1257                                         $tags_arr["#".$basetag] = $url;
1258                                         continue;
1259                                 } elseif(strpos($tag,'@') === 0) {
1260                                         if(strpos($tag,'[url='))
1261                                                 continue;
1262
1263                                         $basetag = substr($tag,1);
1264                                         $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1265                                         $body = str_replace($tag,$url,$body);
1266                                         $tags_arr["@".$basetag] = $url;
1267                                 }
1268                         }
1269                 }
1270
1271
1272                 $tags = implode($tags_arr, ",");
1273
1274         }
1275         return array("body" => $body, "tags" => $tags, "plain" => $plain);
1276 }
1277
1278 /**
1279  * @brief Fetch media entities and add media links to the body
1280  *
1281  * @param object $post Twitter object with the post
1282  * @param array $postarray Array of the item that is about to be posted
1283  *
1284  * @return $picture string Returns a a single picture string if it isn't a media post
1285  */
1286 function twitter_media_entities($post, &$postarray) {
1287
1288         // There are no media entities? So we quit.
1289         if (!is_array($post->extended_entities->media)) {
1290                 return "";
1291         }
1292
1293         // When the post links to an external page, we only take one picture.
1294         // We only do this when there is exactly one media.
1295         if ((count($post->entities->urls) > 0) && (count($post->extended_entities->media) == 1)) {
1296                 $picture = "";
1297                 foreach($post->extended_entities->media AS $medium) {
1298                         if (isset($medium->media_url_https)) {
1299                                 $picture = $medium->media_url_https;
1300                                 $postarray['body'] = str_replace($medium->url, "", $postarray['body']);
1301                         }
1302                 }
1303                 return $picture;
1304         }
1305
1306         // This is a pure media post, first search for all media urls
1307         $media = array();
1308         foreach($post->extended_entities->media AS $medium) {
1309                 switch($medium->type) {
1310                         case 'photo':
1311                                 $media[$medium->url] .= "\n[img]".$medium->media_url_https."[/img]";
1312                                 $postarray['object-type'] = ACTIVITY_OBJ_IMAGE;
1313                                 break;
1314                         case 'video':
1315                         case 'animated_gif':
1316                                 $media[$medium->url] .= "\n[img]".$medium->media_url_https."[/img]";
1317                                 $postarray['object-type'] = ACTIVITY_OBJ_VIDEO;
1318                                 if (is_array($medium->video_info->variants)) {
1319                                         $bitrate = 0;
1320                                         // We take the video with the highest bitrate
1321                                         foreach ($medium->video_info->variants AS $variant) {
1322                                                 if (($variant->content_type == "video/mp4") && ($variant->bitrate >= $bitrate)) {
1323                                                         $media[$medium->url] = "\n[video]".$variant->url."[/video]";
1324                                                         $bitrate = $variant->bitrate;
1325                                                 }
1326                                         }
1327                                 }
1328                                 break;
1329                         // The following code will only be activated for test reasons
1330                         //default:
1331                         //      $postarray['body'] .= print_r($medium, true);
1332                 }
1333         }
1334
1335         // Now we replace the media urls.
1336         foreach ($media AS $key => $value) {
1337                 $postarray['body'] = str_replace($key, "\n".$value."\n", $postarray['body']);
1338         }
1339         return "";
1340 }
1341
1342 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact, $noquote) {
1343
1344         $postarray = array();
1345         $postarray['network'] = NETWORK_TWITTER;
1346         $postarray['gravity'] = 0;
1347         $postarray['uid'] = $uid;
1348         $postarray['wall'] = 0;
1349         $postarray['uri'] = "twitter::".$post->id_str;
1350         $postarray['object'] = json_encode($post);
1351
1352         // Don't import our own comments
1353         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1354                         dbesc($postarray['uri']),
1355                         intval($uid)
1356                 );
1357
1358         if (count($r)) {
1359                 logger("Item with extid ".$postarray['uri']." found.", LOGGER_DEBUG);
1360                 return(array());
1361         }
1362
1363         $contactid = 0;
1364
1365         if ($post->in_reply_to_status_id_str != "") {
1366
1367                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1368
1369                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1370                                 dbesc($parent),
1371                                 intval($uid)
1372                         );
1373                 if (count($r)) {
1374                         $postarray['thr-parent'] = $r[0]["uri"];
1375                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1376                         $postarray['parent'] = $r[0]["parent"];
1377                         $postarray['object-type'] = ACTIVITY_OBJ_COMMENT;
1378                 } else {
1379                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1380                                         dbesc($parent),
1381                                         intval($uid)
1382                                 );
1383                         if (count($r)) {
1384                                 $postarray['thr-parent'] = $r[0]['uri'];
1385                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1386                                 $postarray['parent'] = $r[0]['parent'];
1387                                 $postarray['object-type'] = ACTIVITY_OBJ_COMMENT;
1388                         } else {
1389                                 $postarray['thr-parent'] = $postarray['uri'];
1390                                 $postarray['parent-uri'] = $postarray['uri'];
1391                                 $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
1392                         }
1393                 }
1394
1395                 // Is it me?
1396                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1397
1398                 if ($post->user->id_str == $own_id) {
1399                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1400                                 intval($uid));
1401
1402                         if(count($r)) {
1403                                 $contactid = $r[0]["id"];
1404
1405                                 $postarray['owner-name'] =  $r[0]["name"];
1406                                 $postarray['owner-link'] = $r[0]["url"];
1407                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1408                         } else {
1409                                 logger("No self contact for user ".$uid, LOGGER_DEBUG);
1410                                 return(array());
1411                         }
1412                 }
1413                 // Don't create accounts of people who just comment something
1414                 $create_user = false;
1415         } else {
1416                 $postarray['parent-uri'] = $postarray['uri'];
1417                 $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
1418         }
1419
1420         if ($contactid == 0) {
1421                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1422
1423                 $postarray['owner-name'] = $post->user->name;
1424                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1425                 $postarray['owner-avatar'] = twitter_fix_avatar($post->user->profile_image_url_https);
1426         }
1427
1428         if(($contactid == 0) && !$only_existing_contact) {
1429                 $contactid = $self['id'];
1430         } elseif ($contactid <= 0) {
1431                 logger("Contact ID is zero or less than zero.", LOGGER_DEBUG);
1432                 return(array());
1433         }
1434
1435         $postarray['contact-id'] = $contactid;
1436
1437         $postarray['verb'] = ACTIVITY_POST;
1438         $postarray['author-name'] = $postarray['owner-name'];
1439         $postarray['author-link'] = $postarray['owner-link'];
1440         $postarray['author-avatar'] = $postarray['owner-avatar'];
1441         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1442         $postarray['app'] = strip_tags($post->source);
1443
1444         if ($post->user->protected) {
1445                 $postarray['private'] = 1;
1446                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1447         }
1448
1449         if (is_string($post->full_text)) {
1450                 $postarray['body'] = $post->full_text;
1451         } else {
1452                 $postarray['body'] = $post->text;
1453         }
1454
1455         // When the post contains links then use the correct object type
1456         if (count($post->entities->urls) > 0) {
1457                 $postarray['object-type'] = ACTIVITY_OBJ_BOOKMARK;
1458         }
1459
1460         // Search for media links
1461         $picture = twitter_media_entities($post, $postarray);
1462
1463         $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $picture);
1464         $postarray['body'] = $converted["body"];
1465         $postarray['tag'] = $converted["tags"];
1466         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1467         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1468
1469         $statustext = $converted["plain"];
1470
1471         if (is_string($post->place->name)) {
1472                 $postarray["location"] = $post->place->name;
1473         }
1474         if (is_string($post->place->full_name)) {
1475                 $postarray["location"] = $post->place->full_name;
1476         }
1477         if (is_array($post->geo->coordinates)) {
1478                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1479         }
1480         if (is_array($post->coordinates->coordinates)) {
1481                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1482         }
1483         if (is_object($post->retweeted_status)) {
1484                 $retweet = twitter_createpost($a, $uid, $post->retweeted_status, $self, false, false, $noquote);
1485
1486                 $retweet['object'] = $postarray['object'];
1487                 $retweet['private'] = $postarray['private'];
1488                 $retweet['allow_cid'] = $postarray['allow_cid'];
1489                 $retweet['contact-id'] = $postarray['contact-id'];
1490                 $retweet['owner-name'] = $postarray['owner-name'];
1491                 $retweet['owner-link'] = $postarray['owner-link'];
1492                 $retweet['owner-avatar'] = $postarray['owner-avatar'];
1493
1494                 $postarray = $retweet;
1495         }
1496
1497         if (is_object($post->quoted_status) && !$noquote) {
1498                 $quoted = twitter_createpost($a, $uid, $post->quoted_status, $self, false, false, true);
1499
1500                 $postarray['body'] = $statustext;
1501
1502                 $postarray['body'] .= "\n".share_header($quoted['author-name'], $quoted['author-link'], $quoted['author-avatar'], "",
1503                                                         $quoted['created'], $quoted['plink']);
1504
1505                 $postarray['body'] .= $quoted['body'].'[/share]';
1506         }
1507
1508         return($postarray);
1509 }
1510
1511 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1512
1513         // this whole function doesn't seem to work. Needs complete check
1514
1515         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1516                         intval($uid)
1517                 );
1518
1519         if(!count($user))
1520                 return;
1521
1522         // Is it me?
1523         if (link_compare($user[0]["url"], $postarray['author-link']))
1524                 return;
1525
1526         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1527                         intval($uid),
1528                         dbesc("twitter::".$own_id)
1529                 );
1530
1531         if(!count($own_user))
1532                 return;
1533
1534         // Is it me from twitter?
1535         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1536                 return;
1537
1538         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1539                         dbesc($postarray['parent-uri']),
1540                         intval($uid)
1541                         );
1542
1543         if(count($myconv)) {
1544
1545                 foreach($myconv as $conv) {
1546                         // now if we find a match, it means we're in this conversation
1547
1548                         if(!link_compare($conv['author-link'],$user[0]["url"]) && !link_compare($conv['author-link'],$own_user[0]["url"]))
1549                                 continue;
1550
1551                         require_once('include/enotify.php');
1552
1553                         $conv_parent = $conv['parent'];
1554
1555                         notification(array(
1556                                 'type'         => NOTIFY_COMMENT,
1557                                 'notify_flags' => $user[0]['notify-flags'],
1558                                 'language'     => $user[0]['language'],
1559                                 'to_name'      => $user[0]['username'],
1560                                 'to_email'     => $user[0]['email'],
1561                                 'uid'          => $user[0]['uid'],
1562                                 'item'         => $postarray,
1563                                 'link'         => $a->get_baseurl().'/display/'.urlencode(get_item_guid($top_item)),
1564                                 'source_name'  => $postarray['author-name'],
1565                                 'source_link'  => $postarray['author-link'],
1566                                 'source_photo' => $postarray['author-avatar'],
1567                                 'verb'         => ACTIVITY_POST,
1568                                 'otype'        => 'item',
1569                                 'parent'       => $conv_parent,
1570                         ));
1571
1572                         // only send one notification
1573                         break;
1574                 }
1575         }
1576 }
1577
1578 function twitter_fetchparentposts($a, $uid, $post, $connection, $self, $own_id) {
1579         logger("twitter_fetchparentposts: Fetching for user ".$uid." and post ".$post->id_str, LOGGER_DEBUG);
1580
1581         $posts = array();
1582
1583         while ($post->in_reply_to_status_id_str != "") {
1584                 $parameters = array("trim_user" => false, "tweet_mode" => "extended", "id" => $post->in_reply_to_status_id_str);
1585
1586                 $post = $connection->get('statuses/show', $parameters);
1587
1588                 if (!count($post)) {
1589                         logger("twitter_fetchparentposts: Can't fetch post ".$parameters->id, LOGGER_DEBUG);
1590                         break;
1591                 }
1592
1593                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1594                                 dbesc("twitter::".$post->id_str),
1595                                 intval($uid)
1596                         );
1597
1598                 if (count($r))
1599                         break;
1600
1601                 $posts[] = $post;
1602         }
1603
1604         logger("twitter_fetchparentposts: Fetching ".count($posts)." parents", LOGGER_DEBUG);
1605
1606         $posts = array_reverse($posts);
1607
1608         if (count($posts)) {
1609                 foreach ($posts as $post) {
1610                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false, false);
1611
1612                         if (trim($postarray['body']) == "")
1613                                 continue;
1614
1615                         $item = item_store($postarray);
1616                         $postarray["id"] = $item;
1617
1618                         logger('twitter_fetchparentpost: User '.$self["nick"].' posted parent timeline item '.$item);
1619
1620                         if ($item && !function_exists("check_item_notification"))
1621                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1622                 }
1623         }
1624 }
1625
1626 function twitter_fetchhometimeline($a, $uid) {
1627         $ckey    = get_config('twitter', 'consumerkey');
1628         $csecret = get_config('twitter', 'consumersecret');
1629         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1630         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1631         $create_user = get_pconfig($uid, 'twitter', 'create_user');
1632         $mirror_posts = get_pconfig($uid, 'twitter', 'mirror_posts');
1633
1634         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1635
1636         $application_name  = get_config('twitter', 'application_name');
1637
1638         if ($application_name == "")
1639                 $application_name = $a->get_hostname();
1640
1641         require_once('library/twitteroauth.php');
1642         require_once('include/items.php');
1643
1644         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1645
1646         $own_contact = twitter_fetch_own_contact($a, $uid);
1647
1648         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1649                 intval($own_contact),
1650                 intval($uid));
1651
1652         if(count($r)) {
1653                 $own_id = $r[0]["nick"];
1654         } else {
1655                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1656                 return;
1657         }
1658
1659         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1660                 intval($uid));
1661
1662         if(count($r)) {
1663                 $self = $r[0];
1664         } else {
1665                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1666                 return;
1667         }
1668
1669         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1670                 intval($uid));
1671         if(!count($u)) {
1672                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1673                 return;
1674         }
1675
1676         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true, "tweet_mode" => "extended");
1677         //$parameters["count"] = 200;
1678
1679
1680         // Fetching timeline
1681         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1682
1683         $first_time = ($lastid == "");
1684
1685         if ($lastid <> "")
1686                 $parameters["since_id"] = $lastid;
1687
1688         $items = $connection->get('statuses/home_timeline', $parameters);
1689
1690         if (!is_array($items)) {
1691                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1692                 return;
1693         }
1694
1695         $posts = array_reverse($items);
1696
1697         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1698
1699         if (count($posts)) {
1700                 foreach ($posts as $post) {
1701                         if ($post->id_str > $lastid) {
1702                                 $lastid = $post->id_str;
1703                                 set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1704                         }
1705
1706                         if ($first_time)
1707                                 continue;
1708
1709                         if (stristr($post->source, $application_name) && $post->user->screen_name == $own_id) {
1710                                 logger("twitter_fetchhometimeline: Skip previously sended post", LOGGER_DEBUG);
1711                                 continue;
1712                         }
1713
1714                         if ($mirror_posts && $post->user->screen_name == $own_id && $post->in_reply_to_status_id_str == "") {
1715                                 logger("twitter_fetchhometimeline: Skip post that will be mirrored", LOGGER_DEBUG);
1716                                 continue;
1717                         }
1718
1719                         if ($post->in_reply_to_status_id_str != "")
1720                                 twitter_fetchparentposts($a, $uid, $post, $connection, $self, $own_id);
1721
1722                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true, false);
1723
1724                         if (trim($postarray['body']) == "")
1725                                 continue;
1726
1727                         $item = item_store($postarray);
1728                         $postarray["id"] = $item;
1729
1730                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1731
1732                         if ($item && !function_exists("check_item_notification"))
1733                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1734
1735                 }
1736         }
1737         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1738
1739         // Fetching mentions
1740         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1741
1742         $first_time = ($lastid == "");
1743
1744         if ($lastid <> "")
1745                 $parameters["since_id"] = $lastid;
1746
1747         $items = $connection->get('statuses/mentions_timeline', $parameters);
1748
1749         if (!is_array($items)) {
1750                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1751                 return;
1752         }
1753
1754         $posts = array_reverse($items);
1755
1756         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1757
1758         if (count($posts)) {
1759                 foreach ($posts as $post) {
1760                         if ($post->id_str > $lastid)
1761                                 $lastid = $post->id_str;
1762
1763                         if ($first_time)
1764                                 continue;
1765
1766                         if ($post->in_reply_to_status_id_str != "")
1767                                 twitter_fetchparentposts($a, $uid, $post, $connection, $self, $own_id);
1768
1769                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false, false);
1770
1771                         if (trim($postarray['body']) == "")
1772                                 continue;
1773
1774                         $item = item_store($postarray);
1775                         $postarray["id"] = $item;
1776
1777                         if ($item && function_exists("check_item_notification"))
1778                                 check_item_notification($item, $uid, NOTIFY_TAGSELF);
1779
1780                         if (!isset($postarray["parent"]) || ($postarray["parent"] == 0))
1781                                 $postarray["parent"] = $item;
1782
1783                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1784
1785                         if ($item == 0) {
1786                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1787                                         dbesc($postarray['uri']),
1788                                         intval($uid)
1789                                 );
1790                                 if (count($r)) {
1791                                         $item = $r[0]['id'];
1792                                         $parent_id = $r[0]['parent'];
1793                                 }
1794                         } else
1795                                 $parent_id = $postarray['parent'];
1796
1797                         if (($item != 0) && !function_exists("check_item_notification")) {
1798                                 require_once('include/enotify.php');
1799                                 notification(array(
1800                                         'type'         => NOTIFY_TAGSELF,
1801                                         'notify_flags' => $u[0]['notify-flags'],
1802                                         'language'     => $u[0]['language'],
1803                                         'to_name'      => $u[0]['username'],
1804                                         'to_email'     => $u[0]['email'],
1805                                         'uid'          => $u[0]['uid'],
1806                                         'item'         => $postarray,
1807                                         'link'         => $a->get_baseurl().'/display/'.urlencode(get_item_guid($item)),
1808                                         'source_name'  => $postarray['author-name'],
1809                                         'source_link'  => $postarray['author-link'],
1810                                         'source_photo' => $postarray['author-avatar'],
1811                                         'verb'         => ACTIVITY_TAG,
1812                                         'otype'        => 'item',
1813                                         'parent'       => $parent_id
1814                                 ));
1815                         }
1816                 }
1817         }
1818
1819         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1820 }
1821
1822 function twitter_fetch_own_contact($a, $uid) {
1823         $ckey    = get_config('twitter', 'consumerkey');
1824         $csecret = get_config('twitter', 'consumersecret');
1825         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1826         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1827
1828         $own_id = get_pconfig($uid, 'twitter', 'own_id');
1829
1830         $contact_id = 0;
1831
1832         if ($own_id == "") {
1833                 require_once('library/twitteroauth.php');
1834
1835                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1836
1837                 // Fetching user data
1838                 $user = $connection->get('account/verify_credentials');
1839
1840                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1841
1842                 $contact_id = twitter_fetch_contact($uid, $user, true);
1843
1844         } else {
1845                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1846                         intval($uid), dbesc("twitter::".$own_id));
1847                 if(count($r))
1848                         $contact_id = $r[0]["id"];
1849                 else
1850                         del_pconfig($uid, 'twitter', 'own_id');
1851
1852         }
1853
1854         return($contact_id);
1855 }
1856
1857 function twitter_is_retweet($a, $uid, $body) {
1858         $body = trim($body);
1859
1860         // Skip if it isn't a pure repeated messages
1861         // Does it start with a share?
1862         if (strpos($body, "[share") > 0)
1863                 return(false);
1864
1865         // Does it end with a share?
1866         if (strlen($body) > (strrpos($body, "[/share]") + 8))
1867                 return(false);
1868
1869         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1870         // Skip if there is no shared message in there
1871         if ($body == $attributes)
1872                 return(false);
1873
1874         $link = "";
1875         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1876         if ($matches[1] != "")
1877                 $link = $matches[1];
1878
1879         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1880         if ($matches[1] != "")
1881                 $link = $matches[1];
1882
1883         $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1884         if ($id == $link)
1885                 return(false);
1886
1887         logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1888
1889         $ckey    = get_config('twitter', 'consumerkey');
1890         $csecret = get_config('twitter', 'consumersecret');
1891         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1892         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1893
1894         require_once('library/twitteroauth.php');
1895         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1896
1897         $result = $connection->post('statuses/retweet/'.$id);
1898
1899         logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1900
1901         return(!isset($result->errors));
1902 }
1903 ?>