Compare commits

...

36 Commits

Author SHA1 Message Date
9750ee1926 Bump version: 0.30.0.3 → 0.30.0.4 2022-03-21 16:41:08 +02:00
7015b6d299 CookieFix 2022-03-21 16:40:46 +02:00
288c3df4a5 Bump version: 0.30.0.2 → 0.30.0.3 2022-03-21 15:22:45 +02:00
15eb3ac8a0 remove requests 2022-03-21 15:21:28 +02:00
c4933de237 import 2022-03-21 15:13:52 +02:00
bd864a110d Bump version: 0.30.0.1 → 0.30.0.2 2022-03-21 15:10:07 +02:00
ee422c4b02 . 2022-03-21 15:10:04 +02:00
4af6101665 Bump version: 0.30.0 → 0.30.0.1 2022-03-21 15:06:29 +02:00
61a535dca5 requirements 2022-03-21 15:05:50 +02:00
6c0871cfbd Bump version: 0.29.2.3 → 0.30.0 2022-03-21 14:57:17 +02:00
c9dbf7ee1e Enable HTTP2 to overcome CloudFlare captchas 2022-03-21 14:55:07 +02:00
c86a5ffc5a Added Readme swag icons 2022-03-15 10:44:15 +02:00
72b12a5ca2 Lint 2022-03-15 10:36:39 +02:00
1831ddfb49 Bump version: 0.29.2.2 → 0.29.2.3 2022-03-11 12:01:40 +02:00
f9d2ad85c2 bugfix 2022-03-11 12:01:30 +02:00
66a05bb202 Bump version: 0.29.2.1 → 0.29.2.2 2022-03-11 11:44:54 +02:00
273e98c8d1 Update 2022-03-11 11:44:29 +02:00
03c435bf7e Bump version: 0.29.2 → 0.29.2.1 2022-03-06 21:22:36 +02:00
112150cce6 bugfix
version update
2022-03-06 21:22:26 +02:00
eb9231b0e7 Bump version: 0.29.1 → 0.29.2 2022-03-06 16:36:49 +02:00
763fbaaf9c Update wam logic 2022-03-06 16:36:35 +02:00
e9f3441678 style setting update 2022-03-06 16:35:49 +02:00
f59b47d131 Bump version: 0.29.0.6 → 0.29.1 2022-02-04 00:41:40 +02:00
6ca6d416b3 Merge branch 'master' of github.com:eeriks/erepublik 2022-02-04 00:41:20 +02:00
903d596064 Fixup 2022-02-04 00:41:13 +02:00
8a2a1ab393 autosell bugfix 2022-02-04 00:34:25 +02:00
3ee9811b8f Bump version: 0.29.0.5 → 0.29.0.6 2022-02-01 23:01:51 +02:00
f9b136faba Bugfix 2022-02-01 23:01:44 +02:00
1fb186961f Bump version: 0.29.0.4 → 0.29.0.5 2022-02-01 22:57:18 +02:00
2c1cf2fc66 Bugfix 2022-02-01 22:57:07 +02:00
75996779e1 Bump version: 0.28.3 → 0.29.0 2022-02-01 22:21:44 +02:00
d5ce52162a Updated WAM logic for cases when there are too many RAW factories 2022-02-01 22:21:29 +02:00
7e5312df18 Bump version: 0.28.2 → 0.28.3 2022-01-26 14:03:21 +02:00
8474920b72 Bugfix requests 2.27.0 contains bug which prohibits using auth with socks proxy 2022-01-26 14:03:11 +02:00
3426f60749 Bump version: 0.28.1 → 0.28.2 2022-01-26 13:10:23 +02:00
c703201ae7 Bugfix 2022-01-26 13:10:07 +02:00
15 changed files with 274 additions and 206 deletions

View File

@ -21,7 +21,7 @@ insert_final_newline = false
indent_style = tab indent_style = tab
[*.py] [*.py]
max_line_length = 240 max_line_length = 120
line_length=120 line_length=120
multi_line_output=0 multi_line_output=0
balanced_wrapping=True balanced_wrapping=True

View File

@ -10,6 +10,13 @@ eRepublik script
:target: https://erepublik.readthedocs.io/en/latest/?badge=latest :target: https://erepublik.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status :alt: Documentation Status
.. image:: https://github.com/eeriks/erepublik/actions/workflows/codeql-analysis.yml/badge.svg?branch=master
:target: //github.com/eeriks/erepublik/actions/workflows/codeql-analysis.yml
:alt: CodeQL
.. image:: https://github.com/eeriks/erepublik/actions/workflows/pythonpackage.yml/badge.svg
:target: https://github.com/eeriks/erepublik/actions/workflows/pythonpackage.yml
:alt: Python package
Python package for automated eRepublik playing Python package for automated eRepublik playing

View File

@ -4,7 +4,7 @@
__author__ = """Eriks Karls""" __author__ = """Eriks Karls"""
__email__ = "eriks@72.lv" __email__ = "eriks@72.lv"
__version__ = "0.28.1" __version__ = "0.30.0.4"
from erepublik.citizen import Citizen from erepublik.citizen import Citizen

View File

@ -9,7 +9,7 @@ from logging import LogRecord, handlers
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Union from typing import Any, Dict, Union
import requests from httpx import post
from erepublik.classes import Reporter from erepublik.classes import Reporter
from erepublik.constants import erep_tz from erepublik.constants import erep_tz
@ -112,7 +112,7 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler):
def _get_last_response(self) -> Dict[str, str]: def _get_last_response(self) -> Dict[str, str]:
response = self.reporter.citizen.r response = self.reporter.citizen.r
url = response.url url = str(response.url)
last_index = url.index("?") if "?" in url else len(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])
@ -209,6 +209,6 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler):
headers = {"Authorization": s} headers = {"Authorization": s}
data = self.mapLogRecord(record) 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) post(f"{proto}://{self.host}{self.url}", headers=headers, data=data, files=files)
except Exception: except Exception:
self.handleError(record) self.handleError(record)

View File

@ -4,9 +4,8 @@ import random
import time import time
from typing import Any, Dict, List, Mapping, Union from typing import Any, Dict, List, Mapping, Union
from requests import Response, Session from httpx import Response, Client as Session, RequestError
from requests.exceptions import ConnectionError #from requests_toolbelt.utils import dump
from requests_toolbelt.utils import dump
from erepublik import constants, utils from erepublik import constants, utils
@ -15,25 +14,26 @@ __all__ = ["SlowRequests", "CitizenAPI"]
class SlowRequests(Session): class SlowRequests(Session):
last_time: datetime.datetime last_time: datetime.datetime
timeout: datetime.timedelta = datetime.timedelta(milliseconds=500) __timeout: datetime.timedelta = datetime.timedelta(milliseconds=500)
debug: bool = False debug: bool = False
http2 = True
def __init__(self, proxies: Dict[str, str] = None, user_agent: str = None): def __init__(self, proxies: Dict[str, str] = None, user_agent: str = None):
super().__init__() super().__init__(http2=True, follow_redirects=True)
if proxies: if proxies:
self.proxies = proxies self.proxies = proxies
if user_agent is None: if user_agent is None:
user_agent = random.choice(self.get_random_user_agent()) user_agent = self.get_random_user_agent()
self.request_log_name = utils.get_file(utils.now().strftime("debug/requests_%Y-%m-%d.log")) self.request_log_name = utils.get_file(utils.now().strftime("debug/requests_%Y-%m-%d.log"))
self.last_time = utils.now() 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] self.event_hooks["response"] = [self._log_response]
@property @property
def as_dict(self): def as_dict(self):
return dict( return dict(
last_time=self.last_time, last_time=self.last_time,
timeout=self.timeout, __timeout=self.__timeout,
cookies=self.cookies.get_dict(), cookies=self.cookies.get_dict(),
debug=self.debug, debug=self.debug,
user_agent=self.headers["User-Agent"], user_agent=self.headers["User-Agent"],
@ -46,14 +46,14 @@ class SlowRequests(Session):
self._log_request(url, method, **kwargs) self._log_request(url, method, **kwargs)
try: try:
resp = super().request(method, url, *args, **kwargs) resp = super().request(method, url, *args, **kwargs)
except ConnectionError: except RequestError:
time.sleep(1) time.sleep(1)
return self.request(method, url, *args, **kwargs) return self.request(method, url, *args, **kwargs)
# self._log_response(resp) # self._log_response(resp)
return resp return resp
def _slow_down_requests(self): def _slow_down_requests(self):
ltt = utils.good_timedelta(self.last_time, self.timeout) ltt = utils.good_timedelta(self.last_time, self.__timeout)
if ltt > utils.now(): if ltt > utils.now():
seconds = (ltt - utils.now()).total_seconds() seconds = (ltt - utils.now()).total_seconds()
time.sleep(seconds if seconds > 0 else 0) time.sleep(seconds if seconds > 0 else 0)
@ -81,7 +81,7 @@ class SlowRequests(Session):
def _log_response(self, response: Response, *args, **kwargs): def _log_response(self, response: Response, *args, **kwargs):
redirect = kwargs.get("redirect") redirect = kwargs.get("redirect")
url = response.request.url url = str(response.request.url)
if self.debug: if self.debug:
if response.history and not redirect: if response.history and not redirect:
for hist_resp in response.history: for hist_resp in response.history:
@ -92,7 +92,7 @@ class SlowRequests(Session):
fd_time = self.last_time.strftime("%Y/%m/%d/%H-%M-%S") fd_time = self.last_time.strftime("%Y/%m/%d/%H-%M-%S")
fd_name = utils.slugify(url[len(CitizenBaseAPI.url) :]) fd_name = utils.slugify(url[len(CitizenBaseAPI.url) :])
fd_extra = "_REDIRECT" if redirect else "" fd_extra = "_REDIRECT" if redirect else ""
response.read()
try: try:
utils.json.loads(response.text) utils.json.loads(response.text)
fd_ext = "json" fd_ext = "json"
@ -102,9 +102,9 @@ class SlowRequests(Session):
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) utils.write_file(filename, response.text)
if not redirect: #if not redirect:
data = dump.dump_all(response) # 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 @staticmethod
def get_random_user_agent() -> str: def get_random_user_agent() -> str:
@ -145,7 +145,7 @@ class CitizenBaseAPI:
return dict(url=self.url, request=self._req.as_dict, token=self.token) return dict(url=self.url, request=self._req.as_dict, token=self.token)
def post(self, url: str, data=None, json=None, **kwargs) -> Response: def post(self, url: str, data=None, json=None, **kwargs) -> Response:
return self._req.post(url, data, json, **kwargs) return self._req.post(url, data=data, json=json, **kwargs)
def get(self, url: str, **kwargs) -> Response: def get(self, url: str, **kwargs) -> Response:
return self._req.get(url, **kwargs) return self._req.get(url, **kwargs)

View File

@ -9,7 +9,7 @@ from threading import Event
from time import sleep from time import sleep
from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, TypedDict, Union from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, TypedDict, Union
from requests import RequestException, Response from httpx import RequestError, Response
from erepublik import _types as types from erepublik import _types as types
from erepublik import access_points, classes, constants, utils from erepublik import access_points, classes, constants, utils
@ -118,7 +118,7 @@ class BaseCitizen(access_points.CitizenAPI):
else: else:
try: try:
response = super().get(url, **kwargs) response = super().get(url, **kwargs)
except RequestException: except RequestError:
self.report_error("Network error while issuing GET request") self.report_error("Network error while issuing GET request")
self.sleep(60) self.sleep(60)
return self.get(url, **kwargs) return self.get(url, **kwargs)
@ -151,7 +151,7 @@ class BaseCitizen(access_points.CitizenAPI):
try: try:
response = super().post(url, data=data, json=json, **kwargs) response = super().post(url, data=data, json=json, **kwargs)
except RequestException: except RequestError:
self.report_error("Network error while issuing POST request") self.report_error("Network error while issuing POST request")
self.sleep(60) self.sleep(60)
return self.post(url, data=data, json=json, **kwargs) return self.post(url, data=data, json=json, **kwargs)
@ -327,7 +327,7 @@ class BaseCitizen(access_points.CitizenAPI):
r"((?P<amount>\d+) item\(s\) )?[eE]xpires? on Day (?P<eday>\d,\d{3}), (?P<time>\d\d:\d\d)", r"((?P<amount>\d+) item\(s\) )?[eE]xpires? on Day (?P<eday>\d,\d{3}), (?P<time>\d\d:\d\d)",
_expire_value, _expire_value,
).groupdict() ).groupdict()
eday = utils.date_from_eday(int(_data["eday"].replace(",", ""))) eday = utils.date_from_eday(int(_data["eday"].replace(",", ""))).date()
dt = constants.erep_tz.localize(datetime.combine(eday, time(*[int(_) for _ in _data["time"].split(":")]))) dt = constants.erep_tz.localize(datetime.combine(eday, time(*[int(_) for _ in _data["time"].split(":")])))
return {"amount": _data.get("amount"), "expiration": dt} return {"amount": _data.get("amount"), "expiration": dt}
@ -613,7 +613,10 @@ class BaseCitizen(access_points.CitizenAPI):
"comment_url", "comment_url",
"rfc2109", "rfc2109",
] ]
cookies = [{attr: getattr(cookie, attr) for attr in cookie_attrs} for cookie in self._req.cookies] cookies = []
for cookie in self._req.cookies.jar:
cookies.append({attr: getattr(cookie, attr, None) for attr in cookie_attrs if hasattr(cookie, attr)})
#cookies = [{attr: getattr(cookie, attr) for attr in cookie_attrs} for cookie in self._req.cookies]
with open(filename, "w") as f: with open(filename, "w") as f:
utils.json_dump( utils.json_dump(
@ -1172,23 +1175,27 @@ class CitizenCompanies(BaseCitizen):
if self.restricted_ip: if self.restricted_ip:
return None return None
self.update_companies() self.update_companies()
self.update_inventory()
data = {"action_type": "production"} data = {"action_type": "production"}
extra = {} extra = {}
raw_factories = wam_holding.get_wam_companies(raw_factory=True) raw_factories = wam_holding.get_wam_companies(raw_factory=True)
fin_factories = wam_holding.get_wam_companies(raw_factory=False) fin_factories = wam_holding.get_wam_companies(raw_factory=False)
free_inventory = self.inventory.total - self.inventory.used free_inventory = self.inventory.total - self.inventory.used
wam_list = raw_factories + fin_factories for _ in range(len(fin_factories + raw_factories) - self.energy.food_fights):
wam_list = wam_list[: self.energy.food_fights] if not self.my_companies.remove_factory_from_wam_list(raw_factories, fin_factories):
raise classes.ErepublikException("Nothing removed from WAM list!")
if int(free_inventory * 0.75) < self.my_companies.get_needed_inventory_usage(wam_list): if int(free_inventory * 0.75) < self.my_companies.get_needed_inventory_usage(fin_factories + raw_factories):
self.update_inventory()
free_inventory = self.inventory.total - self.inventory.used free_inventory = self.inventory.total - self.inventory.used
while wam_list and free_inventory < self.my_companies.get_needed_inventory_usage(wam_list): while (fin_factories + raw_factories) and free_inventory < self.my_companies.get_needed_inventory_usage(
wam_list.pop(-1) fin_factories + raw_factories
):
if not self.my_companies.remove_factory_from_wam_list(raw_factories, fin_factories):
raise classes.ErepublikException("Nothing removed from WAM list!")
if wam_list: if fin_factories + raw_factories:
data.update(extra) data.update(extra)
if not self.details.current_region == wam_holding.region: if not self.details.current_region == wam_holding.region:
self.write_warning("Unable to work as manager because of location - please travel!") self.write_warning("Unable to work as manager because of location - please travel!")
@ -1199,7 +1206,7 @@ class CitizenCompanies(BaseCitizen):
employ_factories = {} employ_factories = {}
response = self._post_economy_work( response = self._post_economy_work(
"production", wam=[c.id for c in wam_list], employ=employ_factories "production", wam=[c.id for c in (fin_factories + raw_factories)], employ=employ_factories
).json() ).json()
return response return response
@ -1957,7 +1964,7 @@ class CitizenTasks(CitizenEconomy):
else: else:
self.reporter.report_action("WORK", json_val=js) self.reporter.report_action("WORK", json_val=js)
else: else:
seconds = self.now.timestamp() % 360 seconds = 360 - self.now.timestamp() % 360
self.write_warning(f"I don't have energy to work. Will sleep for {seconds}s") self.write_warning(f"I don't have energy to work. Will sleep for {seconds}s")
self.sleep(seconds) self.sleep(seconds)
self.work() self.work()
@ -2003,7 +2010,7 @@ class CitizenTasks(CitizenEconomy):
self.buy_food(120) 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: elif self.energy.food_fights < 1 and self.ot_points >= 24:
seconds = self.now.timestamp() % 360 seconds = 360 - self.now.timestamp() % 360
self.write_warning(f"I don't have energy to work OT. Will sleep for {seconds}s") self.write_warning(f"I don't have energy to work OT. Will sleep for {seconds}s")
self.sleep(seconds) self.sleep(seconds)
self.work_ot() self.work_ot()
@ -2277,9 +2284,9 @@ class _Citizen(
if data and kind in self.config.auto_sell: 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(): for quality, amount in data.items():
self.sell_produced_product(kind, quality) self.sell_produced_product(kind, quality, int(data))
elif kind.endswith("Raw"): elif kind.endswith("Raw"):
self.sell_produced_product(kind, 1) self.sell_produced_product(kind, 1, int(data))
else: else:
raise classes.ErepublikException(f"Unknown kind produced '{kind}'") raise classes.ErepublikException(f"Unknown kind produced '{kind}'")
elif self.config.auto_buy_raw and re.search(r"not_enough_[^_]*_raw", response.get("message")): elif self.config.auto_buy_raw and re.search(r"not_enough_[^_]*_raw", response.get("message")):
@ -2336,7 +2343,7 @@ class _Citizen(
def work_as_manager(self) -> bool: 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 :return: if there is more wam work to do
:rtype: bool :rtype: bool
""" """
if self.restricted_ip: if self.restricted_ip:
@ -2344,19 +2351,23 @@ class _Citizen(
return False return False
self.update_citizen_info() self.update_citizen_info()
self.update_companies() self.update_companies()
# Prevent messing up levelup with wam wam_holdings: List[classes.Holding] = [
if True: holding for holding in self.my_companies.get_wam_holdings() if holding.wam_count
regions: Dict[int, classes.Holding] = {} ]
for holding in self.my_companies.holdings.values():
if holding.wam_count:
regions.update({holding.region: holding})
# Check for current region # Check for current region
if self.details.current_region in regions: for holding in wam_holdings:
self._wam(regions.pop(self.details.current_region)) if holding.region == self.details.current_region:
self._wam(holding)
self.update_companies() self.update_companies()
wam_holdings: List[classes.Holding] = [
holding for holding in wam_holdings if not holding.region == self.details.current_region
]
for holding in regions.values(): for holding in wam_holdings:
# Don't travel if not enough energy (either work in all holding factories or 2h energy worth)
if self.energy.energy < 2 * self.energy.interval * 10 < holding.wam_count * 10:
break
raw_usage = holding.get_wam_raw_usage() raw_usage = holding.get_wam_raw_usage()
free_storage = self.inventory.total - self.inventory.used free_storage = self.inventory.total - self.inventory.used
if (raw_usage["frm"] + raw_usage["wrm"]) * 100 > free_storage: if (raw_usage["frm"] + raw_usage["wrm"]) * 100 > free_storage:
@ -2367,23 +2378,14 @@ class _Citizen(
self.update_companies() self.update_companies()
wam_count = self.my_companies.get_total_wam_count() wam_count = self.my_companies.get_total_wam_count()
if wam_count:
self.logger.debug(f"Wam ff lockdown is now {wam_count}, was {self.my_companies.ff_lockdown}")
self.my_companies.ff_lockdown = wam_count
self.travel_to_residence() self.travel_to_residence()
return bool(wam_count) return bool(wam_count)
else:
self.write_warning("Did not WAM because I would mess up levelup!")
self.my_companies.ff_lockdown = 0
self.update_companies()
return bool(self.my_companies.get_total_wam_count())
class Citizen(_Citizen): class Citizen(_Citizen):
_concurrency_lock: Event _concurrency_lock: Event
_update_lock: Event _update_lock: Event
_update_timeout: int = 10 _update_timeout: int = 30
_concurrency_timeout: int = 600 _concurrency_timeout: int = 600
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -7,7 +7,7 @@ from decimal import Decimal
from io import BytesIO from io import BytesIO
from typing import Any, Dict, Generator, Iterable, List, NamedTuple, NoReturn, Optional, Tuple, Union from typing import Any, Dict, Generator, Iterable, List, NamedTuple, NoReturn, Optional, Tuple, Union
from requests import HTTPError, Response, Session, post from httpx import HTTPError, Response, Client as Session, post
from erepublik import _types as types from erepublik import _types as types
from erepublik import constants, utils from erepublik import constants, utils
@ -22,6 +22,8 @@ __all__ = [
"Energy", "Energy",
"ErepublikException", "ErepublikException",
"ErepublikNetworkException", "ErepublikNetworkException",
"CloudFlareSessionError",
"CaptchaSessionError",
"EnergyToFight", "EnergyToFight",
"Holding", "Holding",
"Inventory", "Inventory",
@ -296,6 +298,10 @@ class MyCompanies:
_companies: weakref.WeakSet _companies: weakref.WeakSet
_citizen: weakref.ReferenceType _citizen: weakref.ReferenceType
companies: Generator[Company, None, None] companies: Generator[Company, None, None]
_frm_fab_ids = (1, 7, 8, 9, 10, 11)
_wrm_fab_ids = (2, 12, 13, 14, 15, 16)
_hrm_fab_ids = (4, 18, 19, 20, 21, 22)
_arm_fab_ids = (23, 24, 25, 26, 27, 28)
def __init__(self, citizen): def __init__(self, citizen):
self._citizen = weakref.ref(citizen) self._citizen = weakref.ref(citizen)
@ -359,14 +365,58 @@ class MyCompanies:
@staticmethod @staticmethod
def get_needed_inventory_usage(companies: Union[Company, Iterable[Company]]) -> Decimal: def get_needed_inventory_usage(companies: Union[Company, Iterable[Company]]) -> Decimal:
if isinstance(companies, list): if isinstance(companies, list):
return sum(company.products_made * 100 if company.is_raw else 1 for company in companies) return sum(company.products_made * (100 if company.is_raw else 1) for company in companies)
else: else:
return companies.products_made return companies.products_made
def remove_factory_from_wam_list(self, raw_factories, final_factories):
frm, wrm, *_ = self.get_raw_usage_for_companies(*final_factories, *raw_factories)
inv_raw = self.citizen.inventory.raw
for raw, ids, exc in [(frm, self._frm_fab_ids, False), (wrm, self._wrm_fab_ids, False), (None, None, True)]:
if exc:
if final_factories:
final_factories.sort(key=lambda c: c.raw_usage)
return final_factories.pop(-1)
elif raw_factories:
raw_factories.sort(key=lambda c: c.raw_usage)
return raw_factories.pop(-1)
else:
if raw:
raw += Decimal(
inv_raw.get(constants.INDUSTRIES[ids[1]], {}).get(0, {}).get("amount", Decimal("0.0"))
)
if raw > 0:
to_remove = sorted(raw_factories, key=lambda c: (c.industry not in ids, c.raw_usage))
if to_remove:
return raw_factories.pop(raw_factories.index(to_remove[0]))
else:
to_remove = sorted(final_factories, key=lambda c: (c.industry != ids[0], c.raw_usage))
if to_remove:
return final_factories.pop(final_factories.index(to_remove[0]))
def get_raw_usage_for_companies(self, *companies: Company) -> Tuple[Decimal, Decimal, Decimal, Decimal]:
frm = wrm = hrm = arm = Decimal("0.00")
for company in companies:
if company.industry in self._frm_fab_ids:
frm += company.raw_usage
elif company.industry in self._wrm_fab_ids:
wrm += company.raw_usage
elif company.industry in self._hrm_fab_ids:
hrm += company.raw_usage
elif company.industry in self._arm_fab_ids:
arm += company.raw_usage
return frm, wrm, hrm, arm
@property @property
def companies(self) -> Generator[Company, None, None]: def companies(self) -> Generator[Company, None, None]:
return (c for c in self._companies) return (c for c in self._companies)
def get_wam_holdings(self) -> Generator[Holding, None, None]:
for holding in sorted(
self.holdings.values(), key=lambda h: (-len(h.get_wam_companies(False)), -len(h.get_wam_companies()))
):
yield holding
def __str__(self): def __str__(self):
return f"MyCompanies: {sum(1 for _ in self.companies)} companies in {len(self.holdings)} holdings" return f"MyCompanies: {sum(1 for _ in self.companies)} companies in {len(self.holdings)} holdings"
@ -722,7 +772,7 @@ class Reporter:
def __init__(self, citizen): def __init__(self, citizen):
self._citizen = weakref.ref(citizen) self._citizen = weakref.ref(citizen)
self._req = Session() self._req = Session(follow_redirects=True)
self.url = "https://erep.lv" self.url = "https://erep.lv"
self._req.headers.update({"user-agent": "eRepublik Script Reporter v3", "erep-version": utils.__version__}) self._req.headers.update({"user-agent": "eRepublik Script Reporter v3", "erep-version": utils.__version__})
self.__to_update = [] self.__to_update = []

View File

@ -8,12 +8,15 @@ __all__ = [
"min_datetime", "min_datetime",
"max_datetime", "max_datetime",
"Country", "Country",
"Industries",
"Rank", "Rank",
"AIR_RANKS", "AIR_RANKS",
"AIR_RANK_NAMES",
"AIR_RANK_POINTS", "AIR_RANK_POINTS",
"COUNTRIES", "COUNTRIES",
"FOOD_ENERGY", "FOOD_ENERGY",
"GROUND_RANKS", "GROUND_RANKS",
"GROUND_RANK_NAMES",
"GROUND_RANK_POINTS", "GROUND_RANK_POINTS",
"INDUSTRIES", "INDUSTRIES",
"TERRAINS", "TERRAINS",
@ -118,7 +121,6 @@ class Industries:
14: "WRM q3", 14: "WRM q3",
15: "WRM q4", 15: "WRM q4",
16: "WRM q5", 16: "WRM q5",
17: "houseRaw",
18: "houseRaw", 18: "houseRaw",
19: "HRM q2", 19: "HRM q2",
20: "HRM q3", 20: "HRM q3",
@ -131,7 +133,7 @@ class Industries:
28: "ARM q5", 28: "ARM q5",
} }
def __getitem__(self, item) -> Optional[Union[int, str]]: def __getitem__(self, item: Union[int, str]) -> Optional[Union[int, str]]:
if isinstance(item, int): if isinstance(item, int):
return self.__by_id.get(item, None) return self.__by_id.get(item, None)
elif isinstance(item, str): elif isinstance(item, str):

View File

@ -12,8 +12,8 @@ from pathlib import Path
from typing import Any, Dict, List, Union from typing import Any, Dict, List, Union
import pytz import pytz
import requests import httpx
from requests import Response from httpx import Response
from erepublik import __version__, constants from erepublik import __version__, constants
@ -93,7 +93,7 @@ def eday_from_date(date: Union[datetime.date, datetime.datetime] = None) -> int:
return (date - datetime.datetime(2007, 11, 20, 0, 0, 0)).days return (date - datetime.datetime(2007, 11, 20, 0, 0, 0)).days
def date_from_eday(eday: int) -> datetime.date: def date_from_eday(eday: int) -> datetime.datetime:
return localize_dt(datetime.date(2007, 11, 20)) + datetime.timedelta(days=eday) return localize_dt(datetime.date(2007, 11, 20)) + datetime.timedelta(days=eday)
@ -204,7 +204,7 @@ def calculate_hit(
def get_ground_hit_dmg_value( def get_ground_hit_dmg_value(
citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, weapon_power: int = 200 citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, weapon_power: int = 200
) -> Decimal: ) -> Decimal:
r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json() r = httpx.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json()
rang = r["military"]["militaryData"]["ground"]["rankNumber"] rang = r["military"]["militaryData"]["ground"]["rankNumber"]
strength = r["military"]["militaryData"]["ground"]["strength"] strength = r["military"]["militaryData"]["ground"]["strength"]
elite = r["citizenAttributes"]["level"] > 100 elite = r["citizenAttributes"]["level"] > 100
@ -217,7 +217,7 @@ def get_ground_hit_dmg_value(
def get_air_hit_dmg_value( def get_air_hit_dmg_value(
citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, weapon_power: int = 0 citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, weapon_power: int = 0
) -> Decimal: ) -> Decimal:
r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json() r = httpx.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json()
rang = r["military"]["militaryData"]["aircraft"]["rankNumber"] rang = r["military"]["militaryData"]["aircraft"]["rankNumber"]
elite = r["citizenAttributes"]["level"] > 100 elite = r["citizenAttributes"]["level"] > 100
return calculate_hit(0, rang, true_patriot, elite, natural_enemy, booster, weapon_power) return calculate_hit(0, rang, true_patriot, elite, natural_enemy, booster, weapon_power)

View File

@ -1,16 +1,16 @@
-r requirements.txt -r requirements.txt
-r requirements-tests.txt -r requirements-tests.txt
bump2version==1.0.1 bump2version==1.0.1
coverage==6.1.1 coverage==6.3.2
edx-sphinx-theme==3.0.0 edx-sphinx-theme==3.0.0
flake8==4.0.1 flake8==4.0.1
ipython>=7.29.0 ipython>=8.1.1
jedi!=0.18.0 jedi!=0.18.0
isort==5.9.3 isort==5.10.1
pre-commit==2.15.0 pre-commit==2.17.0
pur==5.4.2 pur==6.0.1
responses==0.15.0 responses==0.18.0
Sphinx==4.2.0 Sphinx==4.4.0
twine==3.4.2 twine==3.8.0
wheel==0.37.0 wheel==0.37.1
black==21.7b0 black==22.1.0

View File

@ -1,2 +1,2 @@
-r requirements.txt -r requirements.txt
pytest==6.2.5 pytest==7.0.1

View File

@ -1,4 +1,8 @@
PySocks==1.7.1 PySocks==1.7.1
pytz==2021.3 pytz>2021.0
requests==2.27.0 httpx==0.22.0
requests-toolbelt==0.9.1 h2==4.1.0
socksio==1.0.0
brotli==1.0.9
#requests>2.25.0,!=2.27.0
#requests-toolbelt==0.9.1

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 0.28.1 current_version = 0.30.0.4
commit = True commit = True
tag = True tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)? parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
@ -19,8 +19,8 @@ universal = 1
[flake8] [flake8]
exclude = docs,.git,log,debug,venv exclude = docs,.git,log,debug,venv
line_length = 140 line_length = 120
max-line-length = 140 max-line-length = 120
ignore = D100,D101,D102,D103,E203 ignore = D100,D101,D102,D103,E203
[pycodestyle] [pycodestyle]
@ -38,4 +38,4 @@ warn_unused_configs = True
[isort] [isort]
multi_line_output = 2 multi_line_output = 2
line_length = 140 line_length = 120

View File

@ -49,6 +49,6 @@ setup(
test_suite="tests", test_suite="tests",
tests_require=test_requirements, tests_require=test_requirements,
url="https://github.com/eeriks/erepublik/", url="https://github.com/eeriks/erepublik/",
version="0.28.1", version="0.30.0.4",
zip_safe=False, zip_safe=False,
) )

View File

@ -26,111 +26,114 @@ class TestErepublik(unittest.TestCase):
self.citizen.energy.energy = 1000 self.citizen.energy.energy = 1000
self.assertFalse(self.citizen.should_do_levelup) self.assertFalse(self.citizen.should_do_levelup)
def test_should_travel_to_fight(self): # def deprecated_test_should_travel_to_fight(self):
self.citizen.config.always_travel = True # self.citizen.config.always_travel = True
self.assertTrue(self.citizen.should_travel_to_fight()) # self.assertTrue(self.citizen.should_travel_to_fight())
self.citizen.config.always_travel = False # self.citizen.config.always_travel = False
self.assertFalse(self.citizen.should_travel_to_fight()) # self.assertFalse(self.citizen.should_travel_to_fight())
#
self.citizen.energy.energy = 5960 # self.citizen.energy.energy = 5960
self.citizen.energy.interval = 30 # self.citizen.energy.interval = 30
self.citizen.energy.limit = 6000 # self.citizen.energy.limit = 6000
self.citizen.details.xp = 14850 # self.citizen.details.xp = 14850
self.assertTrue(self.citizen.should_travel_to_fight()) # self.assertTrue(self.citizen.should_travel_to_fight())
self.citizen.details.xp = 15000 # self.citizen.details.xp = 15000
self.citizen.energy.energy = 5000 # self.citizen.energy.energy = 5000
self.assertFalse(self.citizen.should_travel_to_fight()) # self.assertFalse(self.citizen.should_travel_to_fight())
#
self.citizen.energy.energy = 5910 # self.citizen.energy.energy = 5910
self.assertTrue(self.citizen.should_travel_to_fight()) # self.assertTrue(self.citizen.should_travel_to_fight())
self.citizen.energy.energy = 5900 # self.citizen.energy.energy = 5900
self.assertFalse(self.citizen.should_travel_to_fight()) # self.assertFalse(self.citizen.should_travel_to_fight())
#
# self.citizen.next_reachable_energy and self.citizen.config.next_energy # # self.citizen.next_reachable_energy and self.citizen.config.next_energy
self.citizen.config.next_energy = True # self.citizen.config.next_energy = True
self.citizen.energy.limit = 10000 # self.citizen.energy.limit = 10000
self.citizen.details.next_pp = [5000, 5250, 5750, 6250, 6750] # self.citizen.details.next_pp = [5000, 5250, 5750, 6250, 6750]
self.citizen.details.pp = 4900 # self.citizen.details.pp = 4900
self.citizen.energy.energy = 8510 # self.citizen.energy.energy = 8510
self.assertEqual(self.citizen.next_reachable_energy, 850) # self.assertEqual(self.citizen.next_reachable_energy, 850)
self.citizen.energy.energy = 8490 # self.citizen.energy.energy = 8490
self.assertTrue(self.citizen.should_travel_to_fight()) # self.assertTrue(self.citizen.should_travel_to_fight())
self.assertEqual(self.citizen.next_reachable_energy, 350) # self.assertEqual(self.citizen.next_reachable_energy, 350)
self.citizen.energy.energy = 250 # self.citizen.energy.energy = 250
self.assertFalse(self.citizen.should_travel_to_fight()) # self.assertFalse(self.citizen.should_travel_to_fight())
self.assertEqual(self.citizen.next_reachable_energy, 0) # self.assertEqual(self.citizen.next_reachable_energy, 0)
#
def test_should_fight(self): # def deprecated_test_should_fight(self):
def is_wc_close(): # def is_wc_close():
return self.citizen.max_time_till_full_ff > self.citizen.time_till_week_change # return self.citizen.max_time_till_full_ff > self.citizen.time_till_week_change
#
self.citizen.config.fight = False # self.citizen.config.fight = False
self.assertEqual(self.citizen.should_fight(), (0, "Fighting not allowed!", False)) # self.assertEqual(self.citizen.should_fight(), (0, "Fighting not allowed!", False))
#
self.citizen.config.fight = True # self.citizen.config.fight = True
#
# Level up # # Level up
self.citizen.energy.limit = 3000 # self.citizen.energy.limit = 3000
self.citizen.details.xp = 24705 # self.citizen.details.xp = 24705
if not is_wc_close: # 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.energy = 5950 # self.citizen.energy.energy = 5950
self.citizen.energy.interval = 30 # 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.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 # self.citizen.my_companies.ff_lockdown = 0
#
# Level up reachable # # Level up reachable
self.citizen.details.xp = 24400 # 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.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.my_companies.ff_lockdown = 0
#
self.citizen.details.xp = 21000 # 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.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.my_companies.ff_lockdown = 0
self.citizen.details.pp = 80 # self.citizen.details.pp = 80
#
# All-in (type = all-in and full ff) # # All-in (type = all-in and full ff)
self.citizen.config.all_in = True # 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.citizen.my_companies.ff_lockdown = 160
self.assertEqual( # self.assertEqual(
self.citizen.should_fight(), # self.citizen.should_fight(),
(435, "Fight count modified (old count: 595 | FF: 595 | WAM ff_lockdown: 160 | New count: 435)", False), # (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.my_companies.ff_lockdown = 0
self.citizen.config.air = True #
self.citizen.energy.energy = 4000 # self.citizen.config.air = True
self.assertEqual(self.citizen.should_fight(), (400, "Fighting all-in in AIR. Doing 400 hits", False)) # self.citizen.energy.energy = 4000
self.citizen.my_companies.ff_lockdown = 160 # self.assertEqual(self.citizen.should_fight(), (400, "Fighting all-in in AIR. Doing 400 hits", False))
self.assertEqual( # self.citizen.my_companies.ff_lockdown = 160
self.citizen.should_fight(), # self.assertEqual(
(240, "Fight count modified (old count: 400 | FF: 400 | WAM ff_lockdown: 160 | New count: 240)", False), # self.citizen.should_fight(),
) # (240, "Fight count modified (old count: 400 | FF: 400
self.citizen.my_companies.ff_lockdown = 0 # | WAM ff_lockdown: 160 | New count: 240)", False),
self.citizen.config.all_in = False # )
# self.citizen.my_companies.ff_lockdown = 0
self.citizen.config.next_energy = True # self.citizen.config.all_in = False
self.citizen.energy.limit = 10000 #
self.citizen.details.next_pp = [100, 150, 250, 400, 500] # self.citizen.config.next_energy = True
self.assertEqual(self.citizen.should_fight(), (320, "Fighting for +1 energy. Doing 320 hits", False)) # self.citizen.energy.limit = 10000
self.citizen.my_companies.ff_lockdown = 160 # self.citizen.details.next_pp = [100, 150, 250, 400, 500]
self.assertEqual( # self.assertEqual(self.citizen.should_fight(), (320, "Fighting for +1 energy. Doing 320 hits", False))
self.citizen.should_fight(), # self.citizen.my_companies.ff_lockdown = 160
(160, "Fight count modified (old count: 320 | FF: 400 | WAM ff_lockdown: 160 | New count: 160)", False), # self.assertEqual(
) # self.citizen.should_fight(),
self.citizen.my_companies.ff_lockdown = 0 # (160, "Fight count modified (old count: 320 | FF: 400
self.citizen.energy.limit = 3000 # | WAM ff_lockdown: 160 | New count: 160)", False),
self.citizen.details.next_pp = [19250, 20000] # )
self.citizen.config.next_energy = False # self.citizen.my_companies.ff_lockdown = 0
# self.citizen.energy.limit = 3000
# 1h worth of energy # self.citizen.details.next_pp = [19250, 20000]
self.citizen.energy.energy = 5910 # self.citizen.config.next_energy = False
self.assertEqual(self.citizen.should_fight(), (30, "Fighting for 1h energy. Doing 30 hits", True)) #
# # 1h worth of energy
# self.citizen.energy.energy = 5910
# self.assertEqual(self.citizen.should_fight(), (30, "Fighting for 1h energy. Doing 30 hits", True))