From 1f07f2e270140109e40fabaaca1ddeba27d0ed6d Mon Sep 17 00:00:00 2001 From: Eriks Karls Date: Mon, 13 Jan 2020 10:28:42 +0200 Subject: [PATCH] Update: Citizen.set_default_weapon() - eRepublik should return list with all available weapon qualities, but when a battle is just launched, they return only dict with barehands Citizen.fight() - no longer calls self.set_default_weapon() Citizen.find_battle_and_fight() - now calls self.set_default_weapon() just before fighting Citizen.update_war_info() - returns previous battle list if responses 'last_updated' isn't more than 30s old New: Citizen.get_battle_for_war(war_id) - returns Battle instance for specific war, if battle is active for given war --- erepublik/citizen.py | 54 +++++++++++++++++++++++++++----------------- erepublik/classes.py | 10 +++----- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/erepublik/citizen.py b/erepublik/citizen.py index 988f536..915dad0 100644 --- a/erepublik/citizen.py +++ b/erepublik/citizen.py @@ -1,20 +1,20 @@ +import json import re import sys -import warnings -from collections import defaultdict from datetime import datetime, timedelta from itertools import product -from json import loads, dumps +from json import dumps, loads from threading import Event from time import sleep -from typing import Dict, List, Tuple, Any, Union, Set +from typing import Any, Dict, List, Optional, Set, Tuple, Union -from requests import Response, RequestException +from requests import RequestException, Response -from erepublik.classes import (CitizenAPI, Battle, Reporter, Config, Energy, Details, Politics, MyCompanies, - TelegramBot, ErepublikException, BattleDivision, MyJSONEncoder) +from erepublik.classes import (Battle, BattleDivision, CitizenAPI, Config, + Details, Energy, ErepublikException, + MyCompanies, MyJSONEncoder, Politics, Reporter, + TelegramBot) from erepublik.utils import * -from erepublik.utils import process_warning class Citizen(CitizenAPI): @@ -112,6 +112,9 @@ class Citizen(CitizenAPI): def __str__(self) -> str: return f"Citizen {self.name}" + def __repr__(self): + return self.__str__() + def __dict__(self): ret = super().__dict__.copy() ret.pop('stop_threads', None) @@ -178,7 +181,7 @@ class Citizen(CitizenAPI): if j['error'] and j["message"] == 'Too many requests': self.write_log("Made too many requests! Sleeping for 30 seconds.") self.sleep(30) - except: + except (json.JSONDecodeError, KeyError, TypeError): pass if response.status_code >= 400: self.r = response @@ -559,10 +562,13 @@ class Citizen(CitizenAPI): if not self.details.current_country: self.update_citizen_info() - resp_json = self._get_military_campaigns_json_list().json() + if self.__last_war_update_data and self.__last_war_update_data.get('last_updated', 0) + 30 > self.now.timestamp(): + resp_json = self.__last_war_update_data + else: + resp_json = self._get_military_campaigns_json_list().json() if resp_json.get("countries"): if self.all_battles is None: - self.all_battles = defaultdict(Battle) + self.all_battles = {} else: self.all_battles.clear() @@ -638,7 +644,7 @@ class Citizen(CitizenAPI): def now(self) -> datetime: """ Returns aware datetime object localized to US/Pacific (eRepublik time) - :return: datetime.datetime + :return: datetime """ return now() @@ -811,6 +817,7 @@ class Citizen(CitizenAPI): if not self.travel_to_battle(battle_id, country_ids_to_travel): break + self.set_default_weapon(battle_id) self.fight(battle_id, side_id) self.travel_to_residence() self.collect_weekly_reward() @@ -819,8 +826,7 @@ class Citizen(CitizenAPI): def fight(self, battle_id: int, side_id: int = None, count: int = None) -> int: """Fight in a battle. - Will auto activate booster and travel if allowed to do it and - in the beginning will switch to default weapon (air - bare hands, ground - q7, if count > 30, else bare hands. + Will auto activate booster and travel if allowed to do it. :param battle_id: int BattleId - battle to fight in :param side_id: int or None. Battle side to fight in, If side_id not == invader id or not in invader deployed allies list, then defender's side is chosen :param count: How many hits to do, if not specified self.should_fight() is called. @@ -831,11 +837,10 @@ class Citizen(CitizenAPI): battle_id = int(battle_id) if battle_id not in self.all_battles: self.update_war_info() - battle = self.all_battles[battle_id] + battle = self.all_battles.get(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() - self.set_default_weapon(battle_id) error_count = 0 ok_to_fight = True if count is None: @@ -862,7 +867,7 @@ class Citizen(CitizenAPI): return error_count def _shoot(self, battle_id: int, inv_side: bool, zone_id: int): - battle = self.all_battles[battle_id] + battle = self.all_battles.get(battle_id) side_id = battle.invader.id if inv_side else battle.defender.id if battle.is_air: response = self._post_military_fight_air(battle_id, side_id, zone_id) @@ -914,7 +919,7 @@ class Citizen(CitizenAPI): if not isinstance(count, int) or count < 1: count = 1 has_traveled = False - battle = self.all_battles[battle_id] + battle = self.all_battles.get(battle_id) if inv_side: good_countries = [battle.invader.id] + battle.invader.deployed if self.details.current_country not in good_countries: @@ -946,7 +951,7 @@ class Citizen(CitizenAPI): :param division_to: int target division to switch to :return: """ - battle = self.all_battles[battle_id] + battle = self.all_battles.get(battle_id) self._post_main_battlefield_change_division(battle_id, battle.div[division_to].battle_zone_id) def work_ot(self): @@ -2136,8 +2141,10 @@ class Citizen(CitizenAPI): 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 = self.all_battles.get(battle_id) 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() weapon_quality = -1 if not battle.is_air: for weapon in available_weapons: @@ -2147,7 +2154,12 @@ class Citizen(CitizenAPI): 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 = self.all_battles.get(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') + + def get_battle_for_war(self, war_id: int) -> Optional[Battle]: + self.update_war_info() + war_info = self.get_war_status(war_id) + return self.all_battles.get(war_info.get("battle_id"), None) diff --git a/erepublik/classes.py b/erepublik/classes.py index c5d6f31..1aa41bf 100644 --- a/erepublik/classes.py +++ b/erepublik/classes.py @@ -4,9 +4,9 @@ import hashlib import random import threading import time -from collections import deque, defaultdict -from json import JSONDecodeError, loads, JSONEncoder -from typing import Any, Dict, List, Union, Mapping, Iterable, Tuple +from collections import defaultdict, deque +from json import JSONDecodeError, JSONEncoder, loads +from typing import Any, Dict, Iterable, List, Mapping, Tuple, Union from requests import Response, Session, post @@ -567,10 +567,6 @@ Class for unifying eRepublik known endpoints and their required/optional paramet 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) - def _get_military_unit_data(self, unit_id: int, **kwargs) -> Response: params = {"groupId": unit_id, "panel": "members", **kwargs} return self.get("{}/military/military-unit-data/".format(self.url), params=params)