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