Compare commits

...

62 Commits

Author SHA1 Message Date
ed4ffe5af6 Bump version: 0.22.1 → 0.22.1.1 2020-10-22 18:04:13 +03:00
8aa90a7dbf Lint 2020-10-22 18:04:06 +03:00
e798859105 Bump version: 0.22.0 → 0.22.1 2020-10-22 16:27:09 +03:00
241f1642ce Session dump/load, Citizen.inventory update, memory and network optimization, python3.6 support 2020-10-22 16:26:59 +03:00
a4128b5d89 Bump version: 0.21.5.8 → 0.22.0 2020-10-21 14:46:24 +03:00
22c2a0ffd2 New Features!
Dump session to file!
Load session from file!
2020-10-21 14:45:29 +03:00
38f0335354 Bump version: 0.21.5.7 → 0.21.5.8 2020-10-07 09:22:01 +03:00
889435b94e House renewal bugfix 2020-10-07 09:21:36 +03:00
bb16c27674 Bump version: 0.21.5.6 → 0.21.5.7 2020-10-06 12:35:19 +03:00
963d7ca11a bugfix 2020-10-06 12:35:14 +03:00
36c7fefdf7 Bump version: 0.21.5.5 → 0.21.5.6 2020-09-30 08:40:14 +03:00
d9fa30b06e bugfix in default weapon switch 2020-09-30 08:35:35 +03:00
b53dc447f4 switch to deploy 2020-09-30 08:13:36 +03:00
233d8d83f8 Bump version: 0.21.5.4 → 0.21.5.5 2020-09-29 18:02:52 +03:00
ec62d90aa2 wheeloffortune bugfix 2020-09-29 18:02:52 +03:00
0c433a56da Bump version: 0.21.5.3 → 0.21.5.4 2020-09-29 17:38:44 +03:00
ad24338f4d wheeloffortune bugfix 2020-09-29 17:38:26 +03:00
6f4bc65d1b Bump version: 0.21.5.2 → 0.21.5.3 2020-09-29 17:21:00 +03:00
cc09ba7ee7 wheeloffortune argument bugfix 2020-09-29 17:20:53 +03:00
9e1166a460 Bump version: 0.21.5.1 → 0.21.5.2 2020-09-29 17:17:07 +03:00
fb0042c00d wheeloffortune url bugfix 2020-09-29 17:17:00 +03:00
bb800578e7 Bump version: 0.21.5 → 0.21.5.1 2020-09-29 15:06:39 +03:00
7025f750dc PySocks as requirement 2020-09-29 15:06:30 +03:00
bf77f21b60 MyCompanies export as dict optimised 2020-09-29 15:04:51 +03:00
6b7639d7fb Bump version: 0.21.4.8 → 0.21.5 2020-09-29 10:52:36 +03:00
3b1c1928fd Added proxy support 😉 2020-09-29 10:52:14 +03:00
2e26c2db79 Bump version: 0.21.4.7 → 0.21.4.8 2020-09-25 10:10:33 +03:00
78c055fee2 bugfix 2020-09-25 10:10:27 +03:00
fe41c4cdc6 Bump version: 0.21.4.6 → 0.21.4.7 2020-09-23 13:22:38 +03:00
123b6cf4ed Fight reporting unified and moved to Reporter.report_fighting 2020-09-23 13:22:31 +03:00
f652b02443 Fight reporting duplicate 2020-09-23 13:17:46 +03:00
73537e4742 Bump version: 0.21.4.5 → 0.21.4.6 2020-09-23 11:15:32 +03:00
955432e0d2 Check also for maintenance in BaseCitizen._errors_in_response() 2020-09-23 11:15:18 +03:00
1d93864dca Bump version: 0.21.4.4 → 0.21.4.5 2020-09-22 16:30:03 +03:00
c472d688be error logging 2020-09-22 16:29:50 +03:00
bff9a2bec9 Bump version: 0.21.4.3 → 0.21.4.4 2020-09-21 20:40:40 +03:00
973ea40e00 bugfix 2020-09-21 20:40:24 +03:00
52c85fdf28 Bump version: 0.21.4.2 → 0.21.4.3 2020-09-21 20:18:02 +03:00
a889e9efa1 bugfix 2020-09-21 20:17:56 +03:00
a9a0cdc6d5 Bump version: 0.21.4.1 → 0.21.4.2 2020-09-21 12:39:44 +03:00
1c102488b6 Test fix to not run if WC end is near 2020-09-21 12:39:27 +03:00
c38acef2a0 Bump version: 0.21.4 → 0.21.4.1 2020-09-21 11:34:59 +03:00
48b5e473aa doc generation bugfixes 2020-09-21 11:34:50 +03:00
7fadeb1a49 Bump version: 0.21.3 → 0.21.4 2020-09-21 11:26:51 +03:00
b723660f23 Fixups and type checks 2020-09-21 11:26:32 +03:00
f10eeec498 requirement update 2020-09-21 11:24:01 +03:00
230167f93d mypy bugfix 2020-09-21 11:23:43 +03:00
d5ed989e80 Bump version: 0.21.2.2 → 0.21.3 2020-08-18 16:37:27 +03:00
6fc24b8adf requirements 2020-08-18 16:37:07 +03:00
cf797f2f60 Fixes and updates 2020-08-18 13:14:41 +03:00
ad29045ace Bump version: 0.21.2.1 → 0.21.2.2 2020-07-29 11:40:41 +03:00
c919e46af5 PoliticsAPI extended and bugfixed 2020-07-29 11:40:32 +03:00
644b4d70e1 Bump version: 0.21.2 → 0.21.2.1 2020-07-29 11:23:31 +03:00
6dbbd054ba bugfix 2020-07-29 09:27:02 +03:00
0ee952e504 bugfix 2020-07-29 09:21:49 +03:00
bb9b198a53 Bump version: 0.21.1 → 0.21.2 2020-07-28 19:34:19 +03:00
cb22e631ca Merge branch 'memory-optimisation'
* memory-optimisation:
  Company cleanup optimisation
  JSON.dump sort_keys parameter throwing mysterious errors
  Fixed memory leak in Battle and MyCompanies classes
2020-07-28 19:33:52 +03:00
c43e20c8f6 Return all Non-Terrain divisions and their bh damage 2020-07-28 19:33:30 +03:00
c8f41b97af Company cleanup optimisation 2020-07-28 19:25:22 +03:00
d483bcbcb9 JSON.dump sort_keys parameter throwing mysterious errors 2020-07-28 18:29:25 +03:00
a316f277fb Fixed memory leak in Battle and MyCompanies classes 2020-07-28 18:28:03 +03:00
e8c81d17e6 Weapons kind should be singular - 'weapon' 2020-07-19 07:56:15 +03:00
15 changed files with 704 additions and 509 deletions

1
.gitignore vendored
View File

@ -104,3 +104,4 @@ ENV/
debug/
log/
docs/
*dump.json

View File

@ -2,6 +2,15 @@
History
=======
0.22.0 (2020-10-22)
-------------------
* Ability to dump session and restore from file
* Proxy support
* Inventory updates
* Remove market offers
* Memory and network optimizations
* Python 3.6 supported
0.20.0 (2020-06-15)
-------------------
* Massive restructuring

View File

@ -88,9 +88,3 @@ dist: clean ## builds source and wheel package
install: clean ## install the package to the active Python's site-packages
python setup.py install
setcommit:
bash set_commit_id.sh
# commit=`git log -1 --pretty=format:%h`
# sed -i.bak -E "s|COMMIT_ID = \".+\"|COMMIT_ID = \"$(commit)\"|g" erepublik/utils.py
# mv erepublik/utils.py.bak erepublik/utils.py

View File

@ -4,9 +4,9 @@
__author__ = """Eriks Karls"""
__email__ = 'eriks@72.lv'
__version__ = '0.21.1'
__version__ = '0.22.1.1'
from erepublik import classes, utils, constants
from erepublik.citizen import Citizen
__all__ = ["classes", "utils", "Citizen", ]
__all__ = ["classes", "utils", "Citizen", 'constants']

View File

@ -12,32 +12,36 @@ __all__ = ['SlowRequests', 'CitizenAPI']
class SlowRequests(Session):
last_time: datetime.datetime
timeout = datetime.timedelta(milliseconds=500)
uas = [
timeout: datetime.timedelta = datetime.timedelta(milliseconds=500)
uas: List[str] = [
# Chrome
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36', # noqa
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36', # noqa
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36', # noqa
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', # noqa
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', # noqa
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
# FireFox
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:74.0) Gecko/20100101 Firefox/74.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0',
]
debug = False
debug: bool = False
def __init__(self):
def __init__(self, proxies: Dict[str, str] = None):
super().__init__()
if proxies:
self.proxies = proxies
self.request_log_name = utils.get_file(utils.now().strftime("debug/requests_%Y-%m-%d.log"))
self.last_time = utils.now()
self.headers.update({
@ -113,12 +117,13 @@ class SlowRequests(Session):
class CitizenBaseAPI:
url: str = "https://www.erepublik.com/en"
_req: SlowRequests = None
token: str = ""
_req: SlowRequests
token: str
def __init__(self):
""" Class for unifying eRepublik known endpoints and their required/optional parameters """
self._req = SlowRequests()
self.token = ""
def post(self, url: str, data=None, json=None, **kwargs) -> Response:
return self._req.post(url, data, json, **kwargs)
@ -129,6 +134,14 @@ class CitizenBaseAPI:
def _get_main(self) -> Response:
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}'
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'socks5://{host}:{port}'
self._req.proxies = dict(http=url)
class ErepublikAnniversaryAPI(CitizenBaseAPI):
def _post_main_collect_anniversary_reward(self) -> Response:
@ -151,10 +164,10 @@ class ErepublikAnniversaryAPI(CitizenBaseAPI):
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}/wheeloffortune-spin", data={'_token': self.token, "cost": 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}/wheeloffortune-build", data={'_token': self.token})
return self.post(f"{self.url}/main/wheeloffortune-build", data={'_token': self.token})
class ErepublikArticleAPI(CitizenBaseAPI):
@ -331,27 +344,32 @@ class ErepublikEconomyAPI(CitizenBaseAPI):
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, amount: int, buy: bool = False, **kwargs) -> Response:
if buy:
data = dict(_token=self.token, offerId=kwargs['offer'], amount=amount, orderBy="price_asc", currentPage=1,
buyAction=1)
else:
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=amount, sellAction='postOffer')
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:
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:
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:
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:
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 +451,7 @@ class ErepublikMilitaryAPI(CitizenBaseAPI):
class ErepublikPoliticsAPI(CitizenBaseAPI):
def _get_candidate_party(self, party_slug: str) -> Response:
return self.post(f"{self.url}/candidate/{party_slug}")
return self.get(f"{self.url}/candidate/{party_slug}")
def _get_main_party_members(self, party_id: int) -> Response:
return self.get(f"{self.url}/main/party-members/{party_id}")
@ -448,6 +466,13 @@ class ErepublikPoliticsAPI(CitizenBaseAPI):
def _get_presidential_elections(self, country_id: int, timestamp: int) -> Response:
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))
def _get_auto_propose_president_candidate(self, party_slug: str) -> Response:
return self.get(f"{self.url}/auto-propose-president-candidate/{party_slug}")
class ErepublikPresidentAPI(CitizenBaseAPI):
def _post_wars_attack_region(self, war_id: int, region_id: int, region_name: str) -> Response:

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,9 @@
import datetime
import hashlib
import threading
import weakref
from decimal import Decimal
from typing import Any, Dict, List, NamedTuple, Optional, Tuple, Union
from typing import Any, Dict, List, NamedTuple, Tuple, Union, NoReturn, Generator, Iterable
from requests import Response, Session, post
@ -27,26 +28,38 @@ class Holding:
id: int
region: int
companies: List["Company"]
name: str
_citizen = weakref.ReferenceType
def __init__(self, _id: int, region: int, citizen):
self.citizen = citizen
def __init__(self, _id: int, region: int, citizen, name: str = None):
self._citizen = weakref.ref(citizen)
self.id: int = _id
self.region: int = region
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"
else:
name += "companies"
self.name = name
@property
def wam_count(self) -> int:
return sum([company.wam_enabled and not company.already_worked for company in self.companies])
return len([1 for company in self.companies if company.wam_enabled and not company.already_worked])
@property
def wam_companies(self) -> List["Company"]:
def wam_companies(self) -> Iterable["Company"]:
return [company for company in self.companies if company.wam_enabled]
@property
def employable_companies(self) -> List["Company"]:
def employable_companies(self) -> Iterable["Company"]:
return [company for company in self.companies if company.preset_works]
def add_company(self, company: "Company"):
def add_company(self, company: "Company") -> NoReturn:
self.companies.append(company)
self.companies.sort()
@ -60,7 +73,7 @@ class Holding:
wrm += company.raw_usage
return dict(frm=frm, wrm=wrm)
def get_wam_companies(self, raw_factory: bool = None):
def get_wam_companies(self, raw_factory: bool = None) -> List["Company"]:
raw = []
factory = []
for company in self.wam_companies:
@ -69,18 +82,15 @@ class Holding:
raw.append(company)
else:
factory.append(company)
if raw_factory is not None and not raw_factory:
return factory
elif raw_factory is not None and raw_factory:
return raw
elif raw_factory is None:
if raw_factory is None:
return raw + factory
else:
raise ErepublikException("raw_factory should be True/False/None")
return raw if raw_factory else factory
def __str__(self):
name = f"Holding (#{self.id}) with {len(self.companies)} "
if len(self.companies) % 10 == 1:
def __str__(self) -> str:
comp = len(self.companies)
name = f"Holding (#{self.id}) with {comp} "
if comp == 1:
name += "company"
else:
name += "companies"
@ -90,11 +100,17 @@ class Holding:
return str(self)
@property
def as_dict(self):
return dict(name=str(self), id=self.id, region=self.region, companies=self.companies, wam_count=self.wam_count)
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)
@property
def citizen(self):
return self._citizen()
class Company:
_holding: weakref.ReferenceType
holding: Holding
id: int
quality: int
@ -113,7 +129,7 @@ class Company:
base_production: Decimal, wam_enabled: bool, can_wam: bool, cannot_wam_reason: str, industry: int,
already_worked: bool, preset_works: int
):
self.holding: Holding = holding
self._holding = weakref.ref(holding)
self.id: int = _id
self.industry: int = industry
self.quality: int = self._get_real_quality(quality)
@ -163,7 +179,7 @@ class Company:
@property
def _sort_keys(self):
return not self.is_raw, self._internal_industry, -self.quality, self.id
return not self.is_raw, self._internal_industry, self.quality, self.id
def __hash__(self):
return hash(self._sort_keys)
@ -196,7 +212,7 @@ class Company:
return str(self)
@property
def as_dict(self):
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,
@ -211,6 +227,10 @@ class Company:
# noinspection PyProtectedMember
return self.holding.citizen._post_economy_upgrade_company(self.id, level, self.holding.citizen.details.pin)
@property
def holding(self) -> Holding:
return self._holding()
class MyCompanies:
work_units: int = 0
@ -218,13 +238,14 @@ class MyCompanies:
ff_lockdown: int = 0
holdings: Dict[int, Holding]
companies: List[Company]
_companies: weakref.WeakSet
_citizen: weakref.ReferenceType
companies: Generator[Company, None, None]
def __init__(self, citizen):
from erepublik import Citizen
self.citizen: Citizen = citizen
self.holdings: Dict[int, Holding] = dict()
self.companies: List[Company] = list()
self._citizen = weakref.ref(citizen)
self.holdings = dict()
self._companies = weakref.WeakSet()
self.next_ot_time = utils.now()
def prepare_holdings(self, holdings: Dict[str, Dict[str, Any]]):
@ -233,10 +254,11 @@ class 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)})
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
self.holdings.update({0: Holding(0, 0, self.citizen, 'Unassigned')}) # unassigned
def prepare_companies(self, companies: Dict[str, Dict[str, Any]]):
"""
@ -258,7 +280,7 @@ class MyCompanies:
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.append(company)
self._companies.add(company)
holding.add_company(company)
def get_employable_factories(self) -> Dict[int, int]:
@ -268,27 +290,41 @@ class MyCompanies:
return sum([holding.wam_count for holding in self.holdings.values()])
@staticmethod
def get_needed_inventory_usage(companies: Union[Company, List[Company]]) -> Decimal:
def get_needed_inventory_usage(companies: Union[Company, Iterable[Company]]) -> Decimal:
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:
return companies.products_made
@property
def companies(self) -> Generator[Company, None, None]:
return (c for c in self._companies)
def __str__(self):
return f"MyCompanies: {len(self.companies)} companies in {len(self.holdings)} holdings"
return f"MyCompanies: {sum(1 for _ in self.companies)} companies in {len(self.holdings)} holdings"
def __repr__(self):
return str(self)
def __clear_data(self):
for holding in self.holdings.values():
for company in holding.companies: # noqa
del company
holding.companies.clear()
self.companies.clear()
self._companies.clear()
@property
def as_dict(self):
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=self.holdings, company_count=len(self.companies))
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):
return self._citizen()
class Config:
@ -420,24 +456,25 @@ class Energy:
class Details:
xp = 0
cc = 0
pp = 0
pin = None
gold = 0
xp: int = 0
cc: float = 0
pp: int = 0
pin: str = None
gold: float = 0
next_pp: List[int] = None
citizen_id = 0
citizen_id: int = 0
citizenship: constants.Country
current_region = 0
current_region: int = 0
current_country: constants.Country
residence_region = 0
residence_region: int = 0
residence_country: constants.Country
daily_task_done = False
daily_task_reward = False
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, }
daily_task_done: bool = False
daily_task_reward: bool = False
mayhem_skills: Dict[int, int]
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}
@property
def xp_till_level_up(self):
@ -528,10 +565,13 @@ class Reporter:
queue=self.__to_update)
def __init__(self, citizen):
self.citizen = citizen
self._citizen = weakref.ref(citizen)
self._req = Session()
self.url = "https://api.erep.lv"
self._req.headers.update({"user-agent": "Bot reporter v2"})
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
@ -541,6 +581,10 @@ class Reporter:
self.register_account()
self.allowed = True
@property
def citizen(self):
return self._citizen()
def __update_key(self):
self.key = hashlib.md5(bytes(f"{self.name}:{self.email}", encoding="UTF-8")).hexdigest()
@ -548,10 +592,10 @@ class Reporter:
if self.__to_update:
for unreported_data in self.__to_update:
unreported_data.update(player_id=self.citizen_id, key=self.key)
unreported_data = utils.json.load(utils.json.dumps(unreported_data, cls=MyJSONEncoder, sort_keys=True))
unreported_data = utils.json.loads(utils.json.dumps(unreported_data, cls=MyJSONEncoder))
self._req.post("{}/bot/update".format(self.url), json=unreported_data)
self.__to_update.clear()
data = utils.json.loads(utils.json.dumps(data, cls=MyJSONEncoder, sort_keys=True))
data = utils.json.loads(utils.json.dumps(data, cls=MyJSONEncoder))
r = self._req.post("{}/bot/update".format(self.url), json=data)
return r
@ -593,6 +637,12 @@ class Reporter:
else:
self.__to_update.append(json_data)
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)))
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))
@ -604,13 +654,13 @@ class Reporter:
except: # noqa
return []
def fetch_tasks(self) -> Optional[Tuple[str, Tuple[Any]]]:
def fetch_tasks(self) -> List[Union[str, Tuple[Any]]]:
try:
task_response = self._req.get(f'{self.url}/api/v1/command',
params=dict(citizen=self.citizen_id, key=self.key))
return task_response.json().get('task_collection')
except: # noqa
return
return []
class MyJSONEncoder(utils.json.JSONEncoder):
@ -645,28 +695,29 @@ class BattleSide:
deployed: List[constants.Country]
allies: List[constants.Country]
battle: "Battle"
_battle: weakref.ReferenceType
country: constants.Country
defender: bool
is_defender: bool
def __init__(self, battle: "Battle", country: constants.Country, points: int, allies: List[constants.Country],
deployed: List[constants.Country], defender: bool):
self.battle = battle
self._battle = weakref.ref(battle)
self.country = country
self.points = points
self.allies = allies
self.deployed = deployed
self.defender = defender
self.is_defender = defender
@property
def id(self) -> int:
return self.country.id
def __repr__(self):
side_text = "Defender" if self.defender else "Invader "
side_text = "Defender" if self.is_defender else "Invader "
return f"<BattleSide: {side_text} {self.country.name}|{self.points:02d}p>"
def __str__(self):
side_text = "Defender" if self.defender else "Invader "
side_text = "Defender" if self.is_defender else "Invader "
return f"{side_text} {self.country.name} - {self.points:02d} points"
def __format__(self, format_spec):
@ -674,8 +725,12 @@ class BattleSide:
@property
def as_dict(self):
return dict(points=self.points, country=self.country, defender=self.defender, allies=self.allies,
deployed=self.deployed, battle=repr(self.battle))
return dict(points=self.points, country=self.country, is_defender=self.is_defender, allies=self.allies,
deployed=self.deployed)
@property
def battle(self):
return self._battle()
class BattleDivision:
@ -689,11 +744,12 @@ class BattleDivision:
terrain: int
div: int
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, battle=str(self.battle), end=self.div_end)
epic=self.epic, end=self.div_end)
@property
def is_air(self):
@ -715,7 +771,7 @@ class BattleDivision:
:type wall_for: int
:type wall_dom: float
"""
self.battle = battle
self._battle = weakref.ref(battle)
self.id = div_id
self.end = end
self.epic = epic
@ -738,6 +794,10 @@ class BattleDivision:
def __repr__(self):
return f"<BattleDivision #{self.id} (battle #{self.battle.id})>"
@property
def battle(self):
return self._battle()
class Battle:
id: int
@ -756,7 +816,7 @@ class Battle:
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])
region=[self.region_id, self.region_name], link=self.link)
@property
def has_air(self) -> bool:
@ -765,6 +825,10 @@ class Battle:
return True
return not bool(self.zone_id % 4)
@property
def has_started(self) -> bool:
return self.start <= utils.now()
@property
def has_ground(self) -> bool:
for div in self.div.values():
@ -795,13 +859,15 @@ class Battle:
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
[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
[constants.COUNTRIES[row.get('id')] for row in battle.get('def', {}).get('ally_list') if row['deployed']],
True
)
self.div = {}
@ -810,7 +876,7 @@ class Battle:
if data.get('end'):
end = datetime.datetime.fromtimestamp(data.get('end'), tz=constants.erep_tz)
else:
end = utils.localize_dt(datetime.datetime.max - datetime.timedelta(days=1))
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],
@ -828,7 +894,8 @@ class Battle:
else:
time_part = "-{}".format(self.start - time_now)
return f"Battle {self.id} for {self.region_name[:16]} | {self.invader} : {self.defender} | Round time {time_part}"
return (f"Battle {self.id} for {self.region_name[:16]} | "
f"{self.invader} : {self.defender} | Round time {time_part}")
def __repr__(self):
return f"<Battle #{self.id} {self.invader}:{self.defender} R{self.zone_id}>"
@ -888,7 +955,7 @@ class TelegramBot:
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': bool(len(self._threads))}
'initialized': self.__initialized, 'has_threads': not self._threads}
def do_init(self, chat_id: int, token: str, player_name: str = ""):
self.chat_id = chat_id

View File

@ -1,10 +1,14 @@
import datetime
from typing import Dict, Optional, Union
import pytz
__all__ = ["erep_tz", "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')
min_datetime = erep_tz.localize(datetime.datetime(2007, 11, 20))
max_datetime = erep_tz.localize(datetime.datetime(2281, 9, 4))
class Country:
@ -49,17 +53,17 @@ class Country:
class Industries:
__by_name = {'food': 1, 'weapons': 2, 'house': 4, 'aircraft': 23,
'foodraw': 7, 'weaponraw': 12, 'weaponsraw': 12, 'houseraw': 18, 'aircraftraw': 24,
__by_name = {'food': 1, 'weapon': 2, 'house': 4, 'aircraft': 23,
'foodraw': 7, 'weaponraw': 12, 'houseraw': 18, 'aircraftraw': 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: "Weapons", 4: "House", 23: "Aircraft",
__by_id = {1: "Food", 2: "Weapon", 4: "House", 23: "Aircraft",
7: "foodRaw", 8: "FRM q2", 9: "FRM q3", 10: "FRM q4", 11: "FRM q5",
12: "weaponsRaw", 13: "WRM q2", 14: "WRM q3", 15: "WRM q4", 16: "WRM q5",
18: "houseRaw", 19: "HRM q2", 20: "HRM q3", 21: "HRM q4", 22: "HRM 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]]:

View File

@ -1,4 +1,3 @@
import copy
import datetime
import inspect
import os
@ -8,9 +7,10 @@ import textwrap
import time
import traceback
import unicodedata
import warnings
from decimal import Decimal
from pathlib import Path
from typing import Any, List, Mapping, Optional, Union, Dict
from typing import Any, List, Optional, Union, Dict
import requests
@ -27,8 +27,8 @@ __all__ = ['VERSION', 'calculate_hit', 'caught_error', 'date_from_eday', 'eday_f
'process_error', 'process_warning', 'send_email', 'silent_sleep', 'slugify', 'write_file',
'write_interactive_log', 'write_silent_log']
if not sys.version_info >= (3, 7):
raise AssertionError('This script requires Python version 3.7 and higher\n'
if not sys.version_info >= (3, 6):
raise AssertionError('This script requires Python version 3.6 and higher\n'
'But Your version is v{}.{}.{}'.format(*sys.version_info))
VERSION: str = __version__
@ -43,13 +43,12 @@ def localize_timestamp(timestamp: int) -> datetime.datetime:
def localize_dt(dt: Union[datetime.date, datetime.datetime]) -> datetime.datetime:
try:
try:
return constants.erep_tz.localize(dt)
except AttributeError:
return constants.erep_tz.localize(datetime.datetime.combine(dt, datetime.time(0, 0, 0)))
except ValueError:
return dt.astimezone(constants.erep_tz)
if isinstance(dt, datetime.datetime):
return constants.erep_tz.localize(dt)
elif isinstance(dt, datetime.date):
return constants.erep_tz.localize(datetime.datetime.combine(dt, datetime.time(0, 0, 0)))
else:
raise TypeError(f"Argument dt must be and instance of datetime.datetime or datetime.date not {type(dt)}")
def good_timedelta(dt: datetime.datetime, td: datetime.timedelta) -> datetime.datetime:
@ -117,10 +116,12 @@ def _write_log(msg, timestamp: bool = True, should_print: bool = False):
def write_interactive_log(*args, **kwargs):
kwargs.pop("should_print", None)
_write_log(should_print=True, *args, **kwargs)
def write_silent_log(*args, **kwargs):
kwargs.pop("should_print", None)
_write_log(should_print=False, *args, **kwargs)
@ -230,7 +231,7 @@ def send_email(name: str, content: List[Any], player=None, local_vars: Dict[str,
local_vars['citizen'] = repr(local_vars['citizen'])
from erepublik.classes import MyJSONEncoder
files.append(('file', ("local_vars.json", json.dumps(local_vars, cls=MyJSONEncoder, sort_keys=True),
files.append(('file', ("local_vars.json", json.dumps(local_vars, cls=MyJSONEncoder),
"application/json")))
if isinstance(player, Citizen):
files.append(('file', ("instance.json", player.to_json(indent=True), "application/json")))
@ -279,15 +280,15 @@ def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commi
write_silent_log(log_info)
trace = inspect.trace()
if trace:
trace = trace[-1][0].f_locals
if trace.get('__name__') == '__main__':
trace = {'commit_id': trace.get('COMMIT_ID'),
'interactive': trace.get('INTERACTIVE'),
'version': trace.get('__version__'),
'config': trace.get('CONFIG')}
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')})
else:
trace = dict()
send_email(name, content, citizen, local_vars=trace)
local_vars = dict()
send_email(name, content, citizen, local_vars=local_vars)
def process_warning(log_info: str, name: str, exc_info: tuple, citizen=None, commit_id: str = None):
@ -307,10 +308,10 @@ def process_warning(log_info: str, name: str, exc_info: tuple, citizen=None, com
trace = inspect.trace()
if trace:
trace = trace[-1][0].f_locals
local_vars = trace[-1][0].f_locals
else:
trace = dict()
send_email(name, content, citizen, local_vars=trace)
local_vars = dict()
send_email(name, content, citizen, local_vars=local_vars)
def slugify(value, allow_unicode=False) -> str:
@ -368,3 +369,13 @@ def get_air_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_pat
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 _clear_up_battle_memory(battle):
# del battle.invader._battle, battle.defender._battle
# for div_id, division in battle.div.items():
# del division._battle
def deprecation(message):
warnings.warn(message, DeprecationWarning, stacklevel=2)

View File

@ -1,6 +1,6 @@
from datetime import timedelta
from erepublik import Citizen, utils
from erepublik import Citizen, utils, constants
CONFIG = {
'email': 'player@email.com',
@ -18,7 +18,7 @@ def main():
player.set_debug(CONFIG.get('debug', False))
player.login()
now = player.now.replace(second=0, microsecond=0)
dt_max = now.replace(year=9999)
dt_max = constants.max_datetime
tasks = {
'eat': now,
}

View File

@ -1,17 +1,19 @@
bump2version==1.0.0
coverage==5.2
bump2version==1.0.1
coverage==5.3
edx-sphinx-theme==1.5.0
flake8==3.8.3
ipython==7.16.1
isort==5.0.9
pip==20.1.1
PyInstaller==3.6
flake8==3.8.4
ipython>=7.18.1
isort==5.6.4
pip==20.2.4
PyInstaller==4.0
pytz==2020.1
pytest==5.4.3
responses==0.10.15
setuptools==49.2.0
Sphinx==3.1.2
tox==3.16.1
pytest==6.1.1
responses==0.12.0
setuptools==50.3.2
Sphinx==3.2.1
requests==2.24.0
PySocks==1.7.1
tox==3.20.1
twine==3.2.0
watchdog==0.10.3
wheel==0.34.2
wheel==0.35.1

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.21.1
current_version = 0.22.1.1
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
@ -33,7 +33,6 @@ ignore_missing_imports = False
warn_unused_ignores = True
warn_redundant_casts = True
warn_unused_configs = True
plugins = mypy_django_plugin.main
[isort]
multi_line_output = 2

View File

@ -11,11 +11,18 @@ with open('README.rst') as readme_file:
with open('HISTORY.rst') as history_file:
history = history_file.read()
requirements = ['pytz==2020.1', 'requests==2.24.0']
requirements = [
'pytz==2020.1',
'requests==2.24.0',
'PySocks==1.7.1'
]
setup_requirements = []
test_requirements = []
test_requirements = [
"pytest==6.1.0",
"responses==0.12.0"
]
setup(
author="Eriks Karls",
@ -38,11 +45,11 @@ setup(
keywords='erepublik',
name='eRepublik',
packages=find_packages(include=['erepublik']),
python_requires='>=3.7, <4',
python_requires='>=3.6, <4',
setup_requires=setup_requirements,
test_suite='tests',
tests_require=test_requirements,
url='https://github.com/eeriks/erepublik/',
version='0.21.1',
version='0.22.1.1',
zip_safe=False,
)

View File

@ -65,6 +65,8 @@ class TestErepublik(unittest.TestCase):
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))
@ -73,62 +75,63 @@ class TestErepublik(unittest.TestCase):
# Level up
self.citizen.energy.limit = 3000
self.citizen.details.xp = 24705
self.assertEqual(self.citizen.should_fight(), (0, 'Level up', False))
if not is_wc_close:
self.assertEqual(self.citizen.should_fight(), (0, 'Level up', False))
self.citizen.energy.recovered = 3000
self.citizen.energy.recoverable = 2950
self.citizen.energy.interval = 30
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.citizen.my_companies.ff_lockdown = 0
self.citizen.energy.recovered = 3000
self.citizen.energy.recoverable = 2950
self.citizen.energy.interval = 30
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.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.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), (305, 'Fighting for close Levelup. Doing 305 hits', 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.citizen.my_companies.ff_lockdown = 160
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.citizen.my_companies.ff_lockdown = 160
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
self.citizen.details.xp = 21000
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.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.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.citizen.my_companies.ff_lockdown = 0
# 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.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.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.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.citizen.my_companies.ff_lockdown = 0
self.citizen.config.all_in = False
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.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.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.details.next_pp = [100, 150, 250, 400, 500]
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.citizen.my_companies.ff_lockdown = 0
self.citizen.energy.limit = 3000
self.citizen.details.next_pp = [19250, 20000]
self.citizen.config.next_energy = False
self.citizen.config.next_energy = True
self.citizen.energy.limit = 5000
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.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.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))
# 1h worth of energy
self.citizen.energy.recoverable = 2910
self.assertEqual(self.citizen.should_fight(), (30, 'Fighting for 1h energy. Doing 30 hits', True))

View File

@ -1,9 +1,10 @@
[tox]
envlist = py37, flake8
envlist = py37, py38, flake8
[travis]
python =
3.7: py37
3.8: py38
[testenv:flake8]
basepython = python
@ -15,4 +16,3 @@ setenv =
PYTHONPATH = {toxinidir}
commands = python setup.py test