Bugfixes and inventory redone

This commit is contained in:
Eriks K 2021-01-05 15:41:51 +02:00
parent 1e93006c3d
commit 8b9ee5042d
7 changed files with 182 additions and 90 deletions

View File

@ -2,6 +2,13 @@
History History
======= =======
0.23.4 (2021-01-05)
-------------------
* Added expiration data to inventory items
* Inventory is now based on `classes.Inventory`
* Requirement update to make them more flexible regarding versions required
* Restructured inventory
0.23.3 (2020-12-17) 0.23.3 (2020-12-17)
------------------- -------------------
* Fixed carpet bombing * Fixed carpet bombing
@ -29,7 +36,7 @@ History
0.23.0 (2020-11-26) 0.23.0 (2020-11-26)
------------------- -------------------
* ***0.23 - last supported version for Python 3.7.*** * ***0.23 - last officially supported version for Python 3.7.***
* Added `Config.maverick` switch, to allow/deny automated fighting in non native divisions if the player has MaverickPack * Added `Config.maverick` switch, to allow/deny automated fighting in non native divisions if the player has MaverickPack
* Added `CitizenMedia.get_article(article_id:int)` method to get article data * Added `CitizenMedia.get_article(article_id:int)` method to get article data
* Added `CitizenMedia.delete_article(article_id:int)` method to delete article * Added `CitizenMedia.delete_article(article_id:int)` method to delete article

View File

@ -2,7 +2,7 @@ import re
import sys import sys
import warnings import warnings
import weakref import weakref
from datetime import datetime, timedelta from datetime import datetime, timedelta, time
from decimal import Decimal from decimal import Decimal
from itertools import product from itertools import product
from threading import Event from threading import Event
@ -11,7 +11,7 @@ from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, Union
from requests import HTTPError, RequestException, Response from requests import HTTPError, RequestException, Response
from . import access_points, classes, constants, utils from . import access_points, classes, constants, utils, types
from .classes import OfferItem from .classes import OfferItem
@ -20,9 +20,7 @@ class BaseCitizen(access_points.CitizenAPI):
_last_inventory_update: datetime = constants.min_datetime _last_inventory_update: datetime = constants.min_datetime
promos: Dict[str, datetime] = None promos: Dict[str, datetime] = None
inventory: Dict[str, Dict[str, Dict[int, Dict[str, Union[str, int, float]]]]] inventory: classes.Inventory
inventory_status: Dict[str, int]
boosters: Dict[int, Dict[int, int]] = {50: {}, 100: {}}
ot_points: int = 0 ot_points: int = 0
food: Dict[str, int] = {"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0, "total": 0} food: Dict[str, int] = {"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0, "total": 0}
@ -65,8 +63,7 @@ class BaseCitizen(access_points.CitizenAPI):
self.config.email = email self.config.email = email
self.config.password = password self.config.password = password
self.inventory = {} self.inventory = classes.Inventory()
self.inventory_status = dict(used=0, total=0)
self.wheel_of_fortune = False self.wheel_of_fortune = False
def get_csrf_token(self): def get_csrf_token(self):
@ -248,7 +245,7 @@ class BaseCitizen(access_points.CitizenAPI):
""" """
self._update_inventory_data(self._get_economy_inventory_items().json()) self._update_inventory_data(self._get_economy_inventory_items().json())
def get_inventory(self, force: bool = False): def get_inventory(self, force: bool = False) -> classes.Inventory:
if utils.good_timedelta(self._last_inventory_update, timedelta(minutes=2)) < self.now or force: if utils.good_timedelta(self._last_inventory_update, timedelta(minutes=2)) < self.now or force:
self.update_inventory() self.update_inventory()
return self.inventory return self.inventory
@ -257,17 +254,25 @@ class BaseCitizen(access_points.CitizenAPI):
if not isinstance(inv_data, dict): if not isinstance(inv_data, dict):
raise TypeError("Parameter `inv_data` must be dict not '{type(data)}'!") raise TypeError("Parameter `inv_data` must be dict not '{type(data)}'!")
def _expire_value_to_python(_expire_value: str) -> Dict[str, Union[int, datetime]]:
_data = re.search(
r'((?P<amount>\d+) item\(s\) )?[eE]xpires? on Day (?P<eday>\d,\d{3}), (?P<time>\d\d:\d\d)',
_expire_value).groupdict()
eday = utils.date_from_eday(int(_data['eday'].replace(',', '')))
dt = constants.erep_tz.localize(datetime.combine(eday, time(*[int(_) for _ in _data['time'].split(':')])))
return {'amount': _data.get('amount'), 'expiration': dt}
status = inv_data.get("inventoryStatus", {}) status = inv_data.get("inventoryStatus", {})
if status: if status:
self.inventory_status.clear() self.inventory.used = status.get("usedStorage")
self.inventory_status.update(used=status.get("usedStorage"), total=status.get("totalStorage")) self.inventory.total = status.get("totalStorage")
data = inv_data.get('inventoryItems', {}) data = inv_data.get('inventoryItems', {})
if not data: if not data:
return return
self._last_inventory_update = self.now self._last_inventory_update = self.now
self.food.update({"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0}) self.food.update({"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0})
self.eb_small = self.eb_double = self.eb_normal = 0 self.eb_small = self.eb_double = self.eb_normal = 0
active_items: Dict[str, Dict[int, Dict[str, Union[str, int]]]] = {} active_items: types.InvFinal = {}
if data.get("activeEnhancements", {}).get("items", {}): if data.get("activeEnhancements", {}).get("items", {}):
for item_data in data.get("activeEnhancements", {}).get("items", {}).values(): for item_data in data.get("activeEnhancements", {}).get("items", {}).values():
if item_data.get('token'): if item_data.get('token'):
@ -278,29 +283,34 @@ class BaseCitizen(access_points.CitizenAPI):
kind = constants.INDUSTRIES[constants.INDUSTRIES[kind]] kind = constants.INDUSTRIES[constants.INDUSTRIES[kind]]
if kind not in active_items: if kind not in active_items:
active_items[kind] = {} active_items[kind] = {}
expiration_info = []
if item_data.get('attributes').get('expirationInfo'):
expire_info = item_data.get('attributes').get('expirationInfo')
expiration_info = [_expire_value_to_python(v) for v in expire_info['value']]
icon = item_data['icon'] if item_data[ icon = item_data['icon'] if item_data[
'icon'] else "//www.erepublik.net/images/modules/manager/tab_storage.png" 'icon'] else "//www.erepublik.net/images/modules/manager/tab_storage.png"
item_data = dict(name=item_data.get("name"), time_left=item_data['active']['time_left'], icon=icon, inv_item: types.InvFinalItem = dict(
kind=kind, name=item_data.get("name"), time_left=item_data['active']['time_left'], icon=icon,
quality=item_data.get("quality", 0)) kind=kind, expiration=expiration_info, quality=item_data.get("quality", 0)
)
if item_data.get('isPackBooster'): if item_data.get('isPackBooster'):
active_items[kind].update({0: item_data}) active_items[kind].update({0: inv_item})
else: else:
active_items[kind].update({item_data.get("quality"): item_data}) active_items[kind].update({inv_item.get("quality"): inv_item})
final_items: Dict[str, Dict[int, Dict[str, Union[str, int]]]] = {} final_items: types.InvFinal = {}
boosters: types.InvBooster = {}
if data.get("finalProducts", {}).get("items", {}): if data.get("finalProducts", {}).get("items", {}):
for item_data in data.get("finalProducts", {}).get("items", {}).values(): for item_data in data.get("finalProducts", {}).get("items", {}).values():
is_booster: bool = False
name = item_data['name'] name = item_data['name']
if item_data.get('type'): if item_data.get('type'):
if item_data.get('type') in ['damageBoosters', "aircraftDamageBoosters"]: # in ['damageBoosters', "aircraftDamageBoosters", 'prestigePointsBoosters']
kind = f"{item_data['type']}{item_data['quality']}" if item_data.get('type').endswith('Boosters'):
if item_data['quality'] == 5: is_booster = True
self.boosters[50].update({item_data['duration']: item_data['amount']}) kind = item_data['type']
elif item_data['quality'] == 10:
self.boosters[100].update({item_data['duration']: item_data['amount']})
delta = item_data['duration'] delta = item_data['duration']
if delta // 3600: if delta // 3600:
@ -337,6 +347,12 @@ class BaseCitizen(access_points.CitizenAPI):
if constants.INDUSTRIES[kind]: if constants.INDUSTRIES[kind]:
kind = constants.INDUSTRIES[constants.INDUSTRIES[kind]] kind = constants.INDUSTRIES[constants.INDUSTRIES[kind]]
if is_booster:
if kind not in boosters:
boosters[kind] = {}
if item_data.get('quality', 0) not in boosters[kind]:
boosters[kind][item_data['quality']] = {}
else:
if kind not in final_items: if kind not in final_items:
final_items[kind] = {} final_items[kind] = {}
@ -355,10 +371,23 @@ class BaseCitizen(access_points.CitizenAPI):
icon = "/images/modules/pvp/ghost_boosters/icon_booster_30_60.png" icon = "/images/modules/pvp/ghost_boosters/icon_booster_30_60.png"
else: else:
icon = "//www.erepublik.net/images/modules/manager/tab_storage.png" icon = "//www.erepublik.net/images/modules/manager/tab_storage.png"
_item_data = dict(kind=kind, quality=item_data.get('quality', 0), amount=item_data.get('amount', 0),
durability=item_data.get('duration', 0), icon=icon, name=name) expiration_info = []
if item_data.get('type') in ('damageBoosters', "aircraftDamageBoosters"): if item_data.get('attributes'):
_item_data = {_item_data['durability']: _item_data} if item_data.get('attributes').get('expirationInfo'):
expire_info = item_data.get('attributes').get('expirationInfo')
expiration_info = [_expire_value_to_python(v) for v in expire_info['value']]
elif item_data.get('attributes').get('expiration'):
_exp = item_data.get('attributes').get('expiration')
exp_dt = (utils.date_from_eday(int(_exp['value'].replace(',', ''))))
expiration_info = [{'amount': item_data.get('amount'), 'expiration': exp_dt}]
_inv_item: Dict[int, types.InvFinalItem]
inv_item: types.InvFinalItem = dict(
kind=kind, quality=item_data.get('quality', 0), icon=icon, expiration=expiration_info,
amount=item_data.get('amount'), durability=item_data.get('duration', 0), name=name
)
if is_booster:
_inv_item = {inv_item['durability']: inv_item}
else: else:
if item_data.get('type') == 'bomb': if item_data.get('type') == 'bomb':
firepower = 0 firepower = 0
@ -367,11 +396,14 @@ class BaseCitizen(access_points.CitizenAPI):
except AttributeError: except AttributeError:
pass pass
finally: finally:
_item_data.update(fire_power=firepower) inv_item.update(fire_power=firepower)
_item_data = {_item_data['quality']: _item_data} _inv_item = {inv_item['quality']: inv_item}
final_items[kind].update(_item_data) if is_booster:
boosters[kind][inv_item['quality']].update(_inv_item)
else:
final_items[kind].update(_inv_item)
raw_materials: Dict[str, Dict[int, Dict[str, Union[str, int]]]] = {} raw_materials: types.InvRaw = {}
if data.get("rawMaterials", {}).get("items", {}): if data.get("rawMaterials", {}).get("items", {}):
for item_data in data.get("rawMaterials", {}).get("items", {}).values(): for item_data in data.get("rawMaterials", {}).get("items", {}).values():
if item_data['isPartial']: if item_data['isPartial']:
@ -401,8 +433,11 @@ class BaseCitizen(access_points.CitizenAPI):
offers[kind] = {} offers[kind] = {}
offers[kind].update(offer_data) offers[kind].update(offer_data)
self.inventory.clear() self.inventory.active = active_items
self.inventory.update(active=active_items, final=final_items, raw=raw_materials, offers=offers) self.inventory.final = final_items
self.inventory.boosters = boosters
self.inventory.raw = raw_materials
self.inventory.offers = offers
self.food["total"] = sum([self.food[q] * constants.FOOD_ENERGY[q] for q in constants.FOOD_ENERGY]) self.food["total"] = sum([self.food[q] * constants.FOOD_ENERGY[q] for q in constants.FOOD_ENERGY])
def write_log(self, *args, **kwargs): def write_log(self, *args, **kwargs):
@ -946,13 +981,13 @@ class CitizenCompanies(BaseCitizen):
raw_factories = wam_holding.get_wam_companies(raw_factory=True) raw_factories = wam_holding.get_wam_companies(raw_factory=True)
fin_factories = wam_holding.get_wam_companies(raw_factory=False) fin_factories = wam_holding.get_wam_companies(raw_factory=False)
free_inventory = self.inventory_status["total"] - self.inventory_status["used"] free_inventory = self.inventory.total - self.inventory.used
wam_list = raw_factories + fin_factories wam_list = raw_factories + fin_factories
wam_list = wam_list[:self.energy.food_fights] wam_list = wam_list[:self.energy.food_fights]
if int(free_inventory * 0.75) < self.my_companies.get_needed_inventory_usage(wam_list): if int(free_inventory * 0.75) < self.my_companies.get_needed_inventory_usage(wam_list):
self.update_inventory() self.update_inventory()
free_inventory = self.inventory_status["total"] - self.inventory_status["used"] free_inventory = self.inventory.total - self.inventory.used
while wam_list and free_inventory < self.my_companies.get_needed_inventory_usage(wam_list): while wam_list and free_inventory < self.my_companies.get_needed_inventory_usage(wam_list):
wam_list.pop(-1) wam_list.pop(-1)
@ -1021,7 +1056,7 @@ class CitizenEconomy(CitizenTravel):
def check_house_durability(self) -> Dict[int, datetime]: def check_house_durability(self) -> Dict[int, datetime]:
ret = {} ret = {}
inv = self.get_inventory() inv = self.get_inventory()
for house_quality, active_house in inv['active'].get('House', {}).items(): for house_quality, active_house in inv.active.get('House', {}).items():
till = utils.good_timedelta(self.now, timedelta(seconds=active_house['time_left'])) till = utils.good_timedelta(self.now, timedelta(seconds=active_house['time_left']))
ret.update({house_quality: till}) ret.update({house_quality: till})
return ret return ret
@ -1030,7 +1065,7 @@ class CitizenEconomy(CitizenTravel):
original_region = self.details.current_country, self.details.current_region original_region = self.details.current_country, self.details.current_region
ok_to_activate = False ok_to_activate = False
inv = self.get_inventory() inv = self.get_inventory()
if not inv['final'].get('House', {}).get(q, {}): if not inv.final.get('House', {}).get(q, {}):
countries = [self.details.citizenship, ] countries = [self.details.citizenship, ]
if self.details.current_country != self.details.citizenship: if self.details.current_country != self.details.citizenship:
countries.append(self.details.current_country) countries.append(self.details.current_country)
@ -1084,7 +1119,8 @@ class CitizenEconomy(CitizenTravel):
r: Dict[str, Any] = self._post_economy_activate_house(quality).json() r: Dict[str, Any] = self._post_economy_activate_house(quality).json()
self._update_inventory_data(r) self._update_inventory_data(r)
if r.get("status") and not r.get("error"): if r.get("status") and not r.get("error"):
house: Dict[str, Union[int, str]] = self.get_inventory()['active']['House'][quality] inventory = self.get_inventory()
house = inventory.active.get('House', {}).get(quality)
time_left = timedelta(seconds=house["time_left"]) time_left = timedelta(seconds=house["time_left"])
active_until = utils.good_timedelta(self.now, time_left) active_until = utils.good_timedelta(self.now, time_left)
self._report_action( self._report_action(
@ -1164,12 +1200,14 @@ class CitizenEconomy(CitizenTravel):
self.write_log(f"Trying to sell unsupported industry {industry}") self.write_log(f"Trying to sell unsupported industry {industry}")
_inv_qlt = quality if industry in [1, 2, 3, 4, 23] else 0 _inv_qlt = quality if industry in [1, 2, 3, 4, 23] else 0
_kind = 'final' if industry in [1, 2, 4, 23] else 'raw' final_kind = industry in [1, 2, 4, 23]
inventory = self.get_inventory() inventory = self.get_inventory()
items = inventory[_kind].get(constants.INDUSTRIES[industry], {_inv_qlt: {'amount': 0}}) items = (inventory.final if final_kind else inventory.raw).get(constants.INDUSTRIES[industry],
{_inv_qlt: {'amount': 0}})
if items[_inv_qlt]['amount'] < amount: if items[_inv_qlt]['amount'] < amount:
inventory = self.get_inventory(True) inventory = self.get_inventory(True)
items = inventory[_kind].get(constants.INDUSTRIES[industry], {_inv_qlt: {'amount': 0}}) items = (inventory.final if final_kind else inventory.raw).get(constants.INDUSTRIES[industry],
{_inv_qlt: {'amount': 0}})
if items[_inv_qlt]['amount'] < amount: if items[_inv_qlt]['amount'] < amount:
self._report_action("ECONOMY_SELL_PRODUCTS", "Unable to sell! Not enough items in storage!", self._report_action("ECONOMY_SELL_PRODUCTS", "Unable to sell! Not enough items in storage!",
kwargs=dict(inventory=items[_inv_qlt], amount=amount)) kwargs=dict(inventory=items[_inv_qlt], amount=amount))
@ -1788,7 +1826,7 @@ class CitizenMilitary(CitizenTravel):
self._report_action("IP_BLACKLISTED", "Fighting is not allowed from restricted IP!") self._report_action("IP_BLACKLISTED", "Fighting is not allowed from restricted IP!")
return 1 return 1
if not division.is_air and self.config.boosters: if not division.is_air and self.config.boosters:
self.activate_dmg_booster() self.activate_damage_booster(not division.is_air)
if side is None: if side is None:
side = battle.defender if self.details.citizenship in battle.defender.allies + [ side = battle.defender if self.details.citizenship in battle.defender.allies + [
battle.defender.country] else battle.invader battle.defender.country] else battle.invader
@ -1884,7 +1922,8 @@ class CitizenMilitary(CitizenTravel):
return hits, err, damage return hits, err, damage
@utils.wait_for_lock @utils.wait_for_lock
def deploy_bomb(self, battle: classes.Battle, division: classes.BattleDivision, bomb_id: int, inv_side: bool, count: int = 1) -> Optional[int]: def deploy_bomb(self, battle: classes.Battle, division: classes.BattleDivision, bomb_id: int, inv_side: bool,
count: int = 1) -> Optional[int]:
"""Deploy bombs in a battle for given side. """Deploy bombs in a battle for given side.
:param battle: Battle :param battle: Battle
@ -1979,34 +2018,43 @@ class CitizenMilitary(CitizenTravel):
return utils.calculate_hit(0, rang, True, elite, ne, 0, 20 if weapon else 0) return utils.calculate_hit(0, rang, True, elite, ne, 0, 20 if weapon else 0)
def activate_dmg_booster(self): def activate_damage_booster(self, ground: bool = True):
if self.config.boosters: kind = 'damageBoosters' if ground else 'aircraftDamageBoosters'
if not self.get_active_ground_damage_booster(): if self.config.boosters and not self.get_active_damage_booster(ground):
duration = 0 booster: Optional[types.InvFinalItem] = None
for length, amount in self.boosters[50].items(): for quality, data in sorted(self.inventory.boosters.get(kind, {}).items(), key=lambda x: x[0]):
if amount > 2: for _duration, _booster in sorted(data.items(), key=lambda y: y[0]):
duration = length if _booster.get('amount') > 2:
booster = _booster
break break
if duration: break
self._report_action("MILITARY_BOOSTER", f"Activated 50% {duration / 60}h damage booster") if booster:
self._post_economy_activate_booster(5, duration, "damage") self._report_action("MILITARY_BOOSTER", f"Activated {booster['name']}")
self._post_economy_activate_booster(5, booster['durability'], 'damage')
def get_active_damage_booster(self, ground: bool = True) -> int:
kind = 'damageBoosters' if ground else 'aircraftDamageBoosters'
inventory = self.get_inventory()
boosters = inventory.active.get(kind, {})
quality = 0
for q, boost in boosters.items():
if boost['quality'] * 10 > quality:
quality = boost['quality'] * 10
return quality
def get_active_ground_damage_booster(self) -> int: def get_active_ground_damage_booster(self) -> int:
inventory = self.get_inventory() return self.get_active_damage_booster(True)
quality = 0
if inventory['active'].get('damageBoosters', {}).get(10): def get_active_air_damage_booster(self) -> int:
quality = 100 return self.get_active_damage_booster(False)
elif inventory['active'].get('damageBoosters', {}).get(5):
quality = 50
return quality
def activate_battle_effect(self, battle_id: int, kind: str) -> Response: def activate_battle_effect(self, battle_id: int, kind: str) -> Response:
self._report_action('MILITARY_BOOSTER', f'Activated {kind} booster') self._report_action('MILITARY_BOOSTER', f'Activated {kind} booster')
return self._post_main_activate_battle_effect(battle_id, kind, self.details.citizen_id) return self._post_main_activate_battle_effect(battle_id, kind, self.details.citizen_id)
def activate_pp_booster(self, battle_id: int) -> Response: def activate_pp_booster(self) -> Response:
self._report_action('MILITARY_BOOSTER', 'Activated PrestigePoint booster') self._report_action('MILITARY_BOOSTER', 'Activated PrestigePoint booster')
return self._post_military_fight_activate_booster(battle_id, 1, 180, "prestige_points") return self._post_economy_activate_booster(1, 180, "prestige_points")
def _rw_choose_side(self, battle: classes.Battle, side: classes.BattleSide) -> Response: def _rw_choose_side(self, battle: classes.Battle, side: classes.BattleSide) -> Response:
return self._post_main_battlefield_travel(side.id, battle.id) return self._post_main_battlefield_travel(side.id, battle.id)
@ -2181,7 +2229,8 @@ class CitizenPolitics(BaseCitizen):
self._report_action('POLITIC_PARTY_PRESIDENT', 'Applied for party president elections') self._report_action('POLITIC_PARTY_PRESIDENT', 'Applied for party president elections')
return self._get_candidate_party(self.politics.party_slug) return self._get_candidate_party(self.politics.party_slug)
else: else:
self._report_action('POLITIC_CONGRESS', 'Unable to apply for party president elections - not a party member') self._report_action('POLITIC_CONGRESS',
'Unable to apply for party president elections - not a party member')
return None return None
def candidate_for_congress(self, presentation: str = "") -> Optional[Response]: def candidate_for_congress(self, presentation: str = "") -> Optional[Response]:
@ -2630,13 +2679,13 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard,
def send_state_update(self): def send_state_update(self):
data = dict(xp=self.details.xp, cc=self.details.cc, gold=self.details.gold, pp=self.details.pp, data = dict(xp=self.details.xp, cc=self.details.cc, gold=self.details.gold, pp=self.details.pp,
inv_total=self.inventory_status['total'], inv=self.inventory_status['used'], inv_total=self.inventory.total, inv=self.inventory.used,
hp_limit=self.energy.limit, hp_limit=self.energy.limit,
hp_interval=self.energy.interval, hp_available=self.energy.available, food=self.food['total'], ) hp_interval=self.energy.interval, hp_available=self.energy.available, food=self.food['total'], )
self.reporter.send_state_update(**data) self.reporter.send_state_update(**data)
def send_inventory_update(self): def send_inventory_update(self):
self.reporter.report_action("INVENTORY", json_val=self.get_inventory(True)) self.reporter.report_action("INVENTORY", json_val=self.get_inventory(True).as_dict)
def send_my_companies_update(self): def send_my_companies_update(self):
self.reporter.report_action('COMPANIES', json_val=self.my_companies.as_dict) self.reporter.report_action('COMPANIES', json_val=self.my_companies.as_dict)
@ -2774,7 +2823,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard,
for holding in regions.values(): for holding in regions.values():
raw_usage = holding.get_wam_raw_usage() raw_usage = holding.get_wam_raw_usage()
free_storage = self.inventory_status['total'] - self.inventory_status['used'] free_storage = self.inventory.total - self.inventory.used
if (raw_usage['frm'] + raw_usage['wrm']) * 100 > free_storage: if (raw_usage['frm'] + raw_usage['wrm']) * 100 > free_storage:
self._report_action('WAM_UNAVAILABLE', 'Not enough storage!') self._report_action('WAM_UNAVAILABLE', 'Not enough storage!')
continue continue

View File

@ -7,11 +7,12 @@ from typing import Any, Dict, Generator, Iterable, List, NamedTuple, NoReturn, T
from requests import Response, Session, post from requests import Response, Session, post
from . import constants, utils from . import constants, utils, types
__all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException', __all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException',
'ErepublikNetworkException', 'EnergyToFight', 'ErepublikNetworkException', 'EnergyToFight',
'Holding', 'MyCompanies', 'MyJSONEncoder', 'OfferItem', 'Politics', 'Reporter', 'TelegramReporter'] 'Holding', 'MyCompanies', 'MyJSONEncoder', 'OfferItem', 'Politics', 'Reporter', 'TelegramReporter',
'Inventory']
class ErepublikException(Exception): class ErepublikException(Exception):
@ -396,11 +397,11 @@ class Config:
self.spin_wheel_of_fortune = False self.spin_wheel_of_fortune = False
@property @property
def as_dict(self): def as_dict(self) -> Dict[str, Union[bool, int, str, List[str]]]:
return dict(email=self.email, work=self.work, train=self.train, wam=self.wam, ot=self.ot, return dict(email=self.email, work=self.work, train=self.train, wam=self.wam, ot=self.ot,
auto_sell=self.auto_sell, auto_sell_all=self.auto_sell_all, employees=self.employees, auto_sell=self.auto_sell, auto_sell_all=self.auto_sell_all, employees=self.employees,
fight=self.fight, air=self.air, ground=self.ground, all_in=self.all_in, fight=self.fight, air=self.air, ground=self.ground, all_in=self.all_in,
next_energy=self.next_energy, boosters=self.boosters, travel_to_fight=self.travel_to_fight, next_energy=self.next_energy, travel_to_fight=self.travel_to_fight,
always_travel=self.always_travel, epic_hunt=self.epic_hunt, epic_hunt_ebs=self.epic_hunt_ebs, always_travel=self.always_travel, epic_hunt=self.epic_hunt, epic_hunt_ebs=self.epic_hunt_ebs,
rw_def_side=self.rw_def_side, interactive=self.interactive, maverick=self.maverick, rw_def_side=self.rw_def_side, interactive=self.interactive, maverick=self.maverick,
continuous_fighting=self.continuous_fighting, auto_buy_raw=self.auto_buy_raw, continuous_fighting=self.continuous_fighting, auto_buy_raw=self.auto_buy_raw,
@ -454,7 +455,7 @@ class Energy:
return self.recovered + self.recoverable return self.recovered + self.recoverable
@property @property
def as_dict(self): def as_dict(self) -> Dict[str, Union[int, datetime.datetime, bool]]:
return dict(limit=self.limit, interval=self.interval, recoverable=self.recoverable, recovered=self.recovered, return dict(limit=self.limit, interval=self.interval, recoverable=self.recoverable, recovered=self.recovered,
reference_time=self.reference_time, food_fights=self.food_fights, 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_recoverable_full=self.is_recoverable_full, is_recovered_full=self.is_recovered_full,
@ -509,7 +510,7 @@ class Details:
return next_level_up - self.xp return next_level_up - self.xp
@property @property
def as_dict(self): def as_dict(self) -> Dict[str, Union[int, float, str, constants.Country, bool]]:
return dict(xp=self.xp, cc=self.cc, pp=self.pp, pin=self.pin, gold=self.gold, next_pp=self.next_pp, 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, citizen_id=self.citizen_id, citizenship=self.citizenship, current_region=self.current_region,
current_country=self.current_country, residence_region=self.residence_region, current_country=self.current_country, residence_region=self.residence_region,
@ -528,7 +529,7 @@ class Politics:
is_country_president: bool = False is_country_president: bool = False
@property @property
def as_dict(self): def as_dict(self) -> Dict[str, Union[bool, int, str]]:
return dict(is_party_member=self.is_party_member, party_id=self.party_id, party_slug=self.party_slug, 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_party_president=self.is_party_president, is_congressman=self.is_congressman,
is_country_president=self.is_country_president) is_country_president=self.is_country_president)
@ -566,7 +567,7 @@ class Reporter:
return self.citizen.details.citizen_id return self.citizen.details.citizen_id
@property @property
def as_dict(self): def as_dict(self) -> Dict[str, Union[bool, int, str, List[Dict[Any, Any]]]]:
return dict(name=self.name, email=self.email, citizen_id=self.citizen_id, key=self.key, allowed=self.allowed, return dict(name=self.name, email=self.email, citizen_id=self.citizen_id, key=self.key, allowed=self.allowed,
queue=self.__to_update) queue=self.__to_update)
@ -730,7 +731,7 @@ class BattleSide:
return self.country.iso return self.country.iso
@property @property
def as_dict(self): def as_dict(self) -> Dict[str, Union[int, constants.Country, bool, List[constants.Country]]]:
return dict(points=self.points, country=self.country, is_defender=self.is_defender, allies=self.allies, return dict(points=self.points, country=self.country, is_defender=self.is_defender, allies=self.allies,
deployed=self.deployed) deployed=self.deployed)
@ -1024,3 +1025,27 @@ class OfferItem(NamedTuple):
amount: int = 0 amount: int = 0
offer_id: int = 0 offer_id: int = 0
citizen_id: int = 0 citizen_id: int = 0
class Inventory:
final: types.InvFinal
active: types.InvFinal
boosters: types.InvFinal
raw: types.InvRaw
market: types.InvRaw
used: int
total: int
def __init__(self):
self.active = {}
self.final = {}
self.boosters = {}
self.raw = {}
self.offers = {}
self.used = 0
self.total = 0
@property
def as_dict(self) -> Dict[str, Union[types.InvFinal, types.InvRaw, int]]:
return dict(active=self.active, final=self.final, boosters=self.boosters, raw=self.raw, offers=self.offers,
total=self.total, used=self.used)

7
erepublik/types.py Normal file
View File

@ -0,0 +1,7 @@
from datetime import datetime
from typing import Dict, Union, List
InvFinalItem = Dict[str, Union[str, int, List[Dict[str, Union[int, datetime]]]]]
InvBooster = Dict[str, Dict[int, Dict[int, InvFinalItem]]]
InvFinal = Dict[str, Dict[int, InvFinalItem]]
InvRaw = Dict[str, Dict[int, Dict[str, Union[str, int]]]]

View File

@ -10,7 +10,7 @@ import unicodedata
import warnings import warnings
from decimal import Decimal from decimal import Decimal
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Union
import requests import requests
@ -64,7 +64,9 @@ def good_timedelta(dt: datetime.datetime, td: datetime.timedelta) -> datetime.da
return constants.erep_tz.normalize(dt + td) return constants.erep_tz.normalize(dt + td)
def eday_from_date(date: Union[datetime.date, datetime.datetime] = now()) -> int: def eday_from_date(date: Union[datetime.date, datetime.datetime] = None) -> int:
if date is None:
date = now()
if isinstance(date, datetime.date): if isinstance(date, datetime.date):
date = datetime.datetime.combine(date, datetime.time(0, 0, 0)) date = datetime.datetime.combine(date, datetime.time(0, 0, 0))
return (date - datetime.datetime(2007, 11, 20, 0, 0, 0)).days return (date - datetime.datetime(2007, 11, 20, 0, 0, 0)).days
@ -253,7 +255,7 @@ def caught_error(e: Exception):
def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commit_id: str = None, def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commit_id: str = None,
interactive: Optional[bool] = None): interactive: bool = None):
""" """
Process error logging and email sending to developer Process error logging and email sending to developer
:param interactive: Should print interactively :param interactive: Should print interactively
@ -377,6 +379,7 @@ def get_final_hit_dmg(base_dmg: Union[Decimal, float, str], rang: int,
dmg = dmg * 11 / 10 dmg = dmg * 11 / 10
return Decimal(dmg) return Decimal(dmg)
# def _clear_up_battle_memory(battle): # def _clear_up_battle_memory(battle):
# del battle.invader._battle, battle.defender._battle # del battle.invader._battle, battle.defender._battle
# for div_id, division in battle.div.items(): # for div_id, division in battle.div.items():
@ -404,4 +407,5 @@ def wait_for_lock(function):
raise e raise e
instance.concurrency_available.set() instance.concurrency_available.set()
return ret return ret
return wrapper return wrapper

View File

@ -1,18 +1,18 @@
bump2version==1.0.1 bump2version==1.0.1
coverage==5.3 coverage==5.3.1
edx-sphinx-theme==1.5.0 edx-sphinx-theme==1.6.0
flake8==3.8.4 flake8==3.8.4
ipython>=7.19.0 ipython>=7.19.0
isort==5.6.4 isort==5.7.0
pip==20.3.3 pip==20.3.3
PyInstaller==4.1 PyInstaller==4.1
pytz==2020.4 pytz>=2020.0
pytest==6.2.1 pytest==6.2.1
responses==0.12.1 responses==0.12.1
setuptools==51.0.0 setuptools==51.1.1
Sphinx==3.3.1 Sphinx==3.4.2
requests>=2.24.0,<2.26.0 requests>=2.24.0,<2.26.0
PySocks==1.7.1 PySocks==1.7.1
twine==3.2.0 twine==3.3.0
wheel==0.36.2 wheel==0.36.2
pur==5.3.0 pur==5.3.0

View File

@ -12,7 +12,7 @@ with open('HISTORY.rst') as history_file:
history = history_file.read() history = history_file.read()
requirements = [ requirements = [
'pytz==2020.4', 'pytz>=2020.0',
'requests>=2.24.0,<2.26.0', 'requests>=2.24.0,<2.26.0',
'PySocks==1.7.1' 'PySocks==1.7.1'
] ]