From 00d243a7856a5f1e288822cbe829a5cfd057cd16 Mon Sep 17 00:00:00 2001 From: Jason Robinson Date: Sat, 17 Aug 2019 21:02:25 +0300 Subject: [PATCH] Attach local images embedded into Post and Comment as attachments for AP Rip out any embedded images for the senders domain into entity._children list of Image objects. --- federation/entities/activitypub/entities.py | 6 ++--- federation/entities/activitypub/mappers.py | 3 +++ federation/entities/activitypub/mixins.py | 22 +++++++++++++++++++ .../entities/activitypub/test_entities.py | 10 +++++++++ federation/tests/fixtures/entities.py | 21 ++++++++++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/federation/entities/activitypub/entities.py b/federation/entities/activitypub/entities.py index dbe1b56..bb6b865 100644 --- a/federation/entities/activitypub/entities.py +++ b/federation/entities/activitypub/entities.py @@ -8,7 +8,7 @@ from federation.entities.activitypub.constants import ( CONTEXTS_DEFAULT, CONTEXT_MANUALLY_APPROVES_FOLLOWERS, CONTEXT_SENSITIVE, CONTEXT_HASHTAG, CONTEXT_LD_SIGNATURES) from federation.entities.activitypub.enums import ActorType, ObjectType, ActivityType -from federation.entities.activitypub.mixins import ActivitypubEntityMixin, CleanContentMixin +from federation.entities.activitypub.mixins import ActivitypubEntityMixin, CleanContentMixin, AttachImagesMixin from federation.entities.activitypub.objects import ImageObject from federation.entities.base import Profile, Post, Follow, Accept, Comment, Retraction, Share from federation.outbound import handle_send @@ -33,7 +33,7 @@ class ActivitypubAccept(ActivitypubEntityMixin, Accept): return as2 -class ActivitypubComment(ActivitypubEntityMixin, CleanContentMixin, Comment): +class ActivitypubComment(ActivitypubEntityMixin, AttachImagesMixin, CleanContentMixin, Comment): _type = ObjectType.NOTE.value def to_as2(self) -> Dict: @@ -138,7 +138,7 @@ class ActivitypubFollow(ActivitypubEntityMixin, Follow): return as2 -class ActivitypubPost(ActivitypubEntityMixin, CleanContentMixin, Post): +class ActivitypubPost(ActivitypubEntityMixin, AttachImagesMixin, CleanContentMixin, Post): _type = ObjectType.NOTE.value def to_as2(self) -> Dict: diff --git a/federation/entities/activitypub/mappers.py b/federation/entities/activitypub/mappers.py index 12b6e3b..5ccc37d 100644 --- a/federation/entities/activitypub/mappers.py +++ b/federation/entities/activitypub/mappers.py @@ -201,6 +201,9 @@ def get_outbound_entity(entity: BaseEntity, private_key): # # in all situations but is apparently being removed. # # TODO: remove this once Diaspora removes the extra signature # outbound.parent_signature = outbound.signature + if getattr(outbound, "pre_send", None) and isinstance(getattr(outbound, "pre_send"), callable): + # noinspection PyUnresolvedReferences + outbound.pre_send() return outbound diff --git a/federation/entities/activitypub/mixins.py b/federation/entities/activitypub/mixins.py index 2ca15ac..04e1152 100644 --- a/federation/entities/activitypub/mixins.py +++ b/federation/entities/activitypub/mixins.py @@ -1,9 +1,31 @@ import re +from federation.entities.base import Image from federation.entities.mixins import BaseEntity, RawContentMixin from federation.entities.utils import get_base_attributes +class AttachImagesMixin(RawContentMixin): + def pre_send(self) -> None: + """ + Attach any embedded images from the sender server. + """ + actor_domain = re.match(r"https?://([\w.\-]+)", self.actor_id).groups()[0] + actor_domain = actor_domain.replace(".", "\\.") + regex = r"!\[([\w ]*)\]\((https?://%s[\w\/\-.]+\.[jpg|gif|jpeg|png]*)\)" % actor_domain + matches = re.finditer(regex, self.raw_content, re.MULTILINE | re.IGNORECASE) + for match in matches: + groups = match.groups() + self._children.append( + Image( + url=groups[1], + name=groups[0] or "", + ) + ) + self.raw_content = re.sub(regex, "", self.raw_content, re.MULTILINE | re.IGNORECASE) + self.raw_content = self.raw_content.strip() + + class ActivitypubEntityMixin(BaseEntity): _type = None diff --git a/federation/tests/entities/activitypub/test_entities.py b/federation/tests/entities/activitypub/test_entities.py index 0d00452..bcdf891 100644 --- a/federation/tests/entities/activitypub/test_entities.py +++ b/federation/tests/entities/activitypub/test_entities.py @@ -209,3 +209,13 @@ class TestEntitiesPostReceive: def test_post__post_receive__cleans_linkified_tags(self, activitypubpost_linkified_tags): activitypubpost_linkified_tags.post_receive() assert activitypubpost_linkified_tags.raw_content == '

👁️ foobar 👁️

barfoo!
#fanart #mastoart

' + + +class TestEntitiesPreSend: + def test_post_local_images_are_attached(self, activitypubpost_localimages): + activitypubpost_localimages.pre_send() + assert activitypubpost_localimages.raw_content == "#Cycling #lauttasaari #sea #sun" + assert len(activitypubpost_localimages._children) == 4 + image = activitypubpost_localimages._children[3] + assert image.url == "https://jasonrobinson.me/media/uploads/2019/07/16/daa24d89-cedf-4fc7-bad8-74a902541479.jpg" + assert image.name == "foobar barfoo" diff --git a/federation/tests/fixtures/entities.py b/federation/tests/fixtures/entities.py index b5e0bb1..b9b83e8 100644 --- a/federation/tests/fixtures/entities.py +++ b/federation/tests/fixtures/entities.py @@ -70,6 +70,27 @@ def activitypubpost(): ) +@pytest.fixture +def activitypubpost_localimages(): + with freeze_time("2019-04-27"): + return ActivitypubPost( + raw_content=""" +#Cycling #lauttasaari #sea #sun + + +![](https://jasonrobinson.me/media/uploads/2019/07/16/daa24d89-cedf-4fc7-bad8-74a902541476.jpg)![](https://jasonrobinson.me/media/uploads/2019/07/16/daa24d89-cedf-4fc7-bad8-74a902541477.jpg) + +![foobar](https://jasonrobinson.me/media/uploads/2019/07/16/daa24d89-cedf-4fc7-bad8-74a902541478.jpg) +![foobar barfoo](https://jasonrobinson.me/media/uploads/2019/07/16/daa24d89-cedf-4fc7-bad8-74a902541479.jpg) +""", + public=True, + provider_display_name="Socialhome", + id=f"http://127.0.0.1:8000/post/123456/", + activity_id=f"http://127.0.0.1:8000/post/123456/#create", + actor_id=f"https://jasonrobinson.me/u/jaywink/", + ) + + @pytest.fixture def activitypubpost_linkified_tags(): with freeze_time("2019-04-27"):