예제 #1
0
def handle_create(activity):

    PUBLIC_CONTEXT = "https://www.w3.org/ns/activitystreams#Public"

    # Confirm that the status has images on it. At least one

    attachments = activity.object.attachment
    valid_atachments = []
    for attachment in attachments:
        if 'image' in attachment.type:
            valid_atachments.append(attachment)

    # If not end the job
    if not len(valid_atachments):
        return

    # Check who is the actor
    ap_id = ""
    if isinstance(activity.actor, activities.Actor):
        ap_id = activity.actor.id
    elif isinstance(activity.actor, str):
        ap_id = activity.actor

    # Get the profile of the creator
    actor = ActivityPubId(ap_id).get_or_create_remote_user()

    # Get the targets of the status
    targets = [
        x for x in activity.get("to", []) + activity.get("cc", []) +
        activity.get('bcc', [])
    ]

    is_public = PUBLIC_CONTEXT in targets

    followers_of = [
        ActivityPubId(x.replace('/followers', '')).get_or_create_remote_user()
        for x in targets
    ]
    directs = [
        x.split('/')[-1] for x in targets if urlparse(x).hostname == DOMAIN
    ]

    # Data for the new status
    note = activity.object
    data = {
        "caption": note.content,
        "visibility": is_public,
        "user": actor,
        "sensitive": False,
        "remote": True,
        "story": False,
        "ap_id": note.id,
        "identifier": note.id.split('/')[-1]
    }

    try:
        status = Status.create(**data)
        spread_status(status, [], False)
    except:
        return
예제 #2
0
async def handle_follow(activity: Activity) -> bool:

    # Find if the target user is in our system
    followed = UserProfile.get_or_none(ap_id=activity.object)

    if followed:
        logging.debug(f"Starting follow process for {followed.username}")
        # Get the ap_id for the actor
        ap_id = ""
        if isinstance(activity.actor, activities.Actor):
            ap_id = activity.actor.id
        elif isinstance(activity.actor, str):
            ap_id = activity.actor

        # A representation of the remote user
        follower = ActivityPubId(ap_id).get_or_create_remote_user()
        logging.debug(f"New follower: {follower}")
        # Handle if the user must manually approve request
        if followed.is_private:
            FollowRequest.create(account=follower, target=followed)
        else:

            # Handle local things
            #follower.follow(followed)
            #NotificationManager(follower).create_follow_notification(followed)
            message = {
                "@context":
                "https://www.w3.org/ns/activitystreams",
                "type":
                "Accept",
                "to":
                follower.uris.inbox,
                "actor":
                followed.ap_id,

                # this is wrong per litepub, but mastodon < 2.4 is not compliant with that profile.
                "object": {
                    "type": "Follow",
                    "id": activity.id,
                    "object": activity.object,
                    "actor": follower.ap_id
                },
                "id":
                "https://{}/activities/{}".format('anfora.test', uuid.uuid4()),
            }

            response = await push_to_remote_actor(follower, message)

            return response
    else:
        logging.error(f"User not found: {activity.object}")
        return False
예제 #3
0
    def verify(self):

        account = ActivityPubId(self.signature_params['keyId']).uri_to_resource(User)

        if not account:
            self.signature_fail_reason = "Could not retrive account using keyId"
            return 
            
        if self.verify_public_key(account.public_key):
            self.signed_request_account = account
            return True
        else:
            return False
예제 #4
0
def handle_follow(activity, original=None):
    # Find if the target user is in our system
    followed = UserProfile.get_or_none(ap_id=activity.object)

    if followed:
        logging.debug(f"Starting follow process for {followed.username}")
        # Get the ap_id for the actor
        ap_id = ""
        if isinstance(activity.actor, activities.Actor):
            ap_id = activity.actor.id
        elif isinstance(activity.actor, str):
            ap_id = activity.actor

        # A representation of the remote user
        follower = ActivityPubId(ap_id).get_or_create_remote_user()
        logging.debug(f"New follower: {follower}")
        # Handle if the user must manually approve request
        if followed.is_private:
            FollowRequest.create(account=follower, target=followed)
        else:

            # Handle local things
            follower.follow(followed)
            NotificationManager(follower).create_follow_notification(followed)
            message = {
                "@context": "https://www.w3.org/ns/activitystreams",
                "type": "Accept",
                "to": follower.ap_id,
                "actor": followed.ap_id,
                "object": original,
                "id": followed.ap_id + '#accepts/follows/' + str(uuid.uuid4()),
            }

            response = push_to_remote_actor(follower, message)

            return response
    else:
        logging.error(f"User not found: {activity.object}")
        return False
예제 #5
0
    def on_post(self, req, resp, username):
        user = User.get_or_none(username == username)

        if req.context['user'].username != username:
            resp.status = falcon.HTTP_401
            resp.body = json.dumps({"Error": "Access denied"})

        payload = req.get_param('data')
        activity = json.loads(payload, object_hook=as_activitystream)

        if activity.object.type == "Note":
            obj = activity.object
            activity = activities.Create(to=user.uris.followers,
                                         actor=user.uris.id,
                                         object=obj)

        activity.validate()

        if activity.type == "Create":
            if activity.object.type != "Note":
                resp.status = falcon.HTTP_500
                resp.body = json.dumps({"Error": "You only can create notes"})

                activity.object.id = photo.uris.id
                activity.id = store(activity, user)
                #deliver(activity)
                resp.body = json.dumps({"Success": "Delivered successfully"})
                resp.status = falcon.HTTP_200

                #Convert to jpeg

            else:
                resp.status = falcon.HTTP_500
                resp.body = json.dumps({"Error": "No photo attached"})

        if activity.type == "Follow":

            followed = ActivityPubId(
                activity.object).get_or_create_remote_user()
            user = req.context["user"]
            #print(followed.ap_id, user.username, followed.username)
            f = FollowerRelation.create(user=user, follows=followed)

            activity.actor = user.uris.id
            activity.to = followed.uris.id
            #activity.id = store(activity, user)
            deliver(activity)

            resp.body = json.dumps({"Success": "Delivered successfully"})
            resp.status = falcon.HTTP_200
예제 #6
0
    def on_post(self, req, resp):
        user = req.context['user']

        #FIX: Check if it's uri
        obj_id = get_ap_by_uri(req.params['uri'])
        #print(obj_id)
        follow_object = activities.Follow(actor=user.uris.id, object=obj_id)

        #Follow object that needs to be send
        signed_object = LinkedDataSignature(
            follow_object.to_json(context=True))

        #Prepare the object that will be send as response
        following = ActivityPubId(obj_id).get_or_create_remote_user()

        #Sign the activity object
        signed_object.sign(user)

        #Create the task to send the petition
        send_activity(signed_object.json, user, obj_id)

        resp.body = json.dumps(following.to_json(), default=str)
        #resp.body = json.dumps(signed_object.json, default=str)
        resp.status = falcon.HTTP_200
예제 #7
0
def handle_note(activity):
    if isinstance(activity.actor, activities.Actor):
        ap_id = activity.actor.id
    elif isinstance(activity.actor, str):
        ap_id = activity.actor

    person = ActivityPubId(ap_id).get_or_create_remote_user()

    note = Status.get_or_none(ap_id=activity.object.id)

    if not note:
        Status.create(content=activity.object.content,
                      person=person,
                      ap_id=activity.object.id,
                      remote=True)
예제 #8
0
def handle_follow(activity):
    followed = User.get_or_none(ap_id=activity.object)
    print("=> Handling follow")
    if followed:
        if isinstance(activity.actor, activities.Actor):
            ap_id = activity.actor.id
        elif isinstance(activity.actor, str):
            ap_id = activity.actor

        follower = ActivityPubId(ap_id).get_or_create_remote_user()
        FollowerRelation.create(
            user = follower,
            follows = followed
        )

        response = {'Type': 'Accept','Object':activity}


    else:
        print("error handling follow")
예제 #9
0
def spread_status(status: Status,
                  mentions: List[str],
                  ap_spread: bool = False) -> None:
    """
    Given a status we have to: 

    - Populate users timelines
    - Notifiy mentions
    - Send via AP TODO

    if we are spreading to local follers (for example from a external
    request) the ap_spread should be false but if the local user is the
    one creating the spread tag we should use ap_spread = true

    """
    r = redis.StrictRedis(host=os.environ.get('REDIS_HOST', 'localhost'))
    time = status.created_at.timestamp()

    #Populate all the followers timelines
    json_file = json.dumps(status.to_json(), default=str)
    for follower in status.user.followers():
        TimelineManager(follower).push_home(status)
        # Add it to notifications
        r.publish(f'timeline:{follower.id}', f'update {json_file}')

    #Add id to the own timeline
    TimelineManager(status.user).push_home(status)
    r.publish(f'timeline:{status.user.id}', f'update {json_file}')

    # Notify mentioned people

    remote = []

    # Notify mentions
    for user in mentions:
        # We have two formats: local user and remote user
        # if the user is local the user string is of the from
        # 'username' otherwise is '*****@*****.**'

        #target_user = User.get_or_none(User.username==user)
        target_user = ActivityPubId(user).get_or_create_remote_user()
        if target_user:
            # First we check that there is an user with this username
            # and then we get it's profile
            if not target_user.is_remote:
                NotificationManager(
                    status.user).create_mention_notification(target_user)
            else:
                remote.append(target_user)

    # Spread via AP
    if ap_spread:
        create = generate_create_note(status, remote)

        # Can this be the coolest var in the project?
        # it's just a set of addresses. Here I'm taking care
        # to minimize the number of requests to external actors
        # targeting shared inboxes (like a pro, look at me mom)

        targets_spotted = set()

        remote_followers = (UserProfile.select().join(
            FollowerRelation,
            on=FollowerRelation.user).where((UserProfile.is_remote == True) & (
                FollowerRelation.follows == status.user)))

        for follower in remote_followers:
            if follower.public_inbox:
                targets_spotted.add(follower.public_inbox)
            else:
                targets_spotted.add(follower.uris.inbox)

        # We're ready mike, drop cargo to the target
        print(targets_spotted)
        for inbox in targets_spotted:
            push_to_remote_actor(inbox, create.to_json())