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
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
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
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
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
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
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)
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")
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())