Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
82d913bc47 | |||
c462eac369 | |||
d522a4faeb | |||
084a47b07a | |||
5082855440 | |||
c9971f3570 | |||
c0122eccdf | |||
2524173da0 | |||
4a4b991faf | |||
e87c48124c | |||
f0f47566a0 | |||
f88eaccf67 | |||
7be129a781 | |||
32546505b9 | |||
00ceabf8e3 | |||
19113da8e6 | |||
3ad7172925 | |||
179f1a0892 | |||
65adf707e2 | |||
29f9ce5ccc | |||
fa3881bf10 | |||
c1e8e94cba | |||
8133461fd7 | |||
c87333411a | |||
d679006b34 | |||
fa308db074 | |||
a080163af9 | |||
8aa159edf7 | |||
1211a98227 | |||
734a5ef2b6 | |||
5c3b405ca8 | |||
75de8fce96 | |||
01e5e44350 |
@ -4,7 +4,8 @@ History
|
|||||||
|
|
||||||
0.23.2 (2020-12-01)
|
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)*
|
* Added concurrency checks to guard against simultaneous fighting/wam'ing/traveling
|
||||||
|
* For concurrency checking use `utils.wait_for_lock` decorator
|
||||||
|
|
||||||
0.23.1 (2020-12-01)
|
0.23.1 (2020-12-01)
|
||||||
-------------------
|
-------------------
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
__author__ = """Eriks Karls"""
|
__author__ = """Eriks Karls"""
|
||||||
__email__ = 'eriks@72.lv'
|
__email__ = 'eriks@72.lv'
|
||||||
__version__ = '0.23.2.1'
|
__version__ = '0.23.3'
|
||||||
|
|
||||||
from erepublik import classes, utils, constants
|
from erepublik import classes, utils, constants
|
||||||
from erepublik.citizen import Citizen
|
from erepublik.citizen import Citizen
|
||||||
|
@ -15,10 +15,14 @@ class SlowRequests(Session):
|
|||||||
timeout: datetime.timedelta = datetime.timedelta(milliseconds=500)
|
timeout: datetime.timedelta = datetime.timedelta(milliseconds=500)
|
||||||
uas: List[str] = [
|
uas: List[str] = [
|
||||||
# Chrome
|
# 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/86.0.4240.183 Safari/537.36',
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36', # noqa
|
# 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/84.0.4147.125 Safari/537.36',
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36', # noqa
|
# 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/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/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/81.0.4044.138 Safari/537.36',
|
||||||
@ -29,14 +33,10 @@ class SlowRequests(Session):
|
|||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0',
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0',
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0',
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0',
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0',
|
|
||||||
'Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0',
|
'Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0',
|
||||||
'Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0',
|
'Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0',
|
||||||
'Mozilla/5.0 (X11; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0',
|
'Mozilla/5.0 (X11; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0',
|
||||||
'Mozilla/5.0 (X11; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0',
|
'Mozilla/5.0 (X11; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0',
|
||||||
'Mozilla/5.0 (X11; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0',
|
|
||||||
'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0',
|
|
||||||
]
|
]
|
||||||
debug: bool = False
|
debug: bool = False
|
||||||
|
|
||||||
@ -85,11 +85,10 @@ class SlowRequests(Session):
|
|||||||
if params:
|
if params:
|
||||||
args.update({"params": params})
|
args.update({"params": params})
|
||||||
|
|
||||||
body = "[{dt}]\tURL: '{url}'\tMETHOD: {met}\tARGS: {args}\n".format(dt=utils.now().strftime("%F %T"),
|
body = f"[{utils.now().strftime('%F %T')}]\tURL: '{url}'\tMETHOD: {method}\tARGS: {args}\n"
|
||||||
url=url, met=method, args=args)
|
|
||||||
utils.get_file(self.request_log_name)
|
|
||||||
with open(self.request_log_name, 'ab') as file:
|
with open(self.request_log_name, 'ab') as file:
|
||||||
file.write(body.encode("UTF-8"))
|
file.write(body.encode("UTF-8"))
|
||||||
|
pass
|
||||||
|
|
||||||
def _log_response(self, url, resp, redirect: bool = False):
|
def _log_response(self, url, resp, redirect: bool = False):
|
||||||
from erepublik import Citizen
|
from erepublik import Citizen
|
||||||
@ -99,22 +98,20 @@ class SlowRequests(Session):
|
|||||||
self._log_request(hist_resp.request.url, "REDIRECT")
|
self._log_request(hist_resp.request.url, "REDIRECT")
|
||||||
self._log_response(hist_resp.request.url, hist_resp, redirect=True)
|
self._log_response(hist_resp.request.url, hist_resp, redirect=True)
|
||||||
|
|
||||||
file_data = {
|
fd_path = 'debug/requests'
|
||||||
"path": 'debug/requests',
|
fd_time = self.last_time.strftime('%Y/%m/%d/%H-%M-%S')
|
||||||
"time": self.last_time.strftime('%Y/%m/%d/%H-%M-%S'),
|
fd_name = utils.slugify(url[len(Citizen.url):])
|
||||||
"name": utils.slugify(url[len(Citizen.url):]),
|
fd_extra = "_REDIRECT" if redirect else ""
|
||||||
"extra": "_REDIRECT" if redirect else ""
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
utils.json.loads(resp.text)
|
utils.json.loads(resp.text)
|
||||||
file_data.update({"ext": "json"})
|
fd_ext = 'json'
|
||||||
except utils.json.JSONDecodeError:
|
except utils.json.JSONDecodeError:
|
||||||
file_data.update({"ext": "html"})
|
fd_ext = 'html'
|
||||||
|
|
||||||
filename = 'debug/requests/{time}_{name}{extra}.{ext}'.format(**file_data)
|
filename = f'{fd_path}/{fd_time}_{fd_name}{fd_extra}.{fd_ext}'
|
||||||
with open(utils.get_file(filename), 'wb') as f:
|
utils.write_file(filename, resp.text)
|
||||||
f.write(resp.text.encode('utf-8'))
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CitizenBaseAPI:
|
class CitizenBaseAPI:
|
||||||
@ -292,7 +289,6 @@ class ErepublikCountryAPI(CitizenBaseAPI):
|
|||||||
|
|
||||||
def _post_main_country_donate(self, country_id: int, action: str, value: Union[int, float],
|
def _post_main_country_donate(self, country_id: int, action: str, value: Union[int, float],
|
||||||
quality: int = None) -> Response:
|
quality: int = None) -> Response:
|
||||||
|
|
||||||
data = dict(countryId=country_id, action=action, _token=self.token, value=value, quality=quality)
|
data = dict(countryId=country_id, action=action, _token=self.token, value=value, quality=quality)
|
||||||
return self.post(f"{self.url}/main/country-donate", data=data,
|
return self.post(f"{self.url}/main/country-donate", data=data,
|
||||||
headers={"Referer": f"{self.url}/country/economy/Latvia"})
|
headers={"Referer": f"{self.url}/country/economy/Latvia"})
|
||||||
@ -367,16 +363,20 @@ class ErepublikEconomyAPI(CitizenBaseAPI):
|
|||||||
|
|
||||||
|
|
||||||
class ErepublikLeaderBoardAPI(CitizenBaseAPI):
|
class ErepublikLeaderBoardAPI(CitizenBaseAPI):
|
||||||
def _get_main_leaderboards_damage_aircraft_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0) -> Response: # noqa
|
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")
|
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: # noqa
|
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}")
|
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: # noqa
|
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")
|
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: # noqa
|
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}")
|
return self.get(f"{self.url}/main/leaderboards-kills-rankings/{country_id}/{weeks}/{mu_id}/{div}")
|
||||||
|
|
||||||
|
|
||||||
@ -442,8 +442,9 @@ class ErepublikMilitaryAPI(CitizenBaseAPI):
|
|||||||
data.update(page=page)
|
data.update(page=page)
|
||||||
return self.post(f"{self.url}/military/battle-console", data=data)
|
return self.post(f"{self.url}/military/battle-console", data=data)
|
||||||
|
|
||||||
def _post_military_deploy_bomb(self, battle_id: int, bomb_id: int) -> Response:
|
def _post_military_deploy_bomb(self, battle_id: int, division_id: int, side_id: int, bomb_id: int) -> Response:
|
||||||
data = dict(battleId=battle_id, bombId=bomb_id, _token=self.token)
|
data = dict(battleId=battle_id, battleZoneId=division_id, sideId=side_id, sideCountryId=side_id,
|
||||||
|
bombId=bomb_id, _token=self.token)
|
||||||
return self.post(f"{self.url}/military/deploy-bomb", data=data)
|
return self.post(f"{self.url}/military/deploy-bomb", data=data)
|
||||||
|
|
||||||
def _post_military_fight_air(self, battle_id: int, side_id: int, zone_id: int) -> Response:
|
def _post_military_fight_air(self, battle_id: int, side_id: int, zone_id: int) -> Response:
|
||||||
@ -487,7 +488,7 @@ class ErepublikPoliticsAPI(CitizenBaseAPI):
|
|||||||
class ErepublikPresidentAPI(CitizenBaseAPI):
|
class ErepublikPresidentAPI(CitizenBaseAPI):
|
||||||
def _post_wars_attack_region(self, war_id: int, region_id: int, region_name: str) -> Response:
|
def _post_wars_attack_region(self, war_id: int, region_id: int, region_name: str) -> Response:
|
||||||
data = {'_token': self.token, 'warId': war_id, 'regionName': region_name, 'regionNameConfirm': region_name}
|
data = {'_token': self.token, 'warId': war_id, 'regionName': region_name, 'regionNameConfirm': region_name}
|
||||||
return self.post('{}/wars/attack-region/{}/{}'.format(self.url, war_id, region_id), data=data)
|
return self.post(f'{self.url}/wars/attack-region/{war_id}/{region_id}', data=data)
|
||||||
|
|
||||||
def _post_new_war(self, self_country_id: int, attack_country_id: int, debate: str = "") -> Response:
|
def _post_new_war(self, self_country_id: int, attack_country_id: int, debate: str = "") -> Response:
|
||||||
data = dict(requirments=1, _token=self.token, debate=debate,
|
data = dict(requirments=1, _token=self.token, debate=debate,
|
||||||
|
@ -42,8 +42,8 @@ class BaseCitizen(access_points.CitizenAPI):
|
|||||||
politics: classes.Politics = None
|
politics: classes.Politics = None
|
||||||
reporter: classes.Reporter = None
|
reporter: classes.Reporter = None
|
||||||
stop_threads: Event = None
|
stop_threads: Event = None
|
||||||
concurrency_lock: Event = None
|
concurrency_available: Event = None
|
||||||
telegram: classes.TelegramBot = None
|
telegram: classes.TelegramReporter = None
|
||||||
|
|
||||||
r: Response = None
|
r: Response = None
|
||||||
name: str = "Not logged in!"
|
name: str = "Not logged in!"
|
||||||
@ -59,8 +59,9 @@ class BaseCitizen(access_points.CitizenAPI):
|
|||||||
self.my_companies = classes.MyCompanies(self)
|
self.my_companies = classes.MyCompanies(self)
|
||||||
self.reporter = classes.Reporter(self)
|
self.reporter = classes.Reporter(self)
|
||||||
self.stop_threads = Event()
|
self.stop_threads = Event()
|
||||||
self.concurrency_lock = Event()
|
self.concurrency_available = Event()
|
||||||
self.telegram = classes.TelegramBot(stop_event=self.stop_threads)
|
self.concurrency_available.set()
|
||||||
|
self.telegram = classes.TelegramReporter(stop_event=self.stop_threads)
|
||||||
|
|
||||||
self.config.email = email
|
self.config.email = email
|
||||||
self.config.password = password
|
self.config.password = password
|
||||||
@ -428,15 +429,6 @@ class BaseCitizen(access_points.CitizenAPI):
|
|||||||
self.debug = bool(debug)
|
self.debug = bool(debug)
|
||||||
self._req.debug = bool(debug)
|
self._req.debug = bool(debug)
|
||||||
|
|
||||||
def _wait_for_concurrency_cleared(self) -> bool:
|
|
||||||
self.concurrency_lock.wait(600)
|
|
||||||
if self.concurrency_lock.is_set():
|
|
||||||
self.write_log('Unable to acquire concurrency lock in 10min!')
|
|
||||||
if self.debug:
|
|
||||||
self.report_error("Lock not released in 10min!")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def to_json(self, indent: bool = False) -> str:
|
def to_json(self, indent: bool = False) -> str:
|
||||||
return utils.json.dumps(self, cls=classes.MyJSONEncoder, indent=4 if indent else None)
|
return utils.json.dumps(self, cls=classes.MyJSONEncoder, indent=4 if indent else None)
|
||||||
|
|
||||||
@ -615,8 +607,8 @@ class BaseCitizen(access_points.CitizenAPI):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
continue
|
continue
|
||||||
if data:
|
if data:
|
||||||
msgs = ["{count} x {kind}, totaling {} {currency}\n"
|
msgs = [f"{d['count']} x {d['kind']}, totaling {d['count'] * d['reward']} "
|
||||||
"{about}".format(d["count"] * d["reward"], **d) for d in data.values()]
|
f"{d['currency']}" for d in data.values()]
|
||||||
|
|
||||||
msgs = "\n".join(msgs)
|
msgs = "\n".join(msgs)
|
||||||
if self.config.telegram:
|
if self.config.telegram:
|
||||||
@ -944,12 +936,10 @@ class CitizenCompanies(BaseCitizen):
|
|||||||
def work_as_manager_in_holding(self, holding: classes.Holding) -> Optional[Dict[str, Any]]:
|
def work_as_manager_in_holding(self, holding: classes.Holding) -> Optional[Dict[str, Any]]:
|
||||||
return self._work_as_manager(holding)
|
return self._work_as_manager(holding)
|
||||||
|
|
||||||
|
@utils.wait_for_lock
|
||||||
def _work_as_manager(self, wam_holding: classes.Holding) -> Optional[Dict[str, Any]]:
|
def _work_as_manager(self, wam_holding: classes.Holding) -> Optional[Dict[str, Any]]:
|
||||||
if self.restricted_ip:
|
if self.restricted_ip:
|
||||||
return None
|
return None
|
||||||
if not self._wait_for_concurrency_cleared():
|
|
||||||
return
|
|
||||||
self.concurrency_lock.set()
|
|
||||||
self.update_companies()
|
self.update_companies()
|
||||||
data = {"action_type": "production"}
|
data = {"action_type": "production"}
|
||||||
extra = {}
|
extra = {}
|
||||||
@ -971,7 +961,6 @@ class CitizenCompanies(BaseCitizen):
|
|||||||
data.update(extra)
|
data.update(extra)
|
||||||
if not self.details.current_region == wam_holding.region:
|
if not self.details.current_region == wam_holding.region:
|
||||||
self.write_log("Unable to work as manager because of location - please travel!")
|
self.write_log("Unable to work as manager because of location - please travel!")
|
||||||
self.concurrency_lock.clear()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
employ_factories = self.my_companies.get_employable_factories()
|
employ_factories = self.my_companies.get_employable_factories()
|
||||||
@ -980,7 +969,6 @@ class CitizenCompanies(BaseCitizen):
|
|||||||
|
|
||||||
response = self._post_economy_work("production", wam=[c.id for c in wam_list],
|
response = self._post_economy_work("production", wam=[c.id for c in wam_list],
|
||||||
employ=employ_factories).json()
|
employ=employ_factories).json()
|
||||||
self.concurrency_lock.clear()
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def update_companies(self):
|
def update_companies(self):
|
||||||
@ -1038,7 +1026,7 @@ class CitizenEconomy(CitizenTravel):
|
|||||||
ret.update({house_quality: till})
|
ret.update({house_quality: till})
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def buy_and_activate_house(self, q: int) -> Dict[int, datetime]:
|
def buy_and_activate_house(self, q: int) -> Optional[Dict[int, datetime]]:
|
||||||
original_region = self.details.current_country, self.details.current_region
|
original_region = self.details.current_country, self.details.current_region
|
||||||
ok_to_activate = False
|
ok_to_activate = False
|
||||||
inv = self.get_inventory()
|
inv = self.get_inventory()
|
||||||
@ -1051,17 +1039,22 @@ class CitizenEconomy(CitizenTravel):
|
|||||||
|
|
||||||
global_cheapest = self.get_market_offers("House", q)[f"q{q}"]
|
global_cheapest = self.get_market_offers("House", q)[f"q{q}"]
|
||||||
if global_cheapest.price + 200 < local_cheapest.price:
|
if global_cheapest.price + 200 < local_cheapest.price:
|
||||||
if not self._wait_for_concurrency_cleared():
|
if global_cheapest.price + 2000 < self.details.cc:
|
||||||
return self.check_house_durability()
|
if self.travel_to_country(global_cheapest.country):
|
||||||
self.concurrency_lock.set()
|
buy = self.buy_market_offer(global_cheapest, 1)
|
||||||
if self.travel_to_country(global_cheapest.country):
|
else:
|
||||||
buy = self.buy_from_market(global_cheapest.offer_id, 1)
|
buy = dict(error=True, message='Unable to travel!')
|
||||||
else:
|
else:
|
||||||
buy = {'error': True, 'message': 'Unable to travel!'}
|
buy = dict(error=True, message='Not enough money to buy house!')
|
||||||
else:
|
else:
|
||||||
buy = self.buy_from_market(local_cheapest.offer_id, 1)
|
if local_cheapest.price < self.details.cc:
|
||||||
if buy["error"]:
|
buy = self.buy_market_offer(local_cheapest, 1)
|
||||||
msg = f"Unable to buy q{q} house! \n{buy['message']}"
|
else:
|
||||||
|
buy = dict(error=True, message='Not enough money to buy house!')
|
||||||
|
if buy is None:
|
||||||
|
pass
|
||||||
|
elif buy["error"]:
|
||||||
|
msg = f'Unable to buy q{q} house! \n{buy["message"]}'
|
||||||
self.write_log(msg)
|
self.write_log(msg)
|
||||||
else:
|
else:
|
||||||
ok_to_activate = True
|
ok_to_activate = True
|
||||||
@ -1071,7 +1064,6 @@ class CitizenEconomy(CitizenTravel):
|
|||||||
self.activate_house(q)
|
self.activate_house(q)
|
||||||
if original_region[1] != self.details.current_region:
|
if original_region[1] != self.details.current_region:
|
||||||
self._travel(*original_region)
|
self._travel(*original_region)
|
||||||
self.concurrency_lock.clear()
|
|
||||||
return self.check_house_durability()
|
return self.check_house_durability()
|
||||||
|
|
||||||
def renew_houses(self, forced: bool = False) -> Dict[int, datetime]:
|
def renew_houses(self, forced: bool = False) -> Dict[int, datetime]:
|
||||||
@ -1083,7 +1075,9 @@ class CitizenEconomy(CitizenTravel):
|
|||||||
house_durability = self.check_house_durability()
|
house_durability = self.check_house_durability()
|
||||||
for q, active_till in house_durability.items():
|
for q, active_till in house_durability.items():
|
||||||
if utils.good_timedelta(active_till, - timedelta(hours=48)) <= self.now or forced:
|
if utils.good_timedelta(active_till, - timedelta(hours=48)) <= self.now or forced:
|
||||||
house_durability = self.buy_and_activate_house(q)
|
durability = self.buy_and_activate_house(q)
|
||||||
|
if durability:
|
||||||
|
house_durability = durability
|
||||||
return house_durability
|
return house_durability
|
||||||
|
|
||||||
def activate_house(self, quality: int) -> bool:
|
def activate_house(self, quality: int) -> bool:
|
||||||
@ -1164,11 +1158,13 @@ class CitizenEconomy(CitizenTravel):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def post_market_offer(self, industry: int, quality: int, amount: int, price: float) -> bool:
|
def post_market_offer(self, industry: int, quality: int, amount: int, price: float) -> bool:
|
||||||
|
if isinstance(industry, str):
|
||||||
|
industry = constants.INDUSTRIES[industry]
|
||||||
if not constants.INDUSTRIES[industry]:
|
if not constants.INDUSTRIES[industry]:
|
||||||
self.write_log(f"Trying to sell unsupported industry {industry}")
|
self.write_log(f"Trying to sell unsupported industry {industry}")
|
||||||
|
|
||||||
_inv_qlt = quality if industry in [1, 2, 3, 4, 23] else 0
|
_inv_qlt = quality if industry in [1, 2, 3, 4, 23] else 0
|
||||||
_kind = 'final' if industry in [1, 2, 4, 4, 23] else 'raw'
|
_kind = 'final' if industry in [1, 2, 4, 23] else 'raw'
|
||||||
inventory = self.get_inventory()
|
inventory = self.get_inventory()
|
||||||
items = inventory[_kind].get(constants.INDUSTRIES[industry], {_inv_qlt: {'amount': 0}})
|
items = inventory[_kind].get(constants.INDUSTRIES[industry], {_inv_qlt: {'amount': 0}})
|
||||||
if items[_inv_qlt]['amount'] < amount:
|
if items[_inv_qlt]['amount'] < amount:
|
||||||
@ -1193,7 +1189,7 @@ class CitizenEconomy(CitizenTravel):
|
|||||||
self._report_action("ECONOMY_SELL_PRODUCTS", message, kwargs=ret)
|
self._report_action("ECONOMY_SELL_PRODUCTS", message, kwargs=ret)
|
||||||
return not bool(ret.get('error', True))
|
return not bool(ret.get('error', True))
|
||||||
|
|
||||||
def buy_from_market(self, offer: int, amount: int) -> dict:
|
def buy_from_market(self, offer: int, amount: int) -> Dict[str, Any]:
|
||||||
ret = self._post_economy_marketplace_actions('buy', offer=offer, amount=amount)
|
ret = self._post_economy_marketplace_actions('buy', offer=offer, amount=amount)
|
||||||
json_ret = ret.json()
|
json_ret = ret.json()
|
||||||
if not json_ret.get('error', True):
|
if not json_ret.get('error', True):
|
||||||
@ -1203,26 +1199,17 @@ class CitizenEconomy(CitizenTravel):
|
|||||||
self._report_action("BOUGHT_PRODUCTS", json_ret.get('message'), kwargs=json_ret)
|
self._report_action("BOUGHT_PRODUCTS", json_ret.get('message'), kwargs=json_ret)
|
||||||
return json_ret
|
return json_ret
|
||||||
|
|
||||||
def buy_market_offer(self, offer: OfferItem, amount: int = None) -> dict:
|
@utils.wait_for_lock
|
||||||
|
def buy_market_offer(self, offer: OfferItem, amount: int = None) -> Optional[Dict[str, Any]]:
|
||||||
if amount is None or amount > offer.amount:
|
if amount is None or amount > offer.amount:
|
||||||
amount = offer.amount
|
amount = offer.amount
|
||||||
traveled = False
|
traveled = False
|
||||||
if not self.details.current_country == offer.country:
|
if not self.details.current_country == offer.country:
|
||||||
if not self._wait_for_concurrency_cleared():
|
|
||||||
return {'error': True, 'message': 'Concurrency locked for travel'}
|
|
||||||
self.concurrency_lock.set()
|
|
||||||
traveled = True
|
traveled = True
|
||||||
self.travel_to_country(offer.country)
|
self.travel_to_country(offer.country)
|
||||||
ret = self._post_economy_marketplace_actions('buy', offer=offer.offer_id, amount=amount)
|
json_ret = self.buy_from_market(offer.offer_id, amount)
|
||||||
json_ret = ret.json()
|
|
||||||
if not json_ret.get('error', True):
|
|
||||||
self.details.cc = ret.json()['currency']
|
|
||||||
self.details.gold = ret.json()['gold']
|
|
||||||
json_ret.pop("offerUpdate", None)
|
|
||||||
self._report_action("BOUGHT_PRODUCTS", json_ret.get('message'), kwargs=json_ret)
|
|
||||||
if traveled:
|
if traveled:
|
||||||
self.travel_to_residence()
|
self.travel_to_residence()
|
||||||
self.concurrency_lock.clear()
|
|
||||||
return json_ret
|
return json_ret
|
||||||
|
|
||||||
def get_market_offers(
|
def get_market_offers(
|
||||||
@ -1484,9 +1471,8 @@ class CitizenMedia(BaseCitizen):
|
|||||||
article_id = 0
|
article_id = 0
|
||||||
return article_id
|
return article_id
|
||||||
else:
|
else:
|
||||||
raise classes.ErepublikException("Article kind must be one of:\n{}\n'{}' is not supported".format(
|
kinds = "\n".join([f"{k}: {v}" for k, v in kinds.items()])
|
||||||
"\n".join(["{}: {}".format(k, v) for k, v in kinds.items()]), kind
|
raise classes.ErepublikException(f"Article kind must be one of:\n{kinds}\n'{kind}' is not supported")
|
||||||
))
|
|
||||||
|
|
||||||
def get_article(self, article_id: int) -> Dict[str, Any]:
|
def get_article(self, article_id: int) -> Dict[str, Any]:
|
||||||
return self._get_main_article_json(article_id).json()
|
return self._get_main_article_json(article_id).json()
|
||||||
@ -1702,20 +1688,25 @@ class CitizenMilitary(CitizenTravel):
|
|||||||
def has_battle_contribution(self):
|
def has_battle_contribution(self):
|
||||||
return bool(self.__last_war_update_data.get("citizen_contribution", []))
|
return bool(self.__last_war_update_data.get("citizen_contribution", []))
|
||||||
|
|
||||||
def find_battle_to_fight(self, silent: bool = False) -> Tuple[classes.Battle, classes.BattleDivision, classes.BattleSide]:
|
def find_battle_to_fight(self, silent: bool = False) -> Tuple[
|
||||||
|
classes.Battle, classes.BattleDivision, classes.BattleSide
|
||||||
|
]:
|
||||||
self.update_war_info()
|
self.update_war_info()
|
||||||
for battle in self.sorted_battles(self.config.sort_battles_time):
|
for battle in self.sorted_battles(self.config.sort_battles_time):
|
||||||
if not isinstance(battle, classes.Battle):
|
if not isinstance(battle, classes.Battle):
|
||||||
continue
|
continue
|
||||||
|
if battle.is_dict_lib:
|
||||||
|
continue
|
||||||
battle_zone: Optional[classes.BattleDivision] = None
|
battle_zone: Optional[classes.BattleDivision] = None
|
||||||
for div in battle.div.values():
|
for div in battle.div.values():
|
||||||
if div.terrain == 0:
|
if div.terrain == 0:
|
||||||
if div.div_end:
|
if div.div_end:
|
||||||
continue
|
continue
|
||||||
|
maverick_ok = self.maverick and self.config.maverick
|
||||||
if self.config.air and div.is_air:
|
if self.config.air and div.is_air:
|
||||||
battle_zone = div
|
battle_zone = div
|
||||||
break
|
break
|
||||||
elif self.config.ground and not div.is_air and (div.div == self.division or (self.maverick and self.config.maverick)):
|
elif self.config.ground and not div.is_air and (div.div == self.division or maverick_ok):
|
||||||
battle_zone = div
|
battle_zone = div
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@ -1769,18 +1760,15 @@ class CitizenMilitary(CitizenTravel):
|
|||||||
if not self.travel_to_battle(battle, countries_to_travel):
|
if not self.travel_to_battle(battle, countries_to_travel):
|
||||||
break
|
break
|
||||||
|
|
||||||
if not self._wait_for_concurrency_cleared():
|
|
||||||
return
|
|
||||||
self.concurrency_lock.set()
|
|
||||||
if self.change_division(battle, division):
|
if self.change_division(battle, division):
|
||||||
self.set_default_weapon(battle, division)
|
self.set_default_weapon(battle, division)
|
||||||
self.fight(battle, division, side)
|
self.fight(battle, division, side)
|
||||||
self.travel_to_residence()
|
self.travel_to_residence()
|
||||||
self.concurrency_lock.clear()
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@utils.wait_for_lock
|
||||||
def fight(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide = None,
|
def fight(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide = None,
|
||||||
count: int = None) -> int:
|
count: int = None) -> Optional[int]:
|
||||||
"""Fight in a battle.
|
"""Fight in a battle.
|
||||||
|
|
||||||
Will auto activate booster and travel if allowed to do it.
|
Will auto activate booster and travel if allowed to do it.
|
||||||
@ -1823,7 +1811,7 @@ class CitizenMilitary(CitizenTravel):
|
|||||||
else:
|
else:
|
||||||
self._eat('blue')
|
self._eat('blue')
|
||||||
if self.energy.recovered < 50 or error_count >= 10 or count <= 0:
|
if self.energy.recovered < 50 or error_count >= 10 or count <= 0:
|
||||||
self.write_log("Hits: {:>4} | Damage: {}".format(total_hits, total_damage))
|
self.write_log(f"Hits: {total_hits:>4} | Damage: {total_damage}")
|
||||||
ok_to_fight = False
|
ok_to_fight = False
|
||||||
if total_damage:
|
if total_damage:
|
||||||
self.reporter.report_fighting(battle, not side.is_defender, division, total_damage, total_hits)
|
self.reporter.report_fighting(battle, not side.is_defender, division, total_damage, total_hits)
|
||||||
@ -1860,6 +1848,9 @@ class CitizenMilitary(CitizenTravel):
|
|||||||
elif r_json.get("message") == "ZONE_INACTIVE":
|
elif r_json.get("message") == "ZONE_INACTIVE":
|
||||||
self.write_log("Wrong division!!")
|
self.write_log("Wrong division!!")
|
||||||
return 0, 10, 0
|
return 0, 10, 0
|
||||||
|
elif r_json.get("message") == "NON_BELLIGERENT":
|
||||||
|
self.write_log("Dictatorship/Liberation wars are not supported!")
|
||||||
|
return 0, 10, 0
|
||||||
elif r_json.get("message") in ["FIGHT_DISABLED", "DEPLOYMENT_MODE"]:
|
elif r_json.get("message") in ["FIGHT_DISABLED", "DEPLOYMENT_MODE"]:
|
||||||
self._post_main_profile_update('options',
|
self._post_main_profile_update('options',
|
||||||
params='{"optionName":"enable_web_deploy","optionValue":"off"}')
|
params='{"optionName":"enable_web_deploy","optionValue":"off"}')
|
||||||
@ -1881,21 +1872,24 @@ class CitizenMilitary(CitizenTravel):
|
|||||||
|
|
||||||
return hits, err, damage
|
return hits, err, damage
|
||||||
|
|
||||||
def deploy_bomb(self, battle: classes.Battle, bomb_id: int, inv_side: bool = None, count: int = 1) -> int:
|
@utils.wait_for_lock
|
||||||
|
def deploy_bomb(self, battle: classes.Battle, division: classes.BattleDivision, bomb_id: int, inv_side: bool, count: int = 1) -> Optional[int]:
|
||||||
"""Deploy bombs in a battle for given side.
|
"""Deploy bombs in a battle for given side.
|
||||||
|
|
||||||
:param battle: Battle
|
:param battle: Battle
|
||||||
:type battle: Battle
|
:type battle: classes.Battle
|
||||||
|
:param division: BattleDivision
|
||||||
|
:type division: classes.BattleDivision
|
||||||
:param bomb_id: int bomb id
|
:param bomb_id: int bomb id
|
||||||
:param inv_side: should deploy on invader side, if None then will deploy in currently available side
|
:type bomb_id: int
|
||||||
:param count: int how many bombs to deploy
|
:param inv_side: should deploy on invader side
|
||||||
|
:type inv_side: bool
|
||||||
|
:param count: how many bombs to deploy
|
||||||
|
:type count: int
|
||||||
:return: Deployed count
|
:return: Deployed count
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self._wait_for_concurrency_cleared():
|
|
||||||
return 0
|
|
||||||
self.concurrency_lock.set()
|
|
||||||
if not isinstance(count, int) or count < 1:
|
if not isinstance(count, int) or count < 1:
|
||||||
count = 1
|
count = 1
|
||||||
has_traveled = False
|
has_traveled = False
|
||||||
@ -1906,10 +1900,6 @@ class CitizenMilitary(CitizenTravel):
|
|||||||
good_countries = [battle.invader.country] + battle.invader.deployed
|
good_countries = [battle.invader.country] + battle.invader.deployed
|
||||||
if self.details.current_country not in good_countries:
|
if self.details.current_country not in good_countries:
|
||||||
has_traveled = self.travel_to_battle(battle, good_countries)
|
has_traveled = self.travel_to_battle(battle, good_countries)
|
||||||
elif inv_side is not None:
|
|
||||||
good_countries = [battle.defender.country] + battle.defender.deployed
|
|
||||||
if self.details.current_country not in good_countries:
|
|
||||||
has_traveled = self.travel_to_battle(battle, good_countries)
|
|
||||||
else:
|
else:
|
||||||
involved = [battle.invader.country,
|
involved = [battle.invader.country,
|
||||||
battle.defender.country] + battle.invader.deployed + battle.defender.deployed
|
battle.defender.country] + battle.invader.deployed + battle.defender.deployed
|
||||||
@ -1917,7 +1907,7 @@ class CitizenMilitary(CitizenTravel):
|
|||||||
count = 0
|
count = 0
|
||||||
errors = deployed_count = 0
|
errors = deployed_count = 0
|
||||||
while (not deployed_count == count) and errors < 10:
|
while (not deployed_count == count) and errors < 10:
|
||||||
r = self._post_military_deploy_bomb(battle.id, bomb_id).json()
|
r = self._post_military_deploy_bomb(battle.id, division.id, bomb_id).json()
|
||||||
if not r.get('error'):
|
if not r.get('error'):
|
||||||
deployed_count += 1
|
deployed_count += 1
|
||||||
elif r.get('message') == 'LOCKED':
|
elif r.get('message') == 'LOCKED':
|
||||||
@ -1929,7 +1919,6 @@ class CitizenMilitary(CitizenTravel):
|
|||||||
self.travel_to_residence()
|
self.travel_to_residence()
|
||||||
|
|
||||||
self._report_action("MILITARY_BOMB", f"Deployed {deployed_count} bombs in battle {battle.id}")
|
self._report_action("MILITARY_BOMB", f"Deployed {deployed_count} bombs in battle {battle.id}")
|
||||||
self.concurrency_lock.clear()
|
|
||||||
return deployed_count
|
return deployed_count
|
||||||
|
|
||||||
def change_division(self, battle: classes.Battle, division: classes.BattleDivision) -> bool:
|
def change_division(self, battle: classes.Battle, division: classes.BattleDivision) -> bool:
|
||||||
@ -2328,7 +2317,7 @@ class CitizenTasks(BaseCitizen):
|
|||||||
self._eat("blue")
|
self._eat("blue")
|
||||||
if self.energy.food_fights < 1:
|
if self.energy.food_fights < 1:
|
||||||
seconds = (self.energy.reference_time - self.now).total_seconds()
|
seconds = (self.energy.reference_time - self.now).total_seconds()
|
||||||
self.write_log("I don't have energy to work. Will sleep for {}s".format(seconds))
|
self.write_log(f"I don't have energy to work. Will sleep for {seconds}s")
|
||||||
self.sleep(seconds)
|
self.sleep(seconds)
|
||||||
self._eat("blue")
|
self._eat("blue")
|
||||||
self.work()
|
self.work()
|
||||||
@ -2459,11 +2448,10 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
|
|||||||
|
|
||||||
self.update_citizen_info()
|
self.update_citizen_info()
|
||||||
self.reporter.do_init()
|
self.reporter.do_init()
|
||||||
if self.config.telegram:
|
if self.config.telegram and self.config.telegram_chat_id:
|
||||||
# noinspection SpellCheckingInspection
|
self.telegram.do_init(self.config.telegram_chat_id,
|
||||||
self.telegram.do_init(self.config.telegram_chat_id or 620981703,
|
self.config.telegram_token,
|
||||||
self.config.telegram_token or "864251270:AAFzZZdjspI-kIgJVk4gF3TViGFoHnf8H4o",
|
self.name)
|
||||||
"" if self.config.telegram_chat_id or self.config.telegram_token else self.name)
|
|
||||||
self.telegram.send_message(f"*Started* {utils.now():%F %T}")
|
self.telegram.send_message(f"*Started* {utils.now():%F %T}")
|
||||||
|
|
||||||
self.update_all(True)
|
self.update_all(True)
|
||||||
@ -2484,7 +2472,6 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
|
|||||||
if self.details.gold >= 54:
|
if self.details.gold >= 54:
|
||||||
self.buy_tg_contract()
|
self.buy_tg_contract()
|
||||||
else:
|
else:
|
||||||
|
|
||||||
self.write_log(f"Training ground contract active but "
|
self.write_log(f"Training ground contract active but "
|
||||||
f"don't have enough gold ({self.details.gold}g {self.details.cc}cc)")
|
f"don't have enough gold ({self.details.gold}g {self.details.cc}cc)")
|
||||||
if self.energy.is_energy_full and self.config.telegram:
|
if self.energy.is_energy_full and self.config.telegram:
|
||||||
@ -2521,8 +2508,8 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
|
|||||||
data[(title, reward)]['count'] += count
|
data[(title, reward)]['count'] += count
|
||||||
self._post_main_global_alerts_close(medal.get('id'))
|
self._post_main_global_alerts_close(medal.get('id'))
|
||||||
if data:
|
if data:
|
||||||
msgs = ["{count} x {kind},"
|
msgs = [f"{d['count']} x {d['kind']}, totaling {d['count'] * d['reward']} "
|
||||||
" totaling {} {currency}".format(d["count"] * d["reward"], **d) for d in data.values()]
|
f"{d['currency']}" for d in data.values()]
|
||||||
|
|
||||||
msgs = "\n".join(msgs)
|
msgs = "\n".join(msgs)
|
||||||
if self.config.telegram:
|
if self.config.telegram:
|
||||||
@ -2655,7 +2642,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
|
|||||||
if not amount:
|
if not amount:
|
||||||
inv_resp = self._get_economy_inventory_items().json()
|
inv_resp = self._get_economy_inventory_items().json()
|
||||||
category = "rawMaterials" if kind.endswith("Raw") else "finalProducts"
|
category = "rawMaterials" if kind.endswith("Raw") else "finalProducts"
|
||||||
item = "{}_{}".format(constants.INDUSTRIES[kind], quality)
|
item = f"{constants.INDUSTRIES[kind]}_{quality}"
|
||||||
amount = inv_resp.get("inventoryItems").get(category).get("items").get(item).get("amount", 0)
|
amount = inv_resp.get("inventoryItems").get(category).get("items").get(item).get("amount", 0)
|
||||||
|
|
||||||
if amount >= 1:
|
if amount >= 1:
|
||||||
@ -2684,7 +2671,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
|
|||||||
elif kind.endswith("Raw"):
|
elif kind.endswith("Raw"):
|
||||||
self.sell_produced_product(kind, 1)
|
self.sell_produced_product(kind, 1)
|
||||||
else:
|
else:
|
||||||
raise classes.ErepublikException("Unknown kind produced '{kind}'".format(kind=kind))
|
raise classes.ErepublikException(f"Unknown kind produced '{kind}'")
|
||||||
elif self.config.auto_buy_raw and re.search(r"not_enough_[^_]*_raw", response.get("message")):
|
elif self.config.auto_buy_raw and re.search(r"not_enough_[^_]*_raw", response.get("message")):
|
||||||
raw_kind = re.search(r"not_enough_(\w+)_raw", response.get("message"))
|
raw_kind = re.search(r"not_enough_(\w+)_raw", response.get("message"))
|
||||||
if raw_kind:
|
if raw_kind:
|
||||||
@ -2730,7 +2717,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
|
|||||||
self._report_action("WORK_AS_MANAGER", "Not enough money to work as manager!", kwargs=response)
|
self._report_action("WORK_AS_MANAGER", "Not enough money to work as manager!", kwargs=response)
|
||||||
self.write_log("Not enough money to work as manager!")
|
self.write_log("Not enough money to work as manager!")
|
||||||
else:
|
else:
|
||||||
msg = "I was not able to wam and or employ because:\n{}".format(response)
|
msg = f"I was not able to wam and or employ because:\n{response}"
|
||||||
self._report_action("WORK_AS_MANAGER", f"Worked as manager failed: {msg}", kwargs=response)
|
self._report_action("WORK_AS_MANAGER", f"Worked as manager failed: {msg}", kwargs=response)
|
||||||
self.write_log(msg)
|
self.write_log(msg)
|
||||||
|
|
||||||
@ -2769,7 +2756,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
|
|||||||
|
|
||||||
wam_count = self.my_companies.get_total_wam_count()
|
wam_count = self.my_companies.get_total_wam_count()
|
||||||
if wam_count:
|
if wam_count:
|
||||||
self.write_log("Wam ff lockdown is now {}, was {}".format(wam_count, self.my_companies.ff_lockdown))
|
self.write_log(f"Wam ff lockdown is now {wam_count}, was {self.my_companies.ff_lockdown}")
|
||||||
self.my_companies.ff_lockdown = wam_count
|
self.my_companies.ff_lockdown = wam_count
|
||||||
self.travel_to_residence()
|
self.travel_to_residence()
|
||||||
return bool(wam_count)
|
return bool(wam_count)
|
||||||
|
@ -10,7 +10,7 @@ from requests import Response, Session, post
|
|||||||
from . import utils, constants
|
from . import utils, constants
|
||||||
|
|
||||||
__all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException',
|
__all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException',
|
||||||
'Holding', 'MyCompanies', 'MyJSONEncoder', 'OfferItem', 'Politics', 'Reporter', 'TelegramBot']
|
'Holding', 'MyCompanies', 'MyJSONEncoder', 'OfferItem', 'Politics', 'Reporter', 'TelegramReporter']
|
||||||
|
|
||||||
|
|
||||||
class ErepublikException(Exception):
|
class ErepublikException(Exception):
|
||||||
@ -419,7 +419,7 @@ class Energy:
|
|||||||
self._recovery_time = utils.now()
|
self._recovery_time = utils.now()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "{:4}/{:4} + {:4}, {:3}hp/6min".format(self.recovered, self.limit, self.recoverable, self.interval)
|
return f"{self.recovered:4}/{self.limit:4} + {self.recoverable:4}, {self.interval:3}hp/6min"
|
||||||
|
|
||||||
def set_reference_time(self, recovery_time: datetime.datetime):
|
def set_reference_time(self, recovery_time: datetime.datetime):
|
||||||
self._recovery_time = recovery_time.replace(microsecond=0)
|
self._recovery_time = recovery_time.replace(microsecond=0)
|
||||||
@ -598,10 +598,10 @@ class Reporter:
|
|||||||
for unreported_data in self.__to_update:
|
for unreported_data in self.__to_update:
|
||||||
unreported_data.update(player_id=self.citizen_id, key=self.key)
|
unreported_data.update(player_id=self.citizen_id, key=self.key)
|
||||||
unreported_data = utils.json.loads(utils.json.dumps(unreported_data, cls=MyJSONEncoder))
|
unreported_data = utils.json.loads(utils.json.dumps(unreported_data, cls=MyJSONEncoder))
|
||||||
self._req.post("{}/bot/update".format(self.url), json=unreported_data)
|
self._req.post(f"{self.url}/bot/update", json=unreported_data)
|
||||||
self.__to_update.clear()
|
self.__to_update.clear()
|
||||||
data = utils.json.loads(utils.json.dumps(data, cls=MyJSONEncoder))
|
data = utils.json.loads(utils.json.dumps(data, cls=MyJSONEncoder))
|
||||||
r = self._req.post("{}/bot/update".format(self.url), json=data)
|
r = self._req.post(f"{self.url}/bot/update", json=data)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def register_account(self):
|
def register_account(self):
|
||||||
@ -609,8 +609,8 @@ class Reporter:
|
|||||||
try:
|
try:
|
||||||
r = self.__bot_update(dict(key=self.key, check=True, player_id=self.citizen_id))
|
r = self.__bot_update(dict(key=self.key, check=True, player_id=self.citizen_id))
|
||||||
if not r.json().get("status"):
|
if not r.json().get("status"):
|
||||||
self._req.post("{}/bot/register".format(self.url), json=dict(name=self.name, email=self.email,
|
self._req.post(f"{self.url}/bot/register", json=dict(name=self.name, email=self.email,
|
||||||
player_id=self.citizen_id))
|
player_id=self.citizen_id))
|
||||||
finally:
|
finally:
|
||||||
self.__registered = True
|
self.__registered = True
|
||||||
self.allowed = True
|
self.allowed = True
|
||||||
@ -895,9 +895,9 @@ class Battle:
|
|||||||
time_now = utils.now()
|
time_now = utils.now()
|
||||||
is_started = self.start < utils.now()
|
is_started = self.start < utils.now()
|
||||||
if is_started:
|
if is_started:
|
||||||
time_part = " {}".format(time_now - self.start)
|
time_part = f" {time_now - self.start}"
|
||||||
else:
|
else:
|
||||||
time_part = "-{}".format(self.start - time_now)
|
time_part = f"-{self.start - time_now}"
|
||||||
|
|
||||||
return (f"Battle {self.id} for {self.region_name[:16]:16} | "
|
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}")
|
f"{self.invader} : {self.defender} | Round time {time_part} | {'R'+str(self.zone_id):>3}")
|
||||||
@ -937,7 +937,7 @@ class EnergyToFight:
|
|||||||
return self.energy
|
return self.energy
|
||||||
|
|
||||||
|
|
||||||
class TelegramBot:
|
class TelegramReporter:
|
||||||
__initialized: bool = False
|
__initialized: bool = False
|
||||||
__queue: List[str]
|
__queue: List[str]
|
||||||
chat_id: int = 0
|
chat_id: int = 0
|
||||||
@ -962,10 +962,12 @@ class TelegramBot:
|
|||||||
'last_time': self._last_time, 'next_time': self._next_time, 'queue': self.__queue,
|
'last_time': self._last_time, 'next_time': self._next_time, 'queue': self.__queue,
|
||||||
'initialized': self.__initialized, 'has_threads': not self._threads}
|
'initialized': self.__initialized, 'has_threads': not self._threads}
|
||||||
|
|
||||||
def do_init(self, chat_id: int, token: str, player_name: str = ""):
|
def do_init(self, chat_id: int, token: str = None, player_name: str = None):
|
||||||
|
if token is None:
|
||||||
|
token = "864251270:AAFzZZdjspI-kIgJVk4gF3TViGFoHnf8H4o"
|
||||||
self.chat_id = chat_id
|
self.chat_id = chat_id
|
||||||
self.api_url = "https://api.telegram.org/bot{}/sendMessage".format(token)
|
self.api_url = f"https://api.telegram.org/bot{token}/sendMessage"
|
||||||
self.player_name = player_name
|
self.player_name = player_name or ""
|
||||||
self.__initialized = True
|
self.__initialized = True
|
||||||
self._last_time = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=-5))
|
self._last_time = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=-5))
|
||||||
self._last_full_energy_report = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=-30))
|
self._last_full_energy_report = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=-30))
|
||||||
@ -981,7 +983,7 @@ class TelegramBot:
|
|||||||
self._threads = [t for t in self._threads if t.is_alive()]
|
self._threads = [t for t in self._threads if t.is_alive()]
|
||||||
self._next_time = utils.good_timedelta(utils.now(), datetime.timedelta(seconds=20))
|
self._next_time = utils.good_timedelta(utils.now(), datetime.timedelta(seconds=20))
|
||||||
if not self._threads:
|
if not self._threads:
|
||||||
name = "telegram_{}send".format(f"{self.player_name}_" if self.player_name else "")
|
name = f"telegram_{f'{self.player_name}_' if self.player_name else ''}send"
|
||||||
send_thread = threading.Thread(target=self.__send_messages, name=name)
|
send_thread = threading.Thread(target=self.__send_messages, name=name)
|
||||||
send_thread.start()
|
send_thread.start()
|
||||||
self._threads.append(send_thread)
|
self._threads.append(send_thread)
|
||||||
@ -1016,7 +1018,7 @@ class TelegramBot:
|
|||||||
|
|
||||||
|
|
||||||
class OfferItem(NamedTuple):
|
class OfferItem(NamedTuple):
|
||||||
price: float = 99_999.
|
price: float = 999_999_999.
|
||||||
country: constants.Country = constants.Country(0, "", "", "")
|
country: constants.Country = constants.Country(0, "", "", "")
|
||||||
amount: int = 0
|
amount: int = 0
|
||||||
offer_id: int = 0
|
offer_id: int = 0
|
||||||
|
@ -25,7 +25,7 @@ __all__ = ['VERSION', 'calculate_hit', 'caught_error', 'date_from_eday', 'eday_f
|
|||||||
'get_air_hit_dmg_value', 'get_file', 'get_ground_hit_dmg_value', 'get_sleep_seconds', 'good_timedelta',
|
'get_air_hit_dmg_value', 'get_file', 'get_ground_hit_dmg_value', 'get_sleep_seconds', 'good_timedelta',
|
||||||
'interactive_sleep', 'json', 'localize_dt', 'localize_timestamp', 'normalize_html_json', 'now',
|
'interactive_sleep', 'json', 'localize_dt', 'localize_timestamp', 'normalize_html_json', 'now',
|
||||||
'process_error', 'process_warning', 'send_email', 'silent_sleep', 'slugify', 'write_file',
|
'process_error', 'process_warning', 'send_email', 'silent_sleep', 'slugify', 'write_file',
|
||||||
'write_interactive_log', 'write_silent_log']
|
'write_interactive_log', 'write_silent_log', 'get_final_hit_dmg', 'wait_for_lock']
|
||||||
|
|
||||||
if not sys.version_info >= (3, 6):
|
if not sys.version_info >= (3, 6):
|
||||||
raise AssertionError('This script requires Python version 3.6 and higher\n'
|
raise AssertionError('This script requires Python version 3.6 and higher\n'
|
||||||
@ -93,7 +93,7 @@ def interactive_sleep(sleep_seconds: int):
|
|||||||
# seconds = seconds % 30 if seconds % 30 else 30
|
# seconds = seconds % 30 if seconds % 30 else 30
|
||||||
else:
|
else:
|
||||||
seconds = 1
|
seconds = 1
|
||||||
sys.stdout.write("\rSleeping for {:4} more seconds".format(sleep_seconds))
|
sys.stdout.write(f"\rSleeping for {sleep_seconds:4} more seconds")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
time.sleep(seconds)
|
time.sleep(seconds)
|
||||||
sleep_seconds -= seconds
|
sleep_seconds -= seconds
|
||||||
@ -105,7 +105,7 @@ silent_sleep = time.sleep
|
|||||||
|
|
||||||
def _write_log(msg, timestamp: bool = True, should_print: bool = False):
|
def _write_log(msg, timestamp: bool = True, should_print: bool = False):
|
||||||
erep_time_now = now()
|
erep_time_now = now()
|
||||||
txt = "[{}] {}".format(erep_time_now.strftime('%F %T'), msg) if timestamp else msg
|
txt = f"[{erep_time_now.strftime('%F %T')}] {msg}" if timestamp else msg
|
||||||
txt = "\n".join(["\n".join(textwrap.wrap(line, 120)) for line in txt.splitlines()])
|
txt = "\n".join(["\n".join(textwrap.wrap(line, 120)) for line in txt.splitlines()])
|
||||||
if not os.path.isdir('log'):
|
if not os.path.isdir('log'):
|
||||||
os.mkdir('log')
|
os.mkdir('log')
|
||||||
@ -152,7 +152,8 @@ def get_file(filepath: str) -> str:
|
|||||||
def write_file(filename: str, content: str) -> int:
|
def write_file(filename: str, content: str) -> int:
|
||||||
filename = get_file(filename)
|
filename = get_file(filename)
|
||||||
with open(filename, 'ab') as f:
|
with open(filename, 'ab') as f:
|
||||||
return f.write(content.encode("utf-8"))
|
ret = f.write(content.encode("utf-8"))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def write_request(response: requests.Response, is_error: bool = False):
|
def write_request(response: requests.Response, is_error: bool = False):
|
||||||
@ -171,10 +172,10 @@ def write_request(response: requests.Response, is_error: bool = False):
|
|||||||
ext = "html"
|
ext = "html"
|
||||||
|
|
||||||
if not is_error:
|
if not is_error:
|
||||||
filename = "debug/requests/{}_{}.{}".format(now().strftime('%F_%H-%M-%S'), name, ext)
|
filename = f"debug/requests/{now().strftime('%F_%H-%M-%S')}_{name}.{ext}"
|
||||||
write_file(filename, html)
|
write_file(filename, html)
|
||||||
else:
|
else:
|
||||||
return {"name": "{}_{}.{}".format(now().strftime('%F_%H-%M-%S'), name, ext),
|
return {"name": f"{now().strftime('%F_%H-%M-%S')}_{name}.{ext}",
|
||||||
"content": html.encode('utf-8'),
|
"content": html.encode('utf-8'),
|
||||||
"mimetype": "application/json" if ext == "json" else "text/html"}
|
"mimetype": "application/json" if ext == "json" else "text/html"}
|
||||||
|
|
||||||
@ -195,14 +196,14 @@ def send_email(name: str, content: List[Any], player=None, local_vars: Dict[str,
|
|||||||
if promo:
|
if promo:
|
||||||
resp = {"name": "%s.html" % name, "mimetype": "text/html",
|
resp = {"name": "%s.html" % name, "mimetype": "text/html",
|
||||||
"content": file_content_template.format(title="Promo", body="<br/>".join(content))}
|
"content": file_content_template.format(title="Promo", body="<br/>".join(content))}
|
||||||
subject = "[eBot][{}] Promos: {}".format(now().strftime('%F %T'), name)
|
subject = f"[eBot][{now().strftime('%F %T')}] Promos: {name}"
|
||||||
|
|
||||||
elif captcha:
|
elif captcha:
|
||||||
resp = {"name": "%s.html" % name, "mimetype": "text/html",
|
resp = {"name": "%s.html" % name, "mimetype": "text/html",
|
||||||
"content": file_content_template.format(title="ReCaptcha", body="<br/>".join(content))}
|
"content": file_content_template.format(title="ReCaptcha", body="<br/>".join(content))}
|
||||||
subject = "[eBot][{}] RECAPTCHA: {}".format(now().strftime('%F %T'), name)
|
subject = f"[eBot][{now().strftime('%F %T')}] RECAPTCHA: {name}"
|
||||||
else:
|
else:
|
||||||
subject = "[eBot][%s] Bug trace: %s" % (now().strftime('%F %T'), name)
|
subject = f"[eBot][{now().strftime('%F %T')}] Bug trace: {name}"
|
||||||
|
|
||||||
body = "".join(traceback.format_stack()) + \
|
body = "".join(traceback.format_stack()) + \
|
||||||
"\n\n" + \
|
"\n\n" + \
|
||||||
@ -383,3 +384,19 @@ def get_final_hit_dmg(base_dmg: Union[Decimal, float, str], rang: int,
|
|||||||
|
|
||||||
def deprecation(message):
|
def deprecation(message):
|
||||||
warnings.warn(message, DeprecationWarning, stacklevel=2)
|
warnings.warn(message, DeprecationWarning, stacklevel=2)
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_lock(function):
|
||||||
|
def wrapper(instance, *args, **kwargs):
|
||||||
|
if not instance.concurrency_available.wait(600):
|
||||||
|
e = 'Concurrency not freed in 10min!'
|
||||||
|
instance.write_log(e)
|
||||||
|
if instance.debug:
|
||||||
|
instance.report_error(e)
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
instance.concurrency_available.clear()
|
||||||
|
ret = function(instance, *args, **kwargs)
|
||||||
|
instance.concurrency_available.set()
|
||||||
|
return ret
|
||||||
|
return wrapper
|
||||||
|
@ -97,7 +97,7 @@ def main():
|
|||||||
player.set_debug(CONFIG.get('debug', False))
|
player.set_debug(CONFIG.get('debug', False))
|
||||||
player.login()
|
player.login()
|
||||||
if CONFIG.get('battle_launcher'):
|
if CONFIG.get('battle_launcher'):
|
||||||
name = "{}-battle_launcher-{}".format(player.name, threading.active_count() - 1)
|
name = f"{player.name}-battle_launcher-{threading.active_count() - 1}"
|
||||||
state_thread = threading.Thread(target=_battle_launcher, args=(player,), name=name)
|
state_thread = threading.Thread(target=_battle_launcher, args=(player,), name=name)
|
||||||
state_thread.start()
|
state_thread.start()
|
||||||
|
|
||||||
|
@ -94,15 +94,15 @@ def main():
|
|||||||
closest_next_time = dt_max
|
closest_next_time = dt_max
|
||||||
next_tasks = []
|
next_tasks = []
|
||||||
for task, next_time in sorted(tasks.items(), key=lambda s: s[1]):
|
for task, next_time in sorted(tasks.items(), key=lambda s: s[1]):
|
||||||
next_tasks.append("{}: {}".format(next_time.strftime('%F %T'), task))
|
next_tasks.append(f"{next_time.strftime('%F %T')}: {task}")
|
||||||
if next_time < closest_next_time:
|
if next_time < closest_next_time:
|
||||||
closest_next_time = next_time
|
closest_next_time = next_time
|
||||||
sleep_seconds = int(utils.get_sleep_seconds(closest_next_time))
|
sleep_seconds = int(utils.get_sleep_seconds(closest_next_time))
|
||||||
if sleep_seconds <= 0:
|
if sleep_seconds <= 0:
|
||||||
player.write_log(f"Loop detected! Offending task: '{next_tasks[0]}'")
|
player.write_log(f"Loop detected! Offending task: '{next_tasks[0]}'")
|
||||||
player.write_log("My next Tasks and there time:\n" + "\n".join(sorted(next_tasks)))
|
player.write_log("My next Tasks and there time:\n" + "\n".join(sorted(next_tasks)))
|
||||||
player.write_log("Sleeping until (eRep): {} (sleeping for {}s)".format(
|
player.write_log(f"Sleeping until (eRep): {closest_next_time.strftime('%F %T')}"
|
||||||
closest_next_time.strftime("%F %T"), sleep_seconds))
|
f" (sleeping for {sleep_seconds}s)")
|
||||||
seconds_to_sleep = sleep_seconds if sleep_seconds > 0 else 0
|
seconds_to_sleep = sleep_seconds if sleep_seconds > 0 else 0
|
||||||
player.sleep(seconds_to_sleep)
|
player.sleep(seconds_to_sleep)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.23.2.1
|
current_version = 0.23.3
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
|
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
|
||||||
|
2
setup.py
2
setup.py
@ -50,6 +50,6 @@ setup(
|
|||||||
test_suite='tests',
|
test_suite='tests',
|
||||||
tests_require=test_requirements,
|
tests_require=test_requirements,
|
||||||
url='https://github.com/eeriks/erepublik/',
|
url='https://github.com/eeriks/erepublik/',
|
||||||
version='0.23.2.1',
|
version='0.23.3',
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user