mirror of
https://git.friendi.ca/friendica/friendica-addons.git
synced 2024-11-23 16:03:40 +00:00
Merge pull request 'Bluesky: Improved link transmission /thread import' (#1398) from heluecht/friendica-addons:bluesky-links into develop
Reviewed-on: https://git.friendi.ca/friendica/friendica-addons/pulls/1398
This commit is contained in:
commit
ba1af2b73e
1 changed files with 91 additions and 39 deletions
|
@ -6,6 +6,7 @@
|
||||||
* Author: Michael Vogel <https://pirati.ca/profile/heluecht>
|
* Author: Michael Vogel <https://pirati.ca/profile/heluecht>
|
||||||
*
|
*
|
||||||
* @todo
|
* @todo
|
||||||
|
* Currently technical issues in the core:
|
||||||
* - Outgoing mentions
|
* - Outgoing mentions
|
||||||
*
|
*
|
||||||
* Possibly not possible:
|
* Possibly not possible:
|
||||||
|
@ -17,10 +18,8 @@
|
||||||
*
|
*
|
||||||
* Possibly interesting:
|
* Possibly interesting:
|
||||||
* - https://atproto.com/lexicons/com-atproto-label#comatprotolabelsubscribelabels
|
* - https://atproto.com/lexicons/com-atproto-label#comatprotolabelsubscribelabels
|
||||||
* - https://atproto.com/lexicons/com-atproto-moderation#comatprotomoderationcreatereport
|
|
||||||
* - https://atproto.com/lexicons/com-atproto-repo#comatprotorepoputrecord
|
* - https://atproto.com/lexicons/com-atproto-repo#comatprotorepoputrecord
|
||||||
* - https://atproto.com/lexicons/app-bsky-embed#appbskyembedrecordwithmedia
|
* - https://atproto.com/lexicons/app-bsky-embed#appbskyembedrecordwithmedia
|
||||||
* - https://atproto.com/lexicons/app-bsky-feed#appbskyfeedgetpostthread
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Friendica\Content\Text\BBCode;
|
use Friendica\Content\Text\BBCode;
|
||||||
|
@ -614,12 +613,13 @@ function bluesky_create_post(array $item, stdClass $root = null, stdClass $paren
|
||||||
}
|
}
|
||||||
|
|
||||||
$did = DI::pConfig()->get($uid, 'bluesky', 'did');
|
$did = DI::pConfig()->get($uid, 'bluesky', 'did');
|
||||||
$urls = bluesky_get_urls($item['body']);
|
$urls = bluesky_get_urls(Post\Media::removeFromBody($item['body']));
|
||||||
|
$item['body'] = $urls['body'];
|
||||||
|
|
||||||
$msg = Plaintext::getPost($item, 300, false, BBCode::BLUESKY);
|
$msg = Plaintext::getPost($item, 300, false, BBCode::BLUESKY);
|
||||||
foreach ($msg['parts'] as $key => $part) {
|
foreach ($msg['parts'] as $key => $part) {
|
||||||
|
|
||||||
$facets = bluesky_get_facets($part, $urls);
|
$facets = bluesky_get_facets($part, $urls['urls']);
|
||||||
|
|
||||||
$record = [
|
$record = [
|
||||||
'text' => $facets['body'],
|
'text' => $facets['body'],
|
||||||
|
@ -669,19 +669,45 @@ function bluesky_get_urls(string $body): array
|
||||||
$urls = [];
|
$urls = [];
|
||||||
|
|
||||||
// Search for pure links
|
// Search for pure links
|
||||||
if (preg_match_all("/\[url\](https?:.*?)\[\/url\]/ism", $body, $matches)) {
|
if (preg_match_all("/\[url\](https?:.*?)\[\/url\]/ism", $body, $matches, PREG_SET_ORDER)) {
|
||||||
foreach ($matches[1] as $url) {
|
foreach ($matches as $match) {
|
||||||
$urls[] = $url;
|
$text = Strings::getStyledURL($match[1]);
|
||||||
|
$hash = bluesky_get_hash_for_url($match[0], mb_strlen($text));
|
||||||
|
$urls[] = ['url' => $match[1], 'text' => $text, 'hash' => $hash];
|
||||||
|
$body = str_replace($match[0], $hash, $body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for links with descriptions
|
// Search for links with descriptions
|
||||||
if (preg_match_all("/\[url\=(https?:.*?)\].*?\[\/url\]/ism", $body, $matches)) {
|
if (preg_match_all("/\[url\=(https?:.*?)\](.*?)\[\/url\]/ism", $body, $matches, PREG_SET_ORDER)) {
|
||||||
foreach ($matches[1] as $url) {
|
foreach ($matches as $match) {
|
||||||
$urls[] = $url;
|
if ($match[1] == $match[2]) {
|
||||||
|
$text = Strings::getStyledURL($match[1]);
|
||||||
|
} else {
|
||||||
|
$text = $match[2];
|
||||||
|
}
|
||||||
|
if (mb_strlen($text) < 100) {
|
||||||
|
$hash = bluesky_get_hash_for_url($match[0], mb_strlen($text));
|
||||||
|
$urls[] = ['url' => $match[1], 'text' => $text, 'hash' => $hash];
|
||||||
|
$body = str_replace($match[0], $hash, $body);
|
||||||
|
} else {
|
||||||
|
$text = Strings::getStyledURL($match[1]);
|
||||||
|
$hash = bluesky_get_hash_for_url($match[0], mb_strlen($text));
|
||||||
|
$urls[] = ['url' => $match[1], 'text' => $text, 'hash' => $hash];
|
||||||
|
$body = str_replace($match[0], $text . ' ' . $hash, $body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $urls;
|
}
|
||||||
|
|
||||||
|
return ['body' => $body, 'urls' => $urls];
|
||||||
|
}
|
||||||
|
|
||||||
|
function bluesky_get_hash_for_url(string $text, int $linklength): string
|
||||||
|
{
|
||||||
|
if ($linklength <= 10) {
|
||||||
|
return '|' . hash('crc32', $text) . '|';
|
||||||
|
}
|
||||||
|
return substr('|' . hash('crc32', $text) . base64_encode($text), 0, $linklength - 2) . '|';
|
||||||
}
|
}
|
||||||
|
|
||||||
function bluesky_get_facets(string $body, array $urls): array
|
function bluesky_get_facets(string $body, array $urls): array
|
||||||
|
@ -689,7 +715,7 @@ function bluesky_get_facets(string $body, array $urls): array
|
||||||
$facets = [];
|
$facets = [];
|
||||||
|
|
||||||
foreach ($urls as $url) {
|
foreach ($urls as $url) {
|
||||||
$pos = strpos($body, $url);
|
$pos = strpos($body, $url['hash']);
|
||||||
if ($pos === false) {
|
if ($pos === false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -698,16 +724,16 @@ function bluesky_get_facets(string $body, array $urls): array
|
||||||
} else {
|
} else {
|
||||||
$prefix = '';
|
$prefix = '';
|
||||||
}
|
}
|
||||||
$linktext = Strings::getStyledURL($url);
|
|
||||||
$body = $prefix . $linktext . substr($body, $pos + strlen($url));
|
$body = $prefix . $url['text'] . substr($body, $pos + strlen($url['hash']));
|
||||||
|
|
||||||
$facet = new stdClass;
|
$facet = new stdClass;
|
||||||
$facet->index = new stdClass;
|
$facet->index = new stdClass;
|
||||||
$facet->index->byteEnd = $pos + strlen($linktext);
|
$facet->index->byteEnd = $pos + strlen($url['text']);
|
||||||
$facet->index->byteStart = $pos;
|
$facet->index->byteStart = $pos;
|
||||||
|
|
||||||
$feature = new stdClass;
|
$feature = new stdClass;
|
||||||
$feature->uri = $url;
|
$feature->uri = $url['url'];
|
||||||
$type = '$type';
|
$type = '$type';
|
||||||
$feature->$type = 'app.bsky.richtext.facet#link';
|
$feature->$type = 'app.bsky.richtext.facet#link';
|
||||||
|
|
||||||
|
@ -858,8 +884,9 @@ function bluesky_fetch_notifications(int $uid)
|
||||||
$item['gravity'] = Item::GRAVITY_ACTIVITY;
|
$item['gravity'] = Item::GRAVITY_ACTIVITY;
|
||||||
$item['body'] = $item['verb'] = Activity::LIKE;
|
$item['body'] = $item['verb'] = Activity::LIKE;
|
||||||
$item['thr-parent'] = bluesky_get_uri($notification->record->subject);
|
$item['thr-parent'] = bluesky_get_uri($notification->record->subject);
|
||||||
|
$item['thr-parent'] = bluesky_fetch_missing_post($item['thr-parent'], $uid, $item['contact-id']);
|
||||||
$result = Item::insert($item);
|
$result = Item::insert($item);
|
||||||
Logger::debug('Got like', ['uid' => $uid, 'result' => $result]);
|
Logger::debug('Got like', ['uid' => $uid, 'result' => $result, 'uri' => $uri]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'repost':
|
case 'repost':
|
||||||
|
@ -867,32 +894,33 @@ function bluesky_fetch_notifications(int $uid)
|
||||||
$item['gravity'] = Item::GRAVITY_ACTIVITY;
|
$item['gravity'] = Item::GRAVITY_ACTIVITY;
|
||||||
$item['body'] = $item['verb'] = Activity::ANNOUNCE;
|
$item['body'] = $item['verb'] = Activity::ANNOUNCE;
|
||||||
$item['thr-parent'] = bluesky_get_uri($notification->record->subject);
|
$item['thr-parent'] = bluesky_get_uri($notification->record->subject);
|
||||||
|
$item['thr-parent'] = bluesky_fetch_missing_post($item['thr-parent'], $uid, $item['contact-id']);
|
||||||
$result = Item::insert($item);
|
$result = Item::insert($item);
|
||||||
Logger::debug('Got repost', ['uid' => $uid, 'result' => $result]);
|
Logger::debug('Got repost', ['uid' => $uid, 'result' => $result, 'uri' => $uri]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'follow':
|
case 'follow':
|
||||||
$contact = bluesky_get_contact($notification->author, $uid, $uid);
|
$contact = bluesky_get_contact($notification->author, $uid, $uid);
|
||||||
Logger::debug('New follower', ['uid' => $uid, 'nick' => $contact['nick']]);
|
Logger::debug('New follower', ['uid' => $uid, 'nick' => $contact['nick'], 'uri' => $uri]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'mention':
|
case 'mention':
|
||||||
$result = bluesky_process_post($notification, $uid, Item::PR_PUSHED);
|
$result = bluesky_process_post($notification, $uid, Item::PR_PUSHED);
|
||||||
Logger::debug('Got mention', ['uid' => $uid, 'result' => $result]);
|
Logger::debug('Got mention', ['uid' => $uid, 'result' => $result, 'uri' => $uri]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'reply':
|
case 'reply':
|
||||||
$result = bluesky_process_post($notification, $uid, Item::PR_PUSHED);
|
$result = bluesky_process_post($notification, $uid, Item::PR_PUSHED);
|
||||||
Logger::debug('Got reply', ['uid' => $uid, 'result' => $result]);
|
Logger::debug('Got reply', ['uid' => $uid, 'result' => $result, 'uri' => $uri]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'quote':
|
case 'quote':
|
||||||
$result = bluesky_process_post($notification, $uid, Item::PR_PUSHED);
|
$result = bluesky_process_post($notification, $uid, Item::PR_PUSHED);
|
||||||
Logger::debug('Got quote', ['uid' => $uid, 'result' => $result]);
|
Logger::debug('Got quote', ['uid' => $uid, 'result' => $result, 'uri' => $uri]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Logger::notice('Unhandled reason', ['reason' => $notification->reason]);
|
Logger::notice('Unhandled reason', ['reason' => $notification->reason, 'uri' => $uri]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1023,7 +1051,7 @@ function bluesky_get_text(stdClass $record, int $uid): string
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'app.bsky.richtext.facet#mention':
|
case 'app.bsky.richtext.facet#mention':
|
||||||
$contact = Contact::selectFirst(['id'], ['nurl' => $feature->did, 'uid' => [0, $uid]]);
|
$contact = Contact::getByURL($feature->did, null, ['id']);
|
||||||
if (!empty($contact['id'])) {
|
if (!empty($contact['id'])) {
|
||||||
$url = DI::baseUrl() . '/contact/' . $contact['id'];
|
$url = DI::baseUrl() . '/contact/' . $contact['id'];
|
||||||
if (substr($linktext, 0, 1) == '@') {
|
if (substr($linktext, 0, 1) == '@') {
|
||||||
|
@ -1179,21 +1207,16 @@ function bluesky_get_uri_parts(string $uri): ?stdClass
|
||||||
|
|
||||||
function bluesky_fetch_missing_post(string $uri, int $uid, int $causer): string
|
function bluesky_fetch_missing_post(string $uri, int $uid, int $causer): string
|
||||||
{
|
{
|
||||||
if (Post::exists(['uri' => $uri, 'uid' => [$uid, 0]])) {
|
$fetched_uri = bluesky_fetch_post($uri, $uid);
|
||||||
Logger::debug('Post exists', ['uri' => $uri]);
|
if (!empty($fetched_uri)) {
|
||||||
return $uri;
|
return $fetched_uri;
|
||||||
}
|
|
||||||
|
|
||||||
$reply = Post::selectFirst(['uri'], ['extid' => $uri, 'uid' => [$uid, 0]]);
|
|
||||||
if (!empty($reply['uri'])) {
|
|
||||||
return $reply['uri'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::debug('Fetch missing post', ['uri' => $uri]);
|
Logger::debug('Fetch missing post', ['uri' => $uri]);
|
||||||
$class = bluesky_get_uri_class($uri);
|
$class = bluesky_get_uri_class($uri);
|
||||||
$fetch_uri = $class->uri;
|
$fetch_uri = $class->uri;
|
||||||
|
|
||||||
$data = bluesky_get($uid, '/xrpc/app.bsky.feed.getPosts?uris=' . urlencode($fetch_uri), HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . bluesky_get_token($uid)]]]);
|
$data = bluesky_get($uid, '/xrpc/app.bsky.feed.getPostThread?uri=' . urlencode($fetch_uri), HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . bluesky_get_token($uid)]]]);
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -1202,10 +1225,32 @@ function bluesky_fetch_missing_post(string $uri, int $uid, int $causer): string
|
||||||
$cdata = Contact::getPublicAndUserContactID($causer, $uid);
|
$cdata = Contact::getPublicAndUserContactID($causer, $uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($data->posts as $post) {
|
return bluesky_process_thread($data->thread, $uid, $cdata);
|
||||||
$uri = bluesky_get_uri($post);
|
}
|
||||||
$item = bluesky_get_header($post, $uri, $uid, $uid);
|
|
||||||
$item = bluesky_get_content($item, $post->record, $uid);
|
function bluesky_fetch_post(string $uri, int $uid): string
|
||||||
|
{
|
||||||
|
if (Post::exists(['uri' => $uri, 'uid' => [$uid, 0]])) {
|
||||||
|
Logger::debug('Post exists', ['uri' => $uri]);
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$reply = Post::selectFirst(['uri'], ['extid' => $uri, 'uid' => [$uid, 0]]);
|
||||||
|
if (!empty($reply['uri'])) {
|
||||||
|
Logger::debug('Post with extid exists', ['uri' => $uri]);
|
||||||
|
return $reply['uri'];
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function bluesky_process_thread(stdClass $thread, int $uid, array $cdata): string
|
||||||
|
{
|
||||||
|
$uri = bluesky_get_uri($thread->post);
|
||||||
|
$fetched_uri = bluesky_fetch_post($uri, $uid);
|
||||||
|
if (empty($fetched_uri)) {
|
||||||
|
Logger::debug('Process missing post', ['uri' => $uri]);
|
||||||
|
$item = bluesky_get_header($thread->post, $uri, $uid, $uid);
|
||||||
|
$item = bluesky_get_content($item, $thread->post->record, $uid);
|
||||||
|
|
||||||
$item['post-reason'] = Item::PR_FETCHED;
|
$item['post-reason'] = Item::PR_FETCHED;
|
||||||
|
|
||||||
|
@ -1213,11 +1258,18 @@ function bluesky_fetch_missing_post(string $uri, int $uid, int $causer): string
|
||||||
$item['causer-id'] = $cdata['public'];
|
$item['causer-id'] = $cdata['public'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($post->embed)) {
|
if (!empty($thread->post->embed)) {
|
||||||
$item = bluesky_add_media($post->embed, $item, $uid);
|
$item = bluesky_add_media($thread->post->embed, $item, $uid);
|
||||||
}
|
}
|
||||||
$id = Item::insert($item);
|
$id = Item::insert($item);
|
||||||
Logger::debug('Stored item', ['id' => $id, 'uri' => $uri]);
|
Logger::debug('Stored item', ['id' => $id, 'uri' => $uri]);
|
||||||
|
} else {
|
||||||
|
Logger::debug('Post exists', ['uri' => $uri]);
|
||||||
|
$uri = $fetched_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($thread->replies as $reply) {
|
||||||
|
bluesky_process_thread($reply, $uid, $cdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $uri;
|
return $uri;
|
||||||
|
|
Loading…
Reference in a new issue