forked from Hay1tsme/artemis
Compare commits
11 Commits
d439483aec
...
00d3b6e69a
Author | SHA1 | Date | |
---|---|---|---|
|
00d3b6e69a | ||
6a43e0eada | |||
d7e8c9b490 | |||
c65c71e89e | |||
15cf56ed9b | |||
51a47e51d9 | |||
|
58bdeb9290 | ||
9b5283d389 | |||
|
97e1d5da05 | ||
|
eb275062a1 | ||
|
e0e63a9a13 |
@ -1,6 +1,10 @@
|
||||
# Changelog
|
||||
Documenting updates to ARTEMiS, to be updated every time the master branch is pushed to.
|
||||
|
||||
## 20240630
|
||||
### DIVA
|
||||
+ Added configurable festa options'
|
||||
|
||||
## 20240629
|
||||
### CHUNITHM
|
||||
+ Add team points
|
||||
|
@ -1,16 +1,18 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class MainboardPlatformCodes:
|
||||
RINGEDGE = "AALE"
|
||||
RINGWIDE = "AAML"
|
||||
NU = "AAVE"
|
||||
NUSX = "AAWE"
|
||||
ALLS_UX = "ACAE"
|
||||
ALLS_HX = "ACAX"
|
||||
class MainboardPlatformCodes(Enum):
|
||||
RINGEDGE = "AAL"
|
||||
RINGEDGE2 = "AAS"
|
||||
RINGWIDE = "AAM"
|
||||
NU = "AAV"
|
||||
NUSX = "AAW"
|
||||
ALLS = "ACA"
|
||||
#ALLS_UX = "ACAE"
|
||||
#ALLS_HX = "ACAX"
|
||||
|
||||
|
||||
class MainboardRevisions:
|
||||
class MainboardRevisions(Enum):
|
||||
RINGEDGE = 1
|
||||
RINGEDGE2 = 2
|
||||
|
||||
@ -29,11 +31,10 @@ class MainboardRevisions:
|
||||
ALLS_HX2 = 12
|
||||
|
||||
|
||||
class KeychipPlatformsCodes:
|
||||
RING = "A72E"
|
||||
NU = ("A60E", "A60E", "A60E")
|
||||
NUSX = ("A61X", "A69X")
|
||||
ALLS = "A63E"
|
||||
class KeychipPlatformsCodes(Enum):
|
||||
RING = "72"
|
||||
NU = ("60", "61", "69")
|
||||
ALLS = "63"
|
||||
|
||||
|
||||
class AllnetCountryCode(Enum):
|
||||
|
28
core/data/alembic/versions/5ea73f89d982_card_add_memo.py
Normal file
28
core/data/alembic/versions/5ea73f89d982_card_add_memo.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""card_add_memo
|
||||
|
||||
Revision ID: 5ea73f89d982
|
||||
Revises: 745448d83696
|
||||
Create Date: 2024-07-06 22:46:56.992152
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5ea73f89d982'
|
||||
down_revision = '745448d83696'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('aime_card', sa.Column('memo', sa.VARCHAR(length=16), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('aime_card', 'memo')
|
||||
# ### end Alembic commands ###
|
@ -206,17 +206,6 @@ class ArcadeData(BaseData):
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
def format_serial( # TODO: Actual serial stuff
|
||||
self, platform_code: str, platform_rev: int, serial_num: int, append: int = 8888
|
||||
) -> str:
|
||||
return f"{platform_code}{platform_rev:02d}A{serial_num:04d}{append:04d}" # 0x41 = A, 0x52 = R
|
||||
|
||||
def validate_keychip_format(self, serial: str) -> bool:
|
||||
if re.fullmatch(r"^A[0-9]{2}[E|X][-]?[0-9]{2}[A-HJ-NP-Z][0-9]{4}([0-9]{4})?$", serial) is None:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
async def get_arcade_by_name(self, name: str) -> Optional[List[Row]]:
|
||||
sql = arcade.select(or_(arcade.c.name.like(f"%{name}%"), arcade.c.nickname.like(f"%{name}%")))
|
||||
result = await self.execute(sql)
|
||||
@ -230,3 +219,53 @@ class ArcadeData(BaseData):
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
async def get_num_generated_keychips(self) -> Optional[int]:
|
||||
result = await self.execute(select(func.count("serial LIKE 'A69A%'")).select_from(machine))
|
||||
if result:
|
||||
return result.fetchone()['count_1']
|
||||
self.logger.error("Failed to count machine serials that start with A69A!")
|
||||
|
||||
def format_serial(
|
||||
self, platform_code: str, platform_rev: int, serial_letter: str, serial_num: int, append: int, dash: bool = False
|
||||
) -> str:
|
||||
return f"{platform_code}{'-' if dash else ''}{platform_rev:02d}{serial_letter}{serial_num:04d}{append:04d}"
|
||||
|
||||
def validate_keychip_format(self, serial: str) -> bool:
|
||||
# For the 2nd letter, E and X are the only "real" values that have been observed
|
||||
if re.fullmatch(r"^A[0-9]{2}[A-Z][-]?[0-9]{2}[A-HJ-NP-Z][0-9]{4}([0-9]{4})?$", serial) is None:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# Thanks bottersnike!
|
||||
def get_keychip_suffix(self, year: int, month: int) -> str:
|
||||
assert year > 1957
|
||||
assert 1 <= month <= 12
|
||||
|
||||
year -= 1957
|
||||
# Jan/Feb/Mar are from the previous tax year
|
||||
if month < 4:
|
||||
year -= 1
|
||||
assert year >= 1 and year <= 99
|
||||
|
||||
month = ((month - 1) + 9) % 12 # Offset so April=0
|
||||
return f"{year:02}{month // 6:01}{month % 6 + 1:01}"
|
||||
|
||||
|
||||
def parse_keychip_suffix(self, suffix: str) -> tuple[int, int]:
|
||||
year = int(suffix[0:2])
|
||||
half = int(suffix[2])
|
||||
assert half in (0, 1)
|
||||
period = int(suffix[3])
|
||||
assert period in (1, 2, 3, 4, 5, 6)
|
||||
|
||||
month = half * 6 + (period - 1)
|
||||
month = ((month + 3) % 12) + 1 # Offset so Jan=1
|
||||
|
||||
# Jan/Feb/Mar are from the previous tax year
|
||||
if month < 4:
|
||||
year += 1
|
||||
year += 1957
|
||||
|
||||
return (year, month)
|
||||
|
@ -1,6 +1,6 @@
|
||||
from typing import Dict, List, Optional
|
||||
from sqlalchemy import Table, Column, UniqueConstraint
|
||||
from sqlalchemy.types import Integer, String, Boolean, TIMESTAMP, BIGINT
|
||||
from sqlalchemy.types import Integer, String, Boolean, TIMESTAMP, BIGINT, VARCHAR
|
||||
from sqlalchemy.sql.schema import ForeignKey
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.engine import Row
|
||||
@ -19,6 +19,7 @@ aime_card = Table(
|
||||
Column("last_login_date", TIMESTAMP, onupdate=func.now()),
|
||||
Column("is_locked", Boolean, server_default="0"),
|
||||
Column("is_banned", Boolean, server_default="0"),
|
||||
Column("memo", VARCHAR(16)),
|
||||
UniqueConstraint("user", "access_code", name="aime_card_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
@ -148,6 +149,11 @@ class CardData(BaseData):
|
||||
if not result:
|
||||
self.logger.error(f"Failed to change card access code from {old_ac} to {new_ac}")
|
||||
|
||||
async def set_memo_by_access_code(self, access_code: str, memo: str) -> None:
|
||||
result = await self.execute(aime_card.update(aime_card.c.access_code == access_code).values(memo=memo))
|
||||
if not result:
|
||||
self.logger.error(f"Failed to add memo to card {access_code}")
|
||||
|
||||
def to_access_code(self, luid: str) -> str:
|
||||
"""
|
||||
Given a felica cards internal 16 hex character luid, convert it to a 0-padded 20 digit access code as a string
|
||||
|
@ -476,10 +476,10 @@ class FE_User(FE_Base):
|
||||
card_data.append({
|
||||
'access_code': ac,
|
||||
'status': status,
|
||||
'chip_id': "", #None if c['chip_id'] is None else f"{c['chip_id']:X}",
|
||||
'idm': "",
|
||||
'chip_id': c['chip_id'],
|
||||
'idm': c['idm'],
|
||||
'type': c_type,
|
||||
"memo": ""
|
||||
"memo": c['memo']
|
||||
})
|
||||
|
||||
if "e" in request.query_params:
|
||||
@ -516,7 +516,42 @@ class FE_User(FE_Base):
|
||||
return resp
|
||||
|
||||
async def edit_card(self, request: Request) -> RedirectResponse:
|
||||
return RedirectResponse("/user/", 303)
|
||||
frm = await request.form()
|
||||
usr_sesh = self.validate_session(request)
|
||||
if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.USERMOD):
|
||||
return RedirectResponse("/gate/", 303)
|
||||
|
||||
frm = await request.form()
|
||||
ac = frm.get("add_access_code", None)
|
||||
if not ac:
|
||||
return RedirectResponse("/user/?e=999", 303)
|
||||
|
||||
card = await self.data.card.get_card_by_access_code(ac)
|
||||
if not card:
|
||||
return RedirectResponse("/user/?e=2", 303)
|
||||
|
||||
if card['user'] != usr_sesh.user_id and not self.test_perm_minimum(usr_sesh.permissions, PermissionOffset.USERMOD):
|
||||
return RedirectResponse("/user/?e=11", 303)
|
||||
|
||||
if frm.get("add_memo", None):
|
||||
memo = frm.get("add_memo")
|
||||
if len(memo) > 16 or len(memo) == 0:
|
||||
return RedirectResponse("/user/?e=4", 303)
|
||||
await self.data.card.set_memo_by_access_code(ac, memo)
|
||||
|
||||
if frm.get("add_felica_idm", None):
|
||||
idm = frm.get('add_felica_idm')
|
||||
if not all(c in string.hexdigits for c in idm):
|
||||
return RedirectResponse("/user/?e=4", 303)
|
||||
await self.data.card.set_idm_by_access_code(ac, idm)
|
||||
|
||||
if frm.get("add_mifare_chip_id", None):
|
||||
chip_id: str = frm.get('add_mifare_chip_id')
|
||||
if not all(c in string.hexdigits for c in idm):
|
||||
return RedirectResponse("/user/?e=4", 303)
|
||||
await self.data.card.set_chip_id_by_access_code(ac, int(chip_id, 16))
|
||||
|
||||
return RedirectResponse("/user/?s=4", 303)
|
||||
|
||||
async def add_card(self, request: Request) -> RedirectResponse:
|
||||
return RedirectResponse("/user/", 303)
|
||||
@ -791,14 +826,19 @@ class FE_System(FE_Base):
|
||||
return RedirectResponse("/sys/?e=4", 303)
|
||||
|
||||
if not serial:
|
||||
serial = self.data.arcade.format_serial("A69E", 1, random.randint(1, 9999))
|
||||
append = self.data.arcade.get_keychip_suffix(datetime.now().year, datetime.now().month)
|
||||
generated = await self.data.arcade.get_num_generated_keychips()
|
||||
if not generated:
|
||||
generated = 0
|
||||
serial = self.data.arcade.format_serial("A69A", 1, "A", generated + 1, int(append))
|
||||
serial_dash = self.data.arcade.format_serial("A69A", 1, "A", generated + 1, int(append), True)
|
||||
|
||||
cab_id = await self.data.arcade.create_machine(int(shopid), serial, None, game_code if game_code else None)
|
||||
|
||||
return Response(template.render(
|
||||
title=f"{self.core_config.server.name} | System",
|
||||
sesh=vars(usr_sesh),
|
||||
cabadd={"id": cab_id, "serial": serial},
|
||||
cabadd={"id": cab_id, "serial": serial_dash},
|
||||
), media_type="text/html; charset=utf-8")
|
||||
|
||||
async def render_logs(self, request: Request):
|
||||
|
@ -46,7 +46,11 @@ var per_page = 0;
|
||||
var page = 0;
|
||||
|
||||
function update_tbl() {
|
||||
if (TBL_DATA.length == 0) { return; }
|
||||
if (TBL_DATA.length == 0) {
|
||||
document.getElementById("btn_next").disabled = true;
|
||||
document.getElementById("btn_prev").disabled = true;
|
||||
return;
|
||||
}
|
||||
var tbl = document.getElementById("tbl_events");
|
||||
|
||||
for (var i = 0; i < per_page; i++) {
|
||||
@ -183,7 +187,7 @@ function chg_page(num) {
|
||||
document.getElementById("btn_prev").disabled = true;
|
||||
return;
|
||||
} else if (page == 0) {
|
||||
document.getElementById("btn_next").disabled = false;
|
||||
document.getElementById("btn_next").disabled = TBL_DATA.length == 0;
|
||||
document.getElementById("btn_prev").disabled = true;
|
||||
} else {
|
||||
document.getElementById("btn_next").disabled = false;
|
||||
|
@ -49,11 +49,14 @@ function prep_edit_form(access_code, chip_id, idm, card_type, u_memo) {
|
||||
fidm.value = idm;
|
||||
memo.value = u_memo;
|
||||
|
||||
if (card_type == "AmusementIC") {
|
||||
if (access_code.startsWith("3") || access_code.startsWith("010")) {
|
||||
cid.disabled = false;
|
||||
fidm.disabled = true;
|
||||
} else if (access_code.startsWith("5")) {
|
||||
cid.disabled = true;
|
||||
fidm.disabled = false;
|
||||
} else {
|
||||
cid.disabled = false;
|
||||
cid.disabled = true;
|
||||
fidm.disabled = true;
|
||||
}
|
||||
}
|
||||
@ -91,9 +94,14 @@ Card added successfully
|
||||
</form>
|
||||
<br>
|
||||
</div>
|
||||
{% if success is defined and success == 4 %}
|
||||
<div style="background-color: #00AA00; padding: 20px; margin-bottom: 10px; width: 15%;">
|
||||
Update successful
|
||||
</div>
|
||||
{% endif %}
|
||||
<ul style="font-size: 20px;">
|
||||
{% for c in cards %}
|
||||
<li>{{ c.access_code }} ({{ c.type}}): {{ c.status }} <button onclick="prep_edit_form('{{ c.access_code }}', '{{ c.chip_id}}', '{{ c.idm }}', '{{ c.type }}', '{{ c.memo }}')" data-bs-toggle="modal" data-bs-target="#card_edit" class="btn btn-secondary" id="btn_edit_card_{{ c.access_code }}">Edit</button> {% if c.status == 'Active'%}<button class="btn-warning btn">Lock</button>{% elif c.status == 'Locked' %}<button class="btn-warning btn">Unlock</button>{% endif %} <button class="btn-danger btn">Delete</button></li>
|
||||
<li>{{ c.access_code }} ({{ c.type if c.memo is none or not c.memo else c.memo }}): {{ c.status }} <button onclick="prep_edit_form('{{ c.access_code }}', '{{ c.chip_id}}', '{{ c.idm }}', '{{ c.type }}', '{{ c.memo }}')" data-bs-toggle="modal" data-bs-target="#card_edit" class="btn btn-secondary" id="btn_edit_card_{{ c.access_code }}">Edit</button> {% if c.status == 'Active'%}<button class="btn-warning btn">Lock</button>{% elif c.status == 'Locked' %}<button class="btn-warning btn">Unlock</button>{% endif %} <button class="btn-danger btn" {{ "disabled" if cards|length == 1 else ""}}>Delete</button></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
@ -42,6 +42,8 @@ if __name__ == "__main__":
|
||||
|
||||
data = Data(cfg)
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
if args.action == "create":
|
||||
data.create_database()
|
||||
|
||||
@ -55,19 +57,15 @@ if __name__ == "__main__":
|
||||
data.schema_downgrade(args.version)
|
||||
|
||||
elif args.action == "create-owner":
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(data.create_owner(args.email, args.access_code))
|
||||
|
||||
elif args.action == "migrate":
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(data.migrate())
|
||||
|
||||
elif args.action == "create-revision":
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(data.create_revision(args.message))
|
||||
|
||||
elif args.action == "create-autorevision":
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(data.create_revision_auto(args.message))
|
||||
|
||||
else:
|
||||
|
@ -269,10 +269,14 @@ the Shop, Modules and Customizations.
|
||||
|
||||
Config file is located in `config/diva.yaml`.
|
||||
|
||||
| Option | Info |
|
||||
| -------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `unlock_all_modules` | Unlocks all modules (costumes) by default, if set to `False` all modules need to be purchased |
|
||||
| `unlock_all_items` | Unlocks all items (customizations) by default, if set to `False` all items need to be purchased |
|
||||
| Option | Info |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| `festa_enable` | Enable or disable the ingame festa |
|
||||
| `festa_add_VP` | Set the extra VP you get when clearing a song, if festa is not enabled no extra VP will be given |
|
||||
| `festa_multiply_VP` | Multiplier for festa add VP |
|
||||
| `festa_end_time` | Set the date time for when festa will end and not show up in game anymore |
|
||||
| `unlock_all_modules` | Unlocks all modules (costumes) by default, if set to `False` all modules need to be purchased |
|
||||
| `unlock_all_items` | Unlocks all items (customizations) by default, if set to `False` all items need to be purchased |
|
||||
|
||||
### Custom PV Lists (databanks)
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
server:
|
||||
enable: True
|
||||
loglevel: "info"
|
||||
festa_enable: True
|
||||
festa_add_VP: "20,5"
|
||||
festa_multiply_VP: "1,2"
|
||||
festa_end_time: "2029-01-01 00:00:00.0"
|
||||
|
||||
mods:
|
||||
unlock_all_modules: True
|
||||
|
@ -234,11 +234,11 @@ class ChuniFrontend(FE_Base):
|
||||
if usr_sesh.user_id > 0:
|
||||
form_data = await request.form()
|
||||
chunithm_version = form_data.get("version")
|
||||
self.logger.info(f"version change to: {chunithm_version}")
|
||||
self.logger.debug(f"version change to: {chunithm_version}")
|
||||
if(chunithm_version.isdigit()):
|
||||
usr_sesh.chunithm_version=int(chunithm_version)
|
||||
encoded_sesh = self.encode_session(usr_sesh)
|
||||
self.logger.info(f"Created session with JWT {encoded_sesh}")
|
||||
self.logger.debug(f"Created session with JWT {encoded_sesh}")
|
||||
resp = RedirectResponse("/game/chuni/", 303)
|
||||
resp.set_cookie("ARTEMIS_SESH", encoded_sesh)
|
||||
return resp
|
||||
|
@ -264,6 +264,11 @@ class DivaBase:
|
||||
return response
|
||||
|
||||
async def handle_festa_info_request(self, data: Dict) -> Dict:
|
||||
if self.game_config.server.festa_enable:
|
||||
festa_end_time = self.game_config.server.festa_end_time
|
||||
else:
|
||||
festa_end_time = (datetime.datetime.now() - datetime.timedelta(days=365)).strftime("%Y-%m-%d %H:%M:%S") + ".0"
|
||||
|
||||
encoded = "&"
|
||||
params = {
|
||||
"fi_id": "1,2",
|
||||
@ -273,10 +278,10 @@ class DivaBase:
|
||||
"fi_difficulty": "-1,-1",
|
||||
"fi_pv_id_lst": "ALL,ALL",
|
||||
"fi_attr": "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
|
||||
"fi_add_vp": "20,5",
|
||||
"fi_mul_vp": "1,2",
|
||||
"fi_add_vp": f"{self.game_config.server.festa_add_VP}",
|
||||
"fi_mul_vp": f"{self.game_config.server.festa_multiply_VP}",
|
||||
"fi_st": "2019-01-01 00:00:00.0,2019-01-01 00:00:00.0",
|
||||
"fi_et": "2029-01-01 00:00:00.0,2029-01-01 00:00:00.0",
|
||||
"fi_et": f"{festa_end_time},{festa_end_time}",
|
||||
"fi_lut": "{self.time_lut}",
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,29 @@ class DivaServerConfig:
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def festa_enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "diva", "server", "festa_enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def festa_add_VP(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "diva", "server", "festa_add_VP", default="20,5"
|
||||
)
|
||||
|
||||
@property
|
||||
def festa_multiply_VP(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "diva", "server", "festa_multiply_VP", default="1,2"
|
||||
)
|
||||
|
||||
@property
|
||||
def festa_end_time(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "diva", "server", "festa_end_time", default="2029-01-01 00:00:00.0"
|
||||
)
|
||||
|
||||
class DivaModsConfig:
|
||||
def __init__(self, parent_config: "DivaConfig") -> None:
|
||||
|
@ -5,8 +5,9 @@ from starlette.responses import Response, RedirectResponse
|
||||
from os import path
|
||||
import yaml
|
||||
import jinja2
|
||||
from datetime import datetime
|
||||
|
||||
from core.frontend import FE_Base, UserSession
|
||||
from core.frontend import FE_Base, UserSession, PermissionOffset
|
||||
from core.config import CoreConfig
|
||||
from .database import Mai2Data
|
||||
from .config import Mai2Config
|
||||
@ -32,6 +33,12 @@ class Mai2Frontend(FE_Base):
|
||||
Route("/", self.render_GET_playlog, methods=['GET']),
|
||||
Route("/{index}", self.render_GET_playlog, methods=['GET']),
|
||||
]),
|
||||
Mount("/events", routes=[
|
||||
Route("/", self.render_events, methods=['GET']),
|
||||
Route("/{event_id:int}", self.render_event_edit, methods=['GET']),
|
||||
Route("/update", self.update_event, methods=['POST']),
|
||||
Route("/version.change", self.version_change, methods=['POST']),
|
||||
]),
|
||||
Route("/update.name", self.update_name, methods=['POST']),
|
||||
Route("/version.change", self.version_change, methods=['POST']),
|
||||
]
|
||||
@ -43,13 +50,15 @@ class Mai2Frontend(FE_Base):
|
||||
usr_sesh = self.validate_session(request)
|
||||
if not usr_sesh:
|
||||
usr_sesh = UserSession()
|
||||
|
||||
incoming_ver = usr_sesh.maimai_version
|
||||
|
||||
if usr_sesh.user_id > 0:
|
||||
versions = await self.data.profile.get_all_profile_versions(usr_sesh.user_id)
|
||||
profile = []
|
||||
if versions:
|
||||
# maimai_version is -1 means it is not initialized yet, select a default version from existing.
|
||||
if usr_sesh.maimai_version < 0:
|
||||
if incoming_ver < 0:
|
||||
usr_sesh.maimai_version = versions[0]['version']
|
||||
profile = await self.data.profile.get_profile_detail(usr_sesh.user_id, usr_sesh.maimai_version)
|
||||
versions = [x['version'] for x in versions]
|
||||
@ -65,7 +74,7 @@ class Mai2Frontend(FE_Base):
|
||||
cur_version=usr_sesh.maimai_version
|
||||
), media_type="text/html; charset=utf-8")
|
||||
|
||||
if usr_sesh.maimai_version >= 0:
|
||||
if incoming_ver < 0:
|
||||
encoded_sesh = self.encode_session(usr_sesh)
|
||||
resp.delete_cookie("ARTEMIS_SESH")
|
||||
resp.set_cookie("ARTEMIS_SESH", encoded_sesh)
|
||||
@ -80,12 +89,10 @@ class Mai2Frontend(FE_Base):
|
||||
)
|
||||
usr_sesh = self.validate_session(request)
|
||||
if not usr_sesh:
|
||||
print("wtf")
|
||||
usr_sesh = UserSession()
|
||||
|
||||
if usr_sesh.user_id > 0:
|
||||
if usr_sesh.maimai_version < 0:
|
||||
print(usr_sesh.maimai_version)
|
||||
return RedirectResponse("/game/mai2/", 303)
|
||||
path_index = request.path_params.get('index')
|
||||
if not path_index or int(path_index) < 1:
|
||||
@ -175,6 +182,11 @@ class Mai2Frontend(FE_Base):
|
||||
if not usr_sesh:
|
||||
usr_sesh = UserSession()
|
||||
|
||||
if "/events/" in request.url.path:
|
||||
resp = RedirectResponse("/game/mai2/events/", 303)
|
||||
else:
|
||||
resp = RedirectResponse("/game/mai2/", 303)
|
||||
|
||||
if usr_sesh.user_id > 0:
|
||||
form_data = await request.form()
|
||||
maimai_version = form_data.get("version")
|
||||
@ -182,9 +194,108 @@ class Mai2Frontend(FE_Base):
|
||||
if(maimai_version.isdigit()):
|
||||
usr_sesh.maimai_version=int(maimai_version)
|
||||
encoded_sesh = self.encode_session(usr_sesh)
|
||||
self.logger.info(f"Created session with JWT {encoded_sesh}")
|
||||
resp = RedirectResponse("/game/mai2/", 303)
|
||||
self.logger.debug(f"Created session with JWT {encoded_sesh}")
|
||||
resp.set_cookie("ARTEMIS_SESH", encoded_sesh)
|
||||
return resp
|
||||
else:
|
||||
return RedirectResponse("/gate/", 303)
|
||||
return RedirectResponse("/gate/", 303)
|
||||
|
||||
async def render_events(self, request: Request) -> Response:
|
||||
usr_sesh = self.validate_session(request)
|
||||
if not usr_sesh:
|
||||
return RedirectResponse("/gate/", 303)
|
||||
|
||||
if not self.test_perm(usr_sesh.permissions, PermissionOffset.SYSADMIN):
|
||||
return RedirectResponse("/game/mai2/", 303)
|
||||
|
||||
template = self.environment.get_template(
|
||||
"titles/mai2/templates/events/mai2_events.jinja"
|
||||
)
|
||||
|
||||
incoming_ver = usr_sesh.maimai_version
|
||||
evts = []
|
||||
|
||||
if incoming_ver < 0:
|
||||
usr_sesh.maimai_version = Mai2Constants.VER_MAIMAI_DX
|
||||
|
||||
event_list = await self.data.static.get_game_events(usr_sesh.maimai_version)
|
||||
self.logger.info(f"Get events for v{usr_sesh.maimai_version}")
|
||||
|
||||
for event in event_list:
|
||||
evts.append({
|
||||
"id": event['id'],
|
||||
"version": event['version'],
|
||||
"eventId": event['eventId'],
|
||||
"eventType": event['type'],
|
||||
"name": event['name'],
|
||||
"startDate": event['startDate'].strftime("%x %X"),
|
||||
"enabled": "true" if event['enabled'] else "false",
|
||||
})
|
||||
|
||||
resp = Response(template.render(
|
||||
title=f"{self.core_config.server.name} | {self.nav_name} Events",
|
||||
game_list=self.environment.globals["game_list"],
|
||||
sesh=vars(usr_sesh),
|
||||
version_list=Mai2Constants.VERSION_STRING,
|
||||
events=evts
|
||||
), media_type="text/html; charset=utf-8")
|
||||
|
||||
if incoming_ver < 0:
|
||||
encoded_sesh = self.encode_session(usr_sesh)
|
||||
resp.delete_cookie("ARTEMIS_SESH")
|
||||
resp.set_cookie("ARTEMIS_SESH", encoded_sesh)
|
||||
|
||||
return resp
|
||||
|
||||
async def render_event_edit(self, request: Request) -> Response:
|
||||
usr_sesh = self.validate_session(request)
|
||||
if not usr_sesh:
|
||||
return RedirectResponse("/gate/", 303)
|
||||
|
||||
if not self.test_perm(usr_sesh.permissions, PermissionOffset.SYSADMIN):
|
||||
return RedirectResponse("/game/mai2/", 303)
|
||||
|
||||
template = self.environment.get_template(
|
||||
"titles/mai2/templates/events/mai2_event_edit.jinja"
|
||||
)
|
||||
|
||||
evt_id = request.path_params.get("event_id")
|
||||
|
||||
event_id = await self.data.static.get_event_by_id(evt_id)
|
||||
if not event_id:
|
||||
return RedirectResponse("/game/mai2/events/", 303)
|
||||
|
||||
return Response(template.render(
|
||||
title=f"{self.core_config.server.name} | {self.nav_name} Edit Event {evt_id}",
|
||||
game_list=self.environment.globals["game_list"],
|
||||
sesh=vars(usr_sesh),
|
||||
user_id=usr_sesh.user_id,
|
||||
version_list=Mai2Constants.VERSION_STRING,
|
||||
cur_version=usr_sesh.maimai_version,
|
||||
event=event_id._asdict()
|
||||
), media_type="text/html; charset=utf-8")
|
||||
|
||||
async def update_event(self, request: Request) -> RedirectResponse:
|
||||
usr_sesh = self.validate_session(request)
|
||||
if not usr_sesh:
|
||||
return RedirectResponse("/gate/", 303)
|
||||
|
||||
if not self.test_perm(usr_sesh.permissions, PermissionOffset.SYSADMIN):
|
||||
return RedirectResponse("/game/mai2/", 303)
|
||||
|
||||
form_data = await request.form()
|
||||
print(form_data)
|
||||
event_id: int = form_data.get("evtId", None)
|
||||
new_enabled: bool = bool(form_data.get("evtEnabled", False))
|
||||
try:
|
||||
new_start_date: datetime = datetime.strptime(form_data.get("evtStart", None), "%Y-%m-%dT%H:%M:%S")
|
||||
except:
|
||||
new_start_date = None
|
||||
|
||||
print(f"{event_id} {new_enabled} {new_start_date}")
|
||||
if event_id is None or new_start_date is None:
|
||||
return RedirectResponse("/game/mai2/events/?e=4", 303)
|
||||
|
||||
await self.data.static.update_event_by_id(int(event_id), new_enabled, new_start_date)
|
||||
|
||||
return RedirectResponse("/game/mai2/events/?s=1", 303)
|
||||
|
@ -7,6 +7,7 @@ from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from datetime import datetime
|
||||
|
||||
event = Table(
|
||||
"mai2_static_event",
|
||||
@ -248,3 +249,18 @@ class Mai2StaticData(BaseData):
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
async def get_event_by_id(self, table_id: int) -> Optional[Row]:
|
||||
result = await self.execute(event.select(event.c.id == table_id))
|
||||
if result:
|
||||
return result.fetchone()
|
||||
|
||||
async def get_events_by_event_id(self, event_id: int) -> Optional[List[Row]]:
|
||||
result = await self.execute(event.select(event.c.eventId == event_id))
|
||||
if result:
|
||||
return result.fetchall()
|
||||
|
||||
async def update_event_by_id(self, table_id: int, is_enable: bool, start_date: datetime) -> None:
|
||||
result = await self.execute(event.update(event.c.id == table_id).values(enabled=is_enable, startDate = start_date))
|
||||
if not result:
|
||||
self.logger.error(f"Failed to update event {table_id} - {is_enable} {start_date}")
|
||||
|
16
titles/mai2/templates/events/mai2_event_edit.jinja
Normal file
16
titles/mai2/templates/events/mai2_event_edit.jinja
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends "core/templates/index.jinja" %}
|
||||
{% block content %}
|
||||
<!-- TODO: This can probably just be a modal on the main event page -->
|
||||
<form id="frmEvent" method="post" action="/game/mai2/events/update">
|
||||
<h3>Event {{ event.eventId }} for {{ version_list[event.version] }}: {{ event.name }}</h3>
|
||||
<input type="hidden" readonly value="{{event.id}}" id="evtId" name="evtId">
|
||||
<label for="evtEnabled" class="form-label">Enabled</label><br>
|
||||
<input class="form-check-input" type="checkbox" {{ 'checked' if event.enabled else ''}} id="evtEnabled" name="evtEnabled"><br><br>
|
||||
|
||||
<label for="evtStart" class="form-label">Start Date</label><br>
|
||||
<input class="form-input" type="datetime-local" id="evtStart" name="evtStart" value="{{ event.startDate }}"><br><br>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
<button type="cancel" class="btn btn-danger">Delete</button>
|
||||
</form>
|
||||
{% endblock content %}
|
156
titles/mai2/templates/events/mai2_events.jinja
Normal file
156
titles/mai2/templates/events/mai2_events.jinja
Normal file
@ -0,0 +1,156 @@
|
||||
{% extends "core/templates/index.jinja" %}
|
||||
{% block content %}
|
||||
<h1>Events</h1>
|
||||
<form id="verForm" method="POST" action="/game/mai2/events/version.change">
|
||||
<select id="version" name="version" onchange="updateVer()" form="verForm">
|
||||
{% for ver in range(version_list|length) %}
|
||||
<option value="{{ver}}">{{ version_list[ver] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
<table class="table table-dark table-striped-columns" id="tbl_events">
|
||||
<caption>Viewing all events</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Version</th>
|
||||
<th>Event ID</th>
|
||||
<th>Event Type</th>
|
||||
<th>Name</th>
|
||||
<th>Start Date</th>
|
||||
<th>Enabled</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% if events is not defined or events|length == 0 %}
|
||||
<tr>
|
||||
<td colspan="11" style="text-align:center"><i>No Events</i></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
<div id="div_tbl_ctrl">
|
||||
<select id="sel_per_page" onchange="update_tbl()">
|
||||
<option value="10" selected>10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
|
||||
<button class="btn btn-primary" id="btn_prev" disabled onclick="chg_page(-1)"><<</button>
|
||||
<button class="btn btn-primary" id="btn_next" onclick="chg_page(1)">>></button>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
{% if events is defined %}
|
||||
const TBL_DATA = {{events}};
|
||||
{% else %}
|
||||
const TBL_DATA = [];
|
||||
{% endif %}
|
||||
|
||||
document.getElementById("version").value = {{ sesh.maimai_version }};
|
||||
|
||||
var per_page = 0;
|
||||
var page = 0;
|
||||
|
||||
function updateVer() {
|
||||
var sel = document.getElementById("version");
|
||||
var frm = document.getElementById("verForm");
|
||||
|
||||
if (sel.value == {{ sesh.maimai_version }}) {
|
||||
return;
|
||||
}
|
||||
|
||||
frm.submit();
|
||||
}
|
||||
|
||||
function update_tbl() {
|
||||
if (TBL_DATA.length == 0) { return; }
|
||||
var tbl = document.getElementById("tbl_events");
|
||||
|
||||
for (var i = 0; i < per_page; i++) {
|
||||
try{
|
||||
tbl.deleteRow(1);
|
||||
} catch {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
per_page = document.getElementById("sel_per_page").value;
|
||||
|
||||
if (per_page >= TBL_DATA.length) {
|
||||
page = 0;
|
||||
document.getElementById("btn_next").disabled = true;
|
||||
document.getElementById("btn_prev").disabled = true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < per_page; i++) {
|
||||
let off = (page * per_page) + i;
|
||||
if (off >= TBL_DATA.length) {
|
||||
if (page != 0) {
|
||||
document.getElementById("btn_next").disabled = true;
|
||||
document.getElementById("btn_prev").disabled = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
var data = TBL_DATA[off];
|
||||
var row = tbl.insertRow(i + 1);
|
||||
|
||||
var cell_id = row.insertCell(0);
|
||||
cell_id.innerHTML = data.id;
|
||||
|
||||
var cell_ver = row.insertCell(1);
|
||||
cell_ver.innerHTML = data.version;
|
||||
|
||||
var cell_evtid = row.insertCell(2);
|
||||
cell_evtid.innerHTML = data.eventId;
|
||||
|
||||
var cell_evttype = row.insertCell(3);
|
||||
cell_evttype.innerHTML = data.eventType;
|
||||
|
||||
var cell_name = row.insertCell(4);
|
||||
cell_name.innerHTML = data.name;
|
||||
|
||||
var cell_date = row.insertCell(5);
|
||||
cell_date.innerHTML = data.startDate;
|
||||
|
||||
var call_enabled = row.insertCell(6);
|
||||
if (data.enabled === "true")
|
||||
call_enabled.innerHTML = "✔"
|
||||
else
|
||||
call_enabled.innerHTML = "✖"
|
||||
|
||||
var cell_action = row.insertCell(7);
|
||||
cell_action.innerHTML = "<a href=/game/mai2/events/" + data.id +"><button class='btn btn-primary'>🖉</button></a>"
|
||||
}
|
||||
}
|
||||
|
||||
function chg_page(num) {
|
||||
var max_page = TBL_DATA.length / per_page;
|
||||
console.log(max_page);
|
||||
page = page + num;
|
||||
|
||||
|
||||
if (page > max_page && max_page >= 1) {
|
||||
page = max_page;
|
||||
document.getElementById("btn_next").disabled = true;
|
||||
document.getElementById("btn_prev").disabled = false;
|
||||
return;
|
||||
} else if (page < 0) {
|
||||
page = 0;
|
||||
document.getElementById("btn_next").disabled = false;
|
||||
document.getElementById("btn_prev").disabled = true;
|
||||
return;
|
||||
} else if (page == 0) {
|
||||
document.getElementById("btn_next").disabled = false;
|
||||
document.getElementById("btn_prev").disabled = true;
|
||||
} else {
|
||||
document.getElementById("btn_next").disabled = false;
|
||||
document.getElementById("btn_prev").disabled = false;
|
||||
}
|
||||
|
||||
update_tbl();
|
||||
}
|
||||
|
||||
update_tbl();
|
||||
</script>
|
||||
{% endblock content %}
|
@ -3,6 +3,9 @@
|
||||
<ul class="mai2-navi">
|
||||
<li><a class="nav-link" href="/game/mai2/">PROFILE</a></li>
|
||||
<li><a class="nav-link" href="/game/mai2/playlog/">RECORD</a></li>
|
||||
{% if sesh is defined and sesh is not none and "{:08b}".format(sesh.permissions)[4] == "1" %}
|
||||
<li><a class="nav-link" href="/game/mai2/events/">EVENTS</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
<script>
|
||||
@ -12,6 +15,8 @@
|
||||
$('.nav-link[href="/game/mai2/"]').addClass('active');
|
||||
} else if (currentPath.startsWith('/game/mai2/playlog/')) {
|
||||
$('.nav-link[href="/game/mai2/playlog/"]').addClass('active');
|
||||
}
|
||||
} {% if sesh is defined and sesh is not none and "{:08b}".format(sesh.permissions)[4] == "1" %}else if (currentPath.startsWith('/game/mai2/events/')) {
|
||||
$('.nav-link[href="/game/mai2/events/"]').addClass('active');
|
||||
} {% endif %}
|
||||
});
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user