Compare commits

...

69 Commits

Author SHA1 Message Date
08937d06e4 Bump version: 0.23.0 → 0.23.1 2020-11-30 18:20:48 +02:00
cec4510831 Requirement update 2020-11-30 18:20:42 +02:00
cfb9501647 PEP8 2020-11-30 18:16:13 +02:00
d419211955 Get max hit value for divisions on current side 2020-11-30 18:12:36 +02:00
0ea144db17 Added method to get division stats 2020-11-30 17:44:23 +02:00
1418f580cd Spin wheel of fortune updates 2020-11-30 17:43:42 +02:00
49575bddf5 History update for v0.23.0 2020-11-26 18:39:25 +02:00
c874247335 Bump version: 0.22.3.3 → 0.23.0 2020-11-26 18:32:46 +02:00
6e9def4394 Added .get_article(int) and .delete_article(int) methods to CitizenMedia class 2020-11-26 18:32:31 +02:00
d6fbaa7945 Maverick fighting should be explicitly enabled through Citizen.config 2020-11-26 16:16:15 +02:00
eb740c60c7 Bump version: 0.22.3.2 → 0.22.3.3 2020-11-20 18:54:20 +02:00
240c409739 Travel to region bugfix 2020-11-20 18:54:12 +02:00
f348a315c5 Bump version: 0.22.3.1 → 0.22.3.2 2020-11-20 16:57:43 +02:00
7a09eea2b4 CitizenAnniversary.collect_map_quest_node() now accepts argument extra, to collect extra reward 2020-11-20 16:57:36 +02:00
81b1069cf0 Bump version: 0.22.3 → 0.22.3.1 2020-11-20 14:46:04 +02:00
429d43df15 CitizenTasks.work() bugfix: When employer out of money - resign 2020-11-20 14:45:58 +02:00
c81986d65e CitizenEconomy.post_market_offer() bugfix 2020-11-20 14:31:16 +02:00
7bb988f716 PEP8 2020-11-20 14:24:11 +02:00
16cae24712 Bump version: 0.22.2.1 → 0.22.3 2020-11-16 12:12:30 +02:00
3e051fe906 History update 2020-11-16 12:07:20 +02:00
aa9cda9314 eRepublik temp technical difficulties 2020-11-16 12:06:59 +02:00
fc66db8cab Config file generator update 2020-11-16 11:56:37 +02:00
6bbc4f8768 eRepublik has both airplaneRaw and aircraftRaw. Possible future bugs because of this! 2020-11-16 11:56:13 +02:00
160b32a914 Buy market offer directly 2020-11-16 11:52:55 +02:00
a51c3c620e Python round to even bugfix.
`{"stock":10.494533,"consume":10.5}` rounds to 0
2020-11-16 11:52:27 +02:00
a1c26468eb UA updates 2020-11-12 11:47:20 +02:00
4895ae3663 Bump version: 0.22.2 → 0.22.2.1 2020-11-10 15:39:15 +02:00
b8d7cc8d7c Example updates 2020-11-10 15:38:30 +02:00
1d0645b490 Bump version: 0.22.1.5 → 0.22.2 2020-11-09 16:34:52 +02:00
30cf6203b7 History update 2020-11-09 16:34:41 +02:00
a32e88218d CitizenEconomy.get_market_offers now accepts searching tickets and the new aircraft qualities 2020-11-09 16:28:21 +02:00
a031da0ee7 Bump version: 0.22.1.4 → 0.22.1.5 2020-11-04 17:22:44 +02:00
bdb13fa4ae History update, WAM actions are now more verbose 2020-11-04 17:22:35 +02:00
e1e3b33d46 requirement update 2020-11-03 18:10:43 +02:00
e09ca143b1 Bump version: 0.22.1.3 → 0.22.1.4 2020-11-03 14:31:43 +02:00
61d0599295 House renewal bugfix 2020-11-03 14:31:37 +02:00
1ef600492a House renewal bugfix 2020-11-03 14:30:20 +02:00
377eda6445 Inventory booster bugfix 2020-11-03 14:29:42 +02:00
fd13667ca8 Bump version: 0.22.1.2 → 0.22.1.3 2020-10-30 12:58:26 +02:00
0479afcabe Report if out of money to work as manager 2020-10-30 12:58:18 +02:00
486f022f35 Unified Item namig from constants.INDUSTRIES 2020-10-30 12:43:45 +02:00
638373e452 Bump version: 0.22.1.1 → 0.22.1.2 2020-10-27 17:04:40 +02:00
04f357cc70 Bugfix 2020-10-27 17:04:37 +02:00
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
16 changed files with 669 additions and 400 deletions

1
.gitignore vendored
View File

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

View File

@ -2,6 +2,43 @@
History
=======
0.23.0 (2020-11-26)
-------------------
* 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

View File

@ -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": [

View File

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

View File

@ -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',
@ -38,8 +40,10 @@ class SlowRequests(Session):
]
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 +136,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:
@ -149,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:
@ -334,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}")
@ -366,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

View File

@ -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):
@ -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]} | "
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}>"

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, '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]]:

View File

@ -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:
@ -370,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)

View File

@ -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:

View File

@ -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)

View File

@ -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.0.2
responses==0.12.0
setuptools==50.3.0
Sphinx==3.2.1
requests==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

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.21.4.8
current_version = 0.23.1
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?

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.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.4.8',
version='0.23.1',
zip_safe=False,
)

View File

@ -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))

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