diff --git a/.gitignore b/.gitignore index 2684b78..fdc767f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ __pycache__/ data/ test.py +database_config.txt diff --git a/app.py b/app.py index 902cac7..9703f89 100644 --- a/app.py +++ b/app.py @@ -1,10 +1,9 @@ from flask import Flask +from waitress import serve from flask_restful import Api -from modules.KF3.database import Database as KF3DB -from modules.Kingdom.database import Database as KFKDB from modules.Archive.database import Database as KFADB -from modules.proxy import AgreementProxy, LaunchProxy, UpdateProxy +import logging app = Flask(__name__) app.config['JSON_AS_ASCII'] = False @@ -13,11 +12,8 @@ app.databases = {} api = Api(app) KFADB(api) -api.add_resource(LaunchProxy, "/DMM/launch") -api.add_resource(UpdateProxy, "/DMM/filelist", "/DMM/update") -api.add_resource(AgreementProxy, "/DMM/agreement") -KF3DB(api) -KFKDB(api) if __name__ == '__main__': - app.run(host='127.0.0.1', port=8080, debug=True) \ No newline at end of file + #app.run(host='127.0.0.1', port=8081, debug=True) + logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(message)s') + serve(app, listen='*:8081', threads=1) \ No newline at end of file diff --git a/modules/Archive/database.py b/modules/Archive/database.py index ae3b8c4..214e463 100644 --- a/modules/Archive/database.py +++ b/modules/Archive/database.py @@ -1,6 +1,7 @@ import json from flask import Flask +from modules.Archive.endpoints.tag_stats import TagStats from .databaseController import DatabaseController from .endpoints.query import Query @@ -23,108 +24,93 @@ class Database: if "Archive" in self.app.databases: del self.app.databases["Archive"] - self.reload_data("/home/pi/python/Katbots/JapariArchive/database.db") + self.reload_data() self.app.databases["Archive"] = self - api.add_resource(Query, "/Archive/Query") - api.add_resource(NewQuery, "/Archive/NewQuery") - api.add_resource(Command, "/Archive/Command") - api.add_resource(Commands, "/Archive/Commands") - api.add_resource(GetPost, "/Archive/GetPost/") - api.add_resource(GetPosts, "/Archive/GetPosts") - api.add_resource(GetPostsCount, "/Archive/GetPosts/Count") - api.add_resource(AccountStats, "/Archive/AccountStats") - api.add_resource(SetAction, "/Archive/SetAction") + api.add_resource(Query, "/Query") + api.add_resource(NewQuery, "/NewQuery") + api.add_resource(Command, "/Command") + api.add_resource(Commands, "/Commands") + api.add_resource(GetPost, "/GetPost/") + api.add_resource(GetPosts, "/GetPosts") + api.add_resource(GetPostsCount, "/GetPosts/Count") + api.add_resource(AccountStats, "/AccountStats") + api.add_resource(TagStats, "/TagStats") + api.add_resource(SetAction, "/SetAction") def get_accounts(self): query = f''' - SELECT x_handle, id FROM accounts + SELECT x_handle, id FROM x_accounts ORDER BY x_handle ASC''' - return self.db.run_query(query) def get_account_stats(self): - query = '''SELECT x_handle, - SUM(IIF(x_posts.action_taken = 0, 1, 0)) as undecided, - SUM(IIF(x_posts.action_taken = 1, 1, 0)) as approved, - SUM(IIF(x_posts.action_taken = 2, 1, 0)) as deleted, - SUM(IIF(x_posts.action_taken = 3, 1, 0)) as hidden, - SUM(IIF(x_post_details.id is NULL, 1, 0)) as invalid -FROM accounts - LEFT JOIN x_posts on accounts.id = x_posts.account_id - LEFT JOIN x_post_details on x_post_details.id = x_posts.id -WHERE accounts.download_mode != 4 -GROUP BY account_id -ORDER BY x_handle''' + query = 'SELECT * FROM "Artist Stats"' return self.db.run_query(query) + def get_tag_stats(self): + query = 'SELECT * FROM "Tag Stats"' + return self.db.run_query(query) + def get_post(self, id): - query = f'''SELECT x_posts.id, cast(x_posts.id as TEXT) as id_str, x_posts.error_id, x_posts.action_taken, x_posts.is_saved, x_post_details.text, x_post_details.files, x_post_details.date, accounts.x_handle, accounts.rating from x_posts - LEFT JOIN x_post_details - ON x_posts.id = x_post_details.id - LEFT JOIN accounts - ON x_posts.account_id = accounts.id WHERE x_posts.id = {id} + query = f'''SELECT cast(x_posts.id as TEXT), x_posts.*, x_accounts.x_handle, x_accounts.rating from x_posts + LEFT JOIN x_accounts + ON x_posts.account_id = x_accounts.id WHERE x_posts.id = {id} LIMIT 1''' - result = self.db.run_query(query) - if len(result) == 0: - return None - else: - #return most recent post - return result[-1] + return self.db.run_query(query) - def build_where_query(self, artist, actions_taken = [0, 1, 2, 3], last_id = -1, include_ratings = ["SFW", "NSFW"]): - where_query = "WHERE x_post_details.id NOT NULL AND x_posts.error_id = 0" + def build_where_query(self, artist, actions_taken = [0, 1, 2, 3], last_id = -1, include_ratings = ["SFW", "NSFW"], tags = []): + where_query = "WHERE x_posts.error_id = 0" if last_id != -1: where_query += f" AND x_posts.id < {last_id}" if actions_taken != [0, 1, 2, 3]: where_query += " AND (" + " OR ".join([f"x_posts.action_taken = {action}" for action in actions_taken]) + ")" if include_ratings != ["SFW", "NSFW", "NSFL"]: - where_query += " AND (" + " OR ".join([f'accounts.rating = "{rating}"' for rating in include_ratings]) + ")" + where_query += " AND (" + " OR ".join([f'x_accounts.rating = \'{rating}\'' for rating in include_ratings]) + ")" if artist is not None and artist != "": - where_query += f' AND accounts.x_handle = "{artist}"' + where_query += f' AND x_accounts.x_handle = \'{artist}\'' + if len(tags) > 0: + tags = ", ".join(["'" + tag + "'" for tag in tags]) + where_query += f' AND tags @> ARRAY[{tags}]' return where_query - def get_posts_count(self, artist, actions_taken = [0, 1, 2, 3], last_id = -1, include_ratings = ["SFW", "NSFW"]): - where_query = self.build_where_query(artist, actions_taken, last_id, include_ratings) + def get_posts_count(self, artist, actions_taken = [0, 1, 2, 3], last_id = -1, include_ratings = ["SFW", "NSFW"], tags = []): + where_query = self.build_where_query(artist, actions_taken, last_id, include_ratings, tags) query = f''' SELECT count(*) as count FROM x_posts - LEFT JOIN x_post_details - ON x_posts.id = x_post_details.id - LEFT JOIN accounts - ON x_posts.account_id = accounts.id + LEFT JOIN x_accounts + ON x_posts.account_id = x_accounts.id {where_query}''' - result = self.db.run_query(query) + _, result = self.db.run_query(query) return result[0]["count"] - def get_posts(self, num_posts, artist, actions_taken = [0, 1, 2, 3], last_id = -1, offset = 0, include_ratings = ["SFW", "NSFW"], order = "DESC"): + def get_posts(self, num_posts, artist, actions_taken = [0, 1, 2, 3], last_id = -1, offset = 0, include_ratings = ["SFW", "NSFW"], tags = [], order = "DESC"): num_posts = max(1, min(num_posts, 100)) order_by = "RANDOM()" if order == "RAND" else "x_posts.id ASC" if order == "ASC" else "x_posts.id DESC" - where_query = self.build_where_query(artist, actions_taken, last_id, include_ratings) + where_query = self.build_where_query(artist, actions_taken, last_id, include_ratings, tags) query = f''' - SELECT x_posts.id, cast(x_posts.id as TEXT) as id_str, x_posts.action_taken, x_posts.is_saved, x_post_details.text, x_post_details.files, x_post_details.date, accounts.x_handle, accounts.rating FROM x_posts - LEFT JOIN x_post_details - ON x_posts.id = x_post_details.id - LEFT JOIN accounts - ON x_posts.account_id = accounts.id + SELECT x_posts.id, cast(x_posts.id as TEXT) as id_str, action_taken, saved_files != \'{{}}\' as is_saved, text, files, date, x_handle, x_accounts.rating FROM x_posts + LEFT JOIN x_accounts + ON x_posts.account_id = x_accounts.id {where_query} ORDER BY {order_by} LIMIT {num_posts} OFFSET {offset}''' return self.db.run_query(query) - def wrap_query_response(self, result, mode = "json"): + def wrap_query_response(self, result, mode = "json", error : str = ""): if result is None: - response = self.app.response_class(status=400) + response = self.app.response_class(response=error, status=400) else: if mode == "json": response = self.app.response_class( - response=json.dumps(result, ensure_ascii=False, indent=1), + response=json.dumps(result, ensure_ascii=False, indent=1, default=str), status=200, mimetype='application/json' ) @@ -138,5 +124,5 @@ ORDER BY x_handle''' response.headers.add("Access-Control-Allow-Origin", "*") return response - def reload_data(self, database_path): - self.db = DatabaseController(database_path) \ No newline at end of file + def reload_data(self): + self.db = DatabaseController() \ No newline at end of file diff --git a/modules/Archive/databaseController.py b/modules/Archive/databaseController.py index a0f0009..ef80536 100644 --- a/modules/Archive/databaseController.py +++ b/modules/Archive/databaseController.py @@ -1,23 +1,29 @@ import os -import sqlite3 +import psycopg +from psycopg.rows import dict_row -TABLE_ACCOUNTS = "accounts" +TABLE_ACCOUNTS = "x_accounts" TABLE_X = "x_posts" class DatabaseController: - def __init__(self, db_name): - self.conn = sqlite3.connect(db_name) - self.conn.row_factory = sqlite3.Row + def __init__(self): + if not os.path.exists("database_config.txt"): + raise Exception(f"Create 'database_config.txt' in {os.path.abspath(os.curdir)} with database connection string, example: 'dbname=japariarchive user=postgres password=password123'") + with open("database_config.txt", "rt") as file: + connection_string = file.read() + self.conn = psycopg.connect(connection_string, row_factory=dict_row) self.cursor = self.conn.cursor() def run_query(self, query): try: self.cursor.execute(query) - results = [dict(result) for result in self.cursor.fetchall()] - return results + results = self.cursor.fetchall() + self.conn.rollback() + return True, results except Exception as e: - print(e) - return [] + print(query, e) + self.conn.rollback() + return False, str(e) def run_command(self, command): try: diff --git a/modules/Archive/endpoints/account_stats.py b/modules/Archive/endpoints/account_stats.py index b6aa1a4..271c2a4 100644 --- a/modules/Archive/endpoints/account_stats.py +++ b/modules/Archive/endpoints/account_stats.py @@ -9,5 +9,5 @@ if TYPE_CHECKING: class AccountStats(Resource): def get(self): db : Database = app.databases["Archive"] - result = db.get_account_stats() + _, result = db.get_account_stats() return db.wrap_query_response(result) \ No newline at end of file diff --git a/modules/Archive/endpoints/new_query.py b/modules/Archive/endpoints/new_query.py index 37d540c..dabc1bd 100644 --- a/modules/Archive/endpoints/new_query.py +++ b/modules/Archive/endpoints/new_query.py @@ -12,6 +12,8 @@ class NewQuery(Resource): db : Database = app.databases["Archive"] - result = db.db.run_query(query) - - return db.wrap_query_response(result) \ No newline at end of file + status, result = db.db.run_query(query) + if status: + return db.wrap_query_response(result) + else: + return db.wrap_query_response(None, error = result) \ No newline at end of file diff --git a/modules/Archive/endpoints/post.py b/modules/Archive/endpoints/post.py index d83d432..c026c8f 100644 --- a/modules/Archive/endpoints/post.py +++ b/modules/Archive/endpoints/post.py @@ -11,5 +11,5 @@ class GetPost(Resource): id = int(id) db : Database = app.databases["Archive"] - result = db.get_post(id) + _, result = db.get_post(id) return db.wrap_query_response(result) \ No newline at end of file diff --git a/modules/Archive/endpoints/posts.py b/modules/Archive/endpoints/posts.py index 05d235c..f4a8c9d 100644 --- a/modules/Archive/endpoints/posts.py +++ b/modules/Archive/endpoints/posts.py @@ -14,6 +14,7 @@ class GetPosts(Resource): page = int(request.args["page"]) if "page" in request.args else 1 artist = request.args["artist"] if "artist" in request.args else None last_id = int(request.args["last_id"]) if "last_id" in request.args else -1 + tags = request.args["tags"].split() if "tags" in request.args else [] except: response = app.response_class(status=400) response.headers.add("Access-Control-Allow-Origin", "*") @@ -51,5 +52,8 @@ class GetPosts(Resource): real_page = page-1 offset = real_page * count - result = db.get_posts(count, artist, actions_taken=actions, last_id= -1, offset= offset, include_ratings= ratings, order=order) - return db.wrap_query_response(result) \ No newline at end of file + status, result = db.get_posts(count, artist, actions_taken=actions, last_id= -1, offset= offset, include_ratings= ratings, order=order, tags=tags) + if status: + return db.wrap_query_response(result) + else: + return db.wrap_query_response(None, error=result) \ No newline at end of file diff --git a/modules/Archive/endpoints/posts_count.py b/modules/Archive/endpoints/posts_count.py index 68712f4..1c59ca4 100644 --- a/modules/Archive/endpoints/posts_count.py +++ b/modules/Archive/endpoints/posts_count.py @@ -11,6 +11,7 @@ class GetPostsCount(Resource): db : Database = app.databases["Archive"] try: artist = request.args["artist"] if "artist" in request.args else None + tags = request.args["tags"].split() if "tags" in request.args else [] except: response = app.response_class(status=400) response.headers.add("Access-Control-Allow-Origin", "*") @@ -38,5 +39,5 @@ class GetPostsCount(Resource): if ratings == []: ratings = ["SFW", "NSFW"] - result = db.get_posts_count(artist, actions_taken=actions, last_id= -1, include_ratings= ratings) + result = db.get_posts_count(artist, actions_taken=actions, last_id= -1, include_ratings= ratings, tags=tags) return db.wrap_query_response(result, mode="text") \ No newline at end of file diff --git a/modules/Archive/endpoints/query.py b/modules/Archive/endpoints/query.py index 774ef4d..f532f5e 100644 --- a/modules/Archive/endpoints/query.py +++ b/modules/Archive/endpoints/query.py @@ -12,7 +12,9 @@ class Query(Resource): db : Database = app.databases["Archive"] - result = db.db.run_query(query) - result = [list(d.values()) for d in result] - - return db.wrap_query_response(result) \ No newline at end of file + status, result = db.db.run_query(query) + if status: + result = [list(d.values()) for d in result] + return db.wrap_query_response(result) + else: + return db.wrap_query_response(None, error = result) \ No newline at end of file diff --git a/modules/Archive/endpoints/tag_stats.py b/modules/Archive/endpoints/tag_stats.py new file mode 100644 index 0000000..0afdf04 --- /dev/null +++ b/modules/Archive/endpoints/tag_stats.py @@ -0,0 +1,13 @@ +from __future__ import annotations +from flask_restful import Resource +from flask import current_app as app +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from modules.Archive.database import Database + +class TagStats(Resource): + def get(self): + db : Database = app.databases["Archive"] + _, result = db.get_tag_stats() + return db.wrap_query_response(result) \ No newline at end of file diff --git a/modules/KF3/database.py b/modules/KF3/database.py deleted file mode 100644 index ba6ee35..0000000 --- a/modules/KF3/database.py +++ /dev/null @@ -1,562 +0,0 @@ -import gzip -import json -import platform -import UnityPy -from datetime import datetime - -from .statCalculation import get_all_stats, fill_miracle_numbers -from .endpoints.friend import KF3_Friend -from .endpoints.friends import KF3_Friends -from .endpoints.item import KF3_Item -from .endpoints.items import KF3_Items -from .endpoints.update import KF3_Update - -class Database: - def __init__(self, api) -> None: - app = api.app - if "KF3" in app.databases: - del app.databases["KF3"] - - self.reload_data() - - app.databases["KF3"] = self - api.add_resource(KF3_Friend, "/KF3/Friend/") - api.add_resource(KF3_Friends, "/KF3/Friends") - api.add_resource(KF3_Item, "/KF3/Item/") - api.add_resource(KF3_Items, "/KF3/Items") - api.add_resource(KF3_Update, "/KF3/Update") - - def unload_data(self): - self.charaData = {} - self.itemCommon = {} - self.promoteData = {} - self.charaClothesData = {} - self.promotePresetData = {} - self.limitlevel_rising_status = {} - - self.paramArts = {} - self.paramAbilities = {} - self.paramAbilities1 = {} - self.paramAbilities2 = {} - self.paramAlphaBases = {} - self.paramWaitActions = {} - self.paramSpecialAttacks = {} - - self.processed_friends = {} - - def reload_data(self): - self.unload_data() - - if platform.system() == "Windows": - path = "H:\\Apache\\Katworks\\KF\\assets\\KF3\\cache\\" - else: - path = "/var/www/html/Katworks/KF/assets/KF3/develop01/cache/" - - with gzip.open(path + "CHARA_DATA.d", mode="rt", encoding="utf-8") as file: - for entry in json.loads(file.read()): - self.charaData[entry["id"]] = entry - - with gzip.open(path + "ITEM_COMMON.d", mode="rt", encoding="utf-8") as file: - for entry in json.loads(file.read()): - self.itemCommon[entry["id"]] = entry - - with gzip.open(path + "CHARA_PROMOTE_DATA.d", mode="rt", encoding="utf-8") as file: - for entry in json.loads(file.read()): - self.promoteData[entry["promoteId"]] = entry - - with gzip.open(path + "CHARA_PROMOTE_PRESET_DATA.d", mode="rt", encoding="utf-8") as file: - for entry in json.loads(file.read()): - if entry["promotePresetId"] not in self.promotePresetData: - self.promotePresetData[entry["promotePresetId"]] = [] - self.promotePresetData[entry["promotePresetId"]].append(entry) - - with gzip.open(path + "CHARA_CLOTHES_DATA.d", mode="rt", encoding="utf-8") as file: - for entry in json.loads(file.read()): - if entry["clothesPresetId"] not in self.charaClothesData: - self.charaClothesData[entry["clothesPresetId"]] = [] - self.charaClothesData[entry["clothesPresetId"]].append(entry) - - with gzip.open(path + "LIMITLEVEL_RISING_STATUS.d", mode="rt", encoding="utf-8") as file: - for entry in json.loads(file.read()): - self.limitlevel_rising_status[entry["patternId"]] = entry - - self.parse_parameter() - self.process_friends() - print("Data reload finished") - self.update_in_progress = False - - def parse_parameter(self): - if platform.system() == "Windows": - path = "H:\\Apache\\Katworks\\KF\\assets\\KF3\\assets\\" - else: - path = "/var/www/html/Katworks/KF/assets/KF3/develop01/assets/Windows/" - - paramAsset = UnityPy.load(path + "parameter.asset") - for obj in paramAsset.objects: - data = obj.read() - if data.name.split('_')[0] == "ParamAbility": - id = int(data.name.split('_')[1]) - if data.name.endswith("_1"): - self.paramAbilities1[id] = obj.read_typetree() - elif data.name.endswith("_2"): - self.paramAbilities2[id] = obj.read_typetree() - else: - self.paramAbilities[id] = obj.read_typetree() - elif data.name.split('_')[0] == "ParamAlphaBase": - id = int(data.name.split('_')[1]) - self.paramAlphaBases[id] = obj.read_typetree() - elif data.name.split('_')[0] == "ParamArts": - id = int(data.name.split('_')[1]) - self.paramArts[id] = obj.read_typetree() - elif data.name.split('_')[0] == "ParamSpecialAttack": - id = int(data.name.split('_')[1]) - self.paramSpecialAttacks[id] = obj.read_typetree() - elif data.name.split('_')[0] == "ParamWaitAction": - id = int(data.name.split('_')[1]) - self.paramWaitActions[id] = obj.read_typetree() - - def process_friends(self): - for charaID in self.charaData: - self.processed_friends[charaID] = self.process_chara(charaID) - - def process_chara(self, id : int): - chara = {} - - charaData = self.charaData[id] - alphaBase = self.paramAlphaBases[id] if id in self.paramAlphaBases else None - - wrLocked = False - promoIds = [] - promotePresetId = charaData["promotePresetId"] - promos = self.promotePresetData[promotePresetId] if promotePresetId in self.promotePresetData else [] - for promo in promos: - if promo["promoteStepDatetime"] == 1917860400000: - wrLocked = True - continue - else: - promoIds.append([promo["promoteId00"], promo["promoteId01"], promo["promoteId02"], promo["promoteId03"], promo["promoteId04"], promo["promoteId05"]]) - - emptyPromote = {"promoteAtk": 0, "promoteDef": 0, "promoteHp": 0, "promoteAvoid": 0, "promoteActionDamageRatio": 0, "promoteBeatDamageRatio": 0, "promoteTryDamageRatio": 0} - promoteDatas = [[(self.promoteData[id] if id in self.promoteData else emptyPromote) for id in promo] for promo in promoIds] - promote_bonus = {"atk" : 0, "def" : 0, "hp" : 0, "evd" : 0, "beat" : 0, "act" : 0, "try" : 0} - for promoTier in promoteDatas: - for promoteStep in promoTier: - promote_bonus["atk"] += promoteStep["promoteAtk"] - promote_bonus["def"] += promoteStep["promoteDef"] - promote_bonus["hp"] += promoteStep["promoteHp"] - promote_bonus["evd"] += promoteStep["promoteAvoid"] - promote_bonus["act"] += promoteStep["promoteActionDamageRatio"] - promote_bonus["beat"] += promoteStep["promoteBeatDamageRatio"] - promote_bonus["try"] += promoteStep["promoteTryDamageRatio"] - - clothesPresetId = charaData["clothesPresetId"] - clothesDatas = self.charaClothesData[clothesPresetId] if clothesPresetId in self.charaClothesData else [] - costume_bonus = {"atk" : 0, "def" : 0, "hp" : 0, "evd" : 0, "beat" : 0, "act" : 0, "try" : 0} - for clothesData in clothesDatas: - costume_bonus["atk"] += clothesData["atkBonus"] - costume_bonus["def"] += clothesData["defBonus"] - costume_bonus["hp"] += clothesData["hpBonus"] - - chara["id"] = id - chara["promoteBonus"] = promote_bonus - chara["costumeBonus"] = costume_bonus - chara["attribute"] = charaData["attribute"] - chara["name"] = charaData["name"] - chara["nameEn"] = charaData["nameEn"] - chara["nickname"] = charaData["nickname"] - chara["rankLow"] = charaData["rankLow"] - chara["rankHigh"] = charaData["rankHigh"] - chara["castName"] = charaData["castName"] - chara["loginText"] = charaData["loginText"] - chara["flavorText"] = charaData["flavorText"] - chara["kizunaupText"] = charaData["kizunaupText"] - chara["greetingText"] = charaData["greetingText"] - chara["startTimeRaw"] = charaData["startTime"] - chara["startTime"] = datetime.fromtimestamp(charaData["startTime"]/1000.0).strftime('%A, %B %d, %Y') - - chara["wr"] = 4 if wrLocked else 5 - chara["is_wr5"] = chara["wr"] == 5 - chara["has_party_dress"] = False - for clothesData in clothesDatas: - if clothesData["clothesId"] == 8: - chara["has_party_dress"] = True - break - chara["has_rainbow_trait"] = id in self.paramAbilities2 - - chara["risingStatusPatternId"] = patternId = charaData["risingStatusPatternId"] - - if patternId not in self.limitlevel_rising_status: - level_curve = None - else: - level_curve = self.limitlevel_rising_status[patternId] if patternId != 0 else None - - if alphaBase is None: - chara["stats_min"] = {"level" : 0,"status" : 0,"wr" : 0,"hp" : 0,"atk" : 0,"def" : 0,"evd" : 0,"beat" : 0,"act" : 0,"try" : 0} - chara["stats_max"] = {"level" : 0,"status" : 0,"wr" : 0,"hp" : 0,"atk" : 0,"def" : 0,"evd" : 0,"beat" : 0,"act" : 0,"try" : 0} - chara["plasmPoint"] = 0 - chara["cards"] = {0,0,0,0,0} - else: - chara["stats_min"] = get_all_stats(chara, alphaBase, max_level = False, rising_status_pattern=level_curve) - chara["stats_max"] = get_all_stats(chara, alphaBase, max_level = True, rising_status_pattern=level_curve) - chara["plasmPoint"] = alphaBase["plasmPoint"] - chara["cards"] = [ - {"type":alphaBase["orderCardType00"], "value":alphaBase["orderCardValue00"]}, - {"type":alphaBase["orderCardType01"], "value":alphaBase["orderCardValue01"]}, - {"type":alphaBase["orderCardType02"], "value":alphaBase["orderCardValue02"]}, - {"type":alphaBase["orderCardType03"], "value":alphaBase["orderCardValue03"]}, - {"type":alphaBase["orderCardType04"], "value":alphaBase["orderCardValue04"]} - ] - - chara["synergy_flag"] = self.paramArts[id]["authParam"]["SynergyFlag"] if id in self.paramArts else 0 - - chara["arts"] = self.paramArts[id] if id in self.paramArts else None - chara["ability"] = self.paramAbilities[id] if id in self.paramAbilities else None - chara["ability1"] = self.paramAbilities1[id] if id in self.paramAbilities1 else None - chara["ability2"] = self.paramAbilities2[id] if id in self.paramAbilities2 else None - chara["wait_action"] = self.paramWaitActions[id] if id in self.paramWaitActions else None - chara["special_attack"] = self.paramSpecialAttacks[id] if id in self.paramSpecialAttacks else None - - chara["costumes"] = self.get_costumes(id, chara["rankLow"]) - - return chara - - def get_item(self, id : int): - if id in self.itemCommon: - return self.itemCommon[id] - else: - return None - - def get_chara(self, id : int): - if id in self.processed_friends: - return self.processed_friends[id] - else: - return None - - def get_chara_wiki(self, id : int): - chara = self.get_chara(id) - if chara is None: - return None - - lines = [] - - stars_word = chara["rankLow"] - stars_word = "Two" if stars_word == 2 else "Three" if stars_word == 3 else "Four" if stars_word == 4 else "Five" if stars_word == 5 else "Unknown" - - attribute_word = chara["attribute"] - attribute_word = "Funny" if attribute_word == 1 else "Friendly" if attribute_word == 2 else "Relaxed" if attribute_word == 3 else "Lovely" if attribute_word == 4 else "Active" if attribute_word == 5 else "Carefree" if attribute_word == 6 else "Unknown" - - categories = [ - f"[[Category:{attribute_word} KF3 Friends]]", - f"[[Category:{stars_word} Star KF3 Friends]]", - f"[[Category:Missing Content]]", - f"[[Category:Needs Audio]]", - f"{{{{#vardefine:id|{str(chara['id']).zfill(4)}}}}}" - ] - - if chara['has_party_dress']: - categories.append("[[Category:Party Dress KF3 Friends]]") - if chara['is_wr5']: - categories.append("[[Category:Wild Release 5 KF3 Friends]]") - if chara['has_rainbow_trait']: - categories.append("[[Category:Rainbow Trait KF3 Friends]]") - - lines.append(" ".join(categories)) - lines.append("") - lines.append("{{FriendBox/KF3") - lines.append(f"|name={chara['nameEn'].replace('_', ' ')}") - lines.append(f"|apppic={chara['nameEn'].replace('_', ' ')}KF3.png") - lines.append(f"|apprarity={{{{KF3{chara['rankLow']}Star}}}}") - lines.append(f"|seiyuu={chara['castName']}") - lines.append(f"|attribute={attribute_word} {{{{KF3{attribute_word}}}}}") - lines.append(f"|implemented={chara['startTime']} (App)") - lines.append(f"|id={chara['id']}") - lines.append(f"|wr5={'Yes' if chara['is_wr5'] else 'No'}") - lines.append(f"|rainbowtrait={'Yes' if chara['has_rainbow_trait'] else 'No'}") - lines.append(f"|partydress={'Yes' if chara['has_party_dress'] else 'No'}") - lines.append("}}") - lines.append("") - lines.append("{{FriendBuilder/KF3") - lines.append(f"|introduction = '''{chara['nameEn'].replace('_', ' ')}''' is a Friend that appears in the app version of [[Kemono Friends 3]].") - lines.append(f"|status={chara['stats_min']['status']}") - lines.append(f"|hp={chara['stats_min']['hp']}") - lines.append(f"|atk={chara['stats_min']['atk']}") - lines.append(f"|def={chara['stats_min']['def']}") - lines.append(f"|evd={round(chara['stats_min']['evd']/10,1)}%") - lines.append(f"|beat={chara['stats_min']['beat']}%") - lines.append(f"|action={chara['stats_min']['act']}%") - lines.append(f"|try={chara['stats_min']['try']}%") - lines.append(f"|plasm={chara['plasmPoint']}") - lines.append(f"|maxstatus={chara['stats_max']['status']}") - lines.append(f"|maxhp={chara['stats_max']['hp']}") - lines.append(f"|maxatk={chara['stats_max']['atk']}") - lines.append(f"|maxdef={chara['stats_max']['def']}") - lines.append(f"|maxevd={round(chara['stats_max']['evd']/10,1)}%") - lines.append(f"|maxbeat={chara['stats_max']['beat']}%") - lines.append(f"|maxaction={chara['stats_max']['act']}%") - lines.append(f"|maxtry={chara['stats_max']['try']}%") - lines.append("") - cards_str = "|flags=" - for card in chara["cards"]: - cards_str += " {{" - if card["type"] == 1: - cards_str += "Beat}}" - elif card["type"] == 2: - cards_str += f"Action{card['value']}}}}}" - elif card["type"] == 3: - if card["value"] == 20: - cards_str += "TryLow}}" - if card["value"] == 30: - cards_str += "TryMiddle}}" - if card["value"] == 40: - cards_str += "TryHigh}}" - lines.append(cards_str) - cardType = "Beat" if chara["synergy_flag"] == 1 else "Action20" if chara["synergy_flag"] == 2 else "TryHigh" if chara["synergy_flag"] == 3 else "" - lines.append(f"|miracleplus={{{{{cardType}}}}}") - - if chara["arts"] is not None: - miracles = fill_miracle_numbers(chara) - lines.append(f"|miracle={chara['arts']['actionName']}") - for idx,miracle in enumerate(miracles): - lines.append("|miracle" + str(idx + 1) + "=" + miracle.replace("\r\n", "\n").replace("\\n", "\n").replace("\\", " ")) - - if chara["special_attack"] is not None: - lines.append(f"|beatname={chara['special_attack']['actionName']}") - lines.append(f"|beatskill={chara['special_attack']['actionEffect']}") - if chara["wait_action"] is not None: - lines.append(f"|standby={chara['wait_action']['skillName']}") - lines.append(f"|standbyskill={chara['wait_action']['skillEffect']}") - if chara["ability"] is not None: - lines.append(f"|unique={chara['ability']['abilityName']}") - lines.append(f"|uniqueskill={chara['ability']['abilityEffect']}") - if chara["ability1"] is not None: - lines.append(f"|miracletrait={chara['ability1']['abilityName']}") - lines.append(f"|miracletraitskill={chara['ability1']['abilityEffect']}") - else: - lines.append(f"|miracletrait=N/A") - lines.append(f"|miracletraitskill=Not Implemented.") - if chara["ability2"] is not None: - lines.append(f"|rainbowtrait={chara['ability2']['abilityName']}") - lines.append(f"|rainbowtraitskill={chara['ability2']['abilityEffect']}") - else: - lines.append(f"|rainbowtrait=N/A") - lines.append(f"|rainbowtraitskill=Not Implemented.") - - lines.append("") - lines.append(f"|bondup={chara['kizunaupText']}") - lines.append(f"|profile={chara['flavorText']}") - lines.append(f"|greeting={chara['loginText']}") - lines.append(f"|selfintro={chara['greetingText']}") - lines.append(f"|enselfintro=") - - cos_images = "|cos = " + ",".join([costume["image_name"] for costume in chara["costumes"]]) + ",end" - cos_obtain = "|cosobt = " + ",".join([costume["obtain"] for costume in chara["costumes"]]) - cos_names = "|cosname = " + ",".join([costume["name"] for costume in chara["costumes"]]) - - lines.append("") - lines.append(cos_images) - lines.append(cos_names) - lines.append(cos_obtain) - - lines.append("}}") - - return "\n".join(lines) - - def get_costumes(self, id : int, defaultRarity : int): - if id not in self.charaClothesData: - return [] - - output = [] - data = self.charaClothesData[id] - for costume in data: - cos = { - "image_name" : f"icon dressup {costume['id']}.png", - "obtain" : "Limited" - } - cosId = costume['clothesId'] - if cosId == 1: - cos["name"] = "Default" - cos["obtain"] = "Default" - - elif cosId == 2: - cos["name"] = "Personal Fashion" - cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" - - elif cosId == 3: - cos["name"] = "Tracksuit" - cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" - - elif cosId == 4: - cos["name"] = "Park Staff" - cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" - - elif cosId == 5: - cos["name"] = "Café Uniform" - cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" - - elif cosId == 6: - cos["name"] = "Special Outfit 1" - - elif cosId == 8: - cos["name"] = "Party Dress" - cos["obtain"] = "Increase Photo Pocket level to 12" - - elif cosId == 9: - cos["name"] = "School Uniform" - - elif cosId == 10: - cos["name"] = "Gym Clothes" - - elif cosId == 11: - cos["name"] = "Raincoat" - - elif cosId == 12: - cos["name"] = "Hanamaru Animal Swimsuit" - - elif cosId == 13: - cos["name"] = "Cafe Uniform (Green)" - cos["obtain"] = "Dojo reward" - - elif cosId == 14: - cos["name"] = "Cafe Uniform (Blue)" - cos["obtain"] = "Dojo reward" - - elif cosId == 15: - cos["name"] = "Cafe Uniform (Pink)" - cos["obtain"] = "Dojo reward" - - elif cosId == 16: - cos["name"] = "Orihime Costume" - - elif cosId == 17: - cos["name"] = "Arle's Outfit" - - elif cosId == 18: - cos["name"] = "Marine Nanoda! Swimsuit" - - elif cosId == 19: - cos["name"] = "Batten Japari Dan Swimsuit" - - elif cosId == 20: - cos["name"] = "Lifeguard Swimsuit" - - elif cosId == 21: - cos["name"] = "Harvest Festival Outfit" - - elif cosId == 22: - cos["name"] = "Halloween Costume" - - elif cosId == 23: - cos["name"] = "Classic Maid Uniform" - - elif cosId == 24: - cos["name"] = "Arle's Outfit" - - elif cosId == 25: - cos["name"] = "Swimsuit" - - elif cosId == 26: - cos["name"] = "Swimsuit" - - elif cosId == 27: - cos["name"] = "Swimsuit" - - elif cosId == 28: - cos["name"] = "Matching Clothes" - - elif cosId == 29: - cos["name"] = "Frilly Frilly PPP Bikini" - - elif cosId == 30: - cos["name"] = "Venus Flower Bikini" - - elif cosId == 31: - cos["name"] = "Christmas Dress" - - elif cosId == 32: - cos["name"] = "Chinese Shrine Maiden" - - elif cosId == 33: - cos["name"] = "Chinese Dress" - - elif cosId == 34: - cos["name"] = "Hanamaru Stage Costume" - - elif cosId == 35: - cos["name"] = "Camo Outfit" - - elif cosId == 36: - cos["name"] = "でびでび・ふりる" - - elif cosId == 37: - cos["name"] = "夜に紛れし闇の衣" - - elif cosId == 38: - cos["name"] = "Santa" - - elif cosId == 39: - cos["name"] = "Fluffy Pajamas" - - elif cosId == 40: - cos["name"] = "Japanese Armor" - - elif cosId == 41: - cos["name"] = "Miko Outfit" - - elif cosId == 42: - cos["name"] = "PPP Hoodie" - - elif cosId == 43: - cos["name"] = "Blue Tracksuit" - - elif cosId == 44: - cos["name"] = "Park Staff (beige)" - - elif cosId == 45: - cos["name"] = "Sailor School Uniform" - - elif cosId == 46: - cos["name"] = "Snow Knight Dress" - - elif cosId == 47: - cos["name"] = "Sparkling Tree Dress" - - elif cosId == 48: - cos["name"] = "Retro Detective" - - elif cosId == 49: - cos["name"] = "Steampunk Detective" - - elif cosId == 50: - cos["name"] = "鞠と花びらのはんなり和風服" - - elif cosId == 51: - cos["name"] = "Numazu Deep Sea Aquarium Clothes" - - elif cosId == 52: - cos["name"] = "たのしいことをお届け!" - - elif cosId == 53: - cos["name"] = "かわいい勝負ですよ!ワンピース水着" - - elif cosId == 54: - cos["name"] = "どきどき♡ずっきゅんビキニ" - - elif cosId == 55: - cos["name"]= "のんほいパークの服" - - elif cosId == 56: - cos["name"]= "なないろのはごろも" - - elif cosId == 57: - cos["name"]= "4周年!ハッピーラッキーTシャツ" - - elif cosId == 58: - cos["name"]= "純情色のワンピース" - - else: - print(f"{costume['clothesId']}\t{costume['id']}\t{costume['name']}") - continue - - output.append(cos) - return output - diff --git a/modules/KF3/downloader.py b/modules/KF3/downloader.py deleted file mode 100644 index 1951f65..0000000 --- a/modules/KF3/downloader.py +++ /dev/null @@ -1,276 +0,0 @@ - -import base64 -import os -import gzip -import json -import asyncio -import hashlib -import platform - -import aiohttp -from UnityPy import enums - -from ..Shared.utility import divide_chunks -from ..Shared.downloading import download_bytes, download_text -from ..Shared.convert import convert, extract - -servers = [ - "https://parade-mobile-stg-app.kemono-friends-3.jp/", - "https://parade-mobile-develop01-app.kemono-friends-3.jp/", - #"https://parade-mobile-develop02-app.kemono-friends-3.jp/paradesv/", - #"https://parade-mobile-develop03-app.kemono-friends-3.jp/paradesv/", - #"https://parade-mobile-develop04-app.kemono-friends-3.jp/paradesv/", -] - -def decode(data): - # cut off the md5 checksum at the end and the four bytes at the start - hash = bytearray.fromhex(data[:-(2*16)][2*4:]) - key = hashlib.md5(bytearray.fromhex(data[:2*4])).digest() # md5 the key - # xor with the key - for i in range(0, len(hash)): - hash[i] = hash[i] ^ key[i % (len(key))] - return hash.decode("utf-8") - -def encode(text): - hashed = hashlib.md5(text.encode()).digest() - checksum = hashlib.md5((text + "DARAPAB ").encode()).digest() - key = hashlib.md5(hashed[:4]).digest() # md5 the key - # xor the data with the key - text = bytearray(text.encode()) - for i in range(0, len(text)): - text[i] = text[i] ^ key[i % (len(key))] - return hashed[:4].hex() + text.hex() + checksum.hex() - -async def download_cache(server_name, server : str): - session = aiohttp.ClientSession() - downloaded_files = [] - - if platform.system() == "Windows": - path = f"D:\\Codebase\\KFKDecrypt\\assets\\KF3\\{server_name}\\cache\\" - else: - path = f"/var/www/html/Katworks/KF/assets/KF3/{server_name}/cache/" - - os.makedirs(path, exist_ok=True) - - new_mst_ver = {} - old_mst_ver = {} - file_path_mst = path + "mstVersion.txt" - if os.path.exists(file_path_mst): - with open(file_path_mst, "rt", encoding="utf-8") as file: - old_mst_ver = dict([(entry["type"], entry["version"]) for entry in json.load(file)]) - - param = encode(json.dumps({'dmm_viewer_id':0})) - request = await download_bytes(server + "paradesv/common/MstVersion.do?param=" + param, session) - result = gzip.decompress(request) - new_mst = json.loads(result) - new_mst_ver = dict([(entry["type"], entry["version"]) for entry in new_mst["mst_ver"]]) - - for key in new_mst_ver: - file_path_gzip = path + key + ".d" - file_path_json = path + key + ".json" - if os.path.exists(file_path_gzip) and os.path.exists(file_path_json) and key in old_mst_ver: - if new_mst_ver[key] == old_mst_ver[key]: - continue - - downloaded_files.append(key) - param = encode(json.dumps({'type':key, 'dmm_viewer_id':0})) - request = await download_bytes(server + "paradesv/common/MstData.do?param=" + param, session) - result = gzip.decompress(request) - response = json.loads(result) - data = base64.b64decode(response["data"]) - with open(file_path_gzip, "wb") as file: - file.write(data) - with open(file_path_json, "wt", encoding="utf-8") as out_file: - data = gzip.decompress(data) - data = json.loads(data) - # if key == "GACHA_DATA": - # download_banners(data, server_name) - json.dump(data, out_file, ensure_ascii=False, indent=1) - - old_mst_ver[key] = new_mst_ver[key] - - with open(file_path_mst, "wt", encoding="utf-8") as file: - new_json = [{"type":type, "version":version} for type, version in zip(old_mst_ver.keys(), old_mst_ver.values())] - json.dump(new_json, file, ensure_ascii=False) - - await session.close() - return downloaded_files - -async def download_banners(gacha_data, server_name, session): - path = f"/var/www/html/Katworks/KF/assets/KF3/{server_name}/banners/" - os.makedirs(path, exist_ok=True) - - for entry in gacha_data: - banner_name = entry["banner"] - if banner_name == "" or banner_name == None: - continue - banner_name += ".png" - file_path = path + banner_name - if os.path.exists(file_path): - continue - - file_url = "https://parade-mobile-prod-cdn.kemono-friends-3.jp/Texture2D/GachaTop/" + banner_name - file_url_alt = "https://parade-mobile-develop01-app.kemono-friends-3.jp/Texture2D/GachaTop/" + banner_name - - status = 0 - async with session.get(file_url) as resp: - response = await resp.read() - status = resp.status - if status != 200: - async with session.get(file_url_alt) as resp: - response = await resp.read() - status = resp.status - if status != 200: continue - - with open(file_path, "wb") as file: - file.write(response) - - -async def download_files(server_name, asset_bundle_url, srv_platform : str): - def parse_ab_list(filecontent : str): - out = {} - lines = filecontent.replace("\r\n", "\n").split('\n') - for line in lines: - split = line.split('\t') - if len(split) > 1: - out[split[0]] = split[-2] - return out - - async def download_file(url_assets : str, file_name : str, download_path : str, session : aiohttp.ClientSession): - data = await download_bytes(url_assets + file_name, session) - if data != None: - with open(download_path + file_name, "wb") as file: - file.write(data) - if server_name == "develop01": - convert_path = f"/var/www/html/Katworks/KF/assets/KF3/WebGL/assets/" + file_name - extract_path = f"/var/www/html/Katworks/KF/assets/KF3/extracted/" - try: - convert(data, convert_path, enums.BuildTarget.WebGL) - except: - with open(convert_path, "wb") as file: - file.write(data) - if file_name.endswith(".png"): - extract(data, extract_path) - - session = aiohttp.ClientSession() - path = f"/var/www/html/Katworks/KF/assets/KF3/{server_name}/assets/{srv_platform}/" - - os.makedirs(path, exist_ok=True) - - files_to_download = [] - url_base = asset_bundle_url + "/" + srv_platform + "/1.0.0/ja/" - url_list = url_base + "ab_list.txt" - url_env = url_base + "ab_env.txt" - url_assets = url_base + "assets/" - - file_path_env = path + "ab_env.txt" - file_path_list = path + "ab_list.txt" - old_ab_env = "" - - if os.path.exists(file_path_env): - with open(file_path_env, "rt") as file: - old_ab_env = file.read() - - new_ab_env = await download_text(url_env, session) - - if new_ab_env != old_ab_env: - old_ab_list = {} - if os.path.exists(file_path_list): - with open(file_path_list, "rt") as file: - old_ab_list = parse_ab_list(file.read()) - - new_ab_list_file = await download_text(url_list, session) - new_ab_list = parse_ab_list(new_ab_list_file) - - for key in new_ab_list: - file_path = path + key - if os.path.exists(file_path) and key in old_ab_list: - if new_ab_list[key] == old_ab_list[key]: - continue - - files_to_download.append(key) - - chunked_files_to_download = list(divide_chunks(files_to_download, 5)) - - for chunk in chunked_files_to_download: - tasks = [asyncio.create_task(download_file(url_assets, file, path, session)) for file in chunk] - await asyncio.wait(tasks) - print(chunk) - print() - - with open(file_path_env, "wt", encoding="utf-8") as file: - file.write(new_ab_env) - with open(file_path_list, "wt", encoding="utf-8") as file: - file.write(new_ab_list_file) - - await session.close() - return files_to_download - -async def convert_files(): - directory = f"/var/www/html/Katworks/KF/assets/KF3/develop01/assets/Windows/" - with open("/var/www/html/Katworks/KF/assets/KF3/lastUpdate_dev_files.json", "rt", encoding="utf-8") as file: - files_to_convert = json.load(file) - for file_name in os.listdir(directory): - if file_name not in files_to_convert: - continue - - f = os.path.join(directory, file_name) - - if not os.path.isfile(f): - return - - convert_path = f"/var/www/html/Katworks/KF/assets/KF3/WebGL/assets/" + file_name - try: - print(f) - convert(f, convert_path, enums.BuildTarget.WebGL) - except: - print("Conversion failed", f) - -async def manual(): - - # session = aiohttp.ClientSession() - - # path = f"/var/www/html/Katworks/KF/assets/KF3/{server_name}/cache/" - - # await session.close() - - # return - - downloaded_cache = {} - downloaded_files = {} - - async with aiohttp.ClientSession() as session: - param = encode(json.dumps({"version":"1.0.0","dmm_viewer_id":0,"platform":1})) - request = await download_bytes(servers[0] + "paradesv/common/GetUrl.do?param=" + param, session) - result = gzip.decompress(request) - response = json.loads(result) - asset_bundle_url = response["asset_bundle_url"] - urlName = asset_bundle_url.split("-")[2] - - print("downloading from", servers[0]) - downloaded_cache = await download_cache(urlName, servers[0]) - downloaded_files = await download_files(urlName, asset_bundle_url, "Windows") - - if downloaded_cache != [] and downloaded_cache != None: - with open("/var/www/html/Katworks/KF/assets/KF3/lastUpdate_prod_cache.json", "wt", encoding="utf-8") as file: - json.dump(downloaded_cache, file, ensure_ascii=False, indent=1) - if downloaded_files != [] and downloaded_files != None: - with open("/var/www/html/Katworks/KF/assets/KF3/lastUpdate_prod_files.json", "wt", encoding="utf-8") as file: - json.dump(downloaded_files, file, ensure_ascii=False, indent=1) - - print("downloading from", servers[1]) - asset_bundle_url = "https://parade-mobile-develop01-app.kemono-friends-3.jp/AssetBundles/0.0.0/latest" - urlName = asset_bundle_url.split("-")[2] - downloaded_cache = await download_cache(urlName, servers[1]) - downloaded_files = await download_files(urlName, asset_bundle_url, "Windows") - - if downloaded_cache != [] and downloaded_cache != None: - with open("/var/www/html/Katworks/KF/assets/KF3/lastUpdate_dev_cache.json", "wt", encoding="utf-8") as file: - json.dump(downloaded_cache, file, ensure_ascii=False, indent=1) - if downloaded_files != [] and downloaded_files != None: - with open("/var/www/html/Katworks/KF/assets/KF3/lastUpdate_dev_files.json", "wt", encoding="utf-8") as file: - json.dump(downloaded_files, file, ensure_ascii=False, indent=1) - -if __name__ == "__main__": - asyncio.run(manual()) - #asyncio.run(convert_files()) \ No newline at end of file diff --git a/modules/KF3/endpoints/friend.py b/modules/KF3/endpoints/friend.py deleted file mode 100644 index dc28d97..0000000 --- a/modules/KF3/endpoints/friend.py +++ /dev/null @@ -1,30 +0,0 @@ -from flask_restful import Resource -from flask import current_app as app -from flask import request -import json - -class KF3_Friend(Resource): - def get(self, id:int): - from ..database import Database - db : Database = app.databases["KF3"] - - if "wiki" in request.args: - result = db.get_chara_wiki(id) - - response = app.response_class( - response=result, - status=200, - mimetype='text/plain' - ) - else: - result = db.get_chara(id) - result = json.dumps(result, ensure_ascii=False, indent=1) - - response = app.response_class( - response=result, - status=200, - mimetype='application/json' - ) - - response.headers.add("Access-Control-Allow-Origin", "*") - return response \ No newline at end of file diff --git a/modules/KF3/endpoints/friends.py b/modules/KF3/endpoints/friends.py deleted file mode 100644 index 84b1356..0000000 --- a/modules/KF3/endpoints/friends.py +++ /dev/null @@ -1,28 +0,0 @@ -import json -from flask_restful import Resource -from flask import current_app as app -from flask import request - -class KF3_Friends(Resource): - def get(self): - from ..database import Database - db : Database = app.databases["KF3"] - - result = [] - for value in db.processed_friends.values(): - result.append({"id": value["id"], "name": value["nameEn"], "startTime" : value["startTime"], "startTimeRaw" : value["startTimeRaw"]}) - - sort_arg = request.args["sort"] if "sort" in request.args and request.args["sort"] in result[0] else "id" - if sort_arg == "startTime": - sort_arg = "startTimeRaw" - - result = sorted(result, key=lambda f: f[sort_arg]) - - response = app.response_class( - response=json.dumps(result, ensure_ascii=False, indent=1), - status=200, - mimetype='application/json' - ) - response.headers.add("Access-Control-Allow-Origin", "*") - - return response \ No newline at end of file diff --git a/modules/KF3/endpoints/item.py b/modules/KF3/endpoints/item.py deleted file mode 100644 index 5b40fe6..0000000 --- a/modules/KF3/endpoints/item.py +++ /dev/null @@ -1,21 +0,0 @@ -from flask_restful import Resource -from flask import current_app as app -from flask import request_tearing_down -import json - -class KF3_Item(Resource): - def get(self, id:int): - from ..database import Database - db : Database = app.databases["KF3"] - - result = db.get_item(id) - result = json.dumps(result, ensure_ascii=False, indent=1) - - response = app.response_class( - response=result, - status=200, - mimetype='application/json' - ) - - response.headers.add("Access-Control-Allow-Origin", "*") - return response \ No newline at end of file diff --git a/modules/KF3/endpoints/items.py b/modules/KF3/endpoints/items.py deleted file mode 100644 index a264bc1..0000000 --- a/modules/KF3/endpoints/items.py +++ /dev/null @@ -1,21 +0,0 @@ -import json -from flask_restful import Resource -from flask import current_app as app - -class KF3_Items(Resource): - def get(self): - from ..database import Database - db : Database = app.databases["KF3"] - - result = [] - for value in db.itemCommon.values(): - result.append({"id": value["id"], "name": value["name"], "iconName" : value["iconName"], "flavorText" : value["flavorText"]}) - - response = app.response_class( - response=json.dumps(result, ensure_ascii=False, indent=1), - status=200, - mimetype='application/json' - ) - response.headers.add("Access-Control-Allow-Origin", "*") - - return response \ No newline at end of file diff --git a/modules/KF3/endpoints/update.py b/modules/KF3/endpoints/update.py deleted file mode 100644 index a8716fd..0000000 --- a/modules/KF3/endpoints/update.py +++ /dev/null @@ -1,36 +0,0 @@ -import asyncio -from flask_restful import Resource -from flask import current_app as app -from ..downloader import manual - -class KF3_Update(Resource): - def post(self): - from ..database import Database - db : Database = app.databases["KF3"] - - if db.update_in_progress: - response = app.response_class( - response="update in progress", - status=200, - mimetype='text/plain' - ) - else: - self.perform_update() - response = app.response_class( - response="update started, reload the site in a while", - status=200, - mimetype='text/plain' - ) - - response.headers.add("Access-Control-Allow-Origin", "*") - return response - - def get(self): - return self.post() - - def perform_update(self): - from ..database import Database - db : Database = app.databases["KF3"] - print("Update") - #await manual() - db.reload_data() diff --git a/modules/KF3/statCalculation.py b/modules/KF3/statCalculation.py deleted file mode 100644 index b4054b2..0000000 --- a/modules/KF3/statCalculation.py +++ /dev/null @@ -1,133 +0,0 @@ -import json -import math -from collections import defaultdict - -def GetName(charaData): - name = "no name" - if charaData is not None: - if charaData["nameEn"] != "": - name = charaData["nameEn"] - elif charaData["name"] != "": - name = charaData["name"] - if charaData["nickname"] != "": - name += " " + charaData["nickname"] - return name - -def GetAttackBase(alphaBase, level): - if level <= alphaBase["atkLvMiddleNum"]: - t = (level - 1) / (alphaBase["atkLvMiddleNum"]-1) - return lerp(alphaBase["atkParamLv1"], alphaBase["atkParamLvMiddle"], t) - else: - t = (level - alphaBase["atkLvMiddleNum"]) / (99 - alphaBase["atkLvMiddleNum"]) - return lerp(alphaBase["atkParamLvMiddle"], alphaBase["atkParamLv99"], t) - -def GetDefenseBase(alphaBase, level): - if level <= alphaBase["defLvMiddleNum"]: - t = (level - 1) / (alphaBase["defLvMiddleNum"]-1) - return lerp(alphaBase["defParamLv1"], alphaBase["defParamLvMiddle"], t) - else: - t = (level - alphaBase["defLvMiddleNum"]) / (99 - alphaBase["defLvMiddleNum"]) - return lerp(alphaBase["defParamLvMiddle"], alphaBase["defParamLv99"], t) - -def GetHealthBase(alphaBase, level): - if level <= alphaBase["hpLvMiddleNum"]: - t = (level - 1) / (alphaBase["hpLvMiddleNum"]-1) - return lerp(alphaBase["hpParamLv1"], alphaBase["hpParamLvMiddle"], t) - else: - t = (level - alphaBase["hpLvMiddleNum"]) / (99 - alphaBase["hpLvMiddleNum"]) - return lerp(alphaBase["hpParamLvMiddle"], alphaBase["hpParamLv99"], t) - -def get_all_stats(chara, alphaBase, max_level: bool, rising_status_pattern): - level = 99 if max_level else 1 - - hp = GetHealthBase(alphaBase, level) - atk = GetAttackBase(alphaBase, level) - defe = GetDefenseBase(alphaBase, level) - - if max_level: - starBoost = 1 + (chara["rankHigh"] - 1) * 0.02 - hp = int(math.ceil((hp + chara["promoteBonus"]["hp"]) * starBoost) + chara["costumeBonus"]["hp"]) - atk = int(math.ceil((atk + chara["promoteBonus"]["atk"]) * starBoost) + chara["costumeBonus"]["atk"]) - defe = int(math.ceil((defe + chara["promoteBonus"]["def"]) * starBoost) + chara["costumeBonus"]["def"]) - evd = 10 * alphaBase["avoidRatio"] + chara["promoteBonus"]["evd"] - beatBonus = chara["promoteBonus"]["beat"] / 10 - actBonus = chara["promoteBonus"]["act"] / 10 - tryBonus = chara["promoteBonus"]["try"] / 10 - - if rising_status_pattern is not None: - for i in range(51): - hp += rising_status_pattern["hp"] - atk += rising_status_pattern["atk"] - defe += rising_status_pattern["def"] - else: - starBoost = 1 + (chara["rankLow"] - 1) * 0.02 - hp = int(math.ceil(hp * starBoost)) - atk = int(math.ceil(atk * starBoost)) - defe = int(math.ceil(defe * starBoost)) - evd = 10 * alphaBase["avoidRatio"] - beatBonus = 0 - actBonus = 0 - tryBonus = 0 - - - status = hp * 8 / 10 - if hp * 8 % 10 > 0: - status += 1 - status += atk * 3 + defe * 2 - - result = { - "level" : level, - "status" : int(status), - "wr" : chara["rankHigh"] if max_level else chara["rankLow"], - "hp" : hp, - "atk" : atk, - "def" : defe, - "evd" : evd, - "beat" : beatBonus, - "act" : actBonus, - "try" : tryBonus, - } - - return result - -def fill_miracle_numbers(chara): - output = [] - damages = {} - buffs = defaultdict(list) - - for i, damage_param in enumerate(chara["arts"]["damageList"]): - damages[f"[DAMAGE{i}]"] = damage_param - - for i, buff_param in enumerate(chara["arts"]["buffList"]): - buffs[f"[BUFF{i}]"].append(buff_param) - buffs[f"[HEAL{i}]"].append(buff_param) - buffs[f"[INCREMENT{i}]"].append(buff_param) - - for i in range(1, 7): - base_text = chara["arts"]["actionEffect"] - for damage_key, damage_value in damages.items(): - new_value = str(int(damage_value["damageRate"] * (1 + damage_value["growthRate"] * (i - 1)) * 100 + 0.01)) - base_text = base_text.replace(damage_key, new_value) - - for buff_key, buff_value_list in buffs.items(): - buff_value = buff_value_list[0] - if buff_key[1] == 'B': - new_value = str(int(abs(buff_value["coefficient"] * (1 + buff_value["growthRate"] * (i - 1)) - 1) * 100 + 0.01)) - base_text = base_text.replace(buff_key, new_value) - elif buff_key[1] == 'H': - new_value = str(int(buff_value["coefficient"] * (1 + buff_value["growthRate"] * (i - 1)) * 100 + 0.01)) - base_text = base_text.replace(buff_key, new_value) - elif buff_key[1] == 'I': - new_value = str(int(abs(buff_value["increment"]) * (1 + buff_value["growthRate"] * (i - 1)) + 0.01)) - base_text = base_text.replace(buff_key, new_value) - - output.append(base_text) - - return output - -def toJSON(chara): - return json.dumps(chara, default=lambda o: o.__dict__, - sort_keys=True, indent=1, ensure_ascii=False) - -def lerp(a, b, t): - return (1 - t) * a + t * b \ No newline at end of file diff --git a/modules/Kingdom/database.py b/modules/Kingdom/database.py deleted file mode 100644 index f324cf0..0000000 --- a/modules/Kingdom/database.py +++ /dev/null @@ -1,298 +0,0 @@ -import json -import platform -from .numeric import numeric -from .kemono import clean_skill_string, calculate_stats, fill_miracle, get_awaken_materials, get_skill_materials - -from .endpoints.item import Kingdom_Item -from .endpoints.items import Kingdom_Items -from .endpoints.update import Kingdom_Update -from .endpoints.friend import Kingdom_Friend -from .endpoints.friends import Kingdom_Friends - -#element 1-orange 2-blue 3-green -#showSkillType 1-control 2-guard 3-heal 4-support 5-assault 6-aoe - -class Database: - processed_friends = {} - item_stages = {} - - def __init__(self, api) -> None: - app = api.app - if "Kingdom" in app.databases: - del app.databases["Kingdom"] - - self.reload_data() - - app.databases["Kingdom"] = self - - api.add_resource(Kingdom_Friend, "/Kingdom/Friend/") - api.add_resource(Kingdom_Friends, "/Kingdom/Friends") - api.add_resource(Kingdom_Item, "/Kingdom/Item/") - api.add_resource(Kingdom_Items, "/Kingdom/Items") - api.add_resource(Kingdom_Update, "/Kingdom/Update") - - def reload_data(self): - if platform.system() == "Windows": - NUMERICPATH = "H:\\Apache\\Katworks\\KF\\assets\\Kingdom1\\NumericData" - else: - NUMERICPATH = "/var/www/html/Katworks/KF/assets/Kingdom1/NumericData" - - self.kfk_drop = numeric(NUMERICPATH +"/Drop.num", 0) - self.kfk_combo = numeric(NUMERICPATH +"/Combo.num", 0) - self.kfk_skill = numeric(NUMERICPATH +"/Skill.num", 0) - self.kfk_equip = numeric(NUMERICPATH +"/Equip.num", 0) - self.kfk_kemono = numeric(NUMERICPATH +"/Kemono.num", 0) - self.kfk_stages = numeric(NUMERICPATH +"/Stage.num", 0) - self.kfk_drop_group = numeric(NUMERICPATH +"/DropGroup.num", 0) - self.kfk_trust_attri = numeric(NUMERICPATH +"/TrustAttri.num", 0) - self.kfk_kemono_skill = numeric(NUMERICPATH +"/KemonoSkill.num", 0) - self.kfk_kemono_power = numeric(NUMERICPATH +"/KemonoPower.num", 0) - self.kfk_kemono_waken = numeric(NUMERICPATH +"/KemonoWaken.num", 0) - self.kfk_en_item = numeric(NUMERICPATH + "/en/Item.ntxt", 1) - self.kfk_en_equip = numeric(NUMERICPATH + "/en/Equip.ntxt", 1) - self.kfk_en_skill = numeric(NUMERICPATH + "/en/Skill.ntxt", 1) - self.kfk_en_combo = numeric(NUMERICPATH + "/en/Combo.ntxt", 1) - self.kfk_en_kemono = numeric(NUMERICPATH + "/en/Kemono.ntxt", 1) - self.kfk_en_kemono_WkPaDc = numeric(NUMERICPATH + "/en/KemonoWkPaDc.ntxt", 1) - self.kfk_en_kemono_power = numeric(NUMERICPATH + "/en/KemonoPower.ntxt", 1) - self.kfk_en_kemono_waken = numeric(NUMERICPATH + "/en/KemonoWaken.ntxt", 1) - self.kfk_en_kemono_garden = numeric(NUMERICPATH + "/en/KemonoGarden.ntxt", 1) - self.kfk_en_str = numeric(NUMERICPATH + "/en/Str.ntxt", 1) - - self.process_friends() - self.process_stages() - - def process_friends(self): - self.processed_friends = {} - for friend_id in self.kfk_kemono.indexed_data: - if friend_id > 19000: - continue - - friend = self.kfk_kemono.get(friend_id) - - friend["comboSkill"] = "" - combo_friends = [] - for entry in friend["combo"]: - combo = self.kfk_combo.get(entry) - if combo is not None: - combo_en = self.kfk_en_combo.get(combo["comboSkill"]) - friend["comboSkill"] = {"name":combo_en["comboName"], "desc":combo_en["desc"]} - for combo_friend_id in combo["comboKemono"]: - combo_friend = self.kfk_en_kemono.get(combo_friend_id) - if combo_friend is not None and "name" in combo_friend: - combo_friends.append(combo_friend["name"]) - else: - combo_friends.append(combo_friend_id) - - friend["combo"] = combo_friends - - limit_breaks = [] - for bk_value in self.kfk_kemono_power.indexed_data.values(): - if len(limit_breaks) == 5: - break - if(bk_value["kemonosn"] == friend_id): - if bk_value["effectType1"] == 0: - pass - if bk_value["effectType1"] == 1 or bk_value["effectType1"] == 3: - lb = self.kfk_en_kemono_power.get(friend_id * 100 + bk_value["powerlevel"]) - if "name" in lb: - limit_breaks.append({"name": lb["name"], "desc": lb["describe"]}) - else: - limit_breaks.append({"name": None, "desc": lb["describe"]}) - if bk_value["effectType1"] == 2: - raise Exception() - if bk_value["effectType1"] == 4: - lb = self.kfk_en_kemono_WkPaDc.get(bk_value['effectParam1'][0] * 100) - if lb is None: - lb = {"name": None, "describe": None} - limit_breaks.append({"name": lb["name"], "desc": clean_skill_string(lb["describe"])}) - - habits = [] - for habit in friend["habitSn"]: - hbt = self.kfk_en_kemono_WkPaDc.get(habit*100) - if hbt is None: - hbt = {"name": None, "describe": None} - habits.append({"name":hbt["name"], "desc":hbt["describe"]}) - for i in range (3): - if len(habits) < i+1: - habits.append({"name":None, "desc":None}) - - awakens = [] - max_levels = [] - for i in range(3): - awaken_id = friend_id * 100 + i - awaken = self.kfk_kemono_waken.get(awaken_id) - awaken1 = self.kfk_en_kemono_waken.get(awaken_id) - if awaken1 == None: - awaken1 = {"describe":"missing", "brilliance":"missing"} - if awaken != None and awaken1 != None: - awaken["en"] = awaken1 - awakens.append(awaken) - max_levels.append(awaken["levelmax"]) - - def validateValue(obj, valName): - if obj[valName] is None: - obj[valName] = {"name": "", "desc": ""} - if "desc" not in obj[valName]: - obj[valName]["desc"] = None - - friend["name"] = self.kfk_en_kemono.get(friend_id)["name"] - ele = friend["element"] - friend["color"] = "Orange" if ele == 1 else "Green" if ele == 2 else "Blue" if ele == 3 else "Error" - ele = friend["showSkillType"] - friend["role"] = "Control" if ele == 1 else "Guard" if ele == 2 else "Healer" if ele == 3 else "Support" if ele == 4 else "Assault" if ele == 5 else "Assault (AOE)" if ele == 6 else "Error" - friend["maxLvl"] = max_levels - friend["awakenings"] = awakens - friend["maxAwaken"] = max_awaken = len(awakens)-1 - friend["base_stats"] = calculate_stats(friend, 0, 1) - friend["max_stats"] = calculate_stats(friend, max_awaken, friend["maxLvl"][max_awaken], trust_percent=100, trustAttri= next((e for e in self.kfk_trust_attri.indexed_data.values() if e["kemonoSn"] == friend_id), None)) - friend["max_miracle"] = 13 if friend["star"] == 6 or friend["star"] == 5 else 12 if friend["star"] == 4 else 11 - friend["collisionSkill"] = self.kfk_en_skill.get(friend["collisionSkill"]) - friend["gardenSpec1"] = self.kfk_en_kemono_garden.get(friend["gardenSpec1"]) - friend["gardenSpec2"] = self.kfk_en_kemono_garden.get(friend["gardenSpec2"]) - friend["gardenSpec3"] = self.kfk_en_kemono_garden.get(friend["gardenSpec3"]) - friend["describe"] = self.kfk_en_str.get(friend["describe"]) - - friend["attack"] = self.kfk_en_skill.get(friend["attack"]) - - skill_id = friend["skill"] - friend["skill"] = self.kfk_en_skill.get(skill_id) - friend["skill1"] = self.kfk_en_skill.get(skill_id+1) if max_awaken >= 1 else None - friend["skill2"] = self.kfk_en_skill.get(skill_id+2) if max_awaken >= 2 else None - friend["spAttack"] = self.kfk_en_skill.get(friend["spAttack"]) - friend["habitSn"] = habits - friend["limitBreaks"] = limit_breaks - - validateValue(friend, "collisionSkill") - validateValue(friend, "attack") - validateValue(friend, "skill") - validateValue(friend, "skill1") - validateValue(friend, "skill2") - validateValue(friend, "spAttack") - - self.processed_friends[friend_id] = friend - - def process_stages(self): - self.item_stages = {} - for stage in self.kfk_stages.indexed_data.values(): - merged_array = stage["fastReward2_star3"] + stage["fastReward3_star3"] - if len(merged_array) > 0: - for drop_sn in merged_array: - drop = self.kfk_drop.get(drop_sn) - for drop_group_sn in drop["groupSn"]: - drop_group = self.kfk_drop_group.get(drop_group_sn) - if drop_group["type"] == 2 or drop_group["type"] == 1: - #materials - item_id = drop_group["content"] - item = self.kfk_en_item.get(item_id) - if item_id not in self.item_stages: - self.item_stages[item_id] = [] - if stage not in self.item_stages[item_id]: - self.item_stages[item_id].append(stage) - - def get_chara(self, id : int): - #return next(lambda f: f["sn"] == id, None) - if id not in self.processed_friends: - return None - else: - friend = self.processed_friends[id] - data = { - "id": friend["sn"], - "name": friend["name"], - "element": friend["color"], - "role": friend["role"], - "star": friend["star"], - "combo": friend["combo"], - "comboSkill": friend["comboSkill"], - "describe": friend["describe"], - "awakenings": friend["awakenings"], - "max_miracle": friend["max_miracle"], - "stats": { - "base":friend["base_stats"], - "max":friend["max_stats"] - }, - "collision": {"name": friend["collisionSkill"]["name"], "desc": clean_skill_string(friend["collisionSkill"]["desc"])}, - "attack": {"name": friend["attack"]["name"], "desc": clean_skill_string(friend["attack"]["desc"])}, - "skills":[ - {"name": friend["skill"]["name"], "desc": fill_miracle(friend["skill"]["desc"], miracle_level=friend["max_miracle"], kemono_level=friend["maxLvl"][-1])}, - {"name": friend["skill1"]["name"], "desc": fill_miracle(friend["skill1"]["desc"], miracle_level=friend["max_miracle"], kemono_level=friend["maxLvl"][-1])}, - {"name": friend["skill2"]["name"], "desc": fill_miracle(friend["skill2"]["desc"], miracle_level=friend["max_miracle"], kemono_level=friend["maxLvl"][-1])}, - ], - "garden":[ - friend["gardenSpec1"], - friend["gardenSpec2"], - friend["gardenSpec3"], - ], - "habits":[ - {"name": friend["habitSn"][0]["name"], "desc": clean_skill_string(friend["habitSn"][0]["desc"])}, - {"name": friend["habitSn"][1]["name"], "desc": clean_skill_string(friend["habitSn"][1]["desc"])}, - {"name": friend["habitSn"][2]["name"], "desc": clean_skill_string(friend["habitSn"][2]["desc"])} - ], - "limitBreaks":[ - friend["limitBreaks"][0], - friend["limitBreaks"][1], - friend["limitBreaks"][2], - friend["limitBreaks"][3], - friend["limitBreaks"][4] - ], - "awakenMaterials": get_awaken_materials(friend, self.kfk_kemono_waken, self.kfk_en_item), - "skillMaterials": get_skill_materials(friend, self.kfk_kemono_skill, self.kfk_en_item) - } - - return json.dumps(data, sort_keys=False, indent=1, ensure_ascii=False) - - def get_chara_wiki(self, id : int): - friend = self.processed_friends[id] - lines = [] - lines.append("|name=" + friend["name"]) - - #stats - lines.append("|maxLvls=" + ",".join([str(lvl) for lvl in friend["maxLvl"]])) - lines.append("|hp=" + str(friend["base_stats"]["hp"])) - lines.append("|maxhp=" + str(friend["max_stats"]["hp"])) - lines.append("|atk=" + str(friend["base_stats"]["patk"])) - lines.append("|maxatk=" + str(friend["max_stats"]["patk"])) - lines.append("|satk=" + str(friend["base_stats"]["satk"])) - lines.append("|maxsatk=" + str(friend["max_stats"]["satk"])) - lines.append("|pdef=" + str(friend["base_stats"]["pdef"])) - lines.append("|maxpdef=" + str(friend["max_stats"]["pdef"])) - lines.append("|sdef=" + str(friend["base_stats"]["sdef"])) - lines.append("|maxsdef=" + str(friend["max_stats"]["sdef"])) - lines.append("|speed=" + str(friend["base_stats"]["speed"])) - lines.append("|maxspeed=" + str(friend["max_stats"]["speed"])) - - #skills - lines.append("|collisionSkill=" + friend["collisionSkill"]["name"]) - lines.append("|collisionSkillEffect=" + clean_skill_string(friend["collisionSkill"]["desc"])) - lines.append("|attack=" + friend["attack"]["name"]) - lines.append("|attackEffect=" + clean_skill_string(friend["attack"]["desc"])) - lines.append("|skill1=" + friend["skill"]["name"]) - lines.append("|skill1Effect=" + clean_skill_string(friend["skill"]["desc"])) - lines.append("|skill2=" + friend["skill1"]["name"]) - lines.append("|skill2Effect=" + clean_skill_string(friend["skill1"]["desc"])) - lines.append("|skill3=" + friend["skill2"]["name"]) - lines.append("|skill3Effect=" + clean_skill_string(friend["skill2"]["desc"])) - lines.append("|spAttack=" + friend["spAttack"]["name"]) - lines.append("|spAttackEffect=" + clean_skill_string(friend["spAttack"]["desc"])) - - for i in range(0,5): - lb = friend['limitBreaks'][i] - if lb["name"] is None: - lines.append(f"|b{i+1}={friend['limitBreaks'][i]['desc']}") - else: - lines.append(f"|b{i+1}='''{friend['limitBreaks'][i]['name']}'''\n{friend['limitBreaks'][i]['desc']}") - - return "\n".join(lines) - - def get_item(self, id): - result = {"item": None, "stages": []} - item = self.kfk_en_item.get(id) - if item is None: - return result - - result["item"] = item - - if id in self.item_stages: - result["stages"] = self.item_stages[id] - - return json.dumps(result, sort_keys=False, indent=1, ensure_ascii=False) diff --git a/modules/Kingdom/endpoints/friend.py b/modules/Kingdom/endpoints/friend.py deleted file mode 100644 index 00ecb20..0000000 --- a/modules/Kingdom/endpoints/friend.py +++ /dev/null @@ -1,25 +0,0 @@ -from flask_restful import Resource -from flask import current_app as app -from flask import request - -class Kingdom_Friend(Resource): - def get(self, id:int): - if "wiki" in request.args: - result = app.databases["Kingdom"].get_chara_wiki(id) - - response = app.response_class( - response=result, - status=200, - mimetype='text/plain' - ) - else: - result = app.databases["Kingdom"].get_chara(id) - - response = app.response_class( - response=result, - status=200, - mimetype='application/json' - ) - - response.headers.add("Access-Control-Allow-Origin", "*") - return response \ No newline at end of file diff --git a/modules/Kingdom/endpoints/friends.py b/modules/Kingdom/endpoints/friends.py deleted file mode 100644 index a701509..0000000 --- a/modules/Kingdom/endpoints/friends.py +++ /dev/null @@ -1,21 +0,0 @@ -import json -from flask_restful import Resource -from flask import current_app as app, jsonify - -class Kingdom_Friends(Resource): - def get(self): - db = app.databases["Kingdom"] - result = [] - for value in db.processed_friends.values(): - result.append({"id": value["sn"], "name": value["name"], "describe": value["describe"]["content"] if "content" in value["describe"] else ""}) - - result = sorted(result, key=lambda f: f["id"]) - - response = app.response_class( - response=json.dumps(result, ensure_ascii=False, indent=1), - status=200, - mimetype='application/json' - ) - response.headers.add("Access-Control-Allow-Origin", "*") - - return response \ No newline at end of file diff --git a/modules/Kingdom/endpoints/item.py b/modules/Kingdom/endpoints/item.py deleted file mode 100644 index a51c492..0000000 --- a/modules/Kingdom/endpoints/item.py +++ /dev/null @@ -1,16 +0,0 @@ -from flask_restful import Resource -from flask import current_app as app -from flask import request - -class Kingdom_Item(Resource): - def get(self, id:int): - result = app.databases["Kingdom"].get_item(id) - - response = app.response_class( - response=result, - status=200, - mimetype='application/json' - ) - - response.headers.add("Access-Control-Allow-Origin", "*") - return response \ No newline at end of file diff --git a/modules/Kingdom/endpoints/items.py b/modules/Kingdom/endpoints/items.py deleted file mode 100644 index aae3614..0000000 --- a/modules/Kingdom/endpoints/items.py +++ /dev/null @@ -1,23 +0,0 @@ -import json -from flask_restful import Resource -from flask import current_app as app, jsonify - -class Kingdom_Items(Resource): - def get(self): - db = app.databases["Kingdom"] - result = [] - for key in db.kfk_en_item.indexed_data.keys(): - new_value = db.kfk_en_item.indexed_data[key] - new_value["id"] = key - result.append(new_value) - - result = sorted(result, key=lambda f: f["id"]) - - response = app.response_class( - response=json.dumps(result, ensure_ascii=False, indent=1), - status=200, - mimetype='application/json' - ) - response.headers.add("Access-Control-Allow-Origin", "*") - - return response \ No newline at end of file diff --git a/modules/Kingdom/endpoints/update.py b/modules/Kingdom/endpoints/update.py deleted file mode 100644 index 2f88678..0000000 --- a/modules/Kingdom/endpoints/update.py +++ /dev/null @@ -1,28 +0,0 @@ -from flask_restful import Resource -from flask import current_app as app -from flask import request -import json - -class Kingdom_Update(Resource): - def post(self): - from ..database import Database - db : Database = app.databases["Kingdom"] - try: - db.reload_data() - response = app.response_class( - response="update successful", - status=200, - mimetype='text/plain' - ) - except: - response = app.response_class( - response="update failed", - status=500, - mimetype='text/plain' - ) - - response.headers.add("Access-Control-Allow-Origin", "*") - return response - - def get(self): - return self.post() \ No newline at end of file diff --git a/modules/Kingdom/kemono.py b/modules/Kingdom/kemono.py deleted file mode 100644 index 71cba81..0000000 --- a/modules/Kingdom/kemono.py +++ /dev/null @@ -1,161 +0,0 @@ -import re -import math - -def get_skill_materials(friend, kfk_kemono_skill, kfk_en_item): - items_total = [] - kfk_skill = sorted(filter(lambda s: s["kemonosn"] == friend["sn"] and s["type"] == 2, kfk_kemono_skill.indexed_data.values()), key = lambda s: s["sn"]) - for idx, s in enumerate(kfk_skill): - if len(s["item"]) == 0: - continue - items_total.append( - { - "lvl": s["skilllevel"], - "awakenReq": s["wakenlevel"], - "lvlReq": s["kemonolevel"], - "items": list([{"count": num, "name": kfk_en_item.get(item)["name"]} for item, num in zip(s["item"], s["itemnum"])]) - } - ) - return items_total - -def get_awaken_materials(friend, kfk_kemono_waken, kfk_en_item): - items_total = [] - kfk_waken = sorted(filter(lambda s: s["kemonosn"] == friend["sn"], kfk_kemono_waken.indexed_data.values()), key = lambda s: s["sn"]) - for idx, s in enumerate(kfk_waken): - if len(s["item"]) == 0: - continue - items_total.append( - { - "lvl": idx+1, - "items": list([{"count": num, "name": kfk_en_item.get(item)["name"]} for item, num in zip(s["item"], s["itemnum"])]) - } - ) - return items_total - -def calculate_stats(friend, awaken : int, level : int, trust_percent = 0, trustAttri = None): - stats_array = friend["attrScriptParam"] - max_levels = friend["maxLvl"] - max_awaken = friend["maxAwaken"] - - awaken_step = max_awaken + 2 - - atk_ptr = 1 * awaken_step - satk_ptr = 2 * awaken_step - pdef_ptr = 3 * awaken_step - sdef_ptr = 4 * awaken_step - hp_ptr = 5 * awaken_step - speed_ptr = 6 * awaken_step - - stats = {"patk":stats_array[atk_ptr], "satk":stats_array[satk_ptr], "pdef":stats_array[pdef_ptr], "sdef":stats_array[sdef_ptr], "hp":stats_array[hp_ptr], "speed":stats_array[speed_ptr]} - for i in range(awaken + 1): - if i == awaken: - max_level = level - else: - max_level = max_levels[i] - - stats["patk"] += stats_array[atk_ptr + 1 + i] * (max_level-1) - stats["satk"] += stats_array[satk_ptr + 1 + i] * (max_level-1) - stats["pdef"] += stats_array[pdef_ptr + 1 + i] * (max_level-1) - stats["sdef"] += stats_array[sdef_ptr + 1 + i] * (max_level-1) - stats["hp"] += stats_array[hp_ptr + 1 + i] * (max_level-1) - stats["speed"] += stats_array[speed_ptr + 1 + i] * (max_level-1) - - if trust_percent > 0 and trustAttri != None: - if trustAttri["attribute1"] == 1001: - stats["hp"] += trustAttri["attributeValue1"][trust_percent] - elif trustAttri["attribute1"] == 1021: - stats["patk"] += trustAttri["attributeValue1"][trust_percent] - elif trustAttri["attribute1"] == 1031: - stats["satk"] += trustAttri["attributeValue1"][trust_percent] - else: - print(trustAttri["attribute1"]) - - if trustAttri["attribute2"] == 1001: - stats["hp"] += trustAttri["attributeValue2"][trust_percent] - elif trustAttri["attribute2"] == 1021: - stats["patk"] += trustAttri["attributeValue2"][trust_percent] - elif trustAttri["attribute2"] == 1031: - stats["satk"] += trustAttri["attributeValue2"][trust_percent] - else: - print(trustAttri["attribute2"]) - - stats["patk"] = int(round(stats["patk"])) - stats["satk"] = int(round(stats["satk"])) - stats["pdef"] = int(round(stats["pdef"])) - stats["sdef"] = int(round(stats["sdef"])) - stats["hp"] = int(round(stats["hp"])) - stats["speed"] = int(round(stats["speed"])) - - return stats - -def get_stat_growth(friend, awaken : int): - stats_array = friend["attrScriptParam"] - max_awaken = friend["maxAwaken"] - - awaken_step = max_awaken + 2 - - atk_ptr = 1 * awaken_step - satk_ptr = 2 * awaken_step - pdef_ptr = 3 * awaken_step - sdef_ptr = 4 * awaken_step - hp_ptr = 5 * awaken_step - speed_ptr = 6 * awaken_step - - stats = {} - i = awaken - stats["patk"] += stats_array[atk_ptr + 1 + i] - stats["satk"] += stats_array[satk_ptr + 1 + i] - stats["pdef"] += stats_array[pdef_ptr + 1 + i] - stats["sdef"] += stats_array[sdef_ptr + 1 + i] - stats["hp"] += stats_array[hp_ptr + 1 + i] - stats["speed"] += stats_array[speed_ptr + 1 + i] - - return stats - - -def clean_skill_string(input_string): - if input_string is None: - return None - - color_tags_pattern = r"(.*?)<\/color>" - output_string = re.sub(color_tags_pattern, r"\1", input_string) - - link_tags_pattern = r"(.*?)<\/link>" - output_string = re.sub(link_tags_pattern, r"\1", output_string) - - return output_string - -def fill_miracle(input_string, miracle_level = 1, kemono_level = 0): - if input_string is None: - return "" - - pattern = r"\{\{(.*?)\}\}" - def replace_match(match): - key : str = match.group(1).strip() - - if key.startswith("skill | SkillDesc:"): - values = key.replace("skill | SkillDesc:", "").split(',') - values = [float(value) for value in values] - level_up_values = [ - values[1], - values[1], - values[1], - values[2], - values[2], - values[2], - values[3], - values[3], - values[3], - values[4], - values[4], - values[4], - ] - base_value = values[0] - level_up_value = sum(level_up_values[0:miracle_level-1]) + values[5] - level_bonus = (kemono_level - values[7]) * values[6] - return str(math.floor(base_value + level_up_value + level_bonus)) - - return "{{" + key + "}}" - - output_string = re.sub(pattern, replace_match, input_string) - - return clean_skill_string(output_string) \ No newline at end of file diff --git a/modules/Kingdom/numeric.py b/modules/Kingdom/numeric.py deleted file mode 100644 index 5336c60..0000000 --- a/modules/Kingdom/numeric.py +++ /dev/null @@ -1,21 +0,0 @@ -import json - - -class numeric: - def __init__(self, file_path:str, type:int): - self.indexed_data = {} - with open(file_path, "rt", encoding="utf-8") as f: - if type == 0: - values = json.load(f)["Data"] - for value in values: - self.indexed_data[value["sn"]] = value - if type == 1: - values = json.load(f) - for i in range(0, len(values["IDs"])): - id = values["IDs"][i] - self.indexed_data[id] = values["Data"][i] - - def get(self, id): - if id not in self.indexed_data: - return None - return self.indexed_data[id] \ No newline at end of file diff --git a/modules/Nexon/nexondb.py b/modules/Nexon/nexondb.py deleted file mode 100644 index afbfc66..0000000 --- a/modules/Nexon/nexondb.py +++ /dev/null @@ -1,7 +0,0 @@ -class KingdomDB: - - def __init__(self, app) -> None: - if "Nexon" in app.databases: - del app.databases["Nexon"] - - app.databases["Nexon"] = self \ No newline at end of file diff --git a/modules/Shared/convert.py b/modules/Shared/convert.py deleted file mode 100644 index 8a1de3f..0000000 --- a/modules/Shared/convert.py +++ /dev/null @@ -1,45 +0,0 @@ -import io -import os -import UnityPy -from UnityPy import enums -from UnityPy.files import ObjectReader -from PIL import Image - -def convert(input_path : str, output_path : str, platform = enums.BuildTarget.WebGL, enable_read = False): - env = UnityPy.load(input_path) - - for item in env.assets: - item._m_target_platform = int(platform) - - for obj in env.objects: - if obj.type == enums.ClassIDType.Texture2D: - convert_texture(obj, platform) - if enable_read and obj.type == enums.ClassIDType.Mesh: - data = obj.read() - data.m_IsReadable = True - data.save() - - os.makedirs(os.path.dirname(output_path), exist_ok=True) - with open(output_path, "wb") as f: - f.write(env.file.save(packer="lz4")) - -def extract(input_path : str, output_path : str): - env = UnityPy.load(input_path) - - for obj in env.objects: - if obj.type == enums.ClassIDType.Texture2D: - os.makedirs(output_path, exist_ok=True) - data = obj.read() - data.image.save(output_path + data.name + ".png", format="PNG") - -def convert_texture(obj : ObjectReader, target_platform : enums.BuildTarget): - if target_platform == enums.BuildTarget.WebGL: - data = obj.read() - with io.BytesIO() as output: - data.image.save(output, format="PNG") - if data.image.width % 4 == 0 and data.image.height % 4 == 0: - data.m_TextureFormat = enums.TextureFormat.DXT5 - else: - data.m_TextureFormat = enums.TextureFormat.ARGB32 - data.image = Image.open(output) - data.save() \ No newline at end of file diff --git a/modules/Shared/downloading.py b/modules/Shared/downloading.py deleted file mode 100644 index ed856b3..0000000 --- a/modules/Shared/downloading.py +++ /dev/null @@ -1,9 +0,0 @@ -import aiohttp - -async def download_bytes(url, session : aiohttp.ClientSession): - async with session.get(url) as resp: - return await resp.read() - -async def download_text(url, session : aiohttp.ClientSession): - async with session.get(url) as resp: - return await resp.text() \ No newline at end of file diff --git a/modules/Shared/utility.py b/modules/Shared/utility.py deleted file mode 100644 index 6f88f50..0000000 --- a/modules/Shared/utility.py +++ /dev/null @@ -1,3 +0,0 @@ -def divide_chunks(l, n): - for i in range(0, len(l), n): - yield l[i:i + n] \ No newline at end of file diff --git a/modules/proxy.py b/modules/proxy.py deleted file mode 100644 index 1a90a06..0000000 --- a/modules/proxy.py +++ /dev/null @@ -1,48 +0,0 @@ -from flask_restful import Resource -from flask import current_app as app, request, Request - -import requests - -def relayRequest(user_request : Request, url, headers): - with requests.Session() as session: - requests.utils.add_dict_to_cookiejar(session.cookies, user_request.cookies) - response = session.post(url, headers=headers, data=request.data) - - result = app.response_class( - response=response.text, - status=200, - mimetype='application/json' - ) - result.headers.add("Access-Control-Allow-Origin", "*") - - return result - -class LaunchProxy(Resource): - def post(self): - url = "https://apidgp-gameplayer.games.dmm.com/v5/launch/cl" - headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", - "Client-App": "DMMGamePlayer5", - "Client-version": "5.3.12", - "Content-Type": "application/json"} - - return relayRequest(request, url, headers) - -class UpdateProxy(Resource): - def post(self): - url = "https://apidgp-gameplayer.games.dmm.com/v5/r2/launch/cl" - headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", - "Client-App": "DMMGamePlayer5", - "Client-version": "5.3.12", - "Content-Type": "application/json"} - - return relayRequest(request, url, headers) - -class AgreementProxy(Resource): - def post(self): - url = "https://apidgp-gameplayer.games.dmm.com/v5/agreement/confirm/client" - headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", - "Client-App": "DMMGamePlayer5", - "Client-version": "5.3.12", - "Content-Type": "application/json"} - - return relayRequest(request, url, headers) \ No newline at end of file