add web
This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
# friends.roblox.com
|
||||
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify, abort, after_this_request
|
||||
from app.util import auth, websiteFeatures, friends
|
||||
from app.models.user import User
|
||||
from app.models.friend_relationship import FriendRelationship
|
||||
from app.models.friend_request import FriendRequest
|
||||
from app.models.follow_relationship import FollowRelationship
|
||||
from app.extensions import limiter, db, redis_controller, get_remote_address, csrf, user_limiter
|
||||
from flask_wtf.csrf import CSRFError, generate_csrf
|
||||
from sqlalchemy import or_
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from app.services.user_relationships import followings
|
||||
|
||||
FriendsAPIRoute = Blueprint('friendsapi', __name__, url_prefix="/")
|
||||
csrf.exempt(FriendsAPIRoute)
|
||||
@FriendsAPIRoute.errorhandler(CSRFError)
|
||||
def handle_csrf_error(e):
|
||||
ErrorResponse = make_response(jsonify({
|
||||
"errors": [
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Token Validation Failed"
|
||||
}
|
||||
]
|
||||
}))
|
||||
|
||||
ErrorResponse.status_code = 403
|
||||
ErrorResponse.headers["x-csrf-token"] = generate_csrf()
|
||||
return ErrorResponse
|
||||
|
||||
@FriendsAPIRoute.errorhandler(429)
|
||||
def handle_ratelimit_reached(e):
|
||||
return jsonify({
|
||||
"errors": [
|
||||
{
|
||||
"code": 9,
|
||||
"message": "The flood limit has been exceeded."
|
||||
}
|
||||
]
|
||||
}), 429
|
||||
|
||||
@FriendsAPIRoute.before_request
|
||||
def before_request():
|
||||
if "Roblox/" not in request.user_agent.string:
|
||||
csrf.protect()
|
||||
|
||||
@FriendsAPIRoute.route('/v1/my/friends/count', methods=['GET'])
|
||||
@limiter.limit("60/minute")
|
||||
@auth.authenticated_required_api
|
||||
def GetFriendCount():
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
return jsonify( { "count": friends.GetFriendCount( AuthenticatedUser.id ) } ), 200
|
||||
|
||||
@FriendsAPIRoute.route('/v1/users/<int:userId>/friends/count', methods=['GET'])
|
||||
@limiter.limit("60/minute")
|
||||
def GetFriendCountByUserId( userId : int ):
|
||||
UserObj : User = User.query.filter_by( id = userId ).first()
|
||||
if UserObj is None:
|
||||
return jsonify( { "errors": [ { "code": 1, "message": "User not found" } ] } ), 404
|
||||
return jsonify( { "count": friends.GetFriendCount( userId ) } ), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/user/friend-requests/count", methods=["GET"])
|
||||
@limiter.limit("60/minute")
|
||||
@auth.authenticated_required_api
|
||||
def GetMyFriendRequestsCount():
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
FriendRequestCount : int = max(FriendRequest.query.filter_by( requestee_id = AuthenticatedUser.id ).count(), 500)
|
||||
return jsonify( { "count": FriendRequestCount } ), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/users/<int:userid>/friends", methods=["GET"])
|
||||
@limiter.limit("60/minute")
|
||||
def get_user_friends( userid : int ):
|
||||
UserObj : User = User.query.filter_by(id=userid).first()
|
||||
if UserObj is None:
|
||||
return jsonify({"success": False, "message": "Invalid request"}),400
|
||||
|
||||
FriendRelationshipList : list[FriendRelationship] = FriendRelationship.query.filter(or_(FriendRelationship.user_id == userid, FriendRelationship.friend_id == userid)).all()
|
||||
FriendList = []
|
||||
for FriendRelationshipObj in FriendRelationshipList:
|
||||
FriendObj : User = User.query.filter_by(id=FriendRelationshipObj.user_id if FriendRelationshipObj.user_id != userid else FriendRelationshipObj.friend_id).first()
|
||||
if FriendObj is None:
|
||||
continue
|
||||
FriendList.append({
|
||||
"isOnline": FriendObj.lastonline > datetime.utcnow() - timedelta(minutes=1),
|
||||
"isDeleted": False,
|
||||
"friendFrequentScore": 0,
|
||||
"friendFrequentRank": 1,
|
||||
"hasVerifiedBadge": False,
|
||||
"description": FriendObj.description,
|
||||
"created": FriendObj.created.isoformat(),
|
||||
"isBanned": False,
|
||||
"externalAppDisplayName": None,
|
||||
"id": FriendObj.id,
|
||||
"name": FriendObj.username,
|
||||
"displayName": FriendObj.username
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"data": FriendList
|
||||
})
|
||||
|
||||
@FriendsAPIRoute.route("/v1/user/friend-requests/decline-all", methods=["POST"])
|
||||
@limiter.limit("60/minute")
|
||||
@auth.authenticated_required_api
|
||||
def decline_all_friend_requests():
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
FriendRequestList : list[FriendRequest] = FriendRequest.query.filter_by( requestee_id = AuthenticatedUser.id ).all()
|
||||
for FriendRequestObj in FriendRequestList:
|
||||
db.session.delete( FriendRequestObj )
|
||||
db.session.commit()
|
||||
return jsonify({}), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/users/<int:userId>/unfriend", methods=["POST"])
|
||||
@limiter.limit("60/minute")
|
||||
@auth.authenticated_required_api
|
||||
@user_limiter.limit("60/minute")
|
||||
def unfriend_user( userId : int ):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
UserObj : User = User.query.filter_by(id=userId).first()
|
||||
if UserObj is None:
|
||||
return jsonify({ "errors": [{ "code": 1, "message": "The target user is invalid or does not exist"}] }), 400
|
||||
FriendRelationshipObj : FriendRelationship = friends.GetFriendRelationship( AuthenticatedUser.id, UserObj.id )
|
||||
if FriendRelationshipObj is not None:
|
||||
db.session.delete( FriendRelationshipObj )
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({}), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/users/<int:userId>/request-friendship", methods=["POST"])
|
||||
@limiter.limit("15/minute")
|
||||
@auth.authenticated_required_api
|
||||
@user_limiter.limit("15/minute")
|
||||
def request_friendship( userId : int ):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
UserObj : User = User.query.filter_by(id=userId).first()
|
||||
if UserObj is None:
|
||||
return jsonify({ "errors": [{ "code": 1, "message": "The target user is invalid or does not exist"}] }), 400
|
||||
if userId == AuthenticatedUser.id:
|
||||
return jsonify({ "errors": [{ "code": 7, "message": "The user cannot be friends with itself."}] }), 400
|
||||
if friends.GetFriendCount( AuthenticatedUser.id ) >= 200:
|
||||
return jsonify({ "errors": [{ "code": 31, "message": "User with max friends sent friend request."}] }), 400
|
||||
if friends.IsFriends( AuthenticatedUser.id, UserObj.id ):
|
||||
return jsonify({ "errors": [{ "code": 5, "message": "The target user is already a friend."}] }), 400
|
||||
|
||||
OtherFriendRequestObj : FriendRequest = FriendRequest.query.filter_by( requestee_id = AuthenticatedUser.id, requester_id = UserObj.id ).first()
|
||||
if OtherFriendRequestObj is not None:
|
||||
if friends.GetFriendCount( UserObj.id ) >= 200:
|
||||
return jsonify({ "errors": [{ "code": 12, "message": "The target users friends limit has been exceeded."}] }), 400
|
||||
|
||||
db.session.delete( OtherFriendRequestObj )
|
||||
NewFriendRelationshipObj : FriendRelationship = FriendRelationship( user_id = AuthenticatedUser.id, friend_id = UserObj.id )
|
||||
db.session.add( NewFriendRelationshipObj )
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({ "success": True }), 200
|
||||
|
||||
FriendRequestObj : FriendRequest = FriendRequest.query.filter_by( requestee_id = UserObj.id, requester_id = AuthenticatedUser.id ).first()
|
||||
if FriendRequestObj is None:
|
||||
FriendRequestObj = FriendRequest( requestee_id = UserObj.id, requester_id = AuthenticatedUser.id )
|
||||
db.session.add( FriendRequestObj )
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({ "success": True }), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/users/<int:userId>/accept-friend-request", methods=["POST"])
|
||||
@limiter.limit("30/minute")
|
||||
@auth.authenticated_required_api
|
||||
@user_limiter.limit("30/minute")
|
||||
def accept_friend_request( userId : int ):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
UserObj : User = User.query.filter_by(id=userId).first()
|
||||
if UserObj is None:
|
||||
return jsonify({ "errors": [{ "code": 1, "message": "The target user is invalid or does not exist"}] }), 400
|
||||
|
||||
FriendRequestObj : FriendRequest = FriendRequest.query.filter_by( requestee_id = AuthenticatedUser.id, requester_id = UserObj.id ).first()
|
||||
if FriendRequestObj is None:
|
||||
return jsonify({ "errors": [{ "code": 10, "message": "The friend request does not exist."}] }), 400
|
||||
|
||||
if friends.GetFriendCount( AuthenticatedUser.id ) >= 200:
|
||||
return jsonify({ "errors": [{ "code": 11, "message": "The current users friends limit has been exceeded."}] }), 400
|
||||
if friends.GetFriendCount( UserObj.id ) >= 200:
|
||||
return jsonify({ "errors": [{ "code": 12, "message": "The target users friends limit has been exceeded."}] }), 400
|
||||
|
||||
db.session.delete( FriendRequestObj )
|
||||
NewFriendRelationshipObj : FriendRelationship = FriendRelationship( user_id = AuthenticatedUser.id, friend_id = UserObj.id )
|
||||
db.session.add( NewFriendRelationshipObj )
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({}), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/users/<int:userId>/decline-friend-request", methods=["POST"])
|
||||
@limiter.limit("30/minute")
|
||||
@auth.authenticated_required_api
|
||||
@user_limiter.limit("30/minute")
|
||||
def decline_friend_request( userId : int ):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
UserObj : User = User.query.filter_by(id=userId).first()
|
||||
if UserObj is None:
|
||||
return jsonify({ "errors": [{ "code": 1, "message": "The target user is invalid or does not exist"}] }), 400
|
||||
|
||||
FriendRequestObj : FriendRequest = FriendRequest.query.filter_by( requestee_id = AuthenticatedUser.id, requester_id = UserObj.id ).first()
|
||||
if FriendRequestObj is None:
|
||||
return jsonify({ "errors": [{ "code": 10, "message": "The friend request does not exist."}] }), 400
|
||||
|
||||
db.session.delete( FriendRequestObj )
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({}), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/users/<int:userId>/followers/count", methods=["GET"])
|
||||
@limiter.limit("60/minute")
|
||||
def get_user_followers_count( userId : int ):
|
||||
UserObj : User = User.query.filter_by(id=userId).first()
|
||||
if UserObj is None:
|
||||
return jsonify({"success": False, "message": "Invalid request"}),400
|
||||
|
||||
return jsonify( { "count": followings.get_follower_count( requested_user = UserObj ) } ), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/users/<int:userId>/followings/count", methods=["GET"])
|
||||
@limiter.limit("60/minute")
|
||||
def get_user_followings_count( userId : int ):
|
||||
UserObj : User = User.query.filter_by(id=userId).first()
|
||||
if UserObj is None:
|
||||
return jsonify({"success": False, "message": "Invalid request"}),400
|
||||
|
||||
return jsonify( { "count": followings.get_following_count( requested_user = UserObj ) } ), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/user/following-exists", methods=["POST"])
|
||||
@limiter.limit("60/minute")
|
||||
@auth.authenticated_required_api
|
||||
def following_exists():
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
if not request.is_json:
|
||||
return jsonify({ "errors": [{ "code": 0, "message": "An invalid userId was passed in."}] }), 400
|
||||
if "targetUserIds" not in request.json:
|
||||
return jsonify({ "errors": [{ "code": 0, "message": "An invalid userId was passed in."}] }), 400
|
||||
|
||||
TargetUserIds : list[int] = request.json["targetUserIds"]
|
||||
ResponseList = []
|
||||
|
||||
if len(TargetUserIds) > 100:
|
||||
return jsonify({ "errors": [{ "code": 0, "message": "An invalid userId was passed in."}] }), 400
|
||||
|
||||
for TargetUserId in TargetUserIds:
|
||||
if TargetUserId < 1:
|
||||
return jsonify({ "errors": [{ "code": 0, "message": "An invalid userId was passed in."}] }), 400
|
||||
TargetUser : User = User.query.filter_by( id = TargetUserId ).first()
|
||||
if TargetUser is None:
|
||||
return jsonify({ "errors": [{ "code": 0, "message": "An invalid userId was passed in."}] }), 400
|
||||
|
||||
ResponseList.append({
|
||||
"isFollowing": followings.is_following( follower_user = AuthenticatedUser, followed_user = TargetUser ),
|
||||
"isFollowed": followings.is_following( follower_user = TargetUser, followed_user = AuthenticatedUser ),
|
||||
"userId": TargetUserId
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"followings": ResponseList
|
||||
}), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/users/<int:userId>/unfollow", methods=["POST"])
|
||||
@limiter.limit("60/minute")
|
||||
@auth.authenticated_required_api
|
||||
@user_limiter.limit("60/minute")
|
||||
def unfollow_user( userId : int ):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
UserObj : User = User.query.filter_by(id=userId).first()
|
||||
if UserObj is None:
|
||||
return jsonify({ "errors": [{ "code": 1, "message": "The target user is invalid or does not exist"}] }), 400
|
||||
|
||||
try:
|
||||
followings.unfollow_user(
|
||||
current_follower = AuthenticatedUser,
|
||||
followed_user = UserObj
|
||||
)
|
||||
except followings.FollowingExceptions.UserNotFollowing:
|
||||
return jsonify({}), 200
|
||||
except followings.FollowingExceptions.UserRateLimited:
|
||||
return jsonify({ "errors": [{ "code": 9, "message": "The flood limit has been exceeded."}] }), 429
|
||||
except followings.FollowingExceptions.FollowingIsDisabled:
|
||||
return jsonify({ "errors": [{ "code": 0, "message": "Following is disabled."}] }), 500
|
||||
|
||||
return jsonify({}), 200
|
||||
|
||||
@FriendsAPIRoute.route("/v1/users/<int:userId>/follow", methods=["POST"])
|
||||
@limiter.limit("10/minute")
|
||||
@auth.authenticated_required_api
|
||||
@user_limiter.limit("10/minute")
|
||||
def follow_user( userId : int ):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
UserObj : User = User.query.filter_by(id=userId).first()
|
||||
if UserObj is None:
|
||||
return jsonify({ "errors": [{ "code": 1, "message": "The target user is invalid or does not exist"}] }), 400
|
||||
if userId == AuthenticatedUser.id:
|
||||
return jsonify({ "errors": [{ "code": 8, "message": "The user cannot follow itself."}] }), 400
|
||||
|
||||
try:
|
||||
followings.follow_user(
|
||||
follow_user = AuthenticatedUser,
|
||||
followed_user = UserObj
|
||||
)
|
||||
except followings.FollowingExceptions.AlreadyFollowing:
|
||||
return jsonify({ "success": True }), 200
|
||||
except followings.FollowingExceptions.UserRateLimited:
|
||||
return jsonify({ "errors": [{ "code": 9, "message": "The flood limit has been exceeded."}] }), 429
|
||||
except followings.FollowingExceptions.FollowingIsDisabled:
|
||||
return jsonify({ "errors": [{ "code": 0, "message": "Following is disabled."}] }), 500
|
||||
|
||||
return jsonify({ "success": True }), 200
|
||||
Reference in New Issue
Block a user