Compare commits

...

18 Commits

Author SHA1 Message Date
a45dd7e0c3 Bump version: 0.20.4 → 0.21.0 2020-07-11 09:39:25 +03:00
316a826c93 Merge branch 'master' of github.com:eeriks/erepublik 2020-07-11 09:38:20 +03:00
c9710733e2 Typo 2020-07-11 09:37:22 +03:00
9e678d6b51 Bump version: 0.20.3.11 → 0.20.4 2020-07-10 17:13:15 +03:00
88517cb076 Bugfix 2020-07-10 17:09:28 +03:00
01c8aef012 Bugfix: South Africa link 2020-07-10 17:08:58 +03:00
d7ac66bd69 Bump version: 0.20.3.10 → 0.20.3.11 2020-07-10 12:16:53 +03:00
e8739caca1 fix 2020-07-10 12:16:18 +03:00
9df9c1cd87 Bump version: 0.20.3.9 → 0.20.3.10 2020-07-10 00:47:42 +03:00
1b004f163a bugfix 2020-07-10 00:47:36 +03:00
72bd2787d3 Bump version: 0.20.3.8 → 0.20.3.9 2020-07-09 19:11:04 +03:00
bfa6cb1e78 bugfix 2020-07-09 19:10:55 +03:00
1db7c98b47 Bump version: 0.20.3.7 → 0.20.3.8 2020-07-09 18:51:09 +03:00
1f21a71c74 bugfix 2020-07-09 18:51:00 +03:00
0531c8997b Bump version: 0.20.3.6 → 0.20.3.7 2020-07-09 17:42:24 +03:00
5a8a0a3920 mavericks 2020-07-09 17:42:15 +03:00
d938908986 Bump version: 0.20.3.5 → 0.20.3.6 2020-07-09 14:42:51 +03:00
ffbbd25e54 Code cleanup 2020-07-09 14:42:40 +03:00
9 changed files with 449 additions and 414 deletions

View File

@ -4,10 +4,9 @@
__author__ = """Eriks Karls"""
__email__ = 'eriks@72.lv'
__version__ = '0.20.3.5'
__commit_id__ = "fc4295d"
__version__ = '0.21.0'
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}
@ -27,38 +22,37 @@ class BaseCitizen(CitizenAPI):
ot_points: int = 0
food: Dict[str, int] = {"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0, "total": 0}
eb_normal = 0
eb_double = 0
eb_small = 0
division = 0
eb_normal: int = 0
eb_double: int = 0
eb_small: int = 0
division: int = 0
maverick: bool = False
eday = 0
eday: int = 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!"
logged_in: bool = False
commit_id: str = ""
restricted_ip: bool = False
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 +79,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,19 +200,21 @@ 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)
self.maverick = citizen.get("canSwitchDivisions", False)
if citizen.get("dailyOrderDone", False) and not citizen.get("hasDailyOrderReward", False):
self._post_military_group_missions()
self.details.next_pp.sort()
for skill in citizen.get("mySkills", {}).values():
for skill in citizen.get("terrainSkills", {}).values():
self.details.mayhem_skills.update({int(skill["terrain_id"]): int(skill["skill_points"])})
if citizen.get('party', []):
@ -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):
@ -384,9 +380,9 @@ class BaseCitizen(CitizenAPI):
def report_error(self, msg: str = "", is_warning: bool = False):
if is_warning:
utils.process_warning(msg, self.name, sys.exc_info(), self, self.commit_id)
utils.process_warning(msg, self.name, sys.exc_info(), self)
else:
utils.process_error(msg, self.name, sys.exc_info(), self, self.commit_id, None)
utils.process_error(msg, self.name, sys.exc_info(), self, None, None)
def sleep(self, seconds: int):
if seconds < 0:
@ -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,24 @@ 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]
if division.is_air and self.config.air:
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]
elif self.config.ground:
if not division.div == self.division and not self.maverick:
continue
division_medals = self.get_battle_round_data(division)
medal = division_medals[self.details.citizenship == division.battle.defender.country]
if not medal and division.battle.start:
return division
else:
@ -1554,9 +1523,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:
@ -1564,14 +1533,15 @@ class CitizenMilitary(CitizenTravel):
if self.config.air and div.is_air:
battle_zone = div
break
elif self.config.ground and not div.is_air and div.div == self.division:
elif self.config.ground and not div.is_air and (div.div == self.division or self.maverick):
battle_zone = div
break
else:
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
@ -1604,13 +1574,13 @@ class CitizenMilitary(CitizenTravel):
if not self.travel_to_battle(battle, countries_to_travel):
break
self.change_division(battle, battle_zone)
self.set_default_weapon(battle, battle_zone)
self.fight(battle, battle_zone, side)
self.travel_to_residence()
break
if self.change_division(battle, battle_zone):
self.set_default_weapon(battle, battle_zone)
self.fight(battle, battle_zone, side)
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,12 +1602,15 @@ 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:
count = self.should_fight()[0]
self.write_log(f"Fighting in battle for {battle.region_name} on {side} side\n{battle}\n{str(division)}")
total_damage = 0
total_hits = 0
while ok_to_fight and error_count < 10 and count > 0:
@ -1657,7 +1630,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:
@ -1678,8 +1651,12 @@ class CitizenMilitary(CitizenTravel):
pass
elif r_json.get("message") == "NOT_ENOUGH_WEAPONS":
self.set_default_weapon(battle, division)
elif r_json.get("message") == "Cannot activate a zone with a non-native division":
self.write_log("Wrong division!!")
return 0, 10, 0
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 +1675,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 +1701,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 +1721,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) -> bool:
"""Change division.
:param battle: Battle
@ -1752,8 +1730,12 @@ class CitizenMilitary(CitizenTravel):
:type division: BattleDivision
:return:
"""
self._post_main_battlefield_change_division(battle.id, division.id)
self._report_action("MILITARY_DIV_SWITCH", f"Switched to d{division.div} in battle {battle.id}")
resp = self._post_main_battlefield_change_division(battle.id, division.id)
if resp.json().get('error'):
self.write_log(resp.json().get('message'))
return False
self._report_action("MILITARY_DIV_SWITCH", f"Switched to d{division.div} in battle {battle.id}", kwargs=resp.json())
return True
def get_ground_hit_dmg_value(self, rang: int = None, strength: float = None, elite: bool = None, ne: bool = False,
booster_50: bool = False, booster_100: bool = False, tp: bool = True) -> Decimal:
@ -1775,7 +1757,7 @@ class CitizenMilitary(CitizenTravel):
if not rang or elite is None:
r = self._get_main_citizen_profile_json(self.details.citizen_id).json()
if not rang:
rang = r['military']['militaryData']['air']['rankNumber']
rang = r['military']['militaryData']['aircraft']['rankNumber']
if elite is None:
elite = r['citizenAttributes']['level'] > 100
@ -1790,7 +1772,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 +1792,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 +1866,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 +1882,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 +1899,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 +1918,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 +1956,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 +1971,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 +2235,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 +2260,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 +2335,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 +2451,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 +2462,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 +2480,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 +2526,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 +2554,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)
@ -568,8 +548,10 @@ class Reporter:
if self.__to_update:
for unreported_data in self.__to_update:
unreported_data.update(player_id=self.citizen_id, key=self.key)
unreported_data = utils.json.load(utils.json.dumps(unreported_data, cls=MyJSONEncoder, sort_keys=True))
self._req.post("{}/bot/update".format(self.url), json=unreported_data)
self.__to_update.clear()
data = utils.json.loads(utils.json.dumps(data, cls=MyJSONEncoder, sort_keys=True))
r = self._req.post("{}/bot/update".format(self.url), json=data)
return r
@ -614,7 +596,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 +613,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 +628,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 +642,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 +673,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 +691,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 +725,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 +753,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 +787,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 +821,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 +885,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 +945,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'}

184
erepublik/constants.py Normal file
View File

@ -0,0 +1,184 @@
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,
'foodRaw': 7, 'weaponRaw': 12, 'weaponsRaw': 12, 'houseRaw': 18, 'aircraftRaw': 24,
'frm': 7, 'wrm': 12, 'hrm': 18, 'arm': 24,
'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: "foodRaw", 8: "FRM q2", 9: "FRM q3", 10: "FRM q4", 11: "FRM q5",
12: "weaponsRaw", 13: "WRM q2", 14: "WRM q3", 15: "WRM q4", 16: "WRM q5",
18: "houseRaw", 19: "HRM q2", 20: "HRM q3", 21: "HRM q4", 22: "HRM q5",
24: "aircraftRaw", 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,46 @@ 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 __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__ = ['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 +61,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:
@ -247,7 +184,7 @@ def send_email(name: str, content: List[Any], player=None, local_vars: Mapping[A
from erepublik import Citizen
file_content_template = "<html><head><title>{title}</title></head><body>{body}</body></html>"
if isinstance(player, Citizen):
if isinstance(player, Citizen) and player.r:
resp = write_request(player.r, is_error=True)
else:
resp = {"name": "None.html", "mimetype": "text/html",
@ -300,7 +237,7 @@ def normalize_html_json(js: str) -> str:
def caught_error(e: Exception):
process_error(str(e), "Unclassified", sys.exc_info(), None, COMMIT_ID, False)
process_error(str(e), "Unclassified", sys.exc_info(), interactive=False)
def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commit_id: str = None,
@ -322,7 +259,7 @@ def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commi
"""
type_, value_, traceback_ = exc_info
content = [log_info]
content += [f"eRepublik version {VERSION}, commit id {COMMIT_ID}"]
content += [f"eRepublik version {VERSION}"]
if commit_id:
content += [f"Commit id {commit_id}"]
content += [str(value_), str(type_), ''.join(traceback.format_tb(traceback_))]

View File

@ -1,5 +0,0 @@
#!/bin/bash
commit=$(git log -1 --pretty=format:%h)
sed -i.bak -E "s|__commit_id__ = \".+\"|__commit_id__ = \"${commit}\"|g" erepublik/__init__.py
rm erepublik/__init__.py.bak

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.20.3.5
current_version = 0.21.0
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?

View File

@ -11,7 +11,7 @@ with open('README.rst') as readme_file:
with open('HISTORY.rst') as history_file:
history = history_file.read()
requirements = ['pytz==2020.1', 'requests==2.23.0']
requirements = ['pytz==2020.1', 'requests==2.24.0']
setup_requirements = []
@ -43,6 +43,6 @@ setup(
test_suite='tests',
tests_require=test_requirements,
url='https://github.com/eeriks/erepublik/',
version='0.20.3.5',
version='0.21.0',
zip_safe=False,
)