Add hook system
+ Formatting
This commit is contained in:
parent
36dff3d99a
commit
5360b128e0
7 changed files with 350 additions and 206 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
@ -189,12 +191,10 @@ 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
|
||||||
|
|
||||||
|
@ -218,7 +218,9 @@ class Map(BaseModel):
|
||||||
# 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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
46
customiwmserver/hook_system.py
Normal file
46
customiwmserver/hook_system.py
Normal 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")
|
9
customiwmserver/hooks/__init__.py
Normal file
9
customiwmserver/hooks/__init__.py
Normal 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)
|
18
customiwmserver/hooks/webhook.py
Normal file
18
customiwmserver/hooks/webhook.py
Normal 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)
|
|
@ -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":
|
||||||
|
@ -149,29 +173,32 @@ async def stopMapPlay(
|
||||||
|
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue