]> git.mxchange.org Git - friendica-addons.git/blob - twitter/twitter.php
Facebook: First changes to make the connector maybe work with the second version...
[friendica-addons.git] / twitter / twitter.php
1 <?php
2 /**
3  * Name: Twitter Connector
4  * Description: Relay public postings to a connected Twitter account
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 define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes
64
65 function twitter_install() {
66         //  we need some hooks, for the configuration and for sending tweets
67         register_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings');
68         register_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
69         register_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
70         register_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
71         register_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
72         register_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
73         register_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
74         register_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
75         register_hook('expire', 'addon/twitter/twitter.php', 'twitter_expire');
76         logger("installed twitter");
77 }
78
79
80 function twitter_uninstall() {
81         unregister_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings');
82         unregister_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
83         unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
84         unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
85         unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
86         unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
87         unregister_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
88         unregister_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
89         unregister_hook('expire', 'addon/twitter/twitter.php', 'twitter_expire');
90
91         // old setting - remove only
92         unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook');
93         unregister_hook('plugin_settings', 'addon/twitter/twitter.php', 'twitter_settings');
94         unregister_hook('plugin_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
95
96 }
97
98 function twitter_follow($a, &$contact) {
99
100         logger("twitter_follow: Check if contact is twitter contact. ".$contact["url"], LOGGER_DEBUG);
101
102         if (!strstr($contact["url"], "://twitter.com") AND !strstr($contact["url"], "@twitter.com"))
103                 return;
104
105         // contact seems to be a twitter contact, so continue
106         $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $contact["url"]);
107         $nickname = str_replace("@twitter.com", "", $nickname);
108
109         $uid = $a->user["uid"];
110
111         $ckey    = get_config('twitter', 'consumerkey');
112         $csecret = get_config('twitter', 'consumersecret');
113         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
114         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
115
116         require_once("addon/twitter/codebird.php");
117
118         $cb = \Codebird\Codebird::getInstance();
119         $cb->setConsumerKey($ckey, $csecret);
120         $cb->setToken($otoken, $osecret);
121
122         $parameters = array();
123         $parameters["screen_name"] = $nickname;
124
125         $user = $cb->friendships_create($parameters);
126
127         twitter_fetchuser($a, $uid, $nickname);
128
129         $r = q("SELECT name,nick,url,addr,batch,notify,poll,request,confirm,poco,photo,priority,network,alias,pubkey
130                 FROM `contact` WHERE `uid` = %d AND `nick` = '%s'",
131                                 intval($uid),
132                                 dbesc($nickname));
133         if (count($r))
134                 $contact["contact"] = $r[0];
135 }
136
137 function twitter_jot_nets(&$a,&$b) {
138         if(! local_user())
139                 return;
140
141         $tw_post = get_pconfig(local_user(),'twitter','post');
142         if(intval($tw_post) == 1) {
143                 $tw_defpost = get_pconfig(local_user(),'twitter','post_by_default');
144                 $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : '');
145                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="twitter_enable"' . $selected . ' value="1" /> ' 
146                         . t('Post to Twitter') . '</div>';
147         }
148 }
149
150 function twitter_settings_post ($a,$post) {
151         if(! local_user())
152                 return;
153         // don't check twitter settings if twitter submit button is not clicked
154         if (!x($_POST,'twitter-submit'))
155                 return;
156
157         if (isset($_POST['twitter-disconnect'])) {
158                 /***
159                  * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
160                  * from the user configuration
161                  */
162                 del_pconfig(local_user(), 'twitter', 'consumerkey');
163                 del_pconfig(local_user(), 'twitter', 'consumersecret');
164                 del_pconfig(local_user(), 'twitter', 'oauthtoken');
165                 del_pconfig(local_user(), 'twitter', 'oauthsecret');
166                 del_pconfig(local_user(), 'twitter', 'post');
167                 del_pconfig(local_user(), 'twitter', 'post_by_default');
168                 del_pconfig(local_user(), 'twitter', 'lastid');
169                 del_pconfig(local_user(), 'twitter', 'mirror_posts');
170                 del_pconfig(local_user(), 'twitter', 'import');
171                 del_pconfig(local_user(), 'twitter', 'create_user');
172                 del_pconfig(local_user(), 'twitter', 'own_id');
173         } else {
174         if (isset($_POST['twitter-pin'])) {
175                 //  if the user supplied us with a PIN from Twitter, let the magic of OAuth happen
176                 logger('got a Twitter PIN');
177                 require_once('library/twitteroauth.php');
178                 $ckey    = get_config('twitter', 'consumerkey');
179                 $csecret = get_config('twitter', 'consumersecret');
180                 //  the token and secret for which the PIN was generated were hidden in the settings
181                 //  form as token and token2, we need a new connection to Twitter using these token
182                 //  and secret to request a Access Token with the PIN
183                 $connection = new TwitterOAuth($ckey, $csecret, $_POST['twitter-token'], $_POST['twitter-token2']);
184                 $token   = $connection->getAccessToken( $_POST['twitter-pin'] );
185                 //  ok, now that we have the Access Token, save them in the user config
186                 set_pconfig(local_user(),'twitter', 'oauthtoken',  $token['oauth_token']);
187                 set_pconfig(local_user(),'twitter', 'oauthsecret', $token['oauth_token_secret']);
188                 set_pconfig(local_user(),'twitter', 'post', 1);
189                 //  reload the Addon Settings page, if we don't do it see Bug #42
190                 goaway($a->get_baseurl().'/settings/connectors');
191         } else {
192                 //  if no PIN is supplied in the POST variables, the user has changed the setting
193                 //  to post a tweet for every new __public__ posting to the wall
194                 set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable']));
195                 set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default']));
196                 set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
197                 set_pconfig(local_user(), 'twitter', 'import', intval($_POST['twitter-import']));
198                 set_pconfig(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user']));
199
200                 if (!intval($_POST['twitter-mirror']))
201                         del_pconfig(local_user(),'twitter','lastid');
202
203                 info(t('Twitter settings updated.') . EOL);
204         }}
205 }
206 function twitter_settings(&$a,&$s) {
207         if(! local_user())
208                 return;
209         $a->page['htmlhead'] .= '<link rel="stylesheet"  type="text/css" href="' . $a->get_baseurl() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
210         /***
211          * 1) Check that we have global consumer key & secret
212          * 2) If no OAuthtoken & stuff is present, generate button to get some
213          * 3) Checkbox for "Send public notices (140 chars only)
214          */
215         $ckey    = get_config('twitter', 'consumerkey' );
216         $csecret = get_config('twitter', 'consumersecret' );
217         $otoken  = get_pconfig(local_user(), 'twitter', 'oauthtoken'  );
218         $osecret = get_pconfig(local_user(), 'twitter', 'oauthsecret' );
219         $enabled = get_pconfig(local_user(), 'twitter', 'post');
220         $checked = (($enabled) ? ' checked="checked" ' : '');
221         $defenabled = get_pconfig(local_user(),'twitter','post_by_default');
222         $defchecked = (($defenabled) ? ' checked="checked" ' : '');
223         $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts');
224         $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : '');
225         $importenabled = get_pconfig(local_user(),'twitter','import');
226         $importchecked = (($importenabled) ? ' checked="checked" ' : '');
227         $create_userenabled = get_pconfig(local_user(),'twitter','create_user');
228         $create_userchecked = (($create_userenabled) ? ' checked="checked" ' : '');
229
230         $css = (($enabled) ? '' : '-disabled');
231
232         $s .= '<span id="settings_twitter_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
233         $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
234         $s .= '</span>';
235         $s .= '<div id="settings_twitter_expanded" class="settings-block" style="display: none;">';
236         $s .= '<span class="fakelink" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
237         $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
238         $s .= '</span>';
239
240         if ( (!$ckey) && (!$csecret) ) {
241                 /***
242                  * no global consumer keys
243                  * display warning and skip personal config
244                  */
245                 $s .= '<p>'. t('No consumer key pair for Twitter found. Please contact your site administrator.') .'</p>';
246         } else {
247                 /***
248                  * ok we have a consumer key pair now look into the OAuth stuff
249                  */
250                 if ( (!$otoken) && (!$osecret) ) {
251                         /***
252                          * the user has not yet connected the account to twitter...
253                          * get a temporary OAuth key/secret pair and display a button with
254                          * which the user can request a PIN to connect the account to a
255                          * account at Twitter.
256                          */
257                         require_once('library/twitteroauth.php');
258                         $connection = new TwitterOAuth($ckey, $csecret);
259                         $request_token = $connection->getRequestToken();
260                         $token = $request_token['oauth_token'];
261                         /***
262                          *  make some nice form
263                          */
264                         $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>';
265                         $s .= '<a href="'.$connection->getAuthorizeURL($token).'" target="_twitter"><img src="addon/twitter/lighter.png" alt="'.t('Log in with Twitter').'"></a>';
266                         $s .= '<div id="twitter-pin-wrapper">';
267                         $s .= '<label id="twitter-pin-label" for="twitter-pin">'. t('Copy the PIN from Twitter here') .'</label>';
268                         $s .= '<input id="twitter-pin" type="text" name="twitter-pin" />';
269                         $s .= '<input id="twitter-token" type="hidden" name="twitter-token" value="'.$token.'" />';
270                         $s .= '<input id="twitter-token2" type="hidden" name="twitter-token2" value="'.$request_token['oauth_token_secret'].'" />';
271             $s .= '</div><div class="clear"></div>';
272             $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
273                 } else {
274                         /***
275                          *  we have an OAuth key / secret pair for the user
276                          *  so let's give a chance to disable the postings to Twitter
277                          */
278                         require_once('library/twitteroauth.php');
279                         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
280                         $details = $connection->get('account/verify_credentials');
281                         $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>';
282                         $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>';
283                         if ($a->user['hidewall']) {
284                             $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>';
285                         }
286                         $s .= '<div id="twitter-enable-wrapper">';
287                         $s .= '<label id="twitter-enable-label" for="twitter-checkbox">'. t('Allow posting to Twitter'). '</label>';
288                         $s .= '<input id="twitter-checkbox" type="checkbox" name="twitter-enable" value="1" ' . $checked . '/>';
289                         $s .= '<div class="clear"></div>';
290                         $s .= '<label id="twitter-default-label" for="twitter-default">'. t('Send public postings to Twitter by default') .'</label>';
291                         $s .= '<input id="twitter-default" type="checkbox" name="twitter-default" value="1" ' . $defchecked . '/>';
292                         $s .= '<div class="clear"></div>';
293
294                         $s .= '<label id="twitter-mirror-label" for="twitter-mirror">'.t('Mirror all posts from twitter that are no replies').'</label>';
295                         $s .= '<input id="twitter-mirror" type="checkbox" name="twitter-mirror" value="1" '. $mirrorchecked . '/>';
296                         $s .= '<div class="clear"></div>';
297                         $s .= '</div>';
298
299                         $s .= '<label id="twitter-import-label" for="twitter-import">'.t('Import the remote timeline').'</label>';
300                         $s .= '<input id="twitter-import" type="checkbox" name="twitter-import" value="1" '. $importchecked . '/>';
301                         $s .= '<div class="clear"></div>';
302
303                         $s .= '<label id="twitter-create_user-label" for="twitter-create_user">'.t('Automatically create contacts').'</label>';
304                         $s .= '<input id="twitter-create_user" type="checkbox" name="twitter-create_user" value="1" '. $create_userchecked . '/>';
305                         $s .= '<div class="clear"></div>';
306
307                         $s .= '<div id="twitter-disconnect-wrapper">';
308                         $s .= '<label id="twitter-disconnect-label" for="twitter-disconnect">'. t('Clear OAuth configuration') .'</label>';
309                         $s .= '<input id="twitter-disconnect" type="checkbox" name="twitter-disconnect" value="1" />';
310                         $s .= '</div><div class="clear"></div>';
311                         $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>'; 
312                 }
313         }
314         $s .= '</div><div class="clear"></div>';
315 }
316
317
318 function twitter_post_local(&$a,&$b) {
319
320         if($b['edit'])
321                 return;
322
323         if((local_user()) && (local_user() == $b['uid']) && (! $b['private']) && (! $b['parent']) ) {
324
325                 $twitter_post = intval(get_pconfig(local_user(),'twitter','post'));
326                 $twitter_enable = (($twitter_post && x($_REQUEST,'twitter_enable')) ? intval($_REQUEST['twitter_enable']) : 0);
327
328                 // if API is used, default to the chosen settings
329                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'twitter','post_by_default')))
330                         $twitter_enable = 1;
331
332         if(! $twitter_enable)
333             return;
334
335         if(strlen($b['postopts']))
336             $b['postopts'] .= ',';
337         $b['postopts'] .= 'twitter';
338         }
339 }
340
341 function twitter_action($a, $uid, $pid, $action) {
342
343         $ckey    = get_config('twitter', 'consumerkey');
344         $csecret = get_config('twitter', 'consumersecret');
345         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
346         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
347
348         require_once("addon/twitter/codebird.php");
349
350         $cb = \Codebird\Codebird::getInstance();
351         $cb->setConsumerKey($ckey, $csecret);
352         $cb->setToken($otoken, $osecret);
353
354         $post = array('id' => $pid);
355
356         logger("twitter_action '".$action."' ID: ".$pid." data: " . print_r($post, true), LOGGER_DATA);
357
358         switch ($action) {
359                 case "delete":
360                         $result = $cb->statuses_destroy($post);
361                         break;
362                 case "like":
363                         $result = $cb->favorites_create($post);
364                         break;
365                 case "unlike":
366                         $result = $cb->favorites_destroy($post);
367                         break;
368         }
369         logger("twitter_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
370 }
371
372 function twitter_post_hook(&$a,&$b) {
373
374         /**
375          * Post to Twitter
376          */
377
378         require_once("include/network.php");
379
380         if (!get_pconfig($b["uid"],'twitter','import')) {
381                 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
382                         return;
383         }
384
385         if($b['parent'] != $b['id']) {
386                 logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
387
388                 // Looking if its a reply to a twitter post
389                 if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) {
390                         logger("twitter_post_hook: no twitter post ".$b["parent"]);
391                         return;
392                 }
393
394                 $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
395                         dbesc($b["thr-parent"]),
396                         intval($b["uid"]));
397
398                 if(!count($r)) {
399                         logger("twitter_post_hook: no parent found ".$b["thr-parent"]);
400                         return;
401                 } else {
402                         $iscomment = true;
403                         $orig_post = $r[0];
404                 }
405
406                 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
407                 $nickname = "@[url=".$orig_post["author-link"]."]".$nickname."[/url]";
408
409                 logger("twitter_post_hook: comparing ".$nickname." with ".$b["body"], LOGGER_DEBUG);
410                 if (strpos($b["body"], $nickname) === false)
411                         $b["body"] = $nickname." ".$b["body"];
412
413                 logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA);
414         } else {
415                 $iscomment = false;
416
417                 if($b['private'] OR !strstr($b['postopts'],'twitter'))
418                         return;
419         }
420
421         if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
422                 twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
423
424         if($b['verb'] == ACTIVITY_LIKE) {
425                 logger("twitter_post_hook: parameter 2 ".substr($b["thr-parent"], 9), LOGGER_DEBUG);
426                 if ($b['deleted'])
427                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
428                 else
429                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like");
430                 return;
431         }
432
433         if($b['deleted'] || ($b['created'] !== $b['edited']))
434                 return;
435
436         // if post comes from twitter don't send it back
437         if($b['app'] == "Twitter")
438                 return;
439
440         logger('twitter post invoked');
441
442
443         load_pconfig($b['uid'], 'twitter');
444
445         $ckey    = get_config('twitter', 'consumerkey');
446         $csecret = get_config('twitter', 'consumersecret');
447         $otoken  = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
448         $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
449
450         if($ckey && $csecret && $otoken && $osecret) {
451                 logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
452
453                 // If it's a repeated message from twitter then do a native retweet and exit
454                 if (twitter_is_retweet($a, $b['uid'], $b['body']))
455                         return;
456
457                 require_once('library/twitteroauth.php');
458                 require_once('include/bbcode.php');
459                 $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
460
461                 $max_char = 140;
462                 require_once("include/plaintext.php");
463                 $msgarr = plaintext($a, $b, $max_char, true);
464                 $msg = $msgarr["text"];
465
466                 if (($msg == "") AND isset($msgarr["title"]))
467                         $msg = shortenmsg($msgarr["title"], $max_char - 50);
468
469                 $image = "";
470
471                 if (isset($msgarr["url"]))
472                         $msg .= "\n".$msgarr["url"];
473                 elseif (isset($msgarr["image"]))
474                         $image = $msgarr["image"];
475
476                 // and now tweet it :-)
477                 if(strlen($msg) and ($image != "")) {
478                         $img_str = fetch_url($image);
479
480                         $tempfile = tempnam(get_config("system","temppath"), "cache");
481                         file_put_contents($tempfile, $img_str);
482
483                         // Twitter had changed something so that the old library doesn't work anymore
484                         // so we are using a new library for twitter
485                         // To-Do:
486                         // Switching completely to this library with all functions
487                         require_once("addon/twitter/codebird.php");
488
489                         $cb = \Codebird\Codebird::getInstance();
490                         $cb->setConsumerKey($ckey, $csecret);
491                         $cb->setToken($otoken, $osecret);
492
493                         $post = array('status' => $msg, 'media[]' => $tempfile);
494
495                         if ($iscomment)
496                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
497
498                         $result = $cb->statuses_updateWithMedia($post);
499                         unlink($tempfile);
500
501                         logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG);
502                         if ($result->errors OR $result->error) {
503                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
504
505                                 // Workaround: Remove the picture link so that the post can be reposted without it
506                                 $msg .= " ".$image;
507                                 $image = "";
508                         } elseif ($iscomment) {
509                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
510                                 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
511                                         dbesc("twitter::".$result->id_str),
512                                         dbesc($result->text),
513                                         intval($b['id'])
514                                 );
515                         }
516                 }
517
518                 if(strlen($msg) and ($image == "")) {
519                         $url = 'statuses/update';
520                         $post = array('status' => $msg);
521
522                         if ($iscomment)
523                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
524
525                         $result = $tweet->post($url, $post);
526                         logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
527                         if ($result->errors) {
528                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
529
530                                 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
531                                 if (count($r))
532                                         $a->contact = $r[0]["id"];
533
534                                 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
535                                 require_once('include/queue_fn.php');
536                                 add_to_queue($a->contact,NETWORK_TWITTER,$s);
537                                 notice(t('Twitter post failed. Queued for retry.').EOL);
538                         } elseif ($iscomment) {
539                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
540                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
541                                         dbesc("twitter::".$result->id_str),
542                                         intval($b['id'])
543                                 );
544                                 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
545                                 //      dbesc("twitter::".$result->id_str),
546                                 //      dbesc($result->text),
547                                 //      intval($b['id'])
548                                 //);
549                         }
550                 }
551         }
552 }
553
554 function twitter_plugin_admin_post(&$a){
555         $consumerkey    =       ((x($_POST,'consumerkey'))              ? notags(trim($_POST['consumerkey']))   : '');
556         $consumersecret =       ((x($_POST,'consumersecret'))   ? notags(trim($_POST['consumersecret'])): '');
557         $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
558         set_config('twitter','consumerkey',$consumerkey);
559         set_config('twitter','consumersecret',$consumersecret);
560         set_config('twitter','application_name',$applicationname);
561         info( t('Settings updated.'). EOL );
562 }
563 function twitter_plugin_admin(&$a, &$o){
564         $t = get_markup_template( "admin.tpl", "addon/twitter/" );
565
566         $o = replace_macros($t, array(
567                 '$submit' => t('Save Settings'),
568                                                                 // name, label, value, help, [extra values]
569                 '$consumerkey' => array('consumerkey', t('Consumer key'),  get_config('twitter', 'consumerkey' ), ''),
570                 '$consumersecret' => array('consumersecret', t('Consumer secret'),  get_config('twitter', 'consumersecret' ), ''),
571                 '$applicationname' => array('applicationname', t('Name of the Twitter Application'), get_config('twitter','application_name'),t('set this to avoid mirroring postings from ~friendica back to ~friendica'))
572         ));
573 }
574
575 function twitter_cron($a,$b) {
576         $last = get_config('twitter','last_poll');
577
578         $poll_interval = intval(get_config('twitter','poll_interval'));
579         if(! $poll_interval)
580                 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
581
582         if($last) {
583                 $next = $last + ($poll_interval * 60);
584                 if($next > time()) {
585                         logger('twitter: poll intervall not reached');
586                         return;
587                 }
588         }
589         logger('twitter: cron_start');
590
591         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND()");
592         if(count($r)) {
593                 foreach($r as $rr) {
594                         logger('twitter: fetching for user '.$rr['uid']);
595                         twitter_fetchtimeline($a, $rr['uid']);
596                 }
597         }
598
599
600         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
601         if(count($r)) {
602                 foreach($r as $rr) {
603                         logger('twitter: importing timeline from user '.$rr['uid']);
604                         twitter_fetchhometimeline($a, $rr["uid"]);
605
606 /*
607                         // To-Do
608                         // check for new contacts once a day
609                         $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
610                         if($last_contact_check)
611                                 $next_contact_check = $last_contact_check + 86400;
612                         else
613                                 $next_contact_check = 0;
614
615                         if($next_contact_check <= time()) {
616                                 pumpio_getallusers($a, $rr["uid"]);
617                                 set_pconfig($rr['uid'],'pumpio','contact_check',time());
618                         }
619 */
620
621                 }
622         }
623
624         logger('twitter: cron_end');
625
626         set_config('twitter','last_poll', time());
627 }
628
629 function twitter_expire($a,$b) {
630
631         $days = get_config('twitter', 'expire');
632
633         if ($days == 0)
634                 return;
635
636         $r = q("DELETE FROM `item` WHERE `deleted` AND `network` = '%s'", dbesc(NETWORK_TWITTER));
637
638         require_once("include/items.php");
639
640         logger('twitter_expire: expire_start');
641
642         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
643         if(count($r)) {
644                 foreach($r as $rr) {
645                         logger('twitter_expire: user '.$rr['uid']);
646                         item_expire($rr['uid'], $days, NETWORK_TWITTER, true);
647                 }
648         }
649
650         logger('twitter_expire: expire_end');
651 }
652
653 function twitter_fetchtimeline($a, $uid) {
654         $ckey    = get_config('twitter', 'consumerkey');
655         $csecret = get_config('twitter', 'consumersecret');
656         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
657         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
658         $lastid  = get_pconfig($uid, 'twitter', 'lastid');
659
660         $application_name  = get_config('twitter', 'application_name');
661
662         if ($application_name == "")
663                 $application_name = $a->get_hostname();
664
665         $has_picture = false;
666
667         require_once('mod/item.php');
668         require_once('include/items.php');
669
670         require_once('library/twitteroauth.php');
671         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
672
673         $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
674
675         $first_time = ($lastid == "");
676
677         if ($lastid <> "")
678                 $parameters["since_id"] = $lastid;
679
680         $items = $connection->get('statuses/user_timeline', $parameters);
681
682         if (!is_array($items))
683                 return;
684
685         $posts = array_reverse($items);
686
687         if (count($posts)) {
688             foreach ($posts as $post) {
689                 if ($post->id_str > $lastid)
690                         $lastid = $post->id_str;
691
692                 if ($first_time)
693                         continue;
694
695                 if (!strpos($post->source, $application_name)) {
696                         $_SESSION["authenticated"] = true;
697                         $_SESSION["uid"] = $uid;
698
699                         unset($_REQUEST);
700                         $_REQUEST["type"] = "wall";
701                         $_REQUEST["api_source"] = true;
702                         $_REQUEST["profile_uid"] = $uid;
703                         $_REQUEST["source"] = "Twitter";
704
705                         //$_REQUEST["date"] = $post->created_at;
706
707                         $_REQUEST["title"] = "";
708
709                         if (is_object($post->retweeted_status)) {
710
711                                 $_REQUEST['body'] = $post->retweeted_status->text;
712
713                                 // media
714                                 if (is_array($post->retweeted_status->entities->media)) {
715                                         foreach($post->retweeted_status->entities->media AS $media) {
716                                                 switch($media->type) {
717                                                         case 'photo':
718                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
719                                                                 $has_picture = true;
720                                                                 break;
721                                                 }
722                                         }
723                                 }
724
725                                 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $has_picture);
726                                 $_REQUEST['body'] = $converted["body"];
727
728                                 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
729                                         "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
730                                         "' avatar='".$post->retweeted_status->user->profile_image_url_https.
731                                         "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
732                                         $_REQUEST['body'];
733                                 $_REQUEST['body'] .= "[/share]";
734                         } else {
735                                 $_REQUEST["body"] = $post->text;
736
737                                 if (is_array($post->entities->media)) {
738                                         foreach($post->entities->media AS $media) {
739                                                 switch($media->type) {
740                                                         case 'photo':
741                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
742                                                                 $has_picture = true;
743                                                                 break;
744                                                 }
745                                         }
746                                 }
747
748                                 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $has_picture);
749                                 $_REQUEST['body'] = $converted["body"];
750                         }
751
752                         if (is_string($post->place->name))
753                                 $_REQUEST["location"] = $post->place->name;
754
755                         if (is_string($post->place->full_name))
756                                 $_REQUEST["location"] = $post->place->full_name;
757
758                         if (is_array($post->geo->coordinates))
759                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
760
761                         if (is_array($post->coordinates->coordinates))
762                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
763
764                         //print_r($_REQUEST);
765                         logger('twitter: posting for user '.$uid);
766
767 //                      require_once('mod/item.php');
768
769                         item_post($a);
770                 }
771             }
772         }
773         set_pconfig($uid, 'twitter', 'lastid', $lastid);
774 }
775
776 function twitter_queue_hook(&$a,&$b) {
777
778         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
779                 dbesc(NETWORK_TWITTER)
780                 );
781         if(! count($qi))
782                 return;
783
784         require_once('include/queue_fn.php');
785
786         foreach($qi as $x) {
787                 if($x['network'] !== NETWORK_TWITTER)
788                         continue;
789
790                 logger('twitter_queue: run');
791
792                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
793                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
794                         intval($x['cid'])
795                 );
796                 if(! count($r))
797                         continue;
798
799                 $user = $r[0];
800
801                 $ckey    = get_config('twitter', 'consumerkey');
802                 $csecret = get_config('twitter', 'consumersecret');
803                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
804                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
805
806                 $success = false;
807
808                 if ($ckey AND $csecret AND $otoken AND $osecret) {
809
810                         logger('twitter_queue: able to post');
811
812                         $z = unserialize($x['content']);
813
814                         require_once("addon/twitter/codebird.php");
815
816                         $cb = \Codebird\Codebird::getInstance();
817                         $cb->setConsumerKey($ckey, $csecret);
818                         $cb->setToken($otoken, $osecret);
819
820                         if ($z['url'] == "statuses/update")
821                                 $result = $cb->statuses_update($z['post']);
822
823                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
824
825                         if ($result->errors)
826                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
827                         else {
828                                 $success = true;
829                                 remove_queue_item($x['id']);
830                         }
831                 } else
832                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
833
834                 if (!$success) {
835                         logger('twitter_queue: delayed');
836                         update_queue_time($x['id']);
837                 }
838         }
839 }
840
841 function twitter_fetch_contact($uid, $contact, $create_user) {
842
843         // Check if the unique contact is existing
844         // To-Do: only update once a while
845          $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
846                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
847
848         if (count($r) == 0)
849                 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
850                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
851                         dbesc($contact->name),
852                         dbesc($contact->screen_name),
853                         dbesc($contact->profile_image_url_https));
854         else
855                 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
856                         dbesc($contact->name),
857                         dbesc($contact->screen_name),
858                         dbesc($contact->profile_image_url_https),
859                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
860
861         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
862                 intval($uid), dbesc("twitter::".$contact->id_str));
863
864         if(!count($r) AND !$create_user)
865                 return(0);
866
867         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
868                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
869                 return(-1);
870         }
871
872         if(!count($r)) {
873                 // create contact record
874                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
875                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
876                                         `writable`, `blocked`, `readonly`, `pending` )
877                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
878                         intval($uid),
879                         dbesc(datetime_convert()),
880                         dbesc("https://twitter.com/".$contact->screen_name),
881                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
882                         dbesc($contact->screen_name."@twitter.com"),
883                         dbesc("twitter::".$contact->id_str),
884                         dbesc(''),
885                         dbesc("twitter::".$contact->id_str),
886                         dbesc($contact->name),
887                         dbesc($contact->screen_name),
888                         dbesc($contact->profile_image_url_https),
889                         dbesc(NETWORK_TWITTER),
890                         intval(CONTACT_IS_FRIEND),
891                         intval(1),
892                         intval(1)
893                 );
894
895                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
896                         dbesc("twitter::".$contact->id_str),
897                         intval($uid)
898                         );
899
900                 if(! count($r))
901                         return(false);
902
903                 $contact_id  = $r[0]['id'];
904
905                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
906                         intval($uid)
907                 );
908
909                 if($g && intval($g[0]['def_gid'])) {
910                         require_once('include/group.php');
911                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
912                 }
913
914                 require_once("Photo.php");
915
916                 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
917
918                 q("UPDATE `contact` SET `photo` = '%s',
919                                         `thumb` = '%s',
920                                         `micro` = '%s',
921                                         `name-date` = '%s',
922                                         `uri-date` = '%s',
923                                         `avatar-date` = '%s'
924                                 WHERE `id` = %d",
925                         dbesc($photos[0]),
926                         dbesc($photos[1]),
927                         dbesc($photos[2]),
928                         dbesc(datetime_convert()),
929                         dbesc(datetime_convert()),
930                         dbesc(datetime_convert()),
931                         intval($contact_id)
932                 );
933
934         } else {
935                 // update profile photos once every two weeks as we have no notification of when they change.
936
937                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
938                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
939
940                 // check that we have all the photos, this has been known to fail on occasion
941
942                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
943
944                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
945
946                         require_once("Photo.php");
947
948                         $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
949
950                         q("UPDATE `contact` SET `photo` = '%s',
951                                                 `thumb` = '%s',
952                                                 `micro` = '%s',
953                                                 `name-date` = '%s',
954                                                 `uri-date` = '%s',
955                                                 `avatar-date` = '%s',
956                                                 `url` = '%s',
957                                                 `nurl` = '%s',
958                                                 `addr` = '%s',
959                                                 `name` = '%s',
960                                                 `nick` = '%s'
961                                         WHERE `id` = %d",
962                                 dbesc($photos[0]),
963                                 dbesc($photos[1]),
964                                 dbesc($photos[2]),
965                                 dbesc(datetime_convert()),
966                                 dbesc(datetime_convert()),
967                                 dbesc(datetime_convert()),
968                                 dbesc("https://twitter.com/".$contact->screen_name),
969                                 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
970                                 dbesc($contact->screen_name."@twitter.com"),
971                                 dbesc($contact->name),
972                                 dbesc($contact->screen_name),
973                                 intval($r[0]['id'])
974                         );
975                 }
976         }
977
978         return($r[0]["id"]);
979 }
980
981 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
982         $ckey    = get_config('twitter', 'consumerkey');
983         $csecret = get_config('twitter', 'consumersecret');
984         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
985         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
986
987         require_once("addon/twitter/codebird.php");
988
989         $cb = \Codebird\Codebird::getInstance();
990         $cb->setConsumerKey($ckey, $csecret);
991         $cb->setToken($otoken, $osecret);
992
993         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
994                 intval($uid));
995
996         if(count($r)) {
997                 $self = $r[0];
998         } else
999                 return;
1000
1001         $parameters = array();
1002
1003         if ($screen_name != "")
1004                 $parameters["screen_name"] = $screen_name;
1005
1006         if ($user_id != "")
1007                 $parameters["user_id"] = $user_id;
1008
1009         // Fetching user data
1010         $user = $cb->users_show($parameters);
1011
1012         if (!is_object($user))
1013                 return;
1014
1015         $contact_id = twitter_fetch_contact($uid, $user, true);
1016
1017         return $contact_id;
1018 }
1019
1020 function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontincludemedia) {
1021         require_once("include/oembed.php");
1022         require_once("include/network.php");
1023
1024         $tags = "";
1025
1026         if (isset($item->entities->urls)) {
1027                 $type = "";
1028                 $footerurl = "";
1029                 $footerlink = "";
1030                 $footer = "";
1031
1032                 foreach ($item->entities->urls AS $url) {
1033                         if ($url->url AND $url->expanded_url AND $url->display_url) {
1034
1035                                 $expanded_url = original_url($url->expanded_url);
1036
1037                                 $oembed_data = oembed_fetch_url($expanded_url);
1038
1039                                 // Quickfix: Workaround for URL with "[" and "]" in it
1040                                 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1041                                         $expanded_url = $url->url;
1042
1043                                 if ($type == "")
1044                                         $type = $oembed_data->type;
1045
1046                                 if ($oembed_data->type == "video") {
1047                                         //$body = str_replace($url->url,
1048                                         //              "[video]".$expanded_url."[/video]", $body);
1049                                         //$dontincludemedia = true;
1050                                         $type = $oembed_data->type;
1051                                         $footerurl = $expanded_url;
1052                                         $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1053
1054                                         $body = str_replace($url->url, $footerlink, $body);
1055                                 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1056                                         $body = str_replace($url->url,
1057                                                         "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1058                                                         $body);
1059                                         $dontincludemedia = true;
1060                                 } elseif ($oembed_data->type != "link")
1061                                         $body = str_replace($url->url,
1062                                                         "[url=".$expanded_url."]".$expanded_url."[/url]",
1063                                                         $body);
1064                                 else {
1065                                         $img_str = fetch_url($expanded_url, true, $redirects, 4);
1066
1067                                         $tempfile = tempnam(get_config("system","temppath"), "cache");
1068                                         file_put_contents($tempfile, $img_str);
1069                                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1070                                         unlink($tempfile);
1071
1072                                         if (substr($mime, 0, 6) == "image/") {
1073                                                 $type = "photo";
1074                                                 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1075                                                 $dontincludemedia = true;
1076                                         } else {
1077                                                 $type = $oembed_data->type;
1078                                                 $footerurl = $expanded_url;
1079                                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1080
1081                                                 $body = str_replace($url->url, $footerlink, $body);
1082                                         }
1083                                 }
1084                         }
1085                 }
1086
1087                 if ($footerurl != "")
1088                         $footer = add_page_info($footerurl);
1089
1090                 if (($footerlink != "") AND (trim($footer) != "")) {
1091                         $removedlink = trim(str_replace($footerlink, "", $body));
1092
1093                         if (strstr($body, $removedlink))
1094                                 $body = $removedlink;
1095
1096                         $body .= $footer;
1097                 }
1098
1099                 if ($no_tags)
1100                         return(array("body" => $body, "tags" => ""));
1101
1102                 $tags_arr = array();
1103
1104                 foreach ($item->entities->hashtags AS $hashtag) {
1105                         $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1106                         $tags_arr["#".$hashtag->text] = $url;
1107                         $body = str_replace("#".$hashtag->text, $url, $body);
1108                 }
1109
1110                 foreach ($item->entities->user_mentions AS $mention) {
1111                         $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1112                         $tags_arr["@".$mention->screen_name] = $url;
1113                         $body = str_replace("@".$mention->screen_name, $url, $body);
1114                 }
1115
1116                 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1117                 $tags = get_tags($body);
1118
1119                 if(count($tags)) {
1120                         foreach($tags as $tag) {
1121                                 if (strstr(trim($tag), " "))
1122                                         continue;
1123
1124                                 if(strpos($tag,'#') === 0) {
1125                                         if(strpos($tag,'[url='))
1126                                                 continue;
1127
1128                                         // don't link tags that are already embedded in links
1129
1130                                         if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1131                                                 continue;
1132                                         if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1133                                                 continue;
1134
1135                                         $basetag = str_replace('_',' ',substr($tag,1));
1136                                         $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1137                                         $body = str_replace($tag,$url,$body);
1138                                         $tags_arr["#".$basetag] = $url;
1139                                         continue;
1140                                 } elseif(strpos($tag,'@') === 0) {
1141                                         if(strpos($tag,'[url='))
1142                                                 continue;
1143
1144                                         $basetag = substr($tag,1);
1145                                         $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1146                                         $body = str_replace($tag,$url,$body);
1147                                         $tags_arr["@".$basetag] = $url;
1148                                 }
1149                         }
1150                 }
1151
1152
1153                 $tags = implode($tags_arr, ",");
1154
1155         }
1156         return(array("body" => $body, "tags" => $tags));
1157 }
1158
1159 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1160
1161         $has_picture = false;
1162
1163         $postarray = array();
1164         $postarray['network'] = NETWORK_TWITTER;
1165         $postarray['gravity'] = 0;
1166         $postarray['uid'] = $uid;
1167         $postarray['wall'] = 0;
1168         $postarray['uri'] = "twitter::".$post->id_str;
1169
1170         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1171                         dbesc($postarray['uri']),
1172                         intval($uid)
1173                 );
1174
1175         if (count($r))
1176                 return(array());
1177
1178         $contactid = 0;
1179
1180         if ($post->in_reply_to_status_id_str != "") {
1181
1182                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1183
1184                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1185                                 dbesc($parent),
1186                                 intval($uid)
1187                         );
1188                 if (count($r)) {
1189                         $postarray['thr-parent'] = $r[0]["uri"];
1190                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1191                 } else {
1192                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1193                                         dbesc($parent),
1194                                         intval($uid)
1195                                 );
1196                         if (count($r)) {
1197                                 $postarray['thr-parent'] = $r[0]['uri'];
1198                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1199                         } else {
1200                                 $postarray['thr-parent'] = $postarray['uri'];
1201                                 $postarray['parent-uri'] = $postarray['uri'];
1202                         }
1203                 }
1204
1205                 // Is it me?
1206                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1207
1208                 if ($post->user->id_str == $own_id) {
1209                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1210                                 intval($uid));
1211
1212                         if(count($r)) {
1213                                 $contactid = $r[0]["id"];
1214
1215                                 $postarray['owner-name'] =  $r[0]["name"];
1216                                 $postarray['owner-link'] = $r[0]["url"];
1217                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1218                         } else
1219                                 return(array());
1220                 }
1221         } else
1222                 $postarray['parent-uri'] = $postarray['uri'];
1223
1224         if ($contactid == 0) {
1225                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1226
1227                 $postarray['owner-name'] = $post->user->name;
1228                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1229                 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1230         }
1231
1232         if(($contactid == 0) AND !$only_existing_contact)
1233                 $contactid = $self['id'];
1234         elseif ($contactid <= 0)
1235                 return(array());
1236
1237         $postarray['contact-id'] = $contactid;
1238
1239         $postarray['verb'] = ACTIVITY_POST;
1240         $postarray['author-name'] = $postarray['owner-name'];
1241         $postarray['author-link'] = $postarray['owner-link'];
1242         $postarray['author-avatar'] = $postarray['owner-avatar'];
1243         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1244         $postarray['app'] = strip_tags($post->source);
1245
1246         if ($post->user->protected) {
1247                 $postarray['private'] = 1;
1248                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1249         }
1250
1251         $postarray['body'] = $post->text;
1252
1253         // media
1254         if (is_array($post->entities->media)) {
1255                 foreach($post->entities->media AS $media) {
1256                         switch($media->type) {
1257                                 case 'photo':
1258                                         $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1259                                         $has_picture = true;
1260                                         break;
1261                                 default:
1262                                         $postarray['body'] .= print_r($media, true);
1263                         }
1264                 }
1265         }
1266
1267         $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $has_picture);
1268         $postarray['body'] = $converted["body"];
1269         $postarray['tag'] = $converted["tags"];
1270
1271         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1272         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1273
1274         if (is_string($post->place->name))
1275                 $postarray["location"] = $post->place->name;
1276
1277         if (is_string($post->place->full_name))
1278                 $postarray["location"] = $post->place->full_name;
1279
1280         if (is_array($post->geo->coordinates))
1281                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1282
1283         if (is_array($post->coordinates->coordinates))
1284                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1285
1286         if (is_object($post->retweeted_status)) {
1287
1288                 $postarray['body'] = $post->retweeted_status->text;
1289
1290                 // media
1291                 if (is_array($post->retweeted_status->entities->media)) {
1292                         foreach($post->retweeted_status->entities->media AS $media) {
1293                                 switch($media->type) {
1294                                         case 'photo':
1295                                                 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1296                                                 $has_picture = true;
1297                                                 break;
1298                                         default:
1299                                                 $postarray['body'] .= print_r($media, true);
1300                                 }
1301                         }
1302                 }
1303
1304                 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $has_picture);
1305                 $postarray['body'] = $converted["body"];
1306                 $postarray['tag'] = $converted["tags"];
1307
1308                 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1309
1310                 // Deactivated at the moment, since there are problems with answers to retweets
1311                 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1312                         $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1313                                 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1314                                 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1315                                 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1316                                 $postarray['body'];
1317                         $postarray['body'] .= "[/share]";
1318                 } else {
1319                         // Let retweets look like wall-to-wall posts
1320                         $postarray['author-name'] = $post->retweeted_status->user->name;
1321                         $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1322                         $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1323                         //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1324                         //      $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1325                         //      $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1326                         //}
1327                 }
1328
1329         }
1330         return($postarray);
1331 }
1332
1333 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1334
1335         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1336                         intval($uid)
1337                 );
1338
1339         if(!count($user))
1340                 return;
1341
1342         // Is it me?
1343         if (link_compare($user[0]["url"], $postarray['author-link']))
1344                 return;
1345
1346         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1347                         intval($uid),
1348                         dbesc("twitter::".$own_id)
1349                 );
1350
1351         if(!count($own_user))
1352                 return;
1353
1354         // Is it me from twitter?
1355         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1356                 return;
1357
1358         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1359                         dbesc($postarray['parent-uri']),
1360                         intval($uid)
1361                         );
1362
1363         if(count($myconv)) {
1364
1365                 foreach($myconv as $conv) {
1366                         // now if we find a match, it means we're in this conversation
1367
1368                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1369                                 continue;
1370
1371                         require_once('include/enotify.php');
1372
1373                         $conv_parent = $conv['parent'];
1374
1375                         notification(array(
1376                                 'type'         => NOTIFY_COMMENT,
1377                                 'notify_flags' => $user[0]['notify-flags'],
1378                                 'language'     => $user[0]['language'],
1379                                 'to_name'      => $user[0]['username'],
1380                                 'to_email'     => $user[0]['email'],
1381                                 'uid'          => $user[0]['uid'],
1382                                 'item'         => $postarray,
1383                                 'link'             => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1384                                 'source_name'  => $postarray['author-name'],
1385                                 'source_link'  => $postarray['author-link'],
1386                                 'source_photo' => $postarray['author-avatar'],
1387                                 'verb'         => ACTIVITY_POST,
1388                                 'otype'        => 'item',
1389                                 'parent'       => $conv_parent,
1390                         ));
1391
1392                         // only send one notification
1393                         break;
1394                 }
1395         }
1396 }
1397
1398 function twitter_fetchhometimeline($a, $uid) {
1399         $ckey    = get_config('twitter', 'consumerkey');
1400         $csecret = get_config('twitter', 'consumersecret');
1401         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1402         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1403         $create_user = get_pconfig($uid, 'twitter', 'create_user');
1404
1405         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1406
1407         require_once('library/twitteroauth.php');
1408         require_once('include/items.php');
1409
1410         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1411
1412         $own_contact = twitter_fetch_own_contact($a, $uid);
1413
1414         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1415                 intval($own_contact),
1416                 intval($uid));
1417
1418         if(count($r)) {
1419                 $own_id = $r[0]["nick"];
1420         } else {
1421                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1422                 return;
1423         }
1424
1425         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1426                 intval($uid));
1427
1428         if(count($r)) {
1429                 $self = $r[0];
1430         } else {
1431                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1432                 return;
1433         }
1434
1435         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1436                 intval($uid));
1437         if(!count($u)) {
1438                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1439                 return;
1440         }
1441
1442         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1443         //$parameters["count"] = 200;
1444
1445
1446         // Fetching timeline
1447         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1448
1449         $first_time = ($lastid == "");
1450
1451         if ($lastid <> "")
1452                 $parameters["since_id"] = $lastid;
1453
1454         $items = $connection->get('statuses/home_timeline', $parameters);
1455
1456         if (!is_array($items)) {
1457                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1458                 return;
1459         }
1460
1461         $posts = array_reverse($items);
1462
1463         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1464
1465         if (count($posts)) {
1466                 foreach ($posts as $post) {
1467                         if ($post->id_str > $lastid)
1468                                 $lastid = $post->id_str;
1469
1470                         if ($first_time)
1471                                 continue;
1472
1473                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1474
1475                         if (trim($postarray['body']) == "")
1476                                 continue;
1477
1478                         $item = item_store($postarray);
1479
1480                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1481
1482                         if ($item != 0)
1483                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1484
1485                 }
1486         }
1487         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1488
1489         // Fetching mentions
1490         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1491
1492         $first_time = ($lastid == "");
1493
1494         if ($lastid <> "")
1495                 $parameters["since_id"] = $lastid;
1496
1497         $items = $connection->get('statuses/mentions_timeline', $parameters);
1498
1499         if (!is_array($items)) {
1500                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1501                 return;
1502         }
1503
1504         $posts = array_reverse($items);
1505
1506         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1507
1508         if (count($posts)) {
1509                 foreach ($posts as $post) {
1510                         if ($post->id_str > $lastid)
1511                                 $lastid = $post->id_str;
1512
1513                         if ($first_time)
1514                                 continue;
1515
1516                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1517
1518                         if (trim($postarray['body']) == "")
1519                                 continue;
1520
1521                         $item = item_store($postarray);
1522
1523                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1524
1525                         if ($item == 0) {
1526                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1527                                         dbesc($postarray['uri']),
1528                                         intval($uid)
1529                                 );
1530                                 if (count($r))
1531                                         $item = $r[0]['id'];
1532                         }
1533
1534                         if ($item != 0) {
1535                                 require_once('include/enotify.php');
1536                                 notification(array(
1537                                         'type'         => NOTIFY_TAGSELF,
1538                                         'notify_flags' => $u[0]['notify-flags'],
1539                                         'language'     => $u[0]['language'],
1540                                         'to_name'      => $u[0]['username'],
1541                                         'to_email'     => $u[0]['email'],
1542                                         'uid'          => $u[0]['uid'],
1543                                         'item'         => $postarray,
1544                                         'link'         => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1545                                         'source_name'  => $postarray['author-name'],
1546                                         'source_link'  => $postarray['author-link'],
1547                                         'source_photo' => $postarray['author-avatar'],
1548                                         'verb'         => ACTIVITY_TAG,
1549                                         'otype'        => 'item'
1550                                 ));
1551                         }
1552                 }
1553         }
1554
1555         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1556 }
1557
1558 function twitter_fetch_own_contact($a, $uid) {
1559         $ckey    = get_config('twitter', 'consumerkey');
1560         $csecret = get_config('twitter', 'consumersecret');
1561         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1562         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1563
1564         $own_id = get_pconfig($uid, 'twitter', 'own_id');
1565
1566         $contact_id = 0;
1567
1568         if ($own_id == "") {
1569                 require_once('library/twitteroauth.php');
1570
1571                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1572
1573                 // Fetching user data
1574                 $user = $connection->get('account/verify_credentials');
1575
1576                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1577
1578                 $contact_id = twitter_fetch_contact($uid, $user, true);
1579
1580         } else {
1581                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1582                         intval($uid), dbesc("twitter::".$own_id));
1583                 if(count($r))
1584                         $contact_id = $r[0]["id"];
1585                 else
1586                         del_pconfig($uid, 'twitter', 'own_id');
1587
1588         }
1589
1590         return($contact_id);
1591 }
1592
1593 function twitter_is_retweet($a, $uid, $body) {
1594         $body = trim($body);
1595
1596         // Skip if it isn't a pure repeated messages
1597         // Does it start with a share?
1598         if (strpos($body, "[share") > 0)
1599                 return(false);
1600
1601         // Does it end with a share?
1602         if (strlen($body) > (strrpos($body, "[/share]") + 8))
1603                 return(false);
1604
1605         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1606         // Skip if there is no shared message in there
1607         if ($body == $attributes)
1608                 return(false);
1609
1610         $link = "";
1611         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1612         if ($matches[1] != "")
1613                 $link = $matches[1];
1614
1615         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1616         if ($matches[1] != "")
1617                 $link = $matches[1];
1618
1619         $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1620         if ($id == $link)
1621                 return(false);
1622
1623         logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1624
1625         $ckey    = get_config('twitter', 'consumerkey');
1626         $csecret = get_config('twitter', 'consumersecret');
1627         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1628         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1629
1630         require_once('library/twitteroauth.php');
1631         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1632
1633         $result = $connection->post('statuses/retweet/'.$id);
1634
1635         logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1636
1637         return(!isset($result->errors));
1638 }
1639
1640 ?>