Merge pull request 'Tumblr: We can now follow, unfollow, add posts, ...' (#1374) from heluecht/friendica-addons:tumblr-complete into develop
Reviewed-on: https://git.friendi.ca/friendica/friendica-addons/pulls/1374remotes/1696060286440253424/tmp_refs/heads/develop
commit
dac297b942
|
@ -12,6 +12,7 @@ use Friendica\Content\Text\BBCode;
|
|||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Content\Text\NPF;
|
||||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Config\Util\ConfigFileManager;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
|
@ -41,6 +42,7 @@ define('TUMBLR_DEFAULT_POLL_INTERVAL', 10); // given in minutes
|
|||
|
||||
function tumblr_install()
|
||||
{
|
||||
Hook::register('load_config', __FILE__, 'tumblr_load_config');
|
||||
Hook::register('hook_fork', __FILE__, 'tumblr_hook_fork');
|
||||
Hook::register('post_local', __FILE__, 'tumblr_post_local');
|
||||
Hook::register('notifier_normal', __FILE__, 'tumblr_send');
|
||||
|
@ -48,6 +50,175 @@ function tumblr_install()
|
|||
Hook::register('connector_settings', __FILE__, 'tumblr_settings');
|
||||
Hook::register('connector_settings_post', __FILE__, 'tumblr_settings_post');
|
||||
Hook::register('cron', __FILE__, 'tumblr_cron');
|
||||
Hook::register('support_follow', __FILE__, 'tumblr_support_follow');
|
||||
Hook::register('follow', __FILE__, 'tumblr_follow');
|
||||
Hook::register('unfollow', __FILE__, 'tumblr_unfollow');
|
||||
Hook::register('block', __FILE__, 'tumblr_block');
|
||||
Hook::register('unblock', __FILE__, 'tumblr_unblock');
|
||||
Hook::register('check_item_notification', __FILE__, 'tumblr_check_item_notification');
|
||||
Hook::register('probe_detect', __FILE__, 'tumblr_probe_detect');
|
||||
Hook::register('item_by_link', __FILE__, 'tumblr_item_by_link');
|
||||
Logger::info('installed tumblr');
|
||||
}
|
||||
|
||||
function tumblr_load_config(ConfigFileManager $loader)
|
||||
{
|
||||
DI::app()->getConfigCache()->load($loader->loadAddonConfig('tumblr'), \Friendica\Core\Config\ValueObject\Cache::SOURCE_STATIC);
|
||||
}
|
||||
|
||||
function tumblr_check_item_notification(array &$notification_data)
|
||||
{
|
||||
if (!tumblr_enabled_for_user($notification_data['uid'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$page = tumblr_get_page($notification_data['uid']);
|
||||
if (empty($page)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$own_user = Contact::selectFirst(['url', 'alias'], ['uid' => $notification_data['uid'], 'poll' => 'tumblr::'.$page]);
|
||||
if ($own_user) {
|
||||
$notification_data['profiles'][] = $own_user['url'];
|
||||
$notification_data['profiles'][] = $own_user['alias'];
|
||||
}
|
||||
}
|
||||
|
||||
function tumblr_probe_detect(array &$hookData)
|
||||
{
|
||||
// Don't overwrite an existing result
|
||||
if (isset($hookData['result'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid a lookup for the wrong network
|
||||
if (!in_array($hookData['network'], ['', Protocol::TUMBLR])) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::debug('Search for tumblr blog', ['url' => $hookData['uri']]);
|
||||
|
||||
$hookData['result'] = tumblr_get_contact_by_url($hookData['uri']);
|
||||
}
|
||||
|
||||
function tumblr_item_by_link(array &$hookData)
|
||||
{
|
||||
// Don't overwrite an existing result
|
||||
if (isset($hookData['item_id'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tumblr_enabled_for_user($hookData['uid'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preg_match('#^https?://www\.tumblr.com/blog/view/(.+)/(\d+).*#', $hookData['uri'], $matches) && !preg_match('#^https?://www\.tumblr.com/(.+)/(\d+).*#', $hookData['uri'], $matches)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::debug('Found tumblr post', ['url' => $hookData['uri'], 'blog' => $matches[1], 'id' => $matches[2]]);
|
||||
|
||||
$parameters = ['id' => $matches[2], 'reblog_info' => false, 'notes_info' => false, 'npf' => false];
|
||||
$result = tumblr_get($hookData['uid'], 'blog/' . $matches[1] . '/posts', $parameters);
|
||||
if ($result->meta->status > 399) {
|
||||
Logger::notice('Error fetching status', ['meta' => $result->meta, 'response' => $result->response, 'errors' => $result->errors, 'blog' => $matches[1], 'id' => $matches[2]]);
|
||||
return [];
|
||||
}
|
||||
|
||||
Logger::debug('Got post', ['blog' => $matches[1], 'id' => $matches[2], 'result' => $result->response->posts]);
|
||||
if (!empty($result->response->posts)) {
|
||||
$hookData['item_id'] = tumblr_process_post($result->response->posts[0], $hookData['uid']);
|
||||
}
|
||||
}
|
||||
|
||||
function tumblr_support_follow(array &$data)
|
||||
{
|
||||
if ($data['protocol'] == Protocol::TUMBLR) {
|
||||
$data['result'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
function tumblr_follow(array &$hook_data)
|
||||
{
|
||||
$uid = DI::userSession()->getLocalUserId();
|
||||
|
||||
if (!tumblr_enabled_for_user($uid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::debug('Check if contact is Tumblr', ['url' => $hook_data['url']]);
|
||||
|
||||
$fields = tumblr_get_contact_by_url($hook_data['url']);
|
||||
if (empty($fields)) {
|
||||
Logger::debug('Contact is not a Tumblr contact', ['url' => $hook_data['url']]);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = tumblr_post($uid, 'user/follow', ['url' => $fields['url']]);
|
||||
if ($result->meta->status <= 399) {
|
||||
$hook_data['contact'] = $fields;
|
||||
Logger::debug('Successfully start following', ['url' => $fields['url']]);
|
||||
} else {
|
||||
Logger::notice('Following failed', ['meta' => $result->meta, 'response' => $result->response, 'errors' => $result->errors, 'url' => $fields['url']]);
|
||||
}
|
||||
}
|
||||
|
||||
function tumblr_unfollow(array &$hook_data)
|
||||
{
|
||||
if (!tumblr_enabled_for_user($hook_data['uid'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tumblr_get_contact_uuid($hook_data['contact'])) {
|
||||
return;
|
||||
}
|
||||
$result = tumblr_post($hook_data['uid'], 'user/unfollow', ['url' => $hook_data['contact']['url']]);
|
||||
$hook_data['result'] = ($result->meta->status <= 399);
|
||||
}
|
||||
|
||||
function tumblr_block(array &$hook_data)
|
||||
{
|
||||
if (!tumblr_enabled_for_user($hook_data['uid'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$uuid = tumblr_get_contact_uuid($hook_data['contact']);
|
||||
if (!$uuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
$result = tumblr_post($hook_data['uid'], 'blog/' . tumblr_get_page($hook_data['uid']) . '/blocks', ['blocked_tumblelog' => $uuid]);
|
||||
$hook_data['result'] = ($result->meta->status <= 399);
|
||||
|
||||
if ($hook_data['result']) {
|
||||
$cdata = Contact::getPublicAndUserContactID($hook_data['contact']['id'], $hook_data['uid']);
|
||||
if (!empty($cdata['user'])) {
|
||||
Contact::remove($cdata['user']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tumblr_unblock(array &$hook_data)
|
||||
{
|
||||
if (!tumblr_enabled_for_user($hook_data['uid'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$uuid = tumblr_get_contact_uuid($hook_data['contact']);
|
||||
if (!$uuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
$result = tumblr_delete($hook_data['uid'], 'blog/' . tumblr_get_page($hook_data['uid']) . '/blocks', ['blocked_tumblelog' => $uuid]);
|
||||
$hook_data['result'] = ($result->meta->status <= 399);
|
||||
}
|
||||
|
||||
function tumblr_get_contact_uuid(array $contact): string
|
||||
{
|
||||
if (($contact['network'] != Protocol::TUMBLR) || (substr($contact['poll'], 0, 8) != 'tumblr::')) {
|
||||
return '';
|
||||
}
|
||||
return substr($contact['poll'], 8);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -544,26 +715,37 @@ function tumblr_fetch_dashboard(int $uid)
|
|||
continue;
|
||||
}
|
||||
|
||||
$item = tumblr_get_header($post, $uri, $uid);
|
||||
tumblr_process_post($post, $uid, $uri);
|
||||
|
||||
$item = tumblr_get_content($item, $post);
|
||||
|
||||
$id = item::insert($item);
|
||||
|
||||
if ($id) {
|
||||
$stored = Post::selectFirst(['uri-id'], ['id' => $id]);
|
||||
|
||||
if (!empty($post->tags)) {
|
||||
foreach ($post->tags as $tag) {
|
||||
Tag::store($stored['uri-id'], Tag::HASHTAG, $tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DI::pConfig()->set($uid, 'tumblr', 'last_id', $last);
|
||||
}
|
||||
}
|
||||
|
||||
function tumblr_process_post(stdClass $post, int $uid, string $uri = ''): int
|
||||
{
|
||||
if (empty($uri)) {
|
||||
$uri = 'tumblr::' . $post->id_string . ':' . $post->reblog_key;
|
||||
}
|
||||
|
||||
$item = tumblr_get_header($post, $uri, $uid);
|
||||
|
||||
$item = tumblr_get_content($item, $post);
|
||||
|
||||
$id = item::insert($item);
|
||||
|
||||
if ($id) {
|
||||
$stored = Post::selectFirst(['uri-id'], ['id' => $id]);
|
||||
|
||||
if (!empty($post->tags)) {
|
||||
foreach ($post->tags as $tag) {
|
||||
Tag::store($stored['uri-id'], Tag::HASHTAG, $tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial data for the item array
|
||||
*
|
||||
|
@ -969,6 +1151,80 @@ function tumblr_get_blogs(int $uid): array
|
|||
return $blogs;
|
||||
}
|
||||
|
||||
function tumblr_enabled_for_user(int $uid)
|
||||
{
|
||||
return !empty($uid) && !empty(DI::pConfig()->get($uid, 'tumblr', 'access_token')) &&
|
||||
!empty(DI::pConfig()->get($uid, 'tumblr', 'refresh_token')) &&
|
||||
!empty(DI::config()->get('tumblr', 'consumer_key')) &&
|
||||
!empty(DI::config()->get('tumblr', 'consumer_secret'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a contact array from a Tumblr url
|
||||
*
|
||||
* @param string $url
|
||||
* @return array
|
||||
*/
|
||||
function tumblr_get_contact_by_url(string $url): array
|
||||
{
|
||||
$consumer_key = DI::config()->get('tumblr', 'consumer_key');
|
||||
if (empty($consumer_key)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!preg_match('#^https?://tumblr.com/(.+)#', $url, $matches) && !preg_match('#^https?://www\.tumblr.com/(.+)#', $url, $matches) && !preg_match('#^https?://(.+)\.tumblr.com#', $url, $matches)) {
|
||||
$curlResult = DI::httpClient()->get($url);
|
||||
$html = $curlResult->getBody();
|
||||
if (empty($html)) {
|
||||
return [];
|
||||
}
|
||||
$doc = new DOMDocument();
|
||||
@$doc->loadHTML($html);
|
||||
$xpath = new DomXPath($doc);
|
||||
$body = $xpath->query('body');
|
||||
$attributes = tumblr_get_attributes($body->item(0));
|
||||
$blog = $attributes['data-urlencoded-name'] ?? '';
|
||||
} else {
|
||||
$blogs = explode('/', $matches[1]);
|
||||
$blog = $blogs[0] ?? '';
|
||||
}
|
||||
|
||||
if (empty($blog)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$curlResult = DI::httpClient()->get('https://api.tumblr.com/v2/blog/' . $blog . '/info?api_key=' . $consumer_key);
|
||||
$body = $curlResult->getBody();
|
||||
$data = json_decode($body);
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$baseurl = 'https://tumblr.com';
|
||||
$url = $baseurl . '/' . $data->response->blog->name;
|
||||
|
||||
return [
|
||||
'url' => $url,
|
||||
'nurl' => Strings::normaliseLink($url),
|
||||
'addr' => $data->response->blog->name . '@tumblr.com',
|
||||
'alias' => $data->response->blog->url,
|
||||
'batch' => '',
|
||||
'notify' => '',
|
||||
'poll' => 'tumblr::' . $data->response->blog->uuid,
|
||||
'poco' => '',
|
||||
'name' => $data->response->blog->title,
|
||||
'nick' => $data->response->blog->name,
|
||||
'network' => Protocol::TUMBLR,
|
||||
'baseurl' => $baseurl,
|
||||
'pubkey' => '',
|
||||
'priority' => 0,
|
||||
'guid' => $data->response->blog->uuid,
|
||||
'about' => $data->response->blog->description,
|
||||
'photo' => $data->response->blog->avatar[0]->url,
|
||||
'header' => $data->response->blog->theme->header_image_focused,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an OAuth2 GET request
|
||||
*
|
||||
|
@ -1005,6 +1261,27 @@ function tumblr_post(int $uid, string $url, array $parameters): stdClass
|
|||
return tumblr_format_result($curlResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an OAuth2 DELETE request
|
||||
*
|
||||
* @param integer $uid
|
||||
* @param string $url
|
||||
* @param array $parameters
|
||||
* @return stdClass
|
||||
*/
|
||||
function tumblr_delete(int $uid, string $url, array $parameters): stdClass
|
||||
{
|
||||
$url = 'https://api.tumblr.com/v2/' . $url;
|
||||
|
||||
$opts = [
|
||||
HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . tumblr_get_token($uid)]],
|
||||
HttpClientOptions::FORM_PARAMS => $parameters
|
||||
];
|
||||
|
||||
$curlResult = DI::httpClient()->request('delete', $url, $opts);
|
||||
return tumblr_format_result($curlResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the get/post result value
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue