add web
This commit is contained in:
@@ -0,0 +1,550 @@
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify, abort
|
||||
from app.util import auth, redislock, websiteFeatures
|
||||
from app.extensions import limiter, db, csrf, user_limiter
|
||||
from app.models.user_trade_items import UserTradeItem
|
||||
from app.models.user_trades import UserTrade
|
||||
from app.models.user import User
|
||||
from app.models.asset import Asset
|
||||
from app.models.userassets import UserAsset
|
||||
from app.models.usereconomy import UserEconomy
|
||||
from app.models.asset_rap import AssetRap
|
||||
from app.models.limited_item_transfers import LimitedItemTransfer
|
||||
from app.util.membership import GetUserMembership
|
||||
from app.enums.MembershipType import MembershipType
|
||||
from app.enums.TradeStatus import TradeStatus
|
||||
from app.enums.LimitedItemTransferMethod import LimitedItemTransferMethod
|
||||
from datetime import datetime, timedelta
|
||||
import math
|
||||
|
||||
TradesPageRoute = Blueprint('trades', __name__, template_folder="pages")
|
||||
|
||||
@TradesPageRoute.route("/trade", methods=['GET'])
|
||||
@auth.authenticated_required
|
||||
def trades():
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
TradeInfo = []
|
||||
|
||||
PageCategory = request.args.get("category", default="inbound", type=str)
|
||||
PageCategory = PageCategory.lower()
|
||||
PageNumber = request.args.get("page", default=1, type=int)
|
||||
if PageNumber < 1:
|
||||
PageNumber = 1
|
||||
|
||||
if PageCategory == "inbound":
|
||||
TradeListObj : list[UserTrade] = UserTrade.query.filter_by(recipient_userid=AuthenticatedUser.id, status=TradeStatus.Pending).order_by(UserTrade.updated_at.desc()).paginate( per_page=15, page=PageNumber, error_out=False )
|
||||
for TradeObj in TradeListObj:
|
||||
OppositeUser : User = User.query.filter_by(id=TradeObj.sender_userid).first()
|
||||
if OppositeUser is None:
|
||||
continue
|
||||
TradeInfo.append({
|
||||
"TradeID": TradeObj.id,
|
||||
"TradeStatus": TradeObj.status,
|
||||
"Created": datetime.strftime(TradeObj.created_at, "%d/%m/%Y %H:%M:%S UTC"),
|
||||
"Expiration": datetime.strftime(TradeObj.expires_at, "%d/%m/%Y %H:%M:%S UTC"),
|
||||
"OppositeUser": OppositeUser
|
||||
})
|
||||
elif PageCategory == "outbound":
|
||||
TradeListObj : list[UserTrade] = UserTrade.query.filter_by(sender_userid=AuthenticatedUser.id, status=TradeStatus.Pending).order_by(UserTrade.updated_at.desc()).paginate( per_page=15, page=PageNumber, error_out=False )
|
||||
for TradeObj in TradeListObj:
|
||||
OppositeUser : User = User.query.filter_by(id=TradeObj.recipient_userid).first()
|
||||
if OppositeUser is None:
|
||||
continue
|
||||
TradeInfo.append({
|
||||
"TradeID": TradeObj.id,
|
||||
"TradeStatus": TradeObj.status,
|
||||
"Created": datetime.strftime(TradeObj.created_at, "%d/%m/%Y %H:%M:%S UTC"),
|
||||
"Expiration": datetime.strftime(TradeObj.expires_at, "%d/%m/%Y %H:%M:%S UTC"),
|
||||
"OppositeUser": OppositeUser
|
||||
})
|
||||
elif PageCategory == "completed":
|
||||
TradeListObj : list[UserTrade] = UserTrade.query.filter((UserTrade.recipient_userid == AuthenticatedUser.id) | (UserTrade.sender_userid == AuthenticatedUser.id)).filter_by(status=TradeStatus.Accepted).order_by(UserTrade.updated_at.desc()).paginate( per_page=15, page=PageNumber, error_out=False )
|
||||
for TradeObj in TradeListObj:
|
||||
if TradeObj.recipient_userid == AuthenticatedUser.id:
|
||||
OppositeUser : User = User.query.filter_by(id=TradeObj.sender_userid).first()
|
||||
else:
|
||||
OppositeUser : User = User.query.filter_by(id=TradeObj.recipient_userid).first()
|
||||
if OppositeUser is None:
|
||||
continue
|
||||
TradeInfo.append({
|
||||
"TradeID": TradeObj.id,
|
||||
"TradeStatus": TradeObj.status,
|
||||
"Created": datetime.strftime(TradeObj.created_at, "%d/%m/%Y %H:%M:%S UTC"),
|
||||
"Expiration": datetime.strftime(TradeObj.expires_at, "%d/%m/%Y %H:%M:%S UTC"),
|
||||
"OppositeUser": OppositeUser
|
||||
})
|
||||
elif PageCategory == "inactive": # inactive trades
|
||||
TradeListObj : list[UserTrade] = UserTrade.query.filter((UserTrade.recipient_userid == AuthenticatedUser.id) | (UserTrade.sender_userid == AuthenticatedUser.id)).filter( (UserTrade.status == TradeStatus.Declined) | (UserTrade.status == TradeStatus.Expired) | (UserTrade.status == TradeStatus.Cancelled)).order_by(UserTrade.updated_at.desc()).paginate( per_page=15, page=PageNumber, error_out=False )
|
||||
for TradeObj in TradeListObj:
|
||||
if TradeObj.recipient_userid == AuthenticatedUser.id:
|
||||
OppositeUser : User = User.query.filter_by(id=TradeObj.sender_userid).first()
|
||||
else:
|
||||
OppositeUser : User = User.query.filter_by(id=TradeObj.recipient_userid).first()
|
||||
if OppositeUser is None:
|
||||
continue
|
||||
TradeInfo.append({
|
||||
"TradeID": TradeObj.id,
|
||||
"TradeStatus": TradeObj.status,
|
||||
"Created": datetime.strftime(TradeObj.created_at, "%d/%m/%Y %H:%M:%S UTC"),
|
||||
"Expiration": datetime.strftime(TradeObj.expires_at, "%d/%m/%Y %H:%M:%S UTC"),
|
||||
"OppositeUser": OppositeUser
|
||||
})
|
||||
else:
|
||||
return abort(404)
|
||||
|
||||
return render_template("trades/index.html", TradeInfo=TradeInfo, PageCategory=PageCategory, TradeListObj=TradeListObj)
|
||||
|
||||
@TradesPageRoute.route("/trade/<int:userid>/create", methods=['GET'])
|
||||
@auth.authenticated_required
|
||||
def createTrade(userid : int):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
if AuthenticatedUser.id == userid:
|
||||
return abort(400)
|
||||
UserCurrentMembership : MembershipType = GetUserMembership(AuthenticatedUser)
|
||||
if UserCurrentMembership == MembershipType.NonBuildersClub:
|
||||
return redirect("/membership")
|
||||
TargetUser : User = User.query.filter_by(id=userid).first()
|
||||
if TargetUser is None:
|
||||
return abort(404)
|
||||
if TargetUser.accountstatus == 4 or TargetUser.accountstatus == 3:
|
||||
return abort(404)
|
||||
return render_template("trades/create.html", TargetUser=TargetUser, AuthenticatedUser=AuthenticatedUser)
|
||||
|
||||
@TradesPageRoute.route("/trade/<int:userid>/create", methods=['POST'])
|
||||
@auth.authenticated_required
|
||||
@limiter.limit("5/second")
|
||||
@user_limiter.limit("5/second")
|
||||
def createTradePost(userid : int):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
if AuthenticatedUser.id == userid:
|
||||
return jsonify({"success": False, "message": "You cannot trade with yourself."}),400
|
||||
|
||||
if not websiteFeatures.GetWebsiteFeature("ItemTrading"):
|
||||
return jsonify({"success": False, "message": "Item trading is temporarily disabled."}),403
|
||||
|
||||
TargetUser : User = User.query.filter_by(id=userid).first()
|
||||
if TargetUser is None:
|
||||
return jsonify({"success": False, "message": "User not found."}),404
|
||||
if TargetUser.accountstatus == 4 or TargetUser.accountstatus == 3:
|
||||
return jsonify({"success": False, "message": "User not found."}),404
|
||||
TradeRequestData : dict = request.json
|
||||
if TradeRequestData is None:
|
||||
return jsonify({"success": False, "message": "Invalid request data."}),400
|
||||
UserCurrentMembership : MembershipType = GetUserMembership(AuthenticatedUser)
|
||||
if UserCurrentMembership == MembershipType.NonBuildersClub:
|
||||
return jsonify({"success": False, "message": "You must be a Builders Club member to send trades."}),403
|
||||
OppositeUserCurrentMembership : MembershipType = GetUserMembership(TargetUser)
|
||||
if OppositeUserCurrentMembership == MembershipType.NonBuildersClub:
|
||||
return jsonify({"success": False, "message": "The opposite user must be a Builders Club member to accept trades."}),403
|
||||
"""
|
||||
Expected JSON:
|
||||
"RequesterOfferRobux": RequesterOfferRobux, # int must be >= 0 <= 100000
|
||||
"TargetOfferRobux": TargetOfferRobux, # int must be >= 0 and <= 100000
|
||||
"RequesterOfferUAIDs": RequesterOfferUAIDs, # list of int
|
||||
"TargetOfferUAIDs": TargetOfferUAIDs, # list of int
|
||||
"TOTPCode": isTOTPEnabled ? TOTPInputElement.value : null # int or null
|
||||
"""
|
||||
|
||||
# RequesterOfferRobux : int = TradeRequestData.get("RequesterOfferRobux", 0)
|
||||
# TargetOfferRobux : int = TradeRequestData.get("TargetOfferRobux", 0)
|
||||
# RequesterOfferUAIDs : list = TradeRequestData.get("RequesterOfferUAIDs", [])
|
||||
# TargetOfferUAIDs : list = TradeRequestData.get("TargetOfferUAIDs", [])
|
||||
# TOTPCode : int | None = TradeRequestData.get("TOTPCode", None, type=int)
|
||||
|
||||
# if not all(key in TradeRequestData for key in ("RequesterOfferRobux", "TargetOfferRobux", "RequesterOfferUAIDs", "TargetOfferUAIDs", "TOTPCode")):
|
||||
# return jsonify({"success": False, "message": "Invalid request data."}),400
|
||||
# if not isinstance(TradeRequestData["RequesterOfferRobux"], int) or not isinstance(TradeRequestData["TargetOfferRobux"], int):
|
||||
# return jsonify({"success": False, "message": "Invalid request data, Robux Offer must be an integer."}),400
|
||||
# if not isinstance(TradeRequestData["RequesterOfferUAIDs"], list) or not isinstance(TradeRequestData["TargetOfferUAIDs"], list):
|
||||
# return jsonify({"success": False, "message": "Invalid request data, Offered UAIDs must be a list."}),400
|
||||
# if not isinstance(TradeRequestData["TOTPCode"], int) and TradeRequestData["TOTPCode"] is not None:
|
||||
# return jsonify({"success": False, "message": "Invalid request data, TOTP code must be an integer."}),400
|
||||
|
||||
try:
|
||||
assert "RequesterOfferRobux" in TradeRequestData, "Missing parameter RequesterOfferRobux"
|
||||
assert "TargetOfferRobux" in TradeRequestData, "Missing parameter TargetOfferRobux"
|
||||
assert "RequesterOfferUAIDs" in TradeRequestData, "Missing parameter RequesterOfferUAIDs"
|
||||
assert "TargetOfferUAIDs" in TradeRequestData, "Missing parameter TargetOfferUAIDs"
|
||||
assert "TOTPCode" in TradeRequestData, "Missing parameter TOTPCode"
|
||||
assert isinstance(TradeRequestData["RequesterOfferRobux"], int), "Invalid request data, Robux Offer must be an integer."
|
||||
assert isinstance(TradeRequestData["TargetOfferRobux"], int), "Invalid request data, Target Robux Offer must be an integer."
|
||||
assert isinstance(TradeRequestData["RequesterOfferUAIDs"], list), "Invalid request data, Offered UAIDs must be a list."
|
||||
assert isinstance(TradeRequestData["TargetOfferUAIDs"], list), "Invalid request data, Offered UAIDs must be a list."
|
||||
assert isinstance(TradeRequestData["TOTPCode"], int) or TradeRequestData["TOTPCode"] is None, "Invalid request data, TOTP code must be an integer or null."
|
||||
except Exception as e:
|
||||
return jsonify({"success": False, "message": f"Payload Validation failed, {str(e)}"}), 400
|
||||
|
||||
TotalTradesActive : int = UserTrade.query.filter_by(sender_userid=AuthenticatedUser.id, status=TradeStatus.Pending).count()
|
||||
if TotalTradesActive >= 25:
|
||||
return jsonify({"success": False, "message": "You cannot have more than 25 active trades at once."}),400
|
||||
ActiveTradesWithTarget : int = UserTrade.query.filter_by(sender_userid=AuthenticatedUser.id, recipient_userid=TargetUser.id, status=TradeStatus.Pending).count()
|
||||
if ActiveTradesWithTarget >= 2:
|
||||
return jsonify({"success": False, "message": "You cannot have more than 2 active trades with the same user at once."}),400
|
||||
|
||||
RequesterOfferRobux : int = TradeRequestData["RequesterOfferRobux"]
|
||||
TargetOfferRobux : int = TradeRequestData["TargetOfferRobux"]
|
||||
RequesterOfferUAIDs : list = TradeRequestData["RequesterOfferUAIDs"]
|
||||
TargetOfferUAIDs : list = TradeRequestData["TargetOfferUAIDs"]
|
||||
TOTPCode : int | None = TradeRequestData["TOTPCode"]
|
||||
|
||||
if (RequesterOfferRobux < 0 or TargetOfferRobux < 0) or (RequesterOfferRobux > 100000 or TargetOfferRobux > 100000):
|
||||
return jsonify({"success": False, "message": "Invalid request data, Robux Offer must be between 0 - 100000"}),400
|
||||
|
||||
if len(RequesterOfferUAIDs) > 4 or len(TargetOfferUAIDs) > 4:
|
||||
return jsonify({"success": False, "message": "Invalid request data, cannot trade more than 4 items at once."}),400
|
||||
|
||||
if len(RequesterOfferUAIDs) <= 0 or len(TargetOfferUAIDs) <= 0:
|
||||
return jsonify({"success": False, "message": "Invalid request data, both request and offer must contain at least one item"}),400
|
||||
|
||||
if AuthenticatedUser.TOTPEnabled:
|
||||
if TOTPCode is None:
|
||||
return jsonify({"success": False, "message": "Invalid request data, TOTP code is required."}),400
|
||||
if not auth.Validate2FACode(AuthenticatedUser.id, int(TOTPCode)):
|
||||
return jsonify({"success": False, "message": "Invalid request data, TOTP code is invalid."}),400
|
||||
|
||||
for UAID in RequesterOfferUAIDs:
|
||||
UserAssetObj : UserAsset = UserAsset.query.filter_by(id=UAID).first()
|
||||
if UserAssetObj is None:
|
||||
return jsonify({"success": False, "message": "Invalid request data, one of the items is invalid."}),400
|
||||
if UserAssetObj.userid != AuthenticatedUser.id:
|
||||
return jsonify({"success": False, "message": f"Invalid request data, you do not own UAID {str(UAID)}"}),400
|
||||
AssetObj : Asset = Asset.query.filter_by(id=UserAssetObj.assetid).first()
|
||||
if not AssetObj.is_limited or AssetObj.is_for_sale:
|
||||
return jsonify({"success": False, "message": f"Invalid request data, you cannot trade UAID {str(UAID)}"}),400
|
||||
|
||||
for UAID in TargetOfferUAIDs:
|
||||
UserAssetObj : UserAsset = UserAsset.query.filter_by(id=UAID).first()
|
||||
if UserAssetObj is None:
|
||||
return jsonify({"success": False, "message": "Invalid request data, one of the items is invalid."}),400
|
||||
if UserAssetObj.userid != TargetUser.id:
|
||||
return jsonify({"success": False, "message": f"Invalid request data, user does not own UAID {str(UAID)}"}),400
|
||||
AssetObj : Asset = Asset.query.filter_by(id=UserAssetObj.assetid).first()
|
||||
if not AssetObj.is_limited or AssetObj.is_for_sale:
|
||||
return jsonify({"success": False, "message": f"Invalid request data, you cannot trade UAID {str(UAID)}"}),400
|
||||
|
||||
NewUserTrade : UserTrade = UserTrade(
|
||||
sender_userid = AuthenticatedUser.id,
|
||||
recipient_userid = TargetUser.id,
|
||||
sender_userid_robux = RequesterOfferRobux,
|
||||
recipient_userid_robux = TargetOfferRobux,
|
||||
status = TradeStatus.Pending,
|
||||
expires_at = datetime.utcnow() + timedelta(days=7)
|
||||
)
|
||||
try:
|
||||
db.session.add(NewUserTrade)
|
||||
db.session.commit()
|
||||
|
||||
for UAID in RequesterOfferUAIDs:
|
||||
NewTradeAsset : UserTradeItem = UserTradeItem(
|
||||
tradeid = NewUserTrade.id,
|
||||
userid = AuthenticatedUser.id,
|
||||
user_asset_id = UAID
|
||||
)
|
||||
db.session.add(NewTradeAsset)
|
||||
|
||||
for UAID in TargetOfferUAIDs:
|
||||
NewTradeAsset : UserTradeItem = UserTradeItem(
|
||||
tradeid = NewUserTrade.id,
|
||||
userid = TargetUser.id,
|
||||
user_asset_id = UAID
|
||||
)
|
||||
db.session.add(NewTradeAsset)
|
||||
|
||||
db.session.commit()
|
||||
except:
|
||||
db.session.delete(NewUserTrade)
|
||||
return jsonify({"success": False, "message": "Internal server error"}),500
|
||||
return jsonify({"success": True, "message": "Trade request sent.", "tradeId": NewUserTrade.id}),200
|
||||
|
||||
@TradesPageRoute.route("/trade/view/<int:tradeid>", methods=['GET'])
|
||||
@auth.authenticated_required_api
|
||||
def viewTrade(tradeid : int):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
TradeObj : UserTrade = UserTrade.query.filter_by(id=tradeid).first()
|
||||
if TradeObj is None:
|
||||
return abort(404)
|
||||
if TradeObj.sender_userid != AuthenticatedUser.id and TradeObj.recipient_userid != AuthenticatedUser.id:
|
||||
return abort(403)
|
||||
|
||||
if TradeObj.expires_at < datetime.utcnow() and TradeObj.status == TradeStatus.Pending:
|
||||
TradeObj.status = TradeStatus.Expired
|
||||
TradeObj.updated_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
|
||||
TradeItems : list[UserTradeItem] = UserTradeItem.query.filter_by(tradeid=TradeObj.id).all()
|
||||
SenderUser : User = User.query.filter_by(id=TradeObj.sender_userid).first()
|
||||
RecipientUser : User = User.query.filter_by(id=TradeObj.recipient_userid).first()
|
||||
|
||||
SenderItems = []
|
||||
RecipientItems = []
|
||||
|
||||
AssetRAPCache = {}
|
||||
def GetAssetRAP( AssetID : int ):
|
||||
if AssetID in AssetRAPCache:
|
||||
return AssetRAPCache[AssetID]
|
||||
AssetRAPObj : AssetRap | None = AssetRap.query.filter_by(assetid=AssetID).first()
|
||||
if AssetRAPObj is None:
|
||||
return 0
|
||||
AssetRAPCache[AssetID] = AssetRAPObj.rap
|
||||
return AssetRAPCache[AssetID]
|
||||
SenderOfferValue = 0
|
||||
RecipientOfferValue = 0
|
||||
|
||||
for TradeItem in TradeItems:
|
||||
UserAssetObj : UserAsset = UserAsset.query.filter_by(id=TradeItem.user_asset_id).first()
|
||||
AssetObj : Asset = Asset.query.filter_by(id=UserAssetObj.assetid).first()
|
||||
if TradeItem.userid == SenderUser.id:
|
||||
SenderItems.append({
|
||||
"UAID": UserAssetObj.id,
|
||||
"Name": AssetObj.name,
|
||||
"AssetId": AssetObj.id,
|
||||
"RAP": GetAssetRAP(AssetObj.id),
|
||||
"serial": UserAssetObj.serial
|
||||
})
|
||||
SenderOfferValue += GetAssetRAP(AssetObj.id)
|
||||
else:
|
||||
RecipientItems.append({
|
||||
"UAID": UserAssetObj.id,
|
||||
"Name": AssetObj.name,
|
||||
"AssetId": AssetObj.id,
|
||||
"RAP": GetAssetRAP(AssetObj.id),
|
||||
"serial": UserAssetObj.serial
|
||||
})
|
||||
RecipientOfferValue += GetAssetRAP(AssetObj.id)
|
||||
|
||||
SenderOfferValue += TradeObj.sender_userid_robux
|
||||
RecipientOfferValue += TradeObj.recipient_userid_robux
|
||||
OppositeUser = SenderUser if AuthenticatedUser.id == RecipientUser.id else RecipientUser
|
||||
|
||||
return render_template("trades/view.html",
|
||||
TradeObj = TradeObj,
|
||||
SenderUser = SenderUser,
|
||||
RecipientUser = RecipientUser,
|
||||
AuthenticatedUser = AuthenticatedUser,
|
||||
SenderItems = SenderItems,
|
||||
RecipientItems = RecipientItems,
|
||||
SenderOfferValue = SenderOfferValue,
|
||||
RecipientOfferValue = RecipientOfferValue,
|
||||
OppositeUser = OppositeUser)
|
||||
|
||||
@TradesPageRoute.route("/trade/<int:tradeid>/accept", methods=['POST'])
|
||||
@auth.authenticated_required
|
||||
def acceptTrade(tradeid : int):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
TradeObj : UserTrade = UserTrade.query.filter_by(id=tradeid).first()
|
||||
if TradeObj is None:
|
||||
return abort(404)
|
||||
if TradeObj.recipient_userid != AuthenticatedUser.id:
|
||||
return abort(403)
|
||||
if TradeObj.status != TradeStatus.Pending:
|
||||
return abort(403)
|
||||
UserCurrentMembership : MembershipType = GetUserMembership(AuthenticatedUser.id)
|
||||
if UserCurrentMembership == MembershipType.NonBuildersClub:
|
||||
flash("You must be a Builders Club member to accept trades.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
OppositeUser : User = User.query.filter_by(id=TradeObj.sender_userid).first()
|
||||
if OppositeUser is None:
|
||||
flash("An error occured while trying to complete this trade. Please try again later.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
OppositeUserCurrentMembership : MembershipType = GetUserMembership(OppositeUser)
|
||||
if OppositeUserCurrentMembership == MembershipType.NonBuildersClub:
|
||||
flash("The opposite user must be a Builders Club member to accept trades.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
if AuthenticatedUser.TOTPEnabled:
|
||||
TOTPCode : str = request.form.get("totpcode", default=None, type=str)
|
||||
if TOTPCode is None:
|
||||
flash("Invalid TOTP code", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
if not auth.Validate2FACode(AuthenticatedUser.id, TOTPCode):
|
||||
flash("Invalid TOTP code", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
if TradeObj.expires_at < datetime.utcnow():
|
||||
flash("This trade has expired.", "error")
|
||||
TradeObj.status = TradeStatus.Expired
|
||||
db.session.commit()
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
TradeItems : list[UserTradeItem] = UserTradeItem.query.filter_by(tradeid=TradeObj.id).all()
|
||||
for TradeItem in TradeItems:
|
||||
UserAssetObj : UserAsset = UserAsset.query.filter_by(id=TradeItem.user_asset_id).first()
|
||||
if UserAssetObj is None:
|
||||
flash("One of the items no longer exists and this trade cannot be completed.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
if UserAssetObj.userid != TradeItem.userid:
|
||||
flash("One of the items no longer belongs to its original owner and this trade cannot be completed.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
SenderEconomyLock = redislock.acquire_lock(f"economy:{str(TradeObj.sender_userid)}", acquire_timeout=5, lock_timeout=3)
|
||||
RecipientEconomyLock = redislock.acquire_lock(f"economy:{str(TradeObj.recipient_userid)}", acquire_timeout=5, lock_timeout=3)
|
||||
if not SenderEconomyLock or not RecipientEconomyLock:
|
||||
if SenderEconomyLock:
|
||||
redislock.release_lock(f"economy:{str(TradeObj.sender_userid)}", SenderEconomyLock)
|
||||
flash("An error occured while trying to complete this trade. Please try again later.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
SenderEconomyObj : UserEconomy = UserEconomy.query.filter_by(userid=TradeObj.sender_userid).first()
|
||||
RecipientEconomyObj : UserEconomy = UserEconomy.query.filter_by(userid=TradeObj.recipient_userid).first()
|
||||
|
||||
if SenderEconomyObj.robux < TradeObj.sender_userid_robux:
|
||||
flash("The sender does not have enough robux to complete this trade.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
if RecipientEconomyObj.robux < TradeObj.recipient_userid_robux:
|
||||
flash("The recipient does not have enough robux to complete this trade.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
ItemLocks = []
|
||||
for TradeItem in TradeItems:
|
||||
TradeItemLock = redislock.acquire_lock(f"item:{str(TradeItem.user_asset_id)}", acquire_timeout=20, lock_timeout=5)
|
||||
if not TradeItemLock:
|
||||
redislock.release_lock(f"economy:{str(TradeObj.sender_userid)}", SenderEconomyLock)
|
||||
redislock.release_lock(f"economy:{str(TradeObj.recipient_userid)}", RecipientEconomyLock)
|
||||
for ItemLock in ItemLocks:
|
||||
redislock.release_lock(f"item:{str(ItemLock[1])}", ItemLock[0])
|
||||
|
||||
flash("An error occured while trying to complete this trade. Please try again later.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
ItemLocks.append([TradeItemLock, TradeItem.user_asset_id])
|
||||
|
||||
def ReleaseAllLocks():
|
||||
redislock.release_lock(f"economy:{str(TradeObj.sender_userid)}", SenderEconomyLock)
|
||||
redislock.release_lock(f"economy:{str(TradeObj.recipient_userid)}", RecipientEconomyLock)
|
||||
for ItemLock in ItemLocks:
|
||||
redislock.release_lock(f"item:{str(ItemLock[1])}", ItemLock[0])
|
||||
|
||||
SenderEconomyObj.robux -= TradeObj.sender_userid_robux
|
||||
RecipientEconomyObj.robux -= TradeObj.recipient_userid_robux
|
||||
|
||||
if TradeObj.sender_userid_robux > 0:
|
||||
FinalAdded = math.floor(TradeObj.sender_userid_robux * 0.7)
|
||||
RecipientEconomyObj.robux += FinalAdded
|
||||
if TradeObj.recipient_userid_robux > 0:
|
||||
FinalAdded = math.floor(TradeObj.recipient_userid_robux * 0.7)
|
||||
SenderEconomyObj.robux += FinalAdded
|
||||
|
||||
def CreateItemTransferLog( original_owner_id : int, new_owner_id : int, asset_id : int, user_asset_id : int ):
|
||||
NewTransferLog = LimitedItemTransfer(
|
||||
original_owner_id = original_owner_id,
|
||||
new_owner_id = new_owner_id,
|
||||
asset_id = asset_id,
|
||||
user_asset_id = user_asset_id,
|
||||
transfer_method = LimitedItemTransferMethod.Trade,
|
||||
associated_trade_id = TradeObj.id
|
||||
)
|
||||
db.session.add(NewTransferLog)
|
||||
|
||||
for TradeItem in TradeItems:
|
||||
UserAssetObj : UserAsset = UserAsset.query.filter_by(id=TradeItem.user_asset_id).first()
|
||||
if UserAssetObj is None:
|
||||
ReleaseAllLocks()
|
||||
flash("One of the items no longer exists and this trade cannot be completed.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
if UserAssetObj.userid == TradeObj.sender_userid:
|
||||
CreateItemTransferLog( original_owner_id = TradeObj.sender_userid, new_owner_id = TradeObj.recipient_userid, asset_id = UserAssetObj.assetid, user_asset_id = UserAssetObj.id )
|
||||
UserAssetObj.userid = TradeObj.recipient_userid
|
||||
else:
|
||||
CreateItemTransferLog( original_owner_id = TradeObj.sender_userid, new_owner_id = TradeObj.recipient_userid, asset_id = UserAssetObj.assetid, user_asset_id = UserAssetObj.id )
|
||||
UserAssetObj.userid = TradeObj.sender_userid
|
||||
UserAssetObj.is_for_sale = False
|
||||
UserAssetObj.price = 0
|
||||
UserAssetObj.updated = datetime.utcnow()
|
||||
db.session.commit()
|
||||
ReleaseAllLocks()
|
||||
|
||||
TradeObj.status = TradeStatus.Accepted
|
||||
TradeObj.updated_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
@TradesPageRoute.route("/trade/<int:tradeid>/cancel", methods=['POST'])
|
||||
@auth.authenticated_required
|
||||
def cancelTrade(tradeid : int):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
TradeObj : UserTrade = UserTrade.query.filter_by(id=tradeid).first()
|
||||
if TradeObj is None:
|
||||
return abort(404)
|
||||
if TradeObj.sender_userid != AuthenticatedUser.id:
|
||||
return abort(403)
|
||||
if TradeObj.status != TradeStatus.Pending:
|
||||
return abort(403)
|
||||
|
||||
if TradeObj.expires_at < datetime.utcnow():
|
||||
TradeObj.status = TradeStatus.Expired
|
||||
TradeObj.updated_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
flash("This trade has expired.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
TradeObj.status = TradeStatus.Cancelled
|
||||
TradeObj.updated_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
@TradesPageRoute.route("/trade/<int:tradeid>/decline", methods=['POST'])
|
||||
@auth.authenticated_required
|
||||
@csrf.exempt
|
||||
def declineTrade(tradeid : int):
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
TradeObj : UserTrade = UserTrade.query.filter_by(id=tradeid).first()
|
||||
if TradeObj is None:
|
||||
return abort(404)
|
||||
if TradeObj.recipient_userid != AuthenticatedUser.id:
|
||||
return abort(403)
|
||||
if TradeObj.status != TradeStatus.Pending:
|
||||
return abort(403)
|
||||
|
||||
if TradeObj.expires_at < datetime.utcnow():
|
||||
TradeObj.status = TradeStatus.Expired
|
||||
TradeObj.updated_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
flash("This trade has expired.", "error")
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
TradeObj.status = TradeStatus.Declined
|
||||
TradeObj.updated_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
|
||||
return redirect(f"/trade/view/{str(tradeid)}")
|
||||
|
||||
@TradesPageRoute.route("/trade/<int:userid>/inventory", methods=['GET'])
|
||||
@auth.authenticated_required_api
|
||||
def getInventory(userid : int):
|
||||
pageNumber = request.args.get("page", default=1, type=int)
|
||||
|
||||
TargetUser : User = User.query.filter_by(id=userid).first()
|
||||
if TargetUser is None:
|
||||
return abort(404)
|
||||
if TargetUser.accountstatus == 4 or TargetUser.accountstatus == 3:
|
||||
return abort(404)
|
||||
|
||||
UserAssets : list[UserAsset] = UserAsset.query.join(Asset, UserAsset.assetid == Asset.id).filter(UserAsset.userid == TargetUser.id, Asset.is_limited == True, Asset.is_for_sale == False).paginate(page=pageNumber, error_out=False, max_per_page=12).items
|
||||
ReturnInfoData = []
|
||||
|
||||
AssetRAPCache = {}
|
||||
def GetAssetRAP( AssetID : int ):
|
||||
if AssetID in AssetRAPCache:
|
||||
return AssetRAPCache[AssetID]
|
||||
AssetRAPObj : AssetRap | None = AssetRap.query.filter_by(assetid=AssetID).first()
|
||||
if AssetRAPObj is None:
|
||||
return 0
|
||||
AssetRAPCache[AssetID] = AssetRAPObj.rap
|
||||
return AssetRAPCache[AssetID]
|
||||
|
||||
for UserAssetObj in UserAssets:
|
||||
AssetObj : Asset = Asset.query.filter_by(id=UserAssetObj.assetid).first()
|
||||
if AssetObj is None:
|
||||
continue
|
||||
ReturnInfoData.append({
|
||||
"id": AssetObj.id,
|
||||
"name": AssetObj.name,
|
||||
"serialNumber": UserAssetObj.serial,
|
||||
"uaid": UserAssetObj.id,
|
||||
"rap": GetAssetRAP(AssetObj.id),
|
||||
})
|
||||
isThereNextPage = len(UserAssets) == 12
|
||||
return jsonify({
|
||||
"data": ReturnInfoData,
|
||||
"nextPage": isThereNextPage
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user