* Author: Tobias Diekershoff <https://f.diekershoff.de/profile/tobias>
* Author: Michael Vogel <https://pirati.ca/profile/heluecht>
* Maintainer: Hypolite Petovan <https://friendica.mrpetovan.com/profile/hypolite>
+ * Status: unsupported
*
* Copyright (c) 2011-2013 Tobias Diekershoff, Michael Vogel, Hypolite Petovan
* All rights reserved.
* we do not need "Twitter as login". When you've registered the app you get the
* OAuth Consumer key and secret pair for your application/site.
*
- * Add this key pair to your global config/addon.config.php or use the admin panel.
+ * Add this key pair to your config/twitter.config.php file or use the admin panel.
*
- * 'twitter' => [
- * 'consumerkey' => '',
- * 'consumersecret' => '',
- * ],
+ * return [
+ * 'twitter' => [
+ * 'consumerkey' => '',
+ * 'consumersecret' => '',
+ * ],
+ * ];
*
* To activate the addon itself add it to the system.addon
* setting. After this, your user can configure their Twitter account settings
use Friendica\Model\Tag;
use Friendica\Model\User;
use Friendica\Protocol\Activity;
-use Friendica\Core\Config\Util\ConfigFileLoader;
+use Friendica\Core\Config\Util\ConfigFileManager;
use Friendica\Core\System;
+use Friendica\Model\Photo;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Images;
use Friendica\Util\Strings;
// Hook functions
-function twitter_load_config(App $a, ConfigFileLoader $loader)
+function twitter_load_config(ConfigFileManager $loader)
{
- $a->getConfigCache()->load($loader->loadAddonConfig('twitter'));
+ DI::app()->getConfigCache()->load($loader->loadAddonConfig('twitter'), \Friendica\Core\Config\ValueObject\Cache::SOURCE_STATIC);
}
-function twitter_check_item_notification(App $a, array &$notification_data)
+function twitter_check_item_notification(array &$notification_data)
{
$own_id = DI::pConfig()->get($notification_data['uid'], 'twitter', 'own_id');
}
}
-function twitter_support_follow(App $a, array &$data)
+function twitter_support_follow(array &$data)
{
if ($data['protocol'] == Protocol::TWITTER) {
$data['result'] = true;
}
}
-function twitter_follow(App $a, array &$contact)
+function twitter_follow(array &$contact)
{
Logger::info('Check if contact is twitter contact', ['url' => $contact['url']]);
$nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $contact['url']);
$nickname = str_replace('@twitter.com', '', $nickname);
- $uid = $a->getLoggedInUserId();
+ $uid = DI::userSession()->getLocalUserId();
if (!twitter_api_contact('friendships/create', ['network' => Protocol::TWITTER, 'nick' => $nickname], $uid)) {
$contact = null;
}
}
-function twitter_unfollow(App $a, array &$hook_data)
+function twitter_unfollow(array &$hook_data)
{
$hook_data['result'] = twitter_api_contact('friendships/destroy', $hook_data['contact'], $hook_data['uid']);
}
-function twitter_block(App $a, array &$hook_data)
+function twitter_block(array &$hook_data)
{
$hook_data['result'] = twitter_api_contact('blocks/create', $hook_data['contact'], $hook_data['uid']);
}
}
-function twitter_unblock(App $a, array &$hook_data)
+function twitter_unblock(array &$hook_data)
{
$hook_data['result'] = twitter_api_contact('blocks/destroy', $hook_data['contact'], $hook_data['uid']);
}
return (bool)twitter_api_call($uid, $apiPath, ['screen_name' => $contact['nick']]);
}
-function twitter_jot_nets(App $a, array &$jotnets_fields)
+function twitter_jot_nets(array &$jotnets_fields)
{
if (!DI::userSession()->getLocalUserId()) {
return;
}
-function twitter_settings_post(App $a)
+function twitter_settings_post()
{
if (!DI::userSession()->getLocalUserId()) {
return;
}
}
-function twitter_settings(App $a, array &$data)
+function twitter_settings(array &$data)
{
if (!DI::userSession()->getLocalUserId()) {
return;
];
}
-function twitter_hook_fork(App $a, array &$b)
+function twitter_hook_fork(array &$b)
{
DI::logger()->debug('twitter_hook_fork', $b);
return;
}
- if (substr($post['app'], 0, 7) == 'Twitter') {
+ if (substr($post['app'] ?? '', 0, 7) == 'Twitter') {
DI::logger()->info('No Twitter app');
$b['execute'] = false;
return;
}
} else {
// Comments are never exported when we don't import the twitter timeline
- if (!strstr($post['postopts'], 'twitter') || ($post['parent'] != $post['id']) || $post['private']) {
+ if (!strstr($post['postopts'] ?? '', 'twitter') || ($post['parent'] != $post['id']) || $post['private']) {
DI::logger()->info('Comments are never exported when we don\'t import the twitter timeline');
$b['execute'] = false;
return;
}
}
-function twitter_post_local(App $a, array &$b)
+function twitter_post_local(array &$b)
{
if ($b['edit']) {
return;
$b['postopts'] .= 'twitter';
}
-function twitter_probe_detect(App $a, array &$hookData)
+function twitter_probe_detect(array &$hookData)
{
// Don't overwrite an existing result
if (isset($hookData['result'])) {
$user = twitter_fetchuser($nick);
if ($user) {
- $hookData['result'] = twitter_user_to_contact($user);
+ $hookData['result'] = twitter_user_to_contact($user) ?: null;
+ }
+
+ // Authoritative probe should set the result even if the probe was unsuccessful
+ if ($hookData['network'] == Protocol::TWITTER && empty($hookData['result'])) {
+ $hookData['result'] = [];
}
}
-function twitter_item_by_link(App $a, array &$hookData)
+function twitter_item_by_link(array &$hookData)
{
// Don't overwrite an existing result
if (isset($hookData['item_id'])) {
return;
}
- $item = twitter_createpost($a, $hookData['uid'], $status, [], true, false, false);
+ $item = twitter_createpost($hookData['uid'], $status, [], true, false, false);
if (!empty($item)) {
$hookData['item_id'] = Item::insert($item);
}
return (int)$id;
}
-function twitter_post_hook(App $a, array &$b)
+function twitter_post_hook(array &$b)
{
DI::logger()->debug('Invoke post hook', $b);
Logger::info('We have customer key and oauth stuff, going to send.');
// If it's a repeated message from twitter then do a native retweet and exit
- if (twitter_is_retweet($a, $b['uid'], $b['body'])) {
+ if (twitter_is_retweet($b['uid'], $b['body'])) {
return;
}
// and now tweet it :-)
$post = [];
- if (!empty($msgarr['images'])) {
- Logger::info('Got images', ['id' => $b['id'], 'images' => $msgarr['images']]);
+ if (!empty($msgarr['images']) || !empty($msgarr['remote_images'])) {
+ Logger::info('Got images', ['id' => $b['id'], 'images' => $msgarr['images'] ?? [], 'remote_images' => $msgarr['remote_images'] ?? []]);
try {
$media_ids = [];
- foreach ($msgarr['images'] as $image) {
+ foreach ($msgarr['images'] ?? [] as $image) {
if (count($media_ids) == 4) {
continue;
}
+ try {
+ $media_ids[] = twitter_upload_image($connection, $cb, $image, $b);
+ } catch (\Throwable $th) {
+ Logger::warning('Error while uploading image', ['code' => $th->getCode(), 'message' => $th->getMessage()]);
+ }
+ }
- $img_str = DI::httpClient()->fetch($image['url']);
-
- $tempfile = tempnam(System::getTempPath(), '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);
-
- if (isset($media->media_id_string)) {
- $media_ids[] = $media->media_id_string;
-
- if (!empty($image['description'])) {
- $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', ['id' => $b['id'], 'data' => $data, 'return' => $ret]);
- }
- } else {
- Logger::error('Failed upload', ['id' => $b['id'], 'image' => $image['url'], 'return' => $media]);
- throw new Exception('Failed upload of ' . $image['url']);
+ foreach ($msgarr['remote_images'] ?? [] as $image) {
+ if (count($media_ids) == 4) {
+ continue;
+ }
+ try {
+ $media_ids[] = twitter_upload_image($connection, $cb, $image, $b);
+ } catch (\Throwable $th) {
+ Logger::warning('Error while uploading image', ['code' => $th->getCode(), 'message' => $th->getMessage()]);
}
}
$post['media_ids'] = implode(',', $media_ids);
Logger::info('twitter_post send', ['id' => $b['id'], 'result' => $result]);
if (!empty($result->source)) {
- DI::config()->set('twitter', 'application_name', strip_tags($result->source));
+ DI::keyValue()->set('twitter_application_name', strip_tags($result->source));
}
if (!empty($result->errors)) {
}
if (!empty($application_name)) {
- DI::config()->set('twitter', 'application_name', strip_tags($result->source));
+ DI::keyValue()->set('twitter_application_name', strip_tags($application_name));
}
}
}
}
+function twitter_upload_image($connection, $cb, array $image, array $item)
+{
+ if (!empty($image['id'])) {
+ $photo = Photo::selectFirst([], ['id' => $image['id']]);
+ } else {
+ $photo = Photo::createPhotoForExternalResource($image['url']);
+ }
+
+ $tempfile = tempnam(System::getTempPath(), 'cache');
+ file_put_contents($tempfile, Photo::getImageForPhoto($photo));
+
+ Logger::info('Uploading', ['id' => $item['id'], 'image' => $image]);
+ $media = $connection->upload('media/upload', ['media' => $tempfile]);
+
+ unlink($tempfile);
+
+ if (isset($media->media_id_string)) {
+ $media_id = $media->media_id_string;
+
+ if (!empty($image['description'])) {
+ $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', ['id' => $item['id'], 'data' => $data, 'return' => $ret]);
+ }
+ } else {
+ Logger::error('Failed upload', ['id' => $item['id'], 'image' => $image['url'], 'return' => $media]);
+ throw new Exception('Failed upload of ' . $image['url']);
+ }
+
+ return $media_id;
+}
+
function twitter_delete_item(array $item)
{
if (!$item['deleted']) {
}
}
-function twitter_addon_admin_post(App $a)
+function twitter_addon_admin_post()
{
DI::config()->set('twitter', 'consumerkey', trim($_POST['consumerkey'] ?? ''));
DI::config()->set('twitter', 'consumersecret', trim($_POST['consumersecret'] ?? ''));
}
-function twitter_addon_admin(App $a, string &$o)
+function twitter_addon_admin(string &$o)
{
$t = Renderer::getMarkupTemplate('admin.tpl', 'addon/twitter/');
]);
}
-function twitter_cron(App $a)
+function twitter_cron()
{
- $last = DI::config()->get('twitter', 'last_poll');
+ $last = DI::keyValue()->get('twitter_last_poll');
$poll_interval = intval(DI::config()->get('twitter', 'poll_interval'));
if (!$poll_interval) {
$next_contact_check = 0;
if($next_contact_check <= time()) {
- pumpio_getallusers($a, $rr["uid"]);
+ pumpio_getallusers($rr["uid"]);
DI::pConfig()->set($rr['uid'],'pumpio','contact_check',time());
}
*/
Logger::notice('twitter: cron_end');
- DI::config()->set('twitter', 'last_poll', time());
+ DI::keyValue()->set('twitter_last_poll', time());
}
-function twitter_expire(App $a)
+function twitter_expire()
{
$days = DI::config()->get('twitter', 'expire');
Logger::notice('End expiry');
}
-function twitter_prepare_body(App $a, array &$b)
+function twitter_prepare_body(array &$b)
{
if ($b['item']['network'] != Protocol::TWITTER) {
return;
if ($b['preview']) {
$max_char = 280;
$item = $b['item'];
- $item['plink'] = DI::baseUrl()->get() . '/display/' . $item['guid'];
+ $item['plink'] = DI::baseUrl() . '/display/' . $item['guid'];
$condition = ['uri' => $item['thr-parent'], 'uid' => DI::userSession()->getLocalUserId()];
$orig_post = Post::selectFirst(['author-link'], $condition);
/**
* Parse Twitter status URLs since Twitter removed OEmbed
*
- * @param App $a
* @param array $b Expected format:
* [
* 'url' => [URL to parse],
* ]
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
-function twitter_parse_link(App $a, array &$b)
+function twitter_parse_link(array &$b)
{
// Only handle Twitter status URLs
if (!preg_match('#^https?://(?:mobile\.|www\.)?twitter.com/[^/]+/status/(\d+).*#', $b['url'], $matches)) {
return;
}
- $item = twitter_createpost($a, 0, $status, [], true, false, true);
+ $item = twitter_createpost(0, $status, [], true, false, true);
if (empty($item)) {
return;
}
/**
* @brief Build the item array for the mirrored post
*
- * @param App $a Application class
* @param integer $uid User id
* @param object $post Twitter object with the post
*
* @return array item data to be posted
*/
-function twitter_do_mirrorpost(App $a, int $uid, $post)
+function twitter_do_mirrorpost(int $uid, $post)
{
$datarray['uid'] = $uid;
$datarray['extid'] = 'twitter::' . $post->id;
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, -1);
+ $item = twitter_createpost($uid, $post->retweeted_status, ['id' => 0], false, false, true, -1);
if (empty($item)) {
return [];
$datarray['body'] .= $item['body'] . '[/share]';
} else {
- $item = twitter_createpost($a, $uid, $post, ['id' => 0], false, false, false, -1);
+ $item = twitter_createpost($uid, $post, ['id' => 0], false, false, false, -1);
if (empty($item)) {
return [];
/**
* Fetches the Twitter user's own posts
*
- * @param App $a
* @param int $uid
* @return void
* @throws Exception
*/
-function twitter_fetchtimeline(App $a, int $uid): void
+function twitter_fetchtimeline(int $uid): void
{
$ckey = DI::config()->get('twitter', 'consumerkey');
$csecret = DI::config()->get('twitter', 'consumersecret');
$osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
$lastid = DI::pConfig()->get($uid, 'twitter', 'lastid');
- $application_name = DI::config()->get('twitter', 'application_name');
+ $application_name = DI::keyValue()->get('twitter_application_name') ?? '';
if ($application_name == '') {
- $application_name = DI::baseUrl()->getHostname();
+ $application_name = DI::baseUrl()->getHost();
}
$connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
// Ensure to have the own contact
try {
- twitter_fetch_own_contact($a, $uid);
+ twitter_fetch_own_contact($uid);
} catch (TwitterOAuthException $e) {
Logger::notice('Error fetching own contact', ['uid' => $uid, 'message' => $e->getMessage()]);
return;
}
Logger::info('Preparing mirror post', ['twitter-id' => $post->id_str, 'uid' => $uid]);
- $mirrorpost = twitter_do_mirrorpost($a, $uid, $post);
+ $mirrorpost = twitter_do_mirrorpost($uid, $post);
if (empty($mirrorpost['body'])) {
Logger::notice('Body is empty', ['post' => $post, 'mirrorpost' => $mirrorpost]);
// We only probe on Mastodon style URL to reduce the number of unsuccessful probes
twitter_add_contact($url->expanded_url, strpos($url->expanded_url, '@'), $uid);
}
- }
+ }
}
/**
$replacementList = [];
foreach ($status->entities->hashtags AS $hashtag) {
- $replace = '#[url=' . DI::baseUrl()->get() . '/search?tag=' . $hashtag->text . ']' . $hashtag->text . '[/url]';
+ $replace = '#[url=' . DI::baseUrl() . '/search?tag=' . $hashtag->text . ']' . $hashtag->text . '[/url]';
$taglist['#' . $hashtag->text] = ['#', $hashtag->text, ''];
$replacementList[$hashtag->indices[0]] = [
/**
* Undocumented function
*
- * @param App $a
* @param integer $uid User ID
* @param object $post Incoming Twitter post
* @param array $self
* @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, int $uid, $post, array $self, $create_user, bool $only_existing_contact, bool $noquote, int $uriId = 0): array
+function twitter_createpost(int $uid, $post, array $self, $create_user, bool $only_existing_contact, bool $noquote, int $uriId = 0): array
{
$postarray = [];
$postarray['network'] = Protocol::TWITTER;
$postarray['coord'] = $post->coordinates->coordinates[1] . ' ' . $post->coordinates->coordinates[0];
}
if (!empty($post->retweeted_status)) {
- $retweet = twitter_createpost($a, $uid, $post->retweeted_status, $self, false, false, $noquote);
+ $retweet = twitter_createpost($uid, $post->retweeted_status, $self, false, false, $noquote);
if (empty($retweet)) {
return [];
// 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, 0, $post->quoted_status, $self, false, false, true);
+ $quoted = twitter_createpost(0, $post->quoted_status, $self, false, false, true);
if (!empty($quoted)) {
Item::insert($quoted);
$post = Post::selectFirst(['guid', 'uri-id'], ['uri' => $quoted['uri'], 'uid' => 0]);
}
}
-function twitter_fetchparentposts(App $a, int $uid, $post, TwitterOAuth $connection, array $self)
+function twitter_fetchparentposts(int $uid, $post, TwitterOAuth $connection, array $self)
{
Logger::info('Fetching parent posts', ['user' => $uid, 'post' => $post->id_str]);
if (!empty($posts)) {
foreach ($posts as $post) {
- $postarray = twitter_createpost($a, $uid, $post, $self, false, !DI::pConfig()->get($uid, 'twitter', 'create_user'), false);
+ $postarray = twitter_createpost($uid, $post, $self, false, !DI::pConfig()->get($uid, 'twitter', 'create_user'), false);
if (empty($postarray)) {
continue;
/**
* Fetches the posts received by the Twitter user
*
- * @param App $a
* @param int $uid
* @return void
* @throws Exception
*/
-function twitter_fetchhometimeline(App $a, int $uid): void
+function twitter_fetchhometimeline(int $uid): void
{
$ckey = DI::config()->get('twitter', 'consumerkey');
$csecret = DI::config()->get('twitter', 'consumersecret');
Logger::info('Fetching timeline', ['uid' => $uid]);
- $application_name = DI::config()->get('twitter', 'application_name');
+ $application_name = DI::keyValue()->get('twitter_application_name') ?? '';
if ($application_name == '') {
- $application_name = DI::baseUrl()->getHostname();
+ $application_name = DI::baseUrl()->getHost();
}
$connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
try {
- $own_contact = twitter_fetch_own_contact($a, $uid);
+ $own_contact = twitter_fetch_own_contact($uid);
} catch (TwitterOAuthException $e) {
Logger::notice('Error fetching own contact', ['uid' => $uid, 'message' => $e->getMessage()]);
return;
}
if ($post->in_reply_to_status_id_str != '') {
- twitter_fetchparentposts($a, $uid, $post, $connection, $self);
+ twitter_fetchparentposts($uid, $post, $connection, $self);
}
Logger::info('Preparing post ' . $post->id_str . ' for user ' . $uid);
- $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true, false);
+ $postarray = twitter_createpost($uid, $post, $self, $create_user, true, false);
if (empty($postarray)) {
Logger::info('Empty post ' . $post->id_str . ' and user ' . $uid);
}
}
+ $postarray['wall'] = (bool)$notify;
+
$item = Item::insert($postarray, $notify);
$postarray['id'] = $item;
}
if ($post->in_reply_to_status_id_str != '') {
- twitter_fetchparentposts($a, $uid, $post, $connection, $self);
+ twitter_fetchparentposts($uid, $post, $connection, $self);
}
- $postarray = twitter_createpost($a, $uid, $post, $self, false, !$create_user, false);
+ $postarray = twitter_createpost($uid, $post, $self, false, !$create_user, false);
if (empty($postarray)) {
continue;
Logger::info('Last mentions ID for user ' . $uid . ' is now ' . $lastid);
}
-function twitter_fetch_own_contact(App $a, int $uid)
+function twitter_fetch_own_contact(int $uid)
{
$ckey = DI::config()->get('twitter', 'consumerkey');
$csecret = DI::config()->get('twitter', 'consumersecret');
return $contact_id;
}
-function twitter_is_retweet(App $a, int $uid, string $body): bool
+function twitter_is_retweet(int $uid, string $body): bool
{
$body = trim($body);