diff --git a/erepublik/citizen.py b/erepublik/citizen.py
index 89962ca..666fba0 100644
--- a/erepublik/citizen.py
+++ b/erepublik/citizen.py
@@ -5,7 +5,7 @@ import sys
import threading
import time
from json import loads, dumps
-from typing import Dict, List, Tuple, Any, Union
+from typing import Dict, List, Tuple, Any, Union, Mapping
import requests
from requests import Response, RequestException
@@ -21,9 +21,9 @@ class Citizen(classes.CitizenAPI):
all_battles: Dict[int, classes.Battle] = dict()
countries: Dict[int, Dict[str, Union[str, List[int]]]] = dict()
__last_war_update_data = {}
- __last_full_update: datetime.datetime
+ __last_full_update: datetime.datetime = utils.now().min
- active_fs = False
+ active_fs: bool = False
food = {"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0, "total": 0}
inventory = {"used": 0, "total": 0}
@@ -1669,18 +1669,45 @@ class Citizen(classes.CitizenAPI):
for resident in resp["widgets"]["residents"]["residents"]:
self.add_friend(resident["citizenId"])
- def schedule_attack(self, war_id: int, region_id: int, at_time: datetime) -> None:
+ def schedule_attack(self, war_id: int, region_id: int, region_name: str, at_time: datetime) -> None:
if at_time:
self.sleep(utils.get_sleep_seconds(at_time))
self.get_csrf_token()
- self._launch_battle(war_id, region_id)
+ self.launch_attack(war_id, region_id, region_name)
- def get_active_wars_with_regions(self):
- self._get_country_military(self.countries.get(self.details.citizen_id)["name"])
- raise NotImplementedError
+ def get_active_wars(self, country_id: int = None) -> List[int]:
+ r = self._get_country_military(utils.COUNTRY_LINK.get(country_id or self.details.citizenship))
+ all_war_ids = re.findall(r'//www\.erepublik\.com/en/wars/show/(\d+)"', r.text)
+ return [int(wid) for wid in all_war_ids]
- def _launch_battle(self, war_id: int, region_id: int) -> Response:
- return self._post_wars_attack_region(war_id, region_id)
+ def get_war_status(self, war_id: int) -> Dict[str, Union[bool, Dict[int, str]]]:
+ r = self._get_wars_show(war_id)
+ html = r.text
+ ret = {}
+ reg_re = re.compile(fr'data-war-id="{war_id}" data-region-id="(\d+)" data-region-name="([- \w]+)"')
+ if reg_re.findall(html):
+ ret.update(regions={}, can_attack=True)
+ for reg in reg_re.findall(html):
+ ret["regions"].update({str(reg[0]): reg[1]})
+ elif re.search(r'Join', html):
+ battle_id = re.search(r'Join', html).group(1)
+ ret.update(can_attack=False, battle_id=battle_id)
+ else:
+ ret.update(can_attack=False)
+ return ret
+
+ def get_last_battle_of_war_end_time(self, war_id: int) -> datetime:
+ r = self._get_wars_show(war_id)
+ html = r.text
+ last_battle_id = int(re.search(r'', html).group(1))
+ console = self._post_military_battle_console(last_battle_id, 'warList', 1).json()
+ battle = console.get('list')[0]
+ return utils.localize_dt(datetime.datetime.strptime(battle.get('result').get('end'), "%Y-%m-%d %H:%M:%S"))
+
+ def launch_attack(self, war_id: int, region_id: int, region_name: str):
+ self._post_wars_attack_region(war_id, region_id, region_name)
def state_update_repeater(self):
try:
diff --git a/erepublik/classes.py b/erepublik/classes.py
index 0f3bae2..96f9466 100644
--- a/erepublik/classes.py
+++ b/erepublik/classes.py
@@ -5,7 +5,7 @@ import random
import time
from collections import deque
from json import JSONDecodeError, loads, JSONEncoder
-from typing import Any, Dict, List, Union
+from typing import Any, Dict, List, Union, Mapping, Iterable
from requests import Response, Session
@@ -41,7 +41,7 @@ class MyCompanies:
template = dict(id=0, num_factories=0, region_id=0, companies=[])
for holding_id, holding in holdings.items():
- tmp: Dict[str, Union[List[Any], Any]] = {}
+ tmp: Dict[str, Union[Iterable[Any], Any]] = {}
for key in template:
if key == 'companies':
tmp.update({key: []})
@@ -470,7 +470,7 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
def _get_citizen_daily_assistant(self) -> Response:
return self.get("{}/main/citizenDailyAssistant".format(self.url))
- def _get_city_data_residents(self, city: int, page: int = 1, params: Dict[str, Any] = None) -> Response:
+ def _get_city_data_residents(self, city: int, page: int = 1, params: Mapping[str, Any] = None) -> Response:
if params is None:
params = {}
return self.get("{}/main/city-data/{}/residents".format(self.url, city), params={"currentPage": page, **params})
@@ -549,6 +549,9 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
def _get_weekly_challenge_data(self) -> Response:
return self.get("{}/main/weekly-challenge-data".format(self.url))
+ def _get_wars_show(self, war_id: int) -> Response:
+ return self.get("{}/wars/show/{}".format(self.url, war_id))
+
def _post_activate_battle_effect(self, battle: int, kind: str, citizen_id: int) -> Response:
data = dict(battleId=battle, citizenId=citizen_id, type=kind, _token=self.token)
return self.post("{}/main/fight-activateBattleEffect".format(self.url), data=data)
@@ -747,10 +750,14 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
citizen_subject=subject, _token=self.token, citizen_message=body)
return self.post("{}/main/messages-compose/{}}".format(self.url, url_pk), data=data)
- def _post_military_battle_console(self, battle_id: int, round_id: int, division: int) -> Response:
- data = dict(battleId=battle_id, zoneId=round_id, action="battleStatistics", round=round_id, division=division,
- type="damage", leftPage=1, rightPage=1, _token=self.token)
- return self.post("{}/military/battle-console".format(self.url, battle_id), data=data)
+ def _post_military_battle_console(self, battle_id: int, action: str, page: int = 1, **kwargs) -> Response:
+ data = dict(battleId=battle_id, action=action, _token=self.token)
+ if action == "battleStatistics":
+ data.update(round=kwargs["round_id"], zoneId=kwargs["round_id"], leftPage=page, rightPage=page,
+ division=kwargs["division"], type=kwargs.get("type", 'damage'),)
+ elif action == "warList":
+ data.update(page=page)
+ return self.post("{}/military/battle-console".format(self.url), data=data)
def _post_military_deploy_bomb(self, battle_id: int, bomb_id: int) -> Response:
data = dict(battleId=battle_id, bombId=bomb_id, _token=self.token)
diff --git a/erepublik/utils.py b/erepublik/utils.py
index 8afcbd1..74844a0 100644
--- a/erepublik/utils.py
+++ b/erepublik/utils.py
@@ -11,13 +11,12 @@ from collections import deque
from decimal import Decimal
from json import JSONEncoder
from pathlib import Path
-from typing import Union, Dict, Any, List
+from typing import Union, Any, List, NoReturn, Mapping
import pytz
import requests
from requests import Response
-
__all__ = ["FOOD_ENERGY", "COMMIT_ID", "COUNTRIES", "erep_tz",
"now", "localize_dt", "localize_timestamp", "good_timedelta", "eday_from_date", "date_from_eday",
"get_sleep_seconds", "interactive_sleep", "silent_sleep",
@@ -85,6 +84,19 @@ COUNTRIES = {1: 'Romania', 9: 'Brazil', 10: 'Italy', 11: 'France', 12: 'Germany'
82: 'Cyprus', 83: 'Belarus', 84: 'New Zealand', 164: 'Saudi Arabia', 165: 'Egypt',
166: 'United Arab Emirates', 167: 'Albania', 168: 'Georgia', 169: 'Armenia', 170: 'Nigeria', 171: 'Cuba'}
+COUNTRY_LINK = {1: 'Romania', 9: 'Brazil', 11: 'France', 12: 'Germany', 13: 'Hungary', 82: 'Cyprus', 168: 'Georgia',
+ 15: 'Spain', 23: 'Canada', 26: 'Mexico', 27: 'Argentina', 28: 'Venezuela', 80: 'Montenegro', 24: 'USA',
+ 29: 'United-Kingdom', 50: 'Australia', 47: 'South-Korea',171: 'Cuba', 79: 'Republic-of-Macedonia-FYROM',
+ 30: 'Switzerland', 31: 'Netherlands', 32: 'Belgium', 33: 'Austria', 34: 'Czech-Republic', 35: 'Poland',
+ 36: 'Slovakia', 37: 'Norway', 38: 'Sweden', 39: 'Finland', 40: 'Ukraine', 41: 'Russia', 42: 'Bulgaria',
+ 43: 'Turkey', 44: 'Greece', 45: 'Japan', 48: 'India', 49: 'Indonesia', 78: 'Colombia', 68: 'Singapore',
+ 51: 'South Africa', 52: 'Republic-of-Moldova', 53: 'Portugal', 54: 'Ireland', 55: 'Denmark', 56: 'Iran',
+ 57: 'Pakistan', 58: 'Israel', 59: 'Thailand', 61: 'Slovenia', 63: 'Croatia', 64: 'Chile', 65: 'Serbia',
+ 66: 'Malaysia', 67: 'Philippines', 70: 'Estonia', 165: 'Egypt', 14: 'China', 77: 'Peru', 10: 'Italy',
+ 71: 'Latvia', 72: 'Lithuania', 73: 'North-Korea', 74: 'Uruguay', 75: 'Paraguay', 76: 'Bolivia',
+ 81: 'Republic-of-China-Taiwan', 166: 'United-Arab-Emirates', 167: 'Albania', 69: 'Bosnia-Herzegovina',
+ 169: 'Armenia', 83: 'Belarus', 84: 'New-Zealand', 164: 'Saudi-Arabia', 170: 'Nigeria', }
+
class MyJSONEncoder(JSONEncoder):
def default(self, o):
@@ -107,18 +119,22 @@ class MyJSONEncoder(JSONEncoder):
return super().default(o)
-def now():
+def now() -> datetime.datetime:
return datetime.datetime.now(erep_tz).replace(microsecond=0)
-def localize_timestamp(timestamp: int):
+def localize_timestamp(timestamp: int) -> datetime.datetime:
return datetime.datetime.fromtimestamp(timestamp, erep_tz)
-def localize_dt(dt: Union[datetime.date, datetime.datetime]):
- if isinstance(dt, datetime.date):
- dt = datetime.datetime.combine(dt, datetime.time(0, 0, 0))
- return erep_tz.localize(dt)
+def localize_dt(dt: Union[datetime.date, datetime.datetime]) -> datetime.datetime:
+ try:
+ try:
+ return erep_tz.localize(dt)
+ except AttributeError:
+ return erep_tz.localize(datetime.datetime.combine(dt, datetime.time(0, 0, 0)))
+ except ValueError:
+ return dt.astimezone(erep_tz)
def good_timedelta(dt: datetime.datetime, td: datetime.timedelta) -> datetime.datetime:
@@ -237,7 +253,7 @@ def write_request(response: requests.Response, is_error: bool = False):
"mimetype": "application/json" if ext == "json" else "text/html"}
-def send_email(name: str, content: List[Any], player=None, local_vars: Dict[Any, Any] = None,
+def send_email(name: str, content: List[Any], player=None, local_vars: Mapping[Any, Any] = None,
promo: bool = False, captcha: bool = False):
if local_vars is None:
local_vars = {}
@@ -322,11 +338,11 @@ def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commi
send_email(name, bugtrace, citizen, local_vars=trace)
-def report_promo(kind: str, time_untill: datetime.datetime):
+def report_promo(kind: str, time_untill: datetime.datetime) -> NoReturn:
requests.post('https://api.erep.lv/promos/add/', data=dict(kind=kind, time_untill=time_untill))
-def slugify(value, allow_unicode=False):
+def slugify(value, allow_unicode=False) -> str:
"""
Function copied from Django2.2.1 django.utils.text.slugify
Convert to ASCII if 'allow_unicode' is False. Convert spaces to hyphens.