Implement outbox: accept Create activities.

Post /@<username>/outbox

For now delivery is not implemented.
master
Romain Gauthier 2017-07-19 16:43:53 +02:00
rodzic 8c11541674
commit fd8b0d20ca
6 zmienionych plików z 125 dodań i 0 usunięć

Wyświetl plik

@ -1,2 +1,3 @@
from .objects import *
from .verbs import *

Wyświetl plik

@ -57,6 +57,11 @@ class Object(object):
def to_activitystream(self):
return self
class Note(Object):
attributes = Object.attributes + ["content", "actor"]
type = "Note"
class Actor(Object):
attributes = Object.attributes + [
@ -87,6 +92,7 @@ ALLOWED_TYPES = {
"Object": Object,
"Actor": Actor,
"Person": Person,
"Note": Note,
}
def as_activitystream(obj):

Wyświetl plik

@ -0,0 +1,57 @@
from activitypub.activities.objects import ALLOWED_TYPES, Object, Actor
from activitypub.activities.objects import Person, Note
from activitypub.activities import errors
from copy import copy
class Activity(Object):
attributes = Object.attributes + ["actor", "object"]
type = "Activity"
def get_audience(self):
audience = []
for attr in ["to", "bto", "cc", "bcc", "audience"]:
value = getattr(self, attr, None)
if not value:
continue
if isinstance(value, str):
value = [value]
audience += value
return set(audience)
def strip_audience(self):
new = copy(self)
if getattr(new, "bto", None):
delattr(new, "bto")
if getattr(new, "bcc", None):
delattr(new, "bcc")
return new
def validate(self):
pass
class Create(Activity):
type = "Create"
def validate(self):
msg = None
if not getattr(self, "actor", None):
msg = "Invalid Create activity, actor is missing"
elif not getattr(self, "object", None):
msg = "Invalid Create activity, object is missing"
elif not isinstance(self.actor, Actor) and not isinstance(self.actor, str):
msg = "Invalid actor type, must be an Actor or a string"
elif not isinstance(self.object, Object):
msg = "Invalid object type, must be an Object"
if msg:
raise errors.ASValidateException(msg)
ALLOWED_TYPES.update({
"Activity": Activity,
"Create": Create,
})

Wyświetl plik

@ -54,6 +54,29 @@ class Person(Model):
})
return json
class Note(Model):
ap_id = TextField(null=True)
remote = BooleanField(default=False)
person = ForeignKey(Person, related_name='notes')
content = CharField(max_length=500)
likes = ManyToManyField(Person, related_name='liked')
@property
def uris(self):
if self.remote:
ap_id = self.ap_id
else:
ap_id = uri("note", self.person.username, self.id)
return URIs(id=ap_id)
def to_activitystream(self):
return {
"id": self.uris.id,
"content": self.content,
"actor": self.person.uris.id,
}
@receiver(post_save, sender=Person)
@receiver(post_save, sender=Note)
def save_ap_id(sender, instance, created, **kwargs):

Wyświetl plik

@ -5,6 +5,7 @@ from activitypub.views import person, note, new_note, notes, inbox, outbox
from activitypub.views import followers, noop
urlpatterns = [
url(r'^@(\w+)/outbox', outbox, name="outbox"),
url(r'^@([^/]+)$', person, name="person"),
url(r'^admin/', admin.site.urls),
]

Wyświetl plik

@ -15,3 +15,40 @@ def person(request, username):
person = get_object_or_404(Person, username=username)
return JsonResponse(activities.Person(person).to_json(context=True))
@csrf_exempt
def outbox(request, username):
if request.method != "POST":
return HttpResponseNotAllowed(["POST"])
payload = request.body.decode("utf-8")
activity = json.loads(payload, object_hook=as_activitystream)
person = get_object_or_404(Person, username=username)
if activity.type == "Note":
obj = activity
activity = activities.Create(
to=person.uris.followers,
actor=person.uris.id,
object=obj
)
activity.validate()
if activity.type == "Create":
if activity.object.type != "Note":
raise Exception("Sorry, you can only create Notes objects")
content = activity.object.content
note = Note(content=content, person=person)
note.save()
# TODO: check for actor being the right actor object
activity.object.id = note.uris.id
deliver(activity)
return HttpResponseRedirect(note.uris.id)
raise Exception("Invalid Request")
def deliver(activity):
pass