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