This repository has been archived on 2023-09-24. You can view files and clone it, but cannot push or open issues or pull requests.
takahe/users/models/inbox_message.py
2022-11-10 23:42:43 -07:00

80 lines
2.5 KiB
Python

from asgiref.sync import sync_to_async
from django.db import models
from stator.models import State, StateField, StateGraph, StatorModel
from users.models import Follow, Identity
class InboxMessageStates(StateGraph):
received = State(try_interval=300)
processed = State()
received.transitions_to(processed)
@classmethod
async def handle_received(cls, instance: "InboxMessage"):
type = instance.message_type
if type == "follow":
await instance.follow_request()
elif type == "accept":
inner_type = instance.message["object"]["type"].lower()
if inner_type == "follow":
await instance.follow_accepted()
else:
raise ValueError(f"Cannot handle activity of type accept.{inner_type}")
elif type == "undo":
inner_type = instance.message["object"]["type"].lower()
if inner_type == "follow":
await instance.follow_undo()
else:
raise ValueError(f"Cannot handle activity of type undo.{inner_type}")
else:
raise ValueError(f"Cannot handle activity of type {type}")
return cls.processed
class InboxMessage(StatorModel):
"""
an incoming inbox message that needs processing.
Yes, this is kind of its own message queue built on the state graph system.
It's fine. It'll scale up to a decent point.
"""
message = models.JSONField()
state = StateField(InboxMessageStates)
@sync_to_async
def follow_request(self):
"""
Handles an incoming follow request
"""
Follow.remote_created(
source=Identity.by_actor_uri_with_create(self.message["actor"]),
target=Identity.by_actor_uri(self.message["object"]),
uri=self.message["id"],
)
@sync_to_async
def follow_accepted(self):
"""
Handles an incoming acceptance of one of our follow requests
"""
target = Identity.by_actor_uri_with_create(self.message["actor"])
source = Identity.by_actor_uri(self.message["object"]["actor"])
if source is None:
raise ValueError(
f"Follow-Accept has invalid source {self.message['object']['actor']}"
)
Follow.remote_accepted(source=source, target=target)
@property
def message_type(self):
return self.message["type"].lower()
async def follow_undo(self):
"""
Handles an incoming follow undo
"""