syntaxwebsite/app/routes/discord_internal.py

294 lines
10 KiB
Python

"""
All routes inside here is used to communicate internally with our Discord Bot.
"""
from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify, abort
from app.extensions import csrf, redis_controller, get_remote_address
from sqlalchemy import func
from config import Config
from app.models.user import User
from app.models.asset import Asset
from app.models.linked_discord import LinkedDiscord
from app.models.asset_rap import AssetRap
from app.models.placeserver_players import PlaceServerPlayer
from app.enums.MembershipType import MembershipType
from app.services.economy import GetAssetRap
from app.util.membership import GetUserMembership, GiveUserMembership
import datetime
import time
import logging
DiscordInternal = Blueprint('discord_internal', __name__, url_prefix='/internal/discord_bot')
@DiscordInternal.before_request
def before_request():
AuthorizationToken = request.headers.get("InternalAuthorizationKey", default=None, type=str)
if AuthorizationToken != Config.DISCORD_BOT_AUTHTOKEN:
logging.warning(f"Discord Bot Internal API: Unauthorized request from {get_remote_address()}, invalid auth token")
return abort(404)
UserAgent = request.headers.get("User-Agent", default=None, type=str)
if UserAgent != "SyntaxBot/1.0":
logging.warning(f"Discord Bot Internal API: Unauthorized request from {get_remote_address()}, invalid user agent")
return abort(404)
RequesterAddress = get_remote_address()
if RequesterAddress not in Config.DISCORD_BOT_AUTHORISED_IPS:
logging.warning(f"Discord Bot Internal API: Unauthorized request from {RequesterAddress}, not in authorised IP list")
return abort(404)
def GetUserFromId( UserObj : User | int ) -> User | None:
"""
Returns a User object from a User ID.
"""
if isinstance(UserObj, User):
return UserObj
else:
TargetUser : User | None = User.query.filter_by(id=UserObj).first()
if TargetUser is None:
raise Exception("User does not exist.")
return TargetUser
def ReturnUserObject( UserObj : User ) -> dict:
return {
"id": UserObj.id,
"username": UserObj.username,
"last_online": time.mktime(UserObj.lastonline.timetuple()),
"created_at": time.mktime(UserObj.created.timetuple()),
"description": UserObj.description,
"membership": GetUserMembership(UserObj, changeToString=True)
}
def ReturnItemObject( AssetObj : Asset ) -> dict:
return {
"id": AssetObj.id,
"name": AssetObj.name,
"description": AssetObj.description,
"asset_type": AssetObj.asset_type.name,
"creator": ReturnUserObject(GetUserFromId(AssetObj.creator_id)) if AssetObj.creator_type == 0 else None,
"creator_type": AssetObj.creator_type,
"created_at": time.mktime(AssetObj.created_at.timetuple()),
"updated_at": time.mktime(AssetObj.updated_at.timetuple()),
"is_limited": AssetObj.is_limited,
"is_limited_unique": AssetObj.is_limited_unique,
"is_for_sale": AssetObj.is_for_sale,
"asset_rap": GetAssetRap(AssetObj.id) if AssetObj.is_limited and not AssetObj.is_for_sale else None,
"price_robux": AssetObj.price_robux,
"price_tickets": AssetObj.price_tix,
"sales": AssetObj.sale_count
}
@DiscordInternal.route("/UsernameLookup", methods=['GET'])
def UsernameLookup():
Username = request.args.get("username", default=None, type=str)
if Username is None:
return jsonify({
"success": False,
"message": "Invalid username"
})
UserObject : User = User.query.filter(func.lower(User.username) == func.lower(Username)).first()
if UserObject is None:
return jsonify({
"success": False,
"message": "User not found"
})
if UserObject.accountstatus in [3,4]:
return jsonify({
"success": False,
"message": "User not found"
})
return jsonify({
"success": True,
"message": "",
"data": ReturnUserObject(UserObject)
})
@DiscordInternal.route("/UseridLookup", methods=['GET'])
def UseridLookup():
Userid = request.args.get("userid", default=None, type=int)
if Userid is None:
return jsonify({
"success": False,
"message": "Invalid userid"
})
UserObject : User = User.query.filter_by(id=Userid).first()
if UserObject is None:
return jsonify({
"success": False,
"message": "User not found"
})
if UserObject.accountstatus in [3,4]:
return jsonify({
"success": False,
"message": "User not found"
})
return jsonify({
"success": True,
"message": "",
"data": ReturnUserObject(UserObject)
})
@DiscordInternal.route("/ItemLookup", methods=['GET'])
def ItemLookup():
AssetId = request.args.get("itemid", default=None, type=int)
if AssetId is None:
return jsonify({
"success": False,
"message": "Invalid itemid"
})
AssetObject : Asset = Asset.query.filter_by(id=AssetId).first()
if AssetObject is None:
return jsonify({
"success": False,
"message": "Item not found"
})
if AssetObject.asset_type.value not in [2, 8, 11, 12, 17, 18, 19, 27, 28, 29, 30, 31, 32, 41, 42, 43, 44, 45, 46, 47]:
return jsonify({
"success": False,
"message": "Item not found"
})
return jsonify({
"success": True,
"message": "",
"data": ReturnItemObject(AssetObject)
})
@DiscordInternal.route("/UserLookupByDiscordId", methods=['GET'])
def UserLookupByDiscordId():
DiscordId = request.args.get("discordid", default=None, type=int)
if DiscordId is None:
return jsonify({
"success": False,
"message": "Invalid discordid"
})
LinkedDiscordObject : LinkedDiscord = LinkedDiscord.query.filter_by(discord_id=DiscordId).first()
if LinkedDiscordObject is None:
return jsonify({
"success": False,
"message": "User does not have an account linked to their discord account"
})
UserObject : User = User.query.filter_by(id=LinkedDiscordObject.user_id).first()
if UserObject is None:
return jsonify({
"success": False,
"message": "User not found"
})
if UserObject.accountstatus in [3,4]:
return jsonify({
"success": False,
"message": "User not found"
})
return jsonify({
"success": True,
"message": "",
"data": ReturnUserObject(UserObject)
})
@DiscordInternal.route("/AwardUserTurbo", methods=['POST'])
@csrf.exempt
def AwardUserTurbo():
DiscordId = request.args.get("discordid", default=None, type=int)
if DiscordId is None:
return jsonify({
"success": False,
"message": "Invalid discordid"
})
LinkedDiscordObject : LinkedDiscord = LinkedDiscord.query.filter_by(discord_id=DiscordId).first()
if LinkedDiscordObject is None:
return jsonify({
"success": False,
"message": "User does not have an account linked to their discord account"
})
UserObject : User = User.query.filter_by(id=LinkedDiscordObject.user_id).first()
if UserObject is None:
return jsonify({
"success": False,
"message": "User not found"
})
if UserObject.accountstatus in [3,4]:
return jsonify({
"success": False,
"message": "User is currently moderated"
})
UserMembershipStatus : MembershipType = GetUserMembership(UserObject)
if UserMembershipStatus == MembershipType.TurboBuildersClub:
return jsonify({
"success": False,
"message": "User already has Turbo Builders Club"
})
if UserMembershipStatus == MembershipType.OutrageousBuildersClub:
return jsonify({
"success": False,
"message": "User has Outrageous Builders Club"
})
if redis_controller.get(f"discord_bot_award_turbo_{UserObject.id}") is not None:
return jsonify({
"success": False,
"message": "Discord user has recenetly been awarded Turbo Builders Club"
})
GiveUserMembership(UserObject, MembershipType.TurboBuildersClub, expiration=datetime.timedelta(days=14))
redis_controller.set(f"discord_bot_award_turbo_{UserObject.id}", "1", ex=60*60*24*14)
return jsonify({
"success": True,
"message": ""
})
@DiscordInternal.route("/Ping", methods=['GET'])
def Ping():
return jsonify({
"success": True,
"message": "Pong!"
})
@DiscordInternal.route("/VerifyOBCUsers", methods=["POST"])
@csrf.exempt
def VerifyOBCUsers():
JSONPayload = request.get_json()
if JSONPayload is None:
return jsonify({
"success": False,
"message": "Invalid JSON payload"
})
if "users" not in JSONPayload:
return jsonify({
"success": False,
"message": "Invalid JSON payload"
})
BadUsersIds : list[int] = []
for UserId in JSONPayload["users"]:
LinkedDiscordObj : LinkedDiscord = LinkedDiscord.query.filter_by(discord_id=UserId).first()
if LinkedDiscordObj is None:
BadUsersIds.append(UserId)
continue
UserObj : User = User.query.filter_by(id=LinkedDiscordObj.user_id).first()
if UserObj is None:
BadUsersIds.append(UserId)
continue
UserMembershipType : MembershipType = GetUserMembership(UserObj)
if UserMembershipType != MembershipType.OutrageousBuildersClub:
BadUsersIds.append(UserId)
continue
return jsonify({
"success": True,
"message": "",
"bad_users": BadUsersIds
})
@DiscordInternal.route("/GetSiteStats", methods=["GET"])
def SiteStats():
UsersOnline = User.query.filter(User.lastonline > (datetime.datetime.utcnow() - datetime.timedelta(minutes=1))).count()
UsersIngame = PlaceServerPlayer.query.count()
return jsonify({
"success": True,
"message": "",
"data": {
"users_online": UsersOnline,
"users_ingame": UsersIngame
}
})