Compare commits
33 Commits
v0.23.4.4
...
v0.23.4.13
Author | SHA1 | Date | |
---|---|---|---|
0936dee06c | |||
a1c6fb06a0 | |||
c57bf99976 | |||
995b45464f | |||
ebe28c948a | |||
34d7230faf | |||
3235991cce | |||
3fba1d6b3d | |||
88c8d5a9a0 | |||
382749a8d8 | |||
0c877e315b | |||
c95f642fee | |||
da0276f9a6 | |||
f89f91e969 | |||
56c2ca1b6e | |||
3cee2d1f0f | |||
0ed03877ce | |||
491c9f5efe | |||
8445b556e7 | |||
d02c5c2969 | |||
bff4183cb6 | |||
47f5142837 | |||
a32fd039dd | |||
311e684c0c | |||
52038a86d5 | |||
c35a107641 | |||
b53b2f0fae | |||
8da9b6221a | |||
caa41c85f6 | |||
da3b5d5768 | |||
f96d0233f9 | |||
c693a5182e | |||
0dfba6a9ff |
@ -4,7 +4,7 @@
|
||||
|
||||
__author__ = """Eriks Karls"""
|
||||
__email__ = 'eriks@72.lv'
|
||||
__version__ = '0.23.4.4'
|
||||
__version__ = '0.23.4.13'
|
||||
|
||||
from erepublik import classes, constants, utils
|
||||
from erepublik.citizen import Citizen
|
||||
|
@ -13,43 +13,43 @@ __all__ = ['SlowRequests', 'CitizenAPI']
|
||||
class SlowRequests(Session):
|
||||
last_time: datetime.datetime
|
||||
timeout: datetime.timedelta = datetime.timedelta(milliseconds=500)
|
||||
uas: List[str] = [
|
||||
_uas: List[str] = [
|
||||
# Chrome
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36',
|
||||
'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',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 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',
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 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/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',
|
||||
|
||||
# FireFox
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0',
|
||||
]
|
||||
debug: bool = False
|
||||
|
||||
def __init__(self, proxies: Dict[str, str] = None):
|
||||
def __init__(self, proxies: Dict[str, str] = None, user_agent: str = None):
|
||||
super().__init__()
|
||||
if proxies:
|
||||
self.proxies = proxies
|
||||
if user_agent is None:
|
||||
user_agent = random.choice(self._uas)
|
||||
self.request_log_name = utils.get_file(utils.now().strftime("debug/requests_%Y-%m-%d.log"))
|
||||
self.last_time = utils.now()
|
||||
self.headers.update({
|
||||
'User-Agent': random.choice(self.uas)
|
||||
})
|
||||
self.headers.update({'User-Agent': user_agent})
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
return dict(last_time=self.last_time, timeout=self.timeout, user_agent=self.headers['User-Agent'],
|
||||
request_log_name=self.request_log_name, debug=self.debug)
|
||||
return dict(last_time=self.last_time, timeout=self.timeout, cookies=self.cookies.get_dict(), debug=self.debug,
|
||||
user_agent=self.headers['User-Agent'], request_log_name=self.request_log_name, proxies=self.proxies)
|
||||
|
||||
def request(self, method, url, *args, **kwargs):
|
||||
self._slow_down_requests()
|
||||
@ -120,6 +120,10 @@ class CitizenBaseAPI:
|
||||
self._req = SlowRequests()
|
||||
self.token = ""
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
return dict(url=self.url, request=self._req.as_dict, token=self.token)
|
||||
|
||||
def post(self, url: str, data=None, json=None, **kwargs) -> Response:
|
||||
return self._req.post(url, data, json, **kwargs)
|
||||
|
||||
@ -305,11 +309,11 @@ class ErepublikEconomyAPI(CitizenBaseAPI):
|
||||
return self.post(f"{self.url}/main/buyGoldItems", data=data)
|
||||
|
||||
def _post_economy_activate_booster(self, quality: int, duration: int, kind: str) -> Response:
|
||||
data = dict(type=kind, quality=quality, duration=duration, fromInventory=True)
|
||||
data = dict(type=kind, quality=quality, duration=duration, fromInventory=True, _token=self.token)
|
||||
return self.post(f"{self.url}/economy/activateBooster", data=data)
|
||||
|
||||
def _post_economy_activate_house(self, quality: int) -> Response:
|
||||
data = {"action": "activate", "quality": quality, "type": "house", "_token": self.token}
|
||||
data = dict(action="activate", quality=quality, type="house", _token=self.token)
|
||||
return self.post(f"{self.url}/economy/activateHouse", data=data)
|
||||
|
||||
def _post_economy_donate_items_action(self, citizen_id: int, amount: int, industry: int,
|
||||
|
@ -12,6 +12,7 @@ from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, Union
|
||||
from requests import HTTPError, RequestException, Response
|
||||
|
||||
from . import access_points, classes, constants, types, utils
|
||||
from .access_points import SlowRequests
|
||||
from .classes import OfferItem
|
||||
|
||||
|
||||
@ -38,9 +39,9 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
energy: classes.Energy = None
|
||||
details: classes.Details = None
|
||||
politics: classes.Politics = None
|
||||
my_companies: classes.MyCompanies = None
|
||||
reporter: classes.Reporter = None
|
||||
stop_threads: Event = None
|
||||
concurrency_available: Event = None
|
||||
telegram: classes.TelegramReporter = None
|
||||
|
||||
r: Response = None
|
||||
@ -57,8 +58,6 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
self.my_companies = classes.MyCompanies(self)
|
||||
self.reporter = classes.Reporter(self)
|
||||
self.stop_threads = Event()
|
||||
self.concurrency_available = Event()
|
||||
self.concurrency_available.set()
|
||||
self.telegram = classes.TelegramReporter(stop_event=self.stop_threads)
|
||||
|
||||
self.config.email = email
|
||||
@ -238,10 +237,7 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
|
||||
def update_inventory(self):
|
||||
"""
|
||||
Updates class properties and returns structured inventory.
|
||||
Return structure: {status: {used: int, total: int}, items: {active/final/raw: {item_token:{quality: data}}}
|
||||
If item kind is damageBoosters or aircraftDamageBoosters then kind is renamed to kind+quality and duration is
|
||||
used as quality.
|
||||
Updates citizen inventory
|
||||
"""
|
||||
self._update_inventory_data(self._get_economy_inventory_items().json())
|
||||
|
||||
@ -291,8 +287,9 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
if item_data.get('attributes').get('expirationInfo'):
|
||||
expire_info = item_data.get('attributes').get('expirationInfo')
|
||||
expiration_info = [_expire_value_to_python(v) for v in expire_info['value']]
|
||||
icon = item_data['icon'] if item_data[
|
||||
'icon'] else "//www.erepublik.net/images/modules/manager/tab_storage.png"
|
||||
if not item_data.get('icon') and item_data.get('isPackBooster'):
|
||||
item_data['icon'] = f"//www.erepublik.com/images/icons/boosters/52px/{item_data.get('type')}.png"
|
||||
icon = item_data['icon'] if item_data['icon'] else "//www.erepublik.net/images/modules/manager/tab_storage.png"
|
||||
inv_item: types.InvFinalItem = dict(
|
||||
name=item_data.get("name"), time_left=item_data['active']['time_left'], icon=icon,
|
||||
kind=kind, expiration=expiration_info, quality=item_data.get("quality", 0)
|
||||
@ -343,6 +340,9 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
elif q == 15:
|
||||
self.eb_small += amount
|
||||
item_data.update(token='energy_bar')
|
||||
elif q == 16:
|
||||
self.eb_small += amount
|
||||
item_data.update(token='energy_bar')
|
||||
kind = re.sub(r'_q\d\d*', "", item_data.get('token'))
|
||||
|
||||
if item_data.get('token', "") == "house_q100":
|
||||
@ -469,7 +469,7 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
self._req.debug = bool(debug)
|
||||
|
||||
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.ErepublikJSONEncoder, indent=4 if indent else None, sort_keys=True)
|
||||
|
||||
def get_countries_with_regions(self) -> Set[constants.Country]:
|
||||
r_json = self._post_main_travel_data().json()
|
||||
@ -483,13 +483,13 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
filename = f"{self.__class__.__name__}__dump.json"
|
||||
with open(filename, 'w') as f:
|
||||
utils.json.dump(dict(config=self.config, cookies=self._req.cookies.get_dict(),
|
||||
user_agent=self._req.headers.get("User-Agent")), f, cls=classes.MyJSONEncoder)
|
||||
user_agent=self._req.headers.get("User-Agent")), f, cls=classes.ErepublikJSONEncoder)
|
||||
self.write_log(f"Session saved to: '{filename}'")
|
||||
|
||||
@classmethod
|
||||
def load_from_dump(cls, dump_name: str):
|
||||
with open(dump_name) as f:
|
||||
data = utils.json.load(f)
|
||||
data = utils.json.load(f, object_hook=utils.json_decode_object_hook)
|
||||
player = cls(data['config']['email'], "")
|
||||
player._req.cookies.update(data['cookies'])
|
||||
player._req.headers.update({"User-Agent": data['user_agent']})
|
||||
@ -509,7 +509,7 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
self.write_log(f"Resumed as: {self.name}")
|
||||
if re.search('<div id="accountSecurity" class="it-hurts-when-ip">', resp.text):
|
||||
self.restricted_ip = True
|
||||
self.report_error("eRepublik has blacklisted IP. Limited functionality!", True)
|
||||
# self.report_error("eRepublik has blacklisted IP. Limited functionality!", True)
|
||||
|
||||
self.logged_in = True
|
||||
self.get_csrf_token()
|
||||
@ -524,18 +524,29 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
ret = self.__dict__.copy()
|
||||
ret.pop('stop_threads', None)
|
||||
ret.pop('_CitizenMilitary__last_war_update_data', None)
|
||||
ret.update(_properties=dict(
|
||||
now=self.now, should_do_levelup=self.should_do_levelup, is_levelup_reachable=self.is_levelup_reachable,
|
||||
max_time_till_full_ff=self.max_time_till_full_ff, is_levelup_close=self.is_levelup_close,
|
||||
time_till_full_ff=self.time_till_full_ff, time_till_week_change=self.time_till_week_change,
|
||||
next_wc_start=self.next_wc_start, next_reachable_energy=self.next_reachable_energy,
|
||||
health_info=self.health_info))
|
||||
|
||||
ret = super().as_dict
|
||||
ret.update(
|
||||
name=self.name, __str__=self.__str__(),
|
||||
ebs=dict(normal=self.eb_normal, double=self.eb_double, small=self.eb_small),
|
||||
promos=self.promos, inventory=self._inventory.as_dict, ot_points=self.ot_points, food=self.food,
|
||||
division=self.division, maveric=self.maverick, eday=self.eday, wheel_of_fortune=self.wheel_of_fortune,
|
||||
debug=self.debug,
|
||||
logged_in=self.logged_in, restricted_ip=self.restricted_ip, _properties=dict(
|
||||
now=self.now, should_do_levelup=self.should_do_levelup, is_levelup_reachable=self.is_levelup_reachable,
|
||||
max_time_till_full_ff=self.max_time_till_full_ff, is_levelup_close=self.is_levelup_close,
|
||||
time_till_full_ff=self.time_till_full_ff, time_till_week_change=self.time_till_week_change,
|
||||
next_wc_start=self.next_wc_start, next_reachable_energy=self.next_reachable_energy,
|
||||
health_info=self.health_info),
|
||||
_last_full_update=self._last_full_update, _last_inventory_update=self._last_inventory_update,
|
||||
config=self.config.as_dict, energy=self.energy.as_dict, details=self.details.as_dict,
|
||||
politics=self.politics.as_dict, my_companies=self.my_companies.as_dict, reporter=self.reporter.as_dict,
|
||||
telegram=self.telegram.as_dict, stop_threads=self.stop_threads.is_set(), response=self.r,
|
||||
)
|
||||
return ret
|
||||
|
||||
def set_locks(self):
|
||||
self.stop_threads.set()
|
||||
|
||||
@property
|
||||
def health_info(self):
|
||||
ret = f"{self.energy.recovered}/{self.energy.limit} + {self.energy.recoverable}, " \
|
||||
@ -692,6 +703,10 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
self.eb_double -= amount
|
||||
elif q == "12":
|
||||
self.eb_small -= amount
|
||||
elif q == "15":
|
||||
self.eb_small -= amount
|
||||
elif q == "16":
|
||||
self.eb_small -= amount
|
||||
next_recovery = r_json.get("food_remaining_reset").split(":")
|
||||
self.energy.set_reference_time(
|
||||
utils.good_timedelta(self.now, timedelta(seconds=int(next_recovery[1]) * 60 + int(next_recovery[2])))
|
||||
@ -718,7 +733,7 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
self.get_csrf_token()
|
||||
if re.search('<div id="accountSecurity" class="it-hurts-when-ip">', self.r.text):
|
||||
self.restricted_ip = True
|
||||
self.report_error("eRepublik has blacklisted IP. Limited functionality!", True)
|
||||
# self.report_error("eRepublik has blacklisted IP. Limited functionality!", True)
|
||||
|
||||
self.logged_in = True
|
||||
|
||||
@ -733,6 +748,9 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
if response.status_code >= 400:
|
||||
self.r = response
|
||||
if response.status_code >= 500:
|
||||
if self.restricted_ip:
|
||||
self._req.cookies.clear()
|
||||
return True
|
||||
self.write_log("eRepublik servers are having internal troubles. Sleeping for 5 minutes")
|
||||
self.sleep(5 * 60)
|
||||
else:
|
||||
@ -764,7 +782,7 @@ class BaseCitizen(access_points.CitizenAPI):
|
||||
:param msg: Message about the action
|
||||
:param kwargs: Extra information regarding action
|
||||
"""
|
||||
kwargs = utils.json.loads(utils.json.dumps(kwargs or {}, cls=classes.MyJSONEncoder))
|
||||
kwargs = utils.json.loads(utils.json.dumps(kwargs or {}, cls=classes.ErepublikJSONEncoder))
|
||||
action = action[:32]
|
||||
self.write_log(msg)
|
||||
if self.reporter.allowed:
|
||||
@ -975,7 +993,6 @@ class CitizenCompanies(BaseCitizen):
|
||||
def work_as_manager_in_holding(self, holding: classes.Holding) -> Optional[Dict[str, Any]]:
|
||||
return self._work_as_manager(holding)
|
||||
|
||||
@utils.wait_for_lock
|
||||
def _work_as_manager(self, wam_holding: classes.Holding) -> Optional[Dict[str, Any]]:
|
||||
if self.restricted_ip:
|
||||
return None
|
||||
@ -1045,7 +1062,7 @@ class CitizenCompanies(BaseCitizen):
|
||||
|
||||
|
||||
class CitizenEconomy(CitizenTravel):
|
||||
def update_money(self, page: int = 0, currency: int = 62) -> Dict[str, Any]:
|
||||
def update_money(self, page: int = 0, currency: int = 62):
|
||||
"""
|
||||
Gets monetary market offers to get exact amount of CC and Gold available
|
||||
"""
|
||||
@ -1055,7 +1072,6 @@ class CitizenEconomy(CitizenTravel):
|
||||
resp_data = resp.json()
|
||||
self.details.cc = float(resp_data.get("ecash").get("value"))
|
||||
self.details.gold = float(resp_data.get("gold").get("value"))
|
||||
return resp_data
|
||||
|
||||
def check_house_durability(self) -> Dict[int, datetime]:
|
||||
ret = {}
|
||||
@ -1077,7 +1093,7 @@ class CitizenEconomy(CitizenTravel):
|
||||
local_cheapest = sorted(offers, key=lambda o: o.price)[0]
|
||||
|
||||
global_cheapest = self.get_market_offers("House", q)[f"q{q}"]
|
||||
if global_cheapest.price + 200 < local_cheapest.price:
|
||||
if global_cheapest.price + 2000 < local_cheapest.price:
|
||||
if global_cheapest.price + 2000 < self.details.cc:
|
||||
if self.travel_to_country(global_cheapest.country):
|
||||
buy = self.buy_market_offer(global_cheapest, 1)
|
||||
@ -1207,7 +1223,7 @@ class CitizenEconomy(CitizenTravel):
|
||||
items = (self.inventory.final if final_kind else self.inventory.raw).get(constants.INDUSTRIES[industry],
|
||||
{_inv_qlt: {'amount': 0}})
|
||||
if items[_inv_qlt]['amount'] < amount:
|
||||
self.get_inventory(True)
|
||||
self.update_inventory()
|
||||
items = (self.inventory.final if final_kind else self.inventory.raw).get(constants.INDUSTRIES[industry],
|
||||
{_inv_qlt: {'amount': 0}})
|
||||
if items[_inv_qlt]['amount'] < amount:
|
||||
@ -1239,7 +1255,6 @@ class CitizenEconomy(CitizenTravel):
|
||||
self._report_action("BOUGHT_PRODUCTS", json_ret.get('message'), kwargs=json_ret)
|
||||
return json_ret
|
||||
|
||||
@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:
|
||||
amount = offer.amount
|
||||
@ -1366,7 +1381,7 @@ class CitizenEconomy(CitizenTravel):
|
||||
self.update_money()
|
||||
cur = "g" if currency == 62 else "cc"
|
||||
if success:
|
||||
self._report_action("DONATE_MONEY", f"Successfully donated {amount}{cur} to citizen with id {citizen_id}!")
|
||||
self.report_money_donation(citizen_id, amount, currency == 1)
|
||||
else:
|
||||
self._report_action("DONATE_MONEY", f"Unable to donate {amount}{cur}!")
|
||||
return success
|
||||
@ -1450,6 +1465,16 @@ class CitizenEconomy(CitizenTravel):
|
||||
f" treasury", kwargs=r.json())
|
||||
return False
|
||||
|
||||
def report_money_donation(self, citizen_id: int, amount: float, is_currency: bool = True):
|
||||
self.reporter.report_money_donation(citizen_id, amount, is_currency)
|
||||
if self.config.telegram:
|
||||
self.telegram.report_money_donation(citizen_id, amount, is_currency)
|
||||
|
||||
def report_item_donation(self, citizen_id: int, amount: float, quality: int, industry: str):
|
||||
self.reporter.report_item_donation(citizen_id, amount, quality, industry)
|
||||
if self.config.telegram:
|
||||
self.telegram.report_item_donation(citizen_id, amount, f"{industry} q{quality}")
|
||||
|
||||
|
||||
class CitizenLeaderBoard(BaseCitizen):
|
||||
def get_aircraft_damage_rankings(self, country: int, weeks: int = 0, mu: int = 0) -> Dict[str, any]:
|
||||
@ -1530,11 +1555,15 @@ class CitizenMedia(BaseCitizen):
|
||||
|
||||
class CitizenMilitary(CitizenTravel):
|
||||
all_battles: Dict[int, classes.Battle] = None
|
||||
countries: Dict[int, Dict[str, Union[str, List[int]]]] = None
|
||||
__last_war_update_data = None
|
||||
|
||||
active_fs: bool = False
|
||||
boosters: Dict[int, Dict[int, int]] = {100: {}, 50: {}}
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
d = super().as_dict
|
||||
d.update(active_fs=self.active_fs, all_battles=self.all_battles)
|
||||
return d
|
||||
|
||||
def update_war_info(self):
|
||||
if self.__last_war_update_data and self.__last_war_update_data.get('last_updated',
|
||||
@ -1545,17 +1574,6 @@ class CitizenMilitary(CitizenTravel):
|
||||
if r_json.get("countries"):
|
||||
if self.all_battles is None:
|
||||
self.all_battles = {}
|
||||
if self.countries is None:
|
||||
self.countries = {}
|
||||
countries = {}
|
||||
for c_id, c_data in r_json.get("countries").items():
|
||||
if int(c_id) not in countries:
|
||||
countries.update({
|
||||
int(c_id): {"name": c_data.get("name"), "allies": c_data.get("allies")}
|
||||
})
|
||||
else:
|
||||
countries[int(c_id)].update(allies=c_data.get("allies"))
|
||||
self.countries = countries
|
||||
self.__last_war_update_data = r_json
|
||||
if r_json.get("battles"):
|
||||
all_battles = {}
|
||||
@ -1775,7 +1793,8 @@ class CitizenMilitary(CitizenTravel):
|
||||
yield battle, battle_zone, side
|
||||
|
||||
def find_battle_and_fight(self):
|
||||
if self.should_fight()[0]:
|
||||
count = self.should_fight()[0]
|
||||
if count:
|
||||
self.write_log("Checking for battles to fight in...")
|
||||
for battle, division, side in self.find_battle_to_fight():
|
||||
|
||||
@ -1802,25 +1821,26 @@ class CitizenMilitary(CitizenTravel):
|
||||
|
||||
if self.change_division(battle, division):
|
||||
self.set_default_weapon(battle, division)
|
||||
self.fight(battle, division, side)
|
||||
self.fight(battle, division, side, count)
|
||||
self.travel_to_residence()
|
||||
break
|
||||
|
||||
@utils.wait_for_lock
|
||||
def fight(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide = None,
|
||||
count: int = None) -> Optional[int]:
|
||||
count: int = None, use_ebs: bool = False) -> Optional[int]:
|
||||
"""Fight in a battle.
|
||||
|
||||
Will auto activate booster and travel if allowed to do it.
|
||||
:param battle: Battle battle to fight in
|
||||
:type battle: Battle
|
||||
:param division: Division number to fight in available choices
|
||||
:type division: BattleDivision
|
||||
:param side: BattleSide or None. Battle side to fight in, If side not == invader id or not in invader deployed
|
||||
allies list, then defender's side is chosen
|
||||
:type side: BattleSide
|
||||
:param count: How many hits to do, if not specified self.should_fight() is called.
|
||||
:type count: int
|
||||
:param division: Division number to fight in available choices
|
||||
:type division: BattleDivision
|
||||
:param use_ebs: Should use energy bars if count > 0 and not enough food_fights
|
||||
:type use_ebs: bool
|
||||
:return: None if no errors while fighting, otherwise error count.
|
||||
:rtype: int
|
||||
"""
|
||||
@ -1850,15 +1870,13 @@ class CitizenMilitary(CitizenTravel):
|
||||
error_count += error
|
||||
else:
|
||||
self._eat('blue')
|
||||
if count > 0 and self.energy.recovered < 50 and use_ebs:
|
||||
self._eat('orange')
|
||||
if self.energy.recovered < 50 or error_count >= 10 or count <= 0:
|
||||
self.write_log(f"Hits: {total_hits:>4} | Damage: {total_damage}")
|
||||
ok_to_fight = False
|
||||
if total_damage:
|
||||
self.reporter.report_fighting(battle, not side.is_defender, division, total_damage, total_hits)
|
||||
# self.reporter.report_action('FIGHT', dict(battle_id=battle.id, side=side, dmg=total_damage,
|
||||
# air=battle.has_air, hits=total_hits,
|
||||
# round=battle.zone_id,
|
||||
# extra=dict(battle=battle, side=side)))
|
||||
self.report_fighting(battle, not side.is_defender, division, total_damage, total_hits)
|
||||
return error_count
|
||||
|
||||
def _shoot(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide):
|
||||
@ -1923,7 +1941,6 @@ class CitizenMilitary(CitizenTravel):
|
||||
|
||||
return hits, err, damage
|
||||
|
||||
@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.
|
||||
@ -2020,8 +2037,8 @@ class CitizenMilitary(CitizenTravel):
|
||||
|
||||
return utils.calculate_hit(0, rang, True, elite, ne, 0, 20 if weapon else 0)
|
||||
|
||||
def activate_damage_booster(self, ground: bool = True):
|
||||
kind = 'damageBoosters' if ground else 'aircraftDamageBoosters'
|
||||
def activate_damage_booster(self, ground: bool = True) -> int:
|
||||
kind = 'damage' if ground else 'aircraftDamage'
|
||||
if self.config.boosters and not self.get_active_damage_booster(ground):
|
||||
booster: Optional[types.InvFinalItem] = None
|
||||
for quality, data in sorted(self.inventory.boosters.get(kind, {}).items(), key=lambda x: x[0]):
|
||||
@ -2032,12 +2049,14 @@ class CitizenMilitary(CitizenTravel):
|
||||
break
|
||||
break
|
||||
if booster:
|
||||
kind = 'damage' if ground else 'air_damage'
|
||||
self._report_action("MILITARY_BOOSTER", f"Activated {booster['name']}")
|
||||
self._post_economy_activate_booster(booster['quality'], booster['durability'],
|
||||
'damage' if ground else 'air_damage')
|
||||
resp = self._post_economy_activate_booster(booster['quality'], booster['durability'], kind).json()
|
||||
self._update_inventory_data(resp)
|
||||
return self.get_active_damage_booster(ground)
|
||||
|
||||
def get_active_damage_booster(self, ground: bool = True) -> int:
|
||||
kind = 'damageBoosters' if ground else 'aircraftDamageBoosters'
|
||||
kind = 'damage' if ground else 'aircraftDamage'
|
||||
boosters = self.inventory.active.get(kind, {})
|
||||
quality = 0
|
||||
for q, boost in boosters.items():
|
||||
@ -2051,13 +2070,16 @@ class CitizenMilitary(CitizenTravel):
|
||||
def get_active_air_damage_booster(self) -> int:
|
||||
return self.get_active_damage_booster(False)
|
||||
|
||||
def activate_battle_effect(self, battle_id: int, kind: str) -> Response:
|
||||
def activate_battle_effect(self, battle_id: int, kind: str) -> bool:
|
||||
self._report_action('MILITARY_BOOSTER', f'Activated {kind} booster')
|
||||
return self._post_main_activate_battle_effect(battle_id, kind, self.details.citizen_id)
|
||||
resp = self._post_main_activate_battle_effect(battle_id, kind, self.details.citizen_id).json()
|
||||
return not resp.get('error')
|
||||
|
||||
def activate_pp_booster(self, pp_item: types.InvFinalItem) -> Response:
|
||||
def activate_pp_booster(self, pp_item: types.InvFinalItem) -> bool:
|
||||
self._report_action('MILITARY_BOOSTER', f'Activated {pp_item["name"]}')
|
||||
return self._post_economy_activate_booster(pp_item['quality'], pp_item['durability'], 'prestige_points')
|
||||
resp = self._post_economy_activate_booster(pp_item['quality'], pp_item['durability'], 'prestige_points').json()
|
||||
self._update_inventory_data(resp)
|
||||
return pp_item.get('kind') in self.inventory.active
|
||||
|
||||
def _rw_choose_side(self, battle: classes.Battle, side: classes.BattleSide) -> Response:
|
||||
return self._post_main_battlefield_travel(side.id, battle.id)
|
||||
@ -2218,6 +2240,11 @@ class CitizenMilitary(CitizenTravel):
|
||||
if division.wall['dom'] == 50 or division.wall['dom'] > 98:
|
||||
yield division, division.wall['for'] == battle.invader.country.id
|
||||
|
||||
def report_fighting(self, battle: classes.Battle, invader: bool, division: classes.BattleDivision, damage: float, hits: int):
|
||||
self.reporter.report_fighting(battle, invader, division, damage, hits)
|
||||
if self.config.telegram:
|
||||
self.telegram.report_fight(battle, invader, division, damage, hits)
|
||||
|
||||
|
||||
class CitizenPolitics(BaseCitizen):
|
||||
def get_country_parties(self, country: constants.Country = None) -> dict:
|
||||
@ -2371,6 +2398,12 @@ class CitizenTasks(CitizenEconomy):
|
||||
ot_points: int = 0
|
||||
next_ot_time: datetime = None
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
d = super().as_dict
|
||||
d.update(tg_contract=self.tg_contract, ot_points=self.ot_points, next_ot_time=self.next_ot_time)
|
||||
return d
|
||||
|
||||
def eat(self):
|
||||
""" Eat food """
|
||||
self._eat("blue")
|
||||
@ -2498,8 +2531,8 @@ class CitizenTasks(CitizenEconomy):
|
||||
self.ot_points = ot.get("points", 0)
|
||||
|
||||
|
||||
class Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard,
|
||||
CitizenMedia, CitizenPolitics, CitizenSocial, CitizenMilitary, CitizenTasks):
|
||||
class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard,
|
||||
CitizenMedia, CitizenPolitics, CitizenSocial, CitizenMilitary, CitizenTasks):
|
||||
def __init__(self, email: str = "", password: str = "", auto_login: bool = False):
|
||||
super().__init__(email, password)
|
||||
self._last_full_update = constants.min_datetime
|
||||
@ -2510,10 +2543,18 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard,
|
||||
@classmethod
|
||||
def load_from_dump(cls, dump_name: str = ""):
|
||||
filename = dump_name if dump_name else f"{cls.__name__}__dump.json"
|
||||
player: Citizen = super().load_from_dump(filename) # noqa
|
||||
player: _Citizen = super().load_from_dump(filename) # noqa
|
||||
player.login()
|
||||
return player
|
||||
|
||||
def _eat(self, colour: str = "blue") -> Response:
|
||||
resp = super()._eat(colour)
|
||||
if not any([resp.json().get("units_consumed").values()]):
|
||||
if colour == 'orange' and resp.json().get('food_remaining'):
|
||||
self.eat()
|
||||
return self._eat(colour)
|
||||
return resp
|
||||
|
||||
def config_setup(self, **kwargs):
|
||||
self.config.reset()
|
||||
for key, value in kwargs.items():
|
||||
@ -2668,16 +2709,19 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard,
|
||||
|
||||
def state_update_repeater(self):
|
||||
try:
|
||||
start_time = self.now.replace(second=0, microsecond=0)
|
||||
if start_time.minute <= 30:
|
||||
start_time = start_time.replace(minute=30)
|
||||
else:
|
||||
start_time = utils.good_timedelta(start_time.replace(minute=0), timedelta(hours=1))
|
||||
start_time = self.now.replace(minute=(self.now.minute // 10) * 10, second=0, microsecond=0)
|
||||
if not self.restricted_ip:
|
||||
if start_time.minute <= 30:
|
||||
start_time = start_time.replace(minute=30)
|
||||
else:
|
||||
start_time = utils.good_timedelta(start_time.replace(minute=0), timedelta(hours=1))
|
||||
while not self.stop_threads.is_set():
|
||||
start_time = utils.good_timedelta(start_time, timedelta(minutes=10 if self.restricted_ip else 30))
|
||||
self.update_citizen_info()
|
||||
start_time = utils.good_timedelta(start_time, timedelta(minutes=30))
|
||||
self.update_weekly_challenge()
|
||||
self.send_state_update()
|
||||
self.send_inventory_update()
|
||||
self.update_companies()
|
||||
self.send_my_companies_update()
|
||||
sleep_seconds = (start_time - self.now).total_seconds()
|
||||
self.stop_threads.wait(sleep_seconds if sleep_seconds > 0 else 0)
|
||||
@ -2869,3 +2913,161 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard,
|
||||
self.stop_threads.wait(90)
|
||||
except: # noqa
|
||||
self.report_error('Command central is broken')
|
||||
|
||||
|
||||
class Citizen(_Citizen):
|
||||
_concurrency_lock: Event
|
||||
_update_lock: Event
|
||||
_update_timeout: int = 10
|
||||
_concurrency_timeout: int = 600
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._concurrency_lock = Event()
|
||||
self._concurrency_lock.set()
|
||||
self._update_lock = Event()
|
||||
self._update_lock.set()
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def update_weekly_challenge(self):
|
||||
if not self._update_lock.wait(self._update_timeout):
|
||||
e = f'Update concurrency not freed in {self._update_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._update_lock.clear()
|
||||
super().update_weekly_challenge()
|
||||
finally:
|
||||
self._update_lock.set()
|
||||
|
||||
def update_companies(self):
|
||||
if not self._update_lock.wait(self._update_timeout):
|
||||
e = f'Update concurrency not freed in {self._update_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._update_lock.clear()
|
||||
super().update_companies()
|
||||
finally:
|
||||
self._update_lock.set()
|
||||
|
||||
def update_war_info(self):
|
||||
if not self._update_lock.wait(self._update_timeout):
|
||||
e = f'Update concurrency not freed in {self._update_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._update_lock.clear()
|
||||
super().update_war_info()
|
||||
finally:
|
||||
self._update_lock.set()
|
||||
|
||||
def update_job_info(self):
|
||||
if not self._update_lock.wait(self._update_timeout):
|
||||
e = f'Update concurrency not freed in {self._update_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._update_lock.clear()
|
||||
super().update_job_info()
|
||||
finally:
|
||||
self._update_lock.set()
|
||||
|
||||
def update_money(self, page: int = 0, currency: int = 62):
|
||||
if not self._update_lock.wait(self._update_timeout):
|
||||
e = f'Update concurrency not freed in {self._update_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._update_lock.clear()
|
||||
super().update_money(page, currency)
|
||||
finally:
|
||||
self._update_lock.set()
|
||||
|
||||
def update_inventory(self):
|
||||
if not self._update_lock.wait(self._update_timeout):
|
||||
e = f'Update concurrency not freed in {self._update_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._update_lock.clear()
|
||||
super().update_inventory()
|
||||
finally:
|
||||
self._update_lock.set()
|
||||
|
||||
def _work_as_manager(self, wam_holding: classes.Holding) -> Optional[Dict[str, Any]]:
|
||||
if not self._concurrency_lock.wait(self._concurrency_timeout):
|
||||
e = f'Concurrency not freed in {self._concurrency_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._concurrency_lock.clear()
|
||||
return super()._work_as_manager(wam_holding)
|
||||
finally:
|
||||
self._concurrency_lock.set()
|
||||
|
||||
def fight(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide = None,
|
||||
count: int = None, use_ebs: bool = False) -> Optional[int]:
|
||||
if not self._concurrency_lock.wait(self._concurrency_timeout):
|
||||
e = f'Concurrency not freed in {self._concurrency_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._concurrency_lock.clear()
|
||||
return super().fight(battle, division, side, count, use_ebs)
|
||||
finally:
|
||||
self._concurrency_lock.set()
|
||||
|
||||
def deploy_bomb(self, battle: classes.Battle, division: classes.BattleDivision, bomb_id: int, inv_side: bool,
|
||||
count: int = 1) -> Optional[int]:
|
||||
if not self._concurrency_lock.wait(self._concurrency_timeout):
|
||||
e = f'Concurrency not freed in {self._concurrency_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._concurrency_lock.clear()
|
||||
return super().deploy_bomb(battle, division, bomb_id, inv_side, count)
|
||||
finally:
|
||||
self._concurrency_lock.set()
|
||||
|
||||
def buy_market_offer(self, offer: OfferItem, amount: int = None) -> Optional[Dict[str, Any]]:
|
||||
if not self._concurrency_lock.wait(self._concurrency_timeout):
|
||||
e = f'Concurrency not freed in {self._concurrency_timeout}sec!'
|
||||
self.write_log(e)
|
||||
if self.debug:
|
||||
self.report_error(e)
|
||||
return None
|
||||
try:
|
||||
self._concurrency_lock.clear()
|
||||
return super().buy_market_offer(offer, amount)
|
||||
finally:
|
||||
self._concurrency_lock.set()
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
d = super().as_dict
|
||||
d.update(locks=dict(concurrency_lock=self._concurrency_lock.is_set(), update_lock=self._update_lock.is_set(),
|
||||
concurrency_timeout=self._concurrency_timeout, update_timeout=self._update_timeout))
|
||||
return d
|
||||
|
||||
def set_locks(self):
|
||||
super().set_locks()
|
||||
self._concurrency_lock.set()
|
||||
self._update_lock.set()
|
||||
|
@ -3,16 +3,15 @@ import hashlib
|
||||
import threading
|
||||
import weakref
|
||||
from decimal import Decimal
|
||||
from typing import Any, Dict, Generator, Iterable, List, NamedTuple, NoReturn, Tuple, Union
|
||||
from typing import Any, Dict, Generator, Iterable, List, NamedTuple, NoReturn, Union
|
||||
|
||||
from requests import Response, Session, post
|
||||
|
||||
from . import constants, types, utils
|
||||
|
||||
__all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException',
|
||||
'ErepublikNetworkException', 'EnergyToFight',
|
||||
'Holding', 'MyCompanies', 'MyJSONEncoder', 'OfferItem', 'Politics', 'Reporter', 'TelegramReporter',
|
||||
'Inventory']
|
||||
'ErepublikJSONEncoder', 'ErepublikNetworkException', 'EnergyToFight', 'Holding', 'Inventory', 'MyCompanies',
|
||||
'OfferItem', 'Politics', 'Reporter', 'TelegramReporter', ]
|
||||
|
||||
|
||||
class ErepublikException(Exception):
|
||||
@ -599,10 +598,10 @@ class Reporter:
|
||||
if self.__to_update:
|
||||
for unreported_data in self.__to_update:
|
||||
unreported_data.update(player_id=self.citizen_id, key=self.key)
|
||||
unreported_data = utils.json.loads(utils.json.dumps(unreported_data, cls=MyJSONEncoder))
|
||||
unreported_data = utils.json.loads(utils.json.dumps(unreported_data, cls=ErepublikJSONEncoder))
|
||||
self._req.post(f"{self.url}/bot/update", json=unreported_data)
|
||||
self.__to_update.clear()
|
||||
data = utils.json.loads(utils.json.dumps(data, cls=MyJSONEncoder))
|
||||
data = utils.json.loads(utils.json.dumps(data, cls=ErepublikJSONEncoder))
|
||||
r = self._req.post(f"{self.url}/bot/update", json=data)
|
||||
return r
|
||||
|
||||
@ -650,6 +649,16 @@ class Reporter:
|
||||
air=battle.has_air, hits=hits,
|
||||
round=battle.zone_id, extra=dict(battle=battle, side=side, division=division)))
|
||||
|
||||
def report_money_donation(self, citizen_id: int, amount: float, is_currency: bool = True):
|
||||
cur = 'cc' if is_currency else 'gold'
|
||||
self.report_action('DONATE_MONEY', dict(citizen_id=citizen_id, amount=amount, currency=cur),
|
||||
f"Successfully donated {amount}{cur} to citizen with id {citizen_id}!")
|
||||
|
||||
def report_item_donation(self, citizen_id: int, amount: float, quality: int, industry: str):
|
||||
self.report_action('DONATE_ITEMS',
|
||||
dict(citizen_id=citizen_id, amount=amount, quality=quality, industry=industry),
|
||||
f"Successfully donated {amount} x {industry} q{quality} to citizen with id {citizen_id}!")
|
||||
|
||||
def report_promo(self, kind: str, time_until: datetime.datetime):
|
||||
self._req.post(f"{self.url}/promos/add/", data=dict(kind=kind, time_untill=time_until))
|
||||
|
||||
@ -661,16 +670,19 @@ class Reporter:
|
||||
except: # noqa
|
||||
return []
|
||||
|
||||
def fetch_tasks(self) -> List[Union[str, Tuple[Any]]]:
|
||||
def fetch_tasks(self) -> List[Dict[str, Any]]:
|
||||
try:
|
||||
task_response = self._req.get(f'{self.url}/api/v1/command',
|
||||
params=dict(citizen=self.citizen_id, key=self.key))
|
||||
return task_response.json().get('task_collection')
|
||||
task_response = self._req.post(
|
||||
f'{self.url}/api/v1/command', data=dict(citizen=self.citizen_id, key=self.key)).json()
|
||||
if task_response.get('status'):
|
||||
return task_response.get('data')
|
||||
else:
|
||||
return []
|
||||
except: # noqa
|
||||
return []
|
||||
|
||||
|
||||
class MyJSONEncoder(utils.json.JSONEncoder):
|
||||
class ErepublikJSONEncoder(utils.json.JSONEncoder):
|
||||
def default(self, o):
|
||||
from erepublik.citizen import Citizen
|
||||
if isinstance(o, Decimal):
|
||||
@ -684,7 +696,7 @@ class MyJSONEncoder(utils.json.JSONEncoder):
|
||||
return dict(__type__='timedelta', days=o.days, seconds=o.seconds,
|
||||
microseconds=o.microseconds, total_seconds=o.total_seconds())
|
||||
elif isinstance(o, Response):
|
||||
return dict(headers=o.headers.__dict__, url=o.url, text=o.text)
|
||||
return dict(headers=dict(o.__dict__['headers']), url=o.url, text=o.text, status_code=o.status_code)
|
||||
elif hasattr(o, 'as_dict'):
|
||||
return o.as_dict
|
||||
elif isinstance(o, set):
|
||||
@ -974,7 +986,7 @@ class TelegramReporter:
|
||||
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))
|
||||
if self.__queue:
|
||||
self.send_message("\n\n––––––––––––––––––––––\n\n".join(self.__queue))
|
||||
self.send_message("Telegram initialized")
|
||||
|
||||
def send_message(self, message: str) -> bool:
|
||||
self.__queue.append(message)
|
||||
@ -1002,15 +1014,29 @@ class TelegramReporter:
|
||||
new_line = '\n' if multiple else ''
|
||||
self.send_message(f"New award: {new_line}*{msg}*")
|
||||
|
||||
def report_fight(self, battle: "Battle", invader: bool, division: "BattleDivision", damage: float, hits: int):
|
||||
side_txt = (battle.invader if invader else battle.defender).country.iso
|
||||
self.send_message(f"*Fight report*:\n{int(damage):,d} dmg ({hits} hits) in"
|
||||
f" [battle {battle.id} for {battle.region_name[:16]}]({battle.link}) in d{division.div} on "
|
||||
f"{side_txt} side")
|
||||
|
||||
def report_item_donation(self, citizen_id: int, amount: float, product: str):
|
||||
self.send_message(f"*Donation*: {amount} x {product} to citizen "
|
||||
f"[{citizen_id}](https://www.erepublik.com/en/citizen/profile/{citizen_id})")
|
||||
|
||||
def report_money_donation(self, citizen_id: int, amount: float, is_currency: bool = True):
|
||||
self.send_message(f"*Donation*: {amount}{'cc' if is_currency else 'gold'} to citizen "
|
||||
f"[{citizen_id}](https://www.erepublik.com/en/citizen/profile/{citizen_id})")
|
||||
|
||||
def __send_messages(self):
|
||||
while self._next_time > utils.now():
|
||||
if self.__thread_stopper.is_set():
|
||||
break
|
||||
self.__thread_stopper.wait(utils.get_sleep_seconds(self._next_time))
|
||||
|
||||
message = "\n\n––––––––––––––––––––––\n\n".join(self.__queue)
|
||||
message = "\n\n".join(self.__queue)
|
||||
if self.player_name:
|
||||
message = f"Player *{self.player_name}*\n" + message
|
||||
message = f"Player *{self.player_name}*\n\n" + message
|
||||
response = post(self.api_url, json=dict(chat_id=self.chat_id, text=message, parse_mode="Markdown"))
|
||||
self._last_time = utils.now()
|
||||
if response.json().get('ok'):
|
||||
|
@ -12,6 +12,7 @@ from decimal import Decimal
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
import pytz
|
||||
import requests
|
||||
|
||||
from . import __version__, constants
|
||||
@ -25,11 +26,8 @@ __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',
|
||||
'interactive_sleep', 'json', 'localize_dt', 'localize_timestamp', 'normalize_html_json', 'now',
|
||||
'process_error', 'process_warning', 'send_email', 'silent_sleep', 'slugify', 'write_file', 'write_request',
|
||||
'write_interactive_log', 'write_silent_log', 'get_final_hit_dmg', 'wait_for_lock']
|
||||
|
||||
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))
|
||||
'write_interactive_log', 'write_silent_log', 'get_final_hit_dmg', 'wait_for_lock',
|
||||
'json_decode_object_hook', 'json_load', 'json_loads']
|
||||
|
||||
VERSION: str = __version__
|
||||
|
||||
@ -234,8 +232,8 @@ def send_email(name: str, content: List[Any], player=None, local_vars: Dict[str,
|
||||
if isinstance(local_vars.get('citizen'), Citizen):
|
||||
local_vars['citizen'] = repr(local_vars['citizen'])
|
||||
|
||||
from erepublik.classes import MyJSONEncoder
|
||||
files.append(('file', ("local_vars.json", json.dumps(local_vars, cls=MyJSONEncoder),
|
||||
from erepublik.classes import ErepublikJSONEncoder
|
||||
files.append(('file', ("local_vars.json", json.dumps(local_vars, cls=ErepublikJSONEncoder),
|
||||
"application/json")))
|
||||
if isinstance(player, Citizen):
|
||||
files.append(('file', ("instance.json", player.to_json(indent=True), "application/json")))
|
||||
@ -380,12 +378,6 @@ def get_final_hit_dmg(base_dmg: Union[Decimal, float, str], rang: int,
|
||||
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)
|
||||
|
||||
@ -409,3 +401,36 @@ def wait_for_lock(function):
|
||||
return ret
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def json_decode_object_hook(
|
||||
o: Union[Dict[str, Any], List[Any], int, float, str]
|
||||
) -> Union[Dict[str, Any], List[Any], int, float, str, datetime.date, datetime.datetime, datetime.timedelta]:
|
||||
""" Convert classes.ErepublikJSONEncoder datetime, date and timedelta to their python objects
|
||||
|
||||
:param o:
|
||||
:return: Union[Dict[str, Any], List[Any], int, float, str, datetime.date, datetime.datetime, datetime.timedelta]
|
||||
"""
|
||||
if o.get('__type__'):
|
||||
_type = o.get('__type__')
|
||||
if _type == 'datetime':
|
||||
dt = datetime.datetime.strptime(f"{o['date']} {o['time']}", "%Y-%m-%d %H:%M:%S")
|
||||
if o.get('tzinfo'):
|
||||
dt = pytz.timezone(o['tzinfo']).localize(dt)
|
||||
return dt
|
||||
elif _type == 'date':
|
||||
dt = datetime.datetime.strptime(f"{o['date']}", "%Y-%m-%d")
|
||||
return dt.date()
|
||||
elif _type == 'timedelta':
|
||||
return datetime.timedelta(seconds=o['total_seconds'])
|
||||
return o
|
||||
|
||||
|
||||
def json_load(f, **kwargs):
|
||||
kwargs.update(object_hook=json_decode_object_hook)
|
||||
return json.load(f, **kwargs)
|
||||
|
||||
|
||||
def json_loads(s: str, **kwargs):
|
||||
kwargs.update(object_hook=json_decode_object_hook)
|
||||
return json.loads(s, **kwargs)
|
||||
|
@ -1,19 +1,19 @@
|
||||
bump2version==1.0.1
|
||||
coverage==5.3.1
|
||||
edx-sphinx-theme==1.6.0
|
||||
edx-sphinx-theme==1.6.1
|
||||
flake8==3.8.4
|
||||
ipython>=7.19.0
|
||||
isort==5.7.0
|
||||
pip==20.3.3
|
||||
pre-commit==2.9.3
|
||||
pur==5.3.0
|
||||
PyInstaller==4.1
|
||||
PyInstaller==4.2
|
||||
PySocks==1.7.1
|
||||
pytest==6.2.1
|
||||
pytz>=2020.5
|
||||
requests>=2.25.1
|
||||
responses==0.12.1
|
||||
setuptools==51.1.1
|
||||
Sphinx==3.4.2
|
||||
setuptools==51.3.3
|
||||
Sphinx==3.4.3
|
||||
twine==3.3.0
|
||||
wheel==0.36.2
|
||||
|
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 0.23.4.4
|
||||
current_version = 0.23.4.13
|
||||
commit = True
|
||||
tag = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
|
||||
|
Reference in New Issue
Block a user