use Friendica\Content\OEmbed;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\Plaintext;
-use Friendica\Core\Config;
use Friendica\Core\Hook;
-use Friendica\Core\L10n;
use Friendica\Core\Logger;
-use Friendica\Core\PConfig;
use Friendica\Core\Protocol;
use Friendica\Core\Renderer;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
+use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
use Friendica\Model\Group;
use Friendica\Model\Item;
use Friendica\Model\ItemContent;
+use Friendica\Model\ItemURI;
+use Friendica\Model\Tag;
use Friendica\Model\User;
-use Friendica\Object\Image;
+use Friendica\Protocol\Activity;
use Friendica\Util\ConfigFileLoader;
use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Images;
use Friendica\Util\Network;
use Friendica\Util\Strings;
Hook::register('expire' , __FILE__, 'twitter_expire');
Hook::register('prepare_body' , __FILE__, 'twitter_prepare_body');
Hook::register('check_item_notification', __FILE__, 'twitter_check_item_notification');
- Logger::log("installed twitter");
+ Hook::register('probe_detect' , __FILE__, 'twitter_probe_detect');
+ Logger::info("installed twitter");
}
-function twitter_uninstall()
-{
- Hook::unregister('load_config' , __FILE__, 'twitter_load_config');
- Hook::unregister('connector_settings' , __FILE__, 'twitter_settings');
- Hook::unregister('connector_settings_post', __FILE__, 'twitter_settings_post');
- Hook::unregister('hook_fork' , __FILE__, 'twitter_hook_fork');
- Hook::unregister('post_local' , __FILE__, 'twitter_post_local');
- Hook::unregister('notifier_normal' , __FILE__, 'twitter_post_hook');
- Hook::unregister('jot_networks' , __FILE__, 'twitter_jot_nets');
- Hook::unregister('cron' , __FILE__, 'twitter_cron');
- Hook::unregister('follow' , __FILE__, 'twitter_follow');
- Hook::unregister('expire' , __FILE__, 'twitter_expire');
- Hook::unregister('prepare_body' , __FILE__, 'twitter_prepare_body');
- Hook::unregister('check_item_notification', __FILE__, 'twitter_check_item_notification');
-
- // old setting - remove only
- Hook::unregister('post_local_end' , __FILE__, 'twitter_post_hook');
- Hook::unregister('addon_settings' , __FILE__, 'twitter_settings');
- Hook::unregister('addon_settings_post', __FILE__, 'twitter_settings_post');
-}
+// Hook functions
function twitter_load_config(App $a, ConfigFileLoader $loader)
{
function twitter_check_item_notification(App $a, array &$notification_data)
{
- $own_id = PConfig::get($notification_data["uid"], 'twitter', 'own_id');
+ $own_id = DI::pConfig()->get($notification_data["uid"], 'twitter', 'own_id');
$own_user = q("SELECT `url` FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
intval($notification_data["uid"]),
function twitter_follow(App $a, array &$contact)
{
- Logger::log("twitter_follow: Check if contact is twitter contact. " . $contact["url"], Logger::DEBUG);
+ Logger::info('Check if contact is twitter contact', ['url' => $contact["url"]]);
if (!strstr($contact["url"], "://twitter.com") && !strstr($contact["url"], "@twitter.com")) {
return;
$uid = $a->user["uid"];
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
- $otoken = PConfig::get($uid, 'twitter', 'oauthtoken');
- $osecret = PConfig::get($uid, 'twitter', 'oauthsecret');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
+ $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
+ $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
// If the addon is not configured (general or for this user) quit here
if (empty($ckey) || empty($csecret) || empty($otoken) || empty($osecret)) {
$connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
$connection->post('friendships/create', ['screen_name' => $nickname]);
- twitter_fetchuser($a, $uid, $nickname);
+ $user = twitter_fetchuser($nickname);
- $r = q("SELECT name,nick,url,addr,batch,notify,poll,request,confirm,poco,photo,priority,network,alias,pubkey
- FROM `contact` WHERE `uid` = %d AND `nick` = '%s'",
- intval($uid),
- DBA::escape($nickname));
- if (DBA::isResult($r)) {
- $contact["contact"] = $r[0];
+ $contact_id = twitter_fetch_contact($uid, $user, true);
+
+ $contact = Contact::getById($contact_id, ['name', 'nick', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'photo', 'priority', 'network', 'alias', 'pubkey']);
+
+ if (DBA::isResult($contact)) {
+ $contact["contact"] = $contact;
}
}
return;
}
- if (PConfig::get(local_user(), 'twitter', 'post')) {
+ if (DI::pConfig()->get(local_user(), 'twitter', 'post')) {
$jotnets_fields[] = [
'type' => 'checkbox',
'field' => [
'twitter_enable',
- L10n::t('Post to Twitter'),
- PConfig::get(local_user(), 'twitter', 'post_by_default')
+ DI::l10n()->t('Post to Twitter'),
+ DI::pConfig()->get(local_user(), 'twitter', 'post_by_default')
]
];
}
* if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
* from the user configuration
*/
- PConfig::delete(local_user(), 'twitter', 'consumerkey');
- PConfig::delete(local_user(), 'twitter', 'consumersecret');
- PConfig::delete(local_user(), 'twitter', 'oauthtoken');
- PConfig::delete(local_user(), 'twitter', 'oauthsecret');
- PConfig::delete(local_user(), 'twitter', 'post');
- PConfig::delete(local_user(), 'twitter', 'post_by_default');
- PConfig::delete(local_user(), 'twitter', 'lastid');
- PConfig::delete(local_user(), 'twitter', 'mirror_posts');
- PConfig::delete(local_user(), 'twitter', 'import');
- PConfig::delete(local_user(), 'twitter', 'create_user');
- PConfig::delete(local_user(), 'twitter', 'own_id');
+ DI::pConfig()->delete(local_user(), 'twitter', 'consumerkey');
+ DI::pConfig()->delete(local_user(), 'twitter', 'consumersecret');
+ DI::pConfig()->delete(local_user(), 'twitter', 'oauthtoken');
+ DI::pConfig()->delete(local_user(), 'twitter', 'oauthsecret');
+ DI::pConfig()->delete(local_user(), 'twitter', 'post');
+ DI::pConfig()->delete(local_user(), 'twitter', 'post_by_default');
+ DI::pConfig()->delete(local_user(), 'twitter', 'lastid');
+ DI::pConfig()->delete(local_user(), 'twitter', 'mirror_posts');
+ DI::pConfig()->delete(local_user(), 'twitter', 'import');
+ DI::pConfig()->delete(local_user(), 'twitter', 'create_user');
+ DI::pConfig()->delete(local_user(), 'twitter', 'own_id');
} else {
if (isset($_POST['twitter-pin'])) {
// if the user supplied us with a PIN from Twitter, let the magic of OAuth happen
- Logger::log('got a Twitter PIN');
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
+ Logger::notice('got a Twitter PIN');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
// the token and secret for which the PIN was generated were hidden in the settings
// form as token and token2, we need a new connection to Twitter using these token
// and secret to request a Access Token with the PIN
try {
if (empty($_POST['twitter-pin'])) {
- throw new Exception(L10n::t('You submitted an empty PIN, please Sign In with Twitter again to get a new one.'));
+ throw new Exception(DI::l10n()->t('You submitted an empty PIN, please Sign In with Twitter again to get a new one.'));
}
$connection = new TwitterOAuth($ckey, $csecret, $_POST['twitter-token'], $_POST['twitter-token2']);
$token = $connection->oauth("oauth/access_token", ["oauth_verifier" => $_POST['twitter-pin']]);
// ok, now that we have the Access Token, save them in the user config
- PConfig::set(local_user(), 'twitter', 'oauthtoken', $token['oauth_token']);
- PConfig::set(local_user(), 'twitter', 'oauthsecret', $token['oauth_token_secret']);
- PConfig::set(local_user(), 'twitter', 'post', 1);
+ DI::pConfig()->set(local_user(), 'twitter', 'oauthtoken', $token['oauth_token']);
+ DI::pConfig()->set(local_user(), 'twitter', 'oauthsecret', $token['oauth_token_secret']);
+ DI::pConfig()->set(local_user(), 'twitter', 'post', 1);
} catch(Exception $e) {
info($e->getMessage());
} catch(TwitterOAuthException $e) {
info($e->getMessage());
}
// reload the Addon Settings page, if we don't do it see Bug #42
- $a->internalRedirect('settings/connectors');
+ DI::baseUrl()->redirect('settings/connectors');
} else {
// if no PIN is supplied in the POST variables, the user has changed the setting
// to post a tweet for every new __public__ posting to the wall
- PConfig::set(local_user(), 'twitter', 'post', intval($_POST['twitter-enable']));
- PConfig::set(local_user(), 'twitter', 'post_by_default', intval($_POST['twitter-default']));
- PConfig::set(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
- PConfig::set(local_user(), 'twitter', 'import', intval($_POST['twitter-import']));
- PConfig::set(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user']));
+ DI::pConfig()->set(local_user(), 'twitter', 'post', intval($_POST['twitter-enable']));
+ DI::pConfig()->set(local_user(), 'twitter', 'post_by_default', intval($_POST['twitter-default']));
+ DI::pConfig()->set(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
+ DI::pConfig()->set(local_user(), 'twitter', 'import', intval($_POST['twitter-import']));
+ DI::pConfig()->set(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user']));
if (!intval($_POST['twitter-mirror'])) {
- PConfig::delete(local_user(), 'twitter', 'lastid');
+ DI::pConfig()->delete(local_user(), 'twitter', 'lastid');
}
- info(L10n::t('Twitter settings updated.') . EOL);
+ info(DI::l10n()->t('Twitter settings updated.') . EOL);
}
}
}
if (!local_user()) {
return;
}
- $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->getBaseURL() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
+ DI::page()['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . DI::baseUrl()->get() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
/* * *
* 1) Check that we have global consumer key & secret
* 2) If no OAuthtoken & stuff is present, generate button to get some
* 3) Checkbox for "Send public notices (280 chars only)
*/
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
- $otoken = PConfig::get(local_user(), 'twitter', 'oauthtoken');
- $osecret = PConfig::get(local_user(), 'twitter', 'oauthsecret');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
+ $otoken = DI::pConfig()->get(local_user(), 'twitter', 'oauthtoken');
+ $osecret = DI::pConfig()->get(local_user(), 'twitter', 'oauthsecret');
- $enabled = intval(PConfig::get(local_user(), 'twitter', 'post'));
- $defenabled = intval(PConfig::get(local_user(), 'twitter', 'post_by_default'));
- $mirrorenabled = intval(PConfig::get(local_user(), 'twitter', 'mirror_posts'));
- $importenabled = intval(PConfig::get(local_user(), 'twitter', 'import'));
- $create_userenabled = intval(PConfig::get(local_user(), 'twitter', 'create_user'));
+ $enabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'post'));
+ $defenabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'post_by_default'));
+ $mirrorenabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'mirror_posts'));
+ $importenabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'import'));
+ $create_userenabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'create_user'));
$css = (($enabled) ? '' : '-disabled');
$s .= '<span id="settings_twitter_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
- $s .= '<img class="connector' . $css . '" src="images/twitter.png" /><h3 class="connector">' . L10n::t('Twitter Import/Export/Mirror') . '</h3>';
+ $s .= '<img class="connector' . $css . '" src="images/twitter.png" /><h3 class="connector">' . DI::l10n()->t('Twitter Import/Export/Mirror') . '</h3>';
$s .= '</span>';
$s .= '<div id="settings_twitter_expanded" class="settings-block" style="display: none;">';
$s .= '<span class="fakelink" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
- $s .= '<img class="connector' . $css . '" src="images/twitter.png" /><h3 class="connector">' . L10n::t('Twitter Import/Export/Mirror') . '</h3>';
+ $s .= '<img class="connector' . $css . '" src="images/twitter.png" /><h3 class="connector">' . DI::l10n()->t('Twitter Import/Export/Mirror') . '</h3>';
$s .= '</span>';
if ((!$ckey) && (!$csecret)) {
/* no global consumer keys
* display warning and skip personal config
*/
- $s .= '<p>' . L10n::t('No consumer key pair for Twitter found. Please contact your site administrator.') . '</p>';
+ $s .= '<p>' . DI::l10n()->t('No consumer key pair for Twitter found. Please contact your site administrator.') . '</p>';
} else {
// ok we have a consumer key pair now look into the OAuth stuff
if ((!$otoken) && (!$osecret)) {
$connection = new TwitterOAuth($ckey, $csecret);
try {
$result = $connection->oauth('oauth/request_token', ['oauth_callback' => 'oob']);
- $s .= '<p>' . L10n::t('At this Friendica instance the Twitter addon 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>';
- $s .= '<a href="' . $connection->url('oauth/authorize', ['oauth_token' => $result['oauth_token']]) . '" target="_twitter"><img src="addon/twitter/lighter.png" alt="' . L10n::t('Log in with Twitter') . '"></a>';
+ $s .= '<p>' . DI::l10n()->t('At this Friendica instance the Twitter addon 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>';
+ $s .= '<a href="' . $connection->url('oauth/authorize', ['oauth_token' => $result['oauth_token']]) . '" target="_twitter"><img src="addon/twitter/lighter.png" alt="' . DI::l10n()->t('Log in with Twitter') . '"></a>';
$s .= '<div id="twitter-pin-wrapper">';
- $s .= '<label id="twitter-pin-label" for="twitter-pin">' . L10n::t('Copy the PIN from Twitter here') . '</label>';
+ $s .= '<label id="twitter-pin-label" for="twitter-pin">' . DI::l10n()->t('Copy the PIN from Twitter here') . '</label>';
$s .= '<input id="twitter-pin" type="text" name="twitter-pin" />';
$s .= '<input id="twitter-token" type="hidden" name="twitter-token" value="' . $result['oauth_token'] . '" />';
$s .= '<input id="twitter-token2" type="hidden" name="twitter-token2" value="' . $result['oauth_token_secret'] . '" />';
$s .= '</div><div class="clear"></div>';
- $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . L10n::t('Save Settings') . '" /></div>';
+ $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . DI::l10n()->t('Save Settings') . '" /></div>';
} catch (TwitterOAuthException $e) {
- $s .= '<p>' . L10n::t('An error occured: ') . $e->getMessage() . '</p>';
+ $s .= '<p>' . DI::l10n()->t('An error occured: ') . $e->getMessage() . '</p>';
}
} else {
/* * *
property_exists($details, 'description') &&
property_exists($details, 'profile_image_url')) {
$s .= '<div id="twitter-info" >
- <p>' . L10n::t('Currently connected to: ') . '<a href="https://twitter.com/' . $details->screen_name . '" target="_twitter">' . $details->screen_name . '</a>
- <button type="submit" name="twitter-disconnect" value="1">' . L10n::t('Disconnect') . '</button>
+ <p>' . DI::l10n()->t('Currently connected to: ') . '<a href="https://twitter.com/' . $details->screen_name . '" target="_twitter">' . $details->screen_name . '</a>
+ <button type="submit" name="twitter-disconnect" value="1">' . DI::l10n()->t('Disconnect') . '</button>
</p>
<p id="twitter-info-block">
<a href="https://twitter.com/' . $details->screen_name . '" target="_twitter"><img id="twitter-avatar" src="' . $details->profile_image_url . '" /></a>
$s .= '<div class="clear"></div>';
$s .= Renderer::replaceMacros($field_checkbox, [
- '$field' => ['twitter-enable', L10n::t('Allow posting to Twitter'), $enabled, L10n::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.')]
+ '$field' => ['twitter-enable', DI::l10n()->t('Allow posting to Twitter'), $enabled, DI::l10n()->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.')]
]);
if ($a->user['hidewall']) {
- $s .= '<p>' . L10n::t('<strong>Note</strong>: Due to 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>';
+ $s .= '<p>' . DI::l10n()->t('<strong>Note</strong>: Due to 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>';
}
$s .= Renderer::replaceMacros($field_checkbox, [
- '$field' => ['twitter-default', L10n::t('Send public postings to Twitter by default'), $defenabled, '']
+ '$field' => ['twitter-default', DI::l10n()->t('Send public postings to Twitter by default'), $defenabled, '']
]);
$s .= Renderer::replaceMacros($field_checkbox, [
- '$field' => ['twitter-mirror', L10n::t('Mirror all posts from twitter that are no replies'), $mirrorenabled, '']
+ '$field' => ['twitter-mirror', DI::l10n()->t('Mirror all posts from twitter that are no replies'), $mirrorenabled, '']
]);
$s .= Renderer::replaceMacros($field_checkbox, [
- '$field' => ['twitter-import', L10n::t('Import the remote timeline'), $importenabled, '']
+ '$field' => ['twitter-import', DI::l10n()->t('Import the remote timeline'), $importenabled, '']
]);
$s .= Renderer::replaceMacros($field_checkbox, [
- '$field' => ['twitter-create_user', L10n::t('Automatically create contacts'), $create_userenabled, L10n::t('This will automatically create a contact in Friendica as soon as you receive a message from an existing contact via the Twitter network. If you do not enable this, you need to manually add those Twitter contacts in Friendica from whom you would like to see posts here. However if enabled, you cannot merely remove a twitter contact from the Friendica contact list, as it will recreate this contact when they post again.')]
+ '$field' => ['twitter-create_user', DI::l10n()->t('Automatically create contacts'), $create_userenabled, DI::l10n()->t('This will automatically create a contact in Friendica as soon as you receive a message from an existing contact via the Twitter network. If you do not enable this, you need to manually add those Twitter contacts in Friendica from whom you would like to see posts here. However if enabled, you cannot merely remove a twitter contact from the Friendica contact list, as it will recreate this contact when they post again.')]
]);
$s .= '<div class="clear"></div>';
- $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . L10n::t('Save Settings') . '" /></div>';
+ $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . DI::l10n()->t('Save Settings') . '" /></div>';
} catch (TwitterOAuthException $e) {
- $s .= '<p>' . L10n::t('An error occured: ') . $e->getMessage() . '</p>';
+ $s .= '<p>' . DI::l10n()->t('An error occured: ') . $e->getMessage() . '</p>';
}
}
}
return;
}
- if (PConfig::get($post['uid'], 'twitter', 'import')) {
+ if (DI::pConfig()->get($post['uid'], 'twitter', 'import')) {
// Don't fork if it isn't a reply to a twitter post
if (($post['parent'] != $post['id']) && !Item::exists(['id' => $post['parent'], 'network' => Protocol::TWITTER])) {
- Logger::log('No twitter parent found for item ' . $post['id']);
+ Logger::notice('No twitter parent found', ['item' => $post['id']]);
$b['execute'] = false;
return;
}
return;
}
- $twitter_post = intval(PConfig::get(local_user(), 'twitter', 'post'));
+ $twitter_post = intval(DI::pConfig()->get(local_user(), 'twitter', 'post'));
$twitter_enable = (($twitter_post && !empty($_REQUEST['twitter_enable'])) ? intval($_REQUEST['twitter_enable']) : 0);
// if API is used, default to the chosen settings
- if ($b['api_source'] && intval(PConfig::get(local_user(), 'twitter', 'post_by_default'))) {
+ if ($b['api_source'] && intval(DI::pConfig()->get(local_user(), 'twitter', 'post_by_default'))) {
$twitter_enable = 1;
}
$b['postopts'] .= 'twitter';
}
+function twitter_probe_detect(App $a, array &$hookData)
+{
+ // Don't overwrite an existing result
+ if ($hookData['result']) {
+ return;
+ }
+
+ // Avoid a lookup for the wrong network
+ if (!in_array($hookData['network'], ['', Protocol::TWITTER])) {
+ return;
+ }
+
+ if (preg_match('=(.*)@twitter.com=i', $hookData['uri'], $matches)) {
+ $nick = $matches[1];
+ } elseif (preg_match('=https?://(?:mobile\.)?twitter.com/(.*)=i', $hookData['uri'], $matches)) {
+ $nick = $matches[1];
+ } else {
+ return;
+ }
+
+ $user = twitter_fetchuser($nick);
+
+ if ($user) {
+ $hookData['result'] = twitter_user_to_contact($user);
+ }
+}
+
function twitter_action(App $a, $uid, $pid, $action)
{
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
- $otoken = PConfig::get($uid, 'twitter', 'oauthtoken');
- $osecret = PConfig::get($uid, 'twitter', 'oauthsecret');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
+ $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
+ $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
$connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
$post = ['id' => $pid];
- Logger::log("twitter_action '" . $action . "' ID: " . $pid . " data: " . print_r($post, true), Logger::DATA);
+ Logger::debug('before action', ['action' => $action, 'pid' => $pid, 'data' => $post]);
switch ($action) {
- case "delete":
+ case 'delete':
// To-Do: $result = $connection->post('statuses/destroy', $post);
$result = [];
break;
- case "like":
+ case 'like':
$result = $connection->post('favorites/create', $post);
+ if ($connection->getLastHttpCode() != 200) {
+ Logger::error('Unable to create favorite', ['result' => $result]);
+ }
break;
- case "unlike":
+ case 'unlike':
$result = $connection->post('favorites/destroy', $post);
+ if ($connection->getLastHttpCode() != 200) {
+ Logger::error('Unable to destroy favorite', ['result' => $result]);
+ }
break;
default:
- Logger::log('Unhandled action ' . $action, Logger::DEBUG);
+ Logger::warning('Unhandled action', ['action' => $action]);
$result = [];
}
- Logger::log("twitter_action '" . $action . "' send, result: " . print_r($result, true), Logger::DEBUG);
+
+ Logger::info('after action', ['action' => $action, 'result' => $result]);
}
function twitter_post_hook(App $a, array &$b)
{
// Post to Twitter
- if (!PConfig::get($b["uid"], 'twitter', 'import')
+ if (!DI::pConfig()->get($b["uid"], 'twitter', 'import')
&& ($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))) {
return;
}
}
}
- if (($b['verb'] == ACTIVITY_POST) && $b['deleted']) {
+ if (($b['verb'] == Activity::POST) && $b['deleted']) {
twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
}
- if ($b['verb'] == ACTIVITY_LIKE) {
+ if ($b['verb'] == Activity::LIKE) {
Logger::log("twitter_post_hook: parameter 2 " . substr($b["thr-parent"], 9), Logger::DEBUG);
if ($b['deleted']) {
twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
Logger::notice('twitter post invoked', ['id' => $b['id'], 'guid' => $b['guid']]);
- PConfig::load($b['uid'], 'twitter');
+ DI::pConfig()->load($b['uid'], 'twitter');
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
- $otoken = PConfig::get($b['uid'], 'twitter', 'oauthtoken');
- $osecret = PConfig::get($b['uid'], 'twitter', 'oauthsecret');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
+ $otoken = DI::pConfig()->get($b['uid'], 'twitter', 'oauthtoken');
+ $osecret = DI::pConfig()->get($b['uid'], 'twitter', 'oauthsecret');
if ($ckey && $csecret && $otoken && $osecret) {
Logger::log('twitter: we have customer key and oauth stuff, going to send.', Logger::DEBUG);
$b['body'] = twitter_update_mentions($b['body']);
- $msgarr = ItemContent::getPlaintextPost($b, $max_char, true, 8);
- Logger::info('Got plaintext', $msgarr);
+ $msgarr = ItemContent::getPlaintextPost($b, $max_char, true, BBCode::TWITTER);
+ Logger::info('Got plaintext', ['id' => $b['id'], 'message' => $msgarr]);
$msg = $msgarr["text"];
if (($msg == "") && isset($msgarr["title"])) {
$msg = Plaintext::shorten($msgarr["title"], $max_char - 50);
}
- if (($msgarr['url'] == $b['plink']) && !empty($msgarr['images']) && (count($msgarr['images']) <= 4)) {
+ if (!empty($msgarr['url']) && ($msgarr['url'] == $b['plink']) && !empty($msgarr['images']) && (count($msgarr['images']) <= 4)) {
$url_added = false;
} elseif (isset($msgarr["url"]) && ($msgarr["type"] != "photo")) {
$msg .= "\n" . $msgarr["url"];
}
if (empty($msg)) {
+ Logger::info('Empty message', ['id' => $b['id']]);
return;
}
$post = [];
if (!empty($msgarr['images'])) {
+ Logger::info('Got images', ['id' => $b['id'], 'images' => $msgarr['images']]);
try {
$media_ids = [];
foreach ($msgarr['images'] as $image) {
$tempfile = tempnam(get_temppath(), 'cache');
file_put_contents($tempfile, $img_str);
+ Logger::info('Uploading', ['id' => $b['id'], 'image' => $image['url']]);
$media = $connection->upload('media/upload', ['media' => $tempfile]);
unlink($tempfile);
$data = ['media_id' => $media->media_id_string,
'alt_text' => ['text' => substr($image['description'], 0, 420)]];
$ret = $cb->media_metadata_create($data);
- Logger::info('Metadata create', ['data' => $data, 'return' => json_encode($ret)]);
+ Logger::info('Metadata create', ['id' => $b['id'], 'data' => $data, 'return' => json_encode($ret)]);
}
} else {
- throw new Exception('Failed upload of ' . $image['url']);
+ throw new Exception('Failed upload', ['id' => $b['id'], 'image' => $image['url']]);
}
}
$post['media_ids'] = implode(',', $media_ids);
unset($post['media_ids']);
}
} catch (Exception $e) {
- Logger::log('Exception when trying to send to Twitter: ' . $e->getMessage());
+ Logger::info('Exception when trying to send to Twitter', ['id' => $b['id'], 'message' => $e->getMessage()]);
}
}
$url = 'statuses/update';
$result = $connection->post($url, $post);
- Logger::log('twitter_post send, result: ' . print_r($result, true), Logger::DEBUG);
+ Logger::info('twitter_post send', ['id' => $b['id'], 'result' => $result]);
if (!empty($result->source)) {
- Config::set("twitter", "application_name", strip_tags($result->source));
+ DI::config()->set("twitter", "application_name", strip_tags($result->source));
}
if (!empty($result->errors)) {
- Logger::log('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
+ Logger::info('Send to Twitter failed', ['id' => $b['id'], 'error' => $result->errors]);
Worker::defer();
} elseif ($iscomment) {
- Logger::log('twitter_post: Update extid ' . $result->id_str . " for post id " . $b['id']);
+ Logger::info('Update extid', ['id' => $b['id'], 'extid' => $result->id_str]);
Item::update(['extid' => "twitter::" . $result->id_str], ['id' => $b['id']]);
}
}
{
$consumerkey = !empty($_POST['consumerkey']) ? Strings::escapeTags(trim($_POST['consumerkey'])) : '';
$consumersecret = !empty($_POST['consumersecret']) ? Strings::escapeTags(trim($_POST['consumersecret'])) : '';
- Config::set('twitter', 'consumerkey', $consumerkey);
- Config::set('twitter', 'consumersecret', $consumersecret);
- info(L10n::t('Settings updated.') . EOL);
+ DI::config()->set('twitter', 'consumerkey', $consumerkey);
+ DI::config()->set('twitter', 'consumersecret', $consumersecret);
+ info(DI::l10n()->t('Settings updated.') . EOL);
}
function twitter_addon_admin(App $a, &$o)
$t = Renderer::getMarkupTemplate("admin.tpl", "addon/twitter/");
$o = Renderer::replaceMacros($t, [
- '$submit' => L10n::t('Save Settings'),
+ '$submit' => DI::l10n()->t('Save Settings'),
// name, label, value, help, [extra values]
- '$consumerkey' => ['consumerkey', L10n::t('Consumer key'), Config::get('twitter', 'consumerkey'), ''],
- '$consumersecret' => ['consumersecret', L10n::t('Consumer secret'), Config::get('twitter', 'consumersecret'), ''],
+ '$consumerkey' => ['consumerkey', DI::l10n()->t('Consumer key'), DI::config()->get('twitter', 'consumerkey'), ''],
+ '$consumersecret' => ['consumersecret', DI::l10n()->t('Consumer secret'), DI::config()->get('twitter', 'consumersecret'), ''],
]);
}
function twitter_cron(App $a)
{
- $last = Config::get('twitter', 'last_poll');
+ $last = DI::config()->get('twitter', 'last_poll');
- $poll_interval = intval(Config::get('twitter', 'poll_interval'));
+ $poll_interval = intval(DI::config()->get('twitter', 'poll_interval'));
if (!$poll_interval) {
$poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
}
if ($last) {
$next = $last + ($poll_interval * 60);
if ($next > time()) {
- Logger::log('twitter: poll intervall not reached');
+ Logger::notice('twitter: poll intervall not reached');
return;
}
}
- Logger::log('twitter: cron_start');
+ Logger::notice('twitter: cron_start');
$r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1'");
if (DBA::isResult($r)) {
foreach ($r as $rr) {
- Logger::log('twitter: fetching for user ' . $rr['uid']);
+ Logger::notice('Fetching', ['user' => $rr['uid']]);
Worker::add(['priority' => PRIORITY_MEDIUM, 'force_priority' => true], "addon/twitter/twitter_sync.php", 1, (int) $rr['uid']);
}
}
- $abandon_days = intval(Config::get('system', 'account_abandon_days'));
+ $abandon_days = intval(DI::config()->get('system', 'account_abandon_days'));
if ($abandon_days < 1) {
$abandon_days = 0;
}
if ($abandon_days != 0) {
$user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit);
if (!DBA::isResult($user)) {
- Logger::log('abandoned account: timeline from user ' . $rr['uid'] . ' will not be imported');
+ Logger::notice('abandoned account: timeline from user will not be imported', ['user' => $rr['uid']]);
continue;
}
}
- Logger::log('twitter: importing timeline from user ' . $rr['uid']);
+ Logger::notice('importing timeline', ['user' => $rr['uid']]);
Worker::add(['priority' => PRIORITY_MEDIUM, 'force_priority' => true], "addon/twitter/twitter_sync.php", 2, (int) $rr['uid']);
/*
// To-Do
// check for new contacts once a day
- $last_contact_check = PConfig::get($rr['uid'],'pumpio','contact_check');
+ $last_contact_check = DI::pConfig()->get($rr['uid'],'pumpio','contact_check');
if($last_contact_check)
$next_contact_check = $last_contact_check + 86400;
else
if($next_contact_check <= time()) {
pumpio_getallusers($a, $rr["uid"]);
- PConfig::set($rr['uid'],'pumpio','contact_check',time());
+ DI::pConfig()->set($rr['uid'],'pumpio','contact_check',time());
}
*/
}
}
- Logger::log('twitter: cron_end');
+ Logger::notice('twitter: cron_end');
- Config::set('twitter', 'last_poll', time());
+ DI::config()->set('twitter', 'last_poll', time());
}
function twitter_expire(App $a)
{
- $days = Config::get('twitter', 'expire');
+ $days = DI::config()->get('twitter', 'expire');
if ($days == 0) {
return;
}
- $r = Item::select(['id'], ['deleted' => true, 'network' => Protocol::TWITTER]);
+ $r = Item::select(['id', 'guid'], ['deleted' => true, 'network' => Protocol::TWITTER]);
while ($row = DBA::fetch($r)) {
+ Logger::info('[twitter] Delete expired item', ['id' => $row['id'], 'guid' => $row['guid'], 'callstack' => \Friendica\Core\System::callstack()]);
DBA::delete('item', ['id' => $row['id']]);
}
DBA::close($r);
- Logger::log('twitter_expire: expire_start');
+ Logger::notice('twitter_expire: expire_start');
$r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
if (DBA::isResult($r)) {
foreach ($r as $rr) {
- Logger::log('twitter_expire: user ' . $rr['uid']);
+ Logger::notice('twitter_expire', ['user' => $rr['uid']]);
Item::expire($rr['uid'], $days, Protocol::TWITTER, true);
}
}
- Logger::log('twitter_expire: expire_end');
+ Logger::notice('twitter_expire: expire_end');
}
function twitter_prepare_body(App $a, array &$b)
if ($b["preview"]) {
$max_char = 280;
$item = $b["item"];
- $item["plink"] = $a->getBaseURL() . "/display/" . $item["guid"];
+ $item["plink"] = DI::baseUrl()->get() . "/display/" . $item["guid"];
$condition = ['uri' => $item["thr-parent"], 'uid' => local_user()];
$orig_post = Item::selectFirst(['author-link'], $condition);
}
}
- $msgarr = ItemContent::getPlaintextPost($item, $max_char, true, 8);
+ $msgarr = ItemContent::getPlaintextPost($item, $max_char, true, BBCode::TWITTER);
$msg = $msgarr["text"];
if (isset($msgarr["url"]) && ($msgarr["type"] != "photo")) {
if (!empty($post->retweeted_status)) {
// We don't support nested shares, so we mustn't show quotes as shares on retweets
- $item = twitter_createpost($a, $uid, $post->retweeted_status, ['id' => 0], false, false, true);
+ $item = twitter_createpost($a, $uid, $post->retweeted_status, ['id' => 0], false, false, true, -1);
if (empty($item['body'])) {
return [];
}
- $datarray['body'] = "\n" . share_header(
+ $datarray['body'] = "\n" . BBCode::getShareOpeningTag(
$item['author-name'],
$item['author-link'],
$item['author-avatar'],
- '',
- $item['created'],
- $item['plink']
+ $item['plink'],
+ $item['created']
);
$datarray['body'] .= $item['body'] . '[/share]';
} else {
- $item = twitter_createpost($a, $uid, $post, ['id' => 0], false, false, false);
+ $item = twitter_createpost($a, $uid, $post, ['id' => 0], false, false, false, -1);
if (empty($item['body'])) {
return [];
function twitter_fetchtimeline(App $a, $uid)
{
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
- $otoken = PConfig::get($uid, 'twitter', 'oauthtoken');
- $osecret = PConfig::get($uid, 'twitter', 'oauthsecret');
- $lastid = PConfig::get($uid, 'twitter', 'lastid');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
+ $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
+ $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
+ $lastid = DI::pConfig()->get($uid, 'twitter', 'lastid');
- $application_name = Config::get('twitter', 'application_name');
+ $application_name = DI::config()->get('twitter', 'application_name');
if ($application_name == "") {
- $application_name = $a->getHostName();
+ $application_name = DI::baseUrl()->getHostname();
}
$has_picture = false;
try {
$items = $connection->get('statuses/user_timeline', $parameters);
} catch (TwitterOAuthException $e) {
- Logger::log('Error fetching timeline for user ' . $uid . ': ' . $e->getMessage());
+ Logger::notice('Error fetching timeline', ['user' => $uid, 'message' => $e->getMessage()]);
return;
}
if (!is_array($items)) {
- Logger::log('No items for user ' . $uid, Logger::INFO);
+ Logger::notice('No items', ['user' => $uid]);
return;
}
foreach ($posts as $post) {
if ($post->id_str > $lastid) {
$lastid = $post->id_str;
- PConfig::set($uid, 'twitter', 'lastid', $lastid);
+ DI::pConfig()->set($uid, 'twitter', 'lastid', $lastid);
}
if ($first_time) {
}
}
}
- PConfig::set($uid, 'twitter', 'lastid', $lastid);
+ DI::pConfig()->set($uid, 'twitter', 'lastid', $lastid);
Logger::log('Last ID for user ' . $uid . ' is now ' . $lastid, Logger::DEBUG);
}
{
$new_avatar = str_replace("_normal.", ".", $avatar);
- $info = Image::getInfoFromURL($new_avatar);
+ $info = Images::getInfoFromURLCached($new_avatar);
if (!$info) {
$new_avatar = $avatar;
}
return $new_avatar;
}
-function twitter_fetch_contact($uid, $data, $create_user)
+function twitter_get_relation($uid, $target, $contact = [])
+{
+ if (isset($contact['rel'])) {
+ $relation = $contact['rel'];
+ } else {
+ $relation = 0;
+ }
+
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
+ $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
+ $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
+ $own_id = DI::pConfig()->get($uid, 'twitter', 'own_id');
+
+ $connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
+ $parameters = ['source_id' => $own_id, 'target_screen_name' => $target];
+
+ try {
+ $status = $connection->get('friendships/show', $parameters);
+ if ($connection->getLastHttpCode() !== 200) {
+ throw new Exception($status->errors[0]->message ?? 'HTTP response code ' . $connection->getLastHttpCode(), $status->errors[0]->code ?? $connection->getLastHttpCode());
+ }
+
+ $following = $status->relationship->source->following;
+ $followed = $status->relationship->source->followed_by;
+
+ if ($following && !$followed) {
+ $relation = Contact::SHARING;
+ } elseif (!$following && $followed) {
+ $relation = Contact::FOLLOWER;
+ } elseif ($following && $followed) {
+ $relation = Contact::FRIEND;
+ } elseif (!$following && !$followed) {
+ $relation = 0;
+ }
+
+ Logger::info('Fetched friendship relation', ['user' => $uid, 'target' => $target, 'relation' => $relation]);
+ } catch (Throwable $e) {
+ Logger::error('Error fetching friendship status', ['user' => $uid, 'target' => $target, 'message' => $e->getMessage()]);
+ }
+
+ return $relation;
+}
+
+/**
+ * @param $data
+ * @return array
+ */
+function twitter_user_to_contact($data)
{
if (empty($data->id_str)) {
+ return [];
+ }
+
+ $baseurl = 'https://twitter.com';
+ $url = $baseurl . '/' . $data->screen_name;
+ $addr = $data->screen_name . '@twitter.com';
+
+ $fields = [
+ 'url' => $url,
+ 'network' => Protocol::TWITTER,
+ 'alias' => 'twitter::' . $data->id_str,
+ 'baseurl' => $baseurl,
+ 'name' => $data->name,
+ 'nick' => $data->screen_name,
+ 'addr' => $addr,
+ 'location' => $data->location,
+ 'about' => $data->description,
+ 'photo' => twitter_fix_avatar($data->profile_image_url_https),
+ ];
+
+ return $fields;
+}
+
+function twitter_fetch_contact($uid, $data, $create_user)
+{
+ $fields = twitter_user_to_contact($data);
+
+ if (empty($fields)) {
return -1;
}
- $avatar = twitter_fix_avatar($data->profile_image_url_https);
- $url = "https://twitter.com/" . $data->screen_name;
- $addr = $data->screen_name . "@twitter.com";
+ // photo comes from twitter_user_to_contact but shouldn't be saved directly in the contact row
+ $avatar = $fields['photo'];
+ unset($fields['photo']);
- $fields = ['url' => $url, 'network' => Protocol::TWITTER,
- 'name' => $data->name, 'nick' => $data->screen_name, 'addr' => $addr,
- 'location' => $data->location, 'about' => $data->description];
+ // Update the public contact
+ $pcontact = DBA::selectFirst('contact', ['id'], ['uid' => 0, 'alias' => "twitter::" . $data->id_str]);
+ if (DBA::isResult($pcontact)) {
+ $cid = $pcontact['id'];
+ } else {
+ $cid = Contact::getIdForURL($fields['url'], 0, true, $fields);
+ }
- $cid = Contact::getIdForURL($url, 0, true, $fields);
if (!empty($cid)) {
DBA::update('contact', $fields, ['id' => $cid]);
Contact::updateAvatar($avatar, 0, $cid);
$contact = DBA::selectFirst('contact', [], ['uid' => $uid, 'alias' => "twitter::" . $data->id_str]);
if (!DBA::isResult($contact) && !$create_user) {
+ Logger::info('User contact not found', ['uid' => $uid, 'twitter-id' => $data->id_str]);
return 0;
}
if (!DBA::isResult($contact)) {
+ $relation = twitter_get_relation($uid, $data->screen_name);
+
// create contact record
$fields['uid'] = $uid;
$fields['created'] = DateTimeFormat::utcNow();
- $fields['nurl'] = Strings::normaliseLink($url);
- $fields['alias'] = 'twitter::' . $data->id_str;
+ $fields['nurl'] = Strings::normaliseLink($fields['url']);
$fields['poll'] = 'twitter::' . $data->id_str;
- $fields['rel'] = Contact::FRIEND;
+ $fields['rel'] = $relation;
$fields['priority'] = 1;
$fields['writable'] = true;
$fields['blocked'] = false;
}
$contact_id = $contact['id'];
+ $update = false;
- // update profile photos once every twelve hours as we have no notification of when they change.
- $update_photo = ($contact['avatar-date'] < DateTimeFormat::utc('now -12 hours'));
+ // Update the contact relation once per day
+ if ($contact['updated'] < DateTimeFormat::utc('now -24 hours')) {
+ $fields['rel'] = twitter_get_relation($uid, $data->screen_name, $contact);
+ $update = true;
+ }
- // check that we have all the photos, this has been known to fail on occasion
- if (empty($contact['photo']) || empty($contact['thumb']) || empty($contact['micro']) || $update_photo) {
- Logger::log("twitter_fetch_contact: Updating contact " . $data->screen_name, Logger::DEBUG);
+ Contact::updateAvatar($avatar, $uid, $contact['id']);
- Contact::updateAvatar($avatar, $uid, $contact['id']);
+ if ($contact['name'] != $data->name) {
+ $fields['name-date'] = $fields['uri-date'] = DateTimeFormat::utcNow();
+ $update = true;
+ }
- $fields['name-date'] = DateTimeFormat::utcNow();
+ if ($contact['nick'] != $data->screen_name) {
$fields['uri-date'] = DateTimeFormat::utcNow();
+ $update = true;
+ }
+ if (($contact['location'] != $data->location) || ($contact['about'] != $data->description)) {
+ $update = true;
+ }
+
+ if ($update) {
+ $fields['updated'] = DateTimeFormat::utcNow();
DBA::update('contact', $fields, ['id' => $contact['id']]);
+ Logger::info('Updated contact', ['id' => $contact['id'], 'nick' => $data->screen_name]);
}
}
return $contact_id;
}
-function twitter_fetchuser(App $a, $uid, $screen_name = "", $user_id = "")
+/**
+ * @param string $screen_name
+ * @return stdClass|null
+ * @throws Exception
+ */
+function twitter_fetchuser($screen_name)
{
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
- $otoken = PConfig::get($uid, 'twitter', 'oauthtoken');
- $osecret = PConfig::get($uid, 'twitter', 'oauthsecret');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
- $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
- intval($uid));
-
- if (DBA::isResult($r)) {
- $self = $r[0];
- } else {
- return;
- }
-
- $parameters = [];
-
- if ($screen_name != "") {
- $parameters["screen_name"] = $screen_name;
- }
-
- if ($user_id != "") {
- $parameters["user_id"] = $user_id;
- }
-
- // Fetching user data
- $connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
try {
+ // Fetching user data
+ $connection = new TwitterOAuth($ckey, $csecret);
+ $parameters = ['screen_name' => $screen_name];
$user = $connection->get('users/show', $parameters);
} catch (TwitterOAuthException $e) {
- Logger::log('twitter_fetchuser: Error fetching user ' . $uid . ': ' . $e->getMessage());
- return;
+ Logger::log('twitter_fetchuser: Error fetching user ' . $screen_name . ': ' . $e->getMessage());
+ return null;
}
if (!is_object($user)) {
- return;
+ return null;
}
- $contact_id = twitter_fetch_contact($uid, $user, true);
-
- return $contact_id;
+ return $user;
}
-function twitter_expand_entities(App $a, $body, $item, $picture)
+/**
+ * Replaces Twitter entities with Friendica-friendly links.
+ *
+ * The Twitter API gives indices for each entity, which allows for fine-grained replacement.
+ *
+ * First, we need to collect everything that needs to be replaced, what we will replace it with, and the start index.
+ * Then we sort the indices decreasingly, and we replace from the end of the body to the start in order for the next
+ * index to be correct even after the last replacement.
+ *
+ * @param string $body
+ * @param stdClass $status
+ * @param string $picture
+ * @return array
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ */
+function twitter_expand_entities($body, stdClass $status, $picture)
{
$plain = $body;
- $tags_arr = [];
+ $taglist = [];
- foreach ($item->entities->hashtags AS $hashtag) {
- $url = '#[url=' . $a->getBaseURL() . '/search?tag=' . $hashtag->text . ']' . $hashtag->text . '[/url]';
- $tags_arr['#' . $hashtag->text] = $url;
- $body = str_replace('#' . $hashtag->text, $url, $body);
- }
+ $replacementList = [];
- foreach ($item->entities->user_mentions AS $mention) {
- $url = '@[url=https://twitter.com/' . rawurlencode($mention->screen_name) . ']' . $mention->screen_name . '[/url]';
- $tags_arr['@' . $mention->screen_name] = $url;
- $body = str_replace('@' . $mention->screen_name, $url, $body);
- }
+ foreach ($status->entities->hashtags AS $hashtag) {
+ $replace = '#[url=' . DI::baseUrl()->get() . '/search?tag=' . $hashtag->text . ']' . $hashtag->text . '[/url]';
+ $taglist['#' . $hashtag->text] = ['#', $hashtag->text, ''];
- if (isset($item->entities->urls)) {
- $type = '';
- $footerurl = '';
- $footerlink = '';
- $footer = '';
-
- foreach ($item->entities->urls as $url) {
- $plain = str_replace($url->url, '', $plain);
+ $replacementList[$hashtag->indices[0]] = [
+ 'replace' => $replace,
+ 'length' => $hashtag->indices[1] - $hashtag->indices[0],
+ ];
+ }
- if ($url->url && $url->expanded_url && $url->display_url) {
- // Quote tweet, we just remove the quoted tweet URL from the body, the share block will be added later.
- if (isset($item->quoted_status_id_str)
- && substr($url->expanded_url, -strlen($item->quoted_status_id_str)) == $item->quoted_status_id_str ) {
- $body = str_replace($url->url, '', $body);
- continue;
- }
+ foreach ($status->entities->user_mentions AS $mention) {
+ $replace = '@[url=https://twitter.com/' . rawurlencode($mention->screen_name) . ']' . $mention->screen_name . '[/url]';
+ $taglist['@' . $mention->screen_name] = ['@', $mention->screen_name, 'https://twitter.com/' . rawurlencode($mention->screen_name)];
- $expanded_url = $url->expanded_url;
+ $replacementList[$mention->indices[0]] = [
+ 'replace' => $replace,
+ 'length' => $mention->indices[1] - $mention->indices[0],
+ ];
+ }
- $final_url = Network::finalUrl($url->expanded_url);
+ // This URL if set will be used to add an attachment at the bottom of the post
+ $attachmentUrl = '';
- $oembed_data = OEmbed::fetchURL($final_url);
+ foreach ($status->entities->urls ?? [] as $url) {
+ $plain = str_replace($url->url, '', $plain);
- if (empty($oembed_data) || empty($oembed_data->type)) {
- continue;
- }
+ if ($url->url && $url->expanded_url && $url->display_url) {
- // Quickfix: Workaround for URL with '[' and ']' in it
- if (strpos($expanded_url, '[') || strpos($expanded_url, ']')) {
- $expanded_url = $url->url;
- }
+ // Quote tweet, we just remove the quoted tweet URL from the body, the share block will be added later.
+ if (!empty($status->quoted_status) && isset($status->quoted_status_id_str)
+ && substr($url->expanded_url, -strlen($status->quoted_status_id_str)) == $status->quoted_status_id_str
+ ) {
+ $replacementList[$url->indices[0]] = [
+ 'replace' => '',
+ 'length' => $url->indices[1] - $url->indices[0],
+ ];
+ continue;
+ }
- if ($type == '') {
- $type = $oembed_data->type;
- }
+ $expanded_url = $url->expanded_url;
- if ($oembed_data->type == 'video') {
- $type = $oembed_data->type;
- $footerurl = $expanded_url;
- $footerlink = '[url=' . $expanded_url . ']' . $url->display_url . '[/url]';
+ $final_url = Network::finalUrl($url->expanded_url);
- $body = str_replace($url->url, $footerlink, $body);
- } elseif (($oembed_data->type == 'photo') && isset($oembed_data->url)) {
- $body = str_replace($url->url, '[url=' . $expanded_url . '][img]' . $oembed_data->url . '[/img][/url]', $body);
- } elseif ($oembed_data->type != 'link') {
- $body = str_replace($url->url, '[url=' . $expanded_url . ']' . $url->display_url . '[/url]', $body);
- } else {
- $img_str = Network::fetchUrl($final_url, true, 4);
+ $oembed_data = OEmbed::fetchURL($final_url);
- $tempfile = tempnam(get_temppath(), 'cache');
- file_put_contents($tempfile, $img_str);
+ if (empty($oembed_data) || empty($oembed_data->type)) {
+ continue;
+ }
- // See http://php.net/manual/en/function.exif-imagetype.php#79283
- if (filesize($tempfile) > 11) {
- $mime = image_type_to_mime_type(exif_imagetype($tempfile));
- } else {
- $mime = false;
- }
+ // Quickfix: Workaround for URL with '[' and ']' in it
+ if (strpos($expanded_url, '[') || strpos($expanded_url, ']')) {
+ $expanded_url = $url->url;
+ }
- unlink($tempfile);
+ if ($oembed_data->type == 'video') {
+ $attachmentUrl = $expanded_url;
+ $replace = '';
+ } elseif (($oembed_data->type == 'photo') && isset($oembed_data->url)) {
+ $replace = '[url=' . $expanded_url . '][img]' . $oembed_data->url . '[/img][/url]';
+ } elseif ($oembed_data->type != 'link') {
+ $replace = '[url=' . $expanded_url . ']' . $url->display_url . '[/url]';
+ } else {
+ $img_str = Network::fetchUrl($final_url, true, 4);
- if (substr($mime, 0, 6) == 'image/') {
- $type = 'photo';
- $body = str_replace($url->url, '[img]' . $final_url . '[/img]', $body);
- } else {
- $type = $oembed_data->type;
- $footerurl = $expanded_url;
- $footerlink = '[url=' . $expanded_url . ']' . $url->display_url . '[/url]';
+ $tempfile = tempnam(get_temppath(), 'cache');
+ file_put_contents($tempfile, $img_str);
- $body = str_replace($url->url, $footerlink, $body);
- }
+ // See http://php.net/manual/en/function.exif-imagetype.php#79283
+ if (filesize($tempfile) > 11) {
+ $mime = image_type_to_mime_type(exif_imagetype($tempfile));
+ } else {
+ $mime = false;
}
- }
- }
-
- // Footer will be taken care of with a share block in the case of a quote
- if (empty($item->quoted_status)) {
- if ($footerurl != '') {
- $footer = add_page_info($footerurl, false, $picture);
- }
- if (($footerlink != '') && (trim($footer) != '')) {
- $removedlink = trim(str_replace($footerlink, '', $body));
+ unlink($tempfile);
- if (($removedlink == '') || strstr($body, $removedlink)) {
- $body = $removedlink;
+ if (substr($mime, 0, 6) == 'image/') {
+ $replace = '[img]' . $final_url . '[/img]';
+ } else {
+ $attachmentUrl = $expanded_url;
+ $replace = '';
}
-
- $body .= $footer;
}
- if ($footer == '' && $picture != '') {
- $body .= "\n\n[img]" . $picture . "[/img]\n";
- } elseif ($footer == '' && $picture == '') {
- $body = add_page_info_to_body($body);
- }
+ $replacementList[$url->indices[0]] = [
+ 'replace' => $replace,
+ 'length' => $url->indices[1] - $url->indices[0],
+ ];
}
}
- // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
- $tags = BBCode::getTags($body);
+ krsort($replacementList);
- if (count($tags)) {
- foreach ($tags as $tag) {
- if (strstr(trim($tag), ' ')) {
- continue;
- }
-
- if (strpos($tag, '#') === 0) {
- if (strpos($tag, '[url=')) {
- continue;
- }
-
- // don't link tags that are already embedded in links
- if (preg_match('/\[(.*?)' . preg_quote($tag, '/') . '(.*?)\]/', $body)) {
- continue;
- }
- if (preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag, '/') . '(.*?)\)/', $body)) {
- continue;
- }
+ foreach ($replacementList as $startIndex => $parameters) {
+ $body = Strings::substringReplace($body, $parameters['replace'], $startIndex, $parameters['length']);
+ }
- $basetag = str_replace('_', ' ', substr($tag, 1));
- $url = '#[url=' . $a->getBaseURL() . '/search?tag=' . $basetag . ']' . $basetag . '[/url]';
- $body = str_replace($tag, $url, $body);
- $tags_arr['#' . $basetag] = $url;
- } elseif (strpos($tag, '@') === 0) {
- if (strpos($tag, '[url=')) {
- continue;
- }
+ // Footer will be taken care of with a share block in the case of a quote
+ if (empty($status->quoted_status)) {
+ $footer = '';
+ if ($attachmentUrl) {
+ $footer = add_page_info($attachmentUrl, false, $picture);
+ }
- $basetag = substr($tag, 1);
- $url = '@[url=https://twitter.com/' . rawurlencode($basetag) . ']' . $basetag . '[/url]';
- $body = str_replace($tag, $url, $body);
- $tags_arr['@' . $basetag] = $url;
- }
+ if (trim($footer)) {
+ $body .= $footer;
+ } elseif ($picture) {
+ $body .= "\n\n[img]" . $picture . "[/img]\n";
+ } else {
+ $body = add_page_info_to_body($body);
}
}
- $tags = implode($tags_arr, ',');
-
- return ['body' => $body, 'tags' => $tags, 'plain' => $plain];
+ return ['body' => $body, 'plain' => $plain, 'taglist' => $taglist];
}
/**
$media[$medium->url] .= "\n[img]" . $medium->media_url_https . '[/img]';
}
- $postarray['object-type'] = ACTIVITY_OBJ_IMAGE;
+ $postarray['object-type'] = Activity\ObjectType::IMAGE;
break;
case 'video':
case 'animated_gif':
$media[$medium->url] .= "\n[img]" . $medium->media_url_https . '[/img]';
}
- $postarray['object-type'] = ACTIVITY_OBJ_VIDEO;
+ $postarray['object-type'] = Activity\ObjectType::VIDEO;
if (is_array($medium->video_info->variants)) {
$bitrate = 0;
// We take the video with the highest bitrate
return '';
}
-function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $only_existing_contact, $noquote)
+/**
+ * Undocumented function
+ *
+ * @param App $a
+ * @param integer $uid User ID
+ * @param object $post Incoming Twitter post
+ * @param array $self
+ * @param bool $create_user Should users be created?
+ * @param bool $only_existing_contact Only import existing contacts if set to "true"
+ * @param bool $noquote
+ * @param integer $uriid URI Id used to store tags. 0 = create a new one; -1 = don't store tags for this post.
+ * @return array item array
+ */
+function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $only_existing_contact, $noquote, int $uriid = 0)
{
$postarray = [];
$postarray['network'] = Protocol::TWITTER;
$postarray['protocol'] = Conversation::PARCEL_TWITTER;
$postarray['source'] = json_encode($post);
+ if (empty($uriid)) {
+ $uriid = $postarray['uri-id'] = ItemURI::insert(['uri' => $postarray['uri']]);
+ }
+
// Don't import our own comments
if (Item::exists(['extid' => $postarray['uri'], 'uid' => $uid])) {
Logger::log("Item with extid " . $postarray['uri'] . " found.", Logger::DEBUG);
$postarray['thr-parent'] = $parent_item['uri'];
$postarray['parent-uri'] = $parent_item['parent-uri'];
$postarray['parent'] = $parent_item['parent'];
- $postarray['object-type'] = ACTIVITY_OBJ_COMMENT;
+ $postarray['object-type'] = Activity\ObjectType::COMMENT;
} else {
$postarray['thr-parent'] = $postarray['uri'];
$postarray['parent-uri'] = $postarray['uri'];
- $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
+ $postarray['object-type'] = Activity\ObjectType::NOTE;
}
// Is it me?
- $own_id = PConfig::get($uid, 'twitter', 'own_id');
+ $own_id = DI::pConfig()->get($uid, 'twitter', 'own_id');
if ($post->user->id_str == $own_id) {
$r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
$create_user = false;
} else {
$postarray['parent-uri'] = $postarray['uri'];
- $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
+ $postarray['object-type'] = Activity\ObjectType::NOTE;
}
if ($contactid == 0) {
$postarray['contact-id'] = $contactid;
- $postarray['verb'] = ACTIVITY_POST;
+ $postarray['verb'] = Activity::POST;
$postarray['author-name'] = $postarray['owner-name'];
$postarray['author-link'] = $postarray['owner-link'];
$postarray['author-avatar'] = $postarray['owner-avatar'];
// When the post contains links then use the correct object type
if (count($post->entities->urls) > 0) {
- $postarray['object-type'] = ACTIVITY_OBJ_BOOKMARK;
+ $postarray['object-type'] = Activity\ObjectType::BOOKMARK;
}
// Search for media links
$picture = twitter_media_entities($post, $postarray);
- $converted = twitter_expand_entities($a, $postarray['body'], $post, $picture);
- $postarray['body'] = $converted["body"];
- $postarray['tag'] = $converted["tags"];
+ $converted = twitter_expand_entities($postarray['body'], $post, $picture);
+ $postarray['body'] = $converted['body'];
$postarray['created'] = DateTimeFormat::utc($post->created_at);
$postarray['edited'] = DateTimeFormat::utc($post->created_at);
+ if ($uriid > 0) {
+ twitter_store_tags($uriid, $converted['taglist']);
+ }
+
$statustext = $converted["plain"];
if (!empty($post->place->name)) {
Item::insert($retweet);
// CHange the other post into a reshare activity
- $postarray['verb'] = ACTIVITY2_ANNOUNCE;
+ $postarray['verb'] = Activity::ANNOUNCE;
$postarray['gravity'] = GRAVITY_ACTIVITY;
- $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
+ $postarray['object-type'] = Activity\ObjectType::NOTE;
$postarray['thr-parent'] = $retweet['uri'];
$postarray['parent-uri'] = $retweet['uri'];
}
}
- if (!empty($post->quoted_status) && !$noquote) {
- $quoted = twitter_createpost($a, $uid, $post->quoted_status, $self, false, false, true);
-
- if (empty($quoted['body'])) {
- return [];
+ if (!empty($post->quoted_status)) {
+ if ($noquote) {
+ // To avoid recursive share blocks we just provide the link to avoid removing quote context.
+ $postarray['body'] .= "\n\nhttps://twitter.com/" . $post->quoted_status->user->screen_name . "/status/" . $post->quoted_status->id_str;
+ } else {
+ $quoted = twitter_createpost($a, $uid, $post->quoted_status, $self, false, false, true, $uriid);
+ if (!empty($quoted['body'])) {
+ $postarray['body'] .= "\n" . BBCode::getShareOpeningTag(
+ $quoted['author-name'],
+ $quoted['author-link'],
+ $quoted['author-avatar'],
+ $quoted['plink'],
+ $quoted['created']
+ );
+
+ $postarray['body'] .= $quoted['body'] . '[/share]';
+ } else {
+ // Quoted post author is blocked/ignored, so we just provide the link to avoid removing quote context.
+ $postarray['body'] .= "\n\nhttps://twitter.com/" . $post->quoted_status->user->screen_name . "/status/" . $post->quoted_status->id_str;
+ }
}
-
- $postarray['body'] .= "\n" . share_header(
- $quoted['author-name'],
- $quoted['author-link'],
- $quoted['author-avatar'],
- "",
- $quoted['created'],
- $quoted['plink']
- );
-
- $postarray['body'] .= $quoted['body'] . '[/share]';
}
return $postarray;
}
+/**
+ * Store tags and mentions
+ *
+ * @param integer $uriid
+ * @param array $taglist
+ */
+function twitter_store_tags(int $uriid, array $taglist)
+{
+ foreach ($taglist as $tag) {
+ Tag::storeByHash($uriid, $tag[0], $tag[1], $tag[2]);
+ }
+}
+
function twitter_fetchparentposts(App $a, $uid, $post, TwitterOAuth $connection, array $self)
{
Logger::log("twitter_fetchparentposts: Fetching for user " . $uid . " and post " . $post->id_str, Logger::DEBUG);
if (!empty($posts)) {
foreach ($posts as $post) {
- $postarray = twitter_createpost($a, $uid, $post, $self, false, false, false);
+ $postarray = twitter_createpost($a, $uid, $post, $self, false, !DI::pConfig()->get($uid, 'twitter', 'create_user'), false);
if (empty($postarray['body'])) {
continue;
function twitter_fetchhometimeline(App $a, $uid)
{
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
- $otoken = PConfig::get($uid, 'twitter', 'oauthtoken');
- $osecret = PConfig::get($uid, 'twitter', 'oauthsecret');
- $create_user = PConfig::get($uid, 'twitter', 'create_user');
- $mirror_posts = PConfig::get($uid, 'twitter', 'mirror_posts');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
+ $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
+ $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
+ $create_user = DI::pConfig()->get($uid, 'twitter', 'create_user');
+ $mirror_posts = DI::pConfig()->get($uid, 'twitter', 'mirror_posts');
Logger::log("Fetching timeline for user " . $uid, Logger::DEBUG);
- $application_name = Config::get('twitter', 'application_name');
+ $application_name = DI::config()->get('twitter', 'application_name');
if ($application_name == "") {
- $application_name = $a->getHostName();
+ $application_name = DI::baseUrl()->getHostname();
}
$connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
$parameters = ["exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true, "tweet_mode" => "extended", "include_ext_alt_text" => true];
//$parameters["count"] = 200;
// Fetching timeline
- $lastid = PConfig::get($uid, 'twitter', 'lasthometimelineid');
+ $lastid = DI::pConfig()->get($uid, 'twitter', 'lasthometimelineid');
$first_time = ($lastid == "");
foreach ($posts as $post) {
if ($post->id_str > $lastid) {
$lastid = $post->id_str;
- PConfig::set($uid, 'twitter', 'lasthometimelineid', $lastid);
+ DI::pConfig()->set($uid, 'twitter', 'lasthometimelineid', $lastid);
}
if ($first_time) {
}
}
- $item = Item::insert($postarray, false, $notify);
+ $item = Item::insert($postarray, $notify);
$postarray["id"] = $item;
Logger::log('User ' . $uid . ' posted home timeline item ' . $item);
}
}
- PConfig::set($uid, 'twitter', 'lasthometimelineid', $lastid);
+ DI::pConfig()->set($uid, 'twitter', 'lasthometimelineid', $lastid);
Logger::log('Last timeline ID for user ' . $uid . ' is now ' . $lastid, Logger::DEBUG);
// Fetching mentions
- $lastid = PConfig::get($uid, 'twitter', 'lastmentionid');
+ $lastid = DI::pConfig()->get($uid, 'twitter', 'lastmentionid');
$first_time = ($lastid == "");
twitter_fetchparentposts($a, $uid, $post, $connection, $self);
}
- $postarray = twitter_createpost($a, $uid, $post, $self, false, false, false);
+ $postarray = twitter_createpost($a, $uid, $post, $self, false, !$create_user, false);
if (empty($postarray['body'])) {
continue;
}
}
- PConfig::set($uid, 'twitter', 'lastmentionid', $lastid);
+ DI::pConfig()->set($uid, 'twitter', 'lastmentionid', $lastid);
Logger::log('Last mentions ID for user ' . $uid . ' is now ' . $lastid, Logger::DEBUG);
}
function twitter_fetch_own_contact(App $a, $uid)
{
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
- $otoken = PConfig::get($uid, 'twitter', 'oauthtoken');
- $osecret = PConfig::get($uid, 'twitter', 'oauthsecret');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
+ $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
+ $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
- $own_id = PConfig::get($uid, 'twitter', 'own_id');
+ $own_id = DI::pConfig()->get($uid, 'twitter', 'own_id');
$contact_id = 0;
// Fetching user data
// get() may throw TwitterOAuthException, but we will catch it later
$user = $connection->get('account/verify_credentials');
- if (empty($user) || empty($user->id_str)) {
+ if (empty($user->id_str)) {
return false;
}
- PConfig::set($uid, 'twitter', 'own_id', $user->id_str);
+ DI::pConfig()->set($uid, 'twitter', 'own_id', $user->id_str);
$contact_id = twitter_fetch_contact($uid, $user, true);
} else {
if (DBA::isResult($r)) {
$contact_id = $r[0]["id"];
} else {
- PConfig::delete($uid, 'twitter', 'own_id');
+ DI::pConfig()->delete($uid, 'twitter', 'own_id');
}
}
Logger::log('twitter_is_retweet: Retweeting id ' . $id . ' for user ' . $uid, Logger::DEBUG);
- $ckey = Config::get('twitter', 'consumerkey');
- $csecret = Config::get('twitter', 'consumersecret');
- $otoken = PConfig::get($uid, 'twitter', 'oauthtoken');
- $osecret = PConfig::get($uid, 'twitter', 'oauthsecret');
+ $ckey = DI::config()->get('twitter', 'consumerkey');
+ $csecret = DI::config()->get('twitter', 'consumersecret');
+ $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
+ $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
$connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
$result = $connection->post('statuses/retweet/' . $id);
function twitter_convert_share(array $attributes, array $author_contact, $content, $is_quote_share)
{
- if ($author_contact['network'] == Protocol::TWITTER) {
+ if (empty($author_contact)) {
+ return $content . "\n\n" . $attributes['link'];
+ }
+
+ if (!empty($author_contact['network']) && ($author_contact['network'] == Protocol::TWITTER)) {
$mention = '@' . $author_contact['nick'];
} else {
$mention = $author_contact['addr'];