Add hook system

+ Formatting
This commit is contained in:
magmaus3 2022-11-19 13:51:28 +01:00
parent 36dff3d99a
commit 5360b128e0
Signed by: magmaus3
GPG key ID: 966755D3F4A9B251
7 changed files with 350 additions and 206 deletions

View file

@ -1,3 +1,4 @@
import uvicorn import uvicorn
if __name__ == "__main__": if __name__ == "__main__":
uvicorn.run("customiwmserver.main:app", host="0.0.0.0", port=8001, reload=True) uvicorn.run("customiwmserver.main:app", host="0.0.0.0", port=8001, reload=True)

View file

@ -133,11 +133,13 @@ class Notification(BaseModel):
} }
Type: int = 0 Type: int = 0
class Map(BaseModel): class Map(BaseModel):
"""Pydantic model for maps. """Pydantic model for maps.
It looks like a lot of variables are also included in the user It looks like a lot of variables are also included in the user
data. data.
""" """
ID: int = 0 ID: int = 0
CreatorId: int = 0 CreatorId: int = 0
@ -156,7 +158,7 @@ class Map(BaseModel):
DragonCoins: bool = False DragonCoins: bool = False
# Might be duplicates from the User data # Might be duplicates from the User data
ShoesColor: int = 0 ShoesColor: int = 0
PantsColor: int = 0 PantsColor: int = 0
ShirtColor: int = 10897172 ShirtColor: int = 10897172
@ -189,17 +191,15 @@ class Map(BaseModel):
NumThumbsUp: int = 0 NumThumbsUp: int = 0
NumThumbsDown: int = 0 NumThumbsDown: int = 0
TagIDs: str = "" TagIDs: str = ""
TagNames: str = "" TagNames: str = ""
TagFreqs: str = "" TagFreqs: str = ""
PlayCount: int = 0 PlayCount: int = 0
ClearCount: int = 0 ClearCount: int = 0
# I'm not sure how ClearRate is handled # I'm not sure how ClearRate is handled
# in the official server, but I assume it's # in the official server, but I assume it's
# calculated as ClearCount / PlayCount. # calculated as ClearCount / PlayCount.
ClearRate: float = 0.0 ClearRate: float = 0.0
FavoriteCount: int = 0 FavoriteCount: int = 0
@ -216,9 +216,11 @@ class Map(BaseModel):
# Propably not needed, because you can simply read the # Propably not needed, because you can simply read the
# FirstClearUserID, and get the name from there # FirstClearUserID, and get the name from there
# It's still useful for requests though. # It's still useful for requests though.
FirstClearUsername: Union[str, None] = "uwu" FirstClearUsername: Union[str, None] = "uwu"
MapData: str = "" MapData: str = ""
MapReplay: str = "" # Might be also stored in DB as a dict of user IDs and the replays MapReplay: str = (
"" # Might be also stored in DB as a dict of user IDs and the replays
)
Played: bool = False Played: bool = False
Clear: bool = False Clear: bool = False
FullClear: bool = False FullClear: bool = False
@ -229,6 +231,7 @@ class Map(BaseModel):
class MapLeaderboard(BaseModel): class MapLeaderboard(BaseModel):
"""Pydantic model for Map record leaderboards.""" """Pydantic model for Map record leaderboards."""
BestPlaytime: int BestPlaytime: int
BestPlaytimeTime: str BestPlaytimeTime: str
BestReplay: str BestReplay: str
@ -256,7 +259,7 @@ class MapLeaderboard(BaseModel):
FollowerColor: int = 0 FollowerColor: int = 0
SaveEffect: int = 0 SaveEffect: int = 0
TextSnd: int = 0 TextSnd: int = 0
ids_to_names = { ids_to_names = {
0: "Adventure/Variety", 0: "Adventure/Variety",
@ -271,11 +274,11 @@ ids_to_names = {
4: "Auto", 4: "Auto",
} }
def convertTagsToNames(tags): def convertTagsToNames(tags):
"""Converts tag IDs to names""" """Converts tag IDs to names"""
global ids_to_names global ids_to_names
tagNames = [] tagNames = []
for i in tags.split(','): for i in tags.split(","):
tagNames.append(ids_to_names[int(i)]) tagNames.append(ids_to_names[int(i)])
return tagNames return tagNames

View file

@ -14,13 +14,19 @@ reports_collection = db.reports
general_collection = db.general general_collection = db.general
admin_log_collection = db.admin_log admin_log_collection = db.admin_log
def LogAdminAction(action_type: str, action_data: dict, UserID: int = None, success: bool = True):
def LogAdminAction(
action_type: str, action_data: dict, UserID: int = None, success: bool = True
):
"""Log administrator action.""" """Log administrator action."""
admin_log_collection.insert_one({ admin_log_collection.insert_one(
"date": datetime.utcnow(), {
"action_type": action_type, "date": datetime.utcnow(),
"action_data": action_data "action_type": action_type,
}) "action_data": action_data,
}
)
def auth_check(Authorization): def auth_check(Authorization):
"""Checks credentials. """Checks credentials.
@ -33,11 +39,11 @@ def auth_check(Authorization):
""" """
username, password = Authorization.split(":") username, password = Authorization.split(":")
query = user_collection.find_one({"Username": username}) query = user_collection.find_one({"Username": username})
if not query: if not query:
return False, "nouser" return False, "nouser"
if query['Password'] != password: if query["Password"] != password:
return False, "wrongpass" return False, "wrongpass"
return True, query return True, query

View file

@ -0,0 +1,46 @@
from functools import reduce
class HookException(Exception):
pass
class HookMan:
hooks = {}
def __new__(cls):
return reduce(lambda x, y: y(x), cls.hooks, super(HookMan, cls).__new__(cls))
def execute_hooks(self, hook_type, *args, **kwargs):
if hook_type not in self.hooks:
return
for hook in self.hooks[hook_type]:
print(f"Executing hook {hook.__name__}")
try:
hook()(*args, **kwargs)
except Exception as exc:
raise HookException(exc)
@classmethod
def add_hook(cls, func, *args, **kwargs):
"""Adds a hook."""
hook_type = func.hook_type
if hook_type in cls.hooks:
cls.hooks[hook_type] = cls.hooks[hook_type] + [func]
else:
cls.hooks[hook_type] = [func]
hook = HookMan()
# @hook.add_hook
# class example_hook:
# """Example hook."""
# hook_type = "player_login"
#
# def __call__(self, *args, **kwargs):
# print("Hook player_login,", kwargs)
#
#
hook.execute_hooks("player_login", hello="john")

View file

@ -0,0 +1,9 @@
# from . import *
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
for imp, module, ispackage in pkgutil.walk_packages(
path=__path__, prefix=__name__ + "."
):
__import__(module)

View file

@ -0,0 +1,18 @@
#
# Example module.
#
from .. import hook_system
hook = hook_system.hook
@hook.add_hook
class Login:
# Different hook types are executed at different places.
# In this case, the hook is executed when a user logs in.
# Check the documentation for more hook types.
hook_type = "player_login"
def __call__(self, *args, **kwargs):
print("LOGIN HOOK:", args, kwargs)

View file

@ -7,136 +7,160 @@ from typing import Union, Optional, Any
import uvicorn import uvicorn
from . import data_types as types from . import data_types as types
from . import database as db from . import database as db
from . import hook_system
from . import hooks
import pymongo import pymongo
app = FastAPI() app = FastAPI()
hook = hook_system.hook
@app.exception_handler(RequestValidationError) @app.exception_handler(RequestValidationError)
async def http_exception_handler(request, exc): async def http_exception_handler(request, exc):
return PlainTextResponse(f"{str(exc)}", status_code=422) return PlainTextResponse(f"{str(exc)}", status_code=422)
@app.exception_handler(StarletteHTTPException) @app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc): async def http_exception_handler(request, exc):
return PlainTextResponse(f"[{exc.status_code}] {str(exc.detail)}", status_code=exc.status_code) return PlainTextResponse(
f"[{exc.status_code}] {str(exc.detail)}", status_code=exc.status_code
)
## Users ## Users
@app.post("/api/v1/login") @app.post("/api/v1/login")
async def login(username: str = Form(), password: str = Form(), version: str = Form()): async def login(username: str = Form(), password: str = Form(), version: str = Form()):
"""User login""" """User login"""
#FIXME Add login # FIXME Add login
hook.execute_hooks("player_login", username=username)
return {"token": username + ":" + password, "userId": 1} return {"token": username + ":" + password, "userId": 1}
@app.put("/api/v1/user") @app.put("/api/v1/user")
async def create_user(username: str = Form(), password: str = Form(), email: str = Form(), version: str = Form()): async def create_user(
username: str = Form(),
password: str = Form(),
email: str = Form(),
version: str = Form(),
):
"""Create a user with specified credentials""" """Create a user with specified credentials"""
token = username + ":" + password token = username + ":" + password
UserID = len(list(db.user_collection.find({}))) UserID = len(list(db.user_collection.find({})))
if UserID == 0: UserID = 1 if UserID == 0:
UserID = 1
insert_data = { insert_data = {
**types.User( **types.User(Username=username, Email=email, ID=UserID).dict(),
Username=username,
Email=email,
ID=UserID
).dict(),
"Password": password, "Password": password,
} }
db.user_collection.insert_one(insert_data) db.user_collection.insert_one(insert_data)
return {"token": token, "userId": UserID} return {"token": token, "userId": UserID}
@app.get("/api/v1/notifunread") @app.get("/api/v1/notifunread")
async def notifunread(): async def notifunread():
"""Returns the number of unread notifications.""" """Returns the number of unread notifications."""
#FIXME Add notifications # FIXME Add notifications
return 0 return 0
@app.get("/api/v1/refresh") @app.get("/api/v1/refresh")
async def refresh_login(Authorization: Union[str, None] = Header(default=None)): async def refresh_login(Authorization: Union[str, None] = Header(default=None)):
"""Intended for refreshing user token.""" """Intended for refreshing user token."""
return {"token": Authorization} return {"token": Authorization}
@app.get("/api/v1/user/{user_id}") @app.get("/api/v1/user/{user_id}")
async def get_user(user_id: int): async def get_user(user_id: int):
"""Returns specified user's profile.""" """Returns specified user's profile."""
#FIXME use database # FIXME use database
query = db.user_collection.find_one({"ID": user_id}) query = db.user_collection.find_one({"ID": user_id})
del query["Password"] del query["Password"]
return types.User(**query) return types.User(**query)
## Maps ## Maps
@app.get("/api/v1/mapcount") @app.get("/api/v1/mapcount")
async def mapcount(): async def mapcount():
return len(list(db.maps_collection.find({}))) return len(list(db.maps_collection.find({})))
@app.get("/api/v1/useruploadcooldown") @app.get("/api/v1/useruploadcooldown")
async def useruploadcooldown(): async def useruploadcooldown():
"""Limits the amount of levels that user can upload.""" """Limits the amount of levels that user can upload."""
return {"success": True} return {"success": True}
@app.get("/api/v1/map") @app.get("/api/v1/map")
async def search_for_maps( async def search_for_maps(
start: int = 0, start: int = 0,
limit: int = 5, limit: int = 5,
min_diff: float = 0.0, min_diff: float = 0.0,
max_diff: float = 5.0, max_diff: float = 5.0,
order: str = '[{ "Dir": "desc", "Name": "created_at" }]', order: str = '[{ "Dir": "desc", "Name": "created_at" }]',
name: str = "", name: str = "",
author: str = "", author: str = "",
author_id: Optional[int] = None, author_id: Optional[int] = None,
last_x_hours: Optional[int] = None last_x_hours: Optional[int] = None,
): ):
"""Search for maps.""" """Search for maps."""
# query = db.maps_collection.find({ "CreatorId": author_id }).limit(limit) # query = db.maps_collection.find({ "CreatorId": author_id }).limit(limit)
query = db.maps_collection.find({}).limit(limit) query = db.maps_collection.find({}).limit(limit)
entries = [] entries = []
for i in query: for i in query:
del i['_id'] del i["_id"]
del i['MapData'] del i["MapData"]
entries.append(i) entries.append(i)
return entries return entries
@app.get("/api/v1/map/{mapID}") @app.get("/api/v1/map/{mapID}")
async def getMap(mapID: int): async def getMap(mapID: int):
query = db.maps_collection.find_one({ "ID": mapID }) query = db.maps_collection.find_one({"ID": mapID})
del query['_id'] del query["_id"]
return query return query
@app.post("/api/v1/map/{mapID}/start") @app.post("/api/v1/map/{mapID}/start")
async def getMap(mapID: int): async def getMap(mapID: int):
query = db.maps_collection.find_one({ "ID": mapID }) query = db.maps_collection.find_one({"ID": mapID})
del query['_id'] del query["_id"]
returned_resp = { returned_resp = {
"BestDeaths": 0, "BestDeaths": 0,
"BestPlaytime": 0, "BestPlaytime": 0,
"Clear": False, "Clear": False,
"CurMap": query, "CurMap": query,
"Difficulty": 0, "Difficulty": 0,
"Followed": False, "Followed": False,
"Played": True, "Played": True,
"Rating": 5, "Rating": 5,
"TagIDs": "1,8,9", "TagIDs": "1,8,9",
"TagNames": "Boss/Avoidance,Music,Art" "TagNames": "Boss/Avoidance,Music,Art",
} }
return returned_resp return returned_resp
@app.post("/api/v1/map/{mapID}/stop") @app.post("/api/v1/map/{mapID}/stop")
async def stopMapPlay( async def stopMapPlay(
mapID: int, mapID: int,
clear: int = Form(), clear: int = Form(),
deaths: int = Form(), deaths: int = Form(),
playtime: int = Form(), playtime: int = Form(),
totalDeaths: int = Form(), totalDeaths: int = Form(),
totalTime: int = Form(), totalTime: int = Form(),
replayData: str = Form(), replayData: str = Form(),
Authorization: Union[str, None] = Header(default=None), Authorization: Union[str, None] = Header(default=None),
): ):
"""Saves the map replay, and informs the user if their play is a record""" """Saves the map replay, and informs the user if their play is a record"""
authcheck = db.auth_check(Authorization) authcheck = db.auth_check(Authorization)
if not authcheck[0] and authcheck[1] == "nouser": if not authcheck[0] and authcheck[1] == "nouser":
@ -146,32 +170,35 @@ async def stopMapPlay(
elif authcheck[0]: elif authcheck[0]:
NewMapRecord = False NewMapRecord = False
FirstClear = False FirstClear = False
userData = types.User(**authcheck[1]) userData = types.User(**authcheck[1])
query = db.maps_collection.find_one({"ID": mapID, "Leaderboard.UserID": userData.ID}) query = db.maps_collection.find_one(
if query is not None: del query['_id'] {"ID": mapID, "Leaderboard.UserID": userData.ID}
print(__import__("json").dumps(query,indent=4)) )
if query is not None:
del query["_id"]
print(__import__("json").dumps(query, indent=4))
BestUserTime = None BestUserTime = None
BestTime = None BestTime = None
if query is not None and 'Leaderboard' in query: if query is not None and "Leaderboard" in query:
print("-"*3) print("-" * 3)
for i in query['Leaderboard']: for i in query["Leaderboard"]:
if i['UserID'] == userData.ID: if i["UserID"] == userData.ID:
if BestUserTime is None or BestUserTime > i['BestPlaytime'] : if BestUserTime is None or BestUserTime > i["BestPlaytime"]:
BestUserTime = i['BestPlaytime'] BestUserTime = i["BestPlaytime"]
if BestTime is None or BestTime > i['BestPlaytime']: if BestTime is None or BestTime > i["BestPlaytime"]:
BestTime = i["BestPlaytime"] BestTime = i["BestPlaytime"]
if len(query['Leaderboard']) <= 1 and i['UserID'] != userData.ID: if len(query["Leaderboard"]) <= 1 and i["UserID"] != userData.ID:
FirstClear = True FirstClear = True
print(BestUserTime, BestTime) print(BestUserTime, BestTime)
if BestUserTime is None or playtime < BestUserTime: if BestUserTime is None or playtime < BestUserTime:
print(BestUserTime) print(BestUserTime)
updateQuery = db.maps_collection.update_one({"ID": mapID}, updateQuery = db.maps_collection.update_one(
{"ID": mapID},
{ {
"$push": { "$push": {
"Leaderboard": types.MapLeaderboard( "Leaderboard": types.MapLeaderboard(
ShoesColor=userData.ShoesColor, ShoesColor=userData.ShoesColor,
@ -195,41 +222,53 @@ async def stopMapPlay(
FollowerColor=userData.FollowerColor, FollowerColor=userData.FollowerColor,
SaveEffect=userData.SaveEffect, SaveEffect=userData.SaveEffect,
TextSnd=userData.TextSnd, TextSnd=userData.TextSnd,
BestPlaytime=playtime, BestPlaytime=playtime,
BestPlaytimeTime="2020-02-13T15:19:33Z", BestPlaytimeTime="2020-02-13T15:19:33Z",
BestReplay=replayData, BestReplay=replayData,
CreatorName=userData.Username, CreatorName=userData.Username,
UserID=userData.ID UserID=userData.ID,
).dict() ).dict()
},
}, },
}) )
if BestTime is None or playtime < BestTime: if BestTime is None or playtime < BestTime:
print(BestTime, playtime) print(BestTime, playtime)
NewMapRecord = True NewMapRecord = True
hook.execute_hooks(
"map_finished",
user=userData,
mapID=mapID,
clear=clear,
deaths=deaths,
playtime=playtime,
FirstClear=FirstClear,
NewMapRecord=NewMapRecord,
)
return {"FirstClear": FirstClear, "NewMapRecord": NewMapRecord} return {"FirstClear": FirstClear, "NewMapRecord": NewMapRecord}
@app.put("/api/v1/map") @app.put("/api/v1/map")
async def upload_map( async def upload_map(
Authorization: Union[str, None] = Header(default=None), Authorization: Union[str, None] = Header(default=None),
mapName: str = Form(), mapName: str = Form(),
mapDescription: str = Form(default=''), mapDescription: str = Form(default=""),
mapVersion: int = Form(), mapVersion: int = Form(),
mapData: str = Form(), mapData: str = Form(),
file: Optional[UploadFile] = None, file: Optional[UploadFile] = None,
mapReplay: str = Form(), mapReplay: str = Form(),
deaths: int = Form(), deaths: int = Form(),
playtime: int = Form(), playtime: int = Form(),
totalDeaths: int = Form(), totalDeaths: int = Form(),
totalTime: int = Form(), totalTime: int = Form(),
listed: int = Form(), listed: int = Form(),
requiresCancels: int = Form(), requiresCancels: int = Form(),
hideInChallenges: int = Form(), hideInChallenges: int = Form(),
tags: str = Form(), tags: str = Form(),
rng: int = Form(), rng: int = Form(),
clientVersion: float = Form(), clientVersion: float = Form(),
): ):
authcheck = db.auth_check(Authorization) authcheck = db.auth_check(Authorization)
if not authcheck[0] and authcheck[1] == "nouser": if not authcheck[0] and authcheck[1] == "nouser":
raise HTTPException(404, detail="User not found") raise HTTPException(404, detail="User not found")
@ -238,78 +277,79 @@ async def upload_map(
elif authcheck[0]: elif authcheck[0]:
print(authcheck) print(authcheck)
userData = types.User(**authcheck[1]) userData = types.User(**authcheck[1])
MapCode = "SUSYBAKA" #FIXME Add mapcodes correctly MapCode = "SUSYBAKA" # FIXME Add mapcodes correctly
db.maps_collection.insert_one({ db.maps_collection.insert_one(
**types.Map( {
CreatorName=userData.Username, **types.Map(
CreatorId=userData.ID, CreatorName=userData.Username,
CreatorId=userData.ID,
ID=len(list(db.maps_collection.find({}))) + 1, ID=len(list(db.maps_collection.find({}))) + 1,
Name=mapName, Name=mapName,
Description=mapDescription, Description=mapDescription,
Version=mapVersion, Version=mapVersion,
MapCode=MapCode, MapCode=MapCode,
MapData=mapData, MapData=mapData,
Listed=bool(listed), Listed=bool(listed),
HiddenInChallenges=hideInChallenges, HiddenInChallenges=hideInChallenges,
TagIDs=tags, TagIDs=tags,
TagNames=",".join(types.convertTagsToNames(tags)), TagNames=",".join(types.convertTagsToNames(tags)),
ShoesColor=userData.ShoesColor,
ShoesColor=userData.ShoesColor, PantsColor=userData.PantsColor,
PantsColor=userData.PantsColor, ShirtColor=userData.ShirtColor,
ShirtColor=userData.ShirtColor, CapeColor=userData.CapeColor,
CapeColor=userData.CapeColor, SkinColor=userData.SkinColor,
SkinColor=userData.SkinColor, HairColor=userData.HairColor,
HairColor=userData.HairColor, HatSpr=userData.HatSpr,
HatSpr=userData.HatSpr, Country=userData.Country,
Country=userData.Country, HairSpr=userData.HairSpr,
HairSpr=userData.HairSpr, HatColor=userData.HatColor,
HatColor=userData.HatColor, HatColorInv=userData.HatColorInv,
HatColorInv=userData.HatColorInv, FacialExpression=userData.FacialExpression,
FacialExpression=userData.FacialExpression, DeathEffect=userData.DeathEffect,
DeathEffect=userData.DeathEffect, GunSpr=userData.GunSpr,
GunSpr=userData.GunSpr, BulletSpr=userData.BulletSpr,
BulletSpr=userData.BulletSpr, SwordSpr=userData.SwordSpr,
SwordSpr=userData.SwordSpr, Costume=userData.Costume,
Costume=userData.Costume, FollowerSpr=userData.FollowerSpr,
FollowerSpr=userData.FollowerSpr, FollowerColor=userData.FollowerColor,
FollowerColor=userData.FollowerColor, SaveEffect=userData.SaveEffect,
SaveEffect=userData.SaveEffect, TextSnd=userData.TextSnd,
TextSnd=userData.TextSnd, Leaderboard=[
types.MapLeaderboard(
Leaderboard=[ BestPlaytime=playtime,
types.MapLeaderboard( BestPlaytimeTime="2020-02-13T15:19:33Z",
BestPlaytime=playtime, BestReplay=mapReplay,
BestPlaytimeTime="2020-02-13T15:19:33Z", CreatorName=userData.Username,
BestReplay=mapReplay, UserID=userData.ID,
CreatorName=userData.Username,
UserID=userData.ID
).dict() ).dict()
] ],
).dict() ).dict()
}) }
)
return {"MapCode": MapCode} return {"MapCode": MapCode}
#raise HTTPException(501) # raise HTTPException(501)
@app.get("/api/v1/map/{mapID}/besttimes/{maxEntries}") @app.get("/api/v1/map/{mapID}/besttimes/{maxEntries}")
async def getMapLeaderboard(mapID, maxEntries): async def getMapLeaderboard(mapID, maxEntries):
"""Returns maxEntries records for the specified level""" """Returns maxEntries records for the specified level"""
query = db.maps_collection.find_one({ "ID": int(mapID) }) query = db.maps_collection.find_one({"ID": int(mapID)})
if not query: if not query:
raise HTTPException(404, detail="Map not found") raise HTTPException(404, detail="Map not found")
del query['_id'] del query["_id"]
leaderboard = query['Leaderboard'] leaderboard = query["Leaderboard"]
return leaderboard return leaderboard
@app.get("/api/v1/map/{mapID}/userbesttime/{userID}") @app.get("/api/v1/map/{mapID}/userbesttime/{userID}")
async def getPlayerRecord(mapID, userID): async def getPlayerRecord(mapID, userID):
"""Returns specific replay""" """Returns specific replay"""
query = db.maps_collection.find_one({ "ID": int(mapID) }) query = db.maps_collection.find_one({"ID": int(mapID)})
if not query: if not query:
raise HTTPException(404, detail="Map not found") raise HTTPException(404, detail="Map not found")
del query['_id'] del query["_id"]
leaderboard = query['Leaderboard'] leaderboard = query["Leaderboard"]
# Find user # Find user
replayIndex = None replayIndex = None
@ -320,28 +360,32 @@ async def getPlayerRecord(mapID, userID):
break break
print(leaderboard[replayIndex]) print(leaderboard[replayIndex])
if replayIndex is not None: return {"BestMapTime": leaderboard[replayIndex], "Exists": True} if replayIndex is not None:
else: return {"BestMapTime": None, "Exists": False} return {"BestMapTime": leaderboard[replayIndex], "Exists": True}
else:
return {"BestMapTime": None, "Exists": False}
@app.get("/api/v1/map/{mapID}/besttime") @app.get("/api/v1/map/{mapID}/besttime")
async def getBestRecord(mapID: int): async def getBestRecord(mapID: int):
query = db.maps_collection.find_one({ "ID": int(mapID) }) query = db.maps_collection.find_one({"ID": int(mapID)})
BestTime = None BestTime = None
BestPlay = None BestPlay = None
for i in query['Leaderboard']: for i in query["Leaderboard"]:
if BestTime is None or BestTime > i['BestPlaytime']: if BestTime is None or BestTime > i["BestPlaytime"]:
BestTime = i["BestPlaytime"] BestTime = i["BestPlaytime"]
BestPlay = i BestPlay = i
return {"BestMapTime": BestPlay, "Exists": True} return {"BestMapTime": BestPlay, "Exists": True}
@app.post("/api/v1/map/{mapID}/invalidatealltimes") @app.post("/api/v1/map/{mapID}/invalidatealltimes")
async def invalidateAllTimes( async def invalidateAllTimes(
mapID: int, mapID: int,
Reason: int = Body(), Reason: int = Body(),
CustomReason: str = Body(default=""), CustomReason: str = Body(default=""),
Authorization: Union[str, None] = Header(default=None), Authorization: Union[str, None] = Header(default=None),
): ):
"""Removes ALL records from a specific map""" """Removes ALL records from a specific map"""
authcheck = db.auth_check(Authorization) authcheck = db.auth_check(Authorization)
@ -352,39 +396,46 @@ async def invalidateAllTimes(
elif authcheck[0]: elif authcheck[0]:
userData = types.User(**authcheck[1]) userData = types.User(**authcheck[1])
if userData.Admin: if userData.Admin:
query = db.maps_collection.update_one({"ID": mapID}, {"$push": { query = db.maps_collection.update_one(
"Leaderboard": { {"ID": mapID}, {"$push": {"Leaderboard": {"$each": [], "$slice": 0}}}
"$each": [], )
"$slice": 0 db.LogAdminAction(
} action_type="invalidateAllTimes",
} action_data={
}) "Reason": Reason,
db.LogAdminAction(action_type="invalidateAllTimes", "CustomReason": CustomReason,
action_data={ "mapID": mapID,
"Reason": Reason, },
"CustomReason": CustomReason, UserID=userData.ID,
"mapID": mapID )
},
UserID=userData.ID
)
else: else:
db.LogAdminAction(action_type="invalidateAllTimes", action_data={ db.LogAdminAction(
"Reason": Reason, action_type="invalidateAllTimes",
"CustomReason": CustomReason, action_data={
"mapID": mapID, "Reason": Reason,
"unauthorized": True "CustomReason": CustomReason,
}, success=False) "mapID": mapID,
raise HTTPException(403, detail="Attempted to perform an administrator action without permission. This will be reported.") "unauthorized": True,
},
success=False,
)
raise HTTPException(
403,
detail="Attempted to perform an administrator action without permission. This will be reported.",
)
## General ## General
@app.post("/api/v1/reports") @app.post("/api/v1/reports")
async def reportContent( async def reportContent(
Authorization: Union[str, None] = Header(default=None), Authorization: Union[str, None] = Header(default=None),
user_id: int = Form(), user_id: int = Form(),
map_id: Optional[int] = Form(default=None), map_id: Optional[int] = Form(default=None),
report_type: int = Form(), report_type: int = Form(),
content: str = Form() content: str = Form(),
): ):
authcheck = db.auth_check(Authorization) authcheck = db.auth_check(Authorization)
if not authcheck[0] and authcheck[1] == "nouser": if not authcheck[0] and authcheck[1] == "nouser":
@ -392,20 +443,30 @@ async def reportContent(
elif not authcheck[0] and authcheck[1] == "wrongpass": elif not authcheck[0] and authcheck[1] == "wrongpass":
raise HTTPException(403, detail="Wrong password") raise HTTPException(403, detail="Wrong password")
elif authcheck[0]: elif authcheck[0]:
db.reports_collection.insert_one({"user_id": user_id, "map_id": map_id, "report_type": report_type, "content": content}) db.reports_collection.insert_one(
{
"user_id": user_id,
"map_id": map_id,
"report_type": report_type,
"content": content,
}
)
return True return True
@app.get("/api/v1/featuredlist") @app.get("/api/v1/featuredlist")
async def featuredlist(): async def featuredlist():
"""Returns the list id of the weekly levels list.""" """Returns the list id of the weekly levels list."""
#FIXME Add playlists # FIXME Add playlists
return return
@app.get("/api/v1/followcheck") @app.get("/api/v1/followcheck")
async def followcheck(): async def followcheck():
#FIXME Find the purpouse of this endpoint # FIXME Find the purpouse of this endpoint
return 1 return 1
def start(): def start():
"""Launched with `poetry run start` at root level""" """Launched with `poetry run start` at root level"""
uvicorn.run("customiwmserver.main:app", host="0.0.0.0", port=8001, reload=True) uvicorn.run("customiwmserver.main:app", host="0.0.0.0", port=8001, reload=True)