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