[WIP] More progress

This commit is contained in:
Thomas Sileo 2018-06-09 10:15:52 +02:00
parent 2abf0fffd2
commit f0880f0119
2 changed files with 91 additions and 14 deletions

41
little_boxes/README.md Normal file
View file

@ -0,0 +1,41 @@
# Little Boxes
Tiny ActivityPub framework.
## Getting Started
```python
from little_boxes.activitypub import BaseBackend
from little_boxes.activitypub import use_backend
from little_boxes.activitypub import Outbox
from little_boxes.activitypub import Person
from little_boxes.activitypub import Follow
from mydb import db_client
class MyBackend(BaseBackend):
def __init__(self, db_connection):
self.db_connection = db_connection
def inbox_new(self, as_actor, activity):
# Save activity as "as_actor"
# [...]
def post_to_remote_inbox(self, as_actor, payload, recipient):
# Send the activity to the remote actor
# [...]
db_con = db_client()
my_backend = MyBackend(db_con)
use_backend(my_backend)
me = Person({}) # Init an actor
outbox = Outbox(me)
follow = Follow(actor=me, object='http://iri-i-want-follow')
outbox.post(follow)
```

View file

@ -41,6 +41,12 @@ COLLECTION_CTX = [
# Will be used to keep track of all the defined activities # Will be used to keep track of all the defined activities
_ACTIVITY_CLS: Dict['ActivityTypeEnum', Type['_BaseActivity']] = {} _ACTIVITY_CLS: Dict['ActivityTypeEnum', Type['_BaseActivity']] = {}
BACKEND = None
def use_backend(backend_instance):
global BACKEND
BACKEND = backend_instance
class ActivityType(Enum): class ActivityType(Enum):
"""Supported activity `type`.""" """Supported activity `type`."""
@ -109,14 +115,16 @@ def _get_actor_id(actor: ObjectOrIDType) -> str:
return actor return actor
class Actions(object): class BaseBackend(object):
def outbox_is_blocked(self, actor_id: str) -> bool:
return False
def inbox_get_by_iri(self, iri: str) -> 'BaseActivity': def outbox_is_blocked(self, as_actor: 'Person', actor_id: str) -> bool:
"""Returns True if `as_actor` has blocked `actor_id`."""
pass pass
def inbox_new(self, activity: 'BaseActivity') -> None: def inbox_get_by_iri(self, as_actor: 'Person', iri: str) -> 'BaseActivity':
pass
def inbox_new(self, as_actor: 'Person', activity: 'BaseActivity') -> None:
pass pass
def activity_url(self, obj_id: str) -> str: def activity_url(self, obj_id: str) -> str:
@ -404,31 +412,29 @@ class BaseActivity(object, metaclass=_ActivityMeta):
def _undo_inbox(self) -> None: def _undo_inbox(self) -> None:
raise NotImplementedError raise NotImplementedError
def process_from_inbox(self) -> None: def process_from_inbox(self, as_actor: 'Person') -> None:
"""Process the message posted to `as_actor` inbox."""
logger.debug(f'calling main process from inbox hook for {self}') logger.debug(f'calling main process from inbox hook for {self}')
actor = self.get_actor() actor = self.get_actor()
# Check for Block activity # Check for Block activity
# ABC if BACKEND.outbox_is_blocked(as_actor, actor.id):
if self.outbox_is_blocked(actor.id):
# TODO(tsileo): raise ActorBlockedError? # TODO(tsileo): raise ActorBlockedError?
logger.info(f'actor {actor!r} is blocked, dropping the received activity {self!r}') logger.info(f'actor {actor!r} is blocked, dropping the received activity {self!r}')
return return
# ABC if BACKEND.inbox_get_by_iri(as_actor, self.id):
if self.inbox_get_by_iri(self.id):
# The activity is already in the inbox # The activity is already in the inbox
logger.info(f'received duplicate activity {self}, dropping it') logger.info(f'received duplicate activity {self}, dropping it')
return return
try: try:
self._pre_process_from_inbox() self._pre_process_from_inbox(as_actor)
logger.debug('called pre process from inbox hook') logger.debug('called pre process from inbox hook')
except NotImplementedError: except NotImplementedError:
logger.debug('pre process from inbox hook not implemented') logger.debug('pre process from inbox hook not implemented')
# ABC BACKEND.inbox_new(as_actor, self)
self.inbox_new(self)
logger.info('activity {self!r} saved') logger.info('activity {self!r} saved')
try: try:
@ -707,7 +713,10 @@ class Like(BaseActivity):
self.outbox_undo_like(self) self.outbox_undo_like(self)
def build_undo(self) -> BaseActivity: def build_undo(self) -> BaseActivity:
return Undo(object=self.to_dict(embed=True, embed_object_id_only=True)) return Undo(
object=self.to_dict(embed=True, embed_object_id_only=True),
actor=self.get_actor().id,
)
class Announce(BaseActivity): class Announce(BaseActivity):
@ -932,3 +941,30 @@ class Note(BaseActivity):
deleted=deleted, deleted=deleted,
updated=deleted, updated=deleted,
) )
class Box(object):
def __init__(self, actor: Person):
self.actor = actor
class Outbox(Box):
def post(self, activity: BaseActivity) -> None:
if activity.get_actor().id != self.actor.id:
raise ValueError(f'{activity.get_actor()!r} cannot post into {self.actor!r} outbox')
activity.post_to_outbox()
def get(self, activity_iri: str) -> BaseActivity:
pass
def collection(self):
# TODO(tsileo): figure out an API
class Inbox(Box):
def post(self, activity: BaseActivity) -> None:
activity.process_from_inbox(self.actor)