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