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"""
__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", ]

View File

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

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 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'<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')
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">(.*?)</li>', resp.text, re.S | re.M)
ret = {}
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)
votes = re.search(r'<span class="votes">(\d+) votes</span>', 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):

View File

@ -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'}

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