diff --git a/erepublik/__init__.py b/erepublik/__init__.py index 2e115d4..60730b3 100644 --- a/erepublik/__init__.py +++ b/erepublik/__init__.py @@ -5,9 +5,9 @@ __author__ = """Eriks Karls""" __email__ = 'eriks@72.lv' __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 -__all__ = ["classes", "utils", "Citizen"] +__all__ = ["classes", "utils", "Citizen", ] diff --git a/erepublik/access_points.py b/erepublik/access_points.py index 5769a2b..8efb42c 100644 --- a/erepublik/access_points.py +++ b/erepublik/access_points.py @@ -5,7 +5,7 @@ from typing import Any, Dict, List, Mapping, Union from requests import Response, Session -from erepublik import utils +from . import constants, utils __all__ = ['SlowRequests', 'CitizenAPI'] @@ -45,7 +45,7 @@ class SlowRequests(Session): }) @property - def __dict__(self): + 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) @@ -461,13 +461,13 @@ class ErepublikPresidentAPI(CitizenBaseAPI): 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, - countryNameConfirm=utils.COUNTRY_LINK[attack_country_id]) - return self.post("{}/{}/new-war".format(self.url, utils.COUNTRY_LINK[self_country_id]), data=data) + countryNameConfirm=constants.COUNTRIES[attack_country_id].link) + 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: data = dict(requirments=1, _token=self.token, debate=debate, currency=1, value=amount, commit='Propose', 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): diff --git a/erepublik/citizen.py b/erepublik/citizen.py index 703d5bf..2eacbe1 100644 --- a/erepublik/citizen.py +++ b/erepublik/citizen.py @@ -10,16 +10,11 @@ from typing import Any, Callable, Dict, List, NoReturn, Optional, Set, Tuple, Un from requests import HTTPError, RequestException, Response -from erepublik import utils -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 +from . import utils, classes, access_points, constants -class BaseCitizen(CitizenAPI): - __last_full_update: datetime = utils.now().min +class BaseCitizen(access_points.CitizenAPI): + _last_full_update: datetime = utils.now().min promos: Dict[str, datetime] = None inventory: Dict[str, int] = {'used': 0, 'total': 0} @@ -34,13 +29,13 @@ class BaseCitizen(CitizenAPI): eday = 0 - config: Config = None - energy: Energy = None - details: Details = None - politics: Politics = None - reporter: Reporter = None + config: classes.Config = None + energy: classes.Energy = None + details: classes.Details = None + politics: classes.Politics = None + reporter: classes.Reporter = None stop_threads: Event = None - telegram: TelegramBot = None + telegram: classes.TelegramBot = None r: Response = None name: str = "Not logged in!" @@ -51,14 +46,14 @@ class BaseCitizen(CitizenAPI): def __init__(self, email: str = "", password: str = ""): super().__init__() self.commit_id = utils.COMMIT_ID - self.config = Config() - self.energy = Energy() - self.details = Details() - self.politics = Politics() - self.my_companies = MyCompanies(self) - self.reporter = Reporter(self) + self.config = classes.Config() + self.energy = classes.Energy() + self.details = classes.Details() + self.politics = classes.Politics() + self.my_companies = classes.MyCompanies(self) + self.reporter = classes.Reporter(self) 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.password = password @@ -85,7 +80,7 @@ class BaseCitizen(CitizenAPI): self.token = re_login_token.group(1) self._login() 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: self.update_citizen_info(resp.text) except (AttributeError, utils.json.JSONDecodeError, ValueError, KeyError): @@ -206,11 +201,12 @@ class BaseCitizen(CitizenAPI): self.energy.recoverable = citizen.get("energyFromFoodRemaining", 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_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.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.daily_task_done = citizen.get("dailyTasksDone", False) self.details.daily_task_reward = citizen.get("hasReward", False) @@ -359,7 +355,7 @@ class BaseCitizen(CitizenAPI): offers = {} 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'), kind=kind, name=kind) data = {data['quality']: data} @@ -373,7 +369,7 @@ class BaseCitizen(CitizenAPI): "total": j.get("inventoryStatus").get("totalStorage")}) inventory = dict(items=dict(active=active_items, final=final_items, 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 def write_log(self, *args, **kwargs): @@ -397,34 +393,14 @@ class BaseCitizen(CitizenAPI): sleep(seconds) 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: - """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]: + def get_countries_with_regions(self) -> Set[constants.Country]: r_json = self._post_main_travel_data().json() return_set = {*[]} for country_data in r_json['countries'].values(): if country_data['currentRegions']: - return_set.add(COUNTRIES[country_data['id']]) + return_set.add(constants.COUNTRIES[country_data['id']]) return return_set def __str__(self) -> str: @@ -434,10 +410,16 @@ class BaseCitizen(CitizenAPI): return self.__str__() @property - def __dict__(self): - ret = super().__dict__.copy() + 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)) 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 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 def now(self) -> datetime: """ @@ -591,7 +552,7 @@ class BaseCitizen(CitizenAPI): self.telegram.report_medal(f"Level *{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 = { "toCountryId": country.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.sleep(5 * 60) 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|' 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 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] self.write_log(msg) if self.reporter.allowed: @@ -730,11 +691,11 @@ class CitizenAnniversary(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_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) regs = [] if regions: @@ -763,7 +724,7 @@ class CitizenTravel(BaseCitizen): if data.get('alreadyInRegion'): return True else: - country = COUNTRIES[data.get('preselectCountryId')] + country = constants.COUNTRIES[data.get('preselectCountryId')] r_json = self._travel(country, region_id).json() if r_json.get('message', '') == 'success': self._update_citizen_location(country, region_id) @@ -771,7 +732,7 @@ class CitizenTravel(BaseCitizen): return True 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() regs = [] @@ -788,12 +749,12 @@ class CitizenTravel(BaseCitizen): return True 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() if data.get('alreadyInRegion'): return True else: - country = COUNTRIES[data.get('preselectCountryId')] + country = constants.COUNTRIES[data.get('preselectCountryId')] region_id = data.get('preselectRegionId') r_json = self._travel(country, region_id).json() if r_json.get('message', '') == 'success': @@ -802,7 +763,7 @@ class CitizenTravel(BaseCitizen): return True 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]]]: return self._post_main_travel_data( holdingId=holding.id if holding else 0, @@ -810,7 +771,7 @@ class CitizenTravel(BaseCitizen): countryId=country.id if country else 0 ).json().get('regions', []) - def get_travel_countries(self) -> Set[Country]: + def get_travel_countries(self) -> Set[constants.Country]: warnings.simplefilter('always') warnings.warn('CitizenTravel.get_travel_countries() are being deprecated, ' 'please use BaseCitizen.get_countries_with_regions()', DeprecationWarning) @@ -833,10 +794,10 @@ class CitizenCompanies(BaseCitizen): 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) - 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: return None 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_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 """ @@ -1019,7 +980,7 @@ class CitizenEconomy(CitizenTravel): return ret 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}") data = { @@ -1032,7 +993,7 @@ class CitizenEconomy(CitizenTravel): } ret = self._post_economy_marketplace_actions(**data) 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()) return ret @@ -1048,43 +1009,46 @@ class CitizenEconomy(CitizenTravel): self._report_action("BOUGHT_PRODUCTS", "", kwargs=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") q1_industries = ["aircraft"] + list(raw_short_names.values()) - if product_name not in self.available_industries and product_name not 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: + if product_name in raw_short_names: quality = 1 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 if quality: - offers[f"q{quality}"] = OfferItem() + offers[f"q{quality}"] = classes.OfferItem() else: max_quality = 1 if product_name in q1_industries else 5 if product_name == 'house' else 7 for q in range(max_quality): - offers[f"q{q + 1}"] = OfferItem() + offers[f"q{q + 1}"] = classes.OfferItem() if country: - countries: Set[Country] = {country} + countries: Set[constants.Country] = {country} else: - countries: Set[Country] = self.get_countries_with_regions() + countries: Set[constants.Country] = self.get_countries_with_regions() start_dt = self.now iterable = [countries, [quality] if quality else range(1, max_quality + 1)] 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}"] if not r.get("error", False): for offer in r["offers"]: if (obj.price > float(offer["priceWithTaxes"]) or ( obj.price == float(offer["priceWithTaxes"]) and obj.amount < int(offer["amount"]) )): - offers[f"q{q}"] = obj = OfferItem( - float(offer["priceWithTaxes"]), COUNTRIES[int(offer["country_id"])], int(offer["amount"]), + offers[f"q{q}"] = obj = classes.OfferItem( + float(offer["priceWithTaxes"]), + constants.COUNTRIES[int(offer["country_id"])], int(offer["amount"]), int(offer["id"]), int(offer["citizen_id"]) ) 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"] 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 else: - amount = hp_needed // utils.FOOD_ENERGY[cheapest_q] + amount = hp_needed // constants.FOOD_ENERGY[cheapest_q] if amount * cheapest.price < self.details.cc: 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.buy_from_market(cheapest.offer_id, amount) 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: if amount < 1: return 0 - ind = {v: k for k, v in self.available_industries.items()} - self.write_log(f"Donate: {amount:4d}q{quality} {ind[industry_id]} to {citizen_id}") + industry = constants.INDUSTRIES[industry_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) 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}!") self._report_action("DONATE_ITEMS", msg) 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): self._report_action("DONATE_ITEMS", f"Unable to donate {amount}q{quality} " - f"{self.get_industry_name(industry_id)}, not enough left!") + f"{industry}, not enough left!") return 0 available = re.search( r'Cannot transfer the items because the user has only (\d+) free slots in (his|her) storage.', response.text ).group(1) 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!') self.sleep(5) 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() amount = int(amount) if self.details.cc < amount or amount < 20: @@ -1209,7 +1174,7 @@ class CitizenEconomy(CitizenTravel): f" treasury", kwargs=r.json()) 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() amount = amount // 1 if self.food["q" + str(quality)] < amount or amount < 10: @@ -1226,7 +1191,7 @@ class CitizenEconomy(CitizenTravel): f"{country}'s treasury", kwargs=r.json()) 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() if self.details.cc < amount: @@ -1304,13 +1269,13 @@ class CitizenMedia(BaseCitizen): article_id = 0 return article_id 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 )) 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 __last_war_update_data = None @@ -1344,10 +1309,10 @@ class CitizenMilitary(CitizenTravel): if r_json.get("battles"): all_battles = {} 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 - 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() war_info = self.get_war_status(war_id) 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): 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() while not isinstance(available_weapons, list): available_weapons = self._get_military_show_weapons(battle.id).json() @@ -1390,7 +1355,7 @@ class CitizenMilitary(CitizenTravel): pass 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) influence = r.json().get('weaponInfluence') self._report_action("MILITARY_WEAPON", f"Switched to q{quality} weapon," @@ -1442,19 +1407,19 @@ class CitizenMilitary(CitizenTravel): # # self.active_fs = active_fs - def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[Battle]: - cs_battles_priority_air: List[Battle] = [] - cs_battles_priority_ground: List[Battle] = [] - cs_battles_air: List[Battle] = [] - cs_battles_ground: List[Battle] = [] - deployed_battles_air: List[Battle] = [] - deployed_battles_ground: List[Battle] = [] - ally_battles_air: List[Battle] = [] - ally_battles_ground: List[Battle] = [] - other_battles_air: List[Battle] = [] - other_battles_ground: List[Battle] = [] + def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[classes.Battle]: + cs_battles_priority_air: List[classes.Battle] = [] + cs_battles_priority_ground: List[classes.Battle] = [] + cs_battles_air: List[classes.Battle] = [] + cs_battles_ground: List[classes.Battle] = [] + deployed_battles_air: List[classes.Battle] = [] + deployed_battles_ground: List[classes.Battle] = [] + ally_battles_air: List[classes.Battle] = [] + ally_battles_ground: List[classes.Battle] = [] + other_battles_air: List[classes.Battle] = [] + other_battles_ground: List[classes.Battle] = [] - ret_battles: List[Battle] = [] + ret_battles: List[classes.Battle] = [] if sort_by_time: battle_list = sorted(self.all_battles.values(), key=lambda b: b.start) battle_list.reverse() @@ -1520,20 +1485,22 @@ class CitizenMilitary(CitizenTravel): ret_battles = ret_battles + cs_battles + deployed_battles + other_battles return ret_battles - def get_cheap_tp_divisions(self) -> Optional[BattleDivision]: - air_divs: List[Tuple[BattleDivision, int]] = [] - ground_divs: List[Tuple[BattleDivision, int]] = [] + def get_cheap_tp_divisions(self) -> Optional[classes.BattleDivision]: + air_divs: List[Tuple[classes.BattleDivision, int]] = [] + ground_divs: List[Tuple[classes.BattleDivision, int]] = [] for battle in reversed(self.sorted_battles(True, True)): for division in battle.div.values(): if not division.terrain: 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: return division else: air_divs.append((division, medal.get('1').get('raw_value'))) 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: return division else: @@ -1554,9 +1521,9 @@ class CitizenMilitary(CitizenTravel): if self.should_fight()[0]: self.write_log("Checking for battles to fight in...") for battle in self.sorted_battles(self.config.sort_battles_time): - if not isinstance(battle, Battle): + if not isinstance(battle, classes.Battle): continue - battle_zone: Optional[BattleDivision] = None + battle_zone: Optional[classes.BattleDivision] = None for div in battle.div.values(): if div.terrain == 0: if div.div_end: @@ -1571,7 +1538,8 @@ class CitizenMilitary(CitizenTravel): continue if not battle_zone: 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 @@ -1610,7 +1578,7 @@ class CitizenMilitary(CitizenTravel): self.travel_to_residence() 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. 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: self.activate_dmg_booster() 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 ok_to_fight = True if count is None: @@ -1657,7 +1626,7 @@ class CitizenMilitary(CitizenTravel): air=battle.has_air, hits=total_hits)) 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: response = self._post_military_fight_air(battle.id, side.id, division.id) else: @@ -1679,7 +1648,8 @@ class CitizenMilitary(CitizenTravel): elif r_json.get("message") == "NOT_ENOUGH_WEAPONS": self.set_default_weapon(battle, division) 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) else: if r_json.get("message") == "UNKNOWN_SIDE": @@ -1698,7 +1668,7 @@ class CitizenMilitary(CitizenTravel): 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. :param battle: Battle @@ -1724,7 +1694,8 @@ class CitizenMilitary(CitizenTravel): if self.details.current_country not in good_countries: has_traveled = self.travel_to_battle(battle, good_countries) 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: 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}") return deployed_count - def change_division(self, battle: Battle, division: BattleDivision): + def change_division(self, battle: classes.Battle, division: classes.BattleDivision): """Change division. :param battle: Battle @@ -1790,7 +1761,7 @@ class CitizenMilitary(CitizenTravel): duration = length break if duration: - self._report_action("MILITARY_BOOSTER", f"Activated 50% {duration/60}h damage booster") + self._report_action("MILITARY_BOOSTER", f"Activated 50% {duration / 60}h damage booster") self._post_economy_activate_booster(5, duration, "damage") def get_active_ground_damage_booster(self): @@ -1810,7 +1781,7 @@ class CitizenMilitary(CitizenTravel): self._report_action('MILITARY_BOOSTER', 'Activated PrestigePoint booster') 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) def should_travel_to_fight(self) -> bool: @@ -1884,7 +1855,7 @@ class CitizenMilitary(CitizenTravel): 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 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.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) all_war_ids = re.findall(r'//www\.erepublik\.com/en/wars/show/(\d+)"', r.text) 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._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) 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: for region in data.values(): if region['countryId'] in countries: # Is not occupied by other country @@ -1936,7 +1907,7 @@ class CitizenMilitary(CitizenTravel): return True return False - def get_country_mus(self, country: Country) -> Dict[int, str]: + def get_country_mus(self, country: constants.Country) -> Dict[int, str]: ret = {} r = self._get_main_leaderboards_damage_rankings(country.id) for data in r.json()["mu_filter"]: @@ -1974,7 +1945,7 @@ class CitizenMilitary(CitizenTravel): 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) ret = {} for name, id_ in re.findall(r'', r.text): @@ -1989,13 +1960,15 @@ class CitizenPolitics(BaseCitizen): self._report_action('POLITIC_PARTY_PRESIDENT', 'Applied for party president elections') 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]: - timestamp = int(erep_tz.localize(datetime(year, month, 5)).timestamp()) + def get_country_president_election_result(self, country: constants.Country, year: int, month: int) -> Dict[str, int]: + timestamp = int(constants.erep_tz.localize(datetime(year, month, 5)).timestamp()) resp = self._get_presidential_elections(country.id, timestamp) candidates = re.findall(r'class="candidate_info">(.*?)', resp.text, re.S | re.M) ret = {} for candidate in candidates: - name = re.search(r'', candidate) + name = re.search( + r'', + candidate) name = name.group(1) votes = re.search(r'(\d+) votes', candidate).group(1) 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): 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) if auto_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) 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) 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): # 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 else: - self.__last_full_update = self.now + self._last_full_update = self.now self.update_citizen_info() self.update_war_info() self.update_inventory() @@ -2467,7 +2440,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade if not amount: inv_resp = self._get_economy_inventory_items().json() 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) if amount >= 1: @@ -2478,10 +2451,10 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade else: 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) - def _wam(self, holding: Holding) -> NoReturn: + def _wam(self, holding: classes.Holding) -> NoReturn: response = self.work_as_manager_in_holding(holding) if response is None: return @@ -2496,7 +2469,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade elif kind.endswith("Raw"): self.sell_produced_product(kind, 1) 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")): raw_kind = re.search(r"not_enough_(\w+)_raw", response.get("message")) if raw_kind: @@ -2542,7 +2515,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade self.update_companies() # Prevent messing up levelup with 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(): if holding.wam_count: regions.update({holding.region: holding}) @@ -2570,8 +2543,8 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade self.update_companies() return bool(self.my_companies.get_total_wam_count()) - def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[Battle]: - battles: List[Battle] = self.reporter.fetch_battle_priorities(self.details.current_country) + def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[classes.Battle]: + battles: List[classes.Battle] = self.reporter.fetch_battle_priorities(self.details.current_country) return battles + super().sorted_battles(sort_by_time, only_tp) def command_central(self): diff --git a/erepublik/classes.py b/erepublik/classes.py index b98766d..e0afc02 100644 --- a/erepublik/classes.py +++ b/erepublik/classes.py @@ -6,52 +6,10 @@ from typing import Any, Dict, List, NamedTuple, Optional, Tuple, Union from requests import Response, Session, post -from erepublik import utils -from erepublik.utils import json +from . import utils, constants -INDUSTRIES = {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", } - - -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) +__all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException', + 'Holding', 'MyCompanies', 'MyJSONEncoder', 'OfferItem', 'Politics', 'Reporter', 'TelegramBot'] class ErepublikException(Exception): @@ -132,7 +90,7 @@ class Holding: return str(self) @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) @@ -229,7 +187,7 @@ class Company: return self._sort_keys != other._sort_keys 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: name += f" q{self.quality}" return name @@ -238,7 +196,7 @@ class Company: return str(self) @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, 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, @@ -328,7 +286,7 @@ class MyCompanies: self.companies.clear() @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, ff_lockdown=self.ff_lockdown, holdings=self.holdings, company_count=len(self.companies)) @@ -397,7 +355,7 @@ class Config: self.telegram_token = "" @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, 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, @@ -453,6 +411,13 @@ class Energy: def available(self): 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: xp = 0 @@ -462,11 +427,11 @@ class Details: gold = 0 next_pp: List[int] = None citizen_id = 0 - citizenship: Country + citizenship: constants.Country current_region = 0 - current_country: Country + current_country: constants.Country residence_region = 0 - residence_country: Country + residence_country: constants.Country daily_task_done = False daily_task_reward = False mayhem_skills = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0, 14: 0, } @@ -500,6 +465,15 @@ class Details: next_level_up = (1 + (self.xp // 10)) * 10 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: is_party_member: bool = False @@ -510,6 +484,12 @@ class Politics: is_congressman: 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: quality = None @@ -543,7 +523,7 @@ class Reporter: return self.citizen.details.citizen_id @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, queue=self.__to_update) @@ -614,7 +594,7 @@ class Reporter: 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)) - def fetch_battle_priorities(self, country: Country) -> List["Battle"]: + def fetch_battle_priorities(self, country: constants.Country) -> List["Battle"]: try: 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 @@ -631,7 +611,7 @@ class Reporter: return -class MyJSONEncoder(json.JSONEncoder): +class MyJSONEncoder(utils.json.JSONEncoder): def default(self, o): from erepublik.citizen import Citizen if isinstance(o, Decimal): @@ -646,8 +626,8 @@ class MyJSONEncoder(json.JSONEncoder): microseconds=o.microseconds, total_seconds=o.total_seconds()) elif isinstance(o, Response): return dict(headers=o.headers.__dict__, url=o.url, text=o.text) - elif hasattr(o, '__dict__'): - return o.__dict__ + elif hasattr(o, 'as_dict'): + return o.as_dict elif isinstance(o, set): return list(o) elif isinstance(o, Citizen): @@ -660,14 +640,14 @@ class MyJSONEncoder(json.JSONEncoder): class BattleSide: points: int - deployed: List[Country] - allies: List[Country] + deployed: List[constants.Country] + allies: List[constants.Country] battle: "Battle" - country: Country + country: constants.Country defender: bool - def __init__(self, battle: "Battle", country: Country, points: int, allies: List[Country], deployed: List[Country], - defender: bool): + def __init__(self, battle: "Battle", country: constants.Country, points: int, allies: List[constants.Country], + deployed: List[constants.Country], defender: bool): self.battle = battle self.country = country self.points = points @@ -691,7 +671,7 @@ class BattleSide: return self.country.iso @property - def __dict__(self): + def as_dict(self): return dict(points=self.points, country=self.country, defender=self.defender, allies=self.allies, deployed=self.deployed, battle=repr(self.battle)) @@ -709,9 +689,9 @@ class BattleDivision: battle: "Battle" @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, - 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 def is_air(self): @@ -743,7 +723,7 @@ class BattleDivision: @property def terrain_display(self): - return _TERRAINS[self.terrain] + return constants.TERRAINS[self.terrain] def __str__(self): base_name = f"Div #{self.id} d{self.div}" @@ -771,7 +751,7 @@ class Battle: region_name: str @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, dict_lib=self.is_dict_lib, start=self.start, sides={'inv': self.invader, 'def': self.defender}, region=[self.region_id, self.region_name]) @@ -805,27 +785,28 @@ class Battle: self.zone_id = int(battle.get('zone_id')) self.is_rw = bool(battle.get('is_rw')) 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_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, COUNTRIES[battle.get('inv', {}).get('id')], battle.get('inv', {}).get('points'), - [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 + self, constants.COUNTRIES[battle.get('inv', {}).get('id')], battle.get('inv', {}).get('points'), + [constants.COUNTRIES[row.get('id')] for row in battle.get('inv', {}).get('ally_list')], + [constants.COUNTRIES[row.get('id')] for row in battle.get('inv', {}).get('ally_list') if row['deployed']], False ) self.defender = BattleSide( - self, COUNTRIES[battle.get('def', {}).get('id')], battle.get('def', {}).get('points'), - [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 + self, constants.COUNTRIES[battle.get('def', {}).get('id')], battle.get('def', {}).get('points'), + [constants.COUNTRIES[row.get('id')] for row in battle.get('def', {}).get('ally_list')], + [constants.COUNTRIES[row.get('id')] for row in battle.get('def', {}).get('ally_list') if row['deployed']], True ) self.div = {} for div, data in battle.get('div', {}).items(): div = int(div) 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: end = utils.localize_dt(datetime.datetime.max - datetime.timedelta(days=1)) @@ -838,12 +819,12 @@ class Battle: self.div.update({div: battle_div}) def __str__(self): - now = utils.now() + time_now = utils.now() is_started = self.start < utils.now() if is_started: - time_part = " {}".format(now - self.start) + time_part = " {}".format(time_now - self.start) 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}" @@ -902,7 +883,7 @@ class TelegramBot: self._next_time = utils.now() @property - def __dict__(self): + def as_dict(self): 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, 'initialized': self.__initialized, 'has_threads': bool(len(self._threads))} @@ -962,54 +943,7 @@ class TelegramBot: class OfferItem(NamedTuple): price: float = 99_999. - country: Country = Country(0, "", "", "") + country: constants.Country = constants.Country(0, "", "", "") amount: int = 0 offer_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'} diff --git a/erepublik/constants.py b/erepublik/constants.py new file mode 100644 index 0000000..50640b8 --- /dev/null +++ b/erepublik/constants.py @@ -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'} diff --git a/erepublik/utils.py b/erepublik/utils.py index c4e8a9f..1ecd22b 100644 --- a/erepublik/utils.py +++ b/erepublik/utils.py @@ -9,109 +9,47 @@ import traceback import unicodedata from decimal import Decimal 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 -from . import __commit_id__, __version__ +from . import __commit_id__, __version__, constants try: import simplejson as json except ImportError: import json -__all__ = ["FOOD_ENERGY", "COMMIT_ID", "erep_tz", - "now", "localize_dt", "localize_timestamp", "good_timedelta", "eday_from_date", "date_from_eday", - "get_sleep_seconds", "interactive_sleep", "silent_sleep", - "write_silent_log", "write_interactive_log", "get_file", "write_file", - "send_email", "normalize_html_json", "process_error", "process_warning", - 'calculate_hit', 'get_ground_hit_dmg_value', 'get_air_hit_dmg_value'] +__all__ = ['COMMIT_ID', 'VERSION', 'calculate_hit', 'caught_error', 'date_from_eday', 'eday_from_date', + '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_interactive_log', 'write_silent_log'] if not sys.version_info >= (3, 7): raise AssertionError('This script requires Python version 3.7 and higher\n' '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__ 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: - 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: - 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: try: try: - return erep_tz.localize(dt) + return constants.erep_tz.localize(dt) 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: - return dt.astimezone(erep_tz) + return dt.astimezone(constants.erep_tz) 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 :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: