From 9bf7529dda7b936b86cabd74f0976de433058f50 Mon Sep 17 00:00:00 2001 From: gudzpoz Date: Sun, 12 Nov 2023 20:59:49 +0800 Subject: [PATCH] Improve emoji federation and mastodon api compliance --- src/Content/Smilies.php | 28 ++++++++++++++++++++++++ src/Factory/Api/Mastodon/Emoji.php | 21 ++++++++++++++---- src/Factory/Api/Mastodon/Status.php | 10 ++++++++- src/Object/Api/Mastodon/Status.php | 4 ++-- src/Protocol/ActivityPub/Transmitter.php | 23 +++++++++++++++++++ 5 files changed, 79 insertions(+), 7 deletions(-) diff --git a/src/Content/Smilies.php b/src/Content/Smilies.php index 2e1a6cf19c..760bfbce9e 100644 --- a/src/Content/Smilies.php +++ b/src/Content/Smilies.php @@ -152,6 +152,34 @@ class Smilies return $params; } + /** + * Finds all used smilies (like :heart: or :p) in the provided text. + * + * @param string $text that might contain smilie usages (denoted by a starting colon) + * @param bool $extract_url whether to further extract image urls + * @return array with smilie codes (colon included) as the keys, the smilie images as values + */ + public static function extractUsedSmilies(string $text, bool $extract_url = false): array + { + $emojis = []; + + $smilies = self::getList(); + $icons = $smilies['icons']; + foreach ($smilies['texts'] as $i => $name) { + if (strstr($text, $name)) { + $image = $icons[$i]; + if ($extract_url) { + if (preg_match('/src="(.+?)"/', $image, $match)) { + $image = $match[1]; + } else { + continue; + } + } + $emojis[$name] = $image; + } + } + return $emojis; + } /** * Copied from http://php.net/manual/en/function.str-replace.php#88569 diff --git a/src/Factory/Api/Mastodon/Emoji.php b/src/Factory/Api/Mastodon/Emoji.php index 157b91bf91..b7f4ee6ead 100644 --- a/src/Factory/Api/Mastodon/Emoji.php +++ b/src/Factory/Api/Mastodon/Emoji.php @@ -32,19 +32,22 @@ class Emoji extends BaseFactory } /** + * Creates an emoji collection from shortcode => image mappings. + * * @param array $smilies * * @return Emojis */ - public function createCollectionFromSmilies(array $smilies): Emojis + public function createCollectionFromArray(array $smilies): Emojis { $prototype = null; $emojis = []; - foreach ($smilies['texts'] as $key => $shortcode) { - if (preg_match('/src="(.+?)"/', $smilies['icons'][$key], $matches)) { + foreach ($smilies as $shortcode => $icon) { + if (preg_match('/src="(.+?)"/', $icon, $matches)) { $url = $matches[1]; + $shortcode = trim($shortcode, ':'); if ($prototype === null) { $prototype = $this->create($shortcode, $url); @@ -52,9 +55,19 @@ class Emoji extends BaseFactory } else { $emojis[] = \Friendica\Object\Api\Mastodon\Emoji::createFromPrototype($prototype, $shortcode, $url); } - }; + } } return new Emojis($emojis); } + + /** + * @param array $smilies + * + * @return Emojis + */ + public function createCollectionFromSmilies(array $smilies): Emojis + { + return self::createCollectionFromArray(array_combine($smilies['texts'], $smilies['icons'])); + } } diff --git a/src/Factory/Api/Mastodon/Status.php b/src/Factory/Api/Mastodon/Status.php index 4bf5609b9a..aaaa8d3d94 100644 --- a/src/Factory/Api/Mastodon/Status.php +++ b/src/Factory/Api/Mastodon/Status.php @@ -24,6 +24,7 @@ namespace Friendica\Factory\Api\Mastodon; use Friendica\BaseFactory; use Friendica\Content\ContactSelector; use Friendica\Content\Item as ContentItem; +use Friendica\Content\Smilies; use Friendica\Content\Text\BBCode; use Friendica\Core\Logger; use Friendica\Database\Database; @@ -57,6 +58,8 @@ class Status extends BaseFactory private $mstdnCardFactory; /** @var Attachment */ private $mstdnAttachmentFactory; + /** @var Emoji */ + private $mstdnEmojiFactory; /** @var Error */ private $mstdnErrorFactory; /** @var Poll */ @@ -74,6 +77,7 @@ class Status extends BaseFactory Tag $mstdnTagFactory, Card $mstdnCardFactory, Attachment $mstdnAttachmentFactory, + Emoji $mstdnEmojiFactory, Error $mstdnErrorFactory, Poll $mstdnPollFactory, ContentItem $contentItem, @@ -86,6 +90,7 @@ class Status extends BaseFactory $this->mstdnTagFactory = $mstdnTagFactory; $this->mstdnCardFactory = $mstdnCardFactory; $this->mstdnAttachmentFactory = $mstdnAttachmentFactory; + $this->mstdnEmojiFactory = $mstdnEmojiFactory; $this->mstdnErrorFactory = $mstdnErrorFactory; $this->mstdnPollFactory = $mstdnPollFactory; $this->contentItem = $contentItem; @@ -283,6 +288,9 @@ class Status extends BaseFactory } } + $used_smilies = Smilies::extractUsedSmilies($item['body'] ?: $item['raw-body']); + $emojis = $this->mstdnEmojiFactory->createCollectionFromArray($used_smilies)->getArrayCopy(true); + if ($is_reshare) { try { $reshare = $this->createFromUriId($uriId, $uid, $display_quote, false, false)->toArray(); @@ -309,7 +317,7 @@ class Status extends BaseFactory $visibility_data = $uid != $item['uid'] ? null : new FriendicaVisibility($this->aclFormatter->expand($item['allow_cid']), $this->aclFormatter->expand($item['deny_cid']), $this->aclFormatter->expand($item['allow_gid']), $this->aclFormatter->expand($item['deny_gid'])); $friendica = new FriendicaExtension($item['title'] ?? '', $item['changed'], $item['commented'], $item['received'], $counts->dislikes, $origin_dislike, $delivery_data, $visibility_data); - return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $in_reply, $reshare, $friendica, $quote, $poll); + return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $in_reply, $reshare, $friendica, $quote, $poll, $emojis); } /** diff --git a/src/Object/Api/Mastodon/Status.php b/src/Object/Api/Mastodon/Status.php index 122e62f9ca..59d2a6cc58 100644 --- a/src/Object/Api/Mastodon/Status.php +++ b/src/Object/Api/Mastodon/Status.php @@ -107,7 +107,7 @@ class Status extends BaseDataTransferObject * @param array $item * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function __construct(array $item, Account $account, Counts $counts, UserAttributes $userAttributes, bool $sensitive, Application $application, array $mentions, array $tags, Card $card, array $attachments, array $in_reply, array $reblog, FriendicaExtension $friendica, array $quote = null, array $poll = null) + public function __construct(array $item, Account $account, Counts $counts, UserAttributes $userAttributes, bool $sensitive, Application $application, array $mentions, array $tags, Card $card, array $attachments, array $in_reply, array $reblog, FriendicaExtension $friendica, array $quote = null, array $poll = null, array $emojis = null) { $reblogged = !empty($reblog); $this->id = (string)$item['uri-id']; @@ -152,7 +152,7 @@ class Status extends BaseDataTransferObject $this->media_attachments = $reblogged ? [] : $attachments; $this->mentions = $reblogged ? [] : $mentions; $this->tags = $reblogged ? [] : $tags; - $this->emojis = $reblogged ? [] : []; + $this->emojis = $reblogged ? [] : ($emojis ?: []); $this->card = $reblogged ? null : ($card->toArray() ?: null); $this->poll = $reblogged ? null : $poll; $this->friendica = $reblogged ? null : $friendica; diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index b18e247eac..130aa3ab08 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -23,6 +23,7 @@ namespace Friendica\Protocol\ActivityPub; use Friendica\App; use Friendica\Content\Feature; +use Friendica\Content\Smilies; use Friendica\Content\Text\BBCode; use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; @@ -1506,6 +1507,26 @@ class Transmitter return $location; } + /** + * Appends emoji tags to a tag array according to the tags used. + * + * @param array $tags Tag array + * @param string $text Text containing tags like :tag: + */ + private static function addEmojiTags(array &$tags, string $text) + { + foreach (Smilies::extractUsedSmilies($text, true) as $name => $url) { + $tags[] = [ + 'type' => 'Emoji', + 'name' => $name, + 'icon' => [ + 'type' => 'Image', + 'url' => $url, + ], + ]; + } + } + /** * Returns a tag array for a given item array * @@ -1538,6 +1559,8 @@ class Transmitter } } + self::addEmojiTags($tags, $item['body']); + $announce = self::getAnnounceArray($item); // Mention the original author upon commented reshares if (!empty($announce['comment'])) {