diff --git a/erepublik/citizen.py b/erepublik/citizen.py index 77f53fb..777ed18 100644 --- a/erepublik/citizen.py +++ b/erepublik/citizen.py @@ -468,7 +468,7 @@ class BaseCitizen(access_points.CitizenAPI): self._req.debug = bool(debug) def to_json(self, indent: bool = False) -> str: - return utils.json.dumps(self, cls=classes.MyJSONEncoder, indent=4 if indent else None, sort_keys=True) + return utils.json.dumps(self, cls=classes.ErepublikJSONEncoder, indent=4 if indent else None, sort_keys=True) def get_countries_with_regions(self) -> Set[constants.Country]: r_json = self._post_main_travel_data().json() @@ -482,13 +482,13 @@ class BaseCitizen(access_points.CitizenAPI): filename = f"{self.__class__.__name__}__dump.json" with open(filename, 'w') as f: utils.json.dump(dict(config=self.config, cookies=self._req.cookies.get_dict(), - user_agent=self._req.headers.get("User-Agent")), f, cls=classes.MyJSONEncoder) + user_agent=self._req.headers.get("User-Agent")), f, cls=classes.ErepublikJSONEncoder) self.write_log(f"Session saved to: '{filename}'") @classmethod def load_from_dump(cls, dump_name: str): with open(dump_name) as f: - data = utils.json.load(f) + data = utils.json.load(f, object_hook=utils.json_decode_object_hook) player = cls(data['config']['email'], "") player._req.cookies.update(data['cookies']) player._req.headers.update({"User-Agent": data['user_agent']}) @@ -778,7 +778,7 @@ class BaseCitizen(access_points.CitizenAPI): :param msg: Message about the action :param kwargs: Extra information regarding action """ - kwargs = utils.json.loads(utils.json.dumps(kwargs or {}, cls=classes.MyJSONEncoder)) + kwargs = utils.json.loads(utils.json.dumps(kwargs or {}, cls=classes.ErepublikJSONEncoder)) action = action[:32] self.write_log(msg) if self.reporter.allowed: diff --git a/erepublik/classes.py b/erepublik/classes.py index 90f5a6a..a772d9f 100644 --- a/erepublik/classes.py +++ b/erepublik/classes.py @@ -10,9 +10,8 @@ from requests import Response, Session, post from . import constants, types, utils __all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException', - 'ErepublikNetworkException', 'EnergyToFight', - 'Holding', 'MyCompanies', 'MyJSONEncoder', 'OfferItem', 'Politics', 'Reporter', 'TelegramReporter', - 'Inventory'] + 'ErepublikJSONEncoder', 'ErepublikNetworkException', 'EnergyToFight', 'Holding', 'Inventory', 'MyCompanies', + 'OfferItem', 'Politics', 'Reporter', 'TelegramReporter', ] class ErepublikException(Exception): @@ -599,10 +598,10 @@ class Reporter: if self.__to_update: for unreported_data in self.__to_update: unreported_data.update(player_id=self.citizen_id, key=self.key) - unreported_data = utils.json.loads(utils.json.dumps(unreported_data, cls=MyJSONEncoder)) + unreported_data = utils.json.loads(utils.json.dumps(unreported_data, cls=ErepublikJSONEncoder)) self._req.post(f"{self.url}/bot/update", json=unreported_data) self.__to_update.clear() - data = utils.json.loads(utils.json.dumps(data, cls=MyJSONEncoder)) + data = utils.json.loads(utils.json.dumps(data, cls=ErepublikJSONEncoder)) r = self._req.post(f"{self.url}/bot/update", json=data) return r @@ -680,7 +679,7 @@ class Reporter: return [] -class MyJSONEncoder(utils.json.JSONEncoder): +class ErepublikJSONEncoder(utils.json.JSONEncoder): def default(self, o): from erepublik.citizen import Citizen if isinstance(o, Decimal): @@ -694,7 +693,7 @@ class MyJSONEncoder(utils.json.JSONEncoder): return dict(__type__='timedelta', days=o.days, seconds=o.seconds, microseconds=o.microseconds, total_seconds=o.total_seconds()) elif isinstance(o, Response): - return dict(headers=o.headers.__dict__, url=o.url, text=o.text) + return dict(headers=dict(o.__dict__['headers']), url=o.url, text=o.text, status_code=o.status_code) elif hasattr(o, 'as_dict'): return o.as_dict elif isinstance(o, set): diff --git a/erepublik/utils.py b/erepublik/utils.py index 8ac639a..ddd24c9 100644 --- a/erepublik/utils.py +++ b/erepublik/utils.py @@ -12,6 +12,7 @@ from decimal import Decimal from pathlib import Path from typing import Any, Dict, List, Union +import pytz import requests from . import __version__, constants @@ -25,11 +26,8 @@ __all__ = ['VERSION', 'calculate_hit', 'caught_error', 'date_from_eday', 'eday_f 'get_air_hit_dmg_value', 'get_file', 'get_ground_hit_dmg_value', 'get_sleep_seconds', 'good_timedelta', 'interactive_sleep', 'json', 'localize_dt', 'localize_timestamp', 'normalize_html_json', 'now', 'process_error', 'process_warning', 'send_email', 'silent_sleep', 'slugify', 'write_file', 'write_request', - 'write_interactive_log', 'write_silent_log', 'get_final_hit_dmg', 'wait_for_lock'] - -if not sys.version_info >= (3, 6): - raise AssertionError('This script requires Python version 3.6 and higher\n' - 'But Your version is v{}.{}.{}'.format(*sys.version_info)) + 'write_interactive_log', 'write_silent_log', 'get_final_hit_dmg', 'wait_for_lock', + 'json_decode_object_hook', 'json_load', 'json_loads'] VERSION: str = __version__ @@ -234,8 +232,8 @@ def send_email(name: str, content: List[Any], player=None, local_vars: Dict[str, if isinstance(local_vars.get('citizen'), Citizen): local_vars['citizen'] = repr(local_vars['citizen']) - from erepublik.classes import MyJSONEncoder - files.append(('file', ("local_vars.json", json.dumps(local_vars, cls=MyJSONEncoder), + from erepublik.classes import ErepublikJSONEncoder + files.append(('file', ("local_vars.json", json.dumps(local_vars, cls=ErepublikJSONEncoder), "application/json"))) if isinstance(player, Citizen): files.append(('file', ("instance.json", player.to_json(indent=True), "application/json"))) @@ -380,12 +378,6 @@ def get_final_hit_dmg(base_dmg: Union[Decimal, float, str], rang: int, return Decimal(dmg) -# def _clear_up_battle_memory(battle): -# del battle.invader._battle, battle.defender._battle -# for div_id, division in battle.div.items(): -# del division._battle - - def deprecation(message): warnings.warn(message, DeprecationWarning, stacklevel=2) @@ -409,3 +401,36 @@ def wait_for_lock(function): return ret return wrapper + + +def json_decode_object_hook( + o: Union[Dict[str, Any], List[Any], int, float, str] +) -> Union[Dict[str, Any], List[Any], int, float, str, datetime.date, datetime.datetime, datetime.timedelta]: + """ Convert classes.ErepublikJSONEncoder datetime, date and timedelta to their python objects + + :param o: + :return: Union[Dict[str, Any], List[Any], int, float, str, datetime.date, datetime.datetime, datetime.timedelta] + """ + if o.get('__type__'): + _type = o.get('__type__') + if _type == 'datetime': + dt = datetime.datetime.strptime(f"{o['date']} {o['time']}", "%Y-%m-%d %H:%M:%S") + if o.get('tzinfo'): + dt = pytz.timezone(o['tzinfo']).localize(dt) + return dt + elif _type == 'date': + dt = datetime.datetime.strptime(f"{o['date']}", "%Y-%m-%d") + return dt.date() + elif _type == 'timedelta': + return datetime.timedelta(seconds=o['total_seconds']) + return o + + +def json_load(f, **kwargs): + kwargs.update(object_hook=json_decode_object_hook) + return json.load(f, **kwargs) + + +def json_loads(s: str, **kwargs): + kwargs.update(object_hook=json_decode_object_hook) + return json.loads(s, **kwargs)