Compare commits

...

10 Commits

6 changed files with 153 additions and 66 deletions

View File

@ -1,6 +1,6 @@
* eRepublik script version:
* Python version:
* Operating System:
* eRepublik script version:
* Python version:
* Operating System:
### Description
@ -9,7 +9,7 @@ Tell us what happened, what went wrong, and what you expected to happen.
### What I Did
```
``` python
Paste the command(s) you ran and the output.
If there was a crash, please include the traceback here.
```

View File

@ -4,7 +4,7 @@
__author__ = """Eriks Karls"""
__email__ = 'eriks@72.lv'
__version__ = '0.17.0'
__version__ = '0.17.2'
from erepublik import classes, utils
from erepublik.citizen import Citizen

View File

@ -26,9 +26,9 @@ class Citizen(CitizenAPI):
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}
boosters = {100: {}, 50: {}}
food: Dict[str, int] = {"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0, "total": 0}
inventory: Dict[str, int] = {"used": 0, "total": 0}
boosters: Dict[int, Dict[int, int]] = {100: {}, 50: {}}
eb_normal = 0
eb_double = 0
@ -164,6 +164,13 @@ class Citizen(CitizenAPI):
self.logged_in = True
def _errors_in_response(self, response: Response):
try:
j = response.json()
if j['error'] and j["message"] == 'Too many requests':
self.write_log("Made too many requests! Sleeping for 30 seconds.")
self.sleep(30)
except:
pass
if response.status_code >= 400:
self.r = response
if response.status_code >= 500:
@ -174,7 +181,7 @@ class Citizen(CitizenAPI):
return bool(re.search(r'body id="error"|Internal Server Error|'
r'CSRF attack detected|meta http-equiv="refresh"|not_authenticated', response.text))
def get(self, url: str, *args, **kwargs) -> Response:
def get(self, url: str, **kwargs) -> Response:
if (self.now - self._req.last_time).seconds >= 15 * 60:
self.get_csrf_token()
if "params" in kwargs:
@ -188,7 +195,7 @@ class Citizen(CitizenAPI):
except RequestException as e:
self.write_log("Network error while issuing GET request", e)
self.sleep(60)
return self.get(url, *args, **kwargs)
return self.get(url, **kwargs)
try:
self.update_citizen_info(html=response.text)
@ -221,7 +228,7 @@ class Citizen(CitizenAPI):
except RequestException as e:
self.write_log("Network error while issuing POST request", e)
self.sleep(60)
return self.post(url, data, json, **kwargs)
return self.post(url, data=data, json=json, **kwargs)
try:
resp_data = response.json()
@ -254,6 +261,9 @@ class Citizen(CitizenAPI):
r"<div title=\"(.*?)\">", medal, re.M | re.S)
about = info.group(1).strip()
title = info.group(2).strip()
award_id = re.search(r'"wall_enable_alerts_(\d+)', medal)
if award_id:
self._post_main_wall_post_automatic(**{'message': title, 'awardId': award_id.group(1)})
reward, currency = info.group(3).strip().split(" ")
while not isinstance(reward, float):
try:
@ -367,7 +377,7 @@ class Citizen(CitizenAPI):
self._post_military_group_missions()
self.details.next_pp.sort()
for id_, skill in citizen.get("mySkills", {}).items():
for skill in citizen.get("mySkills", {}).values():
self.details.mayhem_skills.update({int(skill["terrain_id"]): int(skill["skill_points"])})
if citizen.get('party', []):
@ -537,7 +547,7 @@ class Citizen(CitizenAPI):
if not self.details.current_country:
self.update_citizen_info()
resp_json = self._get_military_campaigns().json()
resp_json = self._get_military_campaigns_json_list().json()
if resp_json.get("countries"):
self.all_battles = {}
self.countries = {}
@ -550,8 +560,8 @@ class Citizen(CitizenAPI):
self.countries[int(c_id)].update(allies=c_data.get("allies"))
self.__last_war_update_data = resp_json
if resp_json.get("battles"):
for battle_id, battle_data in resp_json.get("battles", {}).items():
self.all_battles.update({int(battle_id): Battle(battle_data)})
for battle_data in resp_json.get("battles", {}).values():
self.all_battles.update({battle_data.get('id'): Battle(battle_data)})
def eat(self):
"""
@ -646,8 +656,7 @@ class Citizen(CitizenAPI):
break
error_count = 0
while self.energy.food_fights > 5 and error_count < 20:
errors = self.fight(battle_id, side_id=side, is_air=False,
count=self.energy.food_fights - 5)
errors = self.fight(battle_id, side_id=side, count=self.energy.food_fights - 5)
if errors:
error_count += errors
if self.config.epic_hunt_ebs:
@ -659,7 +668,7 @@ class Citizen(CitizenAPI):
self.active_fs = active_fs
def sorted_battles(self, sort_by_time: bool = False) -> List[int]:
def sorted_battles(self, sort_by_time: bool = True) -> List[int]:
cs_battles_air: List[int] = []
cs_battles_ground: List[int] = []
deployed_battles_air: List[int] = []
@ -670,8 +679,12 @@ class Citizen(CitizenAPI):
other_battles_ground: List[int] = []
ret_battles = []
for bid, battle in sorted(self.all_battles.items(), key=lambda b: b[1].start if sort_by_time else b[0],
reverse=sort_by_time):
if sort_by_time:
battle_list = sorted(self.all_battles.values(), key=lambda b: b.start)
battle_list.reverse()
else:
battle_list = sorted(self.all_battles.values(), key=lambda b: b.id)
for battle in battle_list:
battle_sides = [battle.invader.id, battle.defender.id]
# Previous battles
@ -779,15 +792,17 @@ class Citizen(CitizenAPI):
if not self.travel_to_battle(battle_id, country_ids_to_travel):
break
self.fight(battle_id, side_id, battle.is_air)
self.fight(battle_id, side_id)
self.travel_to_residence()
self.collect_weekly_reward()
break
def fight(self, battle_id: int, side_id: int, is_air: bool = False, count: int = None):
if not is_air and self.config.boosters:
def fight(self, battle_id: int, side_id: int, count: int = None):
battle = self.all_battles[battle_id]
zone_id = battle.div[11 if battle.is_air else self.division].battle_zone_id
if not battle.is_air and self.config.boosters:
self.activate_dmg_booster()
data = dict(sideId=side_id, battleId=battle_id)
self.set_default_weapon(battle_id)
error_count = 0
ok_to_fight = True
if count is None:
@ -798,7 +813,7 @@ class Citizen(CitizenAPI):
while ok_to_fight and error_count < 10 and count > 0:
while all((count > 0, error_count < 10, self.energy.recovered >= 50)):
hits, error, damage = self._shoot(is_air, data)
hits, error, damage = self._shoot(battle.is_air, battle_id, side_id, zone_id)
count -= hits
total_hits += hits
total_damage += damage
@ -810,15 +825,15 @@ class Citizen(CitizenAPI):
ok_to_fight = False
if total_damage:
self.reporter.report_action(json_val=dict(battle=battle_id, side=side_id, dmg=total_damage,
air=is_air, hits=total_hits), action="FIGHT")
air=battle.is_air, hits=total_hits), action="FIGHT")
if error_count:
return error_count
def _shoot(self, air: bool, data: dict):
def _shoot(self, air: bool, battle_id: int, side_id: int, zone_id: int):
if air:
response = self._post_military_fight_air(data['battleId'], data['sideId'])
response = self._post_military_fight_air(battle_id, side_id, zone_id)
else:
response = self._post_military_fight_ground(data['battleId'], data['sideId'])
response = self._post_military_fight_ground(battle_id, side_id, zone_id)
if "Zone is not meant for " in response.text:
self.sleep(5)
@ -833,9 +848,11 @@ class Citizen(CitizenAPI):
if j_resp.get("error"):
if j_resp.get("message") == "SHOOT_LOCKOUT" or j_resp.get("message") == "ZONE_INACTIVE":
pass
elif j_resp.get("message") == "NOT_ENOUGH_WEAPONS":
self.set_default_weapon(battle_id)
else:
if j_resp.get("message") == "UNKNOWN_SIDE":
self._rw_choose_side(data["battleId"], data["sideId"])
self._rw_choose_side(battle_id, side_id)
err = True
elif j_resp.get("message") == "ENEMY_KILLED":
hits = (self.energy.recovered - j_resp["details"]["wellness"]) // 10
@ -1323,7 +1340,7 @@ class Citizen(CitizenAPI):
if not self.get_active_ground_damage_booster():
duration = 0
for length, amount in self.boosters[50].items():
if amount > 1:
if amount > 2:
duration = length
break
if duration:
@ -2029,3 +2046,24 @@ class Citizen(CitizenAPI):
def speedup_map_quest_node(self, node_id: int):
node = self.get_anniversary_quest_data().get('cities', {}).get(str(node_id), {})
return self._post_map_rewards_speedup(node_id, node.get("skipCost", 0))
def get_available_weapons(self, battle_id: int):
return self._get_military_show_weapons(battle_id).json()
def set_default_weapon(self, battle_id: int) -> int:
battle = self.all_battles[battle_id]
battle_zone = battle.div[11 if battle.is_air else self.division].battle_zone_id
available_weapons = self._get_military_show_weapons(battle_id).json()
weapon_quality = -1
if not battle.is_air:
for weapon in available_weapons:
if weapon['weaponId'] == 7 and weapon['weaponQuantity'] > 30:
weapon_quality = 7
break
return self.change_weapon(battle_id, weapon_quality)
def change_weapon(self, battle_id: int, weapon_quality: int) -> int:
battle = self.all_battles[battle_id]
battle_zone = battle.div[11 if battle.is_air else self.division].battle_zone_id
r = self._post_military_change_weapon(battle_id, battle_zone, weapon_quality)
return r.json().get('weaponInfluence')

View File

@ -271,7 +271,7 @@ class Config:
work = True
train = True
wam = False
auto_sell: List[str] = list()
auto_sell: List[str] = None
auto_sell_all = False
employees = False
fight = False
@ -295,6 +295,9 @@ class Config:
telegram_chat_id = 0
telegram_token = ""
def __init__(self):
self.auto_sell = []
@property
def wt(self):
return self.work and self.train
@ -379,7 +382,7 @@ class Details:
pp = 0
pin = None
gold = 0
next_pp: List[int] = []
next_pp: List[int] = None
citizen_id = 0
citizenship = 0
current_region = 0
@ -390,6 +393,9 @@ class Details:
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, }
def __init__(self):
self.next_pp = []
@property
def xp_till_level_up(self):
if self.xp >= 10000:
@ -452,8 +458,8 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
"""
self._req = SlowRequests()
def post(self, url: str, *args, **kwargs) -> Response:
return self._req.post(url, *args, **kwargs)
def post(self, url: str, data=None, json=None, **kwargs) -> Response:
return self._req.post(url, data, json, **kwargs)
def get(self, url: str, **kwargs) -> Response:
return self._req.get(url, **kwargs)
@ -464,6 +470,9 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
def _get_military_battlefield_choose_side(self, battle: int, side: int) -> Response:
return self.get("{}/military/battlefield-choose-side/{}/{}".format(self.url, battle, side))
def _get_military_show_weapons(self, battle: int) -> Response:
return self.get("{}/military/show-weapons".format(self.url), params={'_token': self.token, 'battleId': battle})
def _get_candidate_party(self, party_slug: str) -> Response:
return self.post("{}/candidate/{}".format(self.url, party_slug))
@ -529,6 +538,9 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
def _get_military_campaigns(self) -> Response:
return self.get("{}/military/campaigns-new/".format(self.url))
def _get_military_campaigns_json_list(self) -> Response:
return self.get("{}/military/campaignsJson/list".format(self.url))
def _get_military_show_weapons(self, battle_id: int) -> Response:
params = {"_token": self.token, "battleId": battle_id}
return self.get("{}/military/show-weapons".format(self.url), params=params)
@ -757,6 +769,10 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
data = dict(type=kind, quality=quality, duration=duration, battleId=battle, _token=self.token)
return self.post("{}/military/fight-activateBooster".format(self.url), data=data)
def _post_military_change_weapon(self, battle: int, battle_zone: int, weapon_level: int,) -> Response:
data = dict(battleId=battle, _token=self.token, battleZoneId=battle_zone, customizationLevel=weapon_level)
return self.post("{}/military/change-weapon".format(self.url), data=data)
def _post_login(self, email: str, password: str) -> Response:
data = dict(csrf_token=self.token, citizen_email=email, citizen_password=password, remember='on')
return self.post("{}/login".format(self.url), data=data)
@ -789,12 +805,12 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
data = dict(battleId=battle_id, bombId=bomb_id, _token=self.token)
return self.post("{}/military/deploy-bomb".format(self.url), data=data)
def _post_military_fight_air(self, battle_id: int, side_id: int) -> Response:
data = dict(sideId=side_id, battleId=battle_id, _token=self.token)
def _post_military_fight_air(self, battle_id: int, side_id: int, zone_id: int) -> Response:
data = dict(sideId=side_id, battleId=battle_id, _token=self.token, battleZoneId=zone_id)
return self.post("{}/military/fight-shoooot/{}".format(self.url, battle_id), data=data)
def _post_military_fight_ground(self, battle_id: int, side_id: int) -> Response:
data = dict(sideId=side_id, battleId=battle_id, _token=self.token)
def _post_military_fight_ground(self, battle_id: int, side_id: int, zone_id: int) -> Response:
data = dict(sideId=side_id, battleId=battle_id, _token=self.token, battleZoneId=zone_id)
return self.post("{}/military/fight-shooot/{}".format(self.url, battle_id), data=data)
def _post_military_group_missions(self) -> Response:
@ -894,6 +910,10 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
data = {"_token": self.token, "post_message": body}
return self.post("{}/main/wall-post/create/json".format(self.url), data=data)
def _post_main_wall_post_automatic(self, **kwargs) -> Response:
kwargs.update(_token=self.token)
return self.post("{}/main/wall-post/create/json".format(self.url), data=kwargs)
def _post_main_wall_post_retrieve(self) -> Response:
data = {"_token": self.token, "page": 1, "switchedFrom": False}
return self.post("{}/main/wall-post/retrieve/json".format(self.url), data=data)
@ -914,9 +934,19 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
data = {'nodeId': node_id, '_token': self.token}
return self.post("{}/main/map-rewards-claim".format(self.url), data=data)
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)
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)
class Reporter:
__to_update: List[Dict[Any, Any]] = []
__to_update: List[Dict[Any, Any]] = None
name: str = ""
email: str = ""
citizen_id: int = 0
@ -932,6 +962,7 @@ class Reporter:
self._req = Session()
self.url = "https://api.erep.lv"
self._req.headers.update({"user-agent": "Bot reporter v2"})
self.__to_update = []
self.__registered: bool = False
def do_init(self, name: str = "", email: str = "", citizen_id: int = 0):
@ -1010,7 +1041,7 @@ class MyJSONEncoder(JSONEncoder):
return dict(headers=o.headers.__dict__, url=o.url, text=o.text)
elif hasattr(o, '__dict__'):
return o.__dict__
elif isinstance(o, deque):
elif isinstance(o, (deque, set)):
return list(o)
elif isinstance(o, Citizen):
return o.to_json()
@ -1033,30 +1064,39 @@ class BattleSide:
class BattleDivision:
end: datetime.datetime
epic: bool
dom_pts: Dict[str, int] = None
wall: Dict[str, Union[int, float]] = None
dom_pts: Dict[str, int]
wall: Dict[str, Union[int, float]]
battle_zone_id: int
def_medal: Dict[str, int]
inv_medal: Dict[str, int]
@property
def div_end(self) -> bool:
return utils.now() >= self.end
def __init__(self, end: datetime.datetime, epic: bool, inv_pts: int, def_pts: int, wall_for: int, wall_dom: float):
def __init__(
self, div_id: int, end: datetime.datetime, epic: bool, inv_pts: int, def_pts: int,
wall_for: int, wall_dom: float, def_medal: Tuple[int, int], inv_medal: Tuple[int, int]
):
self.battle_zone_id = div_id
self.end = end
self.epic = epic
self.dom_pts = dict({"inv": inv_pts, "def": def_pts})
self.wall = dict({"for": wall_for, "dom": wall_dom})
self.def_medal = {"id": def_medal[0], "dmg": def_medal[1]}
self.inv_medal = {"id": inv_medal[0], "dmg": inv_medal[1]}
class Battle:
id: int = 0
war_id: int = 0
zone_id: int = 0
is_rw: bool = False
is_dict_lib: bool = False
start: datetime.datetime = None
invader: BattleSide = None
defender: BattleSide = None
div: Dict[int, BattleDivision] = None
id: int
war_id: int
zone_id: int
is_rw: bool
is_dict_lib: bool
start: datetime.datetime
invader: BattleSide
defender: BattleSide
div: Dict[int, BattleDivision]
@property
def is_air(self) -> bool:
@ -1087,11 +1127,18 @@ class Battle:
else:
end = utils.localize_dt(datetime.datetime.max - datetime.timedelta(days=1))
battle_div = BattleDivision(
end=end, epic=data.get('epic_type') in [1, 5],
inv_pts=data.get('dom_pts').get("inv"), def_pts=data.get('dom_pts').get("def"),
wall_for=data.get('wall').get("for"), wall_dom=data.get('wall').get("dom")
)
if not data['stats']['def']:
def_medal = (0, 0)
else:
def_medal = (data['stats']['def']['citizenId'], data['stats']['def']['damage'])
if not data['stats']['inv']:
inv_medal = (0, 0)
else:
inv_medal = (data['stats']['inv']['citizenId'], data['stats']['inv']['damage'])
battle_div = BattleDivision(end=end, epic=data.get('epic_type') in [1, 5], div_id=data.get('id'),
inv_pts=data.get('dom_pts').get("inv"), def_pts=data.get('dom_pts').get("def"),
wall_for=data.get('wall').get("for"), wall_dom=data.get('wall').get("dom"),
def_medal=def_medal, inv_medal=inv_medal)
self.div.update({div: battle_div})
@ -1141,17 +1188,19 @@ class EnergyToFight:
class TelegramBot:
__initialized = False
__queue: List[str] = []
__queue: List[str]
chat_id = 0
api_url = ""
player_name = ""
__thread_stopper: threading.Event = None
_last_time: datetime.datetime = None
_last_full_energy_report: datetime.datetime = None
_next_time: datetime.datetime = None
_threads: List[threading.Thread] = []
__thread_stopper: threading.Event
_last_time: datetime.datetime
_last_full_energy_report: datetime.datetime
_next_time: datetime.datetime
_threads: List[threading.Thread]
def __init__(self, stop_event: threading.Event = None):
self._threads = []
self.__queue = []
self.__thread_stopper = threading.Event() if stop_event is None else stop_event
def __dict__(self):

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.17.0
current_version = 0.17.2
commit = True
tag = True

View File

@ -42,6 +42,6 @@ setup(
test_suite='tests',
tests_require=test_requirements,
url='https://github.com/eeriks/erepublik/',
version='0.17.0',
version='0.17.2',
zip_safe=False,
)