Skip to content

Commit

Permalink
Revert mhm to old version as there might have problems.
Browse files Browse the repository at this point in the history
  • Loading branch information
shinkuan committed May 27, 2024
1 parent 65082da commit 24ff2d1
Show file tree
Hide file tree
Showing 21 changed files with 4,524 additions and 5,046 deletions.
2 changes: 1 addition & 1 deletion client.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def refresh_log(self) -> None:
if self.dahai_verfication_job is not None:
self.dahai_verfication_job.stop()
self.dahai_verfication_job = None
if liqi_msg['method'] == '.lq.NotifyGameEndResult' or liqi_msg['method'] == '.lq.NotifyGameTerminate':
if liqi_msg['method'] == '.lq.NotifyGameEndResult' or liqi_msg['method'] == '.lq.NotifyGameTerminate' or liqi_msg['method'] == '.lq.FastTest.terminateGame':
self.action_quit()

elif self.syncing:
Expand Down
3,533 changes: 1,751 additions & 1,782 deletions liqi_proto/liqi_pb2.py

Large diffs are not rendered by default.

156 changes: 156 additions & 0 deletions mhm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,159 @@
from rich.console import Console
from rich.logging import RichHandler
from collections import defaultdict
from dataclasses import dataclass, asdict, field
from os.path import exists
from os import environ
from json import load, dump
from logging import getLogger
from pathlib import Path

pRoot = Path(".")

pathConf = pRoot / "mhmp.json"
pathResVer = pRoot / "resver.json"


@dataclass
class ResVer:
version: str = None
emotes: dict[str, list] = None

@classmethod
def fromdict(cls, data: dict):
# purge
if "max_charid" in data:
data.pop("max_charid")
if "emos" in data:
data["emotes"] = data.pop("emos")
return cls(**data)


@dataclass
class Conf:
@dataclass
class Base:
log_level: str = "info"
pure_python_protobuf: bool = False

@dataclass
class Hook:
enable_skins: bool = True
enable_aider: bool = False
enable_chest: bool = False
random_star_char: bool = False
no_cheering_emotes: bool = False

mhm: Base = None
hook: Hook = None
dump: dict = None
mitmdump: dict = None
proxinject: dict = None

@classmethod
def default(cls):
return cls(
mhm=cls.Base(),
hook=cls.Hook(),
dump={"with_dumper": False, "with_termlog": True},
mitmdump={"http2": False, "mode": ["[email protected]:7070"]},
proxinject={"name": "jantama_mahjongsoul", "set-proxy": "127.0.0.1:7070"},
)

@classmethod
def fromdict(cls, data: dict):
# purge
if "server" in data:
data.pop("server")
if "plugin" in data:
data["hook"] = data.pop("plugin")
# to dataclass
for key, struct in [("mhm", cls.Base), ("hook", cls.Hook)]:
if key in data:
data[key] = struct(**data[key])
return cls(**data)


if exists(pathConf):
conf = Conf.fromdict(load(open(pathConf, "r")))
else:
conf = Conf.default()

if exists(pathResVer):
resver = ResVer.fromdict(load(open(pathResVer, "r")))
else:
resver = ResVer()


def fetch_resver():
"""Fetch the latest character id and emojis"""
import requests
import random
import re

rand_a: int = random.randint(0, int(1e9))
rand_b: int = random.randint(0, int(1e9))

ver_url = f"https://game.maj-soul.com/1/version.json?randv={rand_a}{rand_b}"
response = requests.get(ver_url, proxies={"https": None})
response.raise_for_status()
version: str = response.json().get("version")

if resver.version == version:
return

res_url = f"https://game.maj-soul.com/1/resversion{version}.json"
response = requests.get(res_url, proxies={"https": None})
response.raise_for_status()
res_data: dict = response.json()

emotes: defaultdict[str, list[int]] = defaultdict(list)
pattern = rf"en\/extendRes\/emo\/e(\d+)\/(\d+)\.png"

for text in res_data.get("res"):
matches = re.search(pattern, text)

if matches:
charid = matches.group(1)
emo = int(matches.group(2))

if emo == 13:
continue
emotes[charid].append(emo)
for value in emotes.values():
value.sort()

resver.version = version
resver.emotes = {key: value[9:] for key, value in sorted(emotes.items())}

with open(pathResVer, "w") as f:
dump(asdict(resver), f)


def no_cheering_emotes():
exclude = set(range(13, 19))
for emo in resver.emotes.values():
emo[:] = sorted(set(emo) - exclude)


def init():
with console.status("[magenta]Fetch the latest server version") as status:
fetch_resver()
if conf.hook.no_cheering_emotes:
no_cheering_emotes()
if conf.mhm.pure_python_protobuf:
environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python"

with open(pathConf, "w") as f:
dump(asdict(conf), f, indent=2)


# console
console = Console()


# logger
logger = getLogger(__name__)
logger.propagate = False
logger.setLevel(conf.mhm.log_level.upper())
logger.addHandler(RichHandler(markup=True, rich_tracebacks=True))
62 changes: 1 addition & 61 deletions mhm/__main__.py
Original file line number Diff line number Diff line change
@@ -1,64 +1,4 @@
import argparse
import asyncio

from . import console
from .common import start_inject, start_proxy
from .config import config
from .hook import Hook
from .resource import ResourceManager, load_resource


# TODO: Plugins should be independent of this project and should be loaded from a folder
def create_hooks(resger: ResourceManager) -> list[Hook]:
hooks = []
if config.base.aider:
from .hook.aider import DerHook

hooks.append(DerHook())
if config.base.chest:
from .hook.chest import EstHook

hooks.append(EstHook(resger))
if config.base.skins:
from .hook.skins import KinHook

hooks.append(KinHook(resger))
return hooks


def main():
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", action="store_true")
args = parser.parse_args()

console.log("Load Resource")
with console.status("[magenta]Fetch LQC.LQBIN"):
resger = load_resource()
console.log(f"LQBin Version: [cyan3]{resger.version}")
console.log(f"> {len(resger.item_rows):0>3} items")
console.log(f"> {len(resger.title_rows):0>3} titles")
console.log(f"> {len(resger.character_rows):0>3} characters")

console.log("Init Hooks")
hooks = create_hooks(resger)
for h in hooks:
console.log(f"> [cyan3]{h.__class__.__name__}")

async def start():
tasks = set()
if config.mitmdump.args:
tasks.add(start_proxy([h.run for h in hooks], args.verbose))
console.log(f"Start mitmdump @ {config.mitmdump.args.get('mode')}")
if config.proxinject.path:
tasks.add(start_inject())
console.log(f"Start proxinject @ {config.proxinject.args.get('set-proxy')}")
await asyncio.gather(*tasks)

try:
asyncio.run(start())
except KeyboardInterrupt:
pass

from .common import main

if __name__ == "__main__":
main()
Loading

0 comments on commit 24ff2d1

Please sign in to comment.