mirror of
https://git.sr.ht/~tsileo/microblog.pub
synced 2024-12-22 21:24:28 +00:00
[WIP] More progress
This commit is contained in:
parent
2abf0fffd2
commit
f0880f0119
2 changed files with 91 additions and 14 deletions
41
little_boxes/README.md
Normal file
41
little_boxes/README.md
Normal 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)
|
||||||
|
```
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue