diff --git a/activitypub.py b/activitypub.py index f054339..41ef479 100644 --- a/activitypub.py +++ b/activitypub.py @@ -30,6 +30,7 @@ from common import ( host_url, LOCAL_DOMAINS, PRIMARY_DOMAIN, + PROTOCOL_DOMAINS, redirect_wrap, subdomain_wrap, unwrap, @@ -56,6 +57,8 @@ WEB_OPT_OUT_DOMAINS = None FEDI_URL_RE = re.compile(r'https://[^/]+/(@|users/)([^/@]+)(@[^/@]+)?(/(?:statuses/)?[0-9]+)?') +_BOT_ACTOR_IDS = None + def instance_actor(): global _INSTANCE_ACTOR @@ -65,6 +68,15 @@ def instance_actor(): return _INSTANCE_ACTOR +def bot_actor_ids(): + global _BOT_ACTOR_IDS + if _BOT_ACTOR_IDS is None: + from activitypub import ActivityPub + _BOT_ACTOR_IDS = [translate_user_id(id=domain, from_=Web, to=ActivityPub) + for domain in PROTOCOL_DOMAINS] + return _BOT_ACTOR_IDS + + class ActivityPub(User, Protocol): """ActivityPub protocol class. @@ -926,6 +938,7 @@ def inbox(protocol=None, id=None): # follows, or other activity types, since Mastodon doesn't currently mark # those as explicitly public. Use as2's is_public instead of as1's because # as1's interprets unlisted as true. + # TODO: move this to Protocol if type == 'Create' and not as2.is_public(activity, unlisted=False): logger.info('Dropping non-public activity') return 'OK' diff --git a/protocol.py b/protocol.py index 98018f7..5c80cc6 100644 --- a/protocol.py +++ b/protocol.py @@ -804,6 +804,20 @@ class Protocol: from_user.disable_protocol(proto) return 'OK', 200 + elif obj.type == 'post': + to_cc = (util.get_list(inner_obj_as1, 'to') + + util.get_list(inner_obj_as1, 'cc')) + if len(to_cc) == 1 and to_cc[0] in PROTOCOL_DOMAINS: + content = inner_obj_as1.get('content').strip().lower() + logger.info(f'DM to bot user {to_cc}: {content}') + proto = Protocol.for_bridgy_subdomain(to_cc[0]) + assert proto + if content in ('yes', 'ok'): + from_user.enable_protocol(proto) + elif content == 'no': + from_user.disable_protocol(proto) + return 'OK', 200 + # fetch actor if necessary if actor and actor.keys() == set(['id']): logger.info('Fetching actor so we have name, profile photo, etc') diff --git a/tests/test_activitypub.py b/tests/test_activitypub.py index 5d95b75..93174ac 100644 --- a/tests/test_activitypub.py +++ b/tests/test_activitypub.py @@ -829,6 +829,9 @@ class ActivityPubTest(TestCase): def test_inbox_unlisted(self, *mocks): self._test_inbox_with_to_ignored(['@unlisted'], *mocks) + def test_inbox_dm(self, *mocks): + self._test_inbox_with_to_ignored(['http://localhost/web/user.com'], *mocks) + def _test_inbox_with_to_ignored(self, to, mock_head, mock_get, mock_post): Follower.get_or_create(to=self.make_user(ACTOR['id'], cls=ActivityPub), from_=self.user) diff --git a/tests/test_protocol.py b/tests/test_protocol.py index 3c784d4..8b6d9f4 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -1796,7 +1796,7 @@ class ProtocolReceiveTest(TestCase): user = self.make_user('eefake:user', cls=ExplicitEnableFake) self.assertFalse(ExplicitEnableFake.is_enabled_to(Fake, user)) - # protocol isn't enabled yet, block should be a noop + # fake protocol isn't enabled yet, block should be a noop self.assertEqual(('OK', 200), ExplicitEnableFake.receive_as1(block)) user = user.key.get() self.assertEqual([], user.enabled_protocols) @@ -1826,6 +1826,45 @@ class ProtocolReceiveTest(TestCase): self.assertEqual([], user.enabled_protocols) self.assertFalse(ExplicitEnableFake.is_enabled_to(Fake, user)) + def test_dm_no_yes_sets_enabled_protocols(self): + dm = { + 'objectType': 'note', + 'id': 'eefake:dm', + 'actor': 'eefake:user', + 'to': ['fa.brid.gy'], + 'content': 'no', + } + + user = self.make_user('eefake:user', cls=ExplicitEnableFake) + self.assertFalse(ExplicitEnableFake.is_enabled_to(Fake, user)) + + # fake protocol isn't enabled yet, no DM should be a noop + self.assertEqual(('OK', 200), ExplicitEnableFake.receive_as1(dm)) + user = user.key.get() + self.assertEqual([], user.enabled_protocols) + + # yes DM should add to enabled_protocols + dm['id'] += '2' + dm['content'] = 'yes' + self.assertEqual(('OK', 200), ExplicitEnableFake.receive_as1(dm)) + user = user.key.get() + self.assertEqual(['fake'], user.enabled_protocols) + self.assertTrue(ExplicitEnableFake.is_enabled_to(Fake, user)) + + # another yes DM should be a noop + dm['id'] += '3' + self.assertEqual(('OK', 200), ExplicitEnableFake.receive_as1(dm)) + user = user.key.get() + self.assertEqual(['fake'], user.enabled_protocols) + + # block should remove from enabled_protocols + dm['id'] += '4' + dm['content'] = ' \n NO ' + self.assertEqual(('OK', 200), ExplicitEnableFake.receive_as1(dm)) + user = user.key.get() + self.assertEqual([], user.enabled_protocols) + self.assertFalse(ExplicitEnableFake.is_enabled_to(Fake, user)) + def test_receive_task_handler(self): note = { 'id': 'fake:post',