You've already forked KemoFureApi
							
							Compare commits
	
		
			22 Commits
		
	
	
		
			0ce68c606f
			...
			archive
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5c8283ab5b | |||
| f2909d2992 | |||
| 11edbf3abf | |||
| 3f8f9f708f | |||
| 18aa1843cd | |||
| 8733294f13 | |||
| fee0ecfe75 | |||
| fb4ad30d03 | |||
| 2623106212 | |||
| 16c7792b7f | |||
| 2a852388bf | |||
| 50559223ed | |||
| 6b0de05a80 | |||
| 155394ab75 | |||
| 794bf9d30b | |||
| 6157eb1e40 | |||
| db728f21ba | |||
| cbe0074c70 | |||
| 3a247d6ce1 | |||
| e7d21a90bb | |||
| 8b12ca9dae | |||
| 76b6c6cb79 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,4 @@ | |||||||
| __pycache__/ | __pycache__/ | ||||||
| data/ | data/ | ||||||
| test.py | test.py | ||||||
|  | database_config.txt | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								app.py
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								app.py
									
									
									
									
									
								
							| @@ -1,9 +1,9 @@ | |||||||
| from flask import Flask | from flask import Flask | ||||||
|  | from waitress import serve | ||||||
| from flask_restful import Api | 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.Archive.database import Database as KFADB | ||||||
|  | import logging | ||||||
|  |  | ||||||
| app = Flask(__name__) | app = Flask(__name__) | ||||||
| app.config['JSON_AS_ASCII'] = False | app.config['JSON_AS_ASCII'] = False | ||||||
| @@ -11,9 +11,9 @@ app.databases = {} | |||||||
|  |  | ||||||
| api = Api(app) | api = Api(app) | ||||||
|  |  | ||||||
| KF3DB(api) |  | ||||||
| KFKDB(api) |  | ||||||
| KFADB(api) | KFADB(api) | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     app.run(host='127.0.0.1', port=8080, debug=True) |     #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) | ||||||
| @@ -1,22 +1,128 @@ | |||||||
| import json | import json | ||||||
|  | from flask import Flask | ||||||
|  |  | ||||||
|  | from modules.Archive.endpoints.tag_stats import TagStats | ||||||
|  |  | ||||||
| from .databaseController import DatabaseController | from .databaseController import DatabaseController | ||||||
| from .endpoints.query import Archive_Query | from .endpoints.query import Query | ||||||
|  | from .endpoints.command import Command | ||||||
|  | from .endpoints.commands import Commands | ||||||
|  | from .endpoints.post import GetPost | ||||||
|  | from .endpoints.posts import GetPosts | ||||||
|  | from .endpoints.new_query import NewQuery | ||||||
|  | from .endpoints.set_action import SetAction | ||||||
|  | from .endpoints.posts_count import GetPostsCount | ||||||
|  | from .endpoints.account_stats import AccountStats | ||||||
|  |  | ||||||
| class Database: | class Database: | ||||||
|     db : DatabaseController = None |     db : DatabaseController = None | ||||||
|     processed_friends = {} |     app : Flask = None | ||||||
|     item_stages = {} |  | ||||||
|  |  | ||||||
|     def __init__(self, api) -> None: |     def __init__(self, api) -> None: | ||||||
|         app = api.app |         self.app = api.app | ||||||
|         if "Archive" in app.databases: |  | ||||||
|             del app.databases["Archive"] |         if "Archive" in self.app.databases: | ||||||
|  |             del self.app.databases["Archive"] | ||||||
|  |  | ||||||
|         self.reload_data() |         self.reload_data() | ||||||
|  |  | ||||||
|         app.databases["Archive"] = self |         self.app.databases["Archive"] = self | ||||||
|  |  | ||||||
|         api.add_resource(Archive_Query, "/Archive/Query") |         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/<id>") | ||||||
|  |         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 x_accounts  | ||||||
|  |         ORDER BY x_handle ASC''' | ||||||
|  |         return self.db.run_query(query) | ||||||
|  |  | ||||||
|  |     def get_account_stats(self): | ||||||
|  |         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 cast(x_posts.id as TEXT), x_posts.*, x_accounts.x_handle, cast(account_id as TEXT) as account_id, x_accounts.rating, saved_files from x_posts | ||||||
|  |         LEFT JOIN x_accounts | ||||||
|  |             ON x_posts.account_id = x_accounts.id WHERE x_posts.id = {id} | ||||||
|  |         LIMIT 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"], 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'x_accounts.rating = \'{rating}\'' for rating in include_ratings]) + ")" | ||||||
|  |         if artist is not None and 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"], 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_accounts | ||||||
|  |             ON x_posts.account_id = x_accounts.id | ||||||
|  |         {where_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"], 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, tags) | ||||||
|  |  | ||||||
|  |         query = f''' | ||||||
|  |         SELECT x_posts.id, cast(x_posts.id as TEXT) as id_str, action_taken, saved_files, text, files, date, x_handle, cast(account_id as TEXT) as account_id, 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", error : str = ""): | ||||||
|  |         if result is None: | ||||||
|  |             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, default=str), | ||||||
|  |                     status=200, | ||||||
|  |                     mimetype='application/json' | ||||||
|  |             ) | ||||||
|  |             elif mode == "text": | ||||||
|  |                 response = self.app.response_class( | ||||||
|  |                     response=str(result), | ||||||
|  |                     status=200, | ||||||
|  |                     mimetype='text/plain' | ||||||
|  |             ) | ||||||
|  |                  | ||||||
|  |         response.headers.add("Access-Control-Allow-Origin", "*") | ||||||
|  |         return response | ||||||
|  |  | ||||||
|     def reload_data(self): |     def reload_data(self): | ||||||
|         self.db = DatabaseController("/home/pi/python/Katbots/JapariArchive/database.db") |         self.db = DatabaseController() | ||||||
| @@ -1,21 +1,54 @@ | |||||||
| import os | import os | ||||||
| import sqlite3 | import psycopg | ||||||
|  | from psycopg.rows import dict_row | ||||||
|  |  | ||||||
| TABLE_ACCOUNTS  = "accounts" | TABLE_ACCOUNTS  = "x_accounts" | ||||||
| TABLE_X         = "x_posts" | TABLE_X         = "x_posts" | ||||||
|  |  | ||||||
| class DatabaseController: | class DatabaseController: | ||||||
|     def __init__(self, db_name): |     def __init__(self): | ||||||
|         self.conn = sqlite3.connect(db_name, isolation_level="DEFERRED") |         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() |         self.cursor = self.conn.cursor() | ||||||
|  |  | ||||||
|     def run_query(self, query): |     def run_query(self, query): | ||||||
|         try: |         try: | ||||||
|             self.cursor.execute(query) |             self.cursor.execute(query) | ||||||
|             results = self.cursor.fetchall() |             results = self.cursor.fetchall() | ||||||
|             return results |             self.conn.rollback() | ||||||
|         except: |             return True, results | ||||||
|             return None |         except Exception as e: | ||||||
|  |             print(query, e) | ||||||
|  |             self.conn.rollback() | ||||||
|  |             return False, str(e) | ||||||
|  |          | ||||||
|  |     def run_command(self, command): | ||||||
|  |         try: | ||||||
|  |             self.cursor.execute(command) | ||||||
|  |             result = self.cursor.rowcount | ||||||
|  |             self.conn.commit() | ||||||
|  |             return result > 0 | ||||||
|  |         except Exception as e: | ||||||
|  |             self.conn.rollback() | ||||||
|  |             print(command, e) | ||||||
|  |             return False | ||||||
|  |  | ||||||
|  |     def run_commands(self, commands): | ||||||
|  |         last_command = "" | ||||||
|  |         try: | ||||||
|  |             for command in commands: | ||||||
|  |                 last_command = command | ||||||
|  |                 self.cursor.execute(command) | ||||||
|  |             result = self.cursor.rowcount | ||||||
|  |             self.conn.commit() | ||||||
|  |             return result > 0 | ||||||
|  |         except Exception as e: | ||||||
|  |             self.conn.rollback() | ||||||
|  |             print(last_command, e) | ||||||
|  |             return False | ||||||
|          |          | ||||||
|     def close(self): |     def close(self): | ||||||
|         self.conn.close() |         self.conn.close() | ||||||
							
								
								
									
										13
									
								
								modules/Archive/endpoints/account_stats.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								modules/Archive/endpoints/account_stats.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 AccountStats(Resource): | ||||||
|  |     def get(self): | ||||||
|  |         db : Database = app.databases["Archive"] | ||||||
|  |         _, result = db.get_account_stats() | ||||||
|  |         return db.wrap_query_response(result) | ||||||
							
								
								
									
										23
									
								
								modules/Archive/endpoints/command.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								modules/Archive/endpoints/command.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | from __future__ import annotations | ||||||
|  | from flask_restful import Resource | ||||||
|  | from flask import current_app as app, request | ||||||
|  | from typing import TYPE_CHECKING | ||||||
|  | import hashlib | ||||||
|  |  | ||||||
|  | if TYPE_CHECKING: | ||||||
|  |     from modules.Archive.database import Database | ||||||
|  |  | ||||||
|  | class Command(Resource): | ||||||
|  |     def post(self): | ||||||
|  |         db : Database = app.databases["Archive"] | ||||||
|  |         auth = request.headers.get('auth') | ||||||
|  |         if auth is not None: | ||||||
|  |             hash_obj = hashlib.sha256(auth.encode('utf-8')) | ||||||
|  |             if hash_obj.hexdigest() == "63a3b0dba950e1015a110486518e5ceff8cff415041aba3dedb8dc5aa3b3dd16": | ||||||
|  |                 query = request.data.decode("utf-8") | ||||||
|  |                 result = db.db.run_command(query) | ||||||
|  |             else:  | ||||||
|  |                 result = None | ||||||
|  |         else: | ||||||
|  |             result = None | ||||||
|  |         return db.wrap_query_response(result, mode="text") | ||||||
							
								
								
									
										24
									
								
								modules/Archive/endpoints/commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								modules/Archive/endpoints/commands.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | from __future__ import annotations | ||||||
|  | import json | ||||||
|  | from flask_restful import Resource | ||||||
|  | from flask import current_app as app, request | ||||||
|  | from typing import TYPE_CHECKING | ||||||
|  | import hashlib | ||||||
|  |  | ||||||
|  | if TYPE_CHECKING: | ||||||
|  |     from modules.Archive.database import Database | ||||||
|  |  | ||||||
|  | class Commands(Resource): | ||||||
|  |     def post(self): | ||||||
|  |         db : Database = app.databases["Archive"] | ||||||
|  |         auth = request.headers.get('auth') | ||||||
|  |         if auth is not None: | ||||||
|  |             hash_obj = hashlib.sha256(auth.encode('utf-8')) | ||||||
|  |             if hash_obj.hexdigest() == "63a3b0dba950e1015a110486518e5ceff8cff415041aba3dedb8dc5aa3b3dd16": | ||||||
|  |                 data = json.loads(request.data) | ||||||
|  |                 result = db.db.run_commands(data) | ||||||
|  |             else:  | ||||||
|  |                 result = None | ||||||
|  |         else: | ||||||
|  |             result = None | ||||||
|  |         return db.wrap_query_response(result, mode="text") | ||||||
							
								
								
									
										19
									
								
								modules/Archive/endpoints/new_query.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								modules/Archive/endpoints/new_query.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | from __future__ import annotations | ||||||
|  | from flask_restful import Resource | ||||||
|  | from flask import current_app as app, request | ||||||
|  | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
|  | if TYPE_CHECKING: | ||||||
|  |     from modules.Archive.database import Database | ||||||
|  |  | ||||||
|  | class NewQuery(Resource): | ||||||
|  |     def post(self): | ||||||
|  |         query = request.data.decode("utf-8") | ||||||
|  |          | ||||||
|  |         db : Database = app.databases["Archive"] | ||||||
|  |  | ||||||
|  |         status, result = db.db.run_query(query) | ||||||
|  |         if status: | ||||||
|  |             return db.wrap_query_response(result) | ||||||
|  |         else: | ||||||
|  |             return db.wrap_query_response(None, error = result) | ||||||
							
								
								
									
										15
									
								
								modules/Archive/endpoints/post.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								modules/Archive/endpoints/post.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | 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 GetPost(Resource): | ||||||
|  |     def get(self, id): | ||||||
|  |         id = int(id) | ||||||
|  |         db : Database = app.databases["Archive"] | ||||||
|  |  | ||||||
|  |         _, result = db.get_post(id) | ||||||
|  |         return db.wrap_query_response(result)  | ||||||
							
								
								
									
										59
									
								
								modules/Archive/endpoints/posts.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								modules/Archive/endpoints/posts.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | from __future__ import annotations | ||||||
|  | from flask_restful import Resource | ||||||
|  | from flask import current_app as app, request | ||||||
|  | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
|  | if TYPE_CHECKING: | ||||||
|  |     from modules.Archive.database import Database | ||||||
|  |  | ||||||
|  | class GetPosts(Resource): | ||||||
|  |     def get(self): | ||||||
|  |         db : Database = app.databases["Archive"] | ||||||
|  |         try: | ||||||
|  |             count = int(request.args["count"]) if "count" in request.args else 20 | ||||||
|  |             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", "*") | ||||||
|  |             return response    | ||||||
|  |  | ||||||
|  |         actions = [] | ||||||
|  |         if "undecided" in request.args: | ||||||
|  |             actions.append(0) | ||||||
|  |         if "approved" in request.args: | ||||||
|  |             actions.append(1) | ||||||
|  |         if "denied" in request.args: | ||||||
|  |             actions.append(2) | ||||||
|  |         if "hidden" in request.args: | ||||||
|  |             actions.append(3) | ||||||
|  |         if actions == []: | ||||||
|  |             actions = [0,1,2,3] | ||||||
|  |  | ||||||
|  |         ratings = [] | ||||||
|  |         if "SFW" in request.args: | ||||||
|  |             ratings.append("SFW") | ||||||
|  |         if "NSFW" in request.args: | ||||||
|  |             ratings.append("NSFW") | ||||||
|  |         if "NSFL" in request.args: | ||||||
|  |             ratings.append("NSFL") | ||||||
|  |         if ratings == []: | ||||||
|  |             ratings = ["SFW", "NSFW"] | ||||||
|  |  | ||||||
|  |         if "random" in request.args: | ||||||
|  |             order = "RAND" | ||||||
|  |         elif "ascending" in request.args: | ||||||
|  |             order = "ASC" | ||||||
|  |         else: | ||||||
|  |             order = "DESC" | ||||||
|  |  | ||||||
|  |         real_page = page-1 | ||||||
|  |         offset = real_page * count | ||||||
|  |  | ||||||
|  |         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) | ||||||
							
								
								
									
										43
									
								
								modules/Archive/endpoints/posts_count.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								modules/Archive/endpoints/posts_count.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | from __future__ import annotations | ||||||
|  | from flask_restful import Resource | ||||||
|  | from flask import current_app as app, request | ||||||
|  | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
|  | if TYPE_CHECKING: | ||||||
|  |     from modules.Archive.database import Database | ||||||
|  |  | ||||||
|  | class GetPostsCount(Resource): | ||||||
|  |     def get(self): | ||||||
|  |         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", "*") | ||||||
|  |             return response    | ||||||
|  |  | ||||||
|  |         actions = [] | ||||||
|  |         if "undecided" in request.args: | ||||||
|  |             actions.append(0) | ||||||
|  |         if "approved" in request.args: | ||||||
|  |             actions.append(1) | ||||||
|  |         if "denied" in request.args: | ||||||
|  |             actions.append(2) | ||||||
|  |         if "hidden" in request.args: | ||||||
|  |             actions.append(3) | ||||||
|  |         if actions == []: | ||||||
|  |             actions = [0,1,2,3] | ||||||
|  |  | ||||||
|  |         ratings = [] | ||||||
|  |         if "SFW" in request.args: | ||||||
|  |             ratings.append("SFW") | ||||||
|  |         if "NSFW" in request.args: | ||||||
|  |             ratings.append("NSFW") | ||||||
|  |         if "NSFL" in request.args: | ||||||
|  |             ratings.append("NSFL") | ||||||
|  |         if ratings == []: | ||||||
|  |             ratings = ["SFW", "NSFW"] | ||||||
|  |  | ||||||
|  |         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")  | ||||||
| @@ -1,21 +1,20 @@ | |||||||
| import json | from __future__ import annotations | ||||||
| from flask_restful import Resource | from flask_restful import Resource | ||||||
| from flask import current_app as app, jsonify | from flask import current_app as app, request | ||||||
|  | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
| class Archive_Query(Resource): | if TYPE_CHECKING: | ||||||
|     def post(self, query:str): |     from modules.Archive.database import Database | ||||||
|         db = app.databases["Archive"] |  | ||||||
|  |  | ||||||
|         result = db.run_query(query) | class Query(Resource): | ||||||
|  |     def post(self): | ||||||
|  |         query = request.data.decode("utf-8") | ||||||
|          |          | ||||||
|         if result is None: |         db : Database = app.databases["Archive"] | ||||||
|             response = app.response_class(status=400) |  | ||||||
|  |         status, result = db.db.run_query(query) | ||||||
|  |         if status: | ||||||
|  |             result = [list(d.values()) for d in result] | ||||||
|  |             return db.wrap_query_response(result) | ||||||
|         else: |         else: | ||||||
|             response = app.response_class( |             return db.wrap_query_response(None, error = result) | ||||||
|                 response=json.dumps(result, ensure_ascii=False, indent=1), |  | ||||||
|                 status=200, |  | ||||||
|                 mimetype='application/json' |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         response.headers.add("Access-Control-Allow-Origin", "*") |  | ||||||
|         return response    |  | ||||||
							
								
								
									
										35
									
								
								modules/Archive/endpoints/set_action.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								modules/Archive/endpoints/set_action.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | from __future__ import annotations | ||||||
|  | import json | ||||||
|  | from flask_restful import Resource | ||||||
|  | from flask import current_app as app, request | ||||||
|  | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
|  | if TYPE_CHECKING: | ||||||
|  |     from modules.Archive.database import Database | ||||||
|  |  | ||||||
|  | class SetAction(Resource): | ||||||
|  |     def post(self): | ||||||
|  |         data = json.loads(request.data) | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             bypass = ("allow_override" in data) and data["allow_override"] | ||||||
|  |             action = data["action_taken"] | ||||||
|  |             if "id_str" in data: | ||||||
|  |                 id = int(data["id_str"]) | ||||||
|  |             elif "id" in data: | ||||||
|  |                 id = data["id"] | ||||||
|  |             else: | ||||||
|  |                 raise Exception("no id (int) or id_str (str) specified") | ||||||
|  |         except Exception as e: | ||||||
|  |             print(e) | ||||||
|  |             response = app.response_class(response=e, status=400) | ||||||
|  |             response.headers.add("Access-Control-Allow-Origin", "*") | ||||||
|  |             return response | ||||||
|  |  | ||||||
|  |         db : Database = app.databases["Archive"] | ||||||
|  |         query = f"UPDATE x_posts SET action_taken = {action} WHERE id = {id}" | ||||||
|  |         if not bypass: | ||||||
|  |             query += " AND action_taken = 0" | ||||||
|  |  | ||||||
|  |         result = db.db.run_command(query) | ||||||
|  |         return db.wrap_query_response(result, mode="text") | ||||||
							
								
								
									
										13
									
								
								modules/Archive/endpoints/tag_stats.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								modules/Archive/endpoints/tag_stats.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||||
							
								
								
									
										8
									
								
								modules/Archive/load_from_file.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								modules/Archive/load_from_file.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | import sys | ||||||
|  | from databaseController import DatabaseController | ||||||
|  |  | ||||||
|  | db = DatabaseController("/home/pi/python/Katbots/JapariArchive/database.db") | ||||||
|  | with open(sys.argv[1], "rt") as file: | ||||||
|  |     lines = [line for line in file.readlines() if line.strip()] | ||||||
|  |  | ||||||
|  | print(db.run_commands(lines)) | ||||||
| @@ -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/<int:id>") |  | ||||||
|         api.add_resource(KF3_Friends, "/KF3/Friends") |  | ||||||
|         api.add_resource(KF3_Item, "/KF3/Item/<int:id>") |  | ||||||
|         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 |  | ||||||
|                  |  | ||||||
| @@ -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()) |  | ||||||
| @@ -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  |  | ||||||
| @@ -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    |  | ||||||
| @@ -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  |  | ||||||
| @@ -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    |  | ||||||
| @@ -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() |  | ||||||
| @@ -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 |  | ||||||
| @@ -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/<int:id>") |  | ||||||
|         api.add_resource(Kingdom_Friends, "/Kingdom/Friends") |  | ||||||
|         api.add_resource(Kingdom_Item, "/Kingdom/Item/<int:id>") |  | ||||||
|         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) |  | ||||||
| @@ -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   |  | ||||||
| @@ -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    |  | ||||||
| @@ -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   |  | ||||||
| @@ -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    |  | ||||||
| @@ -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() |  | ||||||
| @@ -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=#[A-Fa-f0-9]+>(.*?)<\/color>" |  | ||||||
|     output_string = re.sub(color_tags_pattern, r"\1", input_string) |  | ||||||
|  |  | ||||||
|     link_tags_pattern = r"<link=\".*?\">(.*?)<\/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) |  | ||||||
| @@ -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] |  | ||||||
| @@ -1,7 +0,0 @@ | |||||||
| class KingdomDB: |  | ||||||
|      |  | ||||||
|     def __init__(self, app) -> None: |  | ||||||
|         if "Nexon" in app.databases: |  | ||||||
|             del app.databases["Nexon"] |  | ||||||
|  |  | ||||||
|         app.databases["Nexon"] = self |  | ||||||
| @@ -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() |  | ||||||
| @@ -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() |  | ||||||
| @@ -1,3 +0,0 @@ | |||||||
| def divide_chunks(l, n):  |  | ||||||
|     for i in range(0, len(l), n):   |  | ||||||
|         yield l[i:i + n]  |  | ||||||
		Reference in New Issue
	
	Block a user