Code cleanup

This commit is contained in:
Eriks K 2020-07-09 14:42:40 +03:00
parent 81bd09e13e
commit ffbbd25e54
6 changed files with 407 additions and 380 deletions

View File

@ -5,9 +5,9 @@
__author__ = """Eriks Karls""" __author__ = """Eriks Karls"""
__email__ = 'eriks@72.lv' __email__ = 'eriks@72.lv'
__version__ = '0.20.3.5' __version__ = '0.20.3.5'
__commit_id__ = "fc4295d" __commit_id__ = "81bd09e"
from erepublik import classes, utils from erepublik import classes, utils, constants
from erepublik.citizen import Citizen from erepublik.citizen import Citizen
__all__ = ["classes", "utils", "Citizen"] __all__ = ["classes", "utils", "Citizen", ]

View File

@ -5,7 +5,7 @@ from typing import Any, Dict, List, Mapping, Union
from requests import Response, Session from requests import Response, Session
from erepublik import utils from . import constants, utils
__all__ = ['SlowRequests', 'CitizenAPI'] __all__ = ['SlowRequests', 'CitizenAPI']
@ -45,7 +45,7 @@ class SlowRequests(Session):
}) })
@property @property
def __dict__(self): def as_dict(self):
return dict(last_time=self.last_time, timeout=self.timeout, user_agent=self.headers['User-Agent'], 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) request_log_name=self.request_log_name, debug=self.debug)
@ -461,13 +461,13 @@ class ErepublikPresidentAPI(CitizenBaseAPI):
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,
countryNameConfirm=utils.COUNTRY_LINK[attack_country_id]) countryNameConfirm=constants.COUNTRIES[attack_country_id].link)
return self.post("{}/{}/new-war".format(self.url, utils.COUNTRY_LINK[self_country_id]), data=data) return self.post("{}/{}/new-war".format(self.url, constants.COUNTRIES[self_country_id].link), data=data)
def _post_new_donation(self, country_id: int, amount: int, org_name: str, debate: str = "") -> Response: def _post_new_donation(self, country_id: int, amount: int, org_name: str, debate: str = "") -> Response:
data = dict(requirments=1, _token=self.token, debate=debate, currency=1, value=amount, commit='Propose', data = dict(requirments=1, _token=self.token, debate=debate, currency=1, value=amount, commit='Propose',
type_name=org_name) type_name=org_name)
return self.post("{}/{}/new-donation".format(self.url, utils.COUNTRY_LINK[country_id]), data=data) return self.post("{}/{}/new-donation".format(self.url, constants.COUNTRIES[country_id].link), data=data)
class ErepublikProfileAPI(CitizenBaseAPI): class ErepublikProfileAPI(CitizenBaseAPI):

View File

@ -10,16 +10,11 @@ from typing import Any, Callable, Dict, List, NoReturn, Optional, Set, Tuple, Un
from requests import HTTPError, RequestException, Response from requests import HTTPError, RequestException, Response
from erepublik import utils from . import utils, classes, access_points, constants
from erepublik.access_points import CitizenAPI
from erepublik.classes import COUNTRIES, Battle, BattleDivision, Config, Country, Details, Energy, \
ErepublikException, MyCompanies, MyJSONEncoder, OfferItem, Politics, Reporter, TelegramBot, Holding, BattleSide, \
Company
from erepublik.utils import erep_tz
class BaseCitizen(CitizenAPI): class BaseCitizen(access_points.CitizenAPI):
__last_full_update: datetime = utils.now().min _last_full_update: datetime = utils.now().min
promos: Dict[str, datetime] = None promos: Dict[str, datetime] = None
inventory: Dict[str, int] = {'used': 0, 'total': 0} inventory: Dict[str, int] = {'used': 0, 'total': 0}
@ -34,13 +29,13 @@ class BaseCitizen(CitizenAPI):
eday = 0 eday = 0
config: Config = None config: classes.Config = None
energy: Energy = None energy: classes.Energy = None
details: Details = None details: classes.Details = None
politics: Politics = None politics: classes.Politics = None
reporter: Reporter = None reporter: classes.Reporter = None
stop_threads: Event = None stop_threads: Event = None
telegram: TelegramBot = None telegram: classes.TelegramBot = None
r: Response = None r: Response = None
name: str = "Not logged in!" name: str = "Not logged in!"
@ -51,14 +46,14 @@ class BaseCitizen(CitizenAPI):
def __init__(self, email: str = "", password: str = ""): def __init__(self, email: str = "", password: str = ""):
super().__init__() super().__init__()
self.commit_id = utils.COMMIT_ID self.commit_id = utils.COMMIT_ID
self.config = Config() self.config = classes.Config()
self.energy = Energy() self.energy = classes.Energy()
self.details = Details() self.details = classes.Details()
self.politics = Politics() self.politics = classes.Politics()
self.my_companies = MyCompanies(self) self.my_companies = classes.MyCompanies(self)
self.reporter = Reporter(self) self.reporter = classes.Reporter(self)
self.stop_threads = Event() self.stop_threads = Event()
self.telegram = TelegramBot(stop_event=self.stop_threads) self.telegram = classes.TelegramBot(stop_event=self.stop_threads)
self.config.email = email self.config.email = email
self.config.password = password self.config.password = password
@ -85,7 +80,7 @@ class BaseCitizen(CitizenAPI):
self.token = re_login_token.group(1) self.token = re_login_token.group(1)
self._login() self._login()
else: else:
raise ErepublikException("Something went wrong! Can't find token in page! Exiting!") raise classes.ErepublikException("Something went wrong! Can't find token in page! Exiting!")
try: try:
self.update_citizen_info(resp.text) self.update_citizen_info(resp.text)
except (AttributeError, utils.json.JSONDecodeError, ValueError, KeyError): except (AttributeError, utils.json.JSONDecodeError, ValueError, KeyError):
@ -206,11 +201,12 @@ class BaseCitizen(CitizenAPI):
self.energy.recoverable = citizen.get("energyFromFoodRemaining", 0) self.energy.recoverable = citizen.get("energyFromFoodRemaining", 0)
self.details.current_region = citizen.get("regionLocationId", 0) self.details.current_region = citizen.get("regionLocationId", 0)
self.details.current_country = COUNTRIES.get(citizen.get("countryLocationId", 0)) # country where citizen is located self.details.current_country = constants.COUNTRIES.get(
citizen.get("countryLocationId", 0)) # country where citizen is located
self.details.residence_region = citizen.get("residence", {}).get("regionId", 0) self.details.residence_region = citizen.get("residence", {}).get("regionId", 0)
self.details.residence_country = COUNTRIES.get(citizen.get("residence", {}).get("countryId", 0)) self.details.residence_country = constants.COUNTRIES.get(citizen.get("residence", {}).get("countryId", 0))
self.details.citizen_id = citizen.get("citizenId", 0) self.details.citizen_id = citizen.get("citizenId", 0)
self.details.citizenship = COUNTRIES.get(int(citizen.get("country", 0))) self.details.citizenship = constants.COUNTRIES.get(int(citizen.get("country", 0)))
self.details.xp = citizen.get("currentExperiencePoints", 0) self.details.xp = citizen.get("currentExperiencePoints", 0)
self.details.daily_task_done = citizen.get("dailyTasksDone", False) self.details.daily_task_done = citizen.get("dailyTasksDone", False)
self.details.daily_task_reward = citizen.get("hasReward", False) self.details.daily_task_reward = citizen.get("hasReward", False)
@ -359,7 +355,7 @@ class BaseCitizen(CitizenAPI):
offers = {} offers = {}
for offer in self._get_economy_my_market_offers().json(): for offer in self._get_economy_my_market_offers().json():
kind = self.get_industry_name(offer['industryId']) kind = constants.INDUSTRIES[offer['industryId']]
data = dict(quality=offer.get('quality', 0), amount=offer.get('amount', 0), icon=offer.get('icon'), data = dict(quality=offer.get('quality', 0), amount=offer.get('amount', 0), icon=offer.get('icon'),
kind=kind, name=kind) kind=kind, name=kind)
data = {data['quality']: data} data = {data['quality']: data}
@ -373,7 +369,7 @@ class BaseCitizen(CitizenAPI):
"total": j.get("inventoryStatus").get("totalStorage")}) "total": j.get("inventoryStatus").get("totalStorage")})
inventory = dict(items=dict(active=active_items, final=final_items, inventory = dict(items=dict(active=active_items, final=final_items,
raw=raw_materials, offers=offers), status=self.inventory) raw=raw_materials, offers=offers), status=self.inventory)
self.food["total"] = sum([self.food[q] * utils.FOOD_ENERGY[q] for q in utils.FOOD_ENERGY]) self.food["total"] = sum([self.food[q] * constants.FOOD_ENERGY[q] for q in constants.FOOD_ENERGY])
return inventory return inventory
def write_log(self, *args, **kwargs): def write_log(self, *args, **kwargs):
@ -397,34 +393,14 @@ class BaseCitizen(CitizenAPI):
sleep(seconds) sleep(seconds)
def to_json(self, indent: bool = False) -> str: def to_json(self, indent: bool = False) -> str:
return utils.json.dumps(self.__dict__, cls=MyJSONEncoder, indent=4 if indent else None, sort_keys=True) return utils.json.dumps(self, cls=classes.MyJSONEncoder, indent=4 if indent else None, sort_keys=True)
def get_industry_id(self, industry_name: str) -> int: def get_countries_with_regions(self) -> Set[constants.Country]:
"""Returns industry id
:type industry_name: str
:return: int
"""
return self.available_industries.get(industry_name, 0)
def get_industry_name(self, industry_id: int) -> str:
"""Returns industry name from industry ID
:type industry_id: int
:return: industry name
:rtype: str
"""
for industry_name, ind_id in self.available_industries.items():
if ind_id == industry_id:
return industry_name
return ""
def get_countries_with_regions(self) -> Set[Country]:
r_json = self._post_main_travel_data().json() r_json = self._post_main_travel_data().json()
return_set = {*[]} return_set = {*[]}
for country_data in r_json['countries'].values(): for country_data in r_json['countries'].values():
if country_data['currentRegions']: if country_data['currentRegions']:
return_set.add(COUNTRIES[country_data['id']]) return_set.add(constants.COUNTRIES[country_data['id']])
return return_set return return_set
def __str__(self) -> str: def __str__(self) -> str:
@ -434,10 +410,16 @@ class BaseCitizen(CitizenAPI):
return self.__str__() return self.__str__()
@property @property
def __dict__(self): def as_dict(self):
ret = super().__dict__.copy() ret = self.__dict__.copy()
ret.pop('stop_threads', None) ret.pop('stop_threads', None)
ret.pop('_CitizenMilitary__last_war_update_data', 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))
return ret return ret
@ -510,27 +492,6 @@ class BaseCitizen(CitizenAPI):
can_do_max_amount_of_dmg = self.energy.recoverable + 2 * self.energy.interval >= self.energy.limit can_do_max_amount_of_dmg = self.energy.recoverable + 2 * self.energy.interval >= self.energy.limit
return can_reach_next_level and can_do_max_amount_of_dmg return can_reach_next_level and can_do_max_amount_of_dmg
@property
def available_industries(self) -> Dict[str, int]:
"""
Returns currently available industries as dict(name: id)
:return: Dict[str, int]
"""
return {"food": 1, "weapon": 2, "house": 4, "aircraft": 23,
"foodRaw": 7, "weaponRaw": 12, "houseRaw": 17, "airplaneRaw": 24}
@property
def factories(self) -> Dict[int, str]:
"""Returns factory industries as dict(id: name)
:return: Factory id:name dict
:rtype: Dict[int, str]
"""
return {1: "Food", 2: "Weapons", 4: "House", 23: "Aircraft",
7: "FRM q1", 8: "FRM q2", 9: "FRM q3", 10: "FRM q4", 11: "FRM q5",
12: "WRM q1", 13: "WRM q2", 14: "WRM q3", 15: "WRM q4", 16: "WRM q5",
18: "HRM q1", 19: "HRM q2", 20: "HRM q3", 21: "HRM q4", 22: "HRM q5",
24: "ARM q1", 25: "ARM q2", 26: "ARM q3", 27: "ARM q4", 28: "ARM q5", }
@property @property
def now(self) -> datetime: def now(self) -> datetime:
""" """
@ -591,7 +552,7 @@ class BaseCitizen(CitizenAPI):
self.telegram.report_medal(f"Level *{level}*") self.telegram.report_medal(f"Level *{level}*")
self.reporter.report_action("LEVEL_UP", value=level) self.reporter.report_action("LEVEL_UP", value=level)
def _travel(self, country: Country, region_id: int = 0) -> Response: def _travel(self, country: constants.Country, region_id: int = 0) -> Response:
data = { data = {
"toCountryId": country.id, "toCountryId": country.id,
"inRegionId": region_id, "inRegionId": region_id,
@ -662,7 +623,7 @@ class BaseCitizen(CitizenAPI):
self.write_log("eRepublik servers are having internal troubles. Sleeping for 5 minutes") self.write_log("eRepublik servers are having internal troubles. Sleeping for 5 minutes")
self.sleep(5 * 60) self.sleep(5 * 60)
else: else:
raise ErepublikException(f"HTTP {response.status_code} error!") raise classes.ErepublikException(f"HTTP {response.status_code} error!")
return bool(re.search(r'body id="error"|Internal Server Error|' return bool(re.search(r'body id="error"|Internal Server Error|'
r'CSRF attack detected|meta http-equiv="refresh"|not_authenticated', response.text)) r'CSRF attack detected|meta http-equiv="refresh"|not_authenticated', response.text))
@ -676,7 +637,7 @@ class BaseCitizen(CitizenAPI):
:param msg: Message about the action :param msg: Message about the action
:param kwargs: Extra information regarding action :param kwargs: Extra information regarding action
""" """
kwargs = utils.json.loads(utils.json.dumps(kwargs or {}, cls=MyJSONEncoder)) kwargs = utils.json.loads(utils.json.dumps(kwargs or {}, cls=classes.MyJSONEncoder))
action = action[:32] action = action[:32]
self.write_log(msg) self.write_log(msg)
if self.reporter.allowed: if self.reporter.allowed:
@ -730,11 +691,11 @@ class CitizenAnniversary(BaseCitizen):
class CitizenTravel(BaseCitizen): class CitizenTravel(BaseCitizen):
def _update_citizen_location(self, country: Country, region_id: int): def _update_citizen_location(self, country: constants.Country, region_id: int):
self.details.current_region = region_id self.details.current_region = region_id
self.details.current_country = country self.details.current_country = country
def get_country_travel_region(self, country: Country) -> int: def get_country_travel_region(self, country: constants.Country) -> int:
regions = self.get_travel_regions(country=country) regions = self.get_travel_regions(country=country)
regs = [] regs = []
if regions: if regions:
@ -763,7 +724,7 @@ class CitizenTravel(BaseCitizen):
if data.get('alreadyInRegion'): if data.get('alreadyInRegion'):
return True return True
else: else:
country = COUNTRIES[data.get('preselectCountryId')] country = constants.COUNTRIES[data.get('preselectCountryId')]
r_json = self._travel(country, region_id).json() r_json = self._travel(country, region_id).json()
if r_json.get('message', '') == 'success': if r_json.get('message', '') == 'success':
self._update_citizen_location(country, region_id) self._update_citizen_location(country, region_id)
@ -771,7 +732,7 @@ class CitizenTravel(BaseCitizen):
return True return True
return False return False
def travel_to_country(self, country: Country) -> bool: def travel_to_country(self, country: constants.Country) -> bool:
data = self._post_main_travel_data(countryId=country.id, check="getCountryRegions").json() data = self._post_main_travel_data(countryId=country.id, check="getCountryRegions").json()
regs = [] regs = []
@ -788,12 +749,12 @@ class CitizenTravel(BaseCitizen):
return True return True
return False return False
def travel_to_holding(self, holding: Holding) -> bool: def travel_to_holding(self, holding: classes.Holding) -> bool:
data = self._post_main_travel_data(holdingId=holding.id).json() data = self._post_main_travel_data(holdingId=holding.id).json()
if data.get('alreadyInRegion'): if data.get('alreadyInRegion'):
return True return True
else: else:
country = COUNTRIES[data.get('preselectCountryId')] country = constants.COUNTRIES[data.get('preselectCountryId')]
region_id = data.get('preselectRegionId') region_id = data.get('preselectRegionId')
r_json = self._travel(country, region_id).json() r_json = self._travel(country, region_id).json()
if r_json.get('message', '') == 'success': if r_json.get('message', '') == 'success':
@ -802,7 +763,7 @@ class CitizenTravel(BaseCitizen):
return True return True
return False return False
def get_travel_regions(self, holding: Holding = None, battle: Battle = None, country: Country = None def get_travel_regions(self, holding: classes.Holding = None, battle: classes.Battle = None, country: constants.Country = None
) -> Union[List[Any], Dict[str, Dict[str, Any]]]: ) -> Union[List[Any], Dict[str, Dict[str, Any]]]:
return self._post_main_travel_data( return self._post_main_travel_data(
holdingId=holding.id if holding else 0, holdingId=holding.id if holding else 0,
@ -810,7 +771,7 @@ class CitizenTravel(BaseCitizen):
countryId=country.id if country else 0 countryId=country.id if country else 0
).json().get('regions', []) ).json().get('regions', [])
def get_travel_countries(self) -> Set[Country]: def get_travel_countries(self) -> Set[constants.Country]:
warnings.simplefilter('always') warnings.simplefilter('always')
warnings.warn('CitizenTravel.get_travel_countries() are being deprecated, ' warnings.warn('CitizenTravel.get_travel_countries() are being deprecated, '
'please use BaseCitizen.get_countries_with_regions()', DeprecationWarning) 'please use BaseCitizen.get_countries_with_regions()', DeprecationWarning)
@ -833,10 +794,10 @@ class CitizenCompanies(BaseCitizen):
return ret return ret
def work_as_manager_in_holding(self, holding: 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)
def _work_as_manager(self, wam_holding: Holding = None) -> Optional[Dict[str, Any]]: def _work_as_manager(self, wam_holding: classes.Holding = None) -> Optional[Dict[str, Any]]:
if self.restricted_ip: if self.restricted_ip:
return None return None
self.update_companies() self.update_companies()
@ -879,7 +840,7 @@ class CitizenCompanies(BaseCitizen):
self.my_companies.prepare_holdings(utils.json.loads(have_holdings.group(1))) self.my_companies.prepare_holdings(utils.json.loads(have_holdings.group(1)))
self.my_companies.prepare_companies(utils.json.loads(have_companies.group(1))) self.my_companies.prepare_companies(utils.json.loads(have_companies.group(1)))
def assign_company_to_holding(self, company: Company, holding: Holding) -> Response: def assign_company_to_holding(self, company: classes.Company, holding: classes.Holding) -> Response:
""" """
Assigns factory to new holding Assigns factory to new holding
""" """
@ -1019,7 +980,7 @@ class CitizenEconomy(CitizenTravel):
return ret return ret
def post_market_offer(self, industry: int, quality: int, amount: int, price: float) -> Response: def post_market_offer(self, industry: int, quality: int, amount: int, price: float) -> Response:
if industry not in self.available_industries.values(): 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}")
data = { data = {
@ -1032,7 +993,7 @@ class CitizenEconomy(CitizenTravel):
} }
ret = self._post_economy_marketplace_actions(**data) ret = self._post_economy_marketplace_actions(**data)
message = (f"Posted market offer for {amount}q{quality} " message = (f"Posted market offer for {amount}q{quality} "
f"{self.get_industry_name(industry)} for price {price}cc") f"{constants.INDUSTRIES[industry]} for price {price}cc")
self._report_action("ECONOMY_SELL_PRODUCTS", message, kwargs=ret.json()) self._report_action("ECONOMY_SELL_PRODUCTS", message, kwargs=ret.json())
return ret return ret
@ -1048,43 +1009,46 @@ class CitizenEconomy(CitizenTravel):
self._report_action("BOUGHT_PRODUCTS", "", kwargs=json_ret) self._report_action("BOUGHT_PRODUCTS", "", kwargs=json_ret)
return json_ret return json_ret
def get_market_offers(self, product_name: str, quality: int = None, country: Country = None) -> Dict[str, OfferItem]: def get_market_offers(
self, product_name: str, quality: int = None, country: constants.Country = None
) -> Dict[str, classes.OfferItem]:
raw_short_names = dict(frm="foodRaw", wrm="weaponRaw", hrm="houseRaw", arm="airplaneRaw") raw_short_names = dict(frm="foodRaw", wrm="weaponRaw", hrm="houseRaw", arm="airplaneRaw")
q1_industries = ["aircraft"] + list(raw_short_names.values()) q1_industries = ["aircraft"] + list(raw_short_names.values())
if product_name not in self.available_industries and product_name not in raw_short_names: if product_name in raw_short_names:
self.write_log(f"Industry '{product_name}' not implemented")
raise ErepublikException(f"Industry '{product_name}' not implemented")
elif product_name in raw_short_names:
quality = 1 quality = 1
product_name = raw_short_names[product_name] product_name = raw_short_names[product_name]
elif not constants.INDUSTRIES[product_name]:
self.write_log(f"Industry '{product_name}' not implemented")
raise classes.ErepublikException(f"Industry '{product_name}' not implemented")
offers: Dict[str, OfferItem] = {} offers: Dict[str, classes.OfferItem] = {}
max_quality = 0 max_quality = 0
if quality: if quality:
offers[f"q{quality}"] = OfferItem() offers[f"q{quality}"] = classes.OfferItem()
else: else:
max_quality = 1 if product_name in q1_industries else 5 if product_name == 'house' else 7 max_quality = 1 if product_name in q1_industries else 5 if product_name == 'house' else 7
for q in range(max_quality): for q in range(max_quality):
offers[f"q{q + 1}"] = OfferItem() offers[f"q{q + 1}"] = classes.OfferItem()
if country: if country:
countries: Set[Country] = {country} countries: Set[constants.Country] = {country}
else: else:
countries: Set[Country] = self.get_countries_with_regions() countries: Set[constants.Country] = self.get_countries_with_regions()
start_dt = self.now start_dt = self.now
iterable = [countries, [quality] if quality else range(1, max_quality + 1)] iterable = [countries, [quality] if quality else range(1, max_quality + 1)]
for country, q in product(*iterable): for country, q in product(*iterable):
r = self._post_economy_marketplace(country, self.available_industries[product_name], q).json() r = self._post_economy_marketplace(country, constants.INDUSTRIES[product_name], q).json()
obj = offers[f"q{q}"] obj = offers[f"q{q}"]
if not r.get("error", False): if not r.get("error", False):
for offer in r["offers"]: for offer in r["offers"]:
if (obj.price > float(offer["priceWithTaxes"]) or ( if (obj.price > float(offer["priceWithTaxes"]) or (
obj.price == float(offer["priceWithTaxes"]) and obj.amount < int(offer["amount"]) obj.price == float(offer["priceWithTaxes"]) and obj.amount < int(offer["amount"])
)): )):
offers[f"q{q}"] = obj = OfferItem( offers[f"q{q}"] = obj = classes.OfferItem(
float(offer["priceWithTaxes"]), COUNTRIES[int(offer["country_id"])], int(offer["amount"]), float(offer["priceWithTaxes"]),
constants.COUNTRIES[int(offer["country_id"])], int(offer["amount"]),
int(offer["id"]), int(offer["citizen_id"]) int(offer["id"]), int(offer["citizen_id"])
) )
self.write_log(f"Scraped market in {self.now - start_dt}!") self.write_log(f"Scraped market in {self.now - start_dt}!")
@ -1095,16 +1059,17 @@ class CitizenEconomy(CitizenTravel):
hp_needed = energy_amount if energy_amount else 48 * self.energy.interval * 10 - self.food["total"] hp_needed = energy_amount if energy_amount else 48 * self.energy.interval * 10 - self.food["total"]
local_offers = self.get_market_offers("food", country=self.details.current_country) local_offers = self.get_market_offers("food", country=self.details.current_country)
cheapest_q, cheapest = sorted(local_offers.items(), key=lambda v: v[1].price / utils.FOOD_ENERGY[v[0]])[0] cheapest_q, cheapest = sorted(local_offers.items(), key=lambda v: v[1].price / constants.FOOD_ENERGY[v[0]])[0]
if cheapest.amount * utils.FOOD_ENERGY[cheapest_q] < hp_needed: if cheapest.amount * constants.FOOD_ENERGY[cheapest_q] < hp_needed:
amount = cheapest.amount amount = cheapest.amount
else: else:
amount = hp_needed // utils.FOOD_ENERGY[cheapest_q] amount = hp_needed // constants.FOOD_ENERGY[cheapest_q]
if amount * cheapest.price < self.details.cc: if amount * cheapest.price < self.details.cc:
data = dict(offer=cheapest.offer_id, amount=amount, price=cheapest.price, data = dict(offer=cheapest.offer_id, amount=amount, price=cheapest.price,
cost=amount * cheapest.price, quality=cheapest_q, energy=amount * utils.FOOD_ENERGY[cheapest_q]) cost=amount * cheapest.price, quality=cheapest_q,
energy=amount * constants.FOOD_ENERGY[cheapest_q])
self._report_action("BUY_FOOD", "", kwargs=data) self._report_action("BUY_FOOD", "", kwargs=data)
self.buy_from_market(cheapest.offer_id, amount) self.buy_from_market(cheapest.offer_id, amount)
self.update_inventory() self.update_inventory()
@ -1166,11 +1131,11 @@ class CitizenEconomy(CitizenTravel):
def donate_items(self, citizen_id: int = 1620414, amount: int = 0, industry_id: int = 1, quality: int = 1) -> int: def donate_items(self, citizen_id: int = 1620414, amount: int = 0, industry_id: int = 1, quality: int = 1) -> int:
if amount < 1: if amount < 1:
return 0 return 0
ind = {v: k for k, v in self.available_industries.items()} industry = constants.INDUSTRIES[industry_id]
self.write_log(f"Donate: {amount:4d}q{quality} {ind[industry_id]} to {citizen_id}") self.write_log(f"Donate: {amount:4d}q{quality} {industry} to {citizen_id}")
response = self._post_economy_donate_items_action(citizen_id, amount, industry_id, quality) response = self._post_economy_donate_items_action(citizen_id, amount, industry_id, quality)
if re.search(rf"Successfully transferred {amount} item\(s\) to", response.text): if re.search(rf"Successfully transferred {amount} item\(s\) to", response.text):
msg = (f"Successfully donated {amount}q{quality} {self.get_industry_name(industry_id)} " msg = (f"Successfully donated {amount}q{quality} {industry} "
f"to citizen with id {citizen_id}!") f"to citizen with id {citizen_id}!")
self._report_action("DONATE_ITEMS", msg) self._report_action("DONATE_ITEMS", msg)
return amount return amount
@ -1182,19 +1147,19 @@ class CitizenEconomy(CitizenTravel):
if re.search(r'You do not have enough items in your inventory to make this donation', response.text): if re.search(r'You do not have enough items in your inventory to make this donation', response.text):
self._report_action("DONATE_ITEMS", self._report_action("DONATE_ITEMS",
f"Unable to donate {amount}q{quality} " f"Unable to donate {amount}q{quality} "
f"{self.get_industry_name(industry_id)}, not enough left!") f"{industry}, not enough left!")
return 0 return 0
available = re.search( available = re.search(
r'Cannot transfer the items because the user has only (\d+) free slots in (his|her) storage.', r'Cannot transfer the items because the user has only (\d+) free slots in (his|her) storage.',
response.text response.text
).group(1) ).group(1)
self._report_action('DONATE_ITEMS', self._report_action('DONATE_ITEMS',
f'Unable to donate {amount}q{quality}{self.get_industry_name(industry_id)}' f'Unable to donate {amount}q{quality}{industry}'
f', receiver has only {available} storage left!') f', receiver has only {available} storage left!')
self.sleep(5) self.sleep(5)
return self.donate_items(citizen_id, int(available), industry_id, quality) return self.donate_items(citizen_id, int(available), industry_id, quality)
def contribute_cc_to_country(self, amount, country: Country) -> bool: def contribute_cc_to_country(self, amount, country: constants.Country) -> bool:
self.update_money() self.update_money()
amount = int(amount) amount = int(amount)
if self.details.cc < amount or amount < 20: if self.details.cc < amount or amount < 20:
@ -1209,7 +1174,7 @@ class CitizenEconomy(CitizenTravel):
f" treasury", kwargs=r.json()) f" treasury", kwargs=r.json())
return False return False
def contribute_food_to_country(self, amount, quality, country: Country) -> bool: def contribute_food_to_country(self, amount, quality, country: constants.Country) -> bool:
self.update_inventory() self.update_inventory()
amount = amount // 1 amount = amount // 1
if self.food["q" + str(quality)] < amount or amount < 10: if self.food["q" + str(quality)] < amount or amount < 10:
@ -1226,7 +1191,7 @@ class CitizenEconomy(CitizenTravel):
f"{country}'s treasury", kwargs=r.json()) f"{country}'s treasury", kwargs=r.json())
return False return False
def contribute_gold_to_country(self, amount: int, country: Country) -> bool: def contribute_gold_to_country(self, amount: int, country: constants.Country) -> bool:
self.update_money() self.update_money()
if self.details.cc < amount: if self.details.cc < amount:
@ -1304,13 +1269,13 @@ class CitizenMedia(BaseCitizen):
article_id = 0 article_id = 0
return article_id return article_id
else: else:
raise ErepublikException("Article kind must be one of:\n{}\n'{}' is not supported".format( raise classes.ErepublikException("Article kind must be one of:\n{}\n'{}' is not supported".format(
"\n".join(["{}: {}".format(k, v) for k, v in kinds.items()]), kind "\n".join(["{}: {}".format(k, v) for k, v in kinds.items()]), kind
)) ))
class CitizenMilitary(CitizenTravel): class CitizenMilitary(CitizenTravel):
all_battles: Dict[int, Battle] = None all_battles: Dict[int, classes.Battle] = None
countries: Dict[int, Dict[str, Union[str, List[int]]]] = None countries: Dict[int, Dict[str, Union[str, List[int]]]] = None
__last_war_update_data = None __last_war_update_data = None
@ -1344,10 +1309,10 @@ class CitizenMilitary(CitizenTravel):
if r_json.get("battles"): if r_json.get("battles"):
all_battles = {} all_battles = {}
for battle_data in r_json.get("battles", {}).values(): for battle_data in r_json.get("battles", {}).values():
all_battles[battle_data.get('id')] = Battle(battle_data) all_battles[battle_data.get('id')] = classes.Battle(battle_data)
self.all_battles = all_battles self.all_battles = all_battles
def get_battle_for_war(self, war_id: int) -> Optional[Battle]: def get_battle_for_war(self, war_id: int) -> Optional[classes.Battle]:
self.update_war_info() self.update_war_info()
war_info = self.get_war_status(war_id) war_info = self.get_war_status(war_id)
return self.all_battles.get(war_info.get("battle_id"), None) return self.all_battles.get(war_info.get("battle_id"), None)
@ -1375,7 +1340,7 @@ class CitizenMilitary(CitizenTravel):
def get_available_weapons(self, battle_id: int): def get_available_weapons(self, battle_id: int):
return self._get_military_show_weapons(battle_id).json() return self._get_military_show_weapons(battle_id).json()
def set_default_weapon(self, battle: Battle, division: BattleDivision) -> int: def set_default_weapon(self, battle: classes.Battle, division: classes.BattleDivision) -> int:
available_weapons = self._get_military_show_weapons(battle.id).json() available_weapons = self._get_military_show_weapons(battle.id).json()
while not isinstance(available_weapons, list): while not isinstance(available_weapons, list):
available_weapons = self._get_military_show_weapons(battle.id).json() available_weapons = self._get_military_show_weapons(battle.id).json()
@ -1390,7 +1355,7 @@ class CitizenMilitary(CitizenTravel):
pass pass
return self.change_weapon(battle, weapon_quality, division) return self.change_weapon(battle, weapon_quality, division)
def change_weapon(self, battle: Battle, quality: int, battle_zone: BattleDivision) -> int: def change_weapon(self, battle: classes.Battle, quality: int, battle_zone: classes.BattleDivision) -> int:
r = self._post_military_change_weapon(battle.id, battle_zone.id, quality) r = self._post_military_change_weapon(battle.id, battle_zone.id, quality)
influence = r.json().get('weaponInfluence') influence = r.json().get('weaponInfluence')
self._report_action("MILITARY_WEAPON", f"Switched to q{quality} weapon," self._report_action("MILITARY_WEAPON", f"Switched to q{quality} weapon,"
@ -1442,19 +1407,19 @@ class CitizenMilitary(CitizenTravel):
# #
# self.active_fs = active_fs # self.active_fs = active_fs
def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[Battle]: def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[classes.Battle]:
cs_battles_priority_air: List[Battle] = [] cs_battles_priority_air: List[classes.Battle] = []
cs_battles_priority_ground: List[Battle] = [] cs_battles_priority_ground: List[classes.Battle] = []
cs_battles_air: List[Battle] = [] cs_battles_air: List[classes.Battle] = []
cs_battles_ground: List[Battle] = [] cs_battles_ground: List[classes.Battle] = []
deployed_battles_air: List[Battle] = [] deployed_battles_air: List[classes.Battle] = []
deployed_battles_ground: List[Battle] = [] deployed_battles_ground: List[classes.Battle] = []
ally_battles_air: List[Battle] = [] ally_battles_air: List[classes.Battle] = []
ally_battles_ground: List[Battle] = [] ally_battles_ground: List[classes.Battle] = []
other_battles_air: List[Battle] = [] other_battles_air: List[classes.Battle] = []
other_battles_ground: List[Battle] = [] other_battles_ground: List[classes.Battle] = []
ret_battles: List[Battle] = [] ret_battles: List[classes.Battle] = []
if sort_by_time: if sort_by_time:
battle_list = sorted(self.all_battles.values(), key=lambda b: b.start) battle_list = sorted(self.all_battles.values(), key=lambda b: b.start)
battle_list.reverse() battle_list.reverse()
@ -1520,20 +1485,22 @@ class CitizenMilitary(CitizenTravel):
ret_battles = ret_battles + cs_battles + deployed_battles + other_battles ret_battles = ret_battles + cs_battles + deployed_battles + other_battles
return ret_battles return ret_battles
def get_cheap_tp_divisions(self) -> Optional[BattleDivision]: def get_cheap_tp_divisions(self) -> Optional[classes.BattleDivision]:
air_divs: List[Tuple[BattleDivision, int]] = [] air_divs: List[Tuple[classes.BattleDivision, int]] = []
ground_divs: List[Tuple[BattleDivision, int]] = [] ground_divs: List[Tuple[classes.BattleDivision, int]] = []
for battle in reversed(self.sorted_battles(True, True)): for battle in reversed(self.sorted_battles(True, True)):
for division in battle.div.values(): for division in battle.div.values():
if not division.terrain: if not division.terrain:
if division.is_air: if division.is_air:
medal = self.get_battle_round_data(division)[self.details.citizenship == division.battle.defender.id] medal = self.get_battle_round_data(division)[
self.details.citizenship == division.battle.defender.id]
if not medal and division.battle.start: if not medal and division.battle.start:
return division return division
else: else:
air_divs.append((division, medal.get('1').get('raw_value'))) air_divs.append((division, medal.get('1').get('raw_value')))
else: else:
medal = self.get_battle_round_data(division)[self.details.citizenship == division.battle.defender.id] medal = self.get_battle_round_data(division)[
self.details.citizenship == division.battle.defender.id]
if not medal and division.battle.start: if not medal and division.battle.start:
return division return division
else: else:
@ -1554,9 +1521,9 @@ class CitizenMilitary(CitizenTravel):
if self.should_fight()[0]: if self.should_fight()[0]:
self.write_log("Checking for battles to fight in...") self.write_log("Checking for battles to fight in...")
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, Battle): if not isinstance(battle, classes.Battle):
continue continue
battle_zone: Optional[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:
@ -1571,7 +1538,8 @@ class CitizenMilitary(CitizenTravel):
continue continue
if not battle_zone: if not battle_zone:
continue continue
allies = battle.invader.deployed + battle.defender.deployed + [battle.invader.country, battle.defender.country] allies = battle.invader.deployed + battle.defender.deployed + [battle.invader.country,
battle.defender.country]
travel_needed = self.details.current_country not in allies travel_needed = self.details.current_country not in allies
@ -1610,7 +1578,7 @@ class CitizenMilitary(CitizenTravel):
self.travel_to_residence() self.travel_to_residence()
break break
def fight(self, battle: Battle, division: BattleDivision, side: BattleSide = None, count: int = None) -> int: def fight(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide = None, count: int = None) -> 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.
@ -1632,7 +1600,8 @@ class CitizenMilitary(CitizenTravel):
if not division.is_air and self.config.boosters: if not division.is_air and self.config.boosters:
self.activate_dmg_booster() self.activate_dmg_booster()
if side is None: if side is None:
side = battle.defender if self.details.citizenship in battle.defender.allies + [battle.defender.country] else battle.invader side = battle.defender if self.details.citizenship in battle.defender.allies + [
battle.defender.country] else battle.invader
error_count = 0 error_count = 0
ok_to_fight = True ok_to_fight = True
if count is None: if count is None:
@ -1657,7 +1626,7 @@ class CitizenMilitary(CitizenTravel):
air=battle.has_air, hits=total_hits)) air=battle.has_air, hits=total_hits))
return error_count return error_count
def _shoot(self, battle: Battle, division: BattleDivision, side: BattleSide): def _shoot(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide):
if division.is_air: if division.is_air:
response = self._post_military_fight_air(battle.id, side.id, division.id) response = self._post_military_fight_air(battle.id, side.id, division.id)
else: else:
@ -1679,7 +1648,8 @@ class CitizenMilitary(CitizenTravel):
elif r_json.get("message") == "NOT_ENOUGH_WEAPONS": elif r_json.get("message") == "NOT_ENOUGH_WEAPONS":
self.set_default_weapon(battle, division) self.set_default_weapon(battle, division)
elif r_json.get("message") == "FIGHT_DISABLED": elif r_json.get("message") == "FIGHT_DISABLED":
self._post_main_profile_update('options', params='{"optionName":"enable_web_deploy","optionValue":"off"}') self._post_main_profile_update('options',
params='{"optionName":"enable_web_deploy","optionValue":"off"}')
self.set_default_weapon(battle, division) self.set_default_weapon(battle, division)
else: else:
if r_json.get("message") == "UNKNOWN_SIDE": if r_json.get("message") == "UNKNOWN_SIDE":
@ -1698,7 +1668,7 @@ class CitizenMilitary(CitizenTravel):
return hits, err, damage return hits, err, damage
def deploy_bomb(self, battle: Battle, bomb_id: int, inv_side: bool = None, count: int = 1) -> int: def deploy_bomb(self, battle: classes.Battle, bomb_id: int, inv_side: bool = None, count: int = 1) -> int:
"""Deploy bombs in a battle for given side. """Deploy bombs in a battle for given side.
:param battle: Battle :param battle: Battle
@ -1724,7 +1694,8 @@ class CitizenMilitary(CitizenTravel):
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)
else: else:
involved = [battle.invader.country, battle.defender.country] + battle.invader.deployed + battle.defender.deployed involved = [battle.invader.country,
battle.defender.country] + battle.invader.deployed + battle.defender.deployed
if self.details.current_country not in involved: if self.details.current_country not in involved:
count = 0 count = 0
errors = deployed_count = 0 errors = deployed_count = 0
@ -1743,7 +1714,7 @@ class CitizenMilitary(CitizenTravel):
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}")
return deployed_count return deployed_count
def change_division(self, battle: Battle, division: BattleDivision): def change_division(self, battle: classes.Battle, division: classes.BattleDivision):
"""Change division. """Change division.
:param battle: Battle :param battle: Battle
@ -1810,7 +1781,7 @@ class CitizenMilitary(CitizenTravel):
self._report_action('MILITARY_BOOSTER', 'Activated PrestigePoint booster') self._report_action('MILITARY_BOOSTER', 'Activated PrestigePoint booster')
return self._post_military_fight_activate_booster(battle_id, 1, 180, "prestige_points") return self._post_military_fight_activate_booster(battle_id, 1, 180, "prestige_points")
def _rw_choose_side(self, battle: Battle, side: BattleSide) -> Response: def _rw_choose_side(self, battle: classes.Battle, side: classes.BattleSide) -> Response:
return self._post_main_battlefield_travel(side.id, battle.id) return self._post_main_battlefield_travel(side.id, battle.id)
def should_travel_to_fight(self) -> bool: def should_travel_to_fight(self) -> bool:
@ -1884,7 +1855,7 @@ class CitizenMilitary(CitizenTravel):
return (count if count > 0 else 0), msg, force_fight return (count if count > 0 else 0), msg, force_fight
def get_battle_round_data(self, division: BattleDivision) -> Tuple[Any, Any]: def get_battle_round_data(self, division: classes.BattleDivision) -> Tuple[Any, Any]:
battle = division.battle battle = division.battle
data = dict(zoneId=battle.zone_id, round_id=battle.zone_id, division=division.div, data = dict(zoneId=battle.zone_id, round_id=battle.zone_id, division=division.div,
@ -1900,7 +1871,7 @@ class CitizenMilitary(CitizenTravel):
self.get_csrf_token() self.get_csrf_token()
self.launch_attack(war_id, region_id, region_name) self.launch_attack(war_id, region_id, region_name)
def get_active_wars(self, country: Country = None) -> List[int]: def get_active_wars(self, country: constants.Country = None) -> List[int]:
r = self._get_country_military(country.link if country else self.details.citizenship.link) r = self._get_country_military(country.link if country else self.details.citizenship.link)
all_war_ids = re.findall(r'//www\.erepublik\.com/en/wars/show/(\d+)"', r.text) all_war_ids = re.findall(r'//www\.erepublik\.com/en/wars/show/(\d+)"', r.text)
return [int(wid) for wid in all_war_ids] return [int(wid) for wid in all_war_ids]
@ -1917,11 +1888,11 @@ class CitizenMilitary(CitizenTravel):
self._post_wars_attack_region(war_id, region_id, region_name) self._post_wars_attack_region(war_id, region_id, region_name)
self._report_action("MILITARY_QUEUE_ATTACK", f"Battle for *{region_name}* queued") self._report_action("MILITARY_QUEUE_ATTACK", f"Battle for *{region_name}* queued")
def travel_to_battle(self, battle: Battle, allowed_countries: List[Country]) -> bool: def travel_to_battle(self, battle: classes.Battle, allowed_countries: List[constants.Country]) -> bool:
data = self.get_travel_regions(battle=battle) data = self.get_travel_regions(battle=battle)
regs = [] regs = []
countries: Dict[int, Country] = {c.id: c for c in allowed_countries} countries: Dict[int, constants.Country] = {c.id: c for c in allowed_countries}
if data: if data:
for region in data.values(): for region in data.values():
if region['countryId'] in countries: # Is not occupied by other country if region['countryId'] in countries: # Is not occupied by other country
@ -1936,7 +1907,7 @@ class CitizenMilitary(CitizenTravel):
return True return True
return False return False
def get_country_mus(self, country: Country) -> Dict[int, str]: def get_country_mus(self, country: constants.Country) -> Dict[int, str]:
ret = {} ret = {}
r = self._get_main_leaderboards_damage_rankings(country.id) r = self._get_main_leaderboards_damage_rankings(country.id)
for data in r.json()["mu_filter"]: for data in r.json()["mu_filter"]:
@ -1974,7 +1945,7 @@ class CitizenMilitary(CitizenTravel):
class CitizenPolitics(BaseCitizen): class CitizenPolitics(BaseCitizen):
def get_country_parties(self, country: Country = None) -> dict: def get_country_parties(self, country: constants.Country = None) -> dict:
r = self._get_main_rankings_parties(country.id if country else self.details.citizenship.id) r = self._get_main_rankings_parties(country.id if country else self.details.citizenship.id)
ret = {} ret = {}
for name, id_ in re.findall(r'<a class="dotted" title="([^"]+)" href="/en/party/[\w\d-]+-(\d+)/1">', r.text): for name, id_ in re.findall(r'<a class="dotted" title="([^"]+)" href="/en/party/[\w\d-]+-(\d+)/1">', r.text):
@ -1989,13 +1960,15 @@ class CitizenPolitics(BaseCitizen):
self._report_action('POLITIC_PARTY_PRESIDENT', 'Applied for party president elections') self._report_action('POLITIC_PARTY_PRESIDENT', 'Applied for party president elections')
return self._get_candidate_party(self.politics.party_slug) return self._get_candidate_party(self.politics.party_slug)
def get_country_president_election_result(self, country: Country, year: int, month: int) -> Dict[str, int]: def get_country_president_election_result(self, country: constants.Country, year: int, month: int) -> Dict[str, int]:
timestamp = int(erep_tz.localize(datetime(year, month, 5)).timestamp()) timestamp = int(constants.erep_tz.localize(datetime(year, month, 5)).timestamp())
resp = self._get_presidential_elections(country.id, timestamp) resp = self._get_presidential_elections(country.id, timestamp)
candidates = re.findall(r'class="candidate_info">(.*?)</li>', resp.text, re.S | re.M) candidates = re.findall(r'class="candidate_info">(.*?)</li>', resp.text, re.S | re.M)
ret = {} ret = {}
for candidate in candidates: for candidate in candidates:
name = re.search(r'<a hovercard=1 class="candidate_name" href="//www.erepublik.com/en/citizen/profile/\d+" title="(.*)">', candidate) name = re.search(
r'<a hovercard=1 class="candidate_name" href="//www.erepublik.com/en/citizen/profile/\d+" title="(.*)">',
candidate)
name = name.group(1) name = name.group(1)
votes = re.search(r'<span class="votes">(\d+) votes</span>', candidate).group(1) votes = re.search(r'<span class="votes">(\d+) votes</span>', candidate).group(1)
ret.update({name: int(votes)}) ret.update({name: int(votes)})
@ -2251,7 +2224,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
def __init__(self, email: str = "", password: str = "", auto_login: bool = True): def __init__(self, email: str = "", password: str = "", auto_login: bool = True):
super().__init__(email, password) super().__init__(email, password)
self.__last_full_update = utils.good_timedelta(self.now, - timedelta(minutes=5)) self._last_full_update = utils.good_timedelta(self.now, - timedelta(minutes=5))
self.set_debug(True) self.set_debug(True)
if auto_login: if auto_login:
self.login() self.login()
@ -2276,7 +2249,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
"" if self.config.telegram_chat_id or self.config.telegram_token else 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.__last_full_update = utils.good_timedelta(self.now, - timedelta(minutes=5)) self._last_full_update = utils.good_timedelta(self.now, - timedelta(minutes=5))
self.update_all(True) self.update_all(True)
def update_citizen_info(self, html: str = None): def update_citizen_info(self, html: str = None):
@ -2351,10 +2324,10 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
def update_all(self, force_update=False): def update_all(self, force_update=False):
# Do full update max every 5 min # Do full update max every 5 min
if utils.good_timedelta(self.__last_full_update, timedelta(minutes=5)) > self.now and not force_update: if utils.good_timedelta(self._last_full_update, timedelta(minutes=5)) > self.now and not force_update:
return return
else: else:
self.__last_full_update = self.now self._last_full_update = self.now
self.update_citizen_info() self.update_citizen_info()
self.update_war_info() self.update_war_info()
self.update_inventory() self.update_inventory()
@ -2467,7 +2440,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(self.available_industries[kind], quality) item = "{}_{}".format(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:
@ -2478,10 +2451,10 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
else: else:
price = lowest_price.price - 0.01 price = lowest_price.price - 0.01
self.post_market_offer(industry=self.available_industries[kind], amount=int(amount), self.post_market_offer(industry=constants.INDUSTRIES[kind], amount=int(amount),
quality=int(quality), price=price) quality=int(quality), price=price)
def _wam(self, holding: Holding) -> NoReturn: def _wam(self, holding: classes.Holding) -> NoReturn:
response = self.work_as_manager_in_holding(holding) response = self.work_as_manager_in_holding(holding)
if response is None: if response is None:
return return
@ -2496,7 +2469,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 ErepublikException("Unknown kind produced '{kind}'".format(kind=kind)) raise classes.ErepublikException("Unknown kind produced '{kind}'".format(kind=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:
@ -2542,7 +2515,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
self.update_companies() self.update_companies()
# Prevent messing up levelup with wam # Prevent messing up levelup with wam
if not (self.is_levelup_close and self.config.fight) or self.config.force_wam: if not (self.is_levelup_close and self.config.fight) or self.config.force_wam:
regions: Dict[int, Holding] = {} regions: Dict[int, classes.Holding] = {}
for holding in self.my_companies.holdings.values(): for holding in self.my_companies.holdings.values():
if holding.wam_count: if holding.wam_count:
regions.update({holding.region: holding}) regions.update({holding.region: holding})
@ -2570,8 +2543,8 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
self.update_companies() self.update_companies()
return bool(self.my_companies.get_total_wam_count()) return bool(self.my_companies.get_total_wam_count())
def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[Battle]: def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[classes.Battle]:
battles: List[Battle] = self.reporter.fetch_battle_priorities(self.details.current_country) battles: List[classes.Battle] = self.reporter.fetch_battle_priorities(self.details.current_country)
return battles + super().sorted_battles(sort_by_time, only_tp) return battles + super().sorted_battles(sort_by_time, only_tp)
def command_central(self): def command_central(self):

View File

@ -6,52 +6,10 @@ from typing import Any, Dict, List, NamedTuple, Optional, Tuple, Union
from requests import Response, Session, post from requests import Response, Session, post
from erepublik import utils from . import utils, constants
from erepublik.utils import json
INDUSTRIES = {1: "Food", 2: "Weapons", 4: "House", 23: "Aircraft", __all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException',
7: "FRM q1", 8: "FRM q2", 9: "FRM q3", 10: "FRM q4", 11: "FRM q5", 'Holding', 'MyCompanies', 'MyJSONEncoder', 'OfferItem', 'Politics', 'Reporter', 'TelegramBot']
12: "WRM q1", 13: "WRM q2", 14: "WRM q3", 15: "WRM q4", 16: "WRM q5",
18: "HRM q1", 19: "HRM q2", 20: "HRM q3", 21: "HRM q4", 22: "HRM q5",
24: "ARM q1", 25: "ARM q2", 26: "ARM q3", 27: "ARM q4", 28: "ARM q5", }
class Country:
id: int
name: str
link: str
iso: str
def __init__(self, country_id: int, name: str, link: str, iso: str):
self.id = country_id
self.name = name
self.link = link
self.iso = iso
def __repr__(self):
return f"Country({self.id}, '{self.name}', '{self.link}', '{self.iso}')"
def __str__(self):
return f"#{self.id} {self.name}"
def __format__(self, format_spec):
return self.iso
def __int__(self):
return self.id
def __eq__(self, other):
if isinstance(other, (int, float)):
return self.id == int(other)
else:
try:
return self.id == int(other)
except ValueError:
return self == other
@property
def __dict__(self):
return dict(id=self.id, name=self.name, iso=self.iso)
class ErepublikException(Exception): class ErepublikException(Exception):
@ -132,7 +90,7 @@ class Holding:
return str(self) return str(self)
@property @property
def __dict__(self): def as_dict(self):
return dict(name=str(self), id=self.id, region=self.region, companies=self.companies, wam_count=self.wam_count) return dict(name=str(self), id=self.id, region=self.region, companies=self.companies, wam_count=self.wam_count)
@ -229,7 +187,7 @@ class Company:
return self._sort_keys != other._sort_keys return self._sort_keys != other._sort_keys
def __str__(self): def __str__(self):
name = f"(#{self.id:>9d}) {INDUSTRIES[self.industry]}" name = f"(#{self.id:>9d}) {constants.INDUSTRIES[self.industry]}"
if not self.is_raw: if not self.is_raw:
name += f" q{self.quality}" name += f" q{self.quality}"
return name return name
@ -238,7 +196,7 @@ class Company:
return str(self) return str(self)
@property @property
def __dict__(self): def as_dict(self):
return dict(name=str(self), holding=self.holding.id, id=self.id, quality=self.quality, is_raw=self.is_raw, return dict(name=str(self), holding=self.holding.id, id=self.id, quality=self.quality, is_raw=self.is_raw,
raw_usage=self.raw_usage, products_made=self.products_made, wam_enabled=self.wam_enabled, raw_usage=self.raw_usage, products_made=self.products_made, wam_enabled=self.wam_enabled,
can_wam=self.can_wam, cannot_wam_reason=self.cannot_wam_reason, industry=self.industry, can_wam=self.can_wam, cannot_wam_reason=self.cannot_wam_reason, industry=self.industry,
@ -328,7 +286,7 @@ class MyCompanies:
self.companies.clear() self.companies.clear()
@property @property
def __dict__(self): def as_dict(self):
return dict(name=str(self), work_units=self.work_units, next_ot_time=self.next_ot_time, return dict(name=str(self), work_units=self.work_units, next_ot_time=self.next_ot_time,
ff_lockdown=self.ff_lockdown, holdings=self.holdings, company_count=len(self.companies)) ff_lockdown=self.ff_lockdown, holdings=self.holdings, company_count=len(self.companies))
@ -397,7 +355,7 @@ class Config:
self.telegram_token = "" self.telegram_token = ""
@property @property
def __dict__(self): def as_dict(self):
return dict(email=self.email, work=self.work, train=self.train, wam=self.wam, ot=self.ot, return dict(email=self.email, work=self.work, train=self.train, wam=self.wam, ot=self.ot,
auto_sell=self.auto_sell, auto_sell_all=self.auto_sell_all, employees=self.employees, auto_sell=self.auto_sell, auto_sell_all=self.auto_sell_all, employees=self.employees,
fight=self.fight, air=self.air, ground=self.ground, all_in=self.all_in, fight=self.fight, air=self.air, ground=self.ground, all_in=self.all_in,
@ -453,6 +411,13 @@ class Energy:
def available(self): def available(self):
return self.recovered + self.recoverable return self.recovered + self.recoverable
@property
def as_dict(self):
return dict(limit=self.limit, interval=self.interval, recoverable=self.recoverable, recovered=self.recovered,
reference_time=self.reference_time, food_fights=self.food_fights,
is_recoverable_full=self.is_recoverable_full, is_recovered_full=self.is_recovered_full,
is_energy_full=self.is_energy_full, available=self.available)
class Details: class Details:
xp = 0 xp = 0
@ -462,11 +427,11 @@ class Details:
gold = 0 gold = 0
next_pp: List[int] = None next_pp: List[int] = None
citizen_id = 0 citizen_id = 0
citizenship: Country citizenship: constants.Country
current_region = 0 current_region = 0
current_country: Country current_country: constants.Country
residence_region = 0 residence_region = 0
residence_country: Country residence_country: constants.Country
daily_task_done = False daily_task_done = False
daily_task_reward = False daily_task_reward = False
mayhem_skills = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0, 14: 0, } mayhem_skills = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0, 14: 0, }
@ -500,6 +465,15 @@ class Details:
next_level_up = (1 + (self.xp // 10)) * 10 next_level_up = (1 + (self.xp // 10)) * 10
return next_level_up - self.xp return next_level_up - self.xp
@property
def as_dict(self):
return dict(xp=self.xp, cc=self.cc, pp=self.pp, pin=self.pin, gold=self.gold, next_pp=self.next_pp,
citizen_id=self.citizen_id, citizenship=self.citizenship, current_region=self.current_region,
current_country=self.current_country, residence_region=self.residence_region,
residence_country=self.residence_country, daily_task_done=self.daily_task_done,
daily_task_reward=self.daily_task_reward, mayhem_skills=self.mayhem_skills,
xp_till_level_up=self.xp_till_level_up)
class Politics: class Politics:
is_party_member: bool = False is_party_member: bool = False
@ -510,6 +484,12 @@ class Politics:
is_congressman: bool = False is_congressman: bool = False
is_country_president: bool = False is_country_president: bool = False
@property
def as_dict(self):
return dict(is_party_member=self.is_party_member, party_id=self.party_id, party_slug=self.party_slug,
is_party_president=self.is_party_president, is_congressman=self.is_congressman,
is_country_president=self.is_country_president)
class House: class House:
quality = None quality = None
@ -543,7 +523,7 @@ class Reporter:
return self.citizen.details.citizen_id return self.citizen.details.citizen_id
@property @property
def __dict__(self): def as_dict(self):
return dict(name=self.name, email=self.email, citizen_id=self.citizen_id, key=self.key, allowed=self.allowed, return dict(name=self.name, email=self.email, citizen_id=self.citizen_id, key=self.key, allowed=self.allowed,
queue=self.__to_update) queue=self.__to_update)
@ -614,7 +594,7 @@ class Reporter:
def report_promo(self, kind: str, time_until: datetime.datetime): 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)) self._req.post(f"{self.url}/promos/add/", data=dict(kind=kind, time_untill=time_until))
def fetch_battle_priorities(self, country: Country) -> List["Battle"]: def fetch_battle_priorities(self, country: constants.Country) -> List["Battle"]:
try: try:
battle_response = self._req.get(f'{self.url}/api/v1/battles/{country.id}') battle_response = self._req.get(f'{self.url}/api/v1/battles/{country.id}')
return [self.citizen.all_battles[bid] for bid in battle_response.json().get('battle_ids', []) if return [self.citizen.all_battles[bid] for bid in battle_response.json().get('battle_ids', []) if
@ -631,7 +611,7 @@ class Reporter:
return return
class MyJSONEncoder(json.JSONEncoder): class MyJSONEncoder(utils.json.JSONEncoder):
def default(self, o): def default(self, o):
from erepublik.citizen import Citizen from erepublik.citizen import Citizen
if isinstance(o, Decimal): if isinstance(o, Decimal):
@ -646,8 +626,8 @@ class MyJSONEncoder(json.JSONEncoder):
microseconds=o.microseconds, total_seconds=o.total_seconds()) microseconds=o.microseconds, total_seconds=o.total_seconds())
elif isinstance(o, Response): elif isinstance(o, Response):
return dict(headers=o.headers.__dict__, url=o.url, text=o.text) return dict(headers=o.headers.__dict__, url=o.url, text=o.text)
elif hasattr(o, '__dict__'): elif hasattr(o, 'as_dict'):
return o.__dict__ return o.as_dict
elif isinstance(o, set): elif isinstance(o, set):
return list(o) return list(o)
elif isinstance(o, Citizen): elif isinstance(o, Citizen):
@ -660,14 +640,14 @@ class MyJSONEncoder(json.JSONEncoder):
class BattleSide: class BattleSide:
points: int points: int
deployed: List[Country] deployed: List[constants.Country]
allies: List[Country] allies: List[constants.Country]
battle: "Battle" battle: "Battle"
country: Country country: constants.Country
defender: bool defender: bool
def __init__(self, battle: "Battle", country: Country, points: int, allies: List[Country], deployed: List[Country], def __init__(self, battle: "Battle", country: constants.Country, points: int, allies: List[constants.Country],
defender: bool): deployed: List[constants.Country], defender: bool):
self.battle = battle self.battle = battle
self.country = country self.country = country
self.points = points self.points = points
@ -691,7 +671,7 @@ class BattleSide:
return self.country.iso return self.country.iso
@property @property
def __dict__(self): def as_dict(self):
return dict(points=self.points, country=self.country, defender=self.defender, allies=self.allies, return dict(points=self.points, country=self.country, defender=self.defender, allies=self.allies,
deployed=self.deployed, battle=repr(self.battle)) deployed=self.deployed, battle=repr(self.battle))
@ -709,9 +689,9 @@ class BattleDivision:
battle: "Battle" battle: "Battle"
@property @property
def __dict__(self): def as_dict(self):
return dict(id=self.id, division=self.div, terrain=(self.terrain, self.terrain_display), wall=self.wall, return dict(id=self.id, division=self.div, terrain=(self.terrain, self.terrain_display), wall=self.wall,
dom_pts=self.dom_pts, epic=self.epic, battle=str(self.battle), end=self.div_end) epic=self.epic, battle=str(self.battle), end=self.div_end)
@property @property
def is_air(self): def is_air(self):
@ -743,7 +723,7 @@ class BattleDivision:
@property @property
def terrain_display(self): def terrain_display(self):
return _TERRAINS[self.terrain] return constants.TERRAINS[self.terrain]
def __str__(self): def __str__(self):
base_name = f"Div #{self.id} d{self.div}" base_name = f"Div #{self.id} d{self.div}"
@ -771,7 +751,7 @@ class Battle:
region_name: str region_name: str
@property @property
def __dict__(self): def as_dict(self):
return dict(id=self.id, war_id=self.war_id, divisions=self.div, zone=self.zone_id, rw=self.is_rw, return dict(id=self.id, war_id=self.war_id, divisions=self.div, zone=self.zone_id, rw=self.is_rw,
dict_lib=self.is_dict_lib, start=self.start, sides={'inv': self.invader, 'def': self.defender}, dict_lib=self.is_dict_lib, start=self.start, sides={'inv': self.invader, 'def': self.defender},
region=[self.region_id, self.region_name]) region=[self.region_id, self.region_name])
@ -805,27 +785,28 @@ class Battle:
self.zone_id = int(battle.get('zone_id')) self.zone_id = int(battle.get('zone_id'))
self.is_rw = bool(battle.get('is_rw')) self.is_rw = bool(battle.get('is_rw'))
self.is_as = bool(battle.get('is_as')) self.is_as = bool(battle.get('is_as'))
self.is_dict_lib = bool(battle.get('is_dict')) or bool(battle.get('is_lib'))
self.region_id = battle.get('region', {}).get('id') self.region_id = battle.get('region', {}).get('id')
self.region_name = battle.get('region', {}).get('name') self.region_name = battle.get('region', {}).get('name')
self.start = datetime.datetime.fromtimestamp(int(battle.get('start', 0)), tz=utils.erep_tz) self.start = datetime.datetime.fromtimestamp(int(battle.get('start', 0)), tz=constants.erep_tz)
self.invader = BattleSide( self.invader = BattleSide(
self, COUNTRIES[battle.get('inv', {}).get('id')], battle.get('inv', {}).get('points'), self, constants.COUNTRIES[battle.get('inv', {}).get('id')], battle.get('inv', {}).get('points'),
[COUNTRIES[row.get('id')] for row in battle.get('inv', {}).get('ally_list')], [constants.COUNTRIES[row.get('id')] for row in battle.get('inv', {}).get('ally_list')],
[COUNTRIES[row.get('id')] for row in battle.get('inv', {}).get('ally_list') if row['deployed']], False [constants.COUNTRIES[row.get('id')] for row in battle.get('inv', {}).get('ally_list') if row['deployed']], False
) )
self.defender = BattleSide( self.defender = BattleSide(
self, COUNTRIES[battle.get('def', {}).get('id')], battle.get('def', {}).get('points'), self, constants.COUNTRIES[battle.get('def', {}).get('id')], battle.get('def', {}).get('points'),
[COUNTRIES[row.get('id')] for row in battle.get('def', {}).get('ally_list')], [constants.COUNTRIES[row.get('id')] for row in battle.get('def', {}).get('ally_list')],
[COUNTRIES[row.get('id')] for row in battle.get('def', {}).get('ally_list') if row['deployed']], True [constants.COUNTRIES[row.get('id')] for row in battle.get('def', {}).get('ally_list') if row['deployed']], True
) )
self.div = {} self.div = {}
for div, data in battle.get('div', {}).items(): for div, data in battle.get('div', {}).items():
div = int(div) div = int(div)
if data.get('end'): if data.get('end'):
end = datetime.datetime.fromtimestamp(data.get('end'), tz=utils.erep_tz) end = datetime.datetime.fromtimestamp(data.get('end'), tz=constants.erep_tz)
else: else:
end = utils.localize_dt(datetime.datetime.max - datetime.timedelta(days=1)) end = utils.localize_dt(datetime.datetime.max - datetime.timedelta(days=1))
@ -838,12 +819,12 @@ class Battle:
self.div.update({div: battle_div}) self.div.update({div: battle_div})
def __str__(self): def __str__(self):
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(now - self.start) time_part = " {}".format(time_now - self.start)
else: else:
time_part = "-{}".format(self.start - now) time_part = "-{}".format(self.start - time_now)
return f"Battle {self.id} for {self.region_name[:16]} | {self.invader} : {self.defender} | Round time {time_part}" return f"Battle {self.id} for {self.region_name[:16]} | {self.invader} : {self.defender} | Round time {time_part}"
@ -902,7 +883,7 @@ class TelegramBot:
self._next_time = utils.now() self._next_time = utils.now()
@property @property
def __dict__(self): def as_dict(self):
return {'chat_id': self.chat_id, 'api_url': self.api_url, 'player': self.player_name, return {'chat_id': self.chat_id, 'api_url': self.api_url, 'player': self.player_name,
'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': bool(len(self._threads))} 'initialized': self.__initialized, 'has_threads': bool(len(self._threads))}
@ -962,54 +943,7 @@ class TelegramBot:
class OfferItem(NamedTuple): class OfferItem(NamedTuple):
price: float = 99_999. price: float = 99_999.
country: Country = Country(0, "", "", "") country: constants.Country = constants.Country(0, "", "", "")
amount: int = 0 amount: int = 0
offer_id: int = 0 offer_id: int = 0
citizen_id: int = 0 citizen_id: int = 0
COUNTRIES: Dict[int, Country] = {
1: Country(1, 'Romania', 'Romania', 'ROU'), 9: Country(9, 'Brazil', 'Brazil', 'BRA'),
10: Country(10, 'Italy', 'Italy', 'ITA'), 11: Country(11, 'France', 'France', 'FRA'),
12: Country(12, 'Germany', 'Germany', 'DEU'), 13: Country(13, 'Hungary', 'Hungary', 'HUN'),
14: Country(14, 'China', 'China', 'CHN'), 15: Country(15, 'Spain', 'Spain', 'ESP'),
23: Country(23, 'Canada', 'Canada', 'CAN'), 24: Country(24, 'USA', 'USA', 'USA'),
26: Country(26, 'Mexico', 'Mexico', 'MEX'), 27: Country(27, 'Argentina', 'Argentina', 'ARG'),
28: Country(28, 'Venezuela', 'Venezuela', 'VEN'), 29: Country(29, 'United Kingdom', 'United-Kingdom', 'GBR'),
30: Country(30, 'Switzerland', 'Switzerland', 'CHE'), 31: Country(31, 'Netherlands', 'Netherlands', 'NLD'),
32: Country(32, 'Belgium', 'Belgium', 'BEL'), 33: Country(33, 'Austria', 'Austria', 'AUT'),
34: Country(34, 'Czech Republic', 'Czech-Republic', 'CZE'), 35: Country(35, 'Poland', 'Poland', 'POL'),
36: Country(36, 'Slovakia', 'Slovakia', 'SVK'), 37: Country(37, 'Norway', 'Norway', 'NOR'),
38: Country(38, 'Sweden', 'Sweden', 'SWE'), 39: Country(39, 'Finland', 'Finland', 'FIN'),
40: Country(40, 'Ukraine', 'Ukraine', 'UKR'), 41: Country(41, 'Russia', 'Russia', 'RUS'),
42: Country(42, 'Bulgaria', 'Bulgaria', 'BGR'), 43: Country(43, 'Turkey', 'Turkey', 'TUR'),
44: Country(44, 'Greece', 'Greece', 'GRC'), 45: Country(45, 'Japan', 'Japan', 'JPN'),
47: Country(47, 'South Korea', 'South-Korea', 'KOR'), 48: Country(48, 'India', 'India', 'IND'),
49: Country(49, 'Indonesia', 'Indonesia', 'IDN'), 50: Country(50, 'Australia', 'Australia', 'AUS'),
51: Country(51, 'South Africa', 'South Africa', 'ZAF'),
52: Country(52, 'Republic of Moldova', 'Republic-of-Moldova', 'MDA'),
53: Country(53, 'Portugal', 'Portugal', 'PRT'), 54: Country(54, 'Ireland', 'Ireland', 'IRL'),
55: Country(55, 'Denmark', 'Denmark', 'DNK'), 56: Country(56, 'Iran', 'Iran', 'IRN'),
57: Country(57, 'Pakistan', 'Pakistan', 'PAK'), 58: Country(58, 'Israel', 'Israel', 'ISR'),
59: Country(59, 'Thailand', 'Thailand', 'THA'), 61: Country(61, 'Slovenia', 'Slovenia', 'SVN'),
63: Country(63, 'Croatia', 'Croatia', 'HRV'), 64: Country(64, 'Chile', 'Chile', 'CHL'),
65: Country(65, 'Serbia', 'Serbia', 'SRB'), 66: Country(66, 'Malaysia', 'Malaysia', 'MYS'),
67: Country(67, 'Philippines', 'Philippines', 'PHL'), 68: Country(68, 'Singapore', 'Singapore', 'SGP'),
69: Country(69, 'Bosnia and Herzegovina', 'Bosnia-Herzegovina', 'BiH'),
70: Country(70, 'Estonia', 'Estonia', 'EST'), 80: Country(80, 'Montenegro', 'Montenegro', 'MNE'),
71: Country(71, 'Latvia', 'Latvia', 'LVA'), 72: Country(72, 'Lithuania', 'Lithuania', 'LTU'),
73: Country(73, 'North Korea', 'North-Korea', 'PRK'), 74: Country(74, 'Uruguay', 'Uruguay', 'URY'),
75: Country(75, 'Paraguay', 'Paraguay', 'PRY'), 76: Country(76, 'Bolivia', 'Bolivia', 'BOL'),
77: Country(77, 'Peru', 'Peru', 'PER'), 78: Country(78, 'Colombia', 'Colombia', 'COL'),
79: Country(79, 'Republic of Macedonia (FYROM)', 'Republic-of-Macedonia-FYROM', 'MKD'),
81: Country(81, 'Republic of China (Taiwan)', 'Republic-of-China-Taiwan', 'TWN'),
82: Country(82, 'Cyprus', 'Cyprus', 'CYP'), 167: Country(167, 'Albania', 'Albania', 'ALB'),
83: Country(83, 'Belarus', 'Belarus', 'BLR'), 84: Country(84, 'New Zealand', 'New-Zealand', 'NZL'),
164: Country(164, 'Saudi Arabia', 'Saudi-Arabia', 'SAU'), 165: Country(165, 'Egypt', 'Egypt', 'EGY'),
166: Country(166, 'United Arab Emirates', 'United-Arab-Emirates', 'UAE'),
168: Country(168, 'Georgia', 'Georgia', 'GEO'), 169: Country(169, 'Armenia', 'Armenia', 'ARM'),
170: Country(170, 'Nigeria', 'Nigeria', 'NGA'), 171: Country(171, 'Cuba', 'Cuba', 'CUB')
}
_TERRAINS = {0: "Standard", 1: 'Industrial', 2: 'Urban', 3: 'Suburbs', 4: 'Airport', 5: 'Plains', 6: 'Wasteland',
7: 'Mountains', 8: 'Beach', 9: 'Swamp', 10: 'Mud', 11: 'Hills', 12: 'Jungle', 13: 'Forest', 14: 'Desert'}

182
erepublik/constants.py Normal file
View File

@ -0,0 +1,182 @@
from typing import Dict, Optional, Union
import pytz
__all__ = ["erep_tz", "Country", "AIR_RANKS", "COUNTRIES", "FOOD_ENERGY", "GROUND_RANKS", "GROUND_RANK_POINTS", "INDUSTRIES", "TERRAINS"]
erep_tz = pytz.timezone('US/Pacific')
class Country:
id: int
name: str
link: str
iso: str
def __init__(self, country_id: int, name: str, link: str, iso: str):
self.id = country_id
self.name = name
self.link = link
self.iso = iso
def __repr__(self):
return f"Country({self.id}, '{self.name}', '{self.link}', '{self.iso}')"
def __str__(self):
return f"#{self.id} {self.name}"
def __format__(self, format_spec):
return self.iso
def __int__(self):
return self.id
def __eq__(self, other):
if isinstance(other, (int, float)):
return self.id == int(other)
else:
try:
return self.id == int(other)
except ValueError:
return self == other
@property
def as_dict(self):
return dict(id=self.id, name=self.name, iso=self.iso)
class Industries:
__by_name = {'food': 1, 'weapons': 2, 'house': 4, 'aircraft': 23,
'frm q1': 7, 'frm q2': 8, 'frm q3': 9, 'frm q4': 10, 'frm q5': 11,
'wrm q1': 12, 'wrm q2': 13, 'wrm q3': 14, 'wrm q4': 15, 'wrm q5': 16,
'hrm q1': 18, 'hrm q2': 19, 'hrm q3': 20, 'hrm q4': 21, 'hrm q5': 22,
'arm q1': 24, 'arm q2': 25, 'arm q3': 26, 'arm q4': 27, 'arm q5': 28}
__by_id = {1: "Food", 2: "Weapons", 4: "House", 23: "Aircraft",
7: "FRM q1", 8: "FRM q2", 9: "FRM q3", 10: "FRM q4", 11: "FRM q5",
12: "WRM q1", 13: "WRM q2", 14: "WRM q3", 15: "WRM q4", 16: "WRM q5",
18: "HRM q1", 19: "HRM q2", 20: "HRM q3", 21: "HRM q4", 22: "HRM q5",
24: "ARM q1", 25: "ARM q2", 26: "ARM q3", 27: "ARM q4", 28: "ARM q5"}
def __getitem__(self, item) -> Optional[Union[int, str]]:
if isinstance(item, int):
return self.__by_id.get(item, None)
elif isinstance(item, str):
return self.__by_name.get(item.lower(), None)
return
def __getattr__(self, item) -> Optional[Union[int, str]]:
return self[item]
@property
def as_dict(self):
return dict(by_id=self.__by_id, by_name=self.__by_name)
AIR_RANKS: Dict[int, str] = {
1: "Airman", 2: "Airman 1st Class", 3: "Airman 1st Class*", 4: "Airman 1st Class**", 5: "Airman 1st Class***",
6: "Airman 1st Class****", 7: "Airman 1st Class*****", 8: "Senior Airman", 9: "Senior Airman*",
10: "Senior Airman**", 11: "Senior Airman***", 12: "Senior Airman****", 13: "Senior Airman*****",
14: "Staff Sergeant", 15: "Staff Sergeant*", 16: "Staff Sergeant**", 17: "Staff Sergeant***",
18: "Staff Sergeant****", 19: "Staff Sergeant*****", 20: "Aviator", 21: "Aviator*", 22: "Aviator**",
23: "Aviator***", 24: "Aviator****", 25: "Aviator*****", 26: "Flight Lieutenant", 27: "Flight Lieutenant*",
28: "Flight Lieutenant**", 29: "Flight Lieutenant***", 30: "Flight Lieutenant****", 31: "Flight Lieutenant*****",
32: "Squadron Leader", 33: "Squadron Leader*", 34: "Squadron Leader**", 35: "Squadron Leader***",
36: "Squadron Leader****", 37: "Squadron Leader*****", 38: "Chief Master Sergeant", 39: "Chief Master Sergeant*",
40: "Chief Master Sergeant**", 41: "Chief Master Sergeant***", 42: "Chief Master Sergeant****",
43: "Chief Master Sergeant*****", 44: "Wing Commander", 45: "Wing Commander*", 46: "Wing Commander**",
47: "Wing Commander***", 48: "Wing Commander****", 49: "Wing Commander*****", 50: "Group Captain",
51: "Group Captain*", 52: "Group Captain**", 53: "Group Captain***", 54: "Group Captain****",
55: "Group Captain*****", 56: "Air Commodore", 57: "Air Commodore*", 58: "Air Commodore**", 59: "Air Commodore***",
60: "Air Commodore****", 61: "Air Commodore*****",
}
COUNTRIES: Dict[int, Country] = {
1: Country(1, 'Romania', 'Romania', 'ROU'), 9: Country(9, 'Brazil', 'Brazil', 'BRA'),
10: Country(10, 'Italy', 'Italy', 'ITA'), 11: Country(11, 'France', 'France', 'FRA'),
12: Country(12, 'Germany', 'Germany', 'DEU'), 13: Country(13, 'Hungary', 'Hungary', 'HUN'),
14: Country(14, 'China', 'China', 'CHN'), 15: Country(15, 'Spain', 'Spain', 'ESP'),
23: Country(23, 'Canada', 'Canada', 'CAN'), 24: Country(24, 'USA', 'USA', 'USA'),
26: Country(26, 'Mexico', 'Mexico', 'MEX'), 27: Country(27, 'Argentina', 'Argentina', 'ARG'),
28: Country(28, 'Venezuela', 'Venezuela', 'VEN'), 29: Country(29, 'United Kingdom', 'United-Kingdom', 'GBR'),
30: Country(30, 'Switzerland', 'Switzerland', 'CHE'), 31: Country(31, 'Netherlands', 'Netherlands', 'NLD'),
32: Country(32, 'Belgium', 'Belgium', 'BEL'), 33: Country(33, 'Austria', 'Austria', 'AUT'),
34: Country(34, 'Czech Republic', 'Czech-Republic', 'CZE'), 35: Country(35, 'Poland', 'Poland', 'POL'),
36: Country(36, 'Slovakia', 'Slovakia', 'SVK'), 37: Country(37, 'Norway', 'Norway', 'NOR'),
38: Country(38, 'Sweden', 'Sweden', 'SWE'), 39: Country(39, 'Finland', 'Finland', 'FIN'),
40: Country(40, 'Ukraine', 'Ukraine', 'UKR'), 41: Country(41, 'Russia', 'Russia', 'RUS'),
42: Country(42, 'Bulgaria', 'Bulgaria', 'BGR'), 43: Country(43, 'Turkey', 'Turkey', 'TUR'),
44: Country(44, 'Greece', 'Greece', 'GRC'), 45: Country(45, 'Japan', 'Japan', 'JPN'),
47: Country(47, 'South Korea', 'South-Korea', 'KOR'), 48: Country(48, 'India', 'India', 'IND'),
49: Country(49, 'Indonesia', 'Indonesia', 'IDN'), 50: Country(50, 'Australia', 'Australia', 'AUS'),
51: Country(51, 'South Africa', 'South Africa', 'ZAF'),
52: Country(52, 'Republic of Moldova', 'Republic-of-Moldova', 'MDA'),
53: Country(53, 'Portugal', 'Portugal', 'PRT'), 54: Country(54, 'Ireland', 'Ireland', 'IRL'),
55: Country(55, 'Denmark', 'Denmark', 'DNK'), 56: Country(56, 'Iran', 'Iran', 'IRN'),
57: Country(57, 'Pakistan', 'Pakistan', 'PAK'), 58: Country(58, 'Israel', 'Israel', 'ISR'),
59: Country(59, 'Thailand', 'Thailand', 'THA'), 61: Country(61, 'Slovenia', 'Slovenia', 'SVN'),
63: Country(63, 'Croatia', 'Croatia', 'HRV'), 64: Country(64, 'Chile', 'Chile', 'CHL'),
65: Country(65, 'Serbia', 'Serbia', 'SRB'), 66: Country(66, 'Malaysia', 'Malaysia', 'MYS'),
67: Country(67, 'Philippines', 'Philippines', 'PHL'), 68: Country(68, 'Singapore', 'Singapore', 'SGP'),
69: Country(69, 'Bosnia and Herzegovina', 'Bosnia-Herzegovina', 'BiH'),
70: Country(70, 'Estonia', 'Estonia', 'EST'), 80: Country(80, 'Montenegro', 'Montenegro', 'MNE'),
71: Country(71, 'Latvia', 'Latvia', 'LVA'), 72: Country(72, 'Lithuania', 'Lithuania', 'LTU'),
73: Country(73, 'North Korea', 'North-Korea', 'PRK'), 74: Country(74, 'Uruguay', 'Uruguay', 'URY'),
75: Country(75, 'Paraguay', 'Paraguay', 'PRY'), 76: Country(76, 'Bolivia', 'Bolivia', 'BOL'),
77: Country(77, 'Peru', 'Peru', 'PER'), 78: Country(78, 'Colombia', 'Colombia', 'COL'),
79: Country(79, 'Republic of Macedonia (FYROM)', 'Republic-of-Macedonia-FYROM', 'MKD'),
81: Country(81, 'Republic of China (Taiwan)', 'Republic-of-China-Taiwan', 'TWN'),
82: Country(82, 'Cyprus', 'Cyprus', 'CYP'), 167: Country(167, 'Albania', 'Albania', 'ALB'),
83: Country(83, 'Belarus', 'Belarus', 'BLR'), 84: Country(84, 'New Zealand', 'New-Zealand', 'NZL'),
164: Country(164, 'Saudi Arabia', 'Saudi-Arabia', 'SAU'), 165: Country(165, 'Egypt', 'Egypt', 'EGY'),
166: Country(166, 'United Arab Emirates', 'United-Arab-Emirates', 'UAE'),
168: Country(168, 'Georgia', 'Georgia', 'GEO'), 169: Country(169, 'Armenia', 'Armenia', 'ARM'),
170: Country(170, 'Nigeria', 'Nigeria', 'NGA'), 171: Country(171, 'Cuba', 'Cuba', 'CUB')
}
FOOD_ENERGY: Dict[str, int] = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20)
GROUND_RANKS: Dict[int, str] = {
1: "Recruit", 2: "Private", 3: "Private*", 4: "Private**", 5: "Private***",
6: "Corporal", 7: "Corporal*", 8: "Corporal**", 9: "Corporal***",
10: "Sergeant", 11: "Sergeant*", 12: "Sergeant**", 13: "Sergeant***",
14: "Lieutenant", 15: "Lieutenant*", 16: "Lieutenant**", 17: "Lieutenant***",
18: "Captain", 19: "Captain*", 20: "Captain**", 21: "Captain***",
22: "Major", 23: "Major*", 24: "Major**", 25: "Major***",
26: "Commander", 27: "Commander*", 28: "Commander**", 29: "Commander***",
30: "Lt Colonel", 31: "Lt Colonel*", 32: "Lt Colonel**", 33: "Lt Colonel***",
34: "Colonel", 35: "Colonel*", 36: "Colonel**", 37: "Colonel***",
38: "General", 39: "General*", 40: "General**", 41: "General***",
42: "Field Marshal", 43: "Field Marshal*", 44: "Field Marshal**", 45: "Field Marshal***",
46: "Supreme Marshal", 47: "Supreme Marshal*", 48: "Supreme Marshal**", 49: "Supreme Marshal***",
50: "National Force", 51: "National Force*", 52: "National Force**", 53: "National Force***",
54: "World Class Force", 55: "World Class Force*", 56: "World Class Force**", 57: "World Class Force***",
58: "Legendary Force", 59: "Legendary Force*", 60: "Legendary Force**", 61: "Legendary Force***",
62: "God of War", 63: "God of War*", 64: "God of War**", 65: "God of War***",
66: "Titan", 67: "Titan*", 68: "Titan**", 69: "Titan***",
70: "Legends I", 71: "Legends II", 72: "Legends III", 73: "Legends IV", 74: "Legends V", 75: "Legends VI",
76: "Legends VII", 77: "Legends VIII", 78: "Legends IX", 79: "Legends X", 80: "Legends XI", 81: "Legends XII",
82: "Legends XIII", 83: "Legends XIV", 84: "Legends XV", 85: "Legends XVI", 86: "Legends XVII", 87: "Legends XVIII",
88: "Legends XIX", 89: "Legends XX",
}
GROUND_RANK_POINTS: Dict[int, int] = {
1: 0, 2: 15, 3: 45, 4: 80, 5: 120, 6: 170, 7: 250, 8: 350, 9: 450, 10: 600, 11: 800, 12: 1000,
13: 1400, 14: 1850, 15: 2350, 16: 3000, 17: 3750, 18: 5000, 19: 6500, 20: 9000, 21: 12000,
22: 15500, 23: 20000, 24: 25000, 25: 31000, 26: 40000, 27: 52000, 28: 67000, 29: 85000,
30: 110000, 31: 140000, 32: 180000, 33: 225000, 34: 285000, 35: 355000, 36: 435000, 37: 540000,
38: 660000, 39: 800000, 40: 950000, 41: 1140000, 42: 1350000, 43: 1600000, 44: 1875000,
45: 2185000, 46: 2550000, 47: 3000000, 48: 3500000, 49: 4150000, 50: 4900000, 51: 5800000,
52: 7000000, 53: 9000000, 54: 11500000, 55: 14500000, 56: 18000000, 57: 22000000, 58: 26500000,
59: 31500000, 60: 37000000, 61: 43000000, 62: 50000000, 63: 100000000, 64: 200000000,
65: 500000000, 66: 1000000000, 67: 2000000000, 68: 4000000000, 69: 10000000000, 70: 20000000000,
71: 30000000000, 72: 40000000000, 73: 50000000000, 74: 60000000000, 75: 70000000000,
76: 80000000000, 77: 90000000000, 78: 100000000000, 79: 110000000000, 80: 120000000000,
81: 130000000000, 82: 140000000000, 83: 150000000000, 84: 160000000000, 85: 170000000000,
86: 180000000000, 87: 190000000000, 88: 200000000000, 89: 210000000000
}
INDUSTRIES = Industries()
TERRAINS: Dict[int, str] = {0: "Standard", 1: 'Industrial', 2: 'Urban', 3: 'Suburbs', 4: 'Airport', 5: 'Plains',
6: 'Wasteland', 7: 'Mountains', 8: 'Beach', 9: 'Swamp', 10: 'Mud', 11: 'Hills',
12: 'Jungle', 13: 'Forest', 14: 'Desert'}

View File

@ -9,109 +9,47 @@ import traceback
import unicodedata import unicodedata
from decimal import Decimal from decimal import Decimal
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Mapping, Optional, Union from typing import Any, List, Mapping, Optional, Union
import pytz
import requests import requests
from . import __commit_id__, __version__ from . import __commit_id__, __version__, constants
try: try:
import simplejson as json import simplejson as json
except ImportError: except ImportError:
import json import json
__all__ = ["FOOD_ENERGY", "COMMIT_ID", "erep_tz", __all__ = ['COMMIT_ID', 'VERSION', 'calculate_hit', 'caught_error', 'date_from_eday', 'eday_from_date',
"now", "localize_dt", "localize_timestamp", "good_timedelta", "eday_from_date", "date_from_eday", 'get_air_hit_dmg_value', 'get_file', 'get_ground_hit_dmg_value', 'get_sleep_seconds', 'good_timedelta',
"get_sleep_seconds", "interactive_sleep", "silent_sleep", 'interactive_sleep', 'json', 'localize_dt', 'localize_timestamp', 'normalize_html_json', 'now',
"write_silent_log", "write_interactive_log", "get_file", "write_file", 'process_error', 'process_warning', 'send_email', 'silent_sleep', 'slugify', 'write_file',
"send_email", "normalize_html_json", "process_error", "process_warning", 'write_interactive_log', 'write_silent_log']
'calculate_hit', 'get_ground_hit_dmg_value', 'get_air_hit_dmg_value']
if not sys.version_info >= (3, 7): if not sys.version_info >= (3, 7):
raise AssertionError('This script requires Python version 3.7 and higher\n' raise AssertionError('This script requires Python version 3.7 and higher\n'
'But Your version is v{}.{}.{}'.format(*sys.version_info)) 'But Your version is v{}.{}.{}'.format(*sys.version_info))
FOOD_ENERGY: Dict[str, int] = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20)
COMMIT_ID: str = __commit_id__ COMMIT_ID: str = __commit_id__
VERSION: str = __version__ VERSION: str = __version__
erep_tz = pytz.timezone('US/Pacific')
AIR_RANKS: Dict[int, str] = {
1: "Airman", 2: "Airman 1st Class", 3: "Airman 1st Class*", 4: "Airman 1st Class**", 5: "Airman 1st Class***",
6: "Airman 1st Class****", 7: "Airman 1st Class*****", 8: "Senior Airman", 9: "Senior Airman*",
10: "Senior Airman**", 11: "Senior Airman***", 12: "Senior Airman****", 13: "Senior Airman*****",
14: "Staff Sergeant", 15: "Staff Sergeant*", 16: "Staff Sergeant**", 17: "Staff Sergeant***",
18: "Staff Sergeant****", 19: "Staff Sergeant*****", 20: "Aviator", 21: "Aviator*", 22: "Aviator**",
23: "Aviator***", 24: "Aviator****", 25: "Aviator*****", 26: "Flight Lieutenant", 27: "Flight Lieutenant*",
28: "Flight Lieutenant**", 29: "Flight Lieutenant***", 30: "Flight Lieutenant****", 31: "Flight Lieutenant*****",
32: "Squadron Leader", 33: "Squadron Leader*", 34: "Squadron Leader**", 35: "Squadron Leader***",
36: "Squadron Leader****", 37: "Squadron Leader*****", 38: "Chief Master Sergeant", 39: "Chief Master Sergeant*",
40: "Chief Master Sergeant**", 41: "Chief Master Sergeant***", 42: "Chief Master Sergeant****",
43: "Chief Master Sergeant*****", 44: "Wing Commander", 45: "Wing Commander*", 46: "Wing Commander**",
47: "Wing Commander***", 48: "Wing Commander****", 49: "Wing Commander*****", 50: "Group Captain",
51: "Group Captain*", 52: "Group Captain**", 53: "Group Captain***", 54: "Group Captain****",
55: "Group Captain*****", 56: "Air Commodore", 57: "Air Commodore*", 58: "Air Commodore**", 59: "Air Commodore***",
60: "Air Commodore****", 61: "Air Commodore*****",
}
GROUND_RANKS: Dict[int, str] = {
1: "Recruit", 2: "Private", 3: "Private*", 4: "Private**", 5: "Private***",
6: "Corporal", 7: "Corporal*", 8: "Corporal**", 9: "Corporal***",
10: "Sergeant", 11: "Sergeant*", 12: "Sergeant**", 13: "Sergeant***",
14: "Lieutenant", 15: "Lieutenant*", 16: "Lieutenant**", 17: "Lieutenant***",
18: "Captain", 19: "Captain*", 20: "Captain**", 21: "Captain***",
22: "Major", 23: "Major*", 24: "Major**", 25: "Major***",
26: "Commander", 27: "Commander*", 28: "Commander**", 29: "Commander***",
30: "Lt Colonel", 31: "Lt Colonel*", 32: "Lt Colonel**", 33: "Lt Colonel***",
34: "Colonel", 35: "Colonel*", 36: "Colonel**", 37: "Colonel***",
38: "General", 39: "General*", 40: "General**", 41: "General***",
42: "Field Marshal", 43: "Field Marshal*", 44: "Field Marshal**", 45: "Field Marshal***",
46: "Supreme Marshal", 47: "Supreme Marshal*", 48: "Supreme Marshal**", 49: "Supreme Marshal***",
50: "National Force", 51: "National Force*", 52: "National Force**", 53: "National Force***",
54: "World Class Force", 55: "World Class Force*", 56: "World Class Force**", 57: "World Class Force***",
58: "Legendary Force", 59: "Legendary Force*", 60: "Legendary Force**", 61: "Legendary Force***",
62: "God of War", 63: "God of War*", 64: "God of War**", 65: "God of War***",
66: "Titan", 67: "Titan*", 68: "Titan**", 69: "Titan***",
70: "Legends I", 71: "Legends II", 72: "Legends III", 73: "Legends IV", 74: "Legends V", 75: "Legends VI",
76: "Legends VII", 77: "Legends VIII", 78: "Legends IX", 79: "Legends X", 80: "Legends XI", 81: "Legends XII",
82: "Legends XIII", 83: "Legends XIV", 84: "Legends XV", 85: "Legends XVI", 86: "Legends XVII", 87: "Legends XVIII",
88: "Legends XIX", 89: "Legends XX",
}
GROUND_RANK_POINTS: Dict[int, int] = {
1: 0, 2: 15, 3: 45, 4: 80, 5: 120, 6: 170, 7: 250, 8: 350, 9: 450, 10: 600, 11: 800, 12: 1000,
13: 1400, 14: 1850, 15: 2350, 16: 3000, 17: 3750, 18: 5000, 19: 6500, 20: 9000, 21: 12000,
22: 15500, 23: 20000, 24: 25000, 25: 31000, 26: 40000, 27: 52000, 28: 67000, 29: 85000,
30: 110000, 31: 140000, 32: 180000, 33: 225000, 34: 285000, 35: 355000, 36: 435000, 37: 540000,
38: 660000, 39: 800000, 40: 950000, 41: 1140000, 42: 1350000, 43: 1600000, 44: 1875000,
45: 2185000, 46: 2550000, 47: 3000000, 48: 3500000, 49: 4150000, 50: 4900000, 51: 5800000,
52: 7000000, 53: 9000000, 54: 11500000, 55: 14500000, 56: 18000000, 57: 22000000, 58: 26500000,
59: 31500000, 60: 37000000, 61: 43000000, 62: 50000000, 63: 100000000, 64: 200000000,
65: 500000000, 66: 1000000000, 67: 2000000000, 68: 4000000000, 69: 10000000000, 70: 20000000000,
71: 30000000000, 72: 40000000000, 73: 50000000000, 74: 60000000000, 75: 70000000000,
76: 80000000000, 77: 90000000000, 78: 100000000000, 79: 110000000000, 80: 120000000000,
81: 130000000000, 82: 140000000000, 83: 150000000000, 84: 160000000000, 85: 170000000000,
86: 180000000000, 87: 190000000000, 88: 200000000000, 89: 210000000000
}
def now() -> datetime.datetime: def now() -> datetime.datetime:
return datetime.datetime.now(erep_tz).replace(microsecond=0) return datetime.datetime.now(constants.erep_tz).replace(microsecond=0)
def localize_timestamp(timestamp: int) -> datetime.datetime: def localize_timestamp(timestamp: int) -> datetime.datetime:
return datetime.datetime.fromtimestamp(timestamp, erep_tz) return datetime.datetime.fromtimestamp(timestamp, constants.erep_tz)
def localize_dt(dt: Union[datetime.date, datetime.datetime]) -> datetime.datetime: def localize_dt(dt: Union[datetime.date, datetime.datetime]) -> datetime.datetime:
try: try:
try: try:
return erep_tz.localize(dt) return constants.erep_tz.localize(dt)
except AttributeError: except AttributeError:
return erep_tz.localize(datetime.datetime.combine(dt, datetime.time(0, 0, 0))) return constants.erep_tz.localize(datetime.datetime.combine(dt, datetime.time(0, 0, 0)))
except ValueError: except ValueError:
return dt.astimezone(erep_tz) return dt.astimezone(constants.erep_tz)
def good_timedelta(dt: datetime.datetime, td: datetime.timedelta) -> datetime.datetime: def good_timedelta(dt: datetime.datetime, td: datetime.timedelta) -> datetime.datetime:
@ -124,7 +62,7 @@ def good_timedelta(dt: datetime.datetime, td: datetime.timedelta) -> datetime.da
:return: datetime object with correct timezone when jumped over DST :return: datetime object with correct timezone when jumped over DST
:rtype: datetime.datetime :rtype: datetime.datetime
""" """
return erep_tz.normalize(dt + td) return constants.erep_tz.normalize(dt + td)
def eday_from_date(date: Union[datetime.date, datetime.datetime] = now()) -> int: def eday_from_date(date: Union[datetime.date, datetime.datetime] = now()) -> int: