diff --git a/v8_server/eamuse/services/cardmng.py b/v8_server/eamuse/services/cardmng.py
index 1adf3c9..74116d2 100644
--- a/v8_server/eamuse/services/cardmng.py
+++ b/v8_server/eamuse/services/cardmng.py
@@ -2,10 +2,26 @@ from lxml.builder import E
from v8_server import db
from v8_server.eamuse.services.services import ServiceRequest
-from v8_server.eamuse.utils.xml import get_xml_attrib
+from v8_server.eamuse.xml.utils import (
+ drop_attributes,
+ get_xml_attrib,
+ load_xml_template,
+)
from v8_server.model.user import Card, Profile, RefID, User, UserAccount
+class CardStatus(object):
+ """
+ Possible Card Status Values
+ """
+
+ SUCCESS = 0
+ NO_PROFILE = 109
+ NOT_ALLOWED = 110
+ NOT_REGISTERED = 112
+ INVALID_PIN = 116
+
+
class CardMng(object):
"""
Handle the CardMng (Card Manage) request.
@@ -13,13 +29,6 @@ class CardMng(object):
This is for supporting eAmuse card interaction.
"""
- # List of statuses we return to the game for various card related reasons
- SUCCESS = 0
- NO_PROFILE = 109
- NOT_ALLOWED = 110
- NOT_REGISTERED = 112
- INVALID_PIN = 116
-
# Methods
INQUIRE = "inquire"
GETREFID = "getrefid"
@@ -31,75 +40,59 @@ class CardMng(object):
@classmethod
def inquire(cls, req: ServiceRequest):
"""
- Example Request:
-
-
-
+ Handle a Card Manage Inquire Request
- Example Response:
-
-
-
+ Either the given card id is a brand new user, or a returning user.
+
+ Modifyable XML Text Replacements:
+ refid: The RefID.refid value for an existing user
+ newflag: Set to true for a new user, else 0
+ binded: Set to true if the user has a profile/account, else 0
+ status: See CardMng status consts at the top of this class
"""
+
# Grab the card id
cardid = get_xml_attrib(req.xml[0], "cardid")
+ # Default result for a new unregistered user
+ args = {
+ "refid": "",
+ "newflag": 1,
+ "binded": 0,
+ "status": CardStatus.NOT_REGISTERED,
+ }
+
# Check if a user with this card id already exists
user = User.from_cardid(cardid)
- if user is None:
- # The user doesn't exist, force system to create a new account
- response = E.response(
- E.cardmng(
- {
- "newflag": "1",
- "binded": "0",
- "status": str(CardMng.NOT_REGISTERED),
- }
- )
- )
- else:
+ # We have a returning user
+ if user is not None:
refid = RefID.from_userid(user.userid)
bound = UserAccount.from_userid(user.userid) is not None
if refid is None:
+ # TODO: better exception?
raise Exception("RefID Should not be None here!")
- response = E.response(
- E.cardmng(
- {
- "refid": refid.refid,
- "dataid": refid.refid,
- "newflag": "0",
- "binded": "1" if bound else "0",
- "expired": "0",
- "exflag": "0",
- "useridflag": "1",
- "extidflag": "1",
- "status": str(CardMng.SUCCESS),
- }
- )
+ args["refid"] = refid.refid
+ args["newflag"] = 0
+ args["binded"] = bound
+ args["status"] = CardStatus.SUCCESS
+
+ result = load_xml_template("cardmng", "inquire", args)
+
+ # If we have a brand new user, we can remove unnecessary xml attributes from the
+ # cardmng node
+ if user is None:
+ drop_attributes(
+ result.find("cardmng"),
+ ["refid", "dataid", "expired", "exflag", "useridflag", "extidflag"],
)
- return response
+ return result
@classmethod
def getrefid(cls, req: ServiceRequest):
- """"""
# Grab the card id and pin
cardid = get_xml_attrib(req.xml[0], "cardid")
pin = get_xml_attrib(req.xml[0], "passwd")
@@ -117,7 +110,7 @@ class CardMng(object):
# Generate the refid and return it
refid = RefID.create_with_userid(user.userid)
- return E.response(E.cardmng({"dataid": refid.refid, "refid": refid.refid}))
+ return load_xml_template("cardmng", "getrefid", {"refid": refid.refid})
@classmethod
def authpass(cls, req: ServiceRequest):
@@ -134,13 +127,9 @@ class CardMng(object):
# Check if the pin is valid for the user
user = refid.user
- valid = user.pin == pin
+ status = CardStatus.SUCCESS if user.pin == pin else CardStatus.INVALID_PIN
- return E.response(
- E.cardmng(
- {"status": str(CardMng.SUCCESS if valid else CardMng.INVALID_PIN)}
- )
- )
+ return load_xml_template("cardmng", "authpass", {"status": status})
@classmethod
def bindmodel(cls, req: ServiceRequest):
@@ -157,18 +146,20 @@ class CardMng(object):
db.session.add(profile)
db.session.commit()
- return E.response(E.cardmng({"dataid": refid.refid}))
+ return load_xml_template("cardmng", "bindmodel", {"refid": refid.refid})
@classmethod
def getkeepspan(cls):
"""
Unclear what this method does, return an arbitrary span
"""
- return E.response(E.cardmng({"keepspan": "30"}))
+
+ keepspan = 30
+ return load_xml_template("cardmng", "getkeepspan", {"keepspan": keepspan})
@classmethod
def getdatalist(cls):
"""
Unclear what this method does, return a dummy response
"""
- return E.response(E.cardmng())
+ return load_xml_template("cardmng", "cardmng")
diff --git a/v8_server/eamuse/services/facility.py b/v8_server/eamuse/services/facility.py
index 38fd146..5e70411 100644
--- a/v8_server/eamuse/services/facility.py
+++ b/v8_server/eamuse/services/facility.py
@@ -1,6 +1,6 @@
from lxml.builder import E
-from v8_server.eamuse.utils.xml import XMLBinTypes as T, e_type
+from v8_server.eamuse.xml.utils import XMLBinTypes as T, e_type
class Facility(object):
diff --git a/v8_server/eamuse/services/local.py b/v8_server/eamuse/services/local.py
index cdc08b4..84a49bd 100644
--- a/v8_server/eamuse/services/local.py
+++ b/v8_server/eamuse/services/local.py
@@ -7,7 +7,7 @@ from lxml.builder import E
from v8_server import db
from v8_server.eamuse.services.services import ServiceRequest
from v8_server.eamuse.utils.crc import calculate_crc8
-from v8_server.eamuse.utils.xml import XMLBinTypes as T, e_type, fill, get_xml_attrib
+from v8_server.eamuse.xml.utils import XMLBinTypes as T, e_type, fill, get_xml_attrib
from v8_server.model.song import HitChart
from v8_server.model.user import User, UserAccount
diff --git a/v8_server/eamuse/services/package.py b/v8_server/eamuse/services/package.py
index 8740ae1..79ba53c 100644
--- a/v8_server/eamuse/services/package.py
+++ b/v8_server/eamuse/services/package.py
@@ -1,7 +1,7 @@
from lxml.builder import E
from v8_server.eamuse.services.services import ServiceRequest
-from v8_server.eamuse.utils.xml import get_xml_attrib
+from v8_server.eamuse.xml.utils import get_xml_attrib
class Package(object):
diff --git a/v8_server/eamuse/services/services.py b/v8_server/eamuse/services/services.py
index 5387ee6..c29c189 100644
--- a/v8_server/eamuse/services/services.py
+++ b/v8_server/eamuse/services/services.py
@@ -18,7 +18,7 @@ from v8_server import LOG_PATH, app
from v8_server.eamuse.utils.arc4 import EAmuseARC4
from v8_server.eamuse.utils.eamuse import Model
from v8_server.eamuse.utils.lz77 import Lz77
-from v8_server.eamuse.utils.xml import get_xml_attrib, get_xml_tag
+from v8_server.eamuse.xml.utils import get_xml_attrib, get_xml_tag
# We want a general logger, and a special logger to log requests separately
diff --git a/v8_server/eamuse/utils/xml.py b/v8_server/eamuse/utils/xml.py
deleted file mode 100644
index c49b64a..0000000
--- a/v8_server/eamuse/utils/xml.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from datetime import datetime
-from typing import Dict, Optional
-
-from lxml import etree
-
-
-def fill(count: int, value: str = "0") -> str:
- return " ".join(value for _ in range(0, count))
-
-
-class XMLBinTypes(object):
- s8 = "s8"
- u8 = "u8"
- s16 = "s16"
- u16 = "u16"
- s32 = "s32"
- u32 = "u32"
- s64 = "s64"
- u64 = "u64"
- ip4 = "ip4"
- time = "time"
- str = "str"
-
-
-def e_type(_type, count: Optional[int] = None) -> Dict[str, str]:
- result = {"__type": _type}
- if count is not None:
- result["__count"] = str(count)
- return result
-
-
-def get_xml_tag(xml: etree) -> str:
- return str(xml.tag)
-
-
-def get_xml_attrib(xml: etree, name: str) -> str:
- return str(xml.attrib[name]) if name in xml.attrib else "None"
-
-
-def format_date(timestamp: Optional[int]) -> str:
- if timestamp is None:
- return "None"
-
- dt = datetime.fromtimestamp(timestamp)
- return dt.strftime("%Y-%m-%d %H:%M:%S")
diff --git a/v8_server/eamuse/xml/__init__.py b/v8_server/eamuse/xml/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/v8_server/eamuse/xml/templates/cardmng/authpass.xml b/v8_server/eamuse/xml/templates/cardmng/authpass.xml
new file mode 100644
index 0000000..4c6f978
--- /dev/null
+++ b/v8_server/eamuse/xml/templates/cardmng/authpass.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/v8_server/eamuse/xml/templates/cardmng/bindmodel.xml b/v8_server/eamuse/xml/templates/cardmng/bindmodel.xml
new file mode 100644
index 0000000..afb3385
--- /dev/null
+++ b/v8_server/eamuse/xml/templates/cardmng/bindmodel.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/v8_server/eamuse/xml/templates/cardmng/getdatalist.xml b/v8_server/eamuse/xml/templates/cardmng/getdatalist.xml
new file mode 100644
index 0000000..ecc7764
--- /dev/null
+++ b/v8_server/eamuse/xml/templates/cardmng/getdatalist.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/v8_server/eamuse/xml/templates/cardmng/getkeepspan.xml b/v8_server/eamuse/xml/templates/cardmng/getkeepspan.xml
new file mode 100644
index 0000000..070bc82
--- /dev/null
+++ b/v8_server/eamuse/xml/templates/cardmng/getkeepspan.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/v8_server/eamuse/xml/templates/cardmng/getrefid.xml b/v8_server/eamuse/xml/templates/cardmng/getrefid.xml
new file mode 100644
index 0000000..4e6ca03
--- /dev/null
+++ b/v8_server/eamuse/xml/templates/cardmng/getrefid.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/v8_server/eamuse/xml/templates/cardmng/inquire.xml b/v8_server/eamuse/xml/templates/cardmng/inquire.xml
new file mode 100644
index 0000000..0e442a7
--- /dev/null
+++ b/v8_server/eamuse/xml/templates/cardmng/inquire.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/v8_server/eamuse/xml/utils.py b/v8_server/eamuse/xml/utils.py
new file mode 100644
index 0000000..4ab2dd8
--- /dev/null
+++ b/v8_server/eamuse/xml/utils.py
@@ -0,0 +1,83 @@
+import logging
+from datetime import datetime
+from pathlib import Path
+from typing import Any, Dict, List, Optional
+
+from lxml import etree
+
+
+logger = logging.getLogger(__name__)
+
+
+def load_xml_template(
+ service: str, method: str, args: Optional[Dict[str, Any]], / # noqa: W504
+) -> etree:
+ """
+ Given a service and a method, load the appropriate XML template and return it
+
+ Args:
+ service (str): eAmuse Service Name
+ method (str): eAmuse Method Name
+ args (Dict[str, Any]): Used for formatting the xml string
+
+ Returns:
+ etree: Resulting XML etree
+ """
+ template_path = Path(__file__).parent / "templates"
+ filepath = template_path / service / f"{method}.xml"
+
+ try:
+ xml_str = filepath.open().read()
+ except Exception:
+ raise
+
+ if args is not None:
+ xml_str = xml_str.format(**args)
+
+ return etree.fromstring(xml_str.encode("UTF-8"))
+
+
+def drop_attributes(element: etree, attributes: List[str]) -> None:
+ for attribute in attributes:
+ element.attrib.pop(attribute)
+
+
+def fill(count: int, value: str = "0") -> str:
+ return " ".join(value for _ in range(0, count))
+
+
+class XMLBinTypes(object):
+ s8 = "s8"
+ u8 = "u8"
+ s16 = "s16"
+ u16 = "u16"
+ s32 = "s32"
+ u32 = "u32"
+ s64 = "s64"
+ u64 = "u64"
+ ip4 = "ip4"
+ time = "time"
+ str = "str"
+
+
+def e_type(_type, count: Optional[int] = None) -> Dict[str, str]:
+ result = {"__type": _type}
+ if count is not None:
+ result["__count"] = str(count)
+ return result
+
+
+def get_xml_tag(xml: etree) -> str:
+ return str(xml.tag)
+
+
+def get_xml_attrib(xml: etree, name: str) -> str:
+ return str(xml.attrib[name]) if name in xml.attrib else "None"
+
+
+def format_date(timestamp: Optional[int]) -> str:
+ if timestamp is None:
+ return "None"
+
+ dt = datetime.fromtimestamp(timestamp)
+ return dt.strftime("%Y-%m-%d %H:%M:%S")