import datetime import functools import json import math import os import random import string import time import warnings import constants ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) def svdata(obj: object, code: int = 1, message: str = "成功", errormsg: str = "Invalid API request.", errorcode: int=200) -> tuple: """ Converts a json-serializable object into the KanColle APIv1 format response. :param obj: The object to convert. If this is None, an error will be produced instead. :param code: The 'status code'. Values used by KanColle are 1 for success, 200/201 for error. :param message: The message to respond with. Typically 成功 which means success. :param errormsg: Optional, this the error that should be provided. :return: svdata= + the JSON data required. """ if obj is None: res = { "api_result": 201, "api_result_msg": errormsg, "api_data": obj } else: res = { "api_result": code, "api_result_msg": message, "api_data": obj } # Yay arbitary formats. return "svdata=" + json.dumps(res, separators=(',', ':')), errorcode, {"Content-Type": "application/json"} def load_datadump(filename: str) -> dict: """ Loads a datadump from the data/ directory. Used primarily for api_start2, but can be used for other purposes. :param filename: The dump to load. :return: A dict containing the values loaded from either 'api_data' or the normal JSON data. """ if not os.path.exists("data/" + filename): return {} with open(os.path.join(ROOT_DIR, 'data', filename), encoding="UTF-8") as f: o = json.load(f) return o if 'api_data' not in o else o['api_data'] def prepare_api_blueprint(bp): @bp.errorhandler(403) def api_403(e): return svdata(None, 100, errormsg="Not authorized", errorcode=403) @bp.errorhandler(400) def api_400(e): return svdata({"en_api_error": "Invalid data recieved."}, 201, errormsg="申し訳ありませんがブラウザを再起動し再ログインしてください。", errorcode=400) @bp.errorhandler(404) def api_404(e): return svdata(None, 100, errormsg="Could not find the appropriate data for the request", errorcode=404) def pack_resources(r: list) -> str: return ",".join([str(x) for x in r]) def extract_resources(r: str) -> list: return list(map(int, (x for x in r.split(',')))) def millisecond_timestamp(ts: datetime.datetime = datetime.datetime.now()) -> int: """ Converts a timestamp into a millisecond timestamp. :param ts: Optional: The timestamp to use. Defaults to the current timestamp. :return: The unix timestamp in UTC for the specified timestamp. """ if isinstance(ts, int): ts = datetime.datetime.fromtimestamp(ts) # http://stackoverflow.com/a/8160307 return math.floor(time.mktime(ts.timetuple()) * 1e3 + ts.microsecond / 1e3) def take_items(l, indices): """ Gets all items specified by the indices. >>> getlist = [1, 5, 7] >>> toget = [0, 2] >>> take_items(getlist, toget) # Gets items at index 0 and index 2 from the list. [1, 7] :param l: The list to search from. :param indices: The indices to get from the list. :return: A new list with the specified items. """ return (l[idx] for idx in indices) # http://stackoverflow.com/questions/3438756/some-built-in-to-pad-a-list-in-python def pad(iterable, padding='.', length=7): """ >>> iterable = [1,2,3] >>> list(pad(iterable)) [1, 2, 3, '.', '.', '.', '.'] """ for count, i in enumerate(iterable): yield i while count < length - 1: count += 1 yield padding def get_exp_required(level, current_exp): """ Gets the exp required for the next level. :param level: The level to attain. :param current_exp: Your current exp. """ total = sum(constants.EXP_LEVEL[:level + 1]) return total - current_exp # http://stackoverflow.com/questions/38987/how-can-i-merge-two-python-dictionaries-in-a-single-expression def merge_two_dicts(x, y): """Given two dicts, merge them into a new dict as a shallow copy.""" z = x.copy() z.update(y) return z def generate_api_token(): """ Generate a random API token. :return: A 40 character hexadecimal string. """ return ''.join(random.choice(string.hexdigits) for _ in range(40)).lower() def deprecated(func): '''This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.''' @functools.wraps(func) def new_func(*args, **kwargs): warnings.warn_explicit( "Call to deprecated function {}.".format(func.__name__), category=DeprecationWarning, filename=func.func_code.co_filename, lineno=func.func_code.co_firstlineno + 1 ) return func(*args, **kwargs) return new_func