import asyncio import logging from uuid import uuid4 from . import misc from .application import app from .misc import distill_inboxes, distill_object_id, request async def handle_relay(actor, data, request): cache = app['cache'].objects object_id = misc.distill_object_id(data) if object_id in cache: logging.debug(f'>> already relayed {object_id} as {cache[object_id]}') return activity_id = f"https://{request.host}/activities/{uuid.uuid4()}" message = { "@context": "https://www.w3.org/ns/activitystreams", "type": "Announce", "to": [f"https://{request.host}/followers"], "actor": f"https://{request.host}/actor", "object": object_id, "id": activity_id } logging.debug(f'>> relay: {message}') inboxes = misc.distill_inboxes(actor['id'], object_id) futures = [misc.request(inbox, data=message) for inbox in inboxes] asyncio.ensure_future(asyncio.gather(*futures)) cache[object_id] = activity_id async def handle_forward(actor, data, request): cache = app['cache'].objects object_id = misc.distill_object_id(data) if object_id in cache: logging.debug(f'>> already forwarded {object_id}.') return logging.debug(f'>> Relay {data}') inboxes = misc.distill_inboxes(actor['id'], object_id) futures = [misc.request(inbox, data=data) for inbox in inboxes] asyncio.ensure_future(asyncio.gather(*futures)) cache[object_id] = object_id async def handle_follow(actor, data, request): config = app['config'] database = app['database'] inbox = misc.get_actor_inbox(actor) if inbox not in database.inboxes: database.add_inbox(inbox) asyncio.ensure_future(follow_remote_actor(actor['id'])) message = { "@context": "https://www.w3.org/ns/activitystreams", "type": "Accept", "to": [actor["id"]], "actor": config.actor, # this is wrong per litepub, but mastodon < 2.4 is not compliant with that profile. "object": { "type": "Follow", "id": data["id"], "object": config.actor, "actor": actor["id"] }, "id": f"https://{request.host}/activities/{uuid4()}", } asyncio.ensure_future(misc.request(inbox, message)) async def handle_undo(actor, data, request): if data['object']['type'] != 'Follow': return inbox = app['database'].get_inbox(actor['id']) if not inbox: return app['database'].del_inbox(inbox) await misc.unfollow_remote_actor(actor['id']) processors = { 'Announce': handle_relay, 'Create': handle_relay, 'Delete': handle_forward, 'Follow': handle_follow, 'Undo': handle_undo, 'Update': handle_forward, } async def run_processor(request, data, actor): return await processors[data['type']](actor, data, request)