use Friendica\Model\ItemContent;
use Friendica\Model\ItemURI;
use Friendica\Model\Tag;
-use Friendica\Model\Term;
use Friendica\Model\User;
use Friendica\Protocol\Activity;
use Friendica\Util\ConfigFileLoader;
Hook::register('expire' , __FILE__, 'twitter_expire');
Hook::register('prepare_body' , __FILE__, 'twitter_prepare_body');
Hook::register('check_item_notification', __FILE__, 'twitter_check_item_notification');
+ 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)
{
$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;
}
}
$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 = DI::config()->get('twitter', 'consumerkey');
$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)
$b['body'] = twitter_update_mentions($b['body']);
- $msgarr = ItemContent::getPlaintextPost($b, $max_char, true, 8);
+ $msgarr = ItemContent::getPlaintextPost($b, $max_char, true, BBCode::TWITTER);
Logger::info('Got plaintext', ['id' => $b['id'], 'message' => $msgarr]);
$msg = $msgarr["text"];
}
}
- $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")) {
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]';
try {
$status = $connection->get('friendships/show', $parameters);
- } catch (TwitterOAuthException $e) {
- Logger::info('Error fetching friendship status', ['user' => $uid, 'target' => $target, 'message' => $e->getMessage()]);
- return $relation;
- }
+ 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;
+ $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;
+ }
- 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()]);
}
- Logger::info('Fetched friendship relation', ['user' => $uid, 'target' => $target, 'relation' => $relation]);
-
return $relation;
}
-function twitter_fetch_contact($uid, $data, $create_user)
+/**
+ * @param $data
+ * @return array
+ */
+function twitter_user_to_contact($data)
{
if (empty($data->id_str)) {
- return -1;
+ return [];
}
- $avatar = twitter_fix_avatar($data->profile_image_url_https);
- $url = "https://twitter.com/" . $data->screen_name;
- $addr = $data->screen_name . "@twitter.com";
+ $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;
+}
- $fields = ['url' => $url, 'network' => Protocol::TWITTER,
- 'alias' => 'twitter::' . $data->id_str,
- 'name' => $data->name, 'nick' => $data->screen_name, 'addr' => $addr,
- 'location' => $data->location, 'about' => $data->description];
+function twitter_fetch_contact($uid, $data, $create_user)
+{
+ $fields = twitter_user_to_contact($data);
+
+ if (empty($fields)) {
+ return -1;
+ }
+
+ // photo comes from twitter_user_to_contact but shouldn't be saved directly in the contact row
+ $avatar = $fields['photo'];
+ unset($fields['photo']);
// 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($url, 0, true, $fields);
+ $cid = Contact::getIdForURL($fields['url'], 0, true, $fields);
}
if (!empty($cid)) {
// create contact record
$fields['uid'] = $uid;
$fields['created'] = DateTimeFormat::utcNow();
- $fields['nurl'] = Strings::normaliseLink($url);
+ $fields['nurl'] = Strings::normaliseLink($fields['url']);
$fields['poll'] = 'twitter::' . $data->id_str;
$fields['rel'] = $relation;
$fields['priority'] = 1;
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 = 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');
-
- $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;
}
/**
{
$plain = $body;
- $tags = [];
$taglist = [];
$replacementList = [];
foreach ($status->entities->hashtags AS $hashtag) {
$replace = '#[url=' . DI::baseUrl()->get() . '/search?tag=' . $hashtag->text . ']' . $hashtag->text . '[/url]';
- $tags['#' . $hashtag->text] = $replace;
$taglist['#' . $hashtag->text] = ['#', $hashtag->text, ''];
$replacementList[$hashtag->indices[0]] = [
foreach ($status->entities->user_mentions AS $mention) {
$replace = '@[url=https://twitter.com/' . rawurlencode($mention->screen_name) . ']' . $mention->screen_name . '[/url]';
- $tags['@' . $mention->screen_name] = $replace;
$taglist['@' . $mention->screen_name] = ['@', $mention->screen_name, 'https://twitter.com/' . rawurlencode($mention->screen_name)];
$replacementList[$mention->indices[0]] = [
}
}
- return ['body' => $body, 'tags' => $tags, 'plain' => $plain, 'taglist' => $taglist];
+ return ['body' => $body, 'plain' => $plain, 'taglist' => $taglist];
}
/**
return '';
}
+/**
+ * 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 = [];
$converted = twitter_expand_entities($postarray['body'], $post, $picture);
$postarray['body'] = $converted['body'];
- $postarray['tag'] = implode(',', $converted['tags']);
$postarray['created'] = DateTimeFormat::utc($post->created_at);
$postarray['edited'] = DateTimeFormat::utc($post->created_at);
}
}
- if (!empty($post->quoted_status) && !$noquote) {
- $quoted = twitter_createpost($a, $uid, $post->quoted_status, $self, false, false, true, $uriid);
-
- if (!empty($quoted['body'])) {
- $postarray['body'] .= "\n" . share_header(
- $quoted['author-name'],
- $quoted['author-link'],
- $quoted['author-avatar'],
- "",
- $quoted['created'],
- $quoted['plink']
- );
-
- $postarray['body'] .= $quoted['body'] . '[/share]';
- } else {
- // Quoted post author is blocked/ignored, so we just provide the link to avoid removing quote context.
+ 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;
+ }
}
}
}
}
- $item = Item::insert($postarray, false, $notify);
+ $item = Item::insert($postarray, $notify);
$postarray["id"] = $item;
Logger::log('User ' . $uid . ' posted home timeline item ' . $item);
// 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;
}