def follow(username_or_id): """ Follow an account. --- tags: - Accounts parameters: - name: id in: query type: integer required: true description: User ID to follow responses: 200: description: Returns Relationship schema: $ref: '#/definitions/Relationship' """ current_user = current_token.user if not current_user: abort(400) user = User.query.filter(User.name == username_or_id, User.local.is_(True)).first() if not user: try: user = User.query.filter(User.flake_id == username_or_id).first() except sqlalchemy.exc.DataError: abort(404) if not user: abort(404) actor_me = current_user.actor[0] actor_them = user.actor[0] if user.local: actor_me.follow(None, actor_them) return jsonify([to_json_relationship(current_user, user)]) else: # We need to initiate a follow request # Check if not already following rel = Follower.query.filter( Follower.actor_id == actor_me.id, Follower.target_id == actor_them.id).first() if not rel: # Initiate a Follow request from actor_me to actor_target follow = ap.Follow(actor=actor_me.url, object=actor_them.url) post_to_outbox(follow) return jsonify(""), 202 else: return jsonify({"error": "already following"}), 409
def unfollow(): user = request.args.get("user") actor_me = current_user.actor[0] if user.startswith("https://"): actor = Actor.query.filter(Actor.url == user).first() local_user = actor.user else: local_user = User.query.filter(User.name == user).first() if local_user: # Process local unfollow actor_me.unfollow(local_user.actor[0]) flash(gettext("Unfollow successful"), "success") else: # Might be a remote unfollow # 1. Webfinger the user try: remote_actor_url = get_actor_url(user, debug=current_app.debug) except InvalidURLError: current_app.logger.exception(f"Invalid webfinger URL: {user}") remote_actor_url = None except requests.exceptions.HTTPError: current_app.logger.exception(f"Invali webfinger URL: {user}") remote_actor_url = None if not remote_actor_url: flash(gettext("User not found"), "error") return redirect(url_for("bp_users.profile", name=current_user.name)) # 2. Check if we have a local user actor_target = Actor.query.filter( Actor.url == remote_actor_url).first() if not actor_target: # 2.5 Fetch and save remote actor backend = ap.get_backend() iri = backend.fetch_iri(remote_actor_url) if not iri: flash(gettext("User not found"), "error") return redirect(url_for("bp_main.home")) act = ap.parse_activity(iri) actor_target, user_target = create_remote_actor(act) db.session.add(user_target) db.session.add(actor_target) # 2.5 Get the relation of the follow follow_relation = Follower.query.filter( Follower.actor_id == actor_me.id, Follower.target_id == actor_target.id).first() if not follow_relation: flash(gettext("You don't follow this user"), "error") return redirect(url_for("bp_users.profile", name=current_user.name)) # 3. Fetch the Activity of the Follow accept_activity = Activity.query.filter( Activity.url == follow_relation.activity_url).first() if not accept_activity: current_app.logger.error( f"cannot find accept activity {follow_relation.activity_url}") flash(gettext("Whoops, something went wrong")) return redirect(url_for("bp_users.profile", name=current_user.name)) # Then the Activity ID of the Accept will be the object id activity = ap.parse_activity(payload=accept_activity.payload) # Get the final activity (the Follow one) follow_activity = Activity.query.filter( Activity.url == activity.get_object_id()).first() if not follow_activity: current_app.logger.error( f"cannot find follow activity {activity.get_object_id()}") flash(gettext("Whoops, something went wrong")) return redirect(url_for("bp_users.profile", name=current_user.name)) ap_follow_activity = ap.parse_activity(payload=follow_activity.payload) # 4. Initiate a Follow request from actor_me to actor_target unfollow = ap_follow_activity.build_undo() post_to_outbox(unfollow) flash(gettext("Unfollow request have been transmitted"), "success") return redirect(url_for("bp_users.profile", name=current_user.name))
def follow(): user = request.args.get("user") actor_me = current_user.actor[0] if user.startswith("https://"): actor = Actor.query.filter(Actor.url == user).first() if actor: local_user = actor.user else: local_user = None else: local_user = User.query.filter(User.name == user).first() if local_user: # Process local follow actor_me.follow(None, local_user.actor[0]) flash(gettext("Follow successful"), "success") else: # Might be a remote follow # 1. Webfinger the user try: remote_actor_url = get_actor_url(user, debug=current_app.debug) except InvalidURLError: current_app.logger.exception(f"Invalid webfinger URL: {user}") remote_actor_url = None # except requests.exceptions.HTTPError: # current_app.logger.exception(f"Invali webfinger URL: {user}") # remote_actor_url = None if not remote_actor_url: flash(gettext("User not found"), "error") return redirect(url_for("bp_users.profile", name=current_user.name)) # 2. Check if we have a local user actor_target = Actor.query.filter( Actor.url == remote_actor_url).first() if not actor_target: # 2.5 Fetch and save remote actor backend = ap.get_backend() iri = backend.fetch_iri(remote_actor_url) if not iri: flash(gettext("User not found"), "error") return redirect(url_for("bp_main.home")) act = ap.parse_activity(iri) actor_target, user_target = create_remote_actor(act) db.session.add(user_target) db.session.add(actor_target) # 2.7 Check if we already have a relation rel = Follower.query.filter( Follower.actor_id == actor_me.id, Follower.target_id == actor_target.id).first() if not rel: # 3. Initiate a Follow request from actor_me to actor_target follow = ap.Follow(actor=actor_me.url, object=actor_target.url) post_to_outbox(follow) flash(gettext("Follow request have been transmitted"), "success") else: flash(gettext("You already follow this user", "info")) return redirect(url_for("bp_users.profile", name=current_user.name))
def unfollow(username_or_id): """ Unfollow an account. --- tags: - Accounts parameters: - name: id in: path type: integer required: true description: User ID to follow responses: 200: description: Returns Relationship schema: $ref: '#/definitions/Relationship' """ current_user = current_token.user if not current_user: abort(400) user = User.query.filter(User.name == username_or_id, User.local.is_(True)).first() if not user: try: user = User.query.filter(User.flake_id == username_or_id).first() except sqlalchemy.exc.DataError: abort(404) if not user: abort(404) actor_me = current_user.actor[0] actor_them = user.actor[0] if user.local: actor_me.unfollow(actor_them) return jsonify([to_json_relationship(current_user, user)]) else: # Get the relation of the follow follow_relation = Follower.query.filter( Follower.actor_id == actor_me.id, Follower.target_id == actor_them.id ).first() if not follow_relation: return jsonify({"error": "follow relation not found"}), 404 # Fetch the related Activity of the Follow relation accept_activity = Activity.query.filter(Activity.url == follow_relation.activity_url).first() if not accept_activity: current_app.logger.error(f"cannot find accept activity {follow_relation.activity_url}") return jsonify({"error": "cannot found the accept activity"}), 500 # Then the Activity ID of the ACcept will be the object id activity = ap.parse_activity(payload=accept_activity.payload) # get the final activity (the Follow one) follow_activity = Activity.query.filter(Activity.url == activity.get_object_id()).first() if not follow_activity: current_app.logger.error(f"cannot find follow activity {activity.get_object_id()}") return jsonify({"error": "cannot find follow activity"}), 500 ap_follow_activity = ap.parse_activity(payload=follow_activity.payload) # initiate an Undo of the Follow request unfollow = ap_follow_activity.build_undo() post_to_outbox(unfollow) return jsonify(""), 202