Compare commits
74 Commits
Author | SHA1 | Date | |
---|---|---|---|
684b2a6ba4 | |||
447aee8134 | |||
64249fc3d9 | |||
95a78b6e7e | |||
b338ea598a | |||
b88e7973e8 | |||
c671425531 | |||
08937d06e4 | |||
cec4510831 | |||
cfb9501647 | |||
d419211955 | |||
0ea144db17 | |||
1418f580cd | |||
49575bddf5 | |||
c874247335 | |||
6e9def4394 | |||
d6fbaa7945 | |||
eb740c60c7 | |||
240c409739 | |||
f348a315c5 | |||
7a09eea2b4 | |||
81b1069cf0 | |||
429d43df15 | |||
c81986d65e | |||
7bb988f716 | |||
16cae24712 | |||
3e051fe906 | |||
aa9cda9314 | |||
fc66db8cab | |||
6bbc4f8768 | |||
160b32a914 | |||
a51c3c620e | |||
a1c26468eb | |||
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 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -104,3 +104,4 @@ ENV/
|
||||
debug/
|
||||
log/
|
||||
docs/
|
||||
*dump.json
|
||||
|
50
HISTORY.rst
50
HISTORY.rst
@ -2,6 +2,56 @@
|
||||
History
|
||||
=======
|
||||
|
||||
0.23.2 (2020-12-01)
|
||||
-------------------
|
||||
* Added concurrency checks to guard against simultaneous fighting/wam'ing/traveling *(Note: Probably will make it into a decorator at some time)*
|
||||
|
||||
0.23.1 (2020-12-01)
|
||||
-------------------
|
||||
* Separated battle finding logic from CitizenMilitary.find_battle_and_fight method
|
||||
* Base dmg calculations
|
||||
* Get max hit value for divisions on current side
|
||||
* Added method to get division stats
|
||||
* Wheel of fortune updates
|
||||
|
||||
0.23.0 (2020-11-26)
|
||||
-------------------
|
||||
* ***0.23 - last supported version for Python 3.7.***
|
||||
* Added `Config.maverick` switch, to allow/deny automated fighting in non native divisions if the player has MaverickPack
|
||||
* Added `CitizenMedia.get_article(article_id:int)` method to get article data
|
||||
* Added `CitizenMedia.delete_article(article_id:int)` method to delete article
|
||||
* Fixed `CitizenTravel.travel_to_region(region_id:int)` method
|
||||
* Added `CitizenAnniversary.collect_map_quest_node(node_id:int, extra:bool=False)` to collect also extra rewards
|
||||
* Fixed `CitizenTasks.work()` when employer out of money - resign and find a new job
|
||||
* Fixed `CitizenEconomy.post_market_offer()`
|
||||
|
||||
0.22.3 (2020-11-16)
|
||||
-------------------
|
||||
* Fixed round to even bug when doing wam and not enough raw.
|
||||
* Added meta industry airplaneRaw
|
||||
* Added method `Citizen.buy_market_offer(OfferItem, amount=None)` to directly buy market offer with included travel to country and back.
|
||||
|
||||
0.22.2 (2020-11-09)
|
||||
-------------------
|
||||
* Allow querying market offers for q2-q5 aircrafts
|
||||
* Added "Ticket" industry
|
||||
|
||||
0.22.1 (2020-11-04)
|
||||
-------------------
|
||||
* 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
|
||||
|
@ -93,8 +93,8 @@
|
||||
<label class="custom-control-label" for="wam">Work as manager</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="employ">
|
||||
<label class="custom-control-label" for="employ">Employ employees</label>
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="employees">
|
||||
<label class="custom-control-label" for="employees">Employ employees</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_buy_raw" checked>
|
||||
@ -236,8 +236,8 @@
|
||||
config.debug = debug.checked;
|
||||
let wam = document.getElementById('wam'); // Generated
|
||||
config.wam = wam.checked;
|
||||
let employ = document.getElementById('employ'); // Generated
|
||||
config.employ = employ.checked;
|
||||
let employees = document.getElementById('employees'); // Generated
|
||||
config.employees = employees.checked;
|
||||
|
||||
let auto_buy_raw = document.getElementById('auto_buy_raw'); // Generated
|
||||
let auto_sell_all = document.getElementById('auto_sell_all'); // Generated
|
||||
@ -249,7 +249,7 @@
|
||||
let auto_sell_house = document.getElementById('auto_sell_house'); // Generated
|
||||
let auto_sell_arm = document.getElementById('auto_sell_arm'); // Generated
|
||||
let auto_sell_air = document.getElementById('auto_sell_air'); // Generated
|
||||
if (config.wam || config.employ) {
|
||||
if (config.wam || config.employees) {
|
||||
auto_buy_raw.disabled = false;
|
||||
auto_sell_all.disabled = false;
|
||||
auto_sell_frm.disabled = false;
|
||||
@ -359,7 +359,7 @@
|
||||
"interactive": true,
|
||||
"debug": true,
|
||||
"wam": true,
|
||||
"employ": true,
|
||||
"employees": true,
|
||||
"auto_buy_raw": true,
|
||||
"auto_sell_all": true,
|
||||
"auto_sell": [
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
__author__ = """Eriks Karls"""
|
||||
__email__ = 'eriks@72.lv'
|
||||
__version__ = '0.21.5'
|
||||
__version__ = '0.23.2'
|
||||
|
||||
from erepublik import classes, utils, constants
|
||||
from erepublik.citizen import Citizen
|
||||
|
||||
__all__ = ["classes", "utils", "Citizen", ]
|
||||
__all__ = ["classes", "utils", "Citizen", 'constants']
|
||||
|
@ -15,9 +15,11 @@ class SlowRequests(Session):
|
||||
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/86.0.4240.183 Safari/537.36', # noqa
|
||||
'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 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
|
||||
'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',
|
||||
@ -159,21 +161,26 @@ class ErepublikAnniversaryAPI(CitizenBaseAPI):
|
||||
data = {'nodeId': node_id, '_token': self.token, "currencyCost": currency_amount}
|
||||
return self.post(f"{self.url}/main/map-rewards-speedup", data=data)
|
||||
|
||||
def _post_map_rewards_claim(self, node_id: int) -> Response:
|
||||
def _post_map_rewards_claim(self, node_id: int, extra: bool = False) -> Response:
|
||||
data = {'nodeId': node_id, '_token': self.token}
|
||||
if extra:
|
||||
data['claimExtra'] = 1
|
||||
return self.post(f"{self.url}/main/map-rewards-claim", data=data)
|
||||
|
||||
def _post_main_wheel_of_fortune_spin(self, cost) -> Response:
|
||||
return self.post(f"{self.url}/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):
|
||||
def _get_main_article_json(self, article_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/articleJson/{article_id}")
|
||||
|
||||
def _get_main_delete_article(self, article_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/delete-article/{article_id}/1")
|
||||
|
||||
def _post_main_article_comments(self, article_id: int, page: int = 1) -> Response:
|
||||
data = dict(_token=self.token, articleId=article_id, page=page)
|
||||
if page:
|
||||
@ -344,27 +351,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}")
|
||||
|
||||
|
||||
@ -376,6 +388,9 @@ class ErepublikLocationAPI(CitizenBaseAPI):
|
||||
|
||||
|
||||
class ErepublikMilitaryAPI(CitizenBaseAPI):
|
||||
def _get_military_battle_stats(self, battle_id: int, division: int, division_id: int):
|
||||
return self.get(f"{self.url}/military/battle-stats/{battle_id}/{division}/{division_id}")
|
||||
|
||||
def _get_military_battlefield_choose_side(self, battle_id: int, side_id: int) -> Response:
|
||||
return self.get(f"{self.url}/military/battlefield-choose-side/{battle_id}/{side_id}")
|
||||
|
||||
|
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):
|
||||
@ -339,6 +357,8 @@ class Config:
|
||||
telegram = True
|
||||
telegram_chat_id = 0
|
||||
telegram_token = ""
|
||||
maverick = False
|
||||
spin_wheel_of_fortune = False
|
||||
|
||||
def __init__(self):
|
||||
self.auto_sell = []
|
||||
@ -371,6 +391,8 @@ class Config:
|
||||
self.telegram = True
|
||||
self.telegram_chat_id = 0
|
||||
self.telegram_token = ""
|
||||
self.maverick = False
|
||||
self.spin_wheel_of_fortune = False
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
@ -379,10 +401,11 @@ class Config:
|
||||
fight=self.fight, air=self.air, ground=self.ground, all_in=self.all_in,
|
||||
next_energy=self.next_energy, boosters=self.boosters, travel_to_fight=self.travel_to_fight,
|
||||
always_travel=self.always_travel, epic_hunt=self.epic_hunt, epic_hunt_ebs=self.epic_hunt_ebs,
|
||||
rw_def_side=self.rw_def_side, interactive=self.interactive,
|
||||
rw_def_side=self.rw_def_side, interactive=self.interactive, maverick=self.maverick,
|
||||
continuous_fighting=self.continuous_fighting, auto_buy_raw=self.auto_buy_raw,
|
||||
force_wam=self.force_wam, sort_battles_time=self.sort_battles_time, force_travel=self.force_travel,
|
||||
telegram=self.telegram, telegram_chat_id=self.telegram_chat_id, telegram_token=self.telegram_token)
|
||||
telegram=self.telegram, telegram_chat_id=self.telegram_chat_id, telegram_token=self.telegram_token,
|
||||
spin_wheel_of_fortune=self.spin_wheel_of_fortune)
|
||||
|
||||
|
||||
class Energy:
|
||||
@ -438,24 +461,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):
|
||||
@ -695,11 +719,11 @@ class BattleSide:
|
||||
|
||||
def __repr__(self):
|
||||
side_text = "Defender" if self.is_defender else "Invader "
|
||||
return f"<BattleSide: {side_text} {self.country.name}|{self.points:02d}p>"
|
||||
return f"<BattleSide: {side_text} {self.country.name}|{self.points:>2d}p>"
|
||||
|
||||
def __str__(self):
|
||||
side_text = "Defender" if self.is_defender else "Invader "
|
||||
return f"{side_text} {self.country.name} - {self.points:02d} points"
|
||||
return f"{side_text} {self.country.name} - {self.points:>2d} points"
|
||||
|
||||
def __format__(self, format_spec):
|
||||
return self.country.iso
|
||||
@ -765,7 +789,7 @@ class BattleDivision:
|
||||
return constants.TERRAINS[self.terrain]
|
||||
|
||||
def __str__(self):
|
||||
base_name = f"Div #{self.id} d{self.div}"
|
||||
base_name = f"D{self.div} #{self.id}"
|
||||
if self.terrain:
|
||||
base_name += f" ({self.terrain_display})"
|
||||
if self.div_end:
|
||||
@ -840,13 +864,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 = {}
|
||||
@ -855,7 +881,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],
|
||||
@ -873,7 +899,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]:16} | "
|
||||
f"{self.invader} : {self.defender} | Round time {time_part} | {'R'+str(self.zone_id):>3}")
|
||||
|
||||
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,
|
||||
'foodraw': 7, 'weaponraw': 12, 'houseraw': 18, 'aircraftraw': 24,
|
||||
__by_name = {'food': 1, 'weapon': 2, 'ticket': 3, 'house': 4, 'aircraft': 23,
|
||||
'foodraw': 7, 'weaponraw': 12, 'houseraw': 18, 'aircraftraw': 24, 'airplaneraw': 24,
|
||||
'frm': 7, 'wrm': 12, 'hrm': 18, 'arm': 24,
|
||||
'frm q1': 7, 'frm q2': 8, 'frm q3': 9, 'frm q4': 10, 'frm q5': 11,
|
||||
'wrm q1': 12, 'wrm q2': 13, 'wrm q3': 14, 'wrm q4': 15, 'wrm q5': 16,
|
||||
'hrm q1': 18, 'hrm q2': 19, 'hrm q3': 20, 'hrm q4': 21, 'hrm q5': 22,
|
||||
'arm q1': 24, 'arm q2': 25, 'arm q3': 26, 'arm q4': 27, 'arm q5': 28}
|
||||
__by_id = {1: "Food", 2: "Weapon", 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:
|
||||
@ -337,17 +338,8 @@ def calculate_hit(strength: float, rang: int, tp: bool, elite: bool, ne: bool, b
|
||||
base_wpn = (1 + Decimal(str(weapon / 100)))
|
||||
dmg = 10 * base_str * base_rnk * base_wpn
|
||||
|
||||
if elite:
|
||||
dmg = dmg * 11 / 10
|
||||
|
||||
if tp and rang >= 70:
|
||||
dmg = dmg * (1 + Decimal((rang - 69) / 10))
|
||||
|
||||
dmg = dmg * (100 + booster) / 100
|
||||
|
||||
if ne:
|
||||
dmg = dmg * 11 / 10
|
||||
return round(dmg, dec)
|
||||
dmg = get_final_hit_dmg(dmg, rang, tp=tp, elite=elite, ne=ne, booster=booster)
|
||||
return Decimal(round(dmg, dec))
|
||||
|
||||
|
||||
def get_ground_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False,
|
||||
@ -370,7 +362,24 @@ 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 get_final_hit_dmg(base_dmg: Union[Decimal, float, str], rang: int,
|
||||
tp: bool = False, elite: bool = False, ne: bool = False, booster: int = 0) -> Decimal:
|
||||
dmg = Decimal(str(base_dmg))
|
||||
|
||||
if elite:
|
||||
dmg = dmg * 11 / 10
|
||||
if tp and rang >= 70:
|
||||
dmg = dmg * (1 + Decimal((rang - 69) / 10))
|
||||
dmg = dmg * (100 + booster) / 100
|
||||
if ne:
|
||||
dmg = dmg * 11 / 10
|
||||
return Decimal(dmg)
|
||||
|
||||
# def _clear_up_battle_memory(battle):
|
||||
# del battle.invader._battle, battle.defender._battle
|
||||
# for div_id, division in battle.div.items():
|
||||
# del division._battle
|
||||
|
||||
|
||||
def deprecation(message):
|
||||
warnings.warn(message, DeprecationWarning, stacklevel=2)
|
||||
|
@ -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
|
||||
PyInstaller==4.0
|
||||
pytz==2020.1
|
||||
pytest==6.1.0
|
||||
responses==0.12.0
|
||||
setuptools==50.3.0
|
||||
Sphinx==3.2.1
|
||||
requests[socks]==2.24.0
|
||||
tox==3.20.0
|
||||
flake8==3.8.4
|
||||
ipython>=7.19.0
|
||||
isort==5.6.4
|
||||
pip==20.3
|
||||
PyInstaller==4.1
|
||||
pytz==2020.4
|
||||
pytest==6.1.2
|
||||
responses==0.12.1
|
||||
setuptools==50.3.2
|
||||
Sphinx==3.3.1
|
||||
requests==2.25.0
|
||||
PySocks==1.7.1
|
||||
tox==3.20.1
|
||||
twine==3.2.0
|
||||
watchdog==0.10.3
|
||||
watchdog==0.10.4
|
||||
wheel==0.35.1
|
||||
|
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 0.21.5
|
||||
current_version = 0.23.2
|
||||
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[socks]==2.24.0']
|
||||
requirements = [
|
||||
'pytz==2020.4',
|
||||
'requests==2.25.0',
|
||||
'PySocks==1.7.1'
|
||||
]
|
||||
|
||||
setup_requirements = []
|
||||
|
||||
test_requirements = []
|
||||
test_requirements = [
|
||||
"pytest==6.1.2",
|
||||
"responses==0.12.1"
|
||||
]
|
||||
|
||||
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.5',
|
||||
version='0.23.2',
|
||||
zip_safe=False,
|
||||
)
|
||||
|
@ -2,7 +2,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Tests for `erepublik` package."""
|
||||
from typing import Callable
|
||||
|
||||
from erepublik import Citizen
|
||||
|
||||
@ -66,7 +65,8 @@ class TestErepublik(unittest.TestCase):
|
||||
self.assertEqual(self.citizen.next_reachable_energy, 0)
|
||||
|
||||
def test_should_fight(self):
|
||||
is_wc_close: Callable[[], bool] = lambda: self.citizen.max_time_till_full_ff > self.citizen.time_till_week_change
|
||||
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))
|
||||
|
||||
|
Reference in New Issue
Block a user