diff --git a/Makefile b/Makefile index 9ce0e5f..2e9d3ee 100644 --- a/Makefile +++ b/Makefile @@ -52,12 +52,13 @@ clean-test: ## remove test and coverage artifacts rm -fr .pytest_cache lint: ## check style with flake8 + black erepublik tests flake8 erepublik tests test: ## run tests quickly with the default Python - python setup.py test + python -m unittest -coverage: ## check code coverage quickly with the default Python +coverage: lint ## check code coverage quickly with the default Python coverage run --source erepublik setup.py test coverage report -m coverage html diff --git a/erepublik/__init__.py b/erepublik/__init__.py index 8e00222..1bdccfa 100644 --- a/erepublik/__init__.py +++ b/erepublik/__init__.py @@ -3,9 +3,9 @@ """Top-level package for eRepublik script.""" __author__ = """Eriks Karls""" -__email__ = 'eriks@72.lv' -__version__ = '0.25.1.2' +__email__ = "eriks@72.lv" +__version__ = "0.25.1.2" from erepublik.citizen import Citizen -__all__ = ['Citizen', '__version__'] +__all__ = ["Citizen", "__version__"] diff --git a/erepublik/_logging.py b/erepublik/_logging.py index a19408f..96ed6b1 100644 --- a/erepublik/_logging.py +++ b/erepublik/_logging.py @@ -19,13 +19,13 @@ from erepublik.utils import json, json_dumps, json_loads, slugify class ErepublikFileHandler(handlers.TimedRotatingFileHandler): _file_path: Path - def __init__(self, filename: str = 'log/erepublik.log', *args, **kwargs): + def __init__(self, filename: str = "log/erepublik.log", *args, **kwargs): log_path = Path(filename) self._file_path = log_path log_path.parent.mkdir(parents=True, exist_ok=True) at_time = erep_tz.localize(datetime.datetime.now()).replace(hour=0, minute=0, second=0, microsecond=0) kwargs.update(atTime=at_time) - super().__init__(filename, when='d', *args, **kwargs) + super().__init__(filename, when="d", *args, **kwargs) def doRollover(self) -> None: self._file_path.parent.mkdir(parents=True, exist_ok=True) @@ -88,7 +88,7 @@ class ErepublikFormatter(logging.Formatter): if datefmt: s = dt.strftime(datefmt) else: - s = dt.strftime('%Y-%m-%d %H:%M:%S') + s = dt.strftime("%Y-%m-%d %H:%M:%S") return s def usesTime(self): @@ -99,9 +99,9 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler): def __init__(self, reporter: Reporter): logging.Handler.__init__(self, level=logging.ERROR) self._reporter = weakref.ref(reporter) - self.host = 'erep.lv' - self.url = '/ebot/error/' - self.method = 'POST' + self.host = "erep.lv" + self.url = "/ebot/error/" + self.method = "POST" self.secure = True self.credentials = (str(reporter.citizen_id), reporter.key) self.context = None @@ -115,31 +115,39 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler): url = response.url last_index = url.index("?") if "?" in url else len(response.url) - name = slugify(response.url[len(self.reporter.citizen.url):last_index]) + name = slugify(response.url[len(self.reporter.citizen.url) : last_index]) html = response.text try: json_loads(html) - ext = 'json' + ext = "json" except json.decoder.JSONDecodeError: - ext = 'html' + ext = "html" try: - resp_time = datetime.datetime.strptime( - response.headers.get('date'), '%a, %d %b %Y %H:%M:%S %Z' - ).replace(tzinfo=datetime.timezone.utc).astimezone(erep_tz).strftime('%F_%H-%M-%S') - except: - resp_time = slugify(response.headers.get('date')) - return dict(name=f"{resp_time}_{name}.{ext}", content=html.encode('utf-8'), - mimetype="application/json" if ext == 'json' else "text/html") + resp_time = ( + datetime.datetime.strptime(response.headers.get("date"), "%a, %d %b %Y %H:%M:%S %Z") + .replace(tzinfo=datetime.timezone.utc) + .astimezone(erep_tz) + .strftime("%F_%H-%M-%S") + ) + except: # noqa + resp_time = slugify(response.headers.get("date")) + return dict( + name=f"{resp_time}_{name}.{ext}", content=html.encode("utf-8"), mimetype="application/json" if ext == "json" else "text/html" + ) def _get_local_vars(self) -> str: trace = inspect.trace() local_vars = {} if trace: local_vars = trace[-1][0].f_locals - if local_vars.get('__name__') == '__main__': - local_vars.update(commit_id=local_vars.get('COMMIT_ID'), interactive=local_vars.get('INTERACTIVE'), - version=local_vars.get('__version__'), config=local_vars.get('CONFIG')) + if local_vars.get("__name__") == "__main__": + local_vars.update( + commit_id=local_vars.get("COMMIT_ID"), + interactive=local_vars.get("INTERACTIVE"), + version=local_vars.get("__version__"), + config=local_vars.get("CONFIG"), + ) else: stack = inspect.stack() report_error_caller_found = False @@ -147,18 +155,19 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler): if report_error_caller_found: local_vars = frame.frame.f_locals break - if 'report_error' in str(frame.frame): + if "report_error" in str(frame.frame): report_error_caller_found = True - if 'state_thread' in local_vars: - local_vars.pop('state_thread', None) + if "state_thread" in local_vars: + local_vars.pop("state_thread", None) from erepublik import Citizen - if isinstance(local_vars.get('self'), Citizen): - local_vars['self'] = repr(local_vars['self']) - if isinstance(local_vars.get('player'), Citizen): - local_vars['player'] = repr(local_vars['player']) - if isinstance(local_vars.get('citizen'), Citizen): - local_vars['citizen'] = repr(local_vars['citizen']) + + if isinstance(local_vars.get("self"), Citizen): + local_vars["self"] = repr(local_vars["self"]) + if isinstance(local_vars.get("player"), Citizen): + local_vars["player"] = repr(local_vars["player"]) + if isinstance(local_vars.get("citizen"), Citizen): + local_vars["citizen"] = repr(local_vars["citizen"]) return json_dumps(local_vars) def _get_instance_json(self) -> str: @@ -171,15 +180,17 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler): # Log last response resp = self._get_last_response() - files = [('file', (resp.get('name'), resp.get('content'), resp.get('mimetype'))), ] + files = [ + ("file", (resp.get("name"), resp.get("content"), resp.get("mimetype"))), + ] - files += list(('file', (f, open(f'log/{f}', 'rb'))) for f in os.listdir('log') if f.endswith('.log')) + files += list(("file", (f, open(f"log/{f}", "rb"))) for f in os.listdir("log") if f.endswith(".log")) local_vars_json = self._get_local_vars() if local_vars_json: - files.append(('file', ('local_vars.json', local_vars_json, "application/json"))) + files.append(("file", ("local_vars.json", local_vars_json, "application/json"))) instance_json = self._get_instance_json() if instance_json: - files.append(('file', ('instance.json', instance_json, "application/json"))) + files.append(("file", ("instance.json", instance_json, "application/json"))) data.update(files=files) return data @@ -190,12 +201,12 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler): Send the record to the Web server as a percent-encoded dictionary """ try: - proto = 'https' if self.secure else 'http' + proto = "https" if self.secure else "http" u, p = self.credentials - s = 'Basic ' + base64.b64encode(f'{u}:{p}'.encode('utf-8')).strip().decode('ascii') - headers = {'Authorization': s} + s = "Basic " + base64.b64encode(f"{u}:{p}".encode("utf-8")).strip().decode("ascii") + headers = {"Authorization": s} data = self.mapLogRecord(record) - files = data.pop('files') if 'files' in data else None + files = data.pop("files") if "files" in data else None requests.post(f"{proto}://{self.host}{self.url}", headers=headers, data=data, files=files) except Exception: self.handleError(record) diff --git a/erepublik/access_points.py b/erepublik/access_points.py index 00d53f8..ec447c8 100644 --- a/erepublik/access_points.py +++ b/erepublik/access_points.py @@ -9,7 +9,7 @@ from requests_toolbelt.utils import dump from erepublik import constants, utils -__all__ = ['SlowRequests', 'CitizenAPI'] +__all__ = ["SlowRequests", "CitizenAPI"] class SlowRequests(Session): @@ -25,13 +25,20 @@ class SlowRequests(Session): user_agent = random.choice(self.get_random_user_agent()) self.request_log_name = utils.get_file(utils.now().strftime("debug/requests_%Y-%m-%d.log")) self.last_time = utils.now() - self.headers.update({'User-Agent': user_agent}) + self.headers.update({"User-Agent": user_agent}) self.hooks["response"] = [self._log_response] @property def as_dict(self): - return dict(last_time=self.last_time, timeout=self.timeout, cookies=self.cookies.get_dict(), debug=self.debug, - user_agent=self.headers['User-Agent'], request_log_name=self.request_log_name, proxies=self.proxies) + return dict( + last_time=self.last_time, + timeout=self.timeout, + cookies=self.cookies.get_dict(), + debug=self.debug, + user_agent=self.headers["User-Agent"], + request_log_name=self.request_log_name, + proxies=self.proxies, + ) def request(self, method, url, *args, **kwargs): self._slow_down_requests() @@ -50,70 +57,72 @@ class SlowRequests(Session): def _log_request(self, url, method, data=None, json=None, params=None, **kwargs): if self.debug: args = {} - kwargs.pop('allow_redirects', None) + kwargs.pop("allow_redirects", None) if kwargs: - args.update({'kwargs': kwargs}) + args.update({"kwargs": kwargs}) if data: - args.update({'data': data}) + args.update({"data": data}) if json: - args.update({'json': json}) + args.update({"json": json}) if params: - args.update({'params': params}) + args.update({"params": params}) body = f"[{utils.now().strftime('%F %T')}]\tURL: '{url}'\tMETHOD: {method}\tARGS: {args}\n" - with open(self.request_log_name, 'ab') as file: + with open(self.request_log_name, "ab") as file: file.write(body.encode("UTF-8")) def _log_response(self, response: Response, *args, **kwargs): - redirect = kwargs.get('redirect') + redirect = kwargs.get("redirect") url = response.request.url if self.debug: if response.history and not redirect: for hist_resp in response.history: - self._log_request(hist_resp.request.url, 'REDIRECT') + self._log_request(hist_resp.request.url, "REDIRECT") self._log_response(hist_resp, redirect=True) - fd_path = 'debug/requests' - fd_time = self.last_time.strftime('%Y/%m/%d/%H-%M-%S') - fd_name = utils.slugify(url[len(CitizenBaseAPI.url):]) - fd_extra = '_REDIRECT' if redirect else "" + fd_path = "debug/requests" + fd_time = self.last_time.strftime("%Y/%m/%d/%H-%M-%S") + fd_name = utils.slugify(url[len(CitizenBaseAPI.url) :]) + fd_extra = "_REDIRECT" if redirect else "" try: utils.json.loads(response.text) - fd_ext = 'json' + fd_ext = "json" except utils.json.JSONDecodeError: - fd_ext = 'html' + fd_ext = "html" - filename = f'{fd_path}/{fd_time}_{fd_name}{fd_extra}.{fd_ext}' + filename = f"{fd_path}/{fd_time}_{fd_name}{fd_extra}.{fd_ext}" utils.write_file(filename, response.text) if not redirect: data = dump.dump_all(response) - utils.write_file(f'debug/dumps/{fd_time}_{fd_name}{fd_extra}.{fd_ext}.dump', data.decode('utf8')) + utils.write_file(f"debug/dumps/{fd_time}_{fd_name}{fd_extra}.{fd_ext}.dump", data.decode("utf8")) @staticmethod def get_random_user_agent() -> str: - windows_x64 = 'Windows NT 10.0; Win64; x64' - linux_x64 = 'X11; Linux x86_64' - android_11 = 'Android 11; Mobile' - android_10 = 'Android 10; Mobile' - android_9 = 'Android 9; Mobile' + windows_x64 = "Windows NT 10.0; Win64; x64" + linux_x64 = "X11; Linux x86_64" + android_11 = "Android 11; Mobile" + android_10 = "Android 10; Mobile" + android_9 = "Android 9; Mobile" - firefox_tmplt = 'Mozilla/5.0 ({osystem}; rv:{version}.0) Gecko/20100101 Firefox/{version}.0' + firefox_tmplt = "Mozilla/5.0 ({osystem}; rv:{version}.0) Gecko/20100101 Firefox/{version}.0" ff_version = range(85, 92) - chrome_tmplt = 'Mozilla/5.0 ({osystem}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version} Safari/537.36' - chrome_version = ['85.0.4183.121', - '86.0.4240.183', - '87.0.4280.141', - '88.0.4324.182', - '89.0.4389.128', - '90.0.4430.18', - '91.0.4472.73', - '92.0.4515.14'] + chrome_tmplt = "Mozilla/5.0 ({osystem}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version} Safari/537.36" + chrome_version = [ + "85.0.4183.121", + "86.0.4240.183", + "87.0.4280.141", + "88.0.4324.182", + "89.0.4389.128", + "90.0.4430.18", + "91.0.4472.73", + "92.0.4515.14", + ] uas = [] for osystem in [windows_x64, linux_x64, android_9, android_10, android_11]: @@ -131,7 +140,7 @@ class CitizenBaseAPI: token: str def __init__(self): - """ Class for unifying eRepublik known endpoints and their required/optional parameters """ + """Class for unifying eRepublik known endpoints and their required/optional parameters""" self._req = SlowRequests() self.token = "" @@ -149,75 +158,90 @@ class CitizenBaseAPI: return self.get(self.url) def set_socks_proxy(self, host: str, port: int, username: str = None, password: str = None): - url = f'socks5://{username}:{password}@{host}:{port}' if username and password else f'socks5://{host}:{port}' + url = f"socks5://{username}:{password}@{host}:{port}" if username and password else f"socks5://{host}:{port}" self._req.proxies = dict(http=url, https=url) def set_http_proxy(self, host: str, port: int, username: str = None, password: str = None): - url = f'http://{username}:{password}@{host}:{port}' if username and password else f'http://{host}:{port}' + url = f"http://{username}:{password}@{host}:{port}" if username and password else f"http://{host}:{port}" self._req.proxies = dict(http=url) def _get_main_session_captcha(self) -> Response: - return self.get(f'{self.url}/main/sessionCaptcha') + return self.get(f"{self.url}/main/sessionCaptcha") def _get_main_session_unlock_popup(self) -> Response: - return self.get(f'{self.url}/main/sessionUnlockPopup') + return self.get(f"{self.url}/main/sessionUnlockPopup") def _post_main_session_get_challenge(self, captcha_id: int, image_id: str = "") -> Response: - c = [cookie.name for cookie in self._req.cookies if not cookie.has_nonstandard_attr('HttpOnly')] - env = dict(l=['tets'], s=[], c=c, m=0) + c = [cookie.name for cookie in self._req.cookies if not cookie.has_nonstandard_attr("HttpOnly")] + env = dict(l=["tets"], s=[], c=c, m=0) data = dict(_token=self.token, captchaId=captcha_id, env=utils.b64json(env)) if image_id: data.update(imageId=image_id, isRefresh=True) - return self.post(f'{self.url}/main/sessionGetChallenge', data=data) + return self.post(f"{self.url}/main/sessionGetChallenge", data=data) def _post_main_session_unlock( self, captcha_id: int, image_id: str, challenge_id: str, coords: List[Dict[str, int]], src: str ) -> Response: - c = [cookie.name for cookie in self._req.cookies if not cookie.has_nonstandard_attr('HttpOnly')] - env = dict(l=['tets'], s=[], c=c, m=0) - cookies = dict(sh=hashlib.sha256(','.join(env['l']+env['s']).encode('utf8')).hexdigest(), - ch=hashlib.sha256(','.join(env['c']).encode('utf8')).hexdigest()) - cookie_kwargs = dict(expires=int(time.time())+120, path="/en/main/sessionUnlock", domain='.www.erepublik.com', - secure=True, rest={'HttpOnly': True}) - self._req.cookies.set('sh', cookies['sh'], **cookie_kwargs) - self._req.cookies.set('ch', cookies['ch'], **cookie_kwargs) + c = [cookie.name for cookie in self._req.cookies if not cookie.has_nonstandard_attr("HttpOnly")] + env = dict(l=["tets"], s=[], c=c, m=0) + cookies = dict( + sh=hashlib.sha256(",".join(env["l"] + env["s"]).encode("utf8")).hexdigest(), + ch=hashlib.sha256(",".join(env["c"]).encode("utf8")).hexdigest(), + ) + cookie_kwargs = dict( + expires=int(time.time()) + 120, path="/en/main/sessionUnlock", domain=".www.erepublik.com", secure=True, rest={"HttpOnly": True} + ) + self._req.cookies.set("sh", cookies["sh"], **cookie_kwargs) + self._req.cookies.set("ch", cookies["ch"], **cookie_kwargs) b64_env = utils.b64json(env) - data = dict(_token=self.token, captchaId=captcha_id, imageId=image_id, challengeId=challenge_id, - clickMatrix=utils.json_dumps(coords).replace(' ', ''), isMobile=0, env=b64_env, src=src) - return self.post(f'{self.url}/main/sessionUnlock', data=data, json=data, - headers={'X-Requested-With': 'XMLHttpRequest', 'Referer': 'https://www.erepublik.com/en'}) + data = dict( + _token=self.token, + captchaId=captcha_id, + imageId=image_id, + challengeId=challenge_id, + clickMatrix=utils.json_dumps(coords).replace(" ", ""), + isMobile=0, + env=b64_env, + src=src, + ) + return self.post( + f"{self.url}/main/sessionUnlock", + data=data, + json=data, + headers={"X-Requested-With": "XMLHttpRequest", "Referer": "https://www.erepublik.com/en"}, + ) def _post_energy_refill_get_inventory(self): - return self.post(f'{self.url}/economy/energyRefill-getInventory', data={'_token': self.token}) + return self.post(f"{self.url}/economy/energyRefill-getInventory", data={"_token": self.token}) class ErepublikAnniversaryAPI(CitizenBaseAPI): def _post_main_collect_anniversary_reward(self) -> Response: - return self.post(f"{self.url}/main/collect-anniversary-reward", data={'_token': self.token}) + return self.post(f"{self.url}/main/collect-anniversary-reward", data={"_token": self.token}) # 12th anniversary endpoints def _get_anniversary_quest_data(self) -> Response: return self.get(f"{self.url}/main/anniversaryQuestData") def _post_map_rewards_unlock(self, node_id: int) -> Response: - data = {'nodeId': node_id, '_token': self.token} + data = {"nodeId": node_id, "_token": self.token} return self.post(f"{self.url}/main/map-rewards-unlock", data=data) def _post_map_rewards_speedup(self, node_id: int, currency_amount: int) -> Response: - data = {'nodeId': node_id, '_token': self.token, 'currencyCost': currency_amount} + data = {"nodeId": node_id, "_token": self.token, "currencyCost": currency_amount} return self.post(f"{self.url}/main/map-rewards-speedup", data=data) def _post_map_rewards_claim(self, node_id: int, extra: bool = False) -> Response: - data = {'nodeId': node_id, '_token': self.token} + data = {"nodeId": node_id, "_token": self.token} if extra: - data['claimExtra'] = 1 + data["claimExtra"] = 1 return self.post(f"{self.url}/main/map-rewards-claim", data=data) def _post_main_wheel_of_fortune_spin(self, cost) -> Response: - return self.post(f"{self.url}/main/wheeloffortune-spin", data={'_token': self.token, '_currentCost': cost}) + return self.post(f"{self.url}/main/wheeloffortune-spin", data={"_token": self.token, "_currentCost": cost}) def _post_main_wheel_of_fortune_build(self) -> Response: - return self.post(f"{self.url}/main/wheeloffortune-build", data={'_token': self.token}) + return self.post(f"{self.url}/main/wheeloffortune-build", data={"_token": self.token}) class ErepublikArticleAPI(CitizenBaseAPI): @@ -230,13 +254,13 @@ class ErepublikArticleAPI(CitizenBaseAPI): def _post_main_article_comments(self, article_id: int, page: int = 1) -> Response: data = dict(_token=self.token, articleId=article_id, page=page) if page: - data.update({'page': page}) + data.update({"page": page}) return self.post(f"{self.url}/main/articleComments", data=data) def _post_main_article_comments_create(self, message: str, article_id: int, parent: int = 0) -> Response: data = dict(_token=self.token, message=message, articleId=article_id) if parent: - data.update({'parentId': parent}) + data.update({"parentId": parent}) return self.post(f"{self.url}/main/articleComments/create", data=data) def _post_main_donate_article(self, article_id: int, amount: int) -> Response: @@ -244,8 +268,7 @@ class ErepublikArticleAPI(CitizenBaseAPI): return self.post(f"{self.url}/main/donate-article", data=data) def _post_main_write_article(self, title: str, content: str, country_id: int, kind_id: int) -> Response: - data = dict(_token=self.token, article_name=title, article_body=content, article_location=country_id, - article_category=kind_id) + data = dict(_token=self.token, article_name=title, article_body=content, article_location=country_id, article_category=kind_id) return self.post(f"{self.url}/main/write-article", data=data) def _post_main_vote_article(self, article_id: int) -> Response: @@ -255,13 +278,12 @@ class ErepublikArticleAPI(CitizenBaseAPI): class ErepublikCompanyAPI(CitizenBaseAPI): def _post_economy_assign_to_holding(self, factory_id: int, holding_id: int) -> Response: - data = dict(_token=self.token, factoryId=factory_id, action='assign', holdingCompanyId=holding_id) + data = dict(_token=self.token, factoryId=factory_id, action="assign", holdingCompanyId=holding_id) return self.post(f"{self.url}/economy/assign-to-holding", data=data) def _post_economy_create_company(self, industry_id: int, building_type: int = 1) -> Response: - data = {'_token': self.token, "company[industry_id]": industry_id, "company[building_type]": building_type} - return self.post(f"{self.url}/economy/create-company", data=data, - headers={'Referer': f"{self.url}/economy/create-company"}) + data = {"_token": self.token, "company[industry_id]": industry_id, "company[building_type]": building_type} + return self.post(f"{self.url}/economy/create-company", data=data, headers={"Referer": f"{self.url}/economy/create-company"}) def _get_economy_inventory_items(self) -> Response: return self.get(f"{self.url}/economy/inventory-items/") @@ -278,39 +300,43 @@ class ErepublikCompanyAPI(CitizenBaseAPI): data["grounds[%i][id]" % idx] = tg_id data["grounds[%i][train]" % idx] = 1 if data: - data['_token'] = self.token + data["_token"] = self.token return self.post(f"{self.url}/economy/train", data=data) def _post_economy_upgrade_company(self, factory: int, level: int, pin: str = None) -> Response: - data = dict(_token=self.token, type='upgrade', companyId=factory, level=level, pin="" if pin is None else pin) + data = dict(_token=self.token, type="upgrade", companyId=factory, level=level, pin="" if pin is None else pin) return self.post(f"{self.url}/economy/upgrade-company", data=data) def _post_economy_work(self, action_type: str, wam: List[int] = None, employ: Dict[int, int] = None) -> Response: data: Dict[str, Union[int, str]] = dict(action_type=action_type, _token=self.token) - if action_type == 'production': + if action_type == "production": if employ is None: employ = {} if wam is None: wam = [] max_idx = 0 for company_id in sorted(wam or []): - data.update({ - f"companies[{max_idx}][id]": company_id, - f"companies[{max_idx}][employee_works]": employ.pop(company_id, 0), - f"companies[{max_idx}][own_work]": 1 - }) + data.update( + { + f"companies[{max_idx}][id]": company_id, + f"companies[{max_idx}][employee_works]": employ.pop(company_id, 0), + f"companies[{max_idx}][own_work]": 1, + } + ) max_idx += 1 for company_id in sorted(employ or []): - data.update({ - f"companies[{max_idx}][id]": company_id, - f"companies[{max_idx}][employee_works]": employ.pop(company_id, 0), - f"companies[{max_idx}][own_work]": 0 - }) + data.update( + { + f"companies[{max_idx}][id]": company_id, + f"companies[{max_idx}][employee_works]": employ.pop(company_id, 0), + f"companies[{max_idx}][own_work]": 0, + } + ) max_idx += 1 return self.post(f"{self.url}/economy/work", data=data) def _post_economy_work_overtime(self) -> Response: - data = dict(action_type='workOvertime', _token=self.token) + data = dict(action_type="workOvertime", _token=self.token) return self.post(f"{self.url}/economy/workOvertime", data=data) def _post_economy_job_market_apply(self, citizen_id: int, salary: float) -> Response: @@ -318,29 +344,28 @@ class ErepublikCompanyAPI(CitizenBaseAPI): return self.post(f"{self.url}/economy/job-market-apply", data=data) def _post_economy_resign(self) -> Response: - return self.post(f"{self.url}/economy/resign", - headers={"Content-Type": "application/x-www-form-urlencoded"}, - data={'_token': self.token, 'action_type': 'resign'}) + return self.post( + f"{self.url}/economy/resign", + headers={"Content-Type": "application/x-www-form-urlencoded"}, + data={"_token": self.token, "action_type": "resign"}, + ) def _post_economy_sell_company(self, factory_id: int, pin: int = None, sell: bool = True) -> Response: data = dict(_token=self.token, pin="" if pin is None else pin) if sell: - data.update({'sell': 'sell'}) + data.update({"sell": "sell"}) else: - data.update({'dissolve': factory_id}) - return self.post(f"{self.url}/economy/sell-company/{factory_id}", - data=data, headers={'Referer': self.url}) + data.update({"dissolve": factory_id}) + return self.post(f"{self.url}/economy/sell-company/{factory_id}", data=data, headers={"Referer": self.url}) class ErepublikCountryAPI(CitizenBaseAPI): def _get_country_military(self, country_name: str) -> Response: return self.get(f"{self.url}/country/military/{country_name}") - def _post_main_country_donate(self, country_id: int, action: str, value: Union[int, float], - quality: int = None) -> Response: + def _post_main_country_donate(self, country_id: int, action: str, value: Union[int, float], quality: int = None) -> Response: data = dict(countryId=country_id, action=action, _token=self.token, value=value, quality=quality) - return self.post(f"{self.url}/main/country-donate", data=data, - headers={'Referer': f"{self.url}/country/economy/Latvia"}) + return self.post(f"{self.url}/main/country-donate", data=data, headers={"Referer": f"{self.url}/country/economy/Latvia"}) class ErepublikEconomyAPI(CitizenBaseAPI): @@ -362,20 +387,20 @@ class ErepublikEconomyAPI(CitizenBaseAPI): return self.post(f"{self.url}/economy/activateBooster", data=data) def _post_economy_activate_house(self, quality: int) -> Response: - data = dict(action='activate', quality=quality, type='house', _token=self.token) + data = dict(action="activate", quality=quality, type="house", _token=self.token) return self.post(f"{self.url}/economy/activateHouse", data=data) - def _post_economy_donate_items_action(self, citizen_id: int, amount: int, industry: int, - quality: int) -> Response: + def _post_economy_donate_items_action(self, citizen_id: int, amount: int, industry: int, quality: int) -> Response: data = dict(citizen_id=citizen_id, amount=amount, industry_id=industry, quality=quality, _token=self.token) - return self.post(f"{self.url}/economy/donate-items-action", data=data, - headers={'Referer': f"{self.url}/economy/donate-items/{citizen_id}"}) + return self.post( + f"{self.url}/economy/donate-items-action", data=data, headers={"Referer": f"{self.url}/economy/donate-items/{citizen_id}"} + ) - def _post_economy_donate_money_action(self, citizen_id: int, amount: float = 0.0, - currency: int = 62) -> Response: + def _post_economy_donate_money_action(self, citizen_id: int, amount: float = 0.0, currency: int = 62) -> Response: data = dict(citizen_id=citizen_id, _token=self.token, currency_id=currency, amount=amount) - return self.post(f"{self.url}/economy/donate-money-action", data=data, - headers={'Referer': f"{self.url}/economy/donate-money/{citizen_id}"}) + return self.post( + f"{self.url}/economy/donate-money-action", data=data, headers={"Referer": f"{self.url}/economy/donate-money/{citizen_id}"} + ) def _post_economy_exchange_purchase(self, amount: float, currency: int, offer: int) -> Response: data = dict(_token=self.token, amount=amount, currencyId=currency, offerId=offer) @@ -386,46 +411,56 @@ class ErepublikEconomyAPI(CitizenBaseAPI): return self.post(f"{self.url}/economy/exchange/retrieve/", data=data) def _post_economy_game_tokens_market(self, action: str) -> Response: - assert action in ['retrieve', ] + assert action in [ + "retrieve", + ] data = dict(_token=self.token, action=action) return self.post(f"{self.url}/economy/gameTokensMarketAjax", data=data) - def _post_economy_marketplace(self, country: int, industry: int, quality: int, - order_asc: bool = True) -> Response: - data = dict(countryId=country, industryId=industry, quality=quality, ajaxMarket=1, - orderBy='price_asc' if order_asc else 'price_desc', _token=self.token) + def _post_economy_marketplace(self, country: int, industry: int, quality: int, order_asc: bool = True) -> Response: + data = dict( + countryId=country, + industryId=industry, + quality=quality, + ajaxMarket=1, + orderBy="price_asc" if order_asc else "price_desc", + _token=self.token, + ) return self.post(f"{self.url}/economy/marketplaceAjax", data=data) def _post_economy_marketplace_actions(self, action: str, **kwargs) -> Response: - if action == 'buy': - data = dict(_token=self.token, offerId=kwargs['offer'], amount=kwargs['amount'], - orderBy='price_asc', currentPage=1, buyAction=1) - elif action == 'sell': - data = dict(_token=self.token, countryId=kwargs['country_id'], price=kwargs['price'], - industryId=kwargs['industry'], quality=kwargs['quality'], amount=kwargs['amount'], - sellAction='postOffer') - elif action == 'delete': - data = dict(_token=self.token, offerId=kwargs['offer_id'], sellAction='deleteOffer') + if action == "buy": + data = dict( + _token=self.token, offerId=kwargs["offer"], amount=kwargs["amount"], orderBy="price_asc", currentPage=1, buyAction=1 + ) + elif action == "sell": + data = dict( + _token=self.token, + countryId=kwargs["country_id"], + price=kwargs["price"], + industryId=kwargs["industry"], + quality=kwargs["quality"], + amount=kwargs["amount"], + sellAction="postOffer", + ) + elif action == "delete": + data = dict(_token=self.token, offerId=kwargs["offer_id"], sellAction="deleteOffer") else: raise ValueError(f"Action '{action}' is not supported! Only 'buy/sell/delete' actions are available") return self.post(f"{self.url}/economy/marketplaceActions", data=data) class ErepublikLeaderBoardAPI(CitizenBaseAPI): - def _get_main_leaderboards_damage_aircraft_rankings(self, country_id: int, weeks: int = 0, - mu_id: int = 0) -> Response: # noqa + def _get_main_leaderboards_damage_aircraft_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0) -> Response: # noqa return self.get(f"{self.url}/main/leaderboards-damage-aircraft-rankings/{country_id}/{weeks}/{mu_id}/0") - def _get_main_leaderboards_damage_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0, - div: int = 0) -> Response: # noqa + def _get_main_leaderboards_damage_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0, div: int = 0) -> Response: # noqa return self.get(f"{self.url}/main/leaderboards-damage-rankings/{country_id}/{weeks}/{mu_id}/{div}") - def _get_main_leaderboards_kills_aircraft_rankings(self, country_id: int, weeks: int = 0, - mu_id: int = 0) -> Response: # noqa + def _get_main_leaderboards_kills_aircraft_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0) -> Response: # noqa return self.get(f"{self.url}/main/leaderboards-kills-aircraft-rankings/{country_id}/{weeks}/{mu_id}/0") - def _get_main_leaderboards_kills_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0, - div: int = 0) -> Response: # noqa + def _get_main_leaderboards_kills_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0, div: int = 0) -> Response: # noqa return self.get(f"{self.url}/main/leaderboards-kills-rankings/{country_id}/{weeks}/{mu_id}/{div}") @@ -433,7 +468,7 @@ class ErepublikLocationAPI(CitizenBaseAPI): def _get_main_city_data_residents(self, city_id: int, page: int = 1, params: Mapping[str, Any] = None) -> Response: if params is None: params = {} - return self.get(f"{self.url}/main/city-data/{city_id}/residents", params={'currentPage': page, **params}) + return self.get(f"{self.url}/main/city-data/{city_id}/residents", params={"currentPage": page, **params}) class ErepublikMilitaryAPI(CitizenBaseAPI): @@ -444,7 +479,7 @@ class ErepublikMilitaryAPI(CitizenBaseAPI): return self.get(f"{self.url}/military/battlefield-choose-side/{battle_id}/{side_id}") def _get_military_show_weapons(self, battle_id: int) -> Response: - return self.get(f"{self.url}/military/show-weapons", params={'_token': self.token, 'battleId': battle_id}) + return self.get(f"{self.url}/military/show-weapons", params={"_token": self.token, "battleId": battle_id}) def _get_military_campaigns(self) -> Response: return self.get(f"{self.url}/military/campaigns-new/") @@ -456,7 +491,7 @@ class ErepublikMilitaryAPI(CitizenBaseAPI): return self.get(f"{self.url}/military/campaignsJson/citizen") def _get_military_unit_data(self, unit_id: int, **kwargs) -> Response: - params = {'groupId': unit_id, 'panel': 'members', **kwargs} + params = {"groupId": unit_id, "panel": "members", **kwargs} return self.get(f"{self.url}/military/military-unit-data/", params=params) def _post_main_activate_battle_effect(self, battle_id: int, kind: str, citizen_id: int) -> Response: @@ -480,22 +515,32 @@ class ErepublikMilitaryAPI(CitizenBaseAPI): data = dict(type=kind, quality=quality, duration=duration, battleId=battle_id, _token=self.token) return self.post(f"{self.url}/military/fight-activateBooster", data=data) - def _post_military_change_weapon(self, battle_id: int, battle_zone: int, weapon_level: int, ) -> Response: + def _post_military_change_weapon( + self, + battle_id: int, + battle_zone: int, + weapon_level: int, + ) -> Response: data = dict(battleId=battle_id, _token=self.token, battleZoneId=battle_zone, customizationLevel=weapon_level) return self.post(f"{self.url}/military/change-weapon", data=data) def _post_military_battle_console(self, battle_id: int, action: str, page: int = 1, **kwargs) -> Response: data = dict(battleId=battle_id, action=action, _token=self.token) - if action == 'battleStatistics': - data.update(round=kwargs['round_id'], zoneId=kwargs['round_id'], leftPage=page, rightPage=page, - division=kwargs['division'], type=kwargs.get('type', 'damage'), ) - elif action == 'warList': + if action == "battleStatistics": + data.update( + round=kwargs["round_id"], + zoneId=kwargs["round_id"], + leftPage=page, + rightPage=page, + division=kwargs["division"], + type=kwargs.get("type", "damage"), + ) + elif action == "warList": data.update(page=page) return self.post(f"{self.url}/military/battle-console", data=data) def _post_military_deploy_bomb(self, battle_id: int, division_id: int, side_id: int, bomb_id: int) -> Response: - data = dict(battleId=battle_id, battleZoneId=division_id, sideId=side_id, sideCountryId=side_id, - bombId=bomb_id, _token=self.token) + data = dict(battleId=battle_id, battleZoneId=division_id, sideId=side_id, sideCountryId=side_id, bombId=bomb_id, _token=self.token) return self.post(f"{self.url}/military/deploy-bomb", data=data) def _post_military_fight_air(self, battle_id: int, side_id: int, zone_id: int) -> Response: @@ -517,8 +562,15 @@ class ErepublikMilitaryAPI(CitizenBaseAPI): def _post_fight_deploy_start_deploy( self, battle_id: int, side_id: int, battle_zone_id: int, energy: int, weapon: int, **kwargs ) -> Response: - data = dict(_token=self.token, battleId=battle_id, battleZoneId=battle_zone_id, sideCountryId=side_id, - weaponQuality=weapon, totalEnergy=energy, **kwargs) + data = dict( + _token=self.token, + battleId=battle_id, + battleZoneId=battle_zone_id, + sideCountryId=side_id, + weaponQuality=weapon, + totalEnergy=energy, + **kwargs, + ) return self.post(f"{self.url}/military/fightDeploy-startDeploy", data=data) def _post_military_fight_deploy_deploy_report_data(self, deployment_id: int) -> Response: @@ -544,8 +596,7 @@ class ErepublikPoliticsAPI(CitizenBaseAPI): return self.get(f"{self.url}/main/presidential-elections/{country_id}/{timestamp}") def _post_propose_president_candidate(self, party_slug: str, citizen_id: int) -> Response: - return self.post(f"{self.url}/propose-president-candidate/{party_slug}", - data=dict(_token=self.token, citizen=citizen_id)) + return self.post(f"{self.url}/propose-president-candidate/{party_slug}", data=dict(_token=self.token, citizen=citizen_id)) def _get_auto_propose_president_candidate(self, party_slug: str) -> Response: return self.get(f"{self.url}/auto-propose-president-candidate/{party_slug}") @@ -553,17 +604,15 @@ class ErepublikPoliticsAPI(CitizenBaseAPI): class ErepublikPresidentAPI(CitizenBaseAPI): def _post_wars_attack_region(self, war_id: int, region_id: int, region_name: str) -> Response: - data = {'_token': self.token, 'warId': war_id, 'regionName': region_name, 'regionNameConfirm': region_name} - return self.post(f'{self.url}/wars/attack-region/{war_id}/{region_id}', data=data) + data = {"_token": self.token, "warId": war_id, "regionName": region_name, "regionNameConfirm": region_name} + return self.post(f"{self.url}/wars/attack-region/{war_id}/{region_id}", data=data) def _post_new_war(self, self_country_id: int, attack_country_id: int, debate: str = "") -> Response: - data = dict(requirments=1, _token=self.token, debate=debate, - countryNameConfirm=constants.COUNTRIES[attack_country_id].link) + data = dict(requirments=1, _token=self.token, debate=debate, countryNameConfirm=constants.COUNTRIES[attack_country_id].link) return self.post(f"{self.url}/{constants.COUNTRIES[self_country_id].link}/new-war", data=data) def _post_new_donation(self, country_id: int, amount: int, org_name: str, debate: str = "") -> Response: - data = dict(requirments=1, _token=self.token, debate=debate, currency=1, value=amount, commit='Propose', - type_name=org_name) + data = dict(requirments=1, _token=self.token, debate=debate, currency=1, value=amount, commit="Propose", type_name=org_name) return self.post(f"{self.url}/{constants.COUNTRIES[country_id].link}/new-donation", data=data) @@ -584,10 +633,10 @@ class ErepublikProfileAPI(CitizenBaseAPI): return self.get(f"{self.url}/main/messages-paginated/{page}") def _get_main_money_donation_accept(self, donation_id: int) -> Response: - return self.get(f"{self.url}/main/money-donation/accept/{donation_id}", params={'_token': self.token}) + return self.get(f"{self.url}/main/money-donation/accept/{donation_id}", params={"_token": self.token}) def _get_main_money_donation_reject(self, donation_id: int) -> Response: - return self.get(f"{self.url}/main/money-donation/reject/{donation_id}", params={'_token': self.token}) + return self.get(f"{self.url}/main/money-donation/reject/{donation_id}", params={"_token": self.token}) def _get_main_notifications_ajax_community(self, page: int = 1) -> Response: return self.get(f"{self.url}/main/notificationsAjax/community/{page}") @@ -607,16 +656,16 @@ class ErepublikProfileAPI(CitizenBaseAPI): def _post_main_citizen_add_remove_friend(self, citizen: int, add: bool) -> Response: data = dict(_token=self.token, citizenId=citizen, url="//www.erepublik.com/en/main/citizen-addRemoveFriend") if add: - data.update({'action': 'addFriend'}) + data.update({"action": "addFriend"}) else: - data.update({'action': 'removeFriend'}) + data.update({"action": "removeFriend"}) return self.post(f"{self.url}/main/citizen-addRemoveFriend", data=data) def _post_main_daily_task_reward(self) -> Response: return self.post(f"{self.url}/main/daily-tasks-reward", data=dict(_token=self.token)) def _post_delete_message(self, msg_id: list) -> Response: - data = {'_token': self.token, "delete_message[]": msg_id} + data = {"_token": self.token, "delete_message[]": msg_id} return self.post(f"{self.url}/main/messages-delete", data) def _post_eat(self, color: str) -> Response: @@ -628,37 +677,36 @@ class ErepublikProfileAPI(CitizenBaseAPI): return self.post(f"{self.url}/main/global-alerts/close", data=data) def _post_forgot_password(self, email: str) -> Response: - data = dict(_token=self.token, email=email, commit='Reset password') + data = dict(_token=self.token, email=email, commit="Reset password") return self.post(f"{self.url}/forgot-password", data=data) def _post_login(self, email: str, password: str) -> Response: - data = dict(csrf_token=self.token, citizen_email=email, citizen_password=password, remember='on') + data = dict(csrf_token=self.token, citizen_email=email, citizen_password=password, remember="on") return self.post(f"{self.url}/login", data=data) def _post_main_messages_alert(self, notification_ids: List[int]) -> Response: - data = {'_token': self.token, "delete_alerts[]": notification_ids, 'deleteAllAlerts': '1', 'delete': 'Delete'} + data = {"_token": self.token, "delete_alerts[]": notification_ids, "deleteAllAlerts": "1", "delete": "Delete"} return self.post(f"{self.url}/main/messages-alerts/1", data=data) def _post_main_notifications_ajax_community(self, notification_ids: List[int], page: int = 1) -> Response: - data = {'_token': self.token, "delete_alerts[]": notification_ids} + data = {"_token": self.token, "delete_alerts[]": notification_ids} return self.post(f"{self.url}/main/notificationsAjax/community/{page}", data=data) def _post_main_notifications_ajax_system(self, notification_ids: List[int], page: int = 1) -> Response: - data = {'_token': self.token, "delete_alerts[]": notification_ids} + data = {"_token": self.token, "delete_alerts[]": notification_ids} return self.post(f"{self.url}/main/notificationsAjax/system/{page}", data=data) def _post_main_notifications_ajax_report(self, notification_ids: List[int], page: int = 1) -> Response: - data = {'_token': self.token, "delete_alerts[]": notification_ids} + data = {"_token": self.token, "delete_alerts[]": notification_ids} return self.post(f"{self.url}/main/notificationsAjax/report/{page}", data=data) def _post_main_messages_compose(self, subject: str, body: str, citizens: List[int]) -> Response: url_pk = 0 if len(citizens) > 1 else str(citizens[0]) - data = dict(citizen_name=",".join([str(x) for x in citizens]), - citizen_subject=subject, _token=self.token, citizen_message=body) + data = dict(citizen_name=",".join([str(x) for x in citizens]), citizen_subject=subject, _token=self.token, citizen_message=body) return self.post(f"{self.url}/main/messages-compose/{url_pk}", data=data) def _post_military_group_missions(self) -> Response: - data = dict(action='check', _token=self.token) + data = dict(action="check", _token=self.token) return self.post(f"{self.url}/military/group-missions", data=data) def _post_main_weekly_challenge_reward(self, reward_id: int) -> Response: @@ -670,7 +718,7 @@ class ErepublikProfileAPI(CitizenBaseAPI): return self.post(f"{self.url}/main/weekly-challenge-collect-all", data=data) def _post_main_profile_update(self, action: str, params: str): - data = {'action': action, 'params': params, '_token': self.token} + data = {"action": action, "params": params, "_token": self.token} return self.post(f"{self.url}/main/profile-update", data=data) @@ -687,85 +735,95 @@ class ErepublikWallPostAPI(CitizenBaseAPI): # ## Country def _post_main_country_comment_retrieve(self, post_id: int) -> Response: - data = {'_token': self.token, 'postId': post_id} + data = {"_token": self.token, "postId": post_id} return self.post(f"{self.url}/main/country-comment/retrieve/json", data=data) def _post_main_country_comment_create(self, post_id: int, comment_message: str) -> Response: - data = {'_token': self.token, 'postId': post_id, 'comment_message': comment_message} + data = {"_token": self.token, "postId": post_id, "comment_message": comment_message} return self.post(f"{self.url}/main/country-comment/create/json", data=data) def _post_main_country_post_create(self, body: str, post_as: int) -> Response: - data = {'_token': self.token, 'post_message': body, 'post_as': post_as} + data = {"_token": self.token, "post_message": body, "post_as": post_as} return self.post(f"{self.url}/main/country-post/create/json", data=data) def _post_main_country_post_retrieve(self) -> Response: - data = {'_token': self.token, 'page': 1, 'switchedFrom': False} + data = {"_token": self.token, "page": 1, "switchedFrom": False} return self.post(f"{self.url}/main/country-post/retrieve/json", data=data) # ## Military Unit def _post_main_military_unit_comment_retrieve(self, post_id: int) -> Response: - data = {'_token': self.token, 'postId': post_id} + data = {"_token": self.token, "postId": post_id} return self.post(f"{self.url}/main/military-unit-comment/retrieve/json", data=data) def _post_main_military_unit_comment_create(self, post_id: int, comment_message: str) -> Response: - data = {'_token': self.token, 'postId': post_id, 'comment_message': comment_message} + data = {"_token": self.token, "postId": post_id, "comment_message": comment_message} return self.post(f"{self.url}/main/military-unit-comment/create/json", data=data) def _post_main_military_unit_post_create(self, body: str, post_as: int) -> Response: - data = {'_token': self.token, 'post_message': body, 'post_as': post_as} + data = {"_token": self.token, "post_message": body, "post_as": post_as} return self.post(f"{self.url}/main/military-unit-post/create/json", data=data) def _post_main_military_unit_post_retrieve(self) -> Response: - data = {'_token': self.token, 'page': 1, 'switchedFrom': False} + data = {"_token": self.token, "page": 1, "switchedFrom": False} return self.post(f"{self.url}/main/military-unit-post/retrieve/json", data=data) # ## Party def _post_main_party_comment_retrieve(self, post_id: int) -> Response: - data = {'_token': self.token, 'postId': post_id} + data = {"_token": self.token, "postId": post_id} return self.post(f"{self.url}/main/party-comment/retrieve/json", data=data) def _post_main_party_comment_create(self, post_id: int, comment_message: str) -> Response: - data = {'_token': self.token, 'postId': post_id, 'comment_message': comment_message} + data = {"_token": self.token, "postId": post_id, "comment_message": comment_message} return self.post(f"{self.url}/main/party-comment/create/json", data=data) def _post_main_party_post_create(self, body: str) -> Response: - data = {'_token': self.token, 'post_message': body} + data = {"_token": self.token, "post_message": body} return self.post(f"{self.url}/main/party-post/create/json", data=data) def _post_main_party_post_retrieve(self) -> Response: - data = {'_token': self.token, 'page': 1, 'switchedFrom': False} + data = {"_token": self.token, "page": 1, "switchedFrom": False} return self.post(f"{self.url}/main/party-post/retrieve/json", data=data) # ## Friend's Wall def _post_main_wall_comment_retrieve(self, post_id: int) -> Response: - data = {'_token': self.token, 'postId': post_id} + data = {"_token": self.token, "postId": post_id} return self.post(f"{self.url}/main/wall-comment/retrieve/json", data=data) def _post_main_wall_comment_create(self, post_id: int, comment_message: str) -> Response: - data = {'_token': self.token, 'postId': post_id, 'comment_message': comment_message} + data = {"_token": self.token, "postId": post_id, "comment_message": comment_message} return self.post(f"{self.url}/main/wall-comment/create/json", data=data) def _post_main_wall_post_create(self, body: str) -> Response: - data = {'_token': self.token, 'post_message': body} + data = {"_token": self.token, "post_message": body} return self.post(f"{self.url}/main/wall-post/create/json", data=data) def _post_main_wall_post_retrieve(self) -> Response: - data = {'_token': self.token, 'page': 1, 'switchedFrom': False} + data = {"_token": self.token, "page": 1, "switchedFrom": False} return self.post(f"{self.url}/main/wall-post/retrieve/json", data=data) # ## Medal posting def _post_main_wall_post_automatic(self, message: str, achievement_id: int) -> Response: - return self.post(f"{self.url}/main/wall-post/automatic", data=dict(_token=self.token, message=message, - achievementId=achievement_id)) + return self.post( + f"{self.url}/main/wall-post/automatic", data=dict(_token=self.token, message=message, achievementId=achievement_id) + ) class CitizenAPI( - ErepublikArticleAPI, ErepublikCountryAPI, ErepublikCompanyAPI, ErepublikEconomyAPI, - ErepublikLeaderBoardAPI, ErepublikLocationAPI, ErepublikMilitaryAPI, ErepublikProfileAPI, - ErepublikPresidentAPI, ErepublikPoliticsAPI, ErepublikAnniversaryAPI, ErepublikWallPostAPI, - ErepublikTravelAPI + ErepublikArticleAPI, + ErepublikCountryAPI, + ErepublikCompanyAPI, + ErepublikEconomyAPI, + ErepublikLeaderBoardAPI, + ErepublikLocationAPI, + ErepublikMilitaryAPI, + ErepublikProfileAPI, + ErepublikPresidentAPI, + ErepublikPoliticsAPI, + ErepublikAnniversaryAPI, + ErepublikWallPostAPI, + ErepublikTravelAPI, ): pass diff --git a/erepublik/citizen.py b/erepublik/citizen.py index b0c3fba..2251dba 100644 --- a/erepublik/citizen.py +++ b/erepublik/citizen.py @@ -48,7 +48,7 @@ class BaseCitizen(access_points.CitizenAPI): logger: logging.Logger r: Response = None - name: str = 'Not logged in!' + name: str = "Not logged in!" logged_in: bool = False restricted_ip: bool = False @@ -62,7 +62,7 @@ class BaseCitizen(access_points.CitizenAPI): self.reporter = classes.Reporter(self) self.stop_threads = Event() logger_class = logging.getLoggerClass() - self.logger = logger_class('Citizen') + self.logger = logger_class("Citizen") self.telegram = classes.TelegramReporter(stop_event=self.stop_threads) @@ -86,7 +86,7 @@ class BaseCitizen(access_points.CitizenAPI): html = resp.text self._check_response_for_medals(html) - re_token = re.search(r'var csrfToken = \'(\w{32})\'', html) + re_token = re.search(r"var csrfToken = \'(\w{32})\'", html) re_login_token = re.search(r'', html) if re_token: self.token = re_token.group(1) @@ -104,16 +104,16 @@ class BaseCitizen(access_points.CitizenAPI): def get(self, url: str, **kwargs) -> Response: if (self.now - self._req.last_time).seconds >= 15 * 60: self.get_csrf_token() - if 'params' in kwargs: - if '_token' in kwargs['params']: - kwargs['params']['_token'] = self.token + if "params" in kwargs: + if "_token" in kwargs["params"]: + kwargs["params"]["_token"] = self.token if self.r and url == self.r.url and not url == self.url: # Don't duplicate requests, except for homepage response = self.r else: try: response = super().get(url, **kwargs) except RequestException: - self.report_error('Network error while issuing GET request') + self.report_error("Network error while issuing GET request") self.sleep(60) return self.get(url, **kwargs) @@ -138,31 +138,31 @@ class BaseCitizen(access_points.CitizenAPI): data = {} if (self.now - self._req.last_time).seconds >= 14 * 60: self.get_csrf_token() - if '_token' in data: - data['_token'] = self.token - if '_token' in json: - json['_token'] = self.token + if "_token" in data: + data["_token"] = self.token + if "_token" in json: + json["_token"] = self.token try: response = super().post(url, data=data, json=json, **kwargs) except RequestException: - self.report_error('Network error while issuing POST request') + self.report_error("Network error while issuing POST request") self.sleep(60) return self.post(url, data=data, json=json, **kwargs) try: r_json = response.json() - if (r_json.get('error') or not r_json.get('status')) and r_json.get('message', '') == 'captcha': - self.write_warning('Regular captcha must be filled!', extra=r_json) + if (r_json.get("error") or not r_json.get("status")) and r_json.get("message", "") == "captcha": + self.write_warning("Regular captcha must be filled!", extra=r_json) except (AttributeError, utils.json.JSONDecodeError, ValueError, KeyError): pass if self._errors_in_response(response): self.get_csrf_token() if data: - data.update({'_token': self.token}) + data.update({"_token": self.token}) elif json: - json.update({'_token': self.token}) + json.update({"_token": self.token}) response = self.post(url, data=data, json=json, **kwargs) else: self._check_response_for_medals(response.text) @@ -185,8 +185,8 @@ class BaseCitizen(access_points.CitizenAPI): self.promos = {k: v for k, v in self.promos.items() if v > self.now} send_mail = False for promo in promos: - promo_name = promo.get('id') - expire = utils.localize_timestamp(int(promo.get('expiresAt'))) + promo_name = promo.get("id") + expire = utils.localize_timestamp(int(promo.get("expiresAt"))) if promo_name not in self.promos: send_mail = True self.promos.update({promo_name: expire}) @@ -198,53 +198,50 @@ class BaseCitizen(access_points.CitizenAPI): new_date = re.search(r"var new_date = '(\d*)';", html) if new_date: - self.energy.set_reference_time( - utils.good_timedelta(self.now, timedelta(seconds=int(new_date.group(1)))) - ) + self.energy.set_reference_time(utils.good_timedelta(self.now, timedelta(seconds=int(new_date.group(1))))) ugly_js = re.search(r"var erepublik = ({.*}),\s+", html).group(1) citizen_js = utils.json.loads(ugly_js) - citizen = citizen_js.get('citizen', {}) + citizen = citizen_js.get("citizen", {}) - self.details.citizen_id = int(citizen['citizenId']) - self.name = citizen['name'] + self.details.citizen_id = int(citizen["citizenId"]) + self.name = citizen["name"] - self.eday = citizen_js.get('settings').get('eDay') - self.division = int(citizen.get('division', 0)) + self.eday = citizen_js.get("settings").get("eDay") + self.division = int(citizen.get("division", 0)) - self.energy.interval = citizen.get('energyPerInterval', 0) - self.energy.limit = citizen.get('energyToRecover', 0) - self.energy.energy = citizen.get('energy', 0) - # self.energy.set_reference_time(utils.good_timedelta(self.now, timedelta(seconds=int(next_recovery[1]) * 60 + int(next_recovery[2])))) + self.energy.interval = citizen.get("energyPerInterval", 0) + self.energy.limit = citizen.get("energyToRecover", 0) + self.energy.energy = citizen.get("energy", 0) + # self.energy.set_reference_time(utils.good_timedelta(self.now, + # timedelta(seconds=int(next_recovery[1]) * 60 + int(next_recovery[2])))) - self.details.current_region = citizen.get('regionLocationId', 0) - self.details.current_country = constants.COUNTRIES.get( - citizen.get('countryLocationId', 0)) # country where citizen is located - self.details.residence_region = citizen.get('residence', {}).get('regionId', 0) - self.details.residence_country = constants.COUNTRIES.get(citizen.get('residence', {}).get('countryId', 0)) - self.details.citizen_id = citizen.get('citizenId', 0) - self.details.citizenship = constants.COUNTRIES.get(int(citizen.get('country', 0))) - self.details.xp = citizen.get('currentExperiencePoints', 0) - self.details.level = citizen.get('userLevel', 0) - self.details.daily_task_done = citizen.get('dailyTasksDone', False) - self.details.daily_task_reward = citizen.get('hasReward', False) - self.maverick = citizen.get('canSwitchDivisions', False) - if citizen.get('dailyOrderDone', False) and not citizen.get('hasDailyOrderReward', False): + self.details.current_region = citizen.get("regionLocationId", 0) + self.details.current_country = constants.COUNTRIES.get(citizen.get("countryLocationId", 0)) # country where citizen is located + self.details.residence_region = citizen.get("residence", {}).get("regionId", 0) + self.details.residence_country = constants.COUNTRIES.get(citizen.get("residence", {}).get("countryId", 0)) + self.details.citizen_id = citizen.get("citizenId", 0) + self.details.citizenship = constants.COUNTRIES.get(int(citizen.get("country", 0))) + self.details.xp = citizen.get("currentExperiencePoints", 0) + self.details.level = citizen.get("userLevel", 0) + self.details.daily_task_done = citizen.get("dailyTasksDone", False) + self.details.daily_task_reward = citizen.get("hasReward", False) + self.maverick = citizen.get("canSwitchDivisions", False) + if citizen.get("dailyOrderDone", False) and not citizen.get("hasDailyOrderReward", False): self._post_military_group_missions() self.details.next_pp.sort() - for skill in citizen.get('terrainSkills', {}).values(): - self.details.mayhem_skills.update({int(skill['terrain_id']): int(skill['skill_points'])}) + for skill in citizen.get("terrainSkills", {}).values(): + self.details.mayhem_skills.update({int(skill["terrain_id"]): int(skill["skill_points"])}) - if citizen.get('party', []): - party = citizen.get('party') + if citizen.get("party", []): + party = citizen.get("party") self.politics.is_party_member = True - self.politics.party_id = party.get('party_id') - self.politics.is_party_president = bool(party.get('is_party_president')) + self.politics.party_id = party.get("party_id") + self.politics.is_party_president = bool(party.get("is_party_president")) self.politics.party_slug = f"{party.get('stripped_title')}-{party.get('party_id')}" - self.wheel_of_fortune = bool( - re.search(r'', html)) + self.wheel_of_fortune = bool(re.search(r'', html)) def update_inventory(self): """ @@ -254,26 +251,26 @@ class BaseCitizen(access_points.CitizenAPI): def do_captcha_challenge(self, retry: int = 0) -> bool: r = self._get_main_session_captcha() - data = re.search(r'\$j\.extend\(SERVER_DATA,([^)]+)\)', r.text) + data = re.search(r"\$j\.extend\(SERVER_DATA,([^)]+)\)", r.text) if data: data = utils.json_loads(utils.normalize_html_json(data.group(1))) - captcha_id = data.get('sessionValidation', {}).get("captchaId") + captcha_id = data.get("sessionValidation", {}).get("captchaId") captcha_data = self._post_main_session_get_challenge(captcha_id).json() - coordinates = self.solve_captcha(captcha_data['src']) + coordinates = self.solve_captcha(captcha_data["src"]) while True: r = self._post_main_session_unlock( - captcha_id, captcha_data['imageId'], captcha_data['challengeId'], coordinates, captcha_data['src'] + captcha_id, captcha_data["imageId"], captcha_data["challengeId"], coordinates, captcha_data["src"] ) rj = r.json() - if not rj.get('error') and rj.get('verified'): + if not rj.get("error") and rj.get("verified"): return True else: try: - raise classes.ErepublikException('Captcha failed!') + raise classes.ErepublikException("Captcha failed!") except classes.ErepublikException: - self.report_error('Captcha failed!') - captcha_data = self._post_main_session_get_challenge(captcha_id, captcha_data['imageId']).json() - coordinates = self.solve_captcha(captcha_data['src']) + self.report_error("Captcha failed!") + captcha_data = self._post_main_session_get_challenge(captcha_id, captcha_data["imageId"]).json() + coordinates = self.solve_captcha(captcha_data["src"]) if retry < 1: return False retry -= 1 @@ -292,9 +289,11 @@ class BaseCitizen(access_points.CitizenAPI): message: str result: Optional[List[_API_RESULT]] - solve_data: _API_RETURN = self.post('https://api.erep.lv/captcha/api', data=dict(citizen_id=self.details.citizen_id, src=src, key='CaptchaDevAPI')).json() - if solve_data['status']: - return solve_data.get('result') + solve_data: _API_RETURN = self.post( + "https://api.erep.lv/captcha/api", data=dict(citizen_id=self.details.citizen_id, src=src, key="CaptchaDevAPI") + ).json() + if solve_data["status"]: + return solve_data.get("result") @property def inventory(self) -> classes.Inventory: @@ -311,65 +310,68 @@ class BaseCitizen(access_points.CitizenAPI): def _expire_value_to_python(_expire_value: str) -> Dict[str, Union[int, datetime]]: _data = re.search( - r'((?P\d+) item\(s\) )?[eE]xpires? on Day (?P\d,\d{3}), (?P
", medal, re.M | re.S) + info = re.search( + r"

New Achievement

.*?(.*?)

.*?" + r"achievement_recieved.*?(.*?).*?" + r"
", + medal, + re.M | re.S, + ) about = info.group(1).strip() title = info.group(2).strip() award_id = re.search(r'"wall_enable_alerts_(\d+)', medal) @@ -742,7 +785,7 @@ class BaseCitizen(access_points.CitizenAPI): self._post_main_wall_post_automatic(message=title, achievement_id=award_id) except ValueError: pass - reward, currency = info.group(3).strip().split(' ') + reward, currency = info.group(3).strip().split(" ") while not isinstance(reward, float): try: reward = float(reward) @@ -752,19 +795,18 @@ class BaseCitizen(access_points.CitizenAPI): if (title, reward) not in data: data[(title, reward)] = dict(about=about, kind=title, reward=reward, count=1, currency=currency) else: - data[(title, reward)]['count'] += 1 + data[(title, reward)]["count"] += 1 except AttributeError: continue if data: - msgs = [f"{d['count']} x {d['kind']}, totaling {d['count'] * d['reward']} " - f"{d['currency']}" for d in data.values()] + msgs = [f"{d['count']} x {d['kind']}, totaling {d['count'] * d['reward']} " f"{d['currency']}" for d in data.values()] msgs = "\n".join(msgs) if self.config.telegram: self.telegram.report_medal(msgs) self.write_log(f"Found awards:\n{msgs}") for info in data.values(): - self.reporter.report_action('NEW_MEDAL', info) + self.reporter.report_action("NEW_MEDAL", info) levelup = re.search(r"

Congratulations, you have reached experience level (\d+)

", html) if levelup: @@ -773,17 +815,16 @@ class BaseCitizen(access_points.CitizenAPI): self.write_log(msg) if self.config.telegram: self.telegram.report_medal(f"Level *{level}*") - self.reporter.report_action('LEVEL_UP', value=level) + self.reporter.report_action("LEVEL_UP", value=level) def _travel(self, country: constants.Country, region_id: int = 0) -> Response: data = dict(toCountryId=country.id, inRegionId=region_id) - return self._post_main_travel('moveAction', **data) + return self._post_main_travel("moveAction", **data) def _get_main_party_members(self, party_id: int) -> Dict[int, str]: ret = {} r = super()._get_main_party_members(party_id) - for id_, name in re.findall(r'
', r.text): + for id_, name in re.findall(r'', r.text): ret.update({id_: name}) return ret @@ -800,53 +841,61 @@ class BaseCitizen(access_points.CitizenAPI): self.get_csrf_token() if re.search('
', self.r.text): self.restricted_ip = True - self.write_warning('eRepublik has blacklisted IP. Limited functionality!', ) + self.write_warning( + "eRepublik has blacklisted IP. Limited functionality!", + ) self.logged_in = True def _errors_in_response(self, response: Response): try: j = response.json() - if j['error'] and j['message'] == 'Too many requests': - self.write_warning('Made too many requests! Sleeping for 30 seconds.') + if j["error"] and j["message"] == "Too many requests": + self.write_warning("Made too many requests! Sleeping for 30 seconds.") self.sleep(30) except (utils.json.JSONDecodeError, KeyError, TypeError): pass if response.status_code >= 400: self.r = response - if 'Attention Required! | Cloudflare' in response.text: - self.write_warning('Cloudflare blocked request! You must inject valid CloudFlare cookie!') - raise classes.CloudFlareSessionError(f"CloudFlare session error!", response) - if response.text == 'Please verify your account.' or response.text == 'Forbidden': + if "Attention Required! | Cloudflare" in response.text: + self.write_warning("Cloudflare blocked request! You must inject valid CloudFlare cookie!") + raise classes.CloudFlareSessionError("CloudFlare session error!", response) + if response.text == "Please verify your account." or response.text == "Forbidden": self.do_captcha_challenge() - raise classes.CaptchaSessionError(f"CaptchaSession has expired!", response) + raise classes.CaptchaSessionError("CaptchaSession has expired!", response) elif response.status_code >= 500: if self.restricted_ip: self._req.cookies.clear() return True - self.write_warning('eRepublik servers are having internal troubles. Sleeping for 1 minutes') + self.write_warning("eRepublik servers are having internal troubles. Sleeping for 1 minutes") self.sleep(1 * 60) else: raise classes.ErepublikException(f"HTTP {response.status_code} error!") - if re.search(r'Occasionally there are a couple of things which we need to check or to implement in order make ' - r'your experience in eRepublik more pleasant. Don\'t worry about ongoing battles, timer ' - r'will be stopped during maintenance.', response.text): - self.write_warning('eRepublik is having maintenance. Sleeping for 5 mi#nutes') + if re.search( + r"Occasionally there are a couple of things which we need to check or to implement in order make " + r"your experience in eRepublik more pleasant. Don\'t worry about ongoing battles, timer " + r"will be stopped during maintenance.", + response.text, + ): + self.write_warning("eRepublik is having maintenance. Sleeping for 5 mi#nutes") self.sleep(5 * 60) return True - elif re.search('We are experiencing some tehnical dificulties', response.text): - self.write_warning('eRepublik is having technical difficulties. Sleeping for 5 minutes') + elif re.search("We are experiencing some tehnical dificulties", response.text): + self.write_warning("eRepublik is having technical difficulties. Sleeping for 5 minutes") self.sleep(5 * 60) return True - return bool(re.search(r'body id="error"|Internal Server Error|' - r'CSRF attack detected|meta http-equiv="refresh"|' - r'not_authenticated', response.text)) + return bool( + re.search( + r'body id="error"|Internal Server Error|' r'CSRF attack detected|meta http-equiv="refresh"|' r"not_authenticated", + response.text, + ) + ) def _report_action(self, action: str, msg: str, **kwargs: Optional[Dict[str, Any]]): - """ Report action to all available reporting channels + """Report action to all available reporting channels :type action: str :type msg: str @@ -857,7 +906,7 @@ class BaseCitizen(access_points.CitizenAPI): """ kwargs = utils.json_loads(utils.json_dumps(kwargs or {})) action = action[:32] - if msg.startswith('Unable to'): + if msg.startswith("Unable to"): self.write_warning(msg) else: self.write_log(msg) @@ -881,8 +930,8 @@ class CitizenAnniversary(BaseCitizen): return self._post_map_rewards_claim(node_id, extra) def speedup_map_quest_node(self, node_id: int): - node = self.get_anniversary_quest_data().get('cities', {}).get(str(node_id), {}) - return self._post_map_rewards_speedup(node_id, node.get('skipCost', 0)) + node = self.get_anniversary_quest_data().get("cities", {}).get(str(node_id), {}) + return self._post_map_rewards_speedup(node_id, node.get("skipCost", 0)) def spin_wheel_of_fortune(self, max_cost=0, spin_count=0): if not self.config.spin_wheel_of_fortune: @@ -890,32 +939,32 @@ class CitizenAnniversary(BaseCitizen): return def _write_spin_data(cost: int, prize: str): - self._report_action('WHEEL_SPIN', f"Cost: {cost:4d} | Currency left: {self.details.cc:,} | Prize: {prize}") + self._report_action("WHEEL_SPIN", f"Cost: {cost:4d} | Currency left: {self.details.cc:,} | Prize: {prize}") if not self.wheel_of_fortune: self.update_citizen_info() base = self._post_main_wheel_of_fortune_build().json() - current_cost = 0 if base.get('progress').get('free_spin') else base.get('cost') - current_count = base.get('progress').get('spins') - prizes = base.get('prizes') + current_cost = 0 if base.get("progress").get("free_spin") else base.get("cost") + current_count = base.get("progress").get("spins") + prizes = base.get("prizes") if not max_cost and not spin_count: r = self._post_main_wheel_of_fortune_spin(current_cost).json() - _write_spin_data(current_cost, prizes.get('prizes').get(str(r.get('result'))).get('tooltip')) + _write_spin_data(current_cost, prizes.get("prizes").get(str(r.get("result"))).get("tooltip")) else: while max_cost >= current_cost if max_cost else spin_count >= current_count if spin_count else False: r = self._spin_wheel_of_loosing(current_cost) current_count += 1 - prize_name = prizes.get('prizes').get(str(r.get('result'))).get('tooltip') - if r.get('result') == 7: + prize_name = prizes.get("prizes").get(str(r.get("result"))).get("tooltip") + if r.get("result") == 7: prize_name += f" - {prizes.get('jackpot').get(str(r.get('jackpot'))).get('tooltip')}" _write_spin_data(current_cost, prize_name) - current_cost = r.get('cost') - if r.get('jackpot', 0) == 3: + current_cost = r.get("cost") + if r.get("jackpot", 0) == 3: return def _spin_wheel_of_loosing(self, current_cost: int) -> Dict[str, Any]: r = self._post_main_wheel_of_fortune_spin(current_cost).json() - self.details.cc = float(Decimal(r.get('account'))) + self.details.cc = float(Decimal(r.get("account"))) return r @@ -926,11 +975,11 @@ class CitizenTravel(BaseCitizen): def _travel(self, country: constants.Country, region_id: int = 0) -> bool: r_json = super()._travel(country, region_id).json() - if not bool(r_json.get('error')): + if not bool(r_json.get("error")): self._update_citizen_location(country, region_id) return True else: - if 'Travelling too fast.' in r_json.get('message'): + if "Travelling too fast." in r_json.get("message"): self.sleep(1) return self._travel(country, region_id) return False @@ -940,8 +989,8 @@ class CitizenTravel(BaseCitizen): regs = [] if regions: for region in regions.values(): - if region['countryId'] == country.id: # Is not occupied by other country - regs.append((region['id'], region['distanceInKm'])) + if region["countryId"] == country.id: # Is not occupied by other country + regs.append((region["id"], region["distanceInKm"])) if regs: return min(regs, key=lambda _: int(_[1]))[0] else: @@ -952,67 +1001,67 @@ class CitizenTravel(BaseCitizen): res_r = self.details.residence_region if self.details.residence_country and res_r and not res_r == self.details.current_region: if self._travel(self.details.residence_country, self.details.residence_region): - self._report_action('TRAVEL', 'Traveled to residence') + self._report_action("TRAVEL", "Traveled to residence") return True else: - self._report_action('TRAVEL', 'Unable to travel to residence!') + self._report_action("TRAVEL", "Unable to travel to residence!") return False return True def travel_to_region(self, region_id: int) -> bool: data = self._post_main_travel_data(region_id=region_id).json() - if data.get('alreadyInRegion'): + if data.get("alreadyInRegion"): return True else: country = None - for country_data in data.get('countries').values(): - if region_id in country_data.get('regions'): - country = constants.COUNTRIES[country_data.get('id')] + for country_data in data.get("countries").values(): + if region_id in country_data.get("regions"): + country = constants.COUNTRIES[country_data.get("id")] break if country is None: - raise classes.ErepublikException('Region not found!') + raise classes.ErepublikException("Region not found!") if self._travel(country, region_id): - self._report_action('TRAVEL', 'Traveled to region') + self._report_action("TRAVEL", "Traveled to region") return True else: - self._report_action('TRAVEL', 'Unable to travel to region!') + self._report_action("TRAVEL", "Unable to travel to region!") return False def travel_to_country(self, country: constants.Country) -> bool: - data = self._post_main_travel_data(countryId=country.id, check='getCountryRegions').json() + data = self._post_main_travel_data(countryId=country.id, check="getCountryRegions").json() regs = [] - if data.get('regions'): - for region in data.get('regions').values(): - if region['countryId'] == country: # Is not occupied by other country - regs.append((region['id'], region['distanceInKm'])) + if data.get("regions"): + for region in data.get("regions").values(): + if region["countryId"] == country: # Is not occupied by other country + regs.append((region["id"], region["distanceInKm"])) if regs: region_id = min(regs, key=lambda _: int(_[1]))[0] if self._travel(country, region_id): - self._report_action('TRAVEL', f"Traveled to {country.name}") + self._report_action("TRAVEL", f"Traveled to {country.name}") return True else: - self._report_action('TRAVEL', f"Unable to travel to {country.name}!") + self._report_action("TRAVEL", f"Unable to travel to {country.name}!") return False def travel_to_holding(self, holding: classes.Holding) -> bool: data = self._post_main_travel_data(holdingId=holding.id).json() - if data.get('alreadyInRegion'): + if data.get("alreadyInRegion"): return True else: - country = constants.COUNTRIES[data.get('preselectCountryId')] - region_id = data.get('preselectRegionId') + country = constants.COUNTRIES[data.get("preselectCountryId")] + region_id = data.get("preselectRegionId") if self._travel(country, region_id): - self._report_action('TRAVEL', f"Traveled to {holding}") + self._report_action("TRAVEL", f"Traveled to {holding}") return True else: - self._report_action('TRAVEL', f"Unable to travel to {holding}!") + self._report_action("TRAVEL", f"Unable to travel to {holding}!") def travel_to_battle(self, battle: classes.Battle, allowed_countries: List[constants.Country]) -> bool: data = self.get_travel_regions(battle=battle) @@ -1021,32 +1070,36 @@ class CitizenTravel(BaseCitizen): countries: Dict[int, constants.Country] = {c.id: c for c in allowed_countries} if data: for region in data.values(): - if region['countryId'] in countries: # Is not occupied by other country - regs.append((region['distanceInKm'], region['id'], countries[region['countryId']])) + if region["countryId"] in countries: # Is not occupied by other country + regs.append((region["distanceInKm"], region["id"], countries[region["countryId"]])) if regs: reg = min(regs, key=lambda _: int(_[0])) region_id = reg[1] country = reg[2] if self._travel(country, region_id): - self._report_action('TRAVEL', f"Traveled to {battle}") + self._report_action("TRAVEL", f"Traveled to {battle}") return True else: - self._report_action('TRAVEL', f"Unable to travel to {battle}!") + self._report_action("TRAVEL", f"Unable to travel to {battle}!") return False def get_travel_regions( self, holding: classes.Holding = None, battle: classes.Battle = None, country: constants.Country = None ) -> Union[List[Any], Dict[str, Dict[str, Any]]]: - return self._post_main_travel_data( - holdingId=holding.id if holding else 0, - battleId=battle.id if battle else 0, - countryId=country.id if country else 0 - ).json().get('regions', []) + return ( + self._post_main_travel_data( + holdingId=holding.id if holding else 0, battleId=battle.id if battle else 0, countryId=country.id if country else 0 + ) + .json() + .get("regions", []) + ) def get_travel_countries(self) -> Set[constants.Country]: - warnings.simplefilter('always') - warnings.warn('CitizenTravel.get_travel_countries() are being deprecated, ' - 'please use BaseCitizen.get_countries_with_regions()', DeprecationWarning) + warnings.simplefilter("always") + warnings.warn( + "CitizenTravel.get_travel_countries() are being deprecated, " "please use BaseCitizen.get_countries_with_regions()", + DeprecationWarning, + ) return self.get_countries_with_regions() @@ -1059,8 +1112,8 @@ class CitizenCompanies(BaseCitizen): if work_units_needed: if work_units_needed <= self.my_companies.work_units: - response = self._post_economy_work('production', employ=employee_companies).json() - self.reporter.report_action('WORK_EMPLOYEES', response, response.get('status', False)) + response = self._post_economy_work("production", employ=employee_companies).json() + self.reporter.report_action("WORK_EMPLOYEES", response, response.get("status", False)) self.update_companies() ret = bool(self.my_companies.get_employable_factories()) @@ -1073,14 +1126,14 @@ class CitizenCompanies(BaseCitizen): if self.restricted_ip: return None self.update_companies() - data = {'action_type': 'production'} + data = {"action_type": "production"} extra = {} raw_factories = wam_holding.get_wam_companies(raw_factory=True) fin_factories = wam_holding.get_wam_companies(raw_factory=False) free_inventory = self.inventory.total - self.inventory.used wam_list = raw_factories + fin_factories - wam_list = wam_list[:self.energy.food_fights] + wam_list = wam_list[: self.energy.food_fights] if int(free_inventory * 0.75) < self.my_companies.get_needed_inventory_usage(wam_list): self.update_inventory() @@ -1099,14 +1152,13 @@ class CitizenCompanies(BaseCitizen): if sum(employ_factories.values()) > self.my_companies.work_units: employ_factories = {} - response = self._post_economy_work('production', wam=[c.id for c in wam_list], - employ=employ_factories).json() + response = self._post_economy_work("production", wam=[c.id for c in wam_list], employ=employ_factories).json() return response def update_companies(self): html = self._get_economy_my_companies().text page_details = utils.json.loads(re.search(r"var pageDetails\s+= ({.*});", html).group(1)) - self.my_companies.work_units = int(page_details.get('total_works', 0)) + self.my_companies.work_units = int(page_details.get("total_works", 0)) have_holdings = re.search(r"var holdingCompanies\s+= ({.*}});", html) have_companies = re.search(r"var companies\s+= ({.*}});", html) @@ -1132,8 +1184,8 @@ class CitizenCompanies(BaseCitizen): """ company_name = constants.INDUSTRIES[industry_id] if building_type == 2: - company_name = 'Storage' - self.logger.info(f'{company_name} created!') + company_name = "Storage" + self.logger.info(f"{company_name} created!") return self._post_economy_create_company(industry_id, building_type) @@ -1146,14 +1198,14 @@ class CitizenEconomy(CitizenTravel): currency = 62 resp = self._post_economy_exchange_retrieve(False, page, currency) resp_data = resp.json() - self.details.cc = float(resp_data.get('ecash').get('value')) - self.details.gold = float(resp_data.get('gold').get('value')) + self.details.cc = float(resp_data.get("ecash").get("value")) + self.details.gold = float(resp_data.get("gold").get("value")) def check_house_durability(self) -> Dict[int, datetime]: ret = {} inv = self.inventory - for house_quality, active_house in inv.active.get('House', {}).items(): - till = utils.good_timedelta(self.now, timedelta(seconds=active_house['time_left'])) + for house_quality, active_house in inv.active.get("House", {}).items(): + till = utils.good_timedelta(self.now, timedelta(seconds=active_house["time_left"])) ret.update({house_quality: till}) return ret @@ -1161,30 +1213,32 @@ class CitizenEconomy(CitizenTravel): original_region = self.details.current_country, self.details.current_region ok_to_activate = False inv = self.inventory - if not inv.final.get('House', {}).get(q, {}): - countries = [self.details.citizenship, ] + if not inv.final.get("House", {}).get(q, {}): + countries = [ + self.details.citizenship, + ] if self.details.current_country != self.details.citizenship: countries.append(self.details.current_country) - offers = [self.get_market_offers('House', q, country)[f"q{q}"] for country in countries] + offers = [self.get_market_offers("House", q, country)[f"q{q}"] for country in countries] local_cheapest = sorted(offers, key=lambda o: o.price)[0] - global_cheapest = self.get_market_offers('House', q)[f"q{q}"] + global_cheapest = self.get_market_offers("House", q)[f"q{q}"] if global_cheapest.price + 2000 < local_cheapest.price: if global_cheapest.price + 2000 < self.details.cc: if self.travel_to_country(global_cheapest.country): buy = self.buy_market_offer(global_cheapest, 1) else: - buy = dict(error=True, message='Unable to travel!') + buy = dict(error=True, message="Unable to travel!") else: - buy = dict(error=True, message='Not enough money to buy house!') + buy = dict(error=True, message="Not enough money to buy house!") else: if local_cheapest.price < self.details.cc: buy = self.buy_market_offer(local_cheapest, 1) else: - buy = dict(error=True, message='Not enough money to buy house!') + buy = dict(error=True, message="Not enough money to buy house!") if buy is None: pass - elif buy['error']: + elif buy["error"]: self.write_warning(f'Unable to buy q{q} house! {buy["message"]}') else: ok_to_activate = True @@ -1204,7 +1258,7 @@ class CitizenEconomy(CitizenTravel): """ house_durability = self.check_house_durability() for q, active_till in house_durability.items(): - if utils.good_timedelta(active_till, - timedelta(hours=48)) <= self.now or forced: + if utils.good_timedelta(active_till, -timedelta(hours=48)) <= self.now or forced: durability = self.buy_and_activate_house(q) if durability: house_durability = durability @@ -1213,26 +1267,25 @@ class CitizenEconomy(CitizenTravel): def activate_house(self, quality: int) -> bool: r: Dict[str, Any] = self._post_economy_activate_house(quality).json() self._update_inventory_data(r) - if r.get('status') and not r.get('error'): - house = self.inventory.active.get('House', {}).get(quality) - time_left = timedelta(seconds=house['time_left']) + if r.get("status") and not r.get("error"): + house = self.inventory.active.get("House", {}).get(quality) + time_left = timedelta(seconds=house["time_left"]) active_until = utils.good_timedelta(self.now, time_left) self._report_action( - 'ACTIVATE_HOUSE', - f"Activated {house['name']}. Expires at {active_until.strftime('%F %T')} (after {time_left})" + "ACTIVATE_HOUSE", f"Activated {house['name']}. Expires at {active_until.strftime('%F %T')} (after {time_left})" ) return True return False def get_game_token_offers(self): - r = self._post_economy_game_tokens_market('retrieve').json() - return {v.get('id'): dict(amount=v.get('amount'), price=v.get('price')) for v in r.get('topOffers')} + r = self._post_economy_game_tokens_market("retrieve").json() + return {v.get("id"): dict(amount=v.get("amount"), price=v.get("price")) for v in r.get("topOffers")} def fetch_organisation_account(self, org_id: int): r = self._get_economy_citizen_accounts(org_id) table = re.search(r'()', r.text, re.I | re.M | re.S) if table: - account = re.findall(r'>\s*(\d+.\d+)\s*', table.group(1)) + account = re.findall(r">\s*(\d+.\d+)\s*", table.group(1)) if account: return dict(gold=account[0], cc=account[1], ok=True) @@ -1262,29 +1315,28 @@ class CitizenEconomy(CitizenTravel): ret = [] for offer in self._get_economy_my_market_offers().json(): line = offer.copy() - line.pop('icon', None) + line.pop("icon", None) ret.append(line) return ret def delete_my_market_offer(self, offer_id: int) -> bool: offers = self.get_my_market_offers() for offer in offers: - if offer['id'] == offer_id: - industry = constants.INDUSTRIES[offer['industryId']] - amount = offer['amount'] - q = offer['quality'] - price = offer['price'] - ret = self._post_economy_marketplace_actions('delete', offer_id=offer_id).json() - if ret.get('error'): - self._report_action('ECONOMY_DELETE_OFFER', f"Unable to delete offer: '{ret.get('message')}'", - kwargs=offer) + if offer["id"] == offer_id: + industry = constants.INDUSTRIES[offer["industryId"]] + amount = offer["amount"] + q = offer["quality"] + price = offer["price"] + ret = self._post_economy_marketplace_actions("delete", offer_id=offer_id).json() + if ret.get("error"): + self._report_action("ECONOMY_DELETE_OFFER", f"Unable to delete offer: '{ret.get('message')}'", kwargs=offer) else: - self._report_action('ECONOMY_DELETE_OFFER', - f"Removed offer for {amount} x {industry} q{q} for {price}cc/each", - kwargs=offer) - return not ret.get('error') + self._report_action( + "ECONOMY_DELETE_OFFER", f"Removed offer for {amount} x {industry} q{q} for {price}cc/each", kwargs=offer + ) + return not ret.get("error") else: - self._report_action('ECONOMY_DELETE_OFFER', f"Unable to find offer id{offer_id}", kwargs={'offers': offers}) + self._report_action("ECONOMY_DELETE_OFFER", f"Unable to find offer id{offer_id}", kwargs={"offers": offers}) return False def post_market_offer(self, industry: int, quality: int, amount: int, price: float) -> bool: @@ -1295,33 +1347,34 @@ class CitizenEconomy(CitizenTravel): _inv_qlt = quality if industry in [1, 2, 3, 4, 23] else 0 final_kind = industry in [1, 2, 4, 23] - items = (self.inventory.final if final_kind else self.inventory.raw).get(constants.INDUSTRIES[industry], - {_inv_qlt: {'amount': 0}}) - if items[_inv_qlt]['amount'] < amount: + items = (self.inventory.final if final_kind else self.inventory.raw).get(constants.INDUSTRIES[industry], {_inv_qlt: {"amount": 0}}) + if items[_inv_qlt]["amount"] < amount: self.update_inventory() - items = (self.inventory.final if final_kind else self.inventory.raw).get(constants.INDUSTRIES[industry], - {_inv_qlt: {'amount': 0}}) - if items[_inv_qlt]['amount'] < amount: - self._report_action('ECONOMY_SELL_PRODUCTS', 'Unable to sell! Not enough items in storage!', - kwargs=dict(inventory=items[_inv_qlt], amount=amount)) + items = (self.inventory.final if final_kind else self.inventory.raw).get( + constants.INDUSTRIES[industry], {_inv_qlt: {"amount": 0}} + ) + if items[_inv_qlt]["amount"] < amount: + self._report_action( + "ECONOMY_SELL_PRODUCTS", + "Unable to sell! Not enough items in storage!", + kwargs=dict(inventory=items[_inv_qlt], amount=amount), + ) return False - data = dict(country_id=self.details.citizenship.id, industry=industry, quality=quality, amount=amount, - price=price, buy=False) - ret = self._post_economy_marketplace_actions('sell', **data).json() - message = (f"Posted market offer for {amount}q{quality} " - f"{constants.INDUSTRIES[industry]} for price {price}cc") - self._report_action('ECONOMY_SELL_PRODUCTS', message, kwargs=ret) - return not bool(ret.get('error', True)) + data = dict(country_id=self.details.citizenship.id, industry=industry, quality=quality, amount=amount, price=price, buy=False) + ret = self._post_economy_marketplace_actions("sell", **data).json() + message = f"Posted market offer for {amount}q{quality} " f"{constants.INDUSTRIES[industry]} for price {price}cc" + self._report_action("ECONOMY_SELL_PRODUCTS", message, kwargs=ret) + return not bool(ret.get("error", True)) def buy_from_market(self, offer: int, amount: int) -> Dict[str, Any]: - ret = self._post_economy_marketplace_actions('buy', offer=offer, amount=amount) + ret = self._post_economy_marketplace_actions("buy", offer=offer, amount=amount) json_ret = ret.json() - if not json_ret.get('error', True): - self.details.cc = ret.json()['currency'] - self.details.gold = ret.json()['gold'] - json_ret.pop('offerUpdate', None) - self._report_action('BOUGHT_PRODUCTS', json_ret.get('message'), kwargs=json_ret) + if not json_ret.get("error", True): + self.details.cc = ret.json()["currency"] + self.details.gold = ret.json()["gold"] + json_ret.pop("offerUpdate", None) + self._report_action("BOUGHT_PRODUCTS", json_ret.get("message"), kwargs=json_ret) return json_ret def buy_market_offer(self, offer: classes.OfferItem, amount: int = None) -> Optional[Dict[str, Any]]: @@ -1336,12 +1389,10 @@ class CitizenEconomy(CitizenTravel): self.travel_to_residence() return json_ret - def get_market_offers( - self, product_name: str, quality: int = None, country: constants.Country = None - ) -> Dict[str, classes.OfferItem]: - raw_short_names = dict(frm='foodRaw', wrm='weaponRaw', hrm='houseRaw', arm='airplaneRaw') + def get_market_offers(self, product_name: str, quality: int = None, country: constants.Country = None) -> Dict[str, classes.OfferItem]: + raw_short_names = dict(frm="foodRaw", wrm="weaponRaw", hrm="houseRaw", arm="airplaneRaw") q1_industries = list(raw_short_names.values()) - q5_industries = ['house', 'aircraft', 'ticket'] + q5_industries = ["house", "aircraft", "ticket"] if product_name in raw_short_names: quality = 1 product_name = raw_short_names[product_name] @@ -1369,23 +1420,25 @@ class CitizenEconomy(CitizenTravel): for country, q in product(*iterable): r = self._post_economy_marketplace(country.id, constants.INDUSTRIES[product_name], q).json() obj = offers[f"q{q}"] - if not r.get('error', False): - for offer in r['offers']: - if (obj.price > float(offer['priceWithTaxes']) or ( - obj.price == float(offer['priceWithTaxes']) and obj.amount < int(offer['amount']) - )): + if not r.get("error", False): + for offer in r["offers"]: + if obj.price > float(offer["priceWithTaxes"]) or ( + obj.price == float(offer["priceWithTaxes"]) and obj.amount < int(offer["amount"]) + ): offers[f"q{q}"] = obj = classes.OfferItem( - float(offer['priceWithTaxes']), - constants.COUNTRIES[int(offer['country_id'])], int(offer['amount']), - int(offer['id']), int(offer['citizen_id']) + float(offer["priceWithTaxes"]), + constants.COUNTRIES[int(offer["country_id"])], + int(offer["amount"]), + int(offer["id"]), + int(offer["citizen_id"]), ) self.logger.debug(f"Scraped market in {self.now - start_dt}!") return offers def buy_food(self, energy_amount: int = 0): - hp_needed = energy_amount if energy_amount else 48 * self.energy.interval * 10 - self.food['total'] - local_offers = self.get_market_offers('food', country=self.details.current_country) + hp_needed = energy_amount if energy_amount else 48 * self.energy.interval * 10 - self.food["total"] + local_offers = self.get_market_offers("food", country=self.details.current_country) cheapest_q, cheapest = sorted(local_offers.items(), key=lambda v: v[1].price / constants.FOOD_ENERGY[v[0]])[0] @@ -1395,33 +1448,41 @@ class CitizenEconomy(CitizenTravel): amount = hp_needed // constants.FOOD_ENERGY[cheapest_q] if amount * cheapest.price < self.details.cc: - data = dict(offer=cheapest.offer_id, amount=amount, price=cheapest.price, - cost=amount * cheapest.price, quality=cheapest_q, - energy=amount * constants.FOOD_ENERGY[cheapest_q]) - self._report_action('BUY_FOOD', "", kwargs=data) + data = dict( + offer=cheapest.offer_id, + amount=amount, + price=cheapest.price, + cost=amount * cheapest.price, + quality=cheapest_q, + energy=amount * constants.FOOD_ENERGY[cheapest_q], + ) + self._report_action("BUY_FOOD", "", kwargs=data) self.buy_from_market(cheapest.offer_id, amount) self.update_inventory() else: s = f"Don't have enough money! Needed: {amount * cheapest.price}cc, Have: {self.details.cc}cc" self.write_warning(s) - self._report_action('BUY_FOOD', s) + self._report_action("BUY_FOOD", s) def get_monetary_offers(self, currency: int = 62) -> List[Dict[str, Union[int, float]]]: if currency not in [1, 62]: currency = 62 resp = self._post_economy_exchange_retrieve(False, 0, currency).json() ret = [] - offers = re.findall(r"id='purchase_(\d+)' data-i18n='Buy for' data-currency='GOLD' " - r"data-price='(\d+\.\d+)' data-max='(\d+\.\d+)' trigger='purchase'", - resp['buy_mode'], re.M | re.I | re.S) + offers = re.findall( + r"id='purchase_(\d+)' data-i18n='Buy for' data-currency='GOLD' " + r"data-price='(\d+\.\d+)' data-max='(\d+\.\d+)' trigger='purchase'", + resp["buy_mode"], + re.M | re.I | re.S, + ) for offer_id, price, amount in offers: ret.append(dict(offer_id=int(offer_id), price=float(price), amount=float(amount))) - return sorted(ret, key=lambda o: (o['price'], -o['amount'])) + return sorted(ret, key=lambda o: (o["price"], -o["amount"])) def buy_monetary_market_offer(self, offer: int, amount: float, currency: int) -> int: - """ Buy from monetary market + """Buy from monetary market :param offer: offer id which should be bought :type offer: int @@ -1432,27 +1493,26 @@ class CitizenEconomy(CitizenTravel): :return: """ response = self._post_economy_exchange_purchase(amount, currency, offer) - self.details.cc = float(response.json().get('ecash').get('value')) - self.details.gold = float(response.json().get('gold').get('value')) - if response.json().get('error'): - self._report_action('BUY_GOLD', 'Unable to buy gold!', kwargs=response.json()) + self.details.cc = float(response.json().get("ecash").get("value")) + self.details.gold = float(response.json().get("gold").get("value")) + if response.json().get("error"): + self._report_action("BUY_GOLD", "Unable to buy gold!", kwargs=response.json()) return False else: - self._report_action('BUY_GOLD', f'New amount {self.details.cc}cc, {self.details.gold}g', - kwargs=response.json()) + self._report_action("BUY_GOLD", f"New amount {self.details.cc}cc, {self.details.gold}g", kwargs=response.json()) return True def donate_money(self, citizen_id: int = 1620414, amount: float = 0.0, currency: int = 62) -> bool: - """ currency: gold = 62, cc = 1 """ + """currency: gold = 62, cc = 1""" resp = self._post_economy_donate_money_action(citizen_id, amount, currency) - r = re.search('You do not have enough money in your account to make this donation', resp.text) + r = re.search("You do not have enough money in your account to make this donation", resp.text) success = not bool(r) self.update_money() - cur = 'g' if currency == 62 else 'cc' + cur = "g" if currency == 62 else "cc" if success: self.report_money_donation(citizen_id, amount, currency == 1) else: - self._report_action('DONATE_MONEY', f"Unable to donate {amount}{cur}!") + self._report_action("DONATE_MONEY", f"Unable to donate {amount}{cur}!") return success def donate_items(self, citizen_id: int = 1620414, amount: int = 0, industry_id: int = 1, quality: int = 1) -> int: @@ -1462,27 +1522,23 @@ class CitizenEconomy(CitizenTravel): self.write_log(f"Donate: {amount:4d}q{quality} {industry} to {citizen_id}") response = self._post_economy_donate_items_action(citizen_id, amount, industry_id, quality) if re.search(rf"Successfully transferred {amount} item\(s\) to", response.text): - msg = (f"Successfully donated {amount}q{quality} {industry} " - f"to citizen with id {citizen_id}!") - self._report_action('DONATE_ITEMS', msg) + msg = f"Successfully donated {amount}q{quality} {industry} " f"to citizen with id {citizen_id}!" + self._report_action("DONATE_ITEMS", msg) return amount - elif re.search('You must wait 5 seconds before donating again', response.text): - self.write_warning('Previous donation failed! Must wait at least 5 seconds before next donation!') + elif re.search("You must wait 5 seconds before donating again", response.text): + self.write_warning("Previous donation failed! Must wait at least 5 seconds before next donation!") self.sleep(5) return self.donate_items(citizen_id, int(amount), industry_id, quality) else: - if re.search(r'You do not have enough items in your inventory to make this donation', response.text): - self._report_action('DONATE_ITEMS', - f"Unable to donate {amount}q{quality} " - f"{industry}, not enough left!") + if re.search(r"You do not have enough items in your inventory to make this donation", response.text): + self._report_action("DONATE_ITEMS", f"Unable to donate {amount}q{quality} " f"{industry}, not enough left!") return 0 available = re.search( - r'Cannot transfer the items because the user has only (\d+) free slots in (his|her) storage.', - response.text + r"Cannot transfer the items because the user has only (\d+) free slots in (his|her) storage.", response.text ).group(1) - self._report_action('DONATE_ITEMS', - f'Unable to donate {amount}q{quality}{industry}' - f', receiver has only {available} storage left!') + self._report_action( + "DONATE_ITEMS", f"Unable to donate {amount}q{quality}{industry}" f", receiver has only {available} storage left!" + ) self.sleep(5) return self.donate_items(citizen_id, int(available), industry_id, quality) @@ -1491,31 +1547,30 @@ class CitizenEconomy(CitizenTravel): amount = int(amount) if self.details.cc < amount or amount < 20: return False - data = dict(country=country.id, action='currency', value=amount) - r = self._post_main_country_donate(country.id, 'currency', amount) - if r.json().get('status') or not r.json().get('error'): - self._report_action('CONTRIBUTE_CC', f'Contributed {amount}cc to {country}\'s treasury', kwargs=data) + data = dict(country=country.id, action="currency", value=amount) + r = self._post_main_country_donate(country.id, "currency", amount) + if r.json().get("status") or not r.json().get("error"): + self._report_action("CONTRIBUTE_CC", f"Contributed {amount}cc to {country}'s treasury", kwargs=data) return True else: - self._report_action('CONTRIBUTE_CC', f"Unable to contribute {amount}cc to {country}'s" - f' treasury', kwargs=r.json()) + self._report_action("CONTRIBUTE_CC", f"Unable to contribute {amount}cc to {country}'s" f" treasury", kwargs=r.json()) return False def contribute_food_to_country(self, amount, quality, country: constants.Country) -> bool: self.update_inventory() amount = amount // 1 - if self.food['q' + str(quality)] < amount or amount < 10: + if self.food["q" + str(quality)] < amount or amount < 10: return False - data = dict(country=country.id, action='food', value=amount, quality=quality) - r = self._post_main_country_donate(country.id, 'currency', amount, quality) + data = dict(country=country.id, action="food", value=amount, quality=quality) + r = self._post_main_country_donate(country.id, "currency", amount, quality) - if r.json().get('status') or not r.json().get('error'): - self._report_action('CONTRIBUTE_FOOD', f"Contributed {amount}q{quality} food to " - f"{country}'s treasury", kwargs=data) + if r.json().get("status") or not r.json().get("error"): + self._report_action("CONTRIBUTE_FOOD", f"Contributed {amount}q{quality} food to " f"{country}'s treasury", kwargs=data) return True else: - self._report_action('CONTRIBUTE_FOOD', f"Unable to contribute {amount}q{quality} food to " - f"{country}'s treasury", kwargs=r.json()) + self._report_action( + "CONTRIBUTE_FOOD", f"Unable to contribute {amount}q{quality} food to " f"{country}'s treasury", kwargs=r.json() + ) return False def contribute_gold_to_country(self, amount: int, country: constants.Country) -> bool: @@ -1523,15 +1578,14 @@ class CitizenEconomy(CitizenTravel): if self.details.cc < amount: return False - data = dict(country=country.id, action='gold', value=amount) - r = self._post_main_country_donate(country.id, 'gold', amount) + data = dict(country=country.id, action="gold", value=amount) + r = self._post_main_country_donate(country.id, "gold", amount) - if r.json().get('status') or not r.json().get('error'): - self._report_action('CONTRIBUTE_GOLD', f"Contributed {amount}g to {country}'s treasury", kwargs=data) + if r.json().get("status") or not r.json().get("error"): + self._report_action("CONTRIBUTE_GOLD", f"Contributed {amount}g to {country}'s treasury", kwargs=data) return True else: - self._report_action('CONTRIBUTE_GOLD', f"Unable to contribute {amount}g to {country}'s treasury", - kwargs=r.json()) + self._report_action("CONTRIBUTE_GOLD", f"Unable to contribute {amount}g to {country}'s treasury", kwargs=r.json()) return False def report_money_donation(self, citizen_id: int, amount: float, is_currency: bool = True): @@ -1563,12 +1617,11 @@ class CitizenMedia(BaseCitizen): def endorse_article(self, article_id: int, amount: int) -> bool: if amount in (5, 50, 100): resp = self._post_main_donate_article(article_id, amount).json() - if not bool(resp.get('error')): - self._report_action('ARTICLE_ENDORSE', f"Endorsed article ({article_id}) with {amount}cc") + if not bool(resp.get("error")): + self._report_action("ARTICLE_ENDORSE", f"Endorsed article ({article_id}) with {amount}cc") return True else: - self._report_action('ARTICLE_ENDORSE', f"Unable to endorse article ({article_id}) with {amount}cc", - kwargs=resp) + self._report_action("ARTICLE_ENDORSE", f"Unable to endorse article ({article_id}) with {amount}cc", kwargs=resp) return False else: return False @@ -1576,31 +1629,39 @@ class CitizenMedia(BaseCitizen): def vote_article(self, article_id: int) -> bool: resp = self._post_main_vote_article(article_id).json() - if not bool(resp.get('error')): - self._report_action('ARTICLE_VOTE', f"Voted article {article_id}") + if not bool(resp.get("error")): + self._report_action("ARTICLE_VOTE", f"Voted article {article_id}") return True else: - self._report_action('ARTICLE_VOTE', f"Unable to vote for article {article_id}", kwargs=resp) + self._report_action("ARTICLE_VOTE", f"Unable to vote for article {article_id}", kwargs=resp) return False def get_article_comments(self, article_id: int, page_id: int = 1) -> Dict[str, Any]: return self._post_main_article_comments(article_id, page_id).json() def write_article_comment(self, message: str, article_id: int, parent_id: int = None) -> Response: - self._report_action('ARTICLE_COMMENT', f"Wrote a comment to article ({article_id})", - kwargs=dict(msg=message, article_id=article_id, parent_id=parent_id)) + self._report_action( + "ARTICLE_COMMENT", + f"Wrote a comment to article ({article_id})", + kwargs=dict(msg=message, article_id=article_id, parent_id=parent_id), + ) return self._post_main_article_comments_create(message, article_id, parent_id) def publish_article(self, title: str, content: str, kind: int) -> int: - kinds = {1: 'First steps in eRepublik', 2: 'Battle orders', 3: 'Warfare analysis', - 4: 'Political debates and analysis', 5: 'Financial business', - 6: 'Social interactions and entertainment'} + kinds = { + 1: "First steps in eRepublik", + 2: "Battle orders", + 3: "Warfare analysis", + 4: "Political debates and analysis", + 5: "Financial business", + 6: "Social interactions and entertainment", + } if kind in kinds: - data = {'title': title, 'content': content, 'country': self.details.citizenship.id, 'kind': kind} + data = {"title": title, "content": content, "country": self.details.citizenship.id, "kind": kind} resp = self._post_main_write_article(title, content, self.details.citizenship.id, kind) try: article_id = int(resp.history[1].url.split("/")[-3]) - self._report_action('ARTICLE_PUBLISH', f"Published new article \"{title}\" ({article_id})", kwargs=data) + self._report_action("ARTICLE_PUBLISH", f'Published new article "{title}" ({article_id})', kwargs=data) except: # noqa article_id = 0 return article_id @@ -1613,10 +1674,10 @@ class CitizenMedia(BaseCitizen): def delete_article(self, article_id: int) -> NoReturn: article_data = self.get_article(article_id) - if article_data and article_data['articleData']['canDelete']: - self._report_action('ARTICLE_DELETE', - f"Attempting to delete article '{article_data['article']['title']}' (#{article_id})", - kwargs=article_data) + if article_data and article_data["articleData"]["canDelete"]: + self._report_action( + "ARTICLE_DELETE", f"Attempting to delete article '{article_data['article']['title']}' (#{article_id})", kwargs=article_data + ) self._get_main_delete_article(article_id) else: self.write_warning(f"Unable to delete article (#{article_id})!") @@ -1635,19 +1696,18 @@ class CitizenMilitary(CitizenTravel): return d def update_war_info(self): - if self.__last_war_update_data and self.__last_war_update_data.get('last_updated', - 0) + 30 > self.now.timestamp(): + if self.__last_war_update_data and self.__last_war_update_data.get("last_updated", 0) + 30 > self.now.timestamp(): r_json = self.__last_war_update_data else: r_json = self._get_military_campaigns_json_list().json() - if r_json.get('countries'): + if r_json.get("countries"): if self.all_battles is None: self.all_battles = {} self.__last_war_update_data = r_json - if r_json.get('battles'): + if r_json.get("battles"): all_battles = {} - for battle_data in r_json.get('battles', {}).values(): - all_battles[battle_data.get('id')] = classes.Battle(battle_data) + for battle_data in r_json.get("battles", {}).values(): + all_battles[battle_data.get("id")] = classes.Battle(battle_data) # old_all_battles = self.all_battles self.all_battles = all_battles # for battle in old_all_battles.values(): @@ -1656,7 +1716,7 @@ class CitizenMilitary(CitizenTravel): def get_battle_for_war(self, war_id: int) -> Optional[classes.Battle]: self.update_war_info() war_info = self.get_war_status(war_id) - return self.all_battles.get(war_info.get('battle_id'), None) + return self.all_battles.get(war_info.get("battle_id"), None) def get_war_status(self, war_id: int) -> Dict[str, Union[bool, Dict[int, str]]]: r = self._get_wars_show(war_id) @@ -1666,13 +1726,15 @@ class CitizenMilitary(CitizenTravel): if reg_re.findall(html): ret.update(regions={}, can_attack=True) for reg in reg_re.findall(html): - ret['regions'].update({int(reg[0]): reg[1]}) - elif re.search(r'Join', html): - battle_id = re.search(r'Join', html).group(1) + ret["regions"].update({int(reg[0]): reg[1]}) + elif re.search( + r'Join', html + ): + battle_id = re.search( + r'Join', html + ).group(1) ret.update(can_attack=False, battle_id=int(battle_id)) - elif re.search(r'This war is no longer active.', html): + elif re.search(r"This war is no longer active.", html): ret.update(can_attack=False, ended=True) else: ret.update(can_attack=False) @@ -1690,18 +1752,17 @@ class CitizenMilitary(CitizenTravel): if not division.is_air: for weapon in available_weapons: try: - if weapon['weaponQuantity'] > 30 and weapon['weaponInfluence'] > weapon_damage: - weapon_quality = int(weapon['weaponId']) - weapon_damage = weapon['weaponInfluence'] + if weapon["weaponQuantity"] > 30 and weapon["weaponInfluence"] > weapon_damage: + weapon_quality = int(weapon["weaponId"]) + weapon_damage = weapon["weaponInfluence"] except ValueError: pass return self.change_weapon(battle, weapon_quality, division) def change_weapon(self, battle: classes.Battle, quality: int, battle_zone: classes.BattleDivision) -> int: r = self._post_military_change_weapon(battle.id, battle_zone.id, quality) - influence = r.json().get('weaponInfluence') - self._report_action('MILITARY_WEAPON', f"Switched to q{quality} weapon," - f" new influence {influence}", kwargs=r.json()) + influence = r.json().get("weaponInfluence") + self._report_action("MILITARY_WEAPON", f"Switched to q{quality} weapon," f" new influence {influence}", kwargs=r.json()) return influence def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[classes.Battle]: @@ -1724,12 +1785,12 @@ class CitizenMilitary(CitizenTravel): battle_list = sorted(self.all_battles.values(), key=lambda b: b.id) contribution_json = self._get_military_campaigns_json_citizen().json() - contributions: List[Dict[str, int]] = contribution_json.get('contributions') or [] - contributions.sort(key=lambda b: -b.get('damage')) + contributions: List[Dict[str, int]] = contribution_json.get("contributions") or [] + contributions.sort(key=lambda b: -b.get("damage")) for contribution_battle in contributions: - if contribution_battle.get('battle_id') and contribution_battle.get('battle_id') in self.all_battles: - ret_battles.append(self.all_battles[contribution_battle.get('battle_id')]) + if contribution_battle.get("battle_id") and contribution_battle.get("battle_id") in self.all_battles: + ret_battles.append(self.all_battles[contribution_battle.get("battle_id")]) for battle in battle_list: battle_sides = [battle.invader.country, battle.defender.country] @@ -1796,7 +1857,7 @@ class CitizenMilitary(CitizenTravel): if not medal: air_divs.append((0, division)) else: - air_divs.append((medal.get('1').get('raw_value'), division)) + air_divs.append((medal.get("1").get("raw_value"), division)) elif not division.is_air and self.config.ground: if not division.div == self.division and not check_maverick: continue @@ -1805,19 +1866,17 @@ class CitizenMilitary(CitizenTravel): if not medal: ground_divs.append((0, division)) else: - ground_divs.append((medal.get('1').get('raw_value'), division)) + ground_divs.append((medal.get("1").get("raw_value"), division)) air_divs.sort(key=lambda z: (z[0], z[1].battle.start)) ground_divs.sort(key=lambda z: (z[0], z[1].battle.start)) - return {'air': air_divs, 'ground': ground_divs} + return {"air": air_divs, "ground": ground_divs} @property def has_battle_contribution(self): - return bool(self.__last_war_update_data.get('citizen_contribution', [])) + return bool(self.__last_war_update_data.get("citizen_contribution", [])) - def find_battle_to_fight(self, silent: bool = False) -> Tuple[ - classes.Battle, classes.BattleDivision, classes.BattleSide - ]: + def find_battle_to_fight(self, silent: bool = False) -> Tuple[classes.Battle, classes.BattleDivision, classes.BattleSide]: self.update_war_info() for battle in self.sorted_battles(self.config.sort_battles_time): if not isinstance(battle, classes.Battle): @@ -1840,22 +1899,22 @@ class CitizenMilitary(CitizenTravel): continue if not battle_zone: continue - allies = battle.invader.deployed + battle.defender.deployed + [battle.invader.country, - battle.defender.country] + allies = battle.invader.deployed + battle.defender.deployed + [battle.invader.country, battle.defender.country] travel_needed = self.details.current_country not in allies if battle.is_rw: side = battle.defender if self.config.rw_def_side else battle.invader else: - defender_side = self.details.current_country in battle.defender.allies + [battle.defender.country, ] + defender_side = self.details.current_country in battle.defender.allies + [ + battle.defender.country, + ] side = battle.defender if defender_side else battle.invader if not silent: self.write_log(str(battle)) - travel = (self.config.travel_to_fight and self.should_travel_to_fight() or self.config.force_travel) \ - if travel_needed else True + travel = (self.config.travel_to_fight and self.should_travel_to_fight() or self.config.force_travel) if travel_needed else True if not travel: continue @@ -1864,11 +1923,10 @@ class CitizenMilitary(CitizenTravel): def find_battle_and_fight(self): count = self.should_fight()[0] if count: - self.write_log('Checking for battles to fight in...') + self.write_log("Checking for battles to fight in...") for battle, division, side in self.find_battle_to_fight(): - allies = battle.invader.deployed + battle.defender.deployed + [battle.invader.country, - battle.defender.country] + allies = battle.invader.deployed + battle.defender.deployed + [battle.invader.country, battle.defender.country] travel_needed = self.details.current_country not in allies @@ -1884,8 +1942,14 @@ class CitizenMilitary(CitizenTravel): self.travel_to_residence() break - def fight(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide = None, - count: int = None, use_ebs: bool = False) -> Optional[int]: + def fight( + self, + battle: classes.Battle, + division: classes.BattleDivision, + side: classes.BattleSide = None, + count: int = None, + use_ebs: bool = False, + ) -> Optional[int]: """Fight in a battle. Will auto activate booster and travel if allowed to do it. @@ -1904,14 +1968,13 @@ class CitizenMilitary(CitizenTravel): :rtype: int """ if self.restricted_ip: - self.write_warning('Fighting is not allowed from restricted IP!') - self._report_action('IP_BLACKLISTED', 'Fighting is not allowed from restricted IP!') + self.write_warning("Fighting is not allowed from restricted IP!") + self._report_action("IP_BLACKLISTED", "Fighting is not allowed from restricted IP!") return 1 if not division.is_air and self.config.boosters: self.activate_damage_booster(not division.is_air) if side is None: - side = battle.defender if self.details.citizenship in battle.defender.allies + [ - battle.defender.country] else battle.invader + side = battle.defender if self.details.citizenship in battle.defender.allies + [battle.defender.country] else battle.invader if count is None: count = self.should_fight()[0] @@ -1939,13 +2002,15 @@ class CitizenMilitary(CitizenTravel): self.sleep(count // 3) # TODO: connect to eRepublik's WS and get from there when deploy ends energy_used = 0 if deployment_id: - self.write_warning('If erepublik responds with HTTP 500 Internal Error, it is kind of ok, because deployment has not finished yet.') + self.write_warning( + "If erepublik responds with HTTP 500 Internal Error, it is kind of ok, because deployment has not finished yet." + ) deployment_data = self._post_military_fight_deploy_deploy_report_data(deployment_id).json() - if not deployment_data.get('error'): - data = deployment_data['data'] - total_damage = int(data['damage'].replace(',', '')) - energy_used = int(data['energySpent'].replace(',', '')) - self.details.pp += int(data['rewards']['prestigePoints'].replace(',', '')) + if not deployment_data.get("error"): + data = deployment_data["data"] + total_damage = int(data["damage"].replace(",", "")) + energy_used = int(data["energySpent"].replace(",", "")) + self.details.pp += int(data["rewards"]["prestigePoints"].replace(",", "")) self.report_fighting(battle, not side.is_defender, division, total_damage, energy_used // 10) return energy_used @@ -1955,7 +2020,7 @@ class CitizenMilitary(CitizenTravel): else: response = self._post_military_fight_ground(battle.id, side.id, division.id) - if 'Zone is not meant for ' in response.text: + if "Zone is not meant for " in response.text: self.sleep(5) return 0, 1, 0 try: @@ -1965,54 +2030,54 @@ class CitizenMilitary(CitizenTravel): hits = 0 damage = 0 err = False - if r_json.get('error'): - if r_json.get('message') == 'SHOOT_LOCKOUT': + if r_json.get("error"): + if r_json.get("message") == "SHOOT_LOCKOUT": pass - elif r_json.get('message') == 'NOT_ENOUGH_WEAPONS': + elif r_json.get("message") == "NOT_ENOUGH_WEAPONS": self.set_default_weapon(battle, division) - elif r_json.get('message') == "Cannot activate a zone with a non-native division": - self.write_warning('Wrong division!!') + elif r_json.get("message") == "Cannot activate a zone with a non-native division": + self.write_warning("Wrong division!!") return 0, 10, 0 - elif r_json.get('message') == 'ZONE_INACTIVE': - self.write_warning('Wrong division!!') + elif r_json.get("message") == "ZONE_INACTIVE": + self.write_warning("Wrong division!!") return 0, 10, 0 - elif r_json.get('message') == 'NON_BELLIGERENT': + elif r_json.get("message") == "NON_BELLIGERENT": self.write_warning("Dictatorship/Liberation wars are not supported!") return 0, 10, 0 - elif r_json.get('message') in ['FIGHT_DISABLED', 'DEPLOYMENT_MODE']: - self._post_main_profile_update('options', - params='{"optionName":"enable_web_deploy","optionValue":"off"}') + elif r_json.get("message") in ["FIGHT_DISABLED", "DEPLOYMENT_MODE"]: + self._post_main_profile_update("options", params='{"optionName":"enable_web_deploy","optionValue":"off"}') self.set_default_weapon(battle, division) else: - if r_json.get('message') == 'UNKNOWN_SIDE': + if r_json.get("message") == "UNKNOWN_SIDE": self._rw_choose_side(battle, side) - elif r_json.get('message') == 'CHANGE_LOCATION': + elif r_json.get("message") == "CHANGE_LOCATION": countries = [side.country] + side.deployed self.travel_to_battle(battle, countries) err = True - elif r_json.get('message') == 'ENEMY_KILLED': + elif r_json.get("message") == "ENEMY_KILLED": # Non-InfantryKit players - if r_json['user']['earnedXp']: - hits = r_json['user']['earnedXp'] + if r_json["user"]["earnedXp"]: + hits = r_json["user"]["earnedXp"] # InfantryKit player # The almost always safe way (breaks on levelup hit) - elif self.energy.energy >= r_json['details']['wellness']: # Haven't reached levelup - hits = (self.energy.energy - r_json['details']['wellness']) // 10 + elif self.energy.energy >= r_json["details"]["wellness"]: # Haven't reached levelup + hits = (self.energy.energy - r_json["details"]["wellness"]) // 10 else: - hits = r_json['hits'] - if r_json['user']['epicBattle']: - hits /= 1 + r_json['user']['epicBattle'] + hits = r_json["hits"] + if r_json["user"]["epicBattle"]: + hits /= 1 + r_json["user"]["epicBattle"] - self.energy.energy = r_json['details']['wellness'] - self.details.xp = int(r_json['details']['points']) - damage = r_json['user']['givenDamage'] * (1.1 if r_json['oldEnemy']['isNatural'] else 1) + self.energy.energy = r_json["details"]["wellness"] + self.details.xp = int(r_json["details"]["points"]) + damage = r_json["user"]["givenDamage"] * (1.1 if r_json["oldEnemy"]["isNatural"] else 1) else: err = True return hits, err, damage - def deploy_bomb(self, battle: classes.Battle, division: classes.BattleDivision, bomb_id: int, inv_side: bool, - count: int = 1) -> Optional[int]: + def deploy_bomb( + self, battle: classes.Battle, division: classes.BattleDivision, bomb_id: int, inv_side: bool, count: int = 1 + ) -> Optional[int]: """Deploy bombs in a battle for given side. :param battle: Battle @@ -2040,20 +2105,19 @@ class CitizenMilitary(CitizenTravel): if self.details.current_country not in good_countries: has_traveled = self.travel_to_battle(battle, good_countries) else: - involved = [battle.invader.country, - battle.defender.country] + battle.invader.deployed + battle.defender.deployed + involved = [battle.invader.country, battle.defender.country] + battle.invader.deployed + battle.defender.deployed if self.details.current_country not in involved: count = 0 side = battle.invader if inv_side else battle.defender errors = deployed_count = 0 while (not deployed_count == count) and errors < 10: r = self._post_military_deploy_bomb(battle.id, division.id, side.id, bomb_id).json() - if not r.get('error'): + if not r.get("error"): deployed_count += 1 self.sleep(0.5) - elif r.get('message') == 'LOCKED': + elif r.get("message") == "LOCKED": self.sleep(0.5) - elif r.get('message') == 'INVALID_BOMB': + elif r.get("message") == "INVALID_BOMB": errors = 10 else: errors += 1 @@ -2061,7 +2125,7 @@ class CitizenMilitary(CitizenTravel): if has_traveled: self.travel_to_residence() - self._report_action('MILITARY_BOMB', f"Deployed {deployed_count} bombs in battle {battle.id}") + self._report_action("MILITARY_BOMB", f"Deployed {deployed_count} bombs in battle {battle.id}") return deployed_count def change_division(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide = None) -> bool: @@ -2076,64 +2140,70 @@ class CitizenMilitary(CitizenTravel): :return: """ resp = self._post_main_battlefield_change_division(battle.id, division.id, side.id if side else None) - if resp.json().get('error'): - self.write_warning(resp.json().get('message')) + if resp.json().get("error"): + self.write_warning(resp.json().get("message")) return False - self._report_action('MILITARY_DIV_SWITCH', f"Switched to d{division.div} in battle {battle.id}", - kwargs=resp.json()) + self._report_action("MILITARY_DIV_SWITCH", f"Switched to d{division.div} in battle {battle.id}", kwargs=resp.json()) return True - def get_ground_hit_dmg_value(self, rang: int = None, strength: float = None, elite: bool = None, ne: bool = False, - booster_50: bool = False, booster_100: bool = False, tp: bool = True) -> Decimal: + def get_ground_hit_dmg_value( + self, + rang: int = None, + strength: float = None, + elite: bool = None, + ne: bool = False, + booster_50: bool = False, + booster_100: bool = False, + tp: bool = True, + ) -> Decimal: if not rang or not strength or elite is None: r = self._get_main_citizen_profile_json(self.details.citizen_id).json() if not rang: - rang = r['military']['militaryData']['ground']['rankNumber'] + rang = r["military"]["militaryData"]["ground"]["rankNumber"] if not strength: - strength = r['military']['militaryData']['ground']['strength'] + strength = r["military"]["militaryData"]["ground"]["strength"] if elite is None: - elite = r['citizenAttributes']['level'] > 100 + elite = r["citizenAttributes"]["level"] > 100 if ne: tp = True return utils.calculate_hit(strength, rang, tp, elite, ne, 50 if booster_50 else 100 if booster_100 else 0) - def get_air_hit_dmg_value(self, rang: int = None, elite: bool = None, ne: bool = False, - weapon: bool = False) -> Decimal: + def get_air_hit_dmg_value(self, rang: int = None, elite: bool = None, ne: bool = False, weapon: bool = False) -> Decimal: if not rang or elite is None: r = self._get_main_citizen_profile_json(self.details.citizen_id).json() if not rang: - rang = r['military']['militaryData']['aircraft']['rankNumber'] + rang = r["military"]["militaryData"]["aircraft"]["rankNumber"] if elite is None: - elite = r['citizenAttributes']['level'] > 100 + elite = r["citizenAttributes"]["level"] > 100 return utils.calculate_hit(0, rang, True, elite, ne, 0, 20 if weapon else 0) def activate_damage_booster(self, ground: bool = True) -> int: - kind = 'damage' if ground else 'aircraftDamage' + kind = "damage" if ground else "aircraftDamage" if self.config.boosters and not self.get_active_damage_booster(ground): booster: Optional[types.InvFinalItem] = None for quality, data in sorted(self.inventory.boosters.get(kind, {}).items(), key=lambda x: x[0]): for _duration, _booster in sorted(data.items(), key=lambda y: y[0]): critical_amount = 2 if quality < 10 and ground else 10 - if _booster.get('amount') > critical_amount: + if _booster.get("amount") > critical_amount: booster = _booster break break if booster: - kind = 'damage' if ground else 'air_damage' - self._report_action('MILITARY_BOOSTER', f"Activated {booster['name']}") - resp = self._post_economy_activate_booster(booster['quality'], booster['durability'], kind).json() + kind = "damage" if ground else "air_damage" + self._report_action("MILITARY_BOOSTER", f"Activated {booster['name']}") + resp = self._post_economy_activate_booster(booster["quality"], booster["durability"], kind).json() self._update_inventory_data(resp) return self.get_active_damage_booster(ground) def get_active_damage_booster(self, ground: bool = True) -> int: - kind = 'damage' if ground else 'aircraftDamage' + kind = "damage" if ground else "aircraftDamage" boosters = self.inventory.active.get(kind, {}) quality = 0 for q, boost in boosters.items(): - if boost['quality'] * 10 > quality: - quality = boost['quality'] * 10 + if boost["quality"] * 10 > quality: + quality = boost["quality"] * 10 return quality def get_active_ground_damage_booster(self) -> int: @@ -2143,15 +2213,15 @@ class CitizenMilitary(CitizenTravel): return self.get_active_damage_booster(False) def activate_battle_effect(self, battle_id: int, kind: str) -> bool: - self._report_action('MILITARY_BOOSTER', f'Activated {kind} booster') + self._report_action("MILITARY_BOOSTER", f"Activated {kind} booster") resp = self._post_main_activate_battle_effect(battle_id, kind, self.details.citizen_id).json() - return not resp.get('error') + return not resp.get("error") def activate_pp_booster(self, pp_item: types.InvFinalItem) -> bool: - self._report_action('MILITARY_BOOSTER', f'Activated {pp_item["name"]}') - resp = self._post_economy_activate_booster(pp_item['quality'], pp_item['durability'], 'prestige_points').json() + self._report_action("MILITARY_BOOSTER", f'Activated {pp_item["name"]}') + resp = self._post_economy_activate_booster(pp_item["quality"], pp_item["durability"], "prestige_points").json() self._update_inventory_data(resp) - return pp_item.get('kind') in self.inventory.active + return pp_item.get("kind") in self.inventory.active def _rw_choose_side(self, battle: classes.Battle, side: classes.BattleSide) -> Response: return self._post_main_battlefield_travel(side.id, battle.id) @@ -2171,23 +2241,23 @@ class CitizenMilitary(CitizenTravel): return ret def should_fight(self) -> Tuple[int, str, bool]: - """ Checks if citizen should fight at this moment + """Checks if citizen should fight at this moment :rtype: Tuple[int, str, bool] """ count = 0 force_fight = False - msg = 'Fighting not allowed!' + msg = "Fighting not allowed!" if not self.config.fight: return count, msg, force_fight # Do levelup if self.is_levelup_reachable: - msg = 'Level up' + msg = "Level up" if self.should_do_levelup: count = (self.energy.limit * 3) // 10 force_fight = True else: - self.write_log('Waiting for fully recovered energy before leveling up.') + self.write_log("Waiting for fully recovered energy before leveling up.") # Levelup reachable elif self.is_levelup_close: @@ -2197,12 +2267,12 @@ class CitizenMilitary(CitizenTravel): elif self.details.pp < 75: count = 75 - self.details.pp - msg = 'Obligatory fighting for at least 75pp' + msg = "Obligatory fighting for at least 75pp" force_fight = True elif self.config.continuous_fighting and self.has_battle_contribution: count = self.energy.food_fights - msg = 'Continuing to fight in previous battle' + msg = "Continuing to fight in previous battle" # All-in (type = all-in and full ff) elif self.config.all_in and self.energy.energy + self.energy.interval * 3 >= self.energy.limit: @@ -2225,12 +2295,18 @@ class CitizenMilitary(CitizenTravel): def get_battle_round_data(self, division: classes.BattleDivision) -> Tuple[Any, Any]: battle = division.battle - r = self._post_military_battle_console(battle.id, 'battleStatistics', 1, - zoneId=battle.zone_id, round_id=battle.zone_id, division=division.div, - battleZoneId=division.id, type='damage') + r = self._post_military_battle_console( + battle.id, + "battleStatistics", + 1, + zoneId=battle.zone_id, + round_id=battle.zone_id, + division=division.div, + battleZoneId=division.id, + type="damage", + ) r_json = r.json() - return (r_json.get(str(battle.invader.id)).get('fighterData'), - r_json.get(str(battle.defender.id)).get('fighterData')) + return (r_json.get(str(battle.invader.id)).get("fighterData"), r_json.get(str(battle.defender.id)).get("fighterData")) def get_battle_division_stats(self, division: classes.BattleDivision) -> Dict[str, Any]: battle = division.battle @@ -2238,14 +2314,14 @@ class CitizenMilitary(CitizenTravel): return r.json() def get_division_max_hit(self, division: classes.BattleDivision) -> int: - """ Returns max hit in division for current side (if not on either side returns 0) + """Returns max hit in division for current side (if not on either side returns 0) :param division: BattleDivision for which to get max hit value :type division: classes.BattleDivision :return: max hit value :rtype: int """ - return self.get_battle_division_stats(division).get('maxHit', -1) + return self.get_battle_division_stats(division).get("maxHit", -1) def schedule_attack(self, war_id: int, region_id: int, region_name: str, at_time: datetime): if at_time: @@ -2262,58 +2338,58 @@ class CitizenMilitary(CitizenTravel): r = self._get_wars_show(war_id) html = r.text last_battle_id = int(re.search(r'', html).group(1)) - console = self._post_military_battle_console(last_battle_id, 'warList', 1).json() - battle = console.get('list')[0] - return utils.localize_dt(datetime.strptime(battle.get('result').get('end'), "%Y-%m-%d %H:%M:%S")) + console = self._post_military_battle_console(last_battle_id, "warList", 1).json() + battle = console.get("list")[0] + return utils.localize_dt(datetime.strptime(battle.get("result").get("end"), "%Y-%m-%d %H:%M:%S")) def launch_attack(self, war_id: int, region_id: int, region_name: str): self._post_wars_attack_region(war_id, region_id, region_name) - self._report_action('MILITARY_QUEUE_ATTACK', f"Battle for *{region_name}* queued") + self._report_action("MILITARY_QUEUE_ATTACK", f"Battle for *{region_name}* queued") def get_country_mus(self, country: constants.Country) -> Dict[int, str]: ret = {} r = self._get_main_leaderboards_damage_rankings(country.id) - for data in r.json()['mu_filter']: - if data['id']: - ret.update({data['id']: data['name']}) + for data in r.json()["mu_filter"]: + if data["id"]: + ret.update({data["id"]: data["name"]}) r = self._get_main_leaderboards_damage_aircraft_rankings(country.id) - for data in r.json()['mu_filter']: - if data['id']: - ret.update({data['id']: data['name']}) + for data in r.json()["mu_filter"]: + if data["id"]: + ret.update({data["id"]: data["name"]}) return ret def get_mu_members(self, mu_id: int) -> Dict[int, str]: ret = {} r = self._get_military_unit_data(mu_id) - for page in range(int(r.json()['panelContents']['pages'])): + for page in range(int(r.json()["panelContents"]["pages"])): r = self._get_military_unit_data(mu_id, currentPage=page + 1) - for user in r.json()['panelContents']['members']: - if not user['isDead']: - ret.update({user['citizenId']: user['name']}) + for user in r.json()["panelContents"]["members"]: + if not user["isDead"]: + ret.update({user["citizenId"]: user["name"]}) return ret def get_citizen_weekly_daily_orders_done(self, citizen_id: int = None, weeks_ago: int = 0) -> int: if citizen_id is None: citizen_id = self.details.citizen_id profile = self._get_main_citizen_profile_json(citizen_id).json() - mu_id = profile.get('military', {}).get('militaryUnit', {}).get('id', 0) + mu_id = profile.get("military", {}).get("militaryUnit", {}).get("id", 0) if mu_id: - name = profile.get('citizen', {}).get('name') - member = self._get_military_unit_data(mu_id, currentPage=1, panel='members', sortBy='dailyOrdersCompleted', - weekFilter=f"week{weeks_ago}", search=name).json() - return member.get('panelContents', {}).get('members', [{}])[0].get('dailyOrdersCompleted') + name = profile.get("citizen", {}).get("name") + member = self._get_military_unit_data( + mu_id, currentPage=1, panel="members", sortBy="dailyOrdersCompleted", weekFilter=f"week{weeks_ago}", search=name + ).json() + return member.get("panelContents", {}).get("members", [{}])[0].get("dailyOrdersCompleted") return 0 def get_possibly_empty_medals(self): self.update_war_info() for battle in self.all_battles.values(): for division in battle.div.values(): - if division.wall['dom'] == 50 or division.wall['dom'] > 98: - yield division, division.wall['for'] == battle.invader.country.id + if division.wall["dom"] == 50 or division.wall["dom"] > 98: + yield division, division.wall["for"] == battle.invader.country.id - def report_fighting(self, battle: classes.Battle, invader: bool, division: classes.BattleDivision, damage: float, - hits: int): + def report_fighting(self, battle: classes.Battle, invader: bool, division: classes.BattleDivision, damage: float, hits: int): self.reporter.report_fighting(battle, invader, division, damage, hits) if self.config.telegram: self.telegram.report_fight(battle, invader, division, damage, hits) @@ -2323,11 +2399,11 @@ class CitizenMilitary(CitizenTravel): # if ret.get('recoverableEnergyBuyFood'): # self.buy_food() # return self.get_deploy_inventory(division, side) - if ret.get('captcha'): + if ret.get("captcha"): self.do_captcha_challenge() - if ret.get('error'): - if ret.get('message') == 'Deployment disabled.': - self._post_main_profile_update('options', params='{"optionName":"enable_web_deploy","optionValue":"on"}') + if ret.get("error"): + if ret.get("message") == "Deployment disabled.": + self._post_main_profile_update("options", params='{"optionName":"enable_web_deploy","optionValue":"on"}') return self.get_deploy_inventory(division, side) else: self.report_error(f"Unable to get deployment inventory because: {ret.get('message')}") @@ -2336,21 +2412,21 @@ class CitizenMilitary(CitizenTravel): def deploy(self, division: classes.BattleDivision, side: classes.BattleSide, energy: int, _retry=0) -> int: _energy = int(energy) deploy_inv = self.get_deploy_inventory(division, side) - if not deploy_inv['minEnergy'] <= energy <= deploy_inv['maxEnergy']: + if not deploy_inv["minEnergy"] <= energy <= deploy_inv["maxEnergy"]: return 0 energy_sources = {} source_idx = 0 - recoverable = deploy_inv['recoverableEnergy'] - for source in reversed(sorted(deploy_inv['energySources'], key=lambda s: (s['type'], s.get('quality', 0)))): - if source['type'] == 'pool': - _energy -= source['energy'] - elif source['type'] in ['food', 'energy_bar']: - recovers = source['energy'] // source['amount'] - amount = (recoverable if source['type'] == 'food' else _energy) // recovers - amount = amount if amount < source['amount'] else source['amount'] + recoverable = deploy_inv["recoverableEnergy"] + for source in reversed(sorted(deploy_inv["energySources"], key=lambda s: (s["type"], s.get("quality", 0)))): + if source["type"] == "pool": + _energy -= source["energy"] + elif source["type"] in ["food", "energy_bar"]: + recovers = source["energy"] // source["amount"] + amount = (recoverable if source["type"] == "food" else _energy) // recovers + amount = amount if amount < source["amount"] else source["amount"] if amount > 0: - energy_sources.update({f'energySources[{source_idx}][quality]': source['quality']}) - energy_sources.update({f'energySources[{source_idx}][amount]': amount}) + energy_sources.update({f"energySources[{source_idx}][quality]": source["quality"]}) + energy_sources.update({f"energySources[{source_idx}][amount]": amount}) source_idx += 1 used_energy = amount * recovers recoverable -= used_energy @@ -2362,22 +2438,20 @@ class CitizenMilitary(CitizenTravel): weapon_q = -1 weapon_strength = 0 if not division.is_air: - for weapon in sorted(deploy_inv['weapons'], key=lambda w: w['damageperHit']): - if (weapon['damageperHit'] or 0) > weapon_strength and (weapon['amount'] or 0) > 50: - weapon_q = weapon['quality'] - r = self._post_fight_deploy_start_deploy( - division.battle.id, side.id, division.id, energy, weapon_q, **energy_sources - ).json() - if r.get('error'): + for weapon in sorted(deploy_inv["weapons"], key=lambda w: w["damageperHit"]): + if (weapon["damageperHit"] or 0) > weapon_strength and (weapon["amount"] or 0) > 50: + weapon_q = weapon["quality"] + r = self._post_fight_deploy_start_deploy(division.battle.id, side.id, division.id, energy, weapon_q, **energy_sources).json() + if r.get("error"): self.report_error(f"Deploy failed: '{r.get('message')}'") - if r.get('message') == 'Deployment disabled.': - self._post_main_profile_update('options', params='{"optionName":"enable_web_deploy","optionValue":"on"}') + if r.get("message") == "Deployment disabled.": + self._post_main_profile_update("options", params='{"optionName":"enable_web_deploy","optionValue":"on"}') if _retry < 5: - return self.deploy(division, side, energy, _retry+1) + return self.deploy(division, side, energy, _retry + 1) else: - self.report_error('Unable to deploy 5 times!') + self.report_error("Unable to deploy 5 times!") return 0 - return r.get('deploymentId') + return r.get("deploymentId") class CitizenPolitics(BaseCitizen): @@ -2390,31 +2464,29 @@ class CitizenPolitics(BaseCitizen): def candidate_for_party_presidency(self) -> Optional[Response]: if self.politics.is_party_member: - self._report_action('POLITIC_PARTY_PRESIDENT', 'Applied for party president elections') + self._report_action("POLITIC_PARTY_PRESIDENT", "Applied for party president elections") return self._get_candidate_party(self.politics.party_slug) else: - self._report_action('POLITIC_CONGRESS', - 'Unable to apply for party president elections - not a party member') + self._report_action("POLITIC_CONGRESS", "Unable to apply for party president elections - not a party member") return None def candidate_for_congress(self, presentation: str = "") -> Optional[Response]: if self.politics.is_party_member: - self._report_action('POLITIC_CONGRESS', 'Applied for congress elections') + self._report_action("POLITIC_CONGRESS", "Applied for congress elections") return self._post_candidate_for_congress(presentation) else: - self._report_action('POLITIC_CONGRESS', 'Unable to apply for congress elections - not a party member') + self._report_action("POLITIC_CONGRESS", "Unable to apply for congress elections - not a party member") return None - def get_country_president_election_result( - self, country: constants.Country, year: int, month: int - ) -> Dict[str, int]: + def get_country_president_election_result(self, country: constants.Country, year: int, month: int) -> Dict[str, int]: timestamp = int(constants.erep_tz.localize(datetime(year, month, 5)).timestamp()) resp = self._get_presidential_elections(country.id, timestamp) candidates = re.findall(r'class="candidate_info">(.*?)', resp.text, re.S | re.M) ret = {} for candidate in candidates: - name = re.search(r'', candidate) + name = re.search( + r'', candidate + ) name = name.group(1) votes = re.search(r'(\d+) votes', candidate).group(1) ret.update({name: int(votes)}) @@ -2424,58 +2496,56 @@ class CitizenPolitics(BaseCitizen): class CitizenSocial(BaseCitizen): def send_mail(self, subject: str, msg: str, ids: List[int]): for player_id in ids: - self._report_action('SOCIAL_MESSAGE', f'Sent a message to {player_id}', - kwargs=dict(subject=subject, msg=msg, id=player_id)) + self._report_action("SOCIAL_MESSAGE", f"Sent a message to {player_id}", kwargs=dict(subject=subject, msg=msg, id=player_id)) self._post_main_messages_compose(subject, msg, [player_id]) def write_on_country_wall(self, message: str) -> bool: self._get_main() - post_to_wall_as = re.findall(r'id="post_to_country_as".*?.*', - self.r.text, re.S | re.M) + post_to_wall_as = re.findall(r'id="post_to_country_as".*?.*', self.r.text, re.S | re.M) r = self._post_main_country_post_create(message, max(post_to_wall_as, key=int) if post_to_wall_as else 0) - self._report_action('SOCIAL_WRITE_WALL_COUNTRY', 'Wrote a message to the country wall') + self._report_action("SOCIAL_WRITE_WALL_COUNTRY", "Wrote a message to the country wall") return r.json() def add_friend(self, player_id: int) -> Response: resp = self._get_main_citizen_hovercard(player_id) r_json = resp.json() - if not any([r_json['isBanned'], r_json['isDead'], r_json['isFriend'], r_json['isOrg'], r_json['isSelf']]): + if not any([r_json["isBanned"], r_json["isDead"], r_json["isFriend"], r_json["isOrg"], r_json["isSelf"]]): r = self._post_main_citizen_add_remove_friend(int(player_id), True) self.write_log(f"{r_json['name']:<64} (id:{player_id:>11}) added as friend") - self._report_action('SOCIAL_ADD_FRIEND', f"{r_json['name']:<64} (id:{player_id:>11}) added as friend") + self._report_action("SOCIAL_ADD_FRIEND", f"{r_json['name']:<64} (id:{player_id:>11}) added as friend") return r return resp def add_every_player_as_friend(self): cities = [] cities_dict = {} - self.write_warning('This will take a lot of time.') - rj = self._post_main_travel_data(regionId=662, check='getCountryRegions').json() - for region_data in rj.get('regions', {}).values(): - cities.append(region_data['cityId']) - cities_dict.update({region_data['cityId']: region_data['cityName']}) + self.write_warning("This will take a lot of time.") + rj = self._post_main_travel_data(regionId=662, check="getCountryRegions").json() + for region_data in rj.get("regions", {}).values(): + cities.append(region_data["cityId"]) + cities_dict.update({region_data["cityId"]: region_data["cityName"]}) cities.sort(key=int) for city_id in cities: self.write_log(f"Adding friends from {cities_dict[city_id]} (id: {city_id})") resp = self._get_main_city_data_residents(city_id).json() - for resident in resp['widgets']['residents']['residents']: - self.add_friend(resident['citizenId']) - for page in range(2, resp['widgets']['residents']['numResults'] // 10 + 2): + for resident in resp["widgets"]["residents"]["residents"]: + self.add_friend(resident["citizenId"]) + for page in range(2, resp["widgets"]["residents"]["numResults"] // 10 + 2): r = self._get_main_city_data_residents(city_id, page) resp = r.json() - for resident in resp['widgets']['residents']['residents']: - self.add_friend(resident['citizenId']) + for resident in resp["widgets"]["residents"]["residents"]: + self.add_friend(resident["citizenId"]) def get_community_notifications(self, page: int = 1) -> List[Dict[str, Any]]: - return self._get_main_notifications_ajax_community(page).json().get('alertsList', []) + return self._get_main_notifications_ajax_community(page).json().get("alertsList", []) def get_system_notifications(self, page: int = 1) -> List[Dict[str, Any]]: - return self._get_main_notifications_ajax_system(page).json().get('alertsList', []) + return self._get_main_notifications_ajax_system(page).json().get("alertsList", []) def get_report_notifications(self, page: int = 1) -> List[Dict[str, Any]]: - return self._get_main_notifications_ajax_report(page).json().get('alertsList', []) + return self._get_main_notifications_ajax_report(page).json().get("alertsList", []) def delete_community_notification(self, *notification_ids: int): ids = [] @@ -2496,19 +2566,21 @@ class CitizenSocial(BaseCitizen): self._post_main_notifications_ajax_report(ids) def get_all_notifications(self, page: int = 1) -> Dict[str, List[Dict[str, Any]]]: - return dict(community=self.get_community_notifications(), - system=self.get_system_notifications(page), - report=self.get_report_notifications(page)) + return dict( + community=self.get_community_notifications(), + system=self.get_system_notifications(page), + report=self.get_report_notifications(page), + ) def delete_all_notifications(self): for kind, notifications in self.get_all_notifications(): if notifications: - if kind == 'community': - self.delete_community_notification(*[n['id'] for n in notifications]) - elif kind == 'report': - self.delete_report_notification(*[n['id'] for n in notifications]) - elif kind == 'system': - self.delete_system_notification(*[n['id'] for n in notifications]) + if kind == "community": + self.delete_community_notification(*[n["id"] for n in notifications]) + elif kind == "report": + self.delete_report_notification(*[n["id"] for n in notifications]) + elif kind == "system": + self.delete_system_notification(*[n["id"] for n in notifications]) else: self.report_error(f'Unsupported notification kind: "{kind}"!') @@ -2521,10 +2593,10 @@ class CitizenSocial(BaseCitizen): if citizen_id is None: citizen_id = self.details.citizen_id profile = self.get_citizen_profile(citizen_id) - name = profile.get('citizen', {}).get('name', '') - city_id = profile.get('citizen', {}).get('residenceCityId') + name = profile.get("citizen", {}).get("name", "") + city_id = profile.get("citizen", {}).get("residenceCityId") if city_id: - return self._get_main_city_data_residents(city_id, params={'search': name}).json() + return self._get_main_city_data_residents(city_id, params={"search": name}).json() class CitizenTasks(CitizenEconomy): @@ -2540,19 +2612,19 @@ class CitizenTasks(CitizenEconomy): def work(self): if self.energy.food_fights >= 1: - response = self._post_economy_work('work') + response = self._post_economy_work("work") js = response.json() - good_msg = ['already_worked', 'captcha'] - if not js.get('status') and not js.get('message') in good_msg: - if js.get('message') in ['employee', 'money']: + good_msg = ["already_worked", "captcha"] + if not js.get("status") and not js.get("message") in good_msg: + if js.get("message") in ["employee", "money"]: self.resign_from_employer() self.find_new_job() - elif js.get('message') in ['not_enough_health_food']: + elif js.get("message") in ["not_enough_health_food"]: self.buy_food(120) self.update_citizen_info() self.work() else: - self.reporter.report_action('WORK', json_val=js) + self.reporter.report_action("WORK", json_val=js) else: if self.energy.food_fights < 1: seconds = (self.energy.reference_time - self.now).total_seconds() @@ -2563,23 +2635,23 @@ class CitizenTasks(CitizenEconomy): def train(self): r = self._get_main_training_grounds_json() tg_json = r.json() - self.details.gold = tg_json['page_details']['gold'] - self.tg_contract.update(free_train=tg_json['hasFreeTrain']) - if tg_json['contracts']: - self.tg_contract.update(**tg_json['contracts'][0]) + self.details.gold = tg_json["page_details"]["gold"] + self.tg_contract.update(free_train=tg_json["hasFreeTrain"]) + if tg_json["contracts"]: + self.tg_contract.update(**tg_json["contracts"][0]) tgs = [] - for data in sorted(tg_json['grounds'], key=lambda k: k['cost']): - if data['default'] and not data['trained']: - tgs.append(data['id']) + for data in sorted(tg_json["grounds"], key=lambda k: k["cost"]): + if data["default"] and not data["trained"]: + tgs.append(data["id"]) if tgs: if self.energy.food_fights >= len(tgs): response = self._post_economy_train(tgs) - if not response.json().get('status'): + if not response.json().get("status"): self.update_citizen_info() self.train() else: - self.reporter.report_action('TRAIN', response.json()) + self.reporter.report_action("TRAIN", response.json()) else: if self.energy.food_fights < len(tgs): large = max(self.energy.reference_time, self.now) @@ -2593,15 +2665,15 @@ class CitizenTasks(CitizenEconomy): self.update_job_info() if self.ot_points >= 24 and self.energy.food_fights > 1: r = self._post_economy_work_overtime() - if not r.json().get('status') and r.json().get('message') == 'money': + if not r.json().get("status") and r.json().get("message") == "money": self.resign_from_employer() self.find_new_job() else: - if r.json().get('message') == 'employee': + if r.json().get("message") == "employee": self.find_new_job() - elif r.json().get('message') == 'not_enough_health_food': + elif r.json().get("message") == "not_enough_health_food": self.buy_food(120) - self.reporter.report_action('WORK_OT', r.json()) + self.reporter.report_action("WORK_OT", r.json()) elif self.energy.food_fights < 1 and self.ot_points >= 24: if self.energy.food_fights < 1: large = max(self.energy.reference_time, self.now) @@ -2612,51 +2684,52 @@ class CitizenTasks(CitizenEconomy): def resign_from_employer(self) -> bool: r = self._get_main_job_data() - if r.json().get('isEmployee'): - self._report_action('ECONOMY_RESIGN', 'Resigned from employer!', kwargs=r.json()) + if r.json().get("isEmployee"): + self._report_action("ECONOMY_RESIGN", "Resigned from employer!", kwargs=r.json()) self._post_economy_resign() return True return False def buy_tg_contract(self) -> Response: - ret = self._post_main_buy_gold_items('gold', 'TrainingContract2', 1) + ret = self._post_main_buy_gold_items("gold", "TrainingContract2", 1) try: extra = ret.json() except: # noqa extra = {} - self._report_action('ECONOMY_TG_CONTRACT', 'Bought TG Contract', kwargs=extra) + self._report_action("ECONOMY_TG_CONTRACT", "Bought TG Contract", kwargs=extra) return ret def find_new_job(self) -> bool: r = self._get_economy_job_market_json(self.details.current_country.id) - jobs = r.json().get('jobs') + jobs = r.json().get("jobs") data = dict(citizen_id=0, salary=10) for posting in jobs: - salary = posting.get('salary') - limit = posting.get('salaryLimit', 0) - citizen_id = posting.get('citizen').get('id') + salary = posting.get("salary") + limit = posting.get("salaryLimit", 0) + citizen_id = posting.get("citizen").get("id") - if (not limit or salary * 3 < limit) and salary > data['salary']: + if (not limit or salary * 3 < limit) and salary > data["salary"]: data.update(citizen_id=citizen_id, salary=salary) - return self.apply_to_employer(data['citizen_id'], data['salary']) + return self.apply_to_employer(data["citizen_id"], data["salary"]) def apply_to_employer(self, employer_id: int, salary: float) -> bool: data = dict(citizenId=employer_id, salary=salary) - self._report_action('ECONOMY_APPLY_FOR_JOB', f"I'm working now for #{employer_id}", kwargs=data) + self._report_action("ECONOMY_APPLY_FOR_JOB", f"I'm working now for #{employer_id}", kwargs=data) r = self._post_economy_job_market_apply(employer_id, salary) - return bool(r.json().get('status')) + return bool(r.json().get("status")) def update_job_info(self): resp = self._get_main_job_data() - ot = resp.json().get('overTime', {}) + ot = resp.json().get("overTime", {}) if ot: - self.next_ot_time = utils.localize_timestamp(int(ot.get('nextOverTime', 0))) - self.ot_points = ot.get('points', 0) + self.next_ot_time = utils.localize_timestamp(int(ot.get("nextOverTime", 0))) + self.ot_points = ot.get("points", 0) -class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, - CitizenMedia, CitizenPolitics, CitizenSocial, CitizenMilitary, CitizenTasks): +class _Citizen( + CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, CitizenMedia, CitizenPolitics, CitizenSocial, CitizenMilitary, CitizenTasks +): def __init__(self, email: str = "", password: str = "", auto_login: bool = False): super().__init__(email, password) self._last_full_update = constants.min_datetime @@ -2685,9 +2758,7 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, self.update_citizen_info() self.reporter.do_init() if self.config.telegram and self.config.telegram_chat_id: - self.telegram.do_init(self.config.telegram_chat_id, - self.config.telegram_token, - self.name) + self.telegram.do_init(self.config.telegram_chat_id, self.config.telegram_token, self.name) self.telegram.send_message(f"*Started* {utils.now():%F %T}") self.init_logger() @@ -2703,58 +2774,64 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, return super().update_citizen_info(html) - if self.promos.get('trainingContract'): + if self.promos.get("trainingContract"): if not self.tg_contract: self.train() - if not self.tg_contract['free_train'] and self.tg_contract.get('active', False): + if not self.tg_contract["free_train"] and self.tg_contract.get("active", False): if self.details.gold >= 54: self.buy_tg_contract() else: - self.write_warning('Training ground contract active but ' - f"don't have enough gold ({self.details.gold}g {self.details.cc}cc)") + self.write_warning( + "Training ground contract active but " f"don't have enough gold ({self.details.gold}g {self.details.cc}cc)" + ) if self.energy.is_energy_full and self.config.telegram: self.telegram.report_full_energy(self.energy.energy, self.energy.limit, self.energy.interval) def check_for_notification_medals(self): notifications = self._get_main_citizen_daily_assistant().json() data: Dict[Tuple[str, Union[float, str]], Dict[str, Union[int, str, float]]] = {} - for medal in notifications.get('notifications', []): - if medal.get('details', {}).get('type') == 'citizenAchievement': - params: dict = medal.get('details', {}).get('achievement') - about: str = medal.get('body') - title: str = medal.get('title') - count: int = medal.get('details', {}).get('achievementAmount', 1) + for medal in notifications.get("notifications", []): + if medal.get("details", {}).get("type") == "citizenAchievement": + params: dict = medal.get("details", {}).get("achievement") + about: str = medal.get("body") + title: str = medal.get("title") + count: int = medal.get("details", {}).get("achievementAmount", 1) - award_id: int = medal.get('id') - if award_id and title and medal.get('details').get('isWallMaterial'): + award_id: int = medal.get("id") + if award_id and title and medal.get("details").get("isWallMaterial"): self._post_main_wall_post_automatic(title.lower(), award_id) - if params.get('ccValue'): - reward = params.get('ccValue') - currency = 'Currency' - elif params.get('goldValue'): - reward = params.get('goldValue') - currency = 'Gold' + if params.get("ccValue"): + reward = params.get("ccValue") + currency = "Currency" + elif params.get("goldValue"): + reward = params.get("goldValue") + currency = "Gold" else: - reward = params.get('energyValue') - currency = 'Energy' + reward = params.get("energyValue") + currency = "Energy" if (title, reward) not in data: - data[(title, reward)] = {'about': about, 'kind': title, 'reward': reward, 'count': count, - 'currency': currency, 'params': medal.get('details', {})} + data[(title, reward)] = { + "about": about, + "kind": title, + "reward": reward, + "count": count, + "currency": currency, + "params": medal.get("details", {}), + } else: - data[(title, reward)]['count'] += count - self._post_main_global_alerts_close(medal.get('id')) + data[(title, reward)]["count"] += count + self._post_main_global_alerts_close(medal.get("id")) if data: - msgs = [f"{d['count']} x {d['kind']}, totaling {d['count'] * d['reward']} " - f"{d['currency']}" for d in data.values()] + msgs = [f"{d['count']} x {d['kind']}, totaling {d['count'] * d['reward']} " f"{d['currency']}" for d in data.values()] msgs = "\n".join(msgs) if self.config.telegram: self.telegram.report_medal(msgs, len(data) > 1) self.write_log(f"Found awards:\n{msgs}") for info in data.values(): - self.reporter.report_action('NEW_MEDAL', info) + self.reporter.report_action("NEW_MEDAL", info) def set_pin(self, pin: str): self.details.pin = str(pin[:4]) @@ -2774,19 +2851,18 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, def update_weekly_challenge(self): data = self._get_main_weekly_challenge_data().json() - self.details.pp = data.get('player', {}).get('prestigePoints', 0) + self.details.pp = data.get("player", {}).get("prestigePoints", 0) self.details.next_pp.clear() - max_collectable_id = data.get('maxRewardId') + max_collectable_id = data.get("maxRewardId") should_collect = False - for reward in data.get('rewards', {}).get('normal', {}): - status = reward.get('status', "") - if status == 'rewarded': + for reward in data.get("rewards", {}).get("normal", {}): + status = reward.get("status", "") + if status == "rewarded": continue - elif status == 'completed': + elif status == "completed": should_collect = True - elif reward.get('icon', "") == 'energy_booster': - pps = re.search(r"Reach (\d+) Prestige Points to unlock the following reward: \+1 Energy", - reward.get('tooltip', "")) + elif reward.get("icon", "") == "energy_booster": + pps = re.search(r"Reach (\d+) Prestige Points to unlock the following reward: \+1 Energy", reward.get("tooltip", "")) if pps: self.details.next_pp.append(int(pps.group(1))) if should_collect: @@ -2797,9 +2873,11 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, if count > 0 and not force_fight: if self.energy.food_fights - self.my_companies.ff_lockdown < count: - log_msg = (f"Fight count modified (old count: {count} | FF: {self.energy.food_fights} | " - f"WAM ff_lockdown: {self.my_companies.ff_lockdown} |" - f" New count: {count - self.my_companies.ff_lockdown})") + log_msg = ( + f"Fight count modified (old count: {count} | FF: {self.energy.food_fights} | " + f"WAM ff_lockdown: {self.my_companies.ff_lockdown} |" + f" New count: {count - self.my_companies.ff_lockdown})" + ) count -= self.my_companies.ff_lockdown if count <= 0: count = 0 @@ -2807,8 +2885,7 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, if self.max_time_till_full_ff > self.time_till_week_change: max_count = (int(self.time_till_week_change.total_seconds()) // 360 * self.energy.interval) // 10 - log_msg = ('End for Weekly challenge is near ' - f"(Recoverable until WC end {max_count}hp | want to do {count}hits)") + log_msg = "End for Weekly challenge is near " f"(Recoverable until WC end {max_count}hp | want to do {count}hits)" count = count if max_count > count else max_count if not silent: @@ -2844,83 +2921,86 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, sleep_seconds = (start_time - self.now).total_seconds() self.stop_threads.wait(sleep_seconds if sleep_seconds > 0 else 0) except: # noqa - self.report_error('State updater crashed') + self.report_error("State updater crashed") def send_state_update(self): - data = dict(xp=self.details.xp, cc=self.details.cc, gold=self.details.gold, pp=self.details.pp, - inv_total=self.inventory.total, inv=self.inventory.used, - hp_limit=self.energy.limit, - hp_interval=self.energy.interval, hp_available=self.energy.energy, food=self.food['total'], ) + data = dict( + xp=self.details.xp, + cc=self.details.cc, + gold=self.details.gold, + pp=self.details.pp, + inv_total=self.inventory.total, + inv=self.inventory.used, + hp_limit=self.energy.limit, + hp_interval=self.energy.interval, + hp_available=self.energy.energy, + food=self.food["total"], + ) self.reporter.send_state_update(**data) def send_inventory_update(self): - self.reporter.report_action('INVENTORY', json_val=self.inventory.as_dict) + self.reporter.report_action("INVENTORY", json_val=self.inventory.as_dict) def send_my_companies_update(self): - self.reporter.report_action('COMPANIES', json_val=self.my_companies.as_dict) + self.reporter.report_action("COMPANIES", json_val=self.my_companies.as_dict) def sell_produced_product(self, kind: str, quality: int = 1, amount: int = 0): if not amount: inv_resp = self._get_economy_inventory_items().json() - category = 'rawMaterials' if kind.endswith('Raw') else 'finalProducts' + category = "rawMaterials" if kind.endswith("Raw") else "finalProducts" item = f"{constants.INDUSTRIES[kind]}_{quality}" - amount = inv_resp.get('inventoryItems').get(category).get('items').get(item).get('amount', 0) + amount = inv_resp.get("inventoryItems").get(category).get("items").get(item).get("amount", 0) if amount >= 1: - lowest_price = self.get_market_offers(kind, int(quality), self.details.citizenship)[f'q{int(quality)}'] + lowest_price = self.get_market_offers(kind, int(quality), self.details.citizenship)[f"q{int(quality)}"] if lowest_price.citizen_id == self.details.citizen_id: price = lowest_price.price else: price = lowest_price.price - 0.01 - self.post_market_offer(industry=constants.INDUSTRIES[kind], amount=int(amount), - quality=int(quality), price=price) + self.post_market_offer(industry=constants.INDUSTRIES[kind], amount=int(amount), quality=int(quality), price=price) def _wam(self, holding: classes.Holding) -> NoReturn: response = self.work_as_manager_in_holding(holding) if response is None: return - if response.get('status'): - self._report_action('WORK_AS_MANAGER', 'Worked as manager', kwargs=response) + if response.get("status"): + self._report_action("WORK_AS_MANAGER", "Worked as manager", kwargs=response) if self.config.auto_sell: - for kind, data in response.get('result', {}).get('production', {}).items(): + for kind, data in response.get("result", {}).get("production", {}).items(): if data and kind in self.config.auto_sell: - if kind in ['food', 'weapon', 'house', 'airplane']: + if kind in ["food", "weapon", "house", "airplane"]: for quality, amount in data.items(): self.sell_produced_product(kind, quality) - elif kind.endswith('Raw'): + elif kind.endswith("Raw"): self.sell_produced_product(kind, 1) else: raise classes.ErepublikException(f"Unknown kind produced '{kind}'") - elif self.config.auto_buy_raw and re.search(r"not_enough_[^_]*_raw", response.get('message')): - raw_kind = re.search(r"not_enough_(\w+)_raw", response.get('message')) + elif self.config.auto_buy_raw and re.search(r"not_enough_[^_]*_raw", response.get("message")): + raw_kind = re.search(r"not_enough_(\w+)_raw", response.get("message")) if raw_kind: raw_kind = raw_kind.group(1) - result = response.get('result', {}) - amount_needed = round(result.get('consume', 0) - result.get('stock', 0) + 0.5) + result = response.get("result", {}) + amount_needed = round(result.get("consume", 0) - result.get("stock", 0) + 0.5) self._report_action( - 'WORK_AS_MANAGER', f"Unable to wam! Missing {amount_needed} {raw_kind}, will try to buy.", - kwargs=response + "WORK_AS_MANAGER", f"Unable to wam! Missing {amount_needed} {raw_kind}, will try to buy.", kwargs=response ) start_place = (self.details.current_country, self.details.current_region) while amount_needed > 0: amount = amount_needed - best_offer = self.get_market_offers(f"{raw_kind}Raw")['q1'] + best_offer = self.get_market_offers(f"{raw_kind}Raw")["q1"] amount = best_offer.amount if amount >= best_offer.amount else amount if not best_offer.country == self.details.current_country: self.travel_to_country(best_offer.country) - self._report_action('ECONOMY_BUY', - f"Attempting to buy {amount} {raw_kind} for {best_offer.price * amount}cc") + self._report_action("ECONOMY_BUY", f"Attempting to buy {amount} {raw_kind} for {best_offer.price * amount}cc") rj = self.buy_from_market(amount=amount, offer=best_offer.offer_id) - if not rj.get('error'): + if not rj.get("error"): amount_needed -= amount else: - self.write_warning(rj.get('message', "")) - self._report_action( - 'ECONOMY_BUY', f"Unable to buy products! Reason: {rj.get('message')}", kwargs=rj - ) + self.write_warning(rj.get("message", "")) + self._report_action("ECONOMY_BUY", f"Unable to buy products! Reason: {rj.get('message')}", kwargs=rj) break else: if not start_place == (self.details.current_country, self.details.current_region): @@ -2931,25 +3011,25 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, if not start_place == (self.details.current_country, self.details.current_region): self.travel_to_residence() return - elif response.get('message') == 'not_enough_health_food': + elif response.get("message") == "not_enough_health_food": self.buy_food() self._wam(holding) - elif response.get('message') == 'tax_money': - self._report_action('WORK_AS_MANAGER', 'Not enough money to work as manager!', kwargs=response) - self.write_warning('Not enough money to work as manager!') + elif response.get("message") == "tax_money": + self._report_action("WORK_AS_MANAGER", "Not enough money to work as manager!", kwargs=response) + self.write_warning("Not enough money to work as manager!") else: - msg = f'I was not able to wam and or employ because:\n{response}' - self._report_action('WORK_AS_MANAGER', f'Worked as manager failed: {msg}', kwargs=response) + msg = f"I was not able to wam and or employ because:\n{response}" + self._report_action("WORK_AS_MANAGER", f"Worked as manager failed: {msg}", kwargs=response) self.write_warning(msg) def work_as_manager(self) -> bool: - """ Does Work as Manager in all holdings with wam. If employees assigned - work them also + """Does Work as Manager in all holdings with wam. If employees assigned - work them also :return: if has more wam work to do :rtype: bool """ if self.restricted_ip: - self._report_action('IP_BLACKLISTED', 'Work as manager is not allowed from restricted IP!') + self._report_action("IP_BLACKLISTED", "Work as manager is not allowed from restricted IP!") return False self.update_citizen_info() self.update_companies() @@ -2968,8 +3048,8 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, for holding in regions.values(): raw_usage = holding.get_wam_raw_usage() free_storage = self.inventory.total - self.inventory.used - if (raw_usage['frm'] + raw_usage['wrm']) * 100 > free_storage: - self._report_action('WAM_UNAVAILABLE', 'Not enough storage!') + if (raw_usage["frm"] + raw_usage["wrm"]) * 100 > free_storage: + self._report_action("WAM_UNAVAILABLE", "Not enough storage!") continue self.travel_to_holding(holding) self._wam(holding) @@ -2982,7 +3062,7 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, self.travel_to_residence() return bool(wam_count) else: - self.write_warning('Did not WAM because I would mess up levelup!') + self.write_warning("Did not WAM because I would mess up levelup!") self.my_companies.ff_lockdown = 0 self.update_companies() @@ -3005,7 +3085,7 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard, continue self.stop_threads.wait(90) except: # noqa - self.report_error('Command central is broken') + self.report_error("Command central is broken") class Citizen(_Citizen): @@ -3023,7 +3103,7 @@ class Citizen(_Citizen): def update_weekly_challenge(self): if not self._update_lock.wait(self._update_timeout): - e = f'Update concurrency not freed in {self._update_timeout}sec!' + e = f"Update concurrency not freed in {self._update_timeout}sec!" self.report_error(e) return None try: @@ -3034,7 +3114,7 @@ class Citizen(_Citizen): def update_companies(self): if not self._update_lock.wait(self._update_timeout): - e = f'Update concurrency not freed in {self._update_timeout}sec!' + e = f"Update concurrency not freed in {self._update_timeout}sec!" self.report_error(e) return None try: @@ -3045,7 +3125,7 @@ class Citizen(_Citizen): def update_war_info(self): if not self._update_lock.wait(self._update_timeout): - e = f'Update concurrency not freed in {self._update_timeout}sec!' + e = f"Update concurrency not freed in {self._update_timeout}sec!" self.report_error(e) return None try: @@ -3056,7 +3136,7 @@ class Citizen(_Citizen): def update_job_info(self): if not self._update_lock.wait(self._update_timeout): - e = f'Update concurrency not freed in {self._update_timeout}sec!' + e = f"Update concurrency not freed in {self._update_timeout}sec!" self.report_error(e) return None try: @@ -3067,7 +3147,7 @@ class Citizen(_Citizen): def update_money(self, page: int = 0, currency: int = 62): if not self._update_lock.wait(self._update_timeout): - e = f'Update concurrency not freed in {self._update_timeout}sec!' + e = f"Update concurrency not freed in {self._update_timeout}sec!" self.report_error(e) return None try: @@ -3078,7 +3158,7 @@ class Citizen(_Citizen): def update_inventory(self): if not self._update_lock.wait(self._update_timeout): - e = f'Update concurrency not freed in {self._update_timeout}sec!' + e = f"Update concurrency not freed in {self._update_timeout}sec!" self.report_error(e) return None try: @@ -3089,7 +3169,7 @@ class Citizen(_Citizen): def _work_as_manager(self, wam_holding: classes.Holding) -> Optional[Dict[str, Any]]: if not self._concurrency_lock.wait(self._concurrency_timeout): - e = f'Concurrency not freed in {self._concurrency_timeout}sec!' + e = f"Concurrency not freed in {self._concurrency_timeout}sec!" self.report_error(e) return None try: @@ -3098,10 +3178,16 @@ class Citizen(_Citizen): finally: self._concurrency_lock.set() - def fight(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide = None, - count: int = None, use_ebs: bool = False) -> Optional[int]: + def fight( + self, + battle: classes.Battle, + division: classes.BattleDivision, + side: classes.BattleSide = None, + count: int = None, + use_ebs: bool = False, + ) -> Optional[int]: if not self._concurrency_lock.wait(self._concurrency_timeout): - e = f'Concurrency not freed in {self._concurrency_timeout}sec!' + e = f"Concurrency not freed in {self._concurrency_timeout}sec!" self.report_error(e) return None try: @@ -3110,10 +3196,11 @@ class Citizen(_Citizen): finally: self._concurrency_lock.set() - def deploy_bomb(self, battle: classes.Battle, division: classes.BattleDivision, bomb_id: int, inv_side: bool, - count: int = 1) -> Optional[int]: + def deploy_bomb( + self, battle: classes.Battle, division: classes.BattleDivision, bomb_id: int, inv_side: bool, count: int = 1 + ) -> Optional[int]: if not self._concurrency_lock.wait(self._concurrency_timeout): - e = f'Concurrency not freed in {self._concurrency_timeout}sec!' + e = f"Concurrency not freed in {self._concurrency_timeout}sec!" self.report_error(e) return None try: @@ -3124,7 +3211,7 @@ class Citizen(_Citizen): def buy_market_offer(self, offer: classes.OfferItem, amount: int = None) -> Optional[Dict[str, Any]]: if not self._concurrency_lock.wait(self._concurrency_timeout): - e = f'Concurrency not freed in {self._concurrency_timeout}sec!' + e = f"Concurrency not freed in {self._concurrency_timeout}sec!" self.report_error(e) return None try: @@ -3136,8 +3223,14 @@ class Citizen(_Citizen): @property def as_dict(self): d = super().as_dict - d.update(locks=dict(concurrency_lock=self._concurrency_lock.is_set(), update_lock=self._update_lock.is_set(), - concurrency_timeout=self._concurrency_timeout, update_timeout=self._update_timeout)) + d.update( + locks=dict( + concurrency_lock=self._concurrency_lock.is_set(), + update_lock=self._update_lock.is_set(), + concurrency_timeout=self._concurrency_timeout, + update_timeout=self._update_timeout, + ) + ) return d def set_locks(self): diff --git a/erepublik/classes.py b/erepublik/classes.py index 9236f3c..d747122 100644 --- a/erepublik/classes.py +++ b/erepublik/classes.py @@ -12,9 +12,25 @@ from requests import HTTPError, Response, Session, post from erepublik import _types as types from erepublik import constants, utils -__all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException', - 'ErepublikNetworkException', 'EnergyToFight', 'Holding', 'Inventory', 'MyCompanies', 'OfferItem', 'Politics', - 'Reporter', 'TelegramReporter', ] +__all__ = [ + "Battle", + "BattleDivision", + "BattleSide", + "Company", + "Config", + "Details", + "Energy", + "ErepublikException", + "ErepublikNetworkException", + "EnergyToFight", + "Holding", + "Inventory", + "MyCompanies", + "OfferItem", + "Politics", + "Reporter", + "TelegramReporter", +] class ErepublikException(Exception): @@ -39,7 +55,7 @@ class CaptchaSessionError(ErepublikNetworkException): class Holding: id: int region: int - companies: List['Company'] + companies: List["Company"] name: str _citizen = weakref.ReferenceType @@ -47,16 +63,16 @@ class Holding: self._citizen = weakref.ref(citizen) self.id: int = _id self.region: int = region - self.companies: List['Company'] = list() + self.companies: List["Company"] = list() if name: self.name = name else: comp_sum = len(self.companies) name = f"Holding (#{self.id}) with {comp_sum} " if comp_sum == 1: - name += 'company' + name += "company" else: - name += 'companies' + name += "companies" self.name = name @property @@ -64,20 +80,20 @@ class Holding: return len([1 for company in self.companies if company.wam_enabled and not company.already_worked]) @property - def wam_companies(self) -> Iterable['Company']: + def wam_companies(self) -> Iterable["Company"]: return [company for company in self.companies if company.wam_enabled] @property - def employable_companies(self) -> Iterable['Company']: + def employable_companies(self) -> Iterable["Company"]: return [company for company in self.companies if company.preset_works] - def add_company(self, company: 'Company') -> NoReturn: + def add_company(self, company: "Company") -> NoReturn: self.companies.append(company) self.companies.sort() def get_wam_raw_usage(self) -> Dict[str, Decimal]: - frm = Decimal('0.00') - wrm = Decimal('0.00') + frm = Decimal("0.00") + wrm = Decimal("0.00") for company in self.wam_companies: if company.industry in [1, 7, 8, 9, 10, 11]: frm += company.raw_usage @@ -85,11 +101,11 @@ class Holding: wrm += company.raw_usage return dict(frm=frm, wrm=wrm) - def get_wam_companies(self, raw_factory: bool = None) -> List['Company']: + def get_wam_companies(self, raw_factory: bool = None) -> List["Company"]: raw = [] factory = [] for company in self.wam_companies: - if not company.already_worked and not company.cannot_wam_reason == 'war': + if not company.already_worked and not company.cannot_wam_reason == "war": if company.is_raw: raw.append(company) else: @@ -103,9 +119,9 @@ class Holding: comp = len(self.companies) name = f"Holding (#{self.id}) with {comp} " if comp == 1: - name += 'company' + name += "company" else: - name += 'companies' + name += "companies" return name def __repr__(self): @@ -113,8 +129,7 @@ class Holding: @property def as_dict(self) -> Dict[str, Union[str, int, List[Dict[str, Union[str, int, bool, float, Decimal]]]]]: - return dict(name=self.name, id=self.id, region=self.region, - companies=[c.as_dict for c in self.companies], wam_count=self.wam_count) + return dict(name=self.name, id=self.id, region=self.region, companies=[c.as_dict for c in self.companies], wam_count=self.wam_count) @property def citizen(self): @@ -137,9 +152,20 @@ class Company: preset_works: int def __init__( - self, holding: Holding, _id: int, quality: int, is_raw: bool, effective_bonus: Decimal, raw_usage: Decimal, - base_production: Decimal, wam_enabled: bool, can_wam: bool, cannot_wam_reason: str, industry: int, - already_worked: bool, preset_works: int + self, + holding: Holding, + _id: int, + quality: int, + is_raw: bool, + effective_bonus: Decimal, + raw_usage: Decimal, + base_production: Decimal, + wam_enabled: bool, + can_wam: bool, + cannot_wam_reason: str, + industry: int, + already_worked: bool, + preset_works: int, ): self._holding = weakref.ref(holding) self.id: int = _id @@ -154,7 +180,7 @@ class Company: self.products_made = self.raw_usage = Decimal(base_production) * Decimal(effective_bonus) if not self.is_raw: - self.raw_usage = - self.products_made * raw_usage + self.raw_usage = -self.products_made * raw_usage def _get_real_quality(self, quality) -> int: # 7: 'FRM q1', 8: 'FRM q2', 9: 'FRM q3', 10: 'FRM q4', 11: 'FRM q5', @@ -196,22 +222,22 @@ class Company: def __hash__(self): return hash(self._sort_keys) - def __lt__(self, other: 'Company'): + def __lt__(self, other: "Company"): return self._sort_keys < other._sort_keys - def __le__(self, other: 'Company'): + def __le__(self, other: "Company"): return self._sort_keys <= other._sort_keys - def __gt__(self, other: 'Company'): + def __gt__(self, other: "Company"): return self._sort_keys > other._sort_keys - def __ge__(self, other: 'Company'): + def __ge__(self, other: "Company"): return self._sort_keys >= other._sort_keys - def __eq__(self, other: 'Company'): + def __eq__(self, other: "Company"): return self._sort_keys == other._sort_keys - def __ne__(self, other: 'Company'): + def __ne__(self, other: "Company"): return self._sort_keys != other._sort_keys def __str__(self): @@ -225,10 +251,21 @@ class Company: @property def as_dict(self) -> Dict[str, Union[str, int, bool, float, Decimal]]: - return dict(name=str(self), holding=self.holding.id, id=self.id, quality=self.quality, is_raw=self.is_raw, - raw_usage=self.raw_usage, products_made=self.products_made, wam_enabled=self.wam_enabled, - can_wam=self.can_wam, cannot_wam_reason=self.cannot_wam_reason, industry=self.industry, - already_worked=self.already_worked, preset_works=self.preset_works) + return dict( + name=str(self), + holding=self.holding.id, + id=self.id, + quality=self.quality, + is_raw=self.is_raw, + raw_usage=self.raw_usage, + products_made=self.products_made, + wam_enabled=self.wam_enabled, + can_wam=self.can_wam, + cannot_wam_reason=self.cannot_wam_reason, + industry=self.industry, + already_worked=self.already_worked, + preset_works=self.preset_works, + ) def dissolve(self) -> Response: self.holding.citizen.write_log(f"{self} dissolved!") @@ -265,12 +302,10 @@ class MyCompanies: :param holdings: Parsed JSON to dict from en/economy/myCompanies """ for holding in holdings.values(): - if holding.get('id') not in self.holdings: - self.holdings.update({ - int(holding.get('id')): Holding(holding['id'], holding['region_id'], self.citizen, holding['name']) - }) + if holding.get("id") not in self.holdings: + self.holdings.update({int(holding.get("id")): Holding(holding["id"], holding["region_id"], self.citizen, holding["name"])}) if not self.holdings.get(0): - self.holdings.update({0: Holding(0, 0, self.citizen, 'Unassigned')}) # unassigned + self.holdings.update({0: Holding(0, 0, self.citizen, "Unassigned")}) # unassigned def prepare_companies(self, companies: Dict[str, Dict[str, Any]]): """ @@ -278,19 +313,27 @@ class MyCompanies: """ self.__clear_data() for company_dict in companies.values(): - holding = self.holdings.get(int(company_dict['holding_company_id'])) - quality = company_dict.get('quality') - is_raw = company_dict.get('is_raw') + holding = self.holdings.get(int(company_dict["holding_company_id"])) + quality = company_dict.get("quality") + is_raw = company_dict.get("is_raw") if is_raw: - raw_usage = Decimal('0.0') + raw_usage = Decimal("0.0") else: - raw_usage = Decimal(str(company_dict.get('upgrades').get(str(quality)).get('raw_usage'))) + raw_usage = Decimal(str(company_dict.get("upgrades").get(str(quality)).get("raw_usage"))) company = Company( - holding, company_dict.get('id'), quality, is_raw, - Decimal(str(company_dict.get('effective_bonus'))) / 100, - raw_usage, Decimal(str(company_dict.get('base_production'))), company_dict.get('wam_enabled'), - company_dict.get('can_work_as_manager'), company_dict.get('cannot_work_as_manager_reason'), - company_dict.get('industry_id'), company_dict.get('already_worked'), company_dict.get('preset_works') + holding, + company_dict.get("id"), + quality, + is_raw, + Decimal(str(company_dict.get("effective_bonus"))) / 100, + raw_usage, + Decimal(str(company_dict.get("base_production"))), + company_dict.get("wam_enabled"), + company_dict.get("can_work_as_manager"), + company_dict.get("cannot_work_as_manager_reason"), + company_dict.get("industry_id"), + company_dict.get("already_worked"), + company_dict.get("preset_works"), ) self._companies.add(company) holding.add_company(company) @@ -326,13 +369,20 @@ class MyCompanies: self._companies.clear() @property - def as_dict(self) -> Dict[str, Union[str, int, datetime.datetime, Dict[str, Dict[str, Union[ - str, int, List[Dict[str, Union[str, int, bool, float, Decimal]]]] - ]]]]: - return dict(name=str(self), work_units=self.work_units, next_ot_time=self.next_ot_time, - ff_lockdown=self.ff_lockdown, - holdings={str(hi): h.as_dict for hi, h in self.holdings.items()}, - company_count=sum(1 for _ in self.companies)) + def as_dict( + self, + ) -> Dict[ + str, + Union[str, int, datetime.datetime, Dict[str, Dict[str, Union[str, int, List[Dict[str, Union[str, int, bool, float, Decimal]]]]]]], + ]: + return dict( + name=str(self), + work_units=self.work_units, + next_ot_time=self.next_ot_time, + ff_lockdown=self.ff_lockdown, + holdings={str(hi): h.as_dict for hi, h in self.holdings.items()}, + company_count=sum(1 for _ in self.companies), + ) @property def citizen(self): @@ -408,16 +458,37 @@ class Config: @property def as_dict(self) -> Dict[str, Union[bool, int, str, List[str]]]: - return dict(email=self.email, work=self.work, train=self.train, wam=self.wam, ot=self.ot, - auto_sell=self.auto_sell, auto_sell_all=self.auto_sell_all, employees=self.employees, - fight=self.fight, air=self.air, ground=self.ground, all_in=self.all_in, - next_energy=self.next_energy, travel_to_fight=self.travel_to_fight, - always_travel=self.always_travel, epic_hunt=self.epic_hunt, epic_hunt_ebs=self.epic_hunt_ebs, - rw_def_side=self.rw_def_side, interactive=self.interactive, maverick=self.maverick, - continuous_fighting=self.continuous_fighting, auto_buy_raw=self.auto_buy_raw, - force_wam=self.force_wam, sort_battles_time=self.sort_battles_time, force_travel=self.force_travel, - telegram=self.telegram, telegram_chat_id=self.telegram_chat_id, telegram_token=self.telegram_token, - spin_wheel_of_fortune=self.spin_wheel_of_fortune) + return dict( + email=self.email, + work=self.work, + train=self.train, + wam=self.wam, + ot=self.ot, + auto_sell=self.auto_sell, + auto_sell_all=self.auto_sell_all, + employees=self.employees, + fight=self.fight, + air=self.air, + ground=self.ground, + all_in=self.all_in, + next_energy=self.next_energy, + travel_to_fight=self.travel_to_fight, + always_travel=self.always_travel, + epic_hunt=self.epic_hunt, + epic_hunt_ebs=self.epic_hunt_ebs, + rw_def_side=self.rw_def_side, + interactive=self.interactive, + maverick=self.maverick, + continuous_fighting=self.continuous_fighting, + auto_buy_raw=self.auto_buy_raw, + force_wam=self.force_wam, + sort_battles_time=self.sort_battles_time, + force_travel=self.force_travel, + telegram=self.telegram, + telegram_chat_id=self.telegram_chat_id, + telegram_token=self.telegram_token, + spin_wheel_of_fortune=self.spin_wheel_of_fortune, + ) class Energy: @@ -434,12 +505,12 @@ class Energy: @property def recovered(self): - warnings.warn('Deprecated since auto auto-eat! Will be removed soon. Use Energy.energy', DeprecationWarning) + warnings.warn("Deprecated since auto auto-eat! Will be removed soon. Use Energy.energy", DeprecationWarning) return self.energy @property def recoverable(self): - warnings.warn('Deprecated since auto auto-eat! Will be removed soon. Use Energy.energy', DeprecationWarning) + warnings.warn("Deprecated since auto auto-eat! Will be removed soon. Use Energy.energy", DeprecationWarning) return 0 def set_reference_time(self, recovery_time: datetime.datetime): @@ -459,12 +530,12 @@ class Energy: @property def is_recoverable_full(self): - warnings.warn('Deprecated since auto auto-eat! Will be removed soon. Use Energy.is_energy_full', DeprecationWarning) + warnings.warn("Deprecated since auto auto-eat! Will be removed soon. Use Energy.is_energy_full", DeprecationWarning) return self.is_energy_full @property def is_recovered_full(self): - warnings.warn('Deprecated since auto auto-eat! Will be removed soon. Use Energy.is_energy_full', DeprecationWarning) + warnings.warn("Deprecated since auto auto-eat! Will be removed soon. Use Energy.is_energy_full", DeprecationWarning) return self.is_energy_full @property @@ -473,14 +544,19 @@ class Energy: @property def available(self): - warnings.warn('Deprecated since auto auto-eat! Will be removed soon. Use Energy.energy', DeprecationWarning) + warnings.warn("Deprecated since auto auto-eat! Will be removed soon. Use Energy.energy", DeprecationWarning) return self.energy @property def as_dict(self) -> Dict[str, Union[int, datetime.datetime, bool]]: - return dict(limit=self.limit, interval=self.interval, energy=self.energy, - reference_time=self.reference_time, food_fights=self.food_fights, - is_energy_full=self.is_energy_full) + return dict( + limit=self.limit, + interval=self.interval, + energy=self.energy, + reference_time=self.reference_time, + food_fights=self.food_fights, + is_energy_full=self.is_energy_full, + ) class Details: @@ -504,7 +580,7 @@ class Details: def __init__(self): self.next_pp = [] self.mayhem_skills = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0, 14: 0} - _default_country = constants.Country(0, 'Unknown', 'Unknown', 'XX') + _default_country = constants.Country(0, "Unknown", "Unknown", "XX") self.citizenship = self.current_country = self.residence_country = _default_country @property @@ -535,12 +611,25 @@ class Details: @property def as_dict(self) -> Dict[str, Union[int, float, str, constants.Country, bool]]: - return dict(xp=self.xp, cc=self.cc, pp=self.pp, pin=self.pin, gold=self.gold, next_pp=self.next_pp, - level=self.level, citizen_id=self.citizen_id, citizenship=self.citizenship, - current_region=self.current_region, current_country=self.current_country, - residence_region=self.residence_region, residence_country=self.residence_country, - daily_task_done=self.daily_task_done, daily_task_reward=self.daily_task_reward, - mayhem_skills=self.mayhem_skills, xp_till_level_up=self.xp_till_level_up) + return dict( + xp=self.xp, + cc=self.cc, + pp=self.pp, + pin=self.pin, + gold=self.gold, + next_pp=self.next_pp, + level=self.level, + citizen_id=self.citizen_id, + citizenship=self.citizenship, + current_region=self.current_region, + current_country=self.current_country, + residence_region=self.residence_region, + residence_country=self.residence_country, + daily_task_done=self.daily_task_done, + daily_task_reward=self.daily_task_reward, + mayhem_skills=self.mayhem_skills, + xp_till_level_up=self.xp_till_level_up, + ) @property def is_elite(self): @@ -558,9 +647,14 @@ class Politics: @property def as_dict(self) -> Dict[str, Union[bool, int, str]]: - return dict(is_party_member=self.is_party_member, party_id=self.party_id, party_slug=self.party_slug, - is_party_president=self.is_party_president, is_congressman=self.is_congressman, - is_country_president=self.is_country_president) + return dict( + is_party_member=self.is_party_member, + party_id=self.party_id, + party_slug=self.party_slug, + is_party_president=self.is_party_president, + is_congressman=self.is_congressman, + is_country_president=self.is_country_president, + ) class House: @@ -596,17 +690,22 @@ class Reporter: @property def as_dict(self) -> Dict[str, Union[bool, int, str, List[Dict[Any, Any]]]]: - return dict(name=self.name, email=self.email, citizen_id=self.citizen_id, key=self.key, allowed=self.allowed, - queue=self.__to_update) + return dict( + name=self.name, email=self.email, citizen_id=self.citizen_id, key=self.key, allowed=self.allowed, queue=self.__to_update + ) def __init__(self, citizen): self._citizen = weakref.ref(citizen) self._req = Session() self.url = "https://api.erep.lv" - self._req.headers.update({"user-agent": 'eRepublik Script Reporter v3', - 'erep-version': utils.__version__, - 'erep-user-id': str(self.citizen_id), - 'erep-user-name': self.citizen.name}) + self._req.headers.update( + { + "user-agent": "eRepublik Script Reporter v3", + "erep-version": utils.__version__, + "erep-user-id": str(self.citizen_id), + "erep-user-name": self.citizen.name, + } + ) self.__to_update = [] self.__registered: bool = False @@ -651,67 +750,102 @@ class Reporter: if not self.__registered: r = self.__bot_update(dict(key=self.key, check=True, player_id=self.citizen_id)) if r: - if not r.json().get('status'): - self._req.post(f"{self.url}/bot/register", json=dict(name=self.name, email=self.email, - player_id=self.citizen_id)) + if not r.json().get("status"): + self._req.post(f"{self.url}/bot/register", json=dict(name=self.name, email=self.email, player_id=self.citizen_id)) self.__registered = True self.allowed = True - self.report_action('STARTED', value=utils.now().strftime("%F %T")) + self.report_action("STARTED", value=utils.now().strftime("%F %T")) - def send_state_update(self, xp: int, cc: float, gold: float, inv_total: int, inv: int, - hp_limit: int, hp_interval: int, hp_available: int, food: int, pp: int): + def send_state_update( + self, + xp: int, + cc: float, + gold: float, + inv_total: int, + inv: int, + hp_limit: int, + hp_interval: int, + hp_available: int, + food: int, + pp: int, + ): - data = dict(key=self.key, player_id=self.citizen_id, state=dict( - xp=xp, cc=cc, gold=gold, inv_total=inv_total, inv_free=inv_total - inv, inv=inv, food=food, - pp=pp, hp_limit=hp_limit, hp_interval=hp_interval, hp_available=hp_available, - )) + data = dict( + key=self.key, + player_id=self.citizen_id, + state=dict( + xp=xp, + cc=cc, + gold=gold, + inv_total=inv_total, + inv_free=inv_total - inv, + inv=inv, + food=food, + pp=pp, + hp_limit=hp_limit, + hp_interval=hp_interval, + hp_available=hp_available, + ), + ) self._bot_update(data) def report_action(self, action: str, json_val: Dict[Any, Any] = None, value: str = None): - json_data = dict( - player_id=getattr(self, 'citizen_id', None), log={'action': action}, key=getattr(self, 'key', None) - ) + json_data = dict(player_id=getattr(self, "citizen_id", None), log={"action": action}, key=getattr(self, "key", None)) if json_val: - json_data['log'].update(dict(json=json_val)) + json_data["log"].update(dict(json=json_val)) if value: - json_data['log'].update(dict(value=value)) + json_data["log"].update(dict(value=value)) if not any([self.key, self.email, self.name, self.citizen_id]): return self._bot_update(json_data) - def report_fighting(self, battle: 'Battle', invader: bool, division: 'BattleDivision', damage: float, hits: int): + def report_fighting(self, battle: "Battle", invader: bool, division: "BattleDivision", damage: float, hits: int): side = battle.invader if invader else battle.defender - self.report_action('FIGHT', dict(battle_id=battle.id, side=side, dmg=damage, - air=battle.has_air, hits=hits, - round=battle.zone_id, extra=dict(battle=battle, side=side, division=division))) + self.report_action( + "FIGHT", + dict( + battle_id=battle.id, + side=side, + dmg=damage, + air=battle.has_air, + hits=hits, + round=battle.zone_id, + extra=dict(battle=battle, side=side, division=division), + ), + ) def report_money_donation(self, citizen_id: int, amount: float, is_currency: bool = True): - cur = 'cc' if is_currency else 'gold' - self.report_action('DONATE_MONEY', dict(citizen_id=citizen_id, amount=amount, currency=cur), - f"Successfully donated {amount}{cur} to citizen with id {citizen_id}!") + cur = "cc" if is_currency else "gold" + self.report_action( + "DONATE_MONEY", + dict(citizen_id=citizen_id, amount=amount, currency=cur), + f"Successfully donated {amount}{cur} to citizen with id {citizen_id}!", + ) def report_item_donation(self, citizen_id: int, amount: float, quality: int, industry: str): - self.report_action('DONATE_ITEMS', - dict(citizen_id=citizen_id, amount=amount, quality=quality, industry=industry), - f"Successfully donated {amount} x {industry} q{quality} to citizen with id {citizen_id}!") + self.report_action( + "DONATE_ITEMS", + dict(citizen_id=citizen_id, amount=amount, quality=quality, industry=industry), + f"Successfully donated {amount} x {industry} q{quality} to citizen with id {citizen_id}!", + ) def report_promo(self, kind: str, time_until: datetime.datetime): self._req.post(f"{self.url}/promos/add/", data=dict(kind=kind, time_untill=time_until)) - def fetch_battle_priorities(self, country: constants.Country) -> List['Battle']: + def fetch_battle_priorities(self, country: constants.Country) -> List["Battle"]: try: - battle_response = self._req.get(f'{self.url}/api/v1/battles/{country.id}') - return [self.citizen.all_battles[bid] for bid in battle_response.json().get('battle_ids', []) if - bid in self.citizen.all_battles] + battle_response = self._req.get(f"{self.url}/api/v1/battles/{country.id}") + return [ + self.citizen.all_battles[bid] for bid in battle_response.json().get("battle_ids", []) if bid in self.citizen.all_battles + ] except: # noqa return [] def fetch_tasks(self) -> List[Dict[str, Any]]: try: - task_response = self._req.post( - f'{self.url}/api/v1/command', data=dict(citizen=self.citizen_id, key=self.key)).json() - if task_response.get('status'): - return task_response.get('data') + task_response = self._req.post(f"{self.url}/api/v1/command", data=dict(citizen=self.citizen_id, key=self.key)).json() + if task_response.get("status"): + return task_response.get("data") else: return [] except: # noqa @@ -722,13 +856,20 @@ class BattleSide: points: int deployed: List[constants.Country] allies: List[constants.Country] - battle: 'Battle' + battle: "Battle" _battle: weakref.ReferenceType country: constants.Country is_defender: bool - def __init__(self, battle: 'Battle', country: constants.Country, points: int, allies: List[constants.Country], - deployed: List[constants.Country], defender: bool): + def __init__( + self, + battle: "Battle", + country: constants.Country, + points: int, + allies: List[constants.Country], + deployed: List[constants.Country], + defender: bool, + ): self._battle = weakref.ref(battle) self.country = country self.points = points @@ -741,11 +882,11 @@ class BattleSide: return self.country.id def __repr__(self): - side_text = 'Defender' if self.is_defender else 'Invader ' + side_text = "Defender" if self.is_defender else "Invader " return f"2d}p>" def __str__(self): - side_text = 'Defender' if self.is_defender else 'Invader ' + side_text = "Defender" if self.is_defender else "Invader " return f"{side_text} {self.country.name} - {self.points:>2d} points" def __format__(self, format_spec): @@ -753,8 +894,7 @@ class BattleSide: @property def as_dict(self) -> Dict[str, Union[int, constants.Country, bool, List[constants.Country]]]: - return dict(points=self.points, country=self.country, is_defender=self.is_defender, allies=self.allies, - deployed=self.deployed) + return dict(points=self.points, country=self.country, is_defender=self.is_defender, allies=self.allies, deployed=self.deployed) @property def battle(self): @@ -771,13 +911,14 @@ class BattleDivision: inv_medal: Dict[str, int] terrain: int div: int - battle: 'Battle' + battle: "Battle" _battle: weakref.ReferenceType @property def as_dict(self): - return dict(id=self.id, division=self.div, terrain=(self.terrain, self.terrain_display), wall=self.wall, - epic=self.epic, end=self.div_end) + return dict( + id=self.id, division=self.div, terrain=(self.terrain, self.terrain_display), wall=self.wall, epic=self.epic, end=self.div_end + ) @property def is_air(self): @@ -787,8 +928,17 @@ class BattleDivision: def div_end(self) -> bool: return utils.now() >= self.end - def __init__(self, battle: 'Battle', div_id: int, end: datetime.datetime, epic: bool, div: int, wall_for: int, - wall_dom: float, terrain_id: int = 0): + def __init__( + self, + battle: "Battle", + div_id: int, + end: datetime.datetime, + epic: bool, + div: int, + wall_for: int, + wall_dom: float, + terrain_id: int = 0, + ): """Battle division helper class :type div_id: int @@ -803,7 +953,7 @@ class BattleDivision: self.id = div_id self.end = end self.epic = epic - self.wall = {'for': wall_for, 'dom': wall_dom} + self.wall = {"for": wall_for, "dom": wall_dom} self.terrain = terrain_id self.div = div @@ -816,7 +966,7 @@ class BattleDivision: if self.terrain: base_name += f" ({self.terrain_display})" if self.div_end: - base_name += ' Ended' + base_name += " Ended" return base_name def __repr__(self): @@ -842,9 +992,18 @@ class Battle: @property def as_dict(self): - return dict(id=self.id, war_id=self.war_id, divisions=self.div, zone=self.zone_id, rw=self.is_rw, - dict_lib=self.is_dict_lib, start=self.start, sides={'inv': self.invader, 'def': self.defender}, - region=[self.region_id, self.region_name], link=self.link) + return dict( + id=self.id, + war_id=self.war_id, + divisions=self.div, + zone=self.zone_id, + rw=self.is_rw, + dict_lib=self.is_dict_lib, + start=self.start, + sides={"inv": self.invader, "def": self.defender}, + region=[self.region_id, self.region_name], + link=self.link, + ) @property def has_air(self) -> bool: @@ -874,43 +1033,52 @@ class Battle: :param battle: Dict object for single battle from '/military/campaignsJson/list' response's 'battles' object """ - self.id = int(battle.get('id')) - self.war_id = int(battle.get('war_id')) - self.zone_id = int(battle.get('zone_id')) - self.is_rw = bool(battle.get('is_rw')) - self.is_as = bool(battle.get('is_as')) - self.is_dict_lib = bool(battle.get('is_dict')) or bool(battle.get('is_lib')) - self.region_id = battle.get('region', {}).get('id') - self.region_name = battle.get('region', {}).get('name') - self.start = datetime.datetime.fromtimestamp(int(battle.get('start', 0)), tz=constants.erep_tz) + self.id = int(battle.get("id")) + self.war_id = int(battle.get("war_id")) + self.zone_id = int(battle.get("zone_id")) + self.is_rw = bool(battle.get("is_rw")) + self.is_as = bool(battle.get("is_as")) + self.is_dict_lib = bool(battle.get("is_dict")) or bool(battle.get("is_lib")) + self.region_id = battle.get("region", {}).get("id") + self.region_name = battle.get("region", {}).get("name") + self.start = datetime.datetime.fromtimestamp(int(battle.get("start", 0)), tz=constants.erep_tz) self.invader = BattleSide( - self, constants.COUNTRIES[battle.get('inv', {}).get('id')], battle.get('inv', {}).get('points'), - [constants.COUNTRIES[row.get('id')] for row in battle.get('inv', {}).get('ally_list')], - [constants.COUNTRIES[row.get('id')] for row in battle.get('inv', {}).get('ally_list') if row['deployed']], - False + self, + constants.COUNTRIES[battle.get("inv", {}).get("id")], + battle.get("inv", {}).get("points"), + [constants.COUNTRIES[row.get("id")] for row in battle.get("inv", {}).get("ally_list")], + [constants.COUNTRIES[row.get("id")] for row in battle.get("inv", {}).get("ally_list") if row["deployed"]], + False, ) self.defender = BattleSide( - self, constants.COUNTRIES[battle.get('def', {}).get('id')], battle.get('def', {}).get('points'), - [constants.COUNTRIES[row.get('id')] for row in battle.get('def', {}).get('ally_list')], - [constants.COUNTRIES[row.get('id')] for row in battle.get('def', {}).get('ally_list') if row['deployed']], - True + self, + constants.COUNTRIES[battle.get("def", {}).get("id")], + battle.get("def", {}).get("points"), + [constants.COUNTRIES[row.get("id")] for row in battle.get("def", {}).get("ally_list")], + [constants.COUNTRIES[row.get("id")] for row in battle.get("def", {}).get("ally_list") if row["deployed"]], + True, ) self.div = {} - for div, data in battle.get('div', {}).items(): + for div, data in battle.get("div", {}).items(): div = int(div) - if data.get('end'): - end = datetime.datetime.fromtimestamp(data.get('end'), tz=constants.erep_tz) + if data.get("end"): + end = datetime.datetime.fromtimestamp(data.get("end"), tz=constants.erep_tz) else: end = constants.max_datetime - battle_div = BattleDivision(self, div_id=data.get('id'), div=data.get('div'), end=end, - epic=data.get('epic_type') in [1, 5], - wall_for=data.get('wall').get('for'), - wall_dom=data.get('wall').get('dom'), - terrain_id=data.get('terrain', 0)) + battle_div = BattleDivision( + self, + div_id=data.get("id"), + div=data.get("div"), + end=end, + epic=data.get("epic_type") in [1, 5], + wall_for=data.get("wall").get("for"), + wall_dom=data.get("wall").get("dom"), + terrain_id=data.get("terrain", 0), + ) self.div.update({div: battle_div}) @@ -922,8 +1090,10 @@ class Battle: else: time_part = f"-{self.start - time_now}" - return (f"Battle {self.id} for {self.region_name[:16]:16} | " - f"{self.invader} : {self.defender} | Round time {time_part} | {'R'+str(self.zone_id):>3}") + return ( + f"Battle {self.id} for {self.region_name[:16]:16} | " + f"{self.invader} : {self.defender} | Round time {time_part} | {'R'+str(self.zone_id):>3}" + ) def __repr__(self): return f"" @@ -981,9 +1151,16 @@ class TelegramReporter: @property def as_dict(self): - return {'chat_id': self.chat_id, 'api_url': self.api_url, 'player': self.player_name, - 'last_time': self._last_time, 'next_time': self._next_time, 'queue': self.__queue, - 'initialized': self.__initialized, 'has_threads': not self._threads} + return { + "chat_id": self.chat_id, + "api_url": self.api_url, + "player": self.player_name, + "last_time": self._last_time, + "next_time": self._next_time, + "queue": self.__queue, + "initialized": self.__initialized, + "has_threads": not self._threads, + } def do_init(self, chat_id: int, token: str = None, player_name: str = None): if token is None: @@ -995,7 +1172,7 @@ class TelegramReporter: self._last_time = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=-5)) self._last_full_energy_report = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=-30)) if self.__queue: - self.send_message('Telegram initialized') + self.send_message("Telegram initialized") def send_message(self, message: str) -> bool: self.__queue.append(message) @@ -1020,22 +1197,27 @@ class TelegramReporter: self.send_message(message) def report_medal(self, msg, multiple: bool = True): - new_line = '\n' if multiple else '' + new_line = "\n" if multiple else "" self.send_message(f"New award: {new_line}*{msg}*") - def report_fight(self, battle: 'Battle', invader: bool, division: 'BattleDivision', damage: float, hits: int): + def report_fight(self, battle: "Battle", invader: bool, division: "BattleDivision", damage: float, hits: int): side_txt = (battle.invader if invader else battle.defender).country.iso - self.send_message(f"*Fight report*:\n{int(damage):,d} dmg ({hits} hits) in" - f" [battle {battle.id} for {battle.region_name[:16]}]({battle.link}) in d{division.div} on " - f"{side_txt} side") + self.send_message( + f"*Fight report*:\n{int(damage):,d} dmg ({hits} hits) in" + f" [battle {battle.id} for {battle.region_name[:16]}]({battle.link}) in d{division.div} on " + f"{side_txt} side" + ) def report_item_donation(self, citizen_id: int, amount: float, product: str): - self.send_message(f"*Donation*: {amount} x {product} to citizen " - f"[{citizen_id}](https://www.erepublik.com/en/citizen/profile/{citizen_id})") + self.send_message( + f"*Donation*: {amount} x {product} to citizen " f"[{citizen_id}](https://www.erepublik.com/en/citizen/profile/{citizen_id})" + ) def report_money_donation(self, citizen_id: int, amount: float, is_currency: bool = True): - self.send_message(f"*Donation*: {amount}{'cc' if is_currency else 'gold'} to citizen " - f"[{citizen_id}](https://www.erepublik.com/en/citizen/profile/{citizen_id})") + self.send_message( + f"*Donation*: {amount}{'cc' if is_currency else 'gold'} to citizen " + f"[{citizen_id}](https://www.erepublik.com/en/citizen/profile/{citizen_id})" + ) def __send_messages(self): while self._next_time > utils.now(): @@ -1046,9 +1228,9 @@ class TelegramReporter: message = "\n\n".join(self.__queue) if self.player_name: message = f"Player *{self.player_name}*\n\n" + message - response = post(f"{self.api_url}/sendMessage", json=dict(chat_id=self.chat_id, text=message, parse_mode='Markdown')) + response = post(f"{self.api_url}/sendMessage", json=dict(chat_id=self.chat_id, text=message, parse_mode="Markdown")) self._last_time = utils.now() - if response.json().get('ok'): + if response.json().get("ok"): self.__queue.clear() return True return False @@ -1056,14 +1238,16 @@ class TelegramReporter: def send_photos(self, photos: List[Tuple[str, BytesIO]]): for photo_title, photo in photos: photo.seek(0) - post(f"https://{self.api_url}/sendPhoto", - data=dict(chat_id=self.chat_id, caption=photo_title), - files=[('photo', ("f{utils.slugify(photo_title)}.png", photo))]) + post( + f"https://{self.api_url}/sendPhoto", + data=dict(chat_id=self.chat_id, caption=photo_title), + files=[("photo", ("f{utils.slugify(photo_title)}.png", photo))], + ) return class OfferItem(NamedTuple): - price: float = 999_999_999. + price: float = 999_999_999.0 country: constants.Country = constants.Country(0, "", "", "") amount: int = 0 offer_id: int = 0 @@ -1090,5 +1274,6 @@ class Inventory: @property def as_dict(self) -> Dict[str, Union[types.InvFinal, types.InvRaw, int]]: - return dict(active=self.active, final=self.final, boosters=self.boosters, raw=self.raw, offers=self.offers, - total=self.total, used=self.used) + return dict( + active=self.active, final=self.final, boosters=self.boosters, raw=self.raw, offers=self.offers, total=self.total, used=self.used + ) diff --git a/erepublik/constants.py b/erepublik/constants.py index 575cb63..4893ba5 100644 --- a/erepublik/constants.py +++ b/erepublik/constants.py @@ -3,10 +3,21 @@ from typing import Dict, Optional, Union import pytz -__all__ = ['erep_tz', 'min_datetime', 'max_datetime', 'Country', 'AIR_RANKS', 'COUNTRIES', 'FOOD_ENERGY', - 'GROUND_RANKS', 'GROUND_RANK_POINTS', 'INDUSTRIES', 'TERRAINS'] +__all__ = [ + "erep_tz", + "min_datetime", + "max_datetime", + "Country", + "AIR_RANKS", + "COUNTRIES", + "FOOD_ENERGY", + "GROUND_RANKS", + "GROUND_RANK_POINTS", + "INDUSTRIES", + "TERRAINS", +] -erep_tz = pytz.timezone('US/Pacific') +erep_tz = pytz.timezone("US/Pacific") min_datetime = erep_tz.localize(datetime.datetime(2007, 11, 20)) max_datetime = erep_tz.localize(datetime.datetime(2281, 9, 4)) @@ -53,18 +64,70 @@ class Country: class Industries: - __by_name = {'food': 1, 'weapon': 2, 'ticket': 3, 'house': 4, 'aircraft': 23, - 'foodraw': 7, 'weaponraw': 12, 'houseraw': 18, 'aircraftraw': 24, 'airplaneraw': 24, - 'frm': 7, 'wrm': 12, 'hrm': 18, 'arm': 24, - 'frm q1': 7, 'frm q2': 8, 'frm q3': 9, 'frm q4': 10, 'frm q5': 11, - 'wrm q1': 12, 'wrm q2': 13, 'wrm q3': 14, 'wrm q4': 15, 'wrm q5': 16, - 'hrm q1': 18, 'hrm q2': 19, 'hrm q3': 20, 'hrm q4': 21, 'hrm q5': 22, - 'arm q1': 24, 'arm q2': 25, 'arm q3': 26, 'arm q4': 27, 'arm q5': 28} - __by_id = {1: 'Food', 2: 'Weapon', 3: 'Ticket', 4: 'House', 23: 'Aircraft', - 7: 'foodRaw', 8: 'FRM q2', 9: 'FRM q3', 10: 'FRM q4', 11: 'FRM q5', - 12: 'weaponRaw', 13: 'WRM q2', 14: 'WRM q3', 15: 'WRM q4', 16: 'WRM q5', - 17: 'houseRaw', 18: 'houseRaw', 19: 'HRM q2', 20: 'HRM q3', 21: 'HRM q4', 22: 'HRM q5', - 24: 'aircraftRaw', 25: 'ARM q2', 26: 'ARM q3', 27: 'ARM q4', 28: 'ARM q5'} + __by_name = { + "food": 1, + "weapon": 2, + "ticket": 3, + "house": 4, + "aircraft": 23, + "foodraw": 7, + "weaponraw": 12, + "houseraw": 18, + "aircraftraw": 24, + "airplaneraw": 24, + "frm": 7, + "wrm": 12, + "hrm": 18, + "arm": 24, + "frm q1": 7, + "frm q2": 8, + "frm q3": 9, + "frm q4": 10, + "frm q5": 11, + "wrm q1": 12, + "wrm q2": 13, + "wrm q3": 14, + "wrm q4": 15, + "wrm q5": 16, + "hrm q1": 18, + "hrm q2": 19, + "hrm q3": 20, + "hrm q4": 21, + "hrm q5": 22, + "arm q1": 24, + "arm q2": 25, + "arm q3": 26, + "arm q4": 27, + "arm q5": 28, + } + __by_id = { + 1: "Food", + 2: "Weapon", + 3: "Ticket", + 4: "House", + 23: "Aircraft", + 7: "foodRaw", + 8: "FRM q2", + 9: "FRM q3", + 10: "FRM q4", + 11: "FRM q5", + 12: "weaponRaw", + 13: "WRM q2", + 14: "WRM q3", + 15: "WRM q4", + 16: "WRM q5", + 17: "houseRaw", + 18: "houseRaw", + 19: "HRM q2", + 20: "HRM q3", + 21: "HRM q4", + 22: "HRM q5", + 24: "aircraftRaw", + 25: "ARM q2", + 26: "ARM q3", + 27: "ARM q4", + 28: "ARM q5", + } def __getitem__(self, item) -> Optional[Union[int, str]]: if isinstance(item, int): @@ -144,110 +207,452 @@ class Rank: AIR_RANK_NAMES: Dict[int, str] = { - 1: 'Airman', 2: 'Airman 1st Class', 3: 'Airman 1st Class*', 4: 'Airman 1st Class**', 5: 'Airman 1st Class***', 6: 'Airman 1st Class****', 7: 'Airman 1st Class*****', - 8: 'Senior Airman', 9: 'Senior Airman*', 10: 'Senior Airman**', 11: 'Senior Airman***', 12: 'Senior Airman****', 13: 'Senior Airman*****', - 14: 'Staff Sergeant', 15: 'Staff Sergeant*', 16: 'Staff Sergeant**', 17: 'Staff Sergeant***', 18: 'Staff Sergeant****', 19: 'Staff Sergeant*****', - 20: 'Aviator', 21: 'Aviator*', 22: 'Aviator**', 23: 'Aviator***', 24: 'Aviator****', 25: 'Aviator*****', - 26: 'Flight Lieutenant', 27: 'Flight Lieutenant*', 28: 'Flight Lieutenant**', 29: 'Flight Lieutenant***', 30: 'Flight Lieutenant****', 31: 'Flight Lieutenant*****', - 32: 'Squadron Leader', 33: 'Squadron Leader*', 34: 'Squadron Leader**', 35: 'Squadron Leader***', 36: 'Squadron Leader****', 37: 'Squadron Leader*****', - 38: 'Chief Master Sergeant', 39: 'Chief Master Sergeant*', 40: 'Chief Master Sergeant**', 41: 'Chief Master Sergeant***', 42: 'Chief Master Sergeant****', 43: 'Chief Master Sergeant*****', - 44: 'Wing Commander', 45: 'Wing Commander*', 46: 'Wing Commander**', 47: 'Wing Commander***', 48: 'Wing Commander****', 49: 'Wing Commander*****', - 50: 'Group Captain', 51: 'Group Captain*', 52: 'Group Captain**', 53: 'Group Captain***', 54: 'Group Captain****', 55: 'Group Captain*****', - 56: 'Air Commodore', 57: 'Air Commodore*', 58: 'Air Commodore**', 59: 'Air Commodore***', 60: 'Air Commodore****', 61: 'Air Commodore*****', - 62: 'Air Vice Marshal', 63: 'Air Vice Marshal*', 64: 'Air Vice Marshal**', 65: 'Air Vice Marshal***', 66: 'Air Vice Marshal****', 67: 'Air Vice Marshal*****', - 68: 'Air Marshal', 69: 'Air Marshal*', 70: 'Air Marshal**', 71: 'Air Marshal***', 72: 'Air Marshal****', 73: 'Air Marshal*****', - 74: 'Air Chief Marshal', 75: 'Air Chief Marshal*', 76: 'Air Chief Marshal**', 77: 'Air Chief Marshal***', 78: 'Air Chief Marshal****', 79: 'Air Chief Marshal*****', + 1: "Airman", + 2: "Airman 1st Class", + 3: "Airman 1st Class*", + 4: "Airman 1st Class**", + 5: "Airman 1st Class***", + 6: "Airman 1st Class****", + 7: "Airman 1st Class*****", + 8: "Senior Airman", + 9: "Senior Airman*", + 10: "Senior Airman**", + 11: "Senior Airman***", + 12: "Senior Airman****", + 13: "Senior Airman*****", + 14: "Staff Sergeant", + 15: "Staff Sergeant*", + 16: "Staff Sergeant**", + 17: "Staff Sergeant***", + 18: "Staff Sergeant****", + 19: "Staff Sergeant*****", + 20: "Aviator", + 21: "Aviator*", + 22: "Aviator**", + 23: "Aviator***", + 24: "Aviator****", + 25: "Aviator*****", + 26: "Flight Lieutenant", + 27: "Flight Lieutenant*", + 28: "Flight Lieutenant**", + 29: "Flight Lieutenant***", + 30: "Flight Lieutenant****", + 31: "Flight Lieutenant*****", + 32: "Squadron Leader", + 33: "Squadron Leader*", + 34: "Squadron Leader**", + 35: "Squadron Leader***", + 36: "Squadron Leader****", + 37: "Squadron Leader*****", + 38: "Chief Master Sergeant", + 39: "Chief Master Sergeant*", + 40: "Chief Master Sergeant**", + 41: "Chief Master Sergeant***", + 42: "Chief Master Sergeant****", + 43: "Chief Master Sergeant*****", + 44: "Wing Commander", + 45: "Wing Commander*", + 46: "Wing Commander**", + 47: "Wing Commander***", + 48: "Wing Commander****", + 49: "Wing Commander*****", + 50: "Group Captain", + 51: "Group Captain*", + 52: "Group Captain**", + 53: "Group Captain***", + 54: "Group Captain****", + 55: "Group Captain*****", + 56: "Air Commodore", + 57: "Air Commodore*", + 58: "Air Commodore**", + 59: "Air Commodore***", + 60: "Air Commodore****", + 61: "Air Commodore*****", + 62: "Air Vice Marshal", + 63: "Air Vice Marshal*", + 64: "Air Vice Marshal**", + 65: "Air Vice Marshal***", + 66: "Air Vice Marshal****", + 67: "Air Vice Marshal*****", + 68: "Air Marshal", + 69: "Air Marshal*", + 70: "Air Marshal**", + 71: "Air Marshal***", + 72: "Air Marshal****", + 73: "Air Marshal*****", + 74: "Air Chief Marshal", + 75: "Air Chief Marshal*", + 76: "Air Chief Marshal**", + 77: "Air Chief Marshal***", + 78: "Air Chief Marshal****", + 79: "Air Chief Marshal*****", } AIR_RANK_POINTS: Dict[int, Optional[int]] = { - 1: 0, 2: 10, 3: 25, 4: 45, 5: 70, 6: 100, 7: 140, 8: 190, 9: 270, 10: 380, 11: 530, 12: 850, 13: 1300, 14: 2340, 15: 3300, 16: 4200, 17: 5150, 18: 6100, 19: 7020, 20: 9100, 21: 12750, 22: 16400, 23: 20000, 24: 23650, 25: 27300, - 26: 35500, 27: 48000, 28: 60000, 29: 72400, 30: 84500, 31: 97000, 32: 110000, 33: 140000, 34: 170000, 35: 210000, 36: 290000, 37: 350000, 38: 429000, 39: 601000, 40: 772000, 41: 944000, 42: 1115000, 43: 1287000, - 44: 1673000, 45: 2238000, 46: 2804000, 47: 3369000, 48: 3935000, 49: 4500000, 50: 5020000, 51: 7028000, 52: 9036000, 53: 11044000, 54: 13052000, 55: 15060000, - 56: 19580000, 57: 27412000, 58: 35244000, 59: 43076000, 60: 50908000, 61: 58740000, 62: 76360000, 63: 113166443, 64: 137448000, 65: None, 66: None, 67: None, - 68: None, 69: None, 70: None, 71: None, 72: None, 73: None, 74: None, 75: None, 76: None, 77: None, 78: None, 79: None, + 1: 0, + 2: 10, + 3: 25, + 4: 45, + 5: 70, + 6: 100, + 7: 140, + 8: 190, + 9: 270, + 10: 380, + 11: 530, + 12: 850, + 13: 1300, + 14: 2340, + 15: 3300, + 16: 4200, + 17: 5150, + 18: 6100, + 19: 7020, + 20: 9100, + 21: 12750, + 22: 16400, + 23: 20000, + 24: 23650, + 25: 27300, + 26: 35500, + 27: 48000, + 28: 60000, + 29: 72400, + 30: 84500, + 31: 97000, + 32: 110000, + 33: 140000, + 34: 170000, + 35: 210000, + 36: 290000, + 37: 350000, + 38: 429000, + 39: 601000, + 40: 772000, + 41: 944000, + 42: 1115000, + 43: 1287000, + 44: 1673000, + 45: 2238000, + 46: 2804000, + 47: 3369000, + 48: 3935000, + 49: 4500000, + 50: 5020000, + 51: 7028000, + 52: 9036000, + 53: 11044000, + 54: 13052000, + 55: 15060000, + 56: 19580000, + 57: 27412000, + 58: 35244000, + 59: 43076000, + 60: 50908000, + 61: 58740000, + 62: 76360000, + 63: 113166443, + 64: 137448000, + 65: None, + 66: None, + 67: None, + 68: None, + 69: None, + 70: None, + 71: None, + 72: None, + 73: None, + 74: None, + 75: None, + 76: None, + 77: None, + 78: None, + 79: None, } AIR_RANKS: Dict[int, Rank] = {i: Rank(i, AIR_RANK_NAMES[i], AIR_RANK_POINTS[i], True) for i in range(1, 80)} COUNTRIES: Dict[int, Country] = { - 1: Country(1, 'Romania', 'Romania', 'ROU'), 9: Country(9, 'Brazil', 'Brazil', 'BRA'), - 10: Country(10, 'Italy', 'Italy', 'ITA'), 11: Country(11, 'France', 'France', 'FRA'), - 12: Country(12, 'Germany', 'Germany', 'DEU'), 13: Country(13, 'Hungary', 'Hungary', 'HUN'), - 14: Country(14, 'China', 'China', 'CHN'), 15: Country(15, 'Spain', 'Spain', 'ESP'), - 23: Country(23, 'Canada', 'Canada', 'CAN'), 24: Country(24, 'USA', 'USA', 'USA'), - 26: Country(26, 'Mexico', 'Mexico', 'MEX'), 27: Country(27, 'Argentina', 'Argentina', 'ARG'), - 28: Country(28, 'Venezuela', 'Venezuela', 'VEN'), 29: Country(29, 'United Kingdom', 'United-Kingdom', 'GBR'), - 30: Country(30, 'Switzerland', 'Switzerland', 'CHE'), 31: Country(31, 'Netherlands', 'Netherlands', 'NLD'), - 32: Country(32, 'Belgium', 'Belgium', 'BEL'), 33: Country(33, 'Austria', 'Austria', 'AUT'), - 34: Country(34, 'Czech Republic', 'Czech-Republic', 'CZE'), 35: Country(35, 'Poland', 'Poland', 'POL'), - 36: Country(36, 'Slovakia', 'Slovakia', 'SVK'), 37: Country(37, 'Norway', 'Norway', 'NOR'), - 38: Country(38, 'Sweden', 'Sweden', 'SWE'), 39: Country(39, 'Finland', 'Finland', 'FIN'), - 40: Country(40, 'Ukraine', 'Ukraine', 'UKR'), 41: Country(41, 'Russia', 'Russia', 'RUS'), - 42: Country(42, 'Bulgaria', 'Bulgaria', 'BGR'), 43: Country(43, 'Turkey', 'Turkey', 'TUR'), - 44: Country(44, 'Greece', 'Greece', 'GRC'), 45: Country(45, 'Japan', 'Japan', 'JPN'), - 47: Country(47, 'South Korea', 'South-Korea', 'KOR'), 48: Country(48, 'India', 'India', 'IND'), - 49: Country(49, 'Indonesia', 'Indonesia', 'IDN'), 50: Country(50, 'Australia', 'Australia', 'AUS'), - 51: Country(51, 'South Africa', 'South-Africa', 'ZAF'), - 52: Country(52, 'Republic of Moldova', 'Republic-of-Moldova', 'MDA'), - 53: Country(53, 'Portugal', 'Portugal', 'PRT'), 54: Country(54, 'Ireland', 'Ireland', 'IRL'), - 55: Country(55, 'Denmark', 'Denmark', 'DNK'), 56: Country(56, 'Iran', 'Iran', 'IRN'), - 57: Country(57, 'Pakistan', 'Pakistan', 'PAK'), 58: Country(58, 'Israel', 'Israel', 'ISR'), - 59: Country(59, 'Thailand', 'Thailand', 'THA'), 61: Country(61, 'Slovenia', 'Slovenia', 'SVN'), - 63: Country(63, 'Croatia', 'Croatia', 'HRV'), 64: Country(64, 'Chile', 'Chile', 'CHL'), - 65: Country(65, 'Serbia', 'Serbia', 'SRB'), 66: Country(66, 'Malaysia', 'Malaysia', 'MYS'), - 67: Country(67, 'Philippines', 'Philippines', 'PHL'), 68: Country(68, 'Singapore', 'Singapore', 'SGP'), - 69: Country(69, 'Bosnia and Herzegovina', 'Bosnia-Herzegovina', 'BiH'), - 70: Country(70, 'Estonia', 'Estonia', 'EST'), 80: Country(80, 'Montenegro', 'Montenegro', 'MNE'), - 71: Country(71, 'Latvia', 'Latvia', 'LVA'), 72: Country(72, 'Lithuania', 'Lithuania', 'LTU'), - 73: Country(73, 'North Korea', 'North-Korea', 'PRK'), 74: Country(74, 'Uruguay', 'Uruguay', 'URY'), - 75: Country(75, 'Paraguay', 'Paraguay', 'PRY'), 76: Country(76, 'Bolivia', 'Bolivia', 'BOL'), - 77: Country(77, 'Peru', 'Peru', 'PER'), 78: Country(78, 'Colombia', 'Colombia', 'COL'), - 79: Country(79, 'Republic of Macedonia (FYROM)', 'Republic-of-Macedonia-FYROM', 'MKD'), - 81: Country(81, 'Republic of China (Taiwan)', 'Republic-of-China-Taiwan', 'TWN'), - 82: Country(82, 'Cyprus', 'Cyprus', 'CYP'), 167: Country(167, 'Albania', 'Albania', 'ALB'), - 83: Country(83, 'Belarus', 'Belarus', 'BLR'), 84: Country(84, 'New Zealand', 'New-Zealand', 'NZL'), - 164: Country(164, 'Saudi Arabia', 'Saudi-Arabia', 'SAU'), 165: Country(165, 'Egypt', 'Egypt', 'EGY'), - 166: Country(166, 'United Arab Emirates', 'United-Arab-Emirates', 'UAE'), - 168: Country(168, 'Georgia', 'Georgia', 'GEO'), 169: Country(169, 'Armenia', 'Armenia', 'ARM'), - 170: Country(170, 'Nigeria', 'Nigeria', 'NGA'), 171: Country(171, 'Cuba', 'Cuba', 'CUB') + 1: Country(1, "Romania", "Romania", "ROU"), + 9: Country(9, "Brazil", "Brazil", "BRA"), + 10: Country(10, "Italy", "Italy", "ITA"), + 11: Country(11, "France", "France", "FRA"), + 12: Country(12, "Germany", "Germany", "DEU"), + 13: Country(13, "Hungary", "Hungary", "HUN"), + 14: Country(14, "China", "China", "CHN"), + 15: Country(15, "Spain", "Spain", "ESP"), + 23: Country(23, "Canada", "Canada", "CAN"), + 24: Country(24, "USA", "USA", "USA"), + 26: Country(26, "Mexico", "Mexico", "MEX"), + 27: Country(27, "Argentina", "Argentina", "ARG"), + 28: Country(28, "Venezuela", "Venezuela", "VEN"), + 29: Country(29, "United Kingdom", "United-Kingdom", "GBR"), + 30: Country(30, "Switzerland", "Switzerland", "CHE"), + 31: Country(31, "Netherlands", "Netherlands", "NLD"), + 32: Country(32, "Belgium", "Belgium", "BEL"), + 33: Country(33, "Austria", "Austria", "AUT"), + 34: Country(34, "Czech Republic", "Czech-Republic", "CZE"), + 35: Country(35, "Poland", "Poland", "POL"), + 36: Country(36, "Slovakia", "Slovakia", "SVK"), + 37: Country(37, "Norway", "Norway", "NOR"), + 38: Country(38, "Sweden", "Sweden", "SWE"), + 39: Country(39, "Finland", "Finland", "FIN"), + 40: Country(40, "Ukraine", "Ukraine", "UKR"), + 41: Country(41, "Russia", "Russia", "RUS"), + 42: Country(42, "Bulgaria", "Bulgaria", "BGR"), + 43: Country(43, "Turkey", "Turkey", "TUR"), + 44: Country(44, "Greece", "Greece", "GRC"), + 45: Country(45, "Japan", "Japan", "JPN"), + 47: Country(47, "South Korea", "South-Korea", "KOR"), + 48: Country(48, "India", "India", "IND"), + 49: Country(49, "Indonesia", "Indonesia", "IDN"), + 50: Country(50, "Australia", "Australia", "AUS"), + 51: Country(51, "South Africa", "South-Africa", "ZAF"), + 52: Country(52, "Republic of Moldova", "Republic-of-Moldova", "MDA"), + 53: Country(53, "Portugal", "Portugal", "PRT"), + 54: Country(54, "Ireland", "Ireland", "IRL"), + 55: Country(55, "Denmark", "Denmark", "DNK"), + 56: Country(56, "Iran", "Iran", "IRN"), + 57: Country(57, "Pakistan", "Pakistan", "PAK"), + 58: Country(58, "Israel", "Israel", "ISR"), + 59: Country(59, "Thailand", "Thailand", "THA"), + 61: Country(61, "Slovenia", "Slovenia", "SVN"), + 63: Country(63, "Croatia", "Croatia", "HRV"), + 64: Country(64, "Chile", "Chile", "CHL"), + 65: Country(65, "Serbia", "Serbia", "SRB"), + 66: Country(66, "Malaysia", "Malaysia", "MYS"), + 67: Country(67, "Philippines", "Philippines", "PHL"), + 68: Country(68, "Singapore", "Singapore", "SGP"), + 69: Country(69, "Bosnia and Herzegovina", "Bosnia-Herzegovina", "BiH"), + 70: Country(70, "Estonia", "Estonia", "EST"), + 80: Country(80, "Montenegro", "Montenegro", "MNE"), + 71: Country(71, "Latvia", "Latvia", "LVA"), + 72: Country(72, "Lithuania", "Lithuania", "LTU"), + 73: Country(73, "North Korea", "North-Korea", "PRK"), + 74: Country(74, "Uruguay", "Uruguay", "URY"), + 75: Country(75, "Paraguay", "Paraguay", "PRY"), + 76: Country(76, "Bolivia", "Bolivia", "BOL"), + 77: Country(77, "Peru", "Peru", "PER"), + 78: Country(78, "Colombia", "Colombia", "COL"), + 79: Country(79, "Republic of Macedonia (FYROM)", "Republic-of-Macedonia-FYROM", "MKD"), + 81: Country(81, "Republic of China (Taiwan)", "Republic-of-China-Taiwan", "TWN"), + 82: Country(82, "Cyprus", "Cyprus", "CYP"), + 167: Country(167, "Albania", "Albania", "ALB"), + 83: Country(83, "Belarus", "Belarus", "BLR"), + 84: Country(84, "New Zealand", "New-Zealand", "NZL"), + 164: Country(164, "Saudi Arabia", "Saudi-Arabia", "SAU"), + 165: Country(165, "Egypt", "Egypt", "EGY"), + 166: Country(166, "United Arab Emirates", "United-Arab-Emirates", "UAE"), + 168: Country(168, "Georgia", "Georgia", "GEO"), + 169: Country(169, "Armenia", "Armenia", "ARM"), + 170: Country(170, "Nigeria", "Nigeria", "NGA"), + 171: Country(171, "Cuba", "Cuba", "CUB"), } FOOD_ENERGY: Dict[str, int] = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20) GROUND_RANK_NAMES: Dict[int, str] = { - 1: 'Recruit', 2: 'Private', 3: 'Private*', 4: 'Private**', 5: 'Private***', 6: 'Corporal', 7: 'Corporal*', 8: 'Corporal**', 9: 'Corporal***', - 10: 'Sergeant', 11: 'Sergeant*', 12: 'Sergeant**', 13: 'Sergeant***', 14: 'Lieutenant', 15: 'Lieutenant*', 16: 'Lieutenant**', 17: 'Lieutenant***', - 18: 'Captain', 19: 'Captain*', 20: 'Captain**', 21: 'Captain***', 22: 'Major', 23: 'Major*', 24: 'Major**', 25: 'Major***', - 26: 'Commander', 27: 'Commander*', 28: 'Commander**', 29: 'Commander***', 30: 'Lt Colonel', 31: 'Lt Colonel*', 32: 'Lt Colonel**', 33: 'Lt Colonel***', - 34: 'Colonel', 35: 'Colonel*', 36: 'Colonel**', 37: 'Colonel***', 38: 'General', 39: 'General*', 40: 'General**', 41: 'General***', - 42: 'Field Marshal', 43: 'Field Marshal*', 44: 'Field Marshal**', 45: 'Field Marshal***', 46: 'Supreme Marshal', 47: 'Supreme Marshal*', 48: 'Supreme Marshal**', 49: 'Supreme Marshal***', - 50: 'National Force', 51: 'National Force*', 52: 'National Force**', 53: 'National Force***', 54: 'World Class Force', 55: 'World Class Force*', 56: 'World Class Force**', 57: 'World Class Force***', - 58: 'Legendary Force', 59: 'Legendary Force*', 60: 'Legendary Force**', 61: 'Legendary Force***', 62: 'God of War', 63: 'God of War*', 64: 'God of War**', 65: 'God of War***', - 66: 'Titan', 67: 'Titan*', 68: 'Titan**', 69: 'Titan***', - 70: 'Legends I', 71: 'Legends II', 72: 'Legends III', 73: 'Legends IV', 74: 'Legends V', 75: 'Legends VI', 76: 'Legends VII', 77: 'Legends VIII', 78: 'Legends IX', 79: 'Legends X', - 80: 'Legends XI', 81: 'Legends XII', 82: 'Legends XIII', 83: 'Legends XIV', 84: 'Legends XV', 85: 'Legends XVI', 86: 'Legends XVII', 87: 'Legends XVIII', 88: 'Legends XIX', 89: 'Legends XX' + 1: "Recruit", + 2: "Private", + 3: "Private*", + 4: "Private**", + 5: "Private***", + 6: "Corporal", + 7: "Corporal*", + 8: "Corporal**", + 9: "Corporal***", + 10: "Sergeant", + 11: "Sergeant*", + 12: "Sergeant**", + 13: "Sergeant***", + 14: "Lieutenant", + 15: "Lieutenant*", + 16: "Lieutenant**", + 17: "Lieutenant***", + 18: "Captain", + 19: "Captain*", + 20: "Captain**", + 21: "Captain***", + 22: "Major", + 23: "Major*", + 24: "Major**", + 25: "Major***", + 26: "Commander", + 27: "Commander*", + 28: "Commander**", + 29: "Commander***", + 30: "Lt Colonel", + 31: "Lt Colonel*", + 32: "Lt Colonel**", + 33: "Lt Colonel***", + 34: "Colonel", + 35: "Colonel*", + 36: "Colonel**", + 37: "Colonel***", + 38: "General", + 39: "General*", + 40: "General**", + 41: "General***", + 42: "Field Marshal", + 43: "Field Marshal*", + 44: "Field Marshal**", + 45: "Field Marshal***", + 46: "Supreme Marshal", + 47: "Supreme Marshal*", + 48: "Supreme Marshal**", + 49: "Supreme Marshal***", + 50: "National Force", + 51: "National Force*", + 52: "National Force**", + 53: "National Force***", + 54: "World Class Force", + 55: "World Class Force*", + 56: "World Class Force**", + 57: "World Class Force***", + 58: "Legendary Force", + 59: "Legendary Force*", + 60: "Legendary Force**", + 61: "Legendary Force***", + 62: "God of War", + 63: "God of War*", + 64: "God of War**", + 65: "God of War***", + 66: "Titan", + 67: "Titan*", + 68: "Titan**", + 69: "Titan***", + 70: "Legends I", + 71: "Legends II", + 72: "Legends III", + 73: "Legends IV", + 74: "Legends V", + 75: "Legends VI", + 76: "Legends VII", + 77: "Legends VIII", + 78: "Legends IX", + 79: "Legends X", + 80: "Legends XI", + 81: "Legends XII", + 82: "Legends XIII", + 83: "Legends XIV", + 84: "Legends XV", + 85: "Legends XVI", + 86: "Legends XVII", + 87: "Legends XVIII", + 88: "Legends XIX", + 89: "Legends XX", } GROUND_RANK_POINTS: Dict[int, int] = { - 1: 0, 2: 15, 3: 45, 4: 80, 5: 120, 6: 170, 7: 250, 8: 350, 9: 450, 10: 600, 11: 800, 12: 1000, - 13: 1400, 14: 1850, 15: 2350, 16: 3000, 17: 3750, 18: 5000, 19: 6500, 20: 9000, 21: 12000, - 22: 15500, 23: 20000, 24: 25000, 25: 31000, 26: 40000, 27: 52000, 28: 67000, 29: 85000, - 30: 110000, 31: 140000, 32: 180000, 33: 225000, 34: 285000, 35: 355000, 36: 435000, 37: 540000, - 38: 660000, 39: 800000, 40: 950000, 41: 1140000, 42: 1350000, 43: 1600000, 44: 1875000, - 45: 2185000, 46: 2550000, 47: 3000000, 48: 3500000, 49: 4150000, 50: 4900000, 51: 5800000, - 52: 7000000, 53: 9000000, 54: 11500000, 55: 14500000, 56: 18000000, 57: 22000000, 58: 26500000, - 59: 31500000, 60: 37000000, 61: 43000000, 62: 50000000, 63: 100000000, 64: 200000000, - 65: 500000000, 66: 1000000000, 67: 2000000000, 68: 4000000000, 69: 10000000000, 70: 20000000000, - 71: 30000000000, 72: 40000000000, 73: 50000000000, 74: 60000000000, 75: 70000000000, - 76: 80000000000, 77: 90000000000, 78: 100000000000, 79: 110000000000, 80: 120000000000, - 81: 130000000000, 82: 140000000000, 83: 150000000000, 84: 160000000000, 85: 170000000000, - 86: 180000000000, 87: 190000000000, 88: 200000000000, 89: 210000000000 + 1: 0, + 2: 15, + 3: 45, + 4: 80, + 5: 120, + 6: 170, + 7: 250, + 8: 350, + 9: 450, + 10: 600, + 11: 800, + 12: 1000, + 13: 1400, + 14: 1850, + 15: 2350, + 16: 3000, + 17: 3750, + 18: 5000, + 19: 6500, + 20: 9000, + 21: 12000, + 22: 15500, + 23: 20000, + 24: 25000, + 25: 31000, + 26: 40000, + 27: 52000, + 28: 67000, + 29: 85000, + 30: 110000, + 31: 140000, + 32: 180000, + 33: 225000, + 34: 285000, + 35: 355000, + 36: 435000, + 37: 540000, + 38: 660000, + 39: 800000, + 40: 950000, + 41: 1140000, + 42: 1350000, + 43: 1600000, + 44: 1875000, + 45: 2185000, + 46: 2550000, + 47: 3000000, + 48: 3500000, + 49: 4150000, + 50: 4900000, + 51: 5800000, + 52: 7000000, + 53: 9000000, + 54: 11500000, + 55: 14500000, + 56: 18000000, + 57: 22000000, + 58: 26500000, + 59: 31500000, + 60: 37000000, + 61: 43000000, + 62: 50000000, + 63: 100000000, + 64: 200000000, + 65: 500000000, + 66: 1000000000, + 67: 2000000000, + 68: 4000000000, + 69: 10000000000, + 70: 20000000000, + 71: 30000000000, + 72: 40000000000, + 73: 50000000000, + 74: 60000000000, + 75: 70000000000, + 76: 80000000000, + 77: 90000000000, + 78: 100000000000, + 79: 110000000000, + 80: 120000000000, + 81: 130000000000, + 82: 140000000000, + 83: 150000000000, + 84: 160000000000, + 85: 170000000000, + 86: 180000000000, + 87: 190000000000, + 88: 200000000000, + 89: 210000000000, } GROUND_RANKS: Dict[int, Rank] = {i: Rank(i, GROUND_RANK_NAMES[i], GROUND_RANK_POINTS[i], False) for i in range(1, 90)} INDUSTRIES = Industries() -TERRAINS: Dict[int, str] = {0: 'Standard', 1: 'Industrial', 2: 'Urban', 3: 'Suburbs', 4: 'Airport', 5: 'Plains', - 6: 'Wasteland', 7: 'Mountains', 8: 'Beach', 9: 'Swamp', 10: 'Mud', 11: 'Hills', - 12: 'Jungle', 13: 'Forest', 14: 'Desert'} +TERRAINS: Dict[int, str] = { + 0: "Standard", + 1: "Industrial", + 2: "Urban", + 3: "Suburbs", + 4: "Airport", + 5: "Plains", + 6: "Wasteland", + 7: "Mountains", + 8: "Beach", + 9: "Swamp", + 10: "Mud", + 11: "Hills", + 12: "Jungle", + 13: "Forest", + 14: "Desert", +} diff --git a/erepublik/utils.py b/erepublik/utils.py index 151b90e..58ffe73 100644 --- a/erepublik/utils.py +++ b/erepublik/utils.py @@ -23,10 +23,33 @@ except ImportError: import json __all__ = [ - 'VERSION', 'calculate_hit', 'date_from_eday', 'eday_from_date', 'deprecation', 'get_final_hit_dmg', 'write_file', - 'get_air_hit_dmg_value', 'get_file', 'get_ground_hit_dmg_value', 'get_sleep_seconds', 'good_timedelta', 'slugify', - 'interactive_sleep', 'json', 'localize_dt', 'localize_timestamp', 'normalize_html_json', 'now', 'silent_sleep', - 'json_decode_object_hook', 'json_load', 'json_loads', 'json_dump', 'json_dumps', 'b64json', 'ErepublikJSONEncoder', + "VERSION", + "calculate_hit", + "date_from_eday", + "eday_from_date", + "deprecation", + "get_final_hit_dmg", + "write_file", + "get_air_hit_dmg_value", + "get_file", + "get_ground_hit_dmg_value", + "get_sleep_seconds", + "good_timedelta", + "slugify", + "interactive_sleep", + "json", + "localize_dt", + "localize_timestamp", + "normalize_html_json", + "now", + "silent_sleep", + "json_decode_object_hook", + "json_load", + "json_loads", + "json_dump", + "json_dumps", + "b64json", + "ErepublikJSONEncoder", ] VERSION: str = __version__ @@ -75,7 +98,7 @@ def date_from_eday(eday: int) -> datetime.date: def get_sleep_seconds(time_until: datetime.datetime) -> int: - """ time_until aware datetime object Wrapper for sleeping until """ + """time_until aware datetime object Wrapper for sleeping until""" sleep_seconds = int((time_until - now()).total_seconds()) return sleep_seconds if sleep_seconds > 0 else 0 @@ -107,7 +130,7 @@ def get_file(filepath: str) -> str: file = Path(filepath) if file.exists(): if file.is_dir(): - return str(file / 'new_file.txt') + return str(file / "new_file.txt") else: version = 1 try: @@ -129,16 +152,16 @@ def get_file(filepath: str) -> str: def write_file(filename: str, content: str) -> int: filename = get_file(filename) - with open(filename, 'ab') as f: + with open(filename, "ab") as f: ret = f.write(content.encode("utf-8")) return ret def normalize_html_json(js: str) -> str: - js = re.sub(r' \'(.*?)\'', lambda a: f'"{a.group(1)}"', js) - js = re.sub(r'(\d\d):(\d\d):(\d\d)', r'\1\2\3', js) + js = re.sub(r" \'(.*?)\'", lambda a: f'"{a.group(1)}"', js) + js = re.sub(r"(\d\d):(\d\d):(\d\d)", r"\1\2\3", js) js = re.sub(r'([{\s,])(\w+)(:)(?!"})', r'\1"\2"\3', js) - js = re.sub(r',\s*}', '}', js) + js = re.sub(r",\s*}", "}", js) return js @@ -151,47 +174,51 @@ def slugify(value, allow_unicode=False) -> str: """ value = str(value) if allow_unicode: - value = unicodedata.normalize('NFKC', value) + value = unicodedata.normalize("NFKC", value) else: - value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii') - value = re.sub(r'[^\w\s-]', '_', value).strip().lower() - return re.sub(r'[-\s]+', '-', value) + value = unicodedata.normalize("NFKD", value).encode("ascii", "ignore").decode("ascii") + value = re.sub(r"[^\w\s-]", "_", value).strip().lower() + return re.sub(r"[-\s]+", "-", value) -def calculate_hit(strength: float, rang: int, tp: bool, elite: bool, ne: bool, booster: int = 0, - weapon: int = 200, is_deploy: bool = False) -> Decimal: +def calculate_hit( + strength: float, rang: int, tp: bool, elite: bool, ne: bool, booster: int = 0, weapon: int = 200, is_deploy: bool = False +) -> Decimal: dec = 3 if is_deploy else 0 - base_str = (1 + Decimal(str(round(strength, 3))) / 400) - base_rnk = (1 + Decimal(str(rang / 5))) - base_wpn = (1 + Decimal(str(weapon / 100))) + base_str = 1 + Decimal(str(round(strength, 3))) / 400 + base_rnk = 1 + Decimal(str(rang / 5)) + base_wpn = 1 + Decimal(str(weapon / 100)) dmg = 10 * base_str * base_rnk * base_wpn dmg = get_final_hit_dmg(dmg, rang, tp=tp, elite=elite, ne=ne, booster=booster) return Decimal(round(dmg, dec)) -def get_ground_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, - booster: int = 0, weapon_power: int = 200) -> Decimal: - r = requests.get(f'https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}').json() - rang = r['military']['militaryData']['ground']['rankNumber'] - strength = r['military']['militaryData']['ground']['strength'] - elite = r['citizenAttributes']['level'] > 100 +def get_ground_hit_dmg_value( + citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, weapon_power: int = 200 +) -> Decimal: + r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json() + rang = r["military"]["militaryData"]["ground"]["rankNumber"] + strength = r["military"]["militaryData"]["ground"]["strength"] + elite = r["citizenAttributes"]["level"] > 100 if natural_enemy: true_patriot = True return calculate_hit(strength, rang, true_patriot, elite, natural_enemy, booster, weapon_power) -def get_air_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, - weapon_power: int = 0) -> Decimal: - r = requests.get(f'https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}').json() - rang = r['military']['militaryData']['aircraft']['rankNumber'] - elite = r['citizenAttributes']['level'] > 100 +def get_air_hit_dmg_value( + citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, weapon_power: int = 0 +) -> Decimal: + r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json() + rang = r["military"]["militaryData"]["aircraft"]["rankNumber"] + elite = r["citizenAttributes"]["level"] > 100 return calculate_hit(0, rang, true_patriot, elite, natural_enemy, booster, weapon_power) -def get_final_hit_dmg(base_dmg: Union[Decimal, float, str], rang: int, - tp: bool = False, elite: bool = False, ne: bool = False, booster: int = 0) -> Decimal: +def get_final_hit_dmg( + base_dmg: Union[Decimal, float, str], rang: int, tp: bool = False, elite: bool = False, ne: bool = False, booster: int = 0 +) -> Decimal: dmg = Decimal(str(base_dmg)) if elite: @@ -211,23 +238,23 @@ def deprecation(message): 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 + """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) + 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') + 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']) + elif _type == "timedelta": + return datetime.timedelta(seconds=o["total_seconds"]) return o @@ -242,20 +269,20 @@ def json_loads(s: str, **kwargs): def json_dump(obj, fp, *args, **kwargs): - if not kwargs.get('cls'): + if not kwargs.get("cls"): kwargs.update(cls=ErepublikJSONEncoder) return json.dump(obj, fp, *args, **kwargs) def json_dumps(obj, *args, **kwargs): - if not kwargs.get('cls'): + if not kwargs.get("cls"): kwargs.update(cls=ErepublikJSONEncoder) return json.dumps(obj, *args, **kwargs) def b64json(obj: Union[Dict[str, Union[int, List[str]]], List[str]]): if isinstance(obj, list): - return b64encode(json.dumps(obj, separators=(',', ':')).encode('utf-8')).decode('utf-8') + return b64encode(json.dumps(obj, separators=(",", ":")).encode("utf-8")).decode("utf-8") elif isinstance(obj, (int, str)): return obj elif isinstance(obj, dict): @@ -263,27 +290,34 @@ def b64json(obj: Union[Dict[str, Union[int, List[str]]], List[str]]): obj[k] = b64json(v) else: from .classes import ErepublikException - raise ErepublikException(f'Unhandled object type! obj is {type(obj)}') - return b64encode(json.dumps(obj, separators=(',', ':')).encode('utf-8')).decode('utf-8') + + raise ErepublikException(f"Unhandled object type! obj is {type(obj)}") + return b64encode(json.dumps(obj, separators=(",", ":")).encode("utf-8")).decode("utf-8") class ErepublikJSONEncoder(json.JSONEncoder): def default(self, o): try: from erepublik.citizen import Citizen + if isinstance(o, Decimal): return float(f"{o:.02f}") elif isinstance(o, datetime.datetime): - return dict(__type__='datetime', date=o.strftime("%Y-%m-%d"), time=o.strftime("%H:%M:%S"), - tzinfo=str(o.tzinfo) if o.tzinfo else None) + return dict( + __type__="datetime", + date=o.strftime("%Y-%m-%d"), + time=o.strftime("%H:%M:%S"), + tzinfo=str(o.tzinfo) if o.tzinfo else None, + ) elif isinstance(o, datetime.date): - return dict(__type__='date', date=o.strftime("%Y-%m-%d")) + return dict(__type__="date", date=o.strftime("%Y-%m-%d")) elif isinstance(o, datetime.timedelta): - return dict(__type__='timedelta', days=o.days, seconds=o.seconds, - microseconds=o.microseconds, total_seconds=o.total_seconds()) + 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=dict(o.__dict__['headers']), url=o.url, text=o.text, status_code=o.status_code) - elif hasattr(o, 'as_dict'): + 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): return list(o) @@ -291,7 +325,7 @@ class ErepublikJSONEncoder(json.JSONEncoder): return o.to_json() elif isinstance(o, Logger): return str(o) - elif hasattr(o, '__dict__'): + elif hasattr(o, "__dict__"): return o.__dict__ else: return super().default(o) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7e28eca --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ +[tool.black] +line-length = 140 +target-version = ['py38', 'py39'] + diff --git a/setup.cfg b/setup.cfg index 5b8808e..5ea24ba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,11 +19,13 @@ universal = 1 [flake8] exclude = docs,.git,log,debug,venv -max-line-length = 240 -ignore = D100,D101,D102,D103 +line_length = 140 +max-line-length = 140 +ignore = D100,D101,D102,D103,E203 [pycodestyle] -max-line-length = 240 +line_length = 140 +max-line-length = 140 exclude = .git,log,debug,venv, build [mypy] @@ -36,4 +38,4 @@ warn_unused_configs = True [isort] multi_line_output = 2 -line_length = 240 +line_length = 140 diff --git a/tests/test_erepublik_script.py b/tests/test_erepublik_script.py index 8950dd8..d830d5e 100644 --- a/tests/test_erepublik_script.py +++ b/tests/test_erepublik_script.py @@ -17,14 +17,13 @@ class TestErepublik(unittest.TestCase): self.citizen.config.interactive = False def test_should_do_levelup(self): - self.citizen.energy.recovered = 1900 - self.citizen.energy.recoverable = 2940 + self.citizen.energy.energy = 5950 self.citizen.energy.interval = 30 - self.citizen.energy.limit = 3000 + self.citizen.energy.limit = 6000 self.citizen.details.xp = 14850 self.assertTrue(self.citizen.should_do_levelup) - self.citizen.energy.recoverable = 1000 + self.citizen.energy.energy = 1000 self.assertFalse(self.citizen.should_do_levelup) def test_should_travel_to_fight(self): @@ -33,40 +32,38 @@ class TestErepublik(unittest.TestCase): self.citizen.config.always_travel = False self.assertFalse(self.citizen.should_travel_to_fight()) - self.citizen.energy.recovered = 1900 - self.citizen.energy.recoverable = 2940 + self.citizen.energy.energy = 5960 self.citizen.energy.interval = 30 - self.citizen.energy.limit = 3000 + self.citizen.energy.limit = 6000 self.citizen.details.xp = 14850 self.assertTrue(self.citizen.should_travel_to_fight()) self.citizen.details.xp = 15000 + self.citizen.energy.energy = 5000 self.assertFalse(self.citizen.should_travel_to_fight()) - self.citizen.energy.recovered = 3000 - self.citizen.energy.recoverable = 2910 + self.citizen.energy.energy = 5910 self.assertTrue(self.citizen.should_travel_to_fight()) - self.citizen.energy.recoverable = 2900 + self.citizen.energy.energy = 5900 self.assertFalse(self.citizen.should_travel_to_fight()) # self.citizen.next_reachable_energy and self.citizen.config.next_energy self.citizen.config.next_energy = True - self.citizen.energy.limit = 5000 + self.citizen.energy.limit = 10000 self.citizen.details.next_pp = [5000, 5250, 5750, 6250, 6750] self.citizen.details.pp = 4900 - self.citizen.energy.recovered = 4000 - self.citizen.energy.recoverable = 4510 + self.citizen.energy.energy = 8510 self.assertEqual(self.citizen.next_reachable_energy, 850) - self.citizen.energy.recoverable = 4490 + self.citizen.energy.energy = 8490 self.assertTrue(self.citizen.should_travel_to_fight()) self.assertEqual(self.citizen.next_reachable_energy, 350) - self.citizen.energy.recovered = 100 - self.citizen.energy.recoverable = 150 + self.citizen.energy.energy = 250 self.assertFalse(self.citizen.should_travel_to_fight()) self.assertEqual(self.citizen.next_reachable_energy, 0) def test_should_fight(self): def is_wc_close(): return self.citizen.max_time_till_full_ff > self.citizen.time_till_week_change + self.citizen.config.fight = False self.assertEqual(self.citizen.should_fight(), (0, "Fighting not allowed!", False)) @@ -76,62 +73,64 @@ class TestErepublik(unittest.TestCase): self.citizen.energy.limit = 3000 self.citizen.details.xp = 24705 if not is_wc_close: - self.assertEqual(self.citizen.should_fight(), (0, 'Level up', False)) + self.assertEqual(self.citizen.should_fight(), (0, "Level up", False)) - self.citizen.energy.recovered = 3000 - self.citizen.energy.recoverable = 2950 + self.citizen.energy.energy = 5950 self.citizen.energy.interval = 30 - self.assertEqual(self.citizen.should_fight(), (900, 'Level up', True)) + self.assertEqual(self.citizen.should_fight(), (900, "Level up", True)) self.citizen.my_companies.ff_lockdown = 160 - self.assertEqual(self.citizen.should_fight(), (900, 'Level up', True)) + self.assertEqual(self.citizen.should_fight(), (900, "Level up", True)) self.citizen.my_companies.ff_lockdown = 0 # Level up reachable self.citizen.details.xp = 24400 - self.assertEqual(self.citizen.should_fight(), (305, 'Fighting for close Levelup. Doing 305 hits', True)) + self.assertEqual(self.citizen.should_fight(), (305, "Fighting for close Levelup. Doing 305 hits", True)) self.citizen.my_companies.ff_lockdown = 160 - self.assertEqual(self.citizen.should_fight(), (305, 'Fighting for close Levelup. Doing 305 hits', True)) + self.assertEqual(self.citizen.should_fight(), (305, "Fighting for close Levelup. Doing 305 hits", True)) self.citizen.my_companies.ff_lockdown = 0 self.citizen.details.xp = 21000 - self.assertEqual(self.citizen.should_fight(), (75, 'Obligatory fighting for at least 75pp', True)) + self.assertEqual(self.citizen.should_fight(), (75, "Obligatory fighting for at least 75pp", True)) self.citizen.my_companies.ff_lockdown = 160 - self.assertEqual(self.citizen.should_fight(), (75, 'Obligatory fighting for at least 75pp', True)) + self.assertEqual(self.citizen.should_fight(), (75, "Obligatory fighting for at least 75pp", True)) self.citizen.my_companies.ff_lockdown = 0 self.citizen.details.pp = 80 # All-in (type = all-in and full ff) self.citizen.config.all_in = True - self.assertEqual(self.citizen.should_fight(), (595, 'Fighting all-in. Doing 595 hits', False)) + self.assertEqual(self.citizen.should_fight(), (595, "Fighting all-in. Doing 595 hits", False)) self.citizen.my_companies.ff_lockdown = 160 - self.assertEqual(self.citizen.should_fight(), ( - 435, 'Fight count modified (old count: 595 | FF: 595 | WAM ff_lockdown: 160 | New count: 435)', False - )) + self.assertEqual( + self.citizen.should_fight(), + (435, "Fight count modified (old count: 595 | FF: 595 | WAM ff_lockdown: 160 | New count: 435)", False), + ) self.citizen.my_companies.ff_lockdown = 0 self.citizen.config.air = True - self.citizen.energy.recoverable = 1000 - self.assertEqual(self.citizen.should_fight(), (400, 'Fighting all-in in AIR. Doing 400 hits', False)) + self.citizen.energy.energy = 4000 + self.assertEqual(self.citizen.should_fight(), (400, "Fighting all-in in AIR. Doing 400 hits", False)) self.citizen.my_companies.ff_lockdown = 160 - self.assertEqual(self.citizen.should_fight(), ( - 240, 'Fight count modified (old count: 400 | FF: 400 | WAM ff_lockdown: 160 | New count: 240)', False - )) + self.assertEqual( + self.citizen.should_fight(), + (240, "Fight count modified (old count: 400 | FF: 400 | WAM ff_lockdown: 160 | New count: 240)", False), + ) self.citizen.my_companies.ff_lockdown = 0 self.citizen.config.all_in = False self.citizen.config.next_energy = True - self.citizen.energy.limit = 5000 + self.citizen.energy.limit = 10000 self.citizen.details.next_pp = [100, 150, 250, 400, 500] - self.assertEqual(self.citizen.should_fight(), (320, 'Fighting for +1 energy. Doing 320 hits', False)) + self.assertEqual(self.citizen.should_fight(), (320, "Fighting for +1 energy. Doing 320 hits", False)) self.citizen.my_companies.ff_lockdown = 160 - self.assertEqual(self.citizen.should_fight(), ( - 160, 'Fight count modified (old count: 320 | FF: 400 | WAM ff_lockdown: 160 | New count: 160)', False - )) + self.assertEqual( + self.citizen.should_fight(), + (160, "Fight count modified (old count: 320 | FF: 400 | WAM ff_lockdown: 160 | New count: 160)", False), + ) self.citizen.my_companies.ff_lockdown = 0 self.citizen.energy.limit = 3000 self.citizen.details.next_pp = [19250, 20000] self.citizen.config.next_energy = False # 1h worth of energy - self.citizen.energy.recoverable = 2910 - self.assertEqual(self.citizen.should_fight(), (30, 'Fighting for 1h energy. Doing 30 hits', True)) + self.citizen.energy.energy = 5910 + self.assertEqual(self.citizen.should_fight(), (30, "Fighting for 1h energy. Doing 30 hits", True))