3 * Name: Twitter Connector
4 * Description: Relay public postings to a connected Twitter account
6 * Author: Tobias Diekershoff <https://f.diekershoff.de/profile/tobias>
7 * Author: Michael Vogel <https://pirati.ca/profile/heluecht>
9 * Copyright (c) 2011-2013 Tobias Diekershoff, Michael Vogel
10 * All rights reserved.
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.
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.
36 /* Twitter Plugin for Friendica
38 * Author: Tobias Diekershoff
39 * tobias.diekershoff@gmx.net
41 * License:3-clause BSD license
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
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.
51 * Add this key pair to your global .htconfig.php or use the admin panel.
53 * $a->config['twitter']['consumerkey'] = 'your consumer_key here';
54 * $a->config['twitter']['consumersecret'] = 'your consumer_secret here';
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".
60 * Requirements: PHP5, curl [Slinky library]
63 define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes
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 logger("installed twitter");
79 function twitter_uninstall() {
80 unregister_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings');
81 unregister_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
82 unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
83 unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
84 unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
85 unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
86 unregister_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
87 unregister_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
89 // old setting - remove only
90 unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook');
91 unregister_hook('plugin_settings', 'addon/twitter/twitter.php', 'twitter_settings');
92 unregister_hook('plugin_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
96 function twitter_follow($a, &$contact) {
98 logger("twitter_follow: Check if contact is twitter contact. ".$contact["url"], LOGGER_DEBUG);
100 if (!strstr($contact["url"], "://twitter.com") AND !strstr($contact["url"], "@twitter.com"))
103 // contact seems to be a twitter contact, so continue
104 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $contact["url"]);
105 $nickname = str_replace("@twitter.com", "", $nickname);
107 $uid = $a->user["uid"];
109 $ckey = get_config('twitter', 'consumerkey');
110 $csecret = get_config('twitter', 'consumersecret');
111 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
112 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
114 require_once("addon/twitter/codebird.php");
116 $cb = \Codebird\Codebird::getInstance();
117 $cb->setConsumerKey($ckey, $csecret);
118 $cb->setToken($otoken, $osecret);
120 $parameters = array();
121 $parameters["screen_name"] = $nickname;
123 $user = $cb->friendships_create($parameters);
125 twitter_fetchuser($a, $uid, $nickname);
127 $r = q("SELECT name,nick,url,addr,batch,notify,poll,request,confirm,poco,photo,priority,network,alias,pubkey
128 FROM `contact` WHERE `uid` = %d AND `nick` = '%s'",
132 $contact["contact"] = $r[0];
135 function twitter_jot_nets(&$a,&$b) {
139 $tw_post = get_pconfig(local_user(),'twitter','post');
140 if(intval($tw_post) == 1) {
141 $tw_defpost = get_pconfig(local_user(),'twitter','post_by_default');
142 $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : '');
143 $b .= '<div class="profile-jot-net"><input type="checkbox" name="twitter_enable"' . $selected . ' value="1" /> '
144 . t('Post to Twitter') . '</div>';
148 function twitter_settings_post ($a,$post) {
151 // don't check twitter settings if twitter submit button is not clicked
152 if (!x($_POST,'twitter-submit'))
155 if (isset($_POST['twitter-disconnect'])) {
157 * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
158 * from the user configuration
160 del_pconfig(local_user(), 'twitter', 'consumerkey');
161 del_pconfig(local_user(), 'twitter', 'consumersecret');
162 del_pconfig(local_user(), 'twitter', 'oauthtoken');
163 del_pconfig(local_user(), 'twitter', 'oauthsecret');
164 del_pconfig(local_user(), 'twitter', 'post');
165 del_pconfig(local_user(), 'twitter', 'post_by_default');
166 del_pconfig(local_user(), 'twitter', 'lastid');
167 del_pconfig(local_user(), 'twitter', 'mirror_posts');
168 del_pconfig(local_user(), 'twitter', 'import');
169 del_pconfig(local_user(), 'twitter', 'create_user');
170 del_pconfig(local_user(), 'twitter', 'own_id');
172 if (isset($_POST['twitter-pin'])) {
173 // if the user supplied us with a PIN from Twitter, let the magic of OAuth happen
174 logger('got a Twitter PIN');
175 require_once('library/twitteroauth.php');
176 $ckey = get_config('twitter', 'consumerkey');
177 $csecret = get_config('twitter', 'consumersecret');
178 // the token and secret for which the PIN was generated were hidden in the settings
179 // form as token and token2, we need a new connection to Twitter using these token
180 // and secret to request a Access Token with the PIN
181 $connection = new TwitterOAuth($ckey, $csecret, $_POST['twitter-token'], $_POST['twitter-token2']);
182 $token = $connection->getAccessToken( $_POST['twitter-pin'] );
183 // ok, now that we have the Access Token, save them in the user config
184 set_pconfig(local_user(),'twitter', 'oauthtoken', $token['oauth_token']);
185 set_pconfig(local_user(),'twitter', 'oauthsecret', $token['oauth_token_secret']);
186 set_pconfig(local_user(),'twitter', 'post', 1);
187 // reload the Addon Settings page, if we don't do it see Bug #42
188 goaway($a->get_baseurl().'/settings/connectors');
190 // if no PIN is supplied in the POST variables, the user has changed the setting
191 // to post a tweet for every new __public__ posting to the wall
192 set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable']));
193 set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default']));
194 set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
195 set_pconfig(local_user(), 'twitter', 'import', intval($_POST['twitter-import']));
196 set_pconfig(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user']));
198 if (!intval($_POST['twitter-mirror']))
199 del_pconfig(local_user(),'twitter','lastid');
201 info(t('Twitter settings updated.') . EOL);
204 function twitter_settings(&$a,&$s) {
207 $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->get_baseurl() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
209 * 1) Check that we have global consumer key & secret
210 * 2) If no OAuthtoken & stuff is present, generate button to get some
211 * 3) Checkbox for "Send public notices (140 chars only)
213 $ckey = get_config('twitter', 'consumerkey' );
214 $csecret = get_config('twitter', 'consumersecret' );
215 $otoken = get_pconfig(local_user(), 'twitter', 'oauthtoken' );
216 $osecret = get_pconfig(local_user(), 'twitter', 'oauthsecret' );
217 $enabled = get_pconfig(local_user(), 'twitter', 'post');
218 $checked = (($enabled) ? ' checked="checked" ' : '');
219 $defenabled = get_pconfig(local_user(),'twitter','post_by_default');
220 $defchecked = (($defenabled) ? ' checked="checked" ' : '');
221 $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts');
222 $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : '');
223 $importenabled = get_pconfig(local_user(),'twitter','import');
224 $importchecked = (($importenabled) ? ' checked="checked" ' : '');
225 $create_userenabled = get_pconfig(local_user(),'twitter','create_user');
226 $create_userchecked = (($create_userenabled) ? ' checked="checked" ' : '');
228 $css = (($enabled) ? '' : '-disabled');
230 $s .= '<span id="settings_twitter_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
231 $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
233 $s .= '<div id="settings_twitter_expanded" class="settings-block" style="display: none;">';
234 $s .= '<span class="fakelink" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
235 $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
238 if ( (!$ckey) && (!$csecret) ) {
240 * no global consumer keys
241 * display warning and skip personal config
243 $s .= '<p>'. t('No consumer key pair for Twitter found. Please contact your site administrator.') .'</p>';
246 * ok we have a consumer key pair now look into the OAuth stuff
248 if ( (!$otoken) && (!$osecret) ) {
250 * the user has not yet connected the account to twitter...
251 * get a temporary OAuth key/secret pair and display a button with
252 * which the user can request a PIN to connect the account to a
253 * account at Twitter.
255 require_once('library/twitteroauth.php');
256 $connection = new TwitterOAuth($ckey, $csecret);
257 $request_token = $connection->getRequestToken();
258 $token = $request_token['oauth_token'];
260 * make some nice form
262 $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>';
263 $s .= '<a href="'.$connection->getAuthorizeURL($token).'" target="_twitter"><img src="addon/twitter/lighter.png" alt="'.t('Log in with Twitter').'"></a>';
264 $s .= '<div id="twitter-pin-wrapper">';
265 $s .= '<label id="twitter-pin-label" for="twitter-pin">'. t('Copy the PIN from Twitter here') .'</label>';
266 $s .= '<input id="twitter-pin" type="text" name="twitter-pin" />';
267 $s .= '<input id="twitter-token" type="hidden" name="twitter-token" value="'.$token.'" />';
268 $s .= '<input id="twitter-token2" type="hidden" name="twitter-token2" value="'.$request_token['oauth_token_secret'].'" />';
269 $s .= '</div><div class="clear"></div>';
270 $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
273 * we have an OAuth key / secret pair for the user
274 * so let's give a chance to disable the postings to Twitter
276 require_once('library/twitteroauth.php');
277 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
278 $details = $connection->get('account/verify_credentials');
279 $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>';
280 $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>';
281 if ($a->user['hidewall']) {
282 $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>';
284 $s .= '<div id="twitter-enable-wrapper">';
285 $s .= '<label id="twitter-enable-label" for="twitter-checkbox">'. t('Allow posting to Twitter'). '</label>';
286 $s .= '<input id="twitter-checkbox" type="checkbox" name="twitter-enable" value="1" ' . $checked . '/>';
287 $s .= '<div class="clear"></div>';
288 $s .= '<label id="twitter-default-label" for="twitter-default">'. t('Send public postings to Twitter by default') .'</label>';
289 $s .= '<input id="twitter-default" type="checkbox" name="twitter-default" value="1" ' . $defchecked . '/>';
290 $s .= '<div class="clear"></div>';
292 $s .= '<label id="twitter-mirror-label" for="twitter-mirror">'.t('Mirror all posts from twitter that are no replies').'</label>';
293 $s .= '<input id="twitter-mirror" type="checkbox" name="twitter-mirror" value="1" '. $mirrorchecked . '/>';
294 $s .= '<div class="clear"></div>';
297 $s .= '<label id="twitter-import-label" for="twitter-import">'.t('Import the remote timeline').'</label>';
298 $s .= '<input id="twitter-import" type="checkbox" name="twitter-import" value="1" '. $importchecked . '/>';
299 $s .= '<div class="clear"></div>';
301 $s .= '<label id="twitter-create_user-label" for="twitter-create_user">'.t('Automatically create contacts').'</label>';
302 $s .= '<input id="twitter-create_user" type="checkbox" name="twitter-create_user" value="1" '. $create_userchecked . '/>';
303 $s .= '<div class="clear"></div>';
305 $s .= '<div id="twitter-disconnect-wrapper">';
306 $s .= '<label id="twitter-disconnect-label" for="twitter-disconnect">'. t('Clear OAuth configuration') .'</label>';
307 $s .= '<input id="twitter-disconnect" type="checkbox" name="twitter-disconnect" value="1" />';
308 $s .= '</div><div class="clear"></div>';
309 $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
312 $s .= '</div><div class="clear"></div>';
316 function twitter_post_local(&$a,&$b) {
321 if((local_user()) && (local_user() == $b['uid']) && (! $b['private']) && (! $b['parent']) ) {
323 $twitter_post = intval(get_pconfig(local_user(),'twitter','post'));
324 $twitter_enable = (($twitter_post && x($_REQUEST,'twitter_enable')) ? intval($_REQUEST['twitter_enable']) : 0);
326 // if API is used, default to the chosen settings
327 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'twitter','post_by_default')))
330 if(! $twitter_enable)
333 if(strlen($b['postopts']))
334 $b['postopts'] .= ',';
335 $b['postopts'] .= 'twitter';
339 function twitter_action($a, $uid, $pid, $action) {
341 $ckey = get_config('twitter', 'consumerkey');
342 $csecret = get_config('twitter', 'consumersecret');
343 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
344 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
346 require_once("addon/twitter/codebird.php");
348 $cb = \Codebird\Codebird::getInstance();
349 $cb->setConsumerKey($ckey, $csecret);
350 $cb->setToken($otoken, $osecret);
352 $post = array('id' => $pid);
354 logger("twitter_action '".$action."' ID: ".$pid." data: " . print_r($post, true), LOGGER_DATA);
358 $result = $cb->statuses_destroy($post);
361 $result = $cb->favorites_create($post);
364 $result = $cb->favorites_destroy($post);
367 logger("twitter_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
370 function twitter_post_hook(&$a,&$b) {
376 require_once("include/network.php");
378 if (!get_pconfig($b["uid"],'twitter','import')) {
379 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
383 if($b['parent'] != $b['id']) {
384 logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
386 // Looking if its a reply to a twitter post
387 if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) {
388 logger("twitter_post_hook: no twitter post ".$b["parent"]);
392 $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
393 dbesc($b["thr-parent"]),
397 logger("twitter_post_hook: no parent found ".$b["thr-parent"]);
404 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
405 $nickname = "@[url=".$orig_post["author-link"]."]".$nickname."[/url]";
407 logger("twitter_post_hook: comparing ".$nickname." with ".$b["body"], LOGGER_DEBUG);
408 if (strpos($b["body"], $nickname) === false)
409 $b["body"] = $nickname." ".$b["body"];
411 logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA);
415 if($b['private'] OR !strstr($b['postopts'],'twitter'))
419 if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
420 twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
422 if($b['verb'] == ACTIVITY_LIKE) {
423 logger("twitter_post_hook: parameter 2 ".substr($b["thr-parent"], 9), LOGGER_DEBUG);
425 twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
427 twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like");
431 if($b['deleted'] || ($b['created'] !== $b['edited']))
434 // if post comes from twitter don't send it back
435 if($b['app'] == "Twitter")
438 logger('twitter post invoked');
441 load_pconfig($b['uid'], 'twitter');
443 $ckey = get_config('twitter', 'consumerkey');
444 $csecret = get_config('twitter', 'consumersecret');
445 $otoken = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
446 $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
448 if($ckey && $csecret && $otoken && $osecret) {
449 logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
451 // If it's a repeated message from twitter then do a native retweet and exit
452 if (twitter_is_retweet($a, $b['uid'], $b['body']))
455 require_once('library/twitteroauth.php');
456 require_once('include/bbcode.php');
457 $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
460 require_once("include/plaintext.php");
461 $msgarr = plaintext($a, $b, $max_char, true);
462 $msg = $msgarr["text"];
464 if (($msg == "") AND isset($msgarr["title"]))
465 $msg = shortenmsg($msgarr["title"], $max_char - 50);
469 if (isset($msgarr["url"]))
470 $msg .= "\n".$msgarr["url"];
471 elseif (isset($msgarr["image"]))
472 $image = $msgarr["image"];
474 // and now tweet it :-)
475 if(strlen($msg) and ($image != "")) {
476 $img_str = fetch_url($image);
478 $tempfile = tempnam(get_config("system","temppath"), "cache");
479 file_put_contents($tempfile, $img_str);
481 // Twitter had changed something so that the old library doesn't work anymore
482 // so we are using a new library for twitter
484 // Switching completely to this library with all functions
485 require_once("addon/twitter/codebird.php");
487 $cb = \Codebird\Codebird::getInstance();
488 $cb->setConsumerKey($ckey, $csecret);
489 $cb->setToken($otoken, $osecret);
491 $post = array('status' => $msg, 'media[]' => $tempfile);
494 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
496 $result = $cb->statuses_updateWithMedia($post);
499 logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG);
500 if ($result->errors OR $result->error) {
501 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
503 // Workaround: Remove the picture link so that the post can be reposted without it
506 } elseif ($iscomment) {
507 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
508 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
509 dbesc("twitter::".$result->id_str),
510 dbesc($result->text),
516 if(strlen($msg) and ($image == "")) {
517 $url = 'statuses/update';
518 $post = array('status' => $msg);
521 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
523 $result = $tweet->post($url, $post);
524 logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
525 if ($result->errors) {
526 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
528 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
530 $a->contact = $r[0]["id"];
532 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
533 require_once('include/queue_fn.php');
534 add_to_queue($a->contact,NETWORK_TWITTER,$s);
535 notice(t('Twitter post failed. Queued for retry.').EOL);
536 } elseif ($iscomment) {
537 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
538 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
539 dbesc("twitter::".$result->id_str),
542 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
543 // dbesc("twitter::".$result->id_str),
544 // dbesc($result->text),
552 function twitter_plugin_admin_post(&$a){
553 $consumerkey = ((x($_POST,'consumerkey')) ? notags(trim($_POST['consumerkey'])) : '');
554 $consumersecret = ((x($_POST,'consumersecret')) ? notags(trim($_POST['consumersecret'])): '');
555 $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
556 set_config('twitter','consumerkey',$consumerkey);
557 set_config('twitter','consumersecret',$consumersecret);
558 set_config('twitter','application_name',$applicationname);
559 info( t('Settings updated.'). EOL );
561 function twitter_plugin_admin(&$a, &$o){
562 $t = get_markup_template( "admin.tpl", "addon/twitter/" );
564 $o = replace_macros($t, array(
565 '$submit' => t('Save Settings'),
566 // name, label, value, help, [extra values]
567 '$consumerkey' => array('consumerkey', t('Consumer key'), get_config('twitter', 'consumerkey' ), ''),
568 '$consumersecret' => array('consumersecret', t('Consumer secret'), get_config('twitter', 'consumersecret' ), ''),
569 '$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'))
573 function twitter_cron($a,$b) {
574 $last = get_config('twitter','last_poll');
576 $poll_interval = intval(get_config('twitter','poll_interval'));
578 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
581 $next = $last + ($poll_interval * 60);
583 logger('twitter: poll intervall not reached');
587 logger('twitter: cron_start');
589 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND()");
592 logger('twitter: fetching for user '.$rr['uid']);
593 twitter_fetchtimeline($a, $rr['uid']);
598 $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
601 logger('twitter: importing timeline from user '.$rr['uid']);
602 twitter_fetchhometimeline($a, $rr["uid"]);
606 // check for new contacts once a day
607 $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
608 if($last_contact_check)
609 $next_contact_check = $last_contact_check + 86400;
611 $next_contact_check = 0;
613 if($next_contact_check <= time()) {
614 pumpio_getallusers($a, $rr["uid"]);
615 set_pconfig($rr['uid'],'pumpio','contact_check',time());
622 logger('twitter: cron_end');
624 set_config('twitter','last_poll', time());
627 function twitter_fetchtimeline($a, $uid) {
628 $ckey = get_config('twitter', 'consumerkey');
629 $csecret = get_config('twitter', 'consumersecret');
630 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
631 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
632 $lastid = get_pconfig($uid, 'twitter', 'lastid');
634 $application_name = get_config('twitter', 'application_name');
636 if ($application_name == "")
637 $application_name = $a->get_hostname();
639 $has_picture = false;
641 require_once('mod/item.php');
642 require_once('include/items.php');
644 require_once('library/twitteroauth.php');
645 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
647 $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
649 $first_time = ($lastid == "");
652 $parameters["since_id"] = $lastid;
654 $items = $connection->get('statuses/user_timeline', $parameters);
656 if (!is_array($items))
659 $posts = array_reverse($items);
662 foreach ($posts as $post) {
663 if ($post->id_str > $lastid)
664 $lastid = $post->id_str;
669 if (!strpos($post->source, $application_name)) {
670 $_SESSION["authenticated"] = true;
671 $_SESSION["uid"] = $uid;
674 $_REQUEST["type"] = "wall";
675 $_REQUEST["api_source"] = true;
676 $_REQUEST["profile_uid"] = $uid;
677 $_REQUEST["source"] = "Twitter";
679 //$_REQUEST["date"] = $post->created_at;
681 $_REQUEST["title"] = "";
683 if (is_object($post->retweeted_status)) {
685 $_REQUEST['body'] = $post->retweeted_status->text;
688 if (is_array($post->retweeted_status->entities->media)) {
689 foreach($post->retweeted_status->entities->media AS $media) {
690 switch($media->type) {
692 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
699 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $has_picture);
700 $_REQUEST['body'] = $converted["body"];
702 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
703 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
704 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
705 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
707 $_REQUEST['body'] .= "[/share]";
709 $_REQUEST["body"] = $post->text;
711 if (is_array($post->entities->media)) {
712 foreach($post->entities->media AS $media) {
713 switch($media->type) {
715 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
722 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $has_picture);
723 $_REQUEST['body'] = $converted["body"];
726 if (is_string($post->place->name))
727 $_REQUEST["location"] = $post->place->name;
729 if (is_string($post->place->full_name))
730 $_REQUEST["location"] = $post->place->full_name;
732 if (is_array($post->geo->coordinates))
733 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
735 if (is_array($post->coordinates->coordinates))
736 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
738 //print_r($_REQUEST);
739 logger('twitter: posting for user '.$uid);
741 // require_once('mod/item.php');
747 set_pconfig($uid, 'twitter', 'lastid', $lastid);
750 function twitter_queue_hook(&$a,&$b) {
752 $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
753 dbesc(NETWORK_TWITTER)
758 require_once('include/queue_fn.php');
761 if($x['network'] !== NETWORK_TWITTER)
764 logger('twitter_queue: run');
766 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
767 WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
775 $ckey = get_config('twitter', 'consumerkey');
776 $csecret = get_config('twitter', 'consumersecret');
777 $otoken = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
778 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
782 if ($ckey AND $csecret AND $otoken AND $osecret) {
784 logger('twitter_queue: able to post');
786 $z = unserialize($x['content']);
788 require_once("addon/twitter/codebird.php");
790 $cb = \Codebird\Codebird::getInstance();
791 $cb->setConsumerKey($ckey, $csecret);
792 $cb->setToken($otoken, $osecret);
794 if ($z['url'] == "statuses/update")
795 $result = $cb->statuses_update($z['post']);
797 logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
800 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
803 remove_queue_item($x['id']);
806 logger("twitter_queue: Error getting tokens for user ".$user['uid']);
809 logger('twitter_queue: delayed');
810 update_queue_time($x['id']);
815 function twitter_fetch_contact($uid, $contact, $create_user) {
817 // Check if the unique contact is existing
818 // To-Do: only update once a while
819 $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
820 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
823 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
824 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
825 dbesc($contact->name),
826 dbesc($contact->screen_name),
827 dbesc($contact->profile_image_url_https));
829 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
830 dbesc($contact->name),
831 dbesc($contact->screen_name),
832 dbesc($contact->profile_image_url_https),
833 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
835 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
836 intval($uid), dbesc("twitter::".$contact->id_str));
838 if(!count($r) AND !$create_user)
841 if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
842 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
847 // create contact record
848 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
849 `name`, `nick`, `photo`, `network`, `rel`, `priority`,
850 `writable`, `blocked`, `readonly`, `pending` )
851 VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
853 dbesc(datetime_convert()),
854 dbesc("https://twitter.com/".$contact->screen_name),
855 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
856 dbesc($contact->screen_name."@twitter.com"),
857 dbesc("twitter::".$contact->id_str),
859 dbesc("twitter::".$contact->id_str),
860 dbesc($contact->name),
861 dbesc($contact->screen_name),
862 dbesc($contact->profile_image_url_https),
863 dbesc(NETWORK_TWITTER),
864 intval(CONTACT_IS_FRIEND),
869 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
870 dbesc("twitter::".$contact->id_str),
877 $contact_id = $r[0]['id'];
879 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
883 if($g && intval($g[0]['def_gid'])) {
884 require_once('include/group.php');
885 group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
888 require_once("Photo.php");
890 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
892 q("UPDATE `contact` SET `photo` = '%s',
902 dbesc(datetime_convert()),
903 dbesc(datetime_convert()),
904 dbesc(datetime_convert()),
909 // update profile photos once every two weeks as we have no notification of when they change.
911 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
912 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
914 // check that we have all the photos, this has been known to fail on occasion
916 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
918 logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
920 require_once("Photo.php");
922 $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
924 q("UPDATE `contact` SET `photo` = '%s',
929 `avatar-date` = '%s',
939 dbesc(datetime_convert()),
940 dbesc(datetime_convert()),
941 dbesc(datetime_convert()),
942 dbesc("https://twitter.com/".$contact->screen_name),
943 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
944 dbesc($contact->screen_name."@twitter.com"),
945 dbesc($contact->name),
946 dbesc($contact->screen_name),
955 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
956 $ckey = get_config('twitter', 'consumerkey');
957 $csecret = get_config('twitter', 'consumersecret');
958 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
959 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
961 require_once("addon/twitter/codebird.php");
963 $cb = \Codebird\Codebird::getInstance();
964 $cb->setConsumerKey($ckey, $csecret);
965 $cb->setToken($otoken, $osecret);
967 $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
975 $parameters = array();
977 if ($screen_name != "")
978 $parameters["screen_name"] = $screen_name;
981 $parameters["user_id"] = $user_id;
983 // Fetching user data
984 $user = $cb->users_show($parameters);
986 if (!is_object($user))
989 $contact_id = twitter_fetch_contact($uid, $user, true);
994 function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontincludemedia) {
995 require_once("include/oembed.php");
996 require_once("include/network.php");
1000 if (isset($item->entities->urls)) {
1006 foreach ($item->entities->urls AS $url) {
1007 if ($url->url AND $url->expanded_url AND $url->display_url) {
1009 $expanded_url = original_url($url->expanded_url);
1011 $oembed_data = oembed_fetch_url($expanded_url);
1013 // Quickfix: Workaround for URL with "[" and "]" in it
1014 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1015 $expanded_url = $url->url;
1018 $type = $oembed_data->type;
1020 if ($oembed_data->type == "video") {
1021 //$body = str_replace($url->url,
1022 // "[video]".$expanded_url."[/video]", $body);
1023 //$dontincludemedia = true;
1024 $type = $oembed_data->type;
1025 $footerurl = $expanded_url;
1026 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1028 $body = str_replace($url->url, $footerlink, $body);
1029 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1030 $body = str_replace($url->url,
1031 "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1033 $dontincludemedia = true;
1034 } elseif ($oembed_data->type != "link")
1035 $body = str_replace($url->url,
1036 "[url=".$expanded_url."]".$expanded_url."[/url]",
1039 $img_str = fetch_url($expanded_url, true, $redirects, 4);
1041 $tempfile = tempnam(get_config("system","temppath"), "cache");
1042 file_put_contents($tempfile, $img_str);
1043 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1046 if (substr($mime, 0, 6) == "image/") {
1048 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1049 $dontincludemedia = true;
1051 $type = $oembed_data->type;
1052 $footerurl = $expanded_url;
1053 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1055 $body = str_replace($url->url, $footerlink, $body);
1061 if ($footerurl != "")
1062 $footer = add_page_info($footerurl);
1064 if (($footerlink != "") AND (trim($footer) != "")) {
1065 $removedlink = trim(str_replace($footerlink, "", $body));
1067 if (strstr($body, $removedlink))
1068 $body = $removedlink;
1074 return(array("body" => $body, "tags" => ""));
1076 $tags_arr = array();
1078 foreach ($item->entities->hashtags AS $hashtag) {
1079 $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1080 $tags_arr["#".$hashtag->text] = $url;
1081 $body = str_replace("#".$hashtag->text, $url, $body);
1084 foreach ($item->entities->user_mentions AS $mention) {
1085 $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1086 $tags_arr["@".$mention->screen_name] = $url;
1087 $body = str_replace("@".$mention->screen_name, $url, $body);
1090 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1091 $tags = get_tags($body);
1094 foreach($tags as $tag) {
1095 if (strstr(trim($tag), " "))
1098 if(strpos($tag,'#') === 0) {
1099 if(strpos($tag,'[url='))
1102 // don't link tags that are already embedded in links
1104 if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1106 if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1109 $basetag = str_replace('_',' ',substr($tag,1));
1110 $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1111 $body = str_replace($tag,$url,$body);
1112 $tags_arr["#".$basetag] = $url;
1114 } elseif(strpos($tag,'@') === 0) {
1115 if(strpos($tag,'[url='))
1118 $basetag = substr($tag,1);
1119 $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1120 $body = str_replace($tag,$url,$body);
1121 $tags_arr["@".$basetag] = $url;
1127 $tags = implode($tags_arr, ",");
1130 return(array("body" => $body, "tags" => $tags));
1133 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1135 $has_picture = false;
1137 $postarray = array();
1138 $postarray['network'] = NETWORK_TWITTER;
1139 $postarray['gravity'] = 0;
1140 $postarray['uid'] = $uid;
1141 $postarray['wall'] = 0;
1142 $postarray['uri'] = "twitter::".$post->id_str;
1144 $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1145 dbesc($postarray['uri']),
1154 if ($post->in_reply_to_status_id_str != "") {
1156 $parent = "twitter::".$post->in_reply_to_status_id_str;
1158 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1163 $postarray['thr-parent'] = $r[0]["uri"];
1164 $postarray['parent-uri'] = $r[0]["parent-uri"];
1166 $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1171 $postarray['thr-parent'] = $r[0]['uri'];
1172 $postarray['parent-uri'] = $r[0]['parent-uri'];
1174 $postarray['thr-parent'] = $postarray['uri'];
1175 $postarray['parent-uri'] = $postarray['uri'];
1180 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1182 if ($post->user->id_str == $own_id) {
1183 $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1187 $contactid = $r[0]["id"];
1189 $postarray['owner-name'] = $r[0]["name"];
1190 $postarray['owner-link'] = $r[0]["url"];
1191 $postarray['owner-avatar'] = $r[0]["photo"];
1196 $postarray['parent-uri'] = $postarray['uri'];
1198 if ($contactid == 0) {
1199 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1201 $postarray['owner-name'] = $post->user->name;
1202 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1203 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1206 if(($contactid == 0) AND !$only_existing_contact)
1207 $contactid = $self['id'];
1208 elseif ($contactid <= 0)
1211 $postarray['contact-id'] = $contactid;
1213 $postarray['verb'] = ACTIVITY_POST;
1214 $postarray['author-name'] = $postarray['owner-name'];
1215 $postarray['author-link'] = $postarray['owner-link'];
1216 $postarray['author-avatar'] = $postarray['owner-avatar'];
1217 $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1218 $postarray['app'] = strip_tags($post->source);
1220 if ($post->user->protected) {
1221 $postarray['private'] = 1;
1222 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1225 $postarray['body'] = $post->text;
1228 if (is_array($post->entities->media)) {
1229 foreach($post->entities->media AS $media) {
1230 switch($media->type) {
1232 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1233 $has_picture = true;
1236 $postarray['body'] .= print_r($media, true);
1241 $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $has_picture);
1242 $postarray['body'] = $converted["body"];
1243 $postarray['tag'] = $converted["tags"];
1245 $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1246 $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1248 if (is_string($post->place->name))
1249 $postarray["location"] = $post->place->name;
1251 if (is_string($post->place->full_name))
1252 $postarray["location"] = $post->place->full_name;
1254 if (is_array($post->geo->coordinates))
1255 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1257 if (is_array($post->coordinates->coordinates))
1258 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1260 if (is_object($post->retweeted_status)) {
1262 $postarray['body'] = $post->retweeted_status->text;
1265 if (is_array($post->retweeted_status->entities->media)) {
1266 foreach($post->retweeted_status->entities->media AS $media) {
1267 switch($media->type) {
1269 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1270 $has_picture = true;
1273 $postarray['body'] .= print_r($media, true);
1278 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $has_picture);
1279 $postarray['body'] = $converted["body"];
1280 $postarray['tag'] = $converted["tags"];
1282 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1284 // Deactivated at the moment, since there are problems with answers to retweets
1285 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1286 $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1287 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1288 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1289 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1291 $postarray['body'] .= "[/share]";
1293 // Let retweets look like wall-to-wall posts
1294 $postarray['author-name'] = $post->retweeted_status->user->name;
1295 $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1296 $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1297 //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1298 // $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1299 // $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1307 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1309 $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1317 if (link_compare($user[0]["url"], $postarray['author-link']))
1320 $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1322 dbesc("twitter::".$own_id)
1325 if(!count($own_user))
1328 // Is it me from twitter?
1329 if (link_compare($own_user[0]["url"], $postarray['author-link']))
1332 $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1333 dbesc($postarray['parent-uri']),
1337 if(count($myconv)) {
1339 foreach($myconv as $conv) {
1340 // now if we find a match, it means we're in this conversation
1342 if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1345 require_once('include/enotify.php');
1347 $conv_parent = $conv['parent'];
1350 'type' => NOTIFY_COMMENT,
1351 'notify_flags' => $user[0]['notify-flags'],
1352 'language' => $user[0]['language'],
1353 'to_name' => $user[0]['username'],
1354 'to_email' => $user[0]['email'],
1355 'uid' => $user[0]['uid'],
1356 'item' => $postarray,
1357 'link' => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1358 'source_name' => $postarray['author-name'],
1359 'source_link' => $postarray['author-link'],
1360 'source_photo' => $postarray['author-avatar'],
1361 'verb' => ACTIVITY_POST,
1363 'parent' => $conv_parent,
1366 // only send one notification
1372 function twitter_fetchhometimeline($a, $uid) {
1373 $ckey = get_config('twitter', 'consumerkey');
1374 $csecret = get_config('twitter', 'consumersecret');
1375 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
1376 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1377 $create_user = get_pconfig($uid, 'twitter', 'create_user');
1379 logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1381 require_once('library/twitteroauth.php');
1382 require_once('include/items.php');
1384 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1386 $own_contact = twitter_fetch_own_contact($a, $uid);
1388 $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1389 intval($own_contact),
1393 $own_id = $r[0]["nick"];
1395 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1399 $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1405 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1409 $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1412 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1416 $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1417 //$parameters["count"] = 200;
1420 // Fetching timeline
1421 $lastid = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1423 $first_time = ($lastid == "");
1426 $parameters["since_id"] = $lastid;
1428 $items = $connection->get('statuses/home_timeline', $parameters);
1430 if (!is_array($items)) {
1431 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1435 $posts = array_reverse($items);
1437 logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1439 if (count($posts)) {
1440 foreach ($posts as $post) {
1441 if ($post->id_str > $lastid)
1442 $lastid = $post->id_str;
1447 $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1449 if (trim($postarray['body']) == "")
1452 $item = item_store($postarray);
1454 logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1457 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1461 set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1463 // Fetching mentions
1464 $lastid = get_pconfig($uid, 'twitter', 'lastmentionid');
1466 $first_time = ($lastid == "");
1469 $parameters["since_id"] = $lastid;
1471 $items = $connection->get('statuses/mentions_timeline', $parameters);
1473 if (!is_array($items)) {
1474 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1478 $posts = array_reverse($items);
1480 logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1482 if (count($posts)) {
1483 foreach ($posts as $post) {
1484 if ($post->id_str > $lastid)
1485 $lastid = $post->id_str;
1490 $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1492 if (trim($postarray['body']) == "")
1495 $item = item_store($postarray);
1497 logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1500 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1501 dbesc($postarray['uri']),
1505 $item = $r[0]['id'];
1509 require_once('include/enotify.php');
1511 'type' => NOTIFY_TAGSELF,
1512 'notify_flags' => $u[0]['notify-flags'],
1513 'language' => $u[0]['language'],
1514 'to_name' => $u[0]['username'],
1515 'to_email' => $u[0]['email'],
1516 'uid' => $u[0]['uid'],
1517 'item' => $postarray,
1518 'link' => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1519 'source_name' => $postarray['author-name'],
1520 'source_link' => $postarray['author-link'],
1521 'source_photo' => $postarray['author-avatar'],
1522 'verb' => ACTIVITY_TAG,
1529 set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1532 function twitter_fetch_own_contact($a, $uid) {
1533 $ckey = get_config('twitter', 'consumerkey');
1534 $csecret = get_config('twitter', 'consumersecret');
1535 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
1536 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1538 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1542 if ($own_id == "") {
1543 require_once('library/twitteroauth.php');
1545 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1547 // Fetching user data
1548 $user = $connection->get('account/verify_credentials');
1550 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1552 $contact_id = twitter_fetch_contact($uid, $user, true);
1555 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1556 intval($uid), dbesc("twitter::".$own_id));
1558 $contact_id = $r[0]["id"];
1560 del_pconfig($uid, 'twitter', 'own_id');
1564 return($contact_id);
1567 function twitter_is_retweet($a, $uid, $body) {
1568 $body = trim($body);
1570 // Skip if it isn't a pure repeated messages
1571 // Does it start with a share?
1572 if (strpos($body, "[share") > 0)
1575 // Does it end with a share?
1576 if (strlen($body) > (strrpos($body, "[/share]") + 8))
1579 $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1580 // Skip if there is no shared message in there
1581 if ($body == $attributes)
1585 preg_match("/link='(.*?)'/ism", $attributes, $matches);
1586 if ($matches[1] != "")
1587 $link = $matches[1];
1589 preg_match('/link="(.*?)"/ism', $attributes, $matches);
1590 if ($matches[1] != "")
1591 $link = $matches[1];
1593 $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1597 logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1599 $ckey = get_config('twitter', 'consumerkey');
1600 $csecret = get_config('twitter', 'consumersecret');
1601 $otoken = get_pconfig($uid, 'twitter', 'oauthtoken');
1602 $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1604 require_once('library/twitteroauth.php');
1605 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1607 $result = $connection->post('statuses/retweet/'.$id);
1609 logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1611 return(!isset($result->errors));