syntaxwebsite/app/pages/profiles/profile.py

536 lines
23 KiB
Python

from flask import Blueprint, render_template, request, redirect, url_for, jsonify, make_response, flash, abort
from app.util import auth, friends, membership, websiteFeatures, placeinfo
from app.extensions import db, limiter, csrf, user_limiter
from app.models.user import User
from app.models.friend_request import FriendRequest
from app.models.friend_relationship import FriendRelationship
from datetime import datetime
from app.models.asset import Asset
from app.models.place import Place
from app.models.placeserver_players import PlaceServerPlayer
from app.models.follow_relationship import FollowRelationship
from app.models.user_avatar_asset import UserAvatarAsset
from app.models.userassets import UserAsset
from app.models.universe import Universe
from app.models.place_badge import PlaceBadge, UserBadge
from app.enums.AssetType import AssetType
from sqlalchemy import or_
from app.enums.MembershipType import MembershipType
from slugify import slugify
from app.routes.rate import GetAssetLikesAndDislikes, GetUserVoteStatus, GetAssetVotePercentage, GetAssetFavoriteCount, GetUserFavoriteStatus
from app.services import economy
from app.services.user_relationships import followings
Profile = Blueprint("profile", __name__, template_folder="pages")
@Profile.route("/users/<int:userid>/profile", methods=["GET"])
@auth.authenticated_required
def profile_page(userid):
user : User = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
Friends = friends.GetFriends(user.id)
FriendCount = len(Friends)
DescriptionLines = user.description.split("\n")
FriendsData = []
for friend in Friends:
friendObjData = {}
friendObjData = {
"id": friend.id,
"username": friend.username,
"isonline": True if (datetime.utcnow() - friend.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=friend.id).first() is not None else False
}
FriendsData.append(friendObjData)
FriendsData.sort(key=lambda x: x["isonline"], reverse=True)
FriendsData.sort(key=lambda x: x["ingame"], reverse=True)
FriendsData = FriendsData[:8]
AuthenticatedUser = auth.GetCurrentUser()
if AuthenticatedUser.id != user.id:
isFriends = friends.IsFriends(AuthenticatedUser.id, user.id)
FriendRequestPending = FriendRequest.query.filter_by(requester_id=AuthenticatedUser.id, requestee_id=user.id).first() is not None
FriendRequestToAuthenticatedUser = FriendRequest.query.filter_by(requester_id=user.id, requestee_id=AuthenticatedUser.id).first() is not None
isViewerFollowing = FollowRelationship.query.filter_by(followerUserId=AuthenticatedUser.id, followeeUserId=user.id).first() is not None
doesTargetUserFollowViewer = FollowRelationship.query.filter_by(followerUserId=user.id, followeeUserId=AuthenticatedUser.id).first() is not None
else:
isFriends = False
FriendRequestPending = False
FriendRequestToAuthenticatedUser = False
isViewerFollowing = False
doesTargetUserFollowViewer = False
joindate = user.created.strftime("%d/%m/%Y")
TotalVisits = 0
UniverseList : list[Universe] = Universe.query.filter_by( creator_id = user.id, creator_type = 0 ).all()
for UniverseObj in UniverseList:
TotalVisits += UniverseObj.visit_count
FollowerCount = followings.get_follower_count( user )
FollowingCount = followings.get_following_count( user )
WearingAssets = UserAvatarAsset.query.filter_by(user_id=user.id).all()
membershipValue : int = membership.GetUserMembership(user.id).value
UserGames : list[Asset] = Asset.query.filter_by(creator_id = user.id, creator_type = 0, asset_type = AssetType.Place).all()
UserGamesInfo : list[dict] = []
for UserGame in UserGames:
PlaceObj : Place = Place.query.filter_by(placeid=UserGame.id).first()
if PlaceObj is None:
continue
if PlaceObj.is_public == False:
continue
PlaceObjData = {
"id": UserGame.id,
"name": UserGame.name,
"playingcount": placeinfo.GetPlayingCount( PlaceObj ),
"slug": slugify(UserGame.name, lowercase=False) if UserGame.name is not None else "",
"likePercentage": GetAssetVotePercentage(UserGame.id),
"placeyear": PlaceObj.placeyear
}
UserGamesInfo.append(PlaceObjData)
ProfileData = {
"username": user.username,
"id": user.id,
"friendcount": FriendCount,
"isonline": True if (datetime.utcnow() - user.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=user.id).first() is not None else False,
"isfriends": isFriends,
"friendrequestpending": FriendRequestPending,
"friendrequesttoauthenticateduser": FriendRequestToAuthenticatedUser,
"friendsdata": FriendsData,
"joindate": joindate,
"TotalVisits": TotalVisits,
"FollowerCount": FollowerCount,
"FollowingCount": FollowingCount,
"wearing": WearingAssets,
"isviewerfollowing": isViewerFollowing,
"doestargetuserfollowviewer": doesTargetUserFollowViewer,
"membershipValue": membershipValue,
"UserGames": UserGamesInfo,
"UserRAP": economy.CalculateUserRAP( user )
}
return render_template("profiles/profile.html", profile=ProfileData, descriptionlines=DescriptionLines)
@Profile.route("/users/<int:userid>/addfriend", methods=["POST"])
@limiter.limit("5/minute")
@auth.authenticated_required
@user_limiter.limit("5/minute")
def add_friend(userid):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
if not websiteFeatures.GetWebsiteFeature("SendFriendRequest"):
flash("Adding friends is temporarily disabled.", "error")
return redirect(f"/users/{user.id}/profile")
AuthenticatedUser = auth.GetCurrentUser()
if userid == AuthenticatedUser.id:
flash("You cannot add yourself as a friend.", "error")
return redirect(f"/users/{user.id}/profile")
if FriendRequest.query.filter_by(requester_id=AuthenticatedUser.id, requestee_id=user.id).first() is not None:
flash("You have already sent a friend request to this user.", "error")
return redirect(f"/users/{user.id}/profile")
if friends.IsFriends(AuthenticatedUser.id, user.id):
flash("You are already friends with this user.", "error")
return redirect(f"/users/{user.id}/profile")
friendRequest = FriendRequest(requester_id=AuthenticatedUser.id, requestee_id=user.id)
db.session.add(friendRequest)
db.session.commit()
return redirect(f"/users/{user.id}/profile")
@Profile.route("/users/<int:userid>/unfriend", methods=["POST"])
@limiter.limit("5/minute")
@auth.authenticated_required
@user_limiter.limit("5/minute")
def unfriend(userid):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
AuthenticatedUser = auth.GetCurrentUser()
if not friends.IsFriends(AuthenticatedUser.id, user.id):
flash("You are not friends with this user.", "error")
return handle_redirect(user)
Friendrelationship : FriendRelationship = friends.GetFriendRelationship(AuthenticatedUser.id, user.id)
db.session.delete(Friendrelationship)
db.session.commit()
return handle_redirect(user)
def handle_redirect(user):
wantRedirect = request.args.get("redirect", type=int, default=1)
if wantRedirect == 1:
return redirect(f"/users/{user.id}/profile")
else:
return redirect(request.referrer)
@Profile.route("/users/<int:userid>/acceptfriend", methods=["POST"])
@limiter.limit("30/minute")
@auth.authenticated_required
@user_limiter.limit("30/minute")
def accept_friend(userid):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
if not websiteFeatures.GetWebsiteFeature("SendFriendRequest"):
flash("Adding friends is temporarily disabled.", "error")
return handle_redirect(user)
AuthenticatedUser = auth.GetCurrentUser()
if userid == AuthenticatedUser.id:
flash("You cannot add yourself as a friend.", "error")
return handle_redirect(user)
friendRequest = FriendRequest.query.filter_by(requester_id=user.id, requestee_id=AuthenticatedUser.id).first()
if friendRequest is None:
flash("Friend request not found.", "error")
return handle_redirect(user)
FriendCount = len(friends.GetFriends(user.id))
if FriendCount >= 200:
flash("This user has reached their friend limit.", "error")
return handle_redirect(user)
AuthenticatedUserFriendCount = len(friends.GetFriends(AuthenticatedUser.id))
if AuthenticatedUserFriendCount >= 200:
flash("You have reached your friend limit.", "error")
return handle_redirect(user)
Friendrelationship = FriendRelationship(user_id=user.id, friend_id=AuthenticatedUser.id)
db.session.add(Friendrelationship)
db.session.delete(friendRequest)
db.session.commit()
return handle_redirect(user)
@Profile.route("/users/<int:userid>/declinefriend", methods=["POST"])
@limiter.limit("30/minute")
@auth.authenticated_required
@user_limiter.limit("30/minute")
def decline_friend(userid):
TargetUser = User.query.filter_by(id=userid).first()
if TargetUser is None:
abort(404)
if not websiteFeatures.GetWebsiteFeature("SendFriendRequest"):
flash("Declining friend requests is temporarily disabled.", "error")
return handle_redirect(TargetUser)
AuthenticatedUser = auth.GetCurrentUser()
if userid == AuthenticatedUser.id:
abort(404)
friendRequest = FriendRequest.query.filter_by(requester_id=TargetUser.id, requestee_id=AuthenticatedUser.id).first()
if friendRequest is None:
return handle_redirect(TargetUser)
db.session.delete(friendRequest)
db.session.commit()
return handle_redirect(TargetUser)
@Profile.route("/users/<int:userid>/unfollowuser", methods=["POST"])
@limiter.limit("5/minute")
@auth.authenticated_required
def unfollow_user(userid):
UserObj : User = User.query.filter_by(id=userid).first()
if UserObj is None:
abort(404)
AuthenticatedUser : User = auth.GetCurrentUser()
try:
followings.unfollow_user(
current_follower = AuthenticatedUser, followed_user = UserObj
)
except followings.FollowingExceptions.UserNotFollowing:
flash("You are not following this user.", "error")
return redirect(f"/users/{UserObj.id}/profile")
except followings.FollowingExceptions.UserRateLimited:
flash("You are being rate limited from unfollowing users, please slow down.", "error")
return redirect(f"/users/{UserObj.id}/profile")
return redirect(f"/users/{UserObj.id}/profile")
@Profile.route("/users/<int:userid>/followuser", methods=["POST"])
@limiter.limit("5/minute")
@auth.authenticated_required
def follow_user(userid):
UserObj : User = User.query.filter_by( id = userid ).first()
if UserObj is None:
abort(404)
if UserObj.accountstatus == 4 or UserObj.accountstatus == 3:
abort(404)
AuthenticatedUser : User = auth.GetCurrentUser()
try:
followings.follow_user(
follower_user = AuthenticatedUser, followed_user = UserObj
)
except followings.FollowingExceptions.AlreadyFollowing:
flash("You are already following this user.", "error")
return redirect(f"/users/{UserObj.id}/profile")
except followings.FollowingExceptions.UserRateLimited:
flash("You are being rate limited from following users, please slow down.", "error")
return redirect(f"/users/{UserObj.id}/profile")
except followings.FollowingExceptions.CannotFollowSelf:
flash("You cannot follow yourself.", "error")
return redirect(f"/users/{UserObj.id}/profile")
except followings.FollowingExceptions.FollowingIsDisabled:
flash("Following users is disabled.", "error")
return redirect(f"/users/{UserObj.id}/profile")
return redirect(f"/users/{UserObj.id}/profile")
@Profile.route("/users/<int:userid>/friends", methods=["GET"])
@auth.authenticated_required
def FriendsPage( userid : int ):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
PageNumber = request.args.get("page", 1, type=int)
from app.models.friend_relationship import FriendRelationship
FriendRelationships : list[FriendRelationship] = FriendRelationship.query.filter(
or_(
FriendRelationship.user_id == user.id,
FriendRelationship.friend_id == user.id
)
).join(
User,
or_(
FriendRelationship.user_id == User.id,
FriendRelationship.friend_id == User.id
)
).filter(
User.id != user.id
).order_by(
User.username
).paginate(
page=PageNumber,
per_page=18,
error_out=False
)
FriendUsers : list[User] = []
for FriendRelationshipObj in FriendRelationships:
if FriendRelationshipObj.user_id == user.id:
TargetUserId : int = FriendRelationshipObj.friend_id
else:
TargetUserId : int = FriendRelationshipObj.user_id
FriendUser : User = User.query.filter_by(id=TargetUserId).first()
if FriendUser is not None:
FriendUsers.append({
"id": FriendUser.id,
"username": FriendUser.username,
"isonline": True if (datetime.utcnow() - FriendUser.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=FriendUser.id).first() is not None else False
})
FriendCount = FriendRelationship.query.filter((FriendRelationship.user_id == user.id) | (FriendRelationship.friend_id == user.id)).count()
if FriendRelationships.pages < PageNumber:
redirect(f"/users/{user.id}/friends")
return render_template("profiles/friends.html",
profile=user,
FriendUsers=FriendUsers,
PageNumber=PageNumber,
isThereNextPage=FriendRelationships.has_next,
isTherePreviousPage=FriendRelationships.has_prev,
FriendCount=FriendCount,
TotalPages=FriendRelationships.pages)
@Profile.route("/users/<int:userid>/following", methods=["GET"])
@auth.authenticated_required
def FollowingPage( userid : int ):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
PageNumber = request.args.get("page", 1, type=int)
from app.models.follow_relationship import FollowRelationship
FollowRelationships : list[FollowRelationship] = FollowRelationship.query.filter_by(followerUserId=user.id).join(
User,
FollowRelationship.followeeUserId == User.id
).order_by(
User.username
).paginate(
page=PageNumber,
per_page=18,
error_out=False
)
FollowUsers : list[User] = []
for FollowRelationship in FollowRelationships:
FollowUser : User = User.query.filter_by(id=FollowRelationship.followeeUserId).first()
if FollowUser is not None:
FollowUsers.append({
"id": FollowUser.id,
"username": FollowUser.username,
"isonline": True if (datetime.utcnow() - FollowUser.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=FollowUser.id).first() is not None else False
})
FollowCount = FollowRelationship.query.filter_by(followerUserId=user.id).count()
if FollowRelationships.pages < PageNumber:
redirect(f"/users/{user.id}/following")
return render_template("profiles/following.html",
profile=user,
FollowUsers=FollowUsers,
PageNumber=PageNumber,
isThereNextPage=FollowRelationships.has_next,
isTherePreviousPage=FollowRelationships.has_prev,
FollowCount=FollowCount,
TotalPages=FollowRelationships.pages)
@Profile.route("/users/<int:userid>/followers", methods=["GET"])
@auth.authenticated_required
def FollowersPage( userid : int ):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
PageNumber = request.args.get("page", 1, type=int)
from app.models.follow_relationship import FollowRelationship
FollowRelationships : list[FollowRelationship] = FollowRelationship.query.filter_by(followeeUserId=user.id).join(
User,
FollowRelationship.followerUserId == User.id
).order_by(
User.username
).paginate(
page=PageNumber,
per_page=18,
error_out=False
)
FollowUsers : list[User] = []
for FollowRelationship in FollowRelationships:
FollowUser : User = User.query.filter_by(id=FollowRelationship.followerUserId).first()
if FollowUser is not None:
FollowUsers.append({
"id": FollowUser.id,
"username": FollowUser.username,
"isonline": True if (datetime.utcnow() - FollowUser.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=FollowUser.id).first() is not None else False
})
FollowCount = FollowRelationship.query.filter_by(followeeUserId=user.id).count()
if FollowRelationships.pages < PageNumber:
redirect(f"/users/{user.id}/followers")
return render_template("profiles/followers.html",
profile=user,
FollowUsers=FollowUsers,
PageNumber=PageNumber,
isThereNextPage=FollowRelationships.has_next,
isTherePreviousPage=FollowRelationships.has_prev,
FollowCount=FollowCount,
TotalPages=FollowRelationships.pages)
@Profile.route("/users/<int:userid>/requests", methods=["GET"])
@auth.authenticated_required
def FriendRequestsPage( userid : int ):
AuthenticatedUser = auth.GetCurrentUser()
if AuthenticatedUser.id != userid:
abort(404)
PageNumber = request.args.get("page", 1, type=int)
FriendRequests : list[FriendRequest] = FriendRequest.query.filter_by(requestee_id=userid).order_by(FriendRequest.created_at.desc()).paginate(page=PageNumber, per_page=18, error_out=False)
return render_template("profiles/requests.html",
profile=AuthenticatedUser,
FriendRequests=FriendRequests)
@Profile.route("/users/<int:userid>/requests/clear", methods=["POST"])
@auth.authenticated_required
def ClearFriendRequests(userid : int):
AuthenticatedUser = auth.GetCurrentUser()
if AuthenticatedUser.id != userid:
abort(404)
FriendRequests : list[FriendRequest] = FriendRequest.query.filter_by(requestee_id=userid).all()
for FriendRequestObj in FriendRequests:
db.session.delete(FriendRequestObj)
db.session.commit()
return redirect(f"/users/{userid}/requests")
@Profile.route("/users/<int:userid>/inventory", methods=["GET"])
@auth.authenticated_required
def UserInventoryPage( userid : int ):
UserObj : User = User.query.filter_by( id = userid ).first()
if UserObj is None:
return abort(404)
InventoryTypesDict = {
0: lambda queryObj: queryObj.filter(Asset.asset_type.in_((
AssetType.HairAccessory,
AssetType.FaceAccessory,
AssetType.NeckAccessory,
AssetType.ShoulderAccessory,
AssetType.FrontAccessory,
AssetType.BackAccessory,
AssetType.WaistAccessory,
))),
1: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Audio ),
2: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Package ),
3: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Face ),
4: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Head ),
5: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Hat ),
6: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.GamePass ),
7: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.TShirt ),
8: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Shirt ),
9: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Pants ),
10: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Gear )
}
InventoryTypeToFriendlyName = {
0: "Accessories",
1: "Audio",
2: "Packages",
3: "Faces",
4: "Heads",
5: "Hats",
6: "Passes",
7: "T-Shirts",
8: "Shirts",
9: "Pants",
10: "Gears",
11: "Badges"
}
CategoryType = request.args.get( key = "category", default = 0, type = int )
if CategoryType not in InventoryTypeToFriendlyName:
return redirect(f"/users/{userid}/inventory")
PageNumber = request.args.get( key = "page", default = 1, type = int )
if PageNumber < 1:
PageNumber = 1
if CategoryType not in [11]:
SearchQuery = UserAsset.query.filter_by( userid = userid ).outerjoin( Asset, Asset.id == UserAsset.assetid )
SearchQuery = InventoryTypesDict[CategoryType](SearchQuery).order_by( UserAsset.updated.desc() ).paginate( page = PageNumber, per_page = 24, error_out = False )
for UserAssetObj in SearchQuery.items:
if UserAssetObj.asset.is_limited and not UserAssetObj.asset.is_for_sale:
BestPriceResult : UserAsset = UserAsset.query.filter_by(assetid=UserAssetObj.assetid, is_for_sale=True).order_by(UserAsset.price.asc()).first()
if BestPriceResult is not None:
UserAssetObj.best_price = str(BestPriceResult.price)
else:
UserAssetObj.best_price = "--"
elif CategoryType == 11:
SearchQuery = UserBadge.query.filter_by( user_id = userid ).order_by( UserBadge.awarded_at.desc() ).paginate( page = PageNumber, per_page = 24, error_out = False )
return render_template(
"profiles/inventory.html",
profile = UserObj,
SearchQuery = SearchQuery,
CategoryName = InventoryTypeToFriendlyName[CategoryType],
CategoryIndex = CategoryType
)