Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
4895ae3663 | |||
b8d7cc8d7c | |||
1d0645b490 | |||
30cf6203b7 | |||
a32e88218d | |||
a031da0ee7 | |||
bdb13fa4ae | |||
e1e3b33d46 | |||
e09ca143b1 | |||
61d0599295 | |||
1ef600492a | |||
377eda6445 | |||
fd13667ca8 | |||
0479afcabe | |||
486f022f35 | |||
638373e452 | |||
04f357cc70 | |||
ed4ffe5af6 | |||
8aa90a7dbf | |||
e798859105 | |||
241f1642ce | |||
a4128b5d89 | |||
22c2a0ffd2 | |||
38f0335354 | |||
889435b94e | |||
bb16c27674 | |||
963d7ca11a | |||
36c7fefdf7 | |||
d9fa30b06e | |||
b53dc447f4 | |||
233d8d83f8 | |||
ec62d90aa2 | |||
0c433a56da | |||
ad24338f4d | |||
6f4bc65d1b | |||
cc09ba7ee7 | |||
9e1166a460 | |||
fb0042c00d | |||
bb800578e7 | |||
7025f750dc | |||
bf77f21b60 | |||
6b7639d7fb | |||
3b1c1928fd | |||
2e26c2db79 | |||
78c055fee2 | |||
fe41c4cdc6 | |||
123b6cf4ed | |||
f652b02443 | |||
73537e4742 | |||
955432e0d2 | |||
1d93864dca | |||
c472d688be | |||
bff9a2bec9 | |||
973ea40e00 | |||
52c85fdf28 | |||
a889e9efa1 | |||
a9a0cdc6d5 | |||
1c102488b6 | |||
c38acef2a0 | |||
48b5e473aa |
1
.gitignore
vendored
1
.gitignore
vendored
@ -104,3 +104,4 @@ ENV/
|
||||
debug/
|
||||
log/
|
||||
docs/
|
||||
*dump.json
|
||||
|
21
HISTORY.rst
21
HISTORY.rst
@ -2,6 +2,27 @@
|
||||
History
|
||||
=======
|
||||
|
||||
0.22.2 (2020-11-9)
|
||||
-------------------
|
||||
* Allow querying market offers for q2-q5 aircrafts
|
||||
* Added "Ticket" industry
|
||||
|
||||
0.22.1 (2020-11-4)
|
||||
-------------------
|
||||
* Requirement update
|
||||
* Unified product naming in inventory and other places based on `erepublik.constants.INDUSTRIES` values
|
||||
* `erepublik.Citizen` parameter `auto_login` now defaults to `False`
|
||||
* Continued work on more verbose action and result logging
|
||||
|
||||
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
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
__author__ = """Eriks Karls"""
|
||||
__email__ = 'eriks@72.lv'
|
||||
__version__ = '0.21.4'
|
||||
__version__ = '0.22.2.1'
|
||||
|
||||
from erepublik import classes, utils, constants
|
||||
from erepublik.citizen import Citizen
|
||||
|
||||
__all__ = ["classes", "utils", "Citizen", ]
|
||||
__all__ = ["classes", "utils", "Citizen", 'constants']
|
||||
|
@ -12,8 +12,8 @@ __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
|
||||
@ -36,10 +36,12 @@ class SlowRequests(Session):
|
||||
'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({
|
||||
@ -132,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:
|
||||
@ -154,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):
|
||||
@ -334,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}")
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ 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
|
||||
|
||||
@ -28,27 +28,38 @@ class Holding:
|
||||
id: int
|
||||
region: int
|
||||
companies: List["Company"]
|
||||
name: str
|
||||
_citizen = weakref.ReferenceType
|
||||
|
||||
def __init__(self, _id: int, region: int, 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()
|
||||
|
||||
@ -62,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:
|
||||
@ -71,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"
|
||||
@ -92,8 +100,9 @@ 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):
|
||||
@ -170,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)
|
||||
@ -203,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,
|
||||
@ -219,7 +228,7 @@ class Company:
|
||||
return self.holding.citizen._post_economy_upgrade_company(self.id, level, self.holding.citizen.details.pin)
|
||||
|
||||
@property
|
||||
def holding(self):
|
||||
def holding(self) -> Holding:
|
||||
return self._holding()
|
||||
|
||||
|
||||
@ -229,13 +238,14 @@ class MyCompanies:
|
||||
ff_lockdown: int = 0
|
||||
|
||||
holdings: Dict[int, Holding]
|
||||
companies: weakref.WeakSet
|
||||
_companies: weakref.WeakSet
|
||||
_citizen: weakref.ReferenceType
|
||||
companies: Generator[Company, None, None]
|
||||
|
||||
def __init__(self, citizen):
|
||||
self._citizen = weakref.ref(citizen)
|
||||
self.holdings: Dict[int, Holding] = dict()
|
||||
self.companies: weakref.WeakSet = weakref.WeakSet()
|
||||
self.holdings = dict()
|
||||
self._companies = weakref.WeakSet()
|
||||
self.next_ot_time = utils.now()
|
||||
|
||||
def prepare_holdings(self, holdings: Dict[str, Dict[str, Any]]):
|
||||
@ -245,10 +255,10 @@ 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)
|
||||
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]]):
|
||||
"""
|
||||
@ -270,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.add(company)
|
||||
self._companies.add(company)
|
||||
holding.add_company(company)
|
||||
|
||||
def get_employable_factories(self) -> Dict[int, int]:
|
||||
@ -280,14 +290,18 @@ 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)
|
||||
@ -297,12 +311,16 @@ class MyCompanies:
|
||||
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):
|
||||
@ -438,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):
|
||||
@ -551,7 +570,7 @@ class Reporter:
|
||||
self.url = "https://api.erep.lv"
|
||||
self._req.headers.update({"user-agent": "eRepublik Script Reporter v3",
|
||||
'erep-version': utils.__version__,
|
||||
'erep-user-id': self.citizen_id,
|
||||
'erep-user-id': str(self.citizen_id),
|
||||
'erep-user-name': self.citizen.name})
|
||||
self.__to_update = []
|
||||
self.__registered: bool = False
|
||||
@ -618,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))
|
||||
|
||||
@ -629,7 +654,7 @@ class Reporter:
|
||||
except: # noqa
|
||||
return []
|
||||
|
||||
def fetch_tasks(self) -> List[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))
|
||||
@ -834,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 = {}
|
||||
@ -849,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],
|
||||
@ -867,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}>"
|
||||
|
@ -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, 'weapon': 2, 'house': 4, 'aircraft': 23,
|
||||
__by_name = {'food': 1, 'weapon': 2, 'ticket':3, '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: "Weapon", 4: "House", 23: "Aircraft",
|
||||
__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",
|
||||
18: "houseRaw", 19: "HRM q2", 20: "HRM q3", 21: "HRM q4", 22: "HRM 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]]:
|
||||
|
@ -7,6 +7,7 @@ import textwrap
|
||||
import time
|
||||
import traceback
|
||||
import unicodedata
|
||||
import warnings
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
from typing import Any, List, Optional, Union, Dict
|
||||
@ -26,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__
|
||||
@ -47,7 +48,7 @@ def localize_dt(dt: Union[datetime.date, datetime.datetime]) -> datetime.datetim
|
||||
elif isinstance(dt, datetime.date):
|
||||
return constants.erep_tz.localize(datetime.datetime.combine(dt, datetime.time(0, 0, 0)))
|
||||
else:
|
||||
return dt.astimezone(constants.erep_tz)
|
||||
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:
|
||||
@ -278,14 +279,13 @@ def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commi
|
||||
elif interactive is not None:
|
||||
write_silent_log(log_info)
|
||||
trace = inspect.trace()
|
||||
local_vars = None
|
||||
if trace:
|
||||
trace_local_vars = trace[-1][0].f_locals
|
||||
if trace_local_vars.get('__name__') == '__main__':
|
||||
local_vars = {'commit_id': trace_local_vars.get('COMMIT_ID'),
|
||||
'interactive': trace_local_vars.get('INTERACTIVE'),
|
||||
'version': trace_local_vars.get('__version__'),
|
||||
'config': trace_local_vars.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:
|
||||
local_vars = dict()
|
||||
send_email(name, content, citizen, local_vars=local_vars)
|
||||
@ -371,7 +371,11 @@ def get_air_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_pat
|
||||
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 _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)
|
||||
|
@ -29,7 +29,7 @@ def _battle_launcher(player: Citizen):
|
||||
"""
|
||||
global CONFIG
|
||||
finished_war_ids = {*[]}
|
||||
war_data = CONFIG.get('start_battles', {})
|
||||
war_data = CONFIG.get('battle_launcher', {})
|
||||
war_ids = {int(war_id) for war_id in war_data.keys()}
|
||||
next_attack_time = player.now
|
||||
next_attack_time = next_attack_time.replace(minute=next_attack_time.minute // 5 * 5, second=0)
|
||||
@ -45,7 +45,7 @@ def _battle_launcher(player: Citizen):
|
||||
|
||||
status = player.get_war_status(war_id)
|
||||
if status.get('ended', False):
|
||||
CONFIG['start_battles'].pop(war_id, None)
|
||||
CONFIG['battle_launcher'].pop(war_id, None)
|
||||
finished_war_ids.add(war_id)
|
||||
continue
|
||||
elif not status.get('can_attack'):
|
||||
@ -64,7 +64,15 @@ def _battle_launcher(player: Citizen):
|
||||
player.update_war_info()
|
||||
battle_id = player.get_war_status(war_id).get("battle_id")
|
||||
if battle_id is not None and battle_id in player.all_battles:
|
||||
player.fight(battle_id, player.details.citizenship, hits)
|
||||
battle = player.all_battles.get(battle_id)
|
||||
for division in battle.div.values():
|
||||
if division.div == player.division:
|
||||
div = division
|
||||
break
|
||||
else:
|
||||
player.report_error("Players division not found in the first round!")
|
||||
break
|
||||
player.fight(battle, div, battle.invader, hits)
|
||||
break
|
||||
player.sleep(1)
|
||||
if attacked:
|
||||
|
@ -1,12 +1,16 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from erepublik import Citizen, utils
|
||||
from erepublik import Citizen, utils, constants
|
||||
|
||||
CONFIG = {
|
||||
'email': 'player@email.com',
|
||||
'password': 'Pa$5w0rd!',
|
||||
'interactive': True,
|
||||
'debug': True
|
||||
'debug': True,
|
||||
'work': True,
|
||||
'ot': True, # Work OverTime
|
||||
'wam': True, # WorkAsManager
|
||||
'train': True
|
||||
}
|
||||
|
||||
|
||||
@ -14,11 +18,14 @@ CONFIG = {
|
||||
def main():
|
||||
player = Citizen(email=CONFIG['email'], password=CONFIG['password'], auto_login=False)
|
||||
player.config.interactive = CONFIG['interactive']
|
||||
player.config.fight = CONFIG['fight']
|
||||
player.config.work = CONFIG['work']
|
||||
player.config.train = CONFIG['train']
|
||||
player.config.ot = CONFIG['ot']
|
||||
player.config.wam = CONFIG['wam']
|
||||
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,
|
||||
}
|
||||
@ -76,7 +83,7 @@ def main():
|
||||
tasks.update({'eat': next_time})
|
||||
|
||||
if tasks.get('ot', dt_max) <= now:
|
||||
player.write_log("Doing task: ot")
|
||||
player.write_log("Doing task: work overtime")
|
||||
if now > player.my_companies.next_ot_time:
|
||||
player.work_ot()
|
||||
next_time = now + timedelta(minutes=60)
|
||||
|
@ -1,18 +1,19 @@
|
||||
bump2version==1.0.0
|
||||
bump2version==1.0.1
|
||||
coverage==5.3
|
||||
edx-sphinx-theme==1.5.0
|
||||
flake8==3.8.3
|
||||
ipython==7.18.1
|
||||
isort==5.5.3
|
||||
pip==20.2.3
|
||||
flake8==3.8.4
|
||||
ipython>=7.19.0
|
||||
isort==5.6.4
|
||||
pip==20.2.4
|
||||
PyInstaller==4.0
|
||||
pytz==2020.1
|
||||
pytest==6.0.2
|
||||
pytz==2020.4
|
||||
pytest==6.1.2
|
||||
responses==0.12.0
|
||||
setuptools==50.3.0
|
||||
Sphinx==3.2.1
|
||||
setuptools==50.3.2
|
||||
Sphinx==3.3.0
|
||||
requests==2.24.0
|
||||
tox==3.20.0
|
||||
PySocks==1.7.1
|
||||
tox==3.20.1
|
||||
twine==3.2.0
|
||||
watchdog==0.10.3
|
||||
wheel==0.35.1
|
||||
|
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 0.21.4
|
||||
current_version = 0.22.2.1
|
||||
commit = True
|
||||
tag = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
|
||||
|
15
setup.py
15
setup.py
@ -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.4',
|
||||
'requests==2.24.0',
|
||||
'PySocks==1.7.1'
|
||||
]
|
||||
|
||||
setup_requirements = []
|
||||
|
||||
test_requirements = []
|
||||
test_requirements = [
|
||||
"pytest==6.1.2",
|
||||
"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.4',
|
||||
version='0.22.2.1',
|
||||
zip_safe=False,
|
||||
)
|
||||
|
@ -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))
|
||||
|
Reference in New Issue
Block a user