kopia lustrzana https://gitlab.com/jaywink/federation
186 wiersze
5.8 KiB
Python
186 wiersze
5.8 KiB
Python
from typing import Dict, Tuple
|
|
from mimetypes import guess_type
|
|
|
|
from dirty_validators.basic import Email
|
|
|
|
from federation.entities.activitypub.enums import ActivityType
|
|
from federation.entities.mixins import (
|
|
PublicMixin, TargetIDMixin, ParticipationMixin, CreatedAtMixin, RawContentMixin, OptionalRawContentMixin,
|
|
EntityTypeMixin, ProviderDisplayNameMixin, RootTargetIDMixin, BaseEntity)
|
|
from federation.utils.network import fetch_content_type
|
|
|
|
|
|
class Accept(CreatedAtMixin, TargetIDMixin, BaseEntity):
|
|
"""An acceptance message for some target."""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
# ID not required for accept
|
|
self._required.remove('id')
|
|
|
|
|
|
class Image(OptionalRawContentMixin, CreatedAtMixin, BaseEntity):
|
|
"""Reflects a single image, possibly linked to another object."""
|
|
url: str = ""
|
|
name: str = ""
|
|
height: int = 0
|
|
width: int = 0
|
|
inline: bool = False
|
|
media_type: str = ""
|
|
|
|
_default_activity = ActivityType.CREATE
|
|
_valid_media_types: Tuple[str] = (
|
|
"image/jpeg",
|
|
"image/png",
|
|
"image/gif",
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self._required += ["url"]
|
|
self._required.remove("id")
|
|
self._required.remove("actor_id")
|
|
if self.url and not self.media_type:
|
|
self.media_type = self.get_media_type()
|
|
|
|
def get_media_type(self) -> str:
|
|
media_type = guess_type(self.url)[0] or fetch_content_type(self.url)
|
|
if media_type in self._valid_media_types:
|
|
return media_type
|
|
return ""
|
|
|
|
|
|
class Audio(OptionalRawContentMixin, CreatedAtMixin, BaseEntity):
|
|
pass
|
|
|
|
class Video(OptionalRawContentMixin, CreatedAtMixin, BaseEntity):
|
|
pass
|
|
|
|
class Comment(RawContentMixin, ParticipationMixin, CreatedAtMixin, RootTargetIDMixin, BaseEntity):
|
|
"""Represents a comment, linked to another object."""
|
|
participation = "comment"
|
|
url = ""
|
|
|
|
_allowed_children = (Image,)
|
|
_default_activity = ActivityType.CREATE
|
|
|
|
|
|
class Follow(CreatedAtMixin, TargetIDMixin, BaseEntity):
|
|
"""Represents a handle following or unfollowing another handle."""
|
|
following = True
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self._required += ["following"]
|
|
# ID not required for follow
|
|
self._required.remove('id')
|
|
|
|
|
|
class Post(RawContentMixin, PublicMixin, CreatedAtMixin, ProviderDisplayNameMixin, BaseEntity):
|
|
"""Reflects a post, status message, etc, which will be composed from the message or to the message."""
|
|
location = ""
|
|
url = ""
|
|
|
|
_allowed_children = (Image,)
|
|
_default_activity = ActivityType.CREATE
|
|
|
|
|
|
class Reaction(ParticipationMixin, CreatedAtMixin, BaseEntity):
|
|
"""Represents a reaction to another object, for example a like."""
|
|
participation = "reaction"
|
|
reaction = ""
|
|
|
|
_default_activity = ActivityType.CREATE
|
|
_reaction_valid_values = ["like"]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self._required += ["reaction"]
|
|
|
|
def validate_reaction(self):
|
|
"""Ensure reaction is of a certain type.
|
|
|
|
Mainly for future expansion.
|
|
"""
|
|
if self.reaction not in self._reaction_valid_values:
|
|
raise ValueError("reaction should be one of: {valid}".format(
|
|
valid=", ".join(self._reaction_valid_values)
|
|
))
|
|
|
|
|
|
class Relationship(CreatedAtMixin, TargetIDMixin, BaseEntity):
|
|
"""Represents a relationship between two handles."""
|
|
relationship = ""
|
|
|
|
_relationship_valid_values = ["sharing", "following", "ignoring", "blocking"]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self._required += ["relationship"]
|
|
|
|
def validate_relationship(self):
|
|
"""Ensure relationship is of a certain type."""
|
|
if self.relationship not in self._relationship_valid_values:
|
|
raise ValueError("relationship should be one of: {valid}".format(
|
|
valid=", ".join(self._relationship_valid_values)
|
|
))
|
|
|
|
|
|
class Profile(CreatedAtMixin, OptionalRawContentMixin, PublicMixin, BaseEntity):
|
|
"""Represents a profile for a user."""
|
|
atom_url = ""
|
|
email = ""
|
|
gender = ""
|
|
image_urls = None
|
|
location = ""
|
|
name = ""
|
|
nsfw = False
|
|
public_key = ""
|
|
tag_list = None
|
|
url = ""
|
|
username = ""
|
|
inboxes: Dict = None
|
|
|
|
_allowed_children = (Image,)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.image_urls = {
|
|
"small": "", "medium": "", "large": ""
|
|
}
|
|
self.inboxes = {
|
|
"private": None,
|
|
"public": None,
|
|
}
|
|
self.tag_list = []
|
|
super().__init__(*args, **kwargs)
|
|
# As an exception, a Profile does not require to have an `actor_id`
|
|
self._required.remove('actor_id')
|
|
|
|
def validate_email(self):
|
|
if self.email:
|
|
validator = Email()
|
|
if not validator.is_valid(self.email):
|
|
raise ValueError("Email is not valid")
|
|
|
|
|
|
class Retraction(CreatedAtMixin, TargetIDMixin, EntityTypeMixin, BaseEntity):
|
|
"""Represents a retraction of content by author."""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
# ID not required for retraction
|
|
self._required.remove('id')
|
|
|
|
|
|
class Share(CreatedAtMixin, TargetIDMixin, EntityTypeMixin, OptionalRawContentMixin, PublicMixin,
|
|
ProviderDisplayNameMixin, BaseEntity):
|
|
"""Represents a share of another entity.
|
|
|
|
``entity_type`` defaults to "Post" but can be any base entity class name. It should be the class name of the
|
|
entity that was shared.
|
|
|
|
The optional ``raw_content`` can be used for a "quoted share" case where the sharer adds their own note to the
|
|
share.
|
|
"""
|
|
entity_type = "Post"
|