Merge pull request 'ONGEKI Rival Functionality' (#36) from 2TT/artemis:develop into develop

Reviewed-on: Hay1tsme/artemis#36
This commit is contained in:
Midorica 2023-09-23 16:08:15 +00:00
commit 91791813dc
8 changed files with 224 additions and 13 deletions

View File

@ -21,6 +21,7 @@ class IUserSession(Interface):
userId = Attribute("User's ID") userId = Attribute("User's ID")
current_ip = Attribute("User's current ip address") current_ip = Attribute("User's current ip address")
permissions = Attribute("User's permission level") permissions = Attribute("User's permission level")
ongeki_version = Attribute("User's selected Ongeki Version")
class PermissionOffset(Enum): class PermissionOffset(Enum):
USER = 0 # Regular user USER = 0 # Regular user
@ -36,6 +37,7 @@ class UserSession(object):
self.userId = 0 self.userId = 0
self.current_ip = "0.0.0.0" self.current_ip = "0.0.0.0"
self.permissions = 0 self.permissions = 0
self.ongeki_version = 7
class FrontendServlet(resource.Resource): class FrontendServlet(resource.Resource):

View File

@ -4,6 +4,7 @@
<title>{{ title }}</title> <title>{{ title }}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<style> <style>
html { html {
background-color: #181a1b !important; background-color: #181a1b !important;
@ -77,6 +78,9 @@
margin-bottom: 10px; margin-bottom: 10px;
width: 15%; width: 15%;
} }
.modal-content {
background-color: #181a1b;
}
</style> </style>
</head> </head>
<body> <body>

View File

@ -2,9 +2,11 @@ from titles.ongeki.index import OngekiServlet
from titles.ongeki.const import OngekiConstants from titles.ongeki.const import OngekiConstants
from titles.ongeki.database import OngekiData from titles.ongeki.database import OngekiData
from titles.ongeki.read import OngekiReader from titles.ongeki.read import OngekiReader
from titles.ongeki.frontend import OngekiFrontend
index = OngekiServlet index = OngekiServlet
database = OngekiData database = OngekiData
reader = OngekiReader reader = OngekiReader
frontend = OngekiFrontend
game_codes = [OngekiConstants.GAME_CODE] game_codes = [OngekiConstants.GAME_CODE]
current_schema_version = 5 current_schema_version = 5

View File

@ -978,35 +978,38 @@ class OngekiBase:
""" """
Added in Bright Added in Bright
""" """
rival_list = self.data.profile.get_rivals(data["userId"])
if rival_list is None or len(rival_list) < 1: rival_list = []
user_rivals = self.data.profile.get_rivals(data["userId"])
for rival in user_rivals:
tmp = {}
tmp["rivalUserId"] = rival[0]
rival_list.append(tmp)
if user_rivals is None or len(rival_list) < 1:
return { return {
"userId": data["userId"], "userId": data["userId"],
"length": 0, "length": 0,
"userRivalList": [], "userRivalList": [],
} }
return { return {
"userId": data["userId"], "userId": data["userId"],
"length": len(rival_list), "length": len(rival_list),
"userRivalList": rival_list._asdict(), "userRivalList": rival_list,
} }
def handle_get_user_rival_data_api_reqiest(self, data: Dict) -> Dict: def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
""" """
Added in Bright Added in Bright
""" """
rivals = [] rivals = []
for rival in data["userRivalList"]: for rival in data["userRivalList"]:
name = self.data.profile.get_profile_name( name = self.data.profile.get_profile_name(
rival["rivalUserId"], self.version rival["rivalUserId"], self.version
) )
if name is None: if name is None:
continue continue
rivals.append({"rivalUserId": rival["rivalUserId"], "rivalUserName": name})
rivals.append({"rivalUserId": rival["rival"], "rivalUserName": name})
return { return {
"userId": data["userId"], "userId": data["userId"],
"length": len(rivals), "length": len(rivals),
@ -1027,7 +1030,6 @@ class OngekiBase:
for song in music["userMusicList"]: for song in music["userMusicList"]:
song["userRivalMusicDetailList"] = song["userMusicDetailList"] song["userRivalMusicDetailList"] = song["userMusicDetailList"]
song.pop("userMusicDetailList") song.pop("userMusicDetailList")
return { return {
"userId": data["userId"], "userId": data["userId"],
"rivalUserId": rival_id, "rivalUserId": rival_id,

87
titles/ongeki/frontend.py Normal file
View File

@ -0,0 +1,87 @@
import yaml
import jinja2
from twisted.web.http import Request
from os import path
from twisted.web.util import redirectTo
from twisted.web.server import Session
from core.frontend import FE_Base, IUserSession
from core.config import CoreConfig
from titles.ongeki.config import OngekiConfig
from titles.ongeki.const import OngekiConstants
from titles.ongeki.database import OngekiData
from titles.ongeki.base import OngekiBase
class OngekiFrontend(FE_Base):
def __init__(
self, cfg: CoreConfig, environment: jinja2.Environment, cfg_dir: str
) -> None:
super().__init__(cfg, environment)
self.data = OngekiData(cfg)
self.game_cfg = OngekiConfig()
if path.exists(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"):
self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"))
)
self.nav_name = "O.N.G.E.K.I."
self.version_list = OngekiConstants.VERSION_NAMES
def render_GET(self, request: Request) -> bytes:
template = self.environment.get_template(
"titles/ongeki/frontend/ongeki_index.jinja"
)
sesh: Session = request.getSession()
usr_sesh = IUserSession(sesh)
self.version = usr_sesh.ongeki_version
if getattr(usr_sesh, "userId", 0) != 0:
profile_data =self.data.profile.get_profile_data(usr_sesh.userId, self.version)
rival_list = self.data.profile.get_rivals(usr_sesh.userId)
rival_data = {
"userRivalList": rival_list,
"userId": usr_sesh.userId
}
rival_info = OngekiBase.handle_get_user_rival_data_api_request(self, rival_data)
return template.render(
data=self.data.profile,
title=f"{self.core_config.server.name} | {self.nav_name}",
game_list=self.environment.globals["game_list"],
gachas=self.game_cfg.gachas.enabled_gachas,
profile_data=profile_data,
rival_info=rival_info["userRivalDataList"],
version_list=self.version_list,
version=self.version,
sesh=vars(usr_sesh)
).encode("utf-16")
else:
return redirectTo(b"/gate/", request)
def render_POST(self, request: Request):
uri = request.uri.decode()
sesh: Session = request.getSession()
usr_sesh = IUserSession(sesh)
if hasattr(usr_sesh, "userId"):
if uri == "/game/ongeki/rival.add":
rival_id = request.args[b"rivalUserId"][0].decode()
self.data.profile.put_rival(usr_sesh.userId, rival_id)
# self.logger.info(f"{usr_sesh.userId} added a rival")
return redirectTo(b"/game/ongeki/", request)
elif uri == "/game/ongeki/rival.delete":
rival_id = request.args[b"rivalUserId"][0].decode()
self.data.profile.delete_rival(usr_sesh.userId, rival_id)
# self.logger.info(f"{response}")
return redirectTo(b"/game/ongeki/", request)
elif uri == "/game/ongeki/version.change":
ongeki_version=request.args[b"version"][0].decode()
if(ongeki_version.isdigit()):
usr_sesh.ongeki_version=int(ongeki_version)
return redirectTo(b"/game/ongeki/", request)
else:
return b"Something went wrong"
else:
return b"User is not logged in"

View File

@ -0,0 +1,24 @@
function deleteRival(rivalUserId){
$(document).ready(function () {
$.post("/game/ongeki/rival.delete",
{
rivalUserId
},
function(data,status){
window.location.replace("/game/ongeki/")
})
});
}
function changeVersion(sel){
$(document).ready(function () {
$.post("/game/ongeki/version.change",
{
version: sel.value
},
function(data,status){
window.location.replace("/game/ongeki/")
})
});
}

View File

@ -0,0 +1,83 @@
{% extends "core/frontend/index.jinja" %}
{% block content %}
{% if sesh is defined and sesh["userId"] > 0 %}
<br>
<br>
<br>
<div class="container">
<div class="row">
<h2> Profile </h2>
<h3>Version:
<select name="version" id="version" onChange="changeVersion(this)">
{% for ver in version_list %}
<option value={{loop.index0}} {{ "selected" if loop.index0==version else "" }} >{{ver}}</option>
{% endfor %}
</select>
</h3>
<hr>
</div>
<div class="row">
<div class="col">
<h2> Name: {{ profile_data.userName if profile_data.userName is defined else "Profile not found" }}</h2>
</div>
<div class="col">
<h4> ID: {{ profile_data.user if profile_data.user is defined else 'Profile not found' }}</h4>
</div>
</div>
<hr>
<div class="row">
<h2> Rivals <button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#rival_add">Add</button></h2>
</div>
<div class="row">
<table class="table table-dark table-hover">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
{% for rival in rival_info%}
<tr id="{{rival.rivalUserId}}">
<td>{{rival.rivalUserId}}</td>
<td>{{rival.rivalUserName}}</td>
<td><button class="btn-danger btn btn-sm" onclick="deleteRival({{rival.rivalUserId}})">Delete</button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="modal fade" id="rival_add" tabindex="-1" aria-labelledby="card_add_label" data-bs-theme="dark" aria-hidden="true">
<form id="rival" action="/game/ongeki/rival.add" method="post">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Note:<br>
Please use the ID show next to your name in the profile page.
<br>
<label for="rivalUserId">ID:&nbsp;</label><input form="rival" id="rivalUserId" name="rivalUserId" maxlength="5" type="number" required>
</div>
<div class="modal-footer">
<input type=submit class="btn btn-primary" type="button" form="rival" value="Add">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</form>
</div>
</div>
<script>
{% include 'titles/ongeki/frontend/js/ongeki_scripts.js' %}
</script>
{% else %}
<h2>Not Currently Logged In</h2>
{% endif %}
{% endblock content %}

View File

@ -3,7 +3,7 @@ from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, an
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger
from sqlalchemy.engine.base import Connection from sqlalchemy.engine.base import Connection
from sqlalchemy.schema import ForeignKey from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql import func, select from sqlalchemy.sql import func, select, delete
from sqlalchemy.engine import Row from sqlalchemy.engine import Row
from sqlalchemy.dialects.mysql import insert from sqlalchemy.dialects.mysql import insert
@ -269,7 +269,7 @@ class OngekiProfileData(BaseData):
return None return None
return row["userName"] return row["userName"]
def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]: def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]:
sql = ( sql = (
select([profile, option]) select([profile, option])
@ -499,7 +499,7 @@ class OngekiProfileData(BaseData):
def put_rival(self, aime_id: int, rival_id: int) -> Optional[int]: def put_rival(self, aime_id: int, rival_id: int) -> Optional[int]:
sql = insert(rival).values(user=aime_id, rivalUserId=rival_id) sql = insert(rival).values(user=aime_id, rivalUserId=rival_id)
conflict = sql.on_duplicate_key_update(rival=rival_id) conflict = sql.on_duplicate_key_update(rivalUserId=rival_id)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: if result is None:
@ -508,3 +508,10 @@ class OngekiProfileData(BaseData):
) )
return None return None
return result.lastrowid return result.lastrowid
def delete_rival(self, aime_id: int, rival_id: int) -> Optional[int]:
sql = delete(rival).where(rival.c.user==aime_id, rival.c.rivalUserId==rival_id)
result = self.execute(sql)
if result is None:
self.logger.error(f"delete_rival: failed to delete! aime_id: {aime_id}, rival_id: {rival_id}")
else:
return result.rowcount