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