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