Compare commits

...

15 Commits

Author SHA1 Message Date
8cf86fb9d3 Bump version: 0.24.2.3 → 0.24.2.4 2021-04-27 12:21:54 +03:00
cf927df6e6 bugfix 2021-04-27 12:21:50 +03:00
a6f5dbd05f Bump version: 0.24.2.2 → 0.24.2.3 2021-04-27 12:19:22 +03:00
967afa472f Rank.__str__ 2021-04-27 12:18:56 +03:00
a65568cd0c Bump version: 0.24.2.1 → 0.24.2.2 2021-04-27 11:05:57 +03:00
6e45334d99 Rank comparision 2021-04-27 11:05:51 +03:00
936a1010a6 Bump version: 0.24.2 → 0.24.2.1 2021-04-26 13:12:32 +03:00
acc528cb1d bugfix 2021-04-26 13:12:28 +03:00
614d273104 Bump version: 0.24.1 → 0.24.2 2021-04-23 13:46:51 +03:00
95966764e8 Updated rank constants 2021-04-23 13:45:58 +03:00
f52b078e6a Captcha challange solving simplification 2021-03-05 16:11:52 +02:00
3af27f6512 Cookie dump migration 2021-03-03 13:15:26 +02:00
6276242260 bugfixes 2021-03-03 13:13:19 +02:00
45623de97b Reporter update to queue messages if network error occures 2021-02-06 15:33:08 +02:00
25f932121c Code cleanup and JSONEncoder update to support dumping Logger instances 2021-02-06 15:32:30 +02:00
13 changed files with 217 additions and 171 deletions

View File

@ -21,6 +21,7 @@ insert_final_newline = false
indent_style = tab indent_style = tab
[*.py] [*.py]
max_line_length = 240
line_length=120 line_length=120
multi_line_output=0 multi_line_output=0
balanced_wrapping=True balanced_wrapping=True

View File

@ -10,4 +10,4 @@ repos:
- id: check-added-large-files - id: check-added-large-files
default_language_version: default_language_version:
python: python3.7 python: python3.8

View File

@ -36,14 +36,6 @@ erepublik.constants module
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
erepublik.types module
----------------------
.. automodule:: erepublik.types
:members:
:undoc-members:
:show-inheritance:
erepublik.utils module erepublik.utils module
---------------------- ----------------------
@ -52,6 +44,14 @@ erepublik.utils module
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
erepublik.ws module
-------------------
.. automodule:: erepublik.ws
:members:
:undoc-members:
:show-inheritance:
Module contents Module contents
--------------- ---------------

View File

@ -4,9 +4,8 @@
__author__ = """Eriks Karls""" __author__ = """Eriks Karls"""
__email__ = 'eriks@72.lv' __email__ = 'eriks@72.lv'
__version__ = '0.24.1' __version__ = '0.24.2.4'
from erepublik import classes, constants, utils
from erepublik.citizen import Citizen from erepublik.citizen import Citizen
__all__ = ["classes", "utils", "Citizen", 'constants'] __all__ = ['Citizen', '__version__']

View File

@ -49,7 +49,7 @@ class ErepublikFormatter(logging.Formatter):
default_fmt = "[%(asctime)s] %(levelname)s: %(msg)s" default_fmt = "[%(asctime)s] %(levelname)s: %(msg)s"
def converter(self, timestamp: Union[int, float]) -> datetime.datetime: def converter(self, timestamp: Union[int, float]) -> datetime.datetime:
return datetime.datetime.utcfromtimestamp(timestamp).astimezone(erep_tz) return datetime.datetime.fromtimestamp(timestamp).astimezone(erep_tz)
def format(self, record: logging.LogRecord) -> str: def format(self, record: logging.LogRecord) -> str:
""" """

View File

@ -3,11 +3,11 @@ import hashlib
import random import random
import time import time
from typing import Any, Dict, List, Mapping, Union from typing import Any, Dict, List, Mapping, Union
from requests_toolbelt.utils import dump
from requests import Response, Session from requests import Response, Session
from requests_toolbelt.utils import dump
from . import constants, utils from erepublik import constants, utils
__all__ = ['SlowRequests', 'CitizenAPI'] __all__ = ['SlowRequests', 'CitizenAPI']
@ -163,18 +163,12 @@ class CitizenBaseAPI:
def _post_main_session_unlock( def _post_main_session_unlock(
self, captcha_id: int, image_id: str, challenge_id: str, coords: List[Dict[str, int]], src: str self, captcha_id: int, image_id: str, challenge_id: str, coords: List[Dict[str, int]], src: str
) -> Response: ) -> Response:
if not self._req.cookies.get('l_chathwe'): c = [cookie.name for cookie in self._req.cookies if not cookie.has_nonstandard_attr('HttpOnly')]
self._req.cookies.set('l_chathwe', 1, expires=int(time.time())+300, path="/en/military", domain='.www.erepublik.com', secure=True) env = dict(l=['tets'], s=[], c=c, m=0)
if self._req.cookies.get('sh'):
self._req.cookies.pop('sh')
if self._req.cookies.get('ch'):
self._req.cookies.pop('ch')
c = [cookie.name for cookie in self._req.cookies if cookie.domain == '.www.erepublik.com' and not cookie.name.startswith('erpk')]
env = dict(l=['tets'], s=[], c=c + ['l_chathwe'], m=0)
cookies = dict(sh=hashlib.sha256(','.join(env['l']+env['s']).encode('utf8')).hexdigest(), cookies = dict(sh=hashlib.sha256(','.join(env['l']+env['s']).encode('utf8')).hexdigest(),
ch=hashlib.sha256(','.join(env['c']).encode('utf8')).hexdigest()) ch=hashlib.sha256(','.join(env['c']).encode('utf8')).hexdigest())
cookie_kwargs = dict(expires=int(time.time())+120, path="/en/main/sessionUnlock", domain='.www.erepublik.com', cookie_kwargs = dict(expires=int(time.time())+120, path="/en/main/sessionUnlock", domain='.www.erepublik.com',
secure=True) secure=True, rest={'HttpOnly': True})
self._req.cookies.set('sh', cookies['sh'], **cookie_kwargs) self._req.cookies.set('sh', cookies['sh'], **cookie_kwargs)
self._req.cookies.set('ch', cookies['ch'], **cookie_kwargs) self._req.cookies.set('ch', cookies['ch'], **cookie_kwargs)
b64_env = utils.b64json(env) b64_env = utils.b64json(env)

View File

@ -9,11 +9,12 @@ from threading import Event
from time import sleep from time import sleep
from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, Union from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, Union
from requests import RequestException, Response, HTTPError from requests import HTTPError, RequestException, Response
from . import access_points, classes, constants, _types as types, utils from erepublik import _types as types
from .classes import OfferItem from erepublik import access_points, classes, constants, utils
from ._logging import ErepublikErrorHTTTPHandler, ErepublikFileHandler, ErepublikFormatter, ErepublikLogConsoleHandler from erepublik._logging import ErepublikErrorHTTTPHandler, ErepublikFileHandler, ErepublikFormatter, \
ErepublikLogConsoleHandler
class BaseCitizen(access_points.CitizenAPI): class BaseCitizen(access_points.CitizenAPI):
@ -279,7 +280,7 @@ class BaseCitizen(access_points.CitizenAPI):
return self._post_main_session_get_challenge(captcha_id, image_id).json() return self._post_main_session_get_challenge(captcha_id, image_id).json()
def solve_captcha(self, src: str) -> List[Dict[str, int]]: def solve_captcha(self, src: str) -> List[Dict[str, int]]:
raise NotImplemented return []
@property @property
def inventory(self) -> classes.Inventory: def inventory(self) -> classes.Inventory:
@ -569,8 +570,14 @@ class BaseCitizen(access_points.CitizenAPI):
with open(dump_name) as f: with open(dump_name) as f:
data = utils.json.load(f, object_hook=utils.json_decode_object_hook) data = utils.json.load(f, object_hook=utils.json_decode_object_hook)
player = cls(data['config']['email'], "") player = cls(data['config']['email'], "")
for cookie in data['cookies']: if data.get('cookies'):
player._req.cookies.set(**cookie) cookies = data.get('cookies')
if isinstance(cookies, list):
for cookie in data['cookies']:
player._req.cookies.set(**cookie)
else:
player._req.cookies.update(cookies)
player._req.headers.update({"User-Agent": data['user_agent']}) player._req.headers.update({"User-Agent": data['user_agent']})
for k, v in data.get('config', {}).items(): for k, v in data.get('config', {}).items():
if hasattr(player.config, k): if hasattr(player.config, k):
@ -1327,7 +1334,7 @@ class CitizenEconomy(CitizenTravel):
self._report_action('BOUGHT_PRODUCTS', json_ret.get('message'), kwargs=json_ret) self._report_action('BOUGHT_PRODUCTS', json_ret.get('message'), kwargs=json_ret)
return json_ret return json_ret
def buy_market_offer(self, offer: OfferItem, amount: int = None) -> Optional[Dict[str, Any]]: def buy_market_offer(self, offer: classes.OfferItem, amount: int = None) -> Optional[Dict[str, Any]]:
if amount is None or amount > offer.amount: if amount is None or amount > offer.amount:
amount = offer.amount amount = offer.amount
traveled = False traveled = False
@ -2734,7 +2741,7 @@ class _Citizen(CitizenAnniversary, CitizenCompanies, CitizenLeaderBoard,
self.buy_tg_contract() self.buy_tg_contract()
else: else:
self.write_warning('Training ground contract active but ' self.write_warning('Training ground contract active but '
f"don't have enough gold ({self.details.gold}g {self.details.cc}cc)") f"don't have enough gold ({self.details.gold}g {self.details.cc}cc)")
if self.energy.is_energy_full and self.config.telegram: if self.energy.is_energy_full and self.config.telegram:
self.telegram.report_full_energy(self.energy.available, self.energy.limit, self.energy.interval) self.telegram.report_full_energy(self.energy.available, self.energy.limit, self.energy.interval)
@ -3170,7 +3177,7 @@ class Citizen(_Citizen):
finally: finally:
self._concurrency_lock.set() self._concurrency_lock.set()
def buy_market_offer(self, offer: OfferItem, amount: int = None) -> Optional[Dict[str, Any]]: def buy_market_offer(self, offer: classes.OfferItem, amount: int = None) -> Optional[Dict[str, Any]]:
if not self._concurrency_lock.wait(self._concurrency_timeout): if not self._concurrency_lock.wait(self._concurrency_timeout):
e = f'Concurrency not freed in {self._concurrency_timeout}sec!' e = f'Concurrency not freed in {self._concurrency_timeout}sec!'
self.report_error(e) self.report_error(e)

View File

@ -3,11 +3,13 @@ import hashlib
import threading import threading
import weakref import weakref
from decimal import Decimal from decimal import Decimal
from typing import Any, Dict, Generator, Iterable, List, NamedTuple, NoReturn, Union from io import BytesIO
from typing import Any, Dict, Generator, Iterable, List, NamedTuple, NoReturn, Optional, Tuple, Union
from requests import Response, Session, post from requests import HTTPError, Response, Session, post
from . import constants, _types as types, utils from erepublik import _types as types
from erepublik import constants, utils
__all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException', __all__ = ['Battle', 'BattleDivision', 'BattleSide', 'Company', 'Config', 'Details', 'Energy', 'ErepublikException',
'ErepublikNetworkException', 'EnergyToFight', 'Holding', 'Inventory', 'MyCompanies', 'OfferItem', 'Politics', 'ErepublikNetworkException', 'EnergyToFight', 'Holding', 'Inventory', 'MyCompanies', 'OfferItem', 'Politics',
@ -606,20 +608,32 @@ class Reporter:
for unreported_data in self.__to_update: for unreported_data in self.__to_update:
unreported_data.update(player_id=self.citizen_id, key=self.key) unreported_data.update(player_id=self.citizen_id, key=self.key)
unreported_data = utils.json_loads(utils.json_dumps(unreported_data)) unreported_data = utils.json_loads(utils.json_dumps(unreported_data))
self._req.post(f"{self.url}/bot/update", json=unreported_data) r = self._req.post(f"{self.url}/bot/update", json=unreported_data)
r.raise_for_status()
self.__to_update.clear() self.__to_update.clear()
data = utils.json.loads(utils.json_dumps(data)) data = utils.json.loads(utils.json_dumps(data))
r = self._req.post(f"{self.url}/bot/update", json=data) r = self._req.post(f"{self.url}/bot/update", json=data)
r.raise_for_status()
return r return r
def _bot_update(self, data: Dict[str, Any]) -> Optional[Response]:
if not self.__registered:
self.do_init()
if self.allowed:
try:
return self.__bot_update(data)
except HTTPError:
self.__to_update.append(data)
else:
self.__to_update.append(data)
def register_account(self): def register_account(self):
if not self.__registered: if not self.__registered:
try: r = self.__bot_update(dict(key=self.key, check=True, player_id=self.citizen_id))
r = self.__bot_update(dict(key=self.key, check=True, player_id=self.citizen_id)) if r:
if not r.json().get('status'): if not r.json().get('status'):
self._req.post(f"{self.url}/bot/register", json=dict(name=self.name, email=self.email, self._req.post(f"{self.url}/bot/register", json=dict(name=self.name, email=self.email,
player_id=self.citizen_id)) player_id=self.citizen_id))
finally:
self.__registered = True self.__registered = True
self.allowed = True self.allowed = True
self.report_action('STARTED', value=utils.now().strftime("%F %T")) self.report_action('STARTED', value=utils.now().strftime("%F %T"))
@ -631,9 +645,7 @@ class Reporter:
xp=xp, cc=cc, gold=gold, inv_total=inv_total, inv_free=inv_total - inv, inv=inv, food=food, xp=xp, cc=cc, gold=gold, inv_total=inv_total, inv_free=inv_total - inv, inv=inv, food=food,
pp=pp, hp_limit=hp_limit, hp_interval=hp_interval, hp_available=hp_available, pp=pp, hp_limit=hp_limit, hp_interval=hp_interval, hp_available=hp_available,
)) ))
self._bot_update(data)
if self.allowed:
self.__bot_update(data)
def report_action(self, action: str, json_val: Dict[Any, Any] = None, value: str = None): def report_action(self, action: str, json_val: Dict[Any, Any] = None, value: str = None):
json_data = dict( json_data = dict(
@ -645,10 +657,7 @@ class Reporter:
json_data['log'].update(dict(value=value)) json_data['log'].update(dict(value=value))
if not any([self.key, self.email, self.name, self.citizen_id]): if not any([self.key, self.email, self.name, self.citizen_id]):
return return
if self.allowed: self._bot_update(json_data)
self.__bot_update(json_data)
else:
self.__to_update.append(json_data)
def report_fighting(self, battle: 'Battle', invader: bool, division: 'BattleDivision', damage: float, hits: int): def report_fighting(self, battle: 'Battle', invader: bool, division: 'BattleDivision', damage: float, hits: int):
side = battle.invader if invader else battle.defender side = battle.invader if invader else battle.defender
@ -960,7 +969,7 @@ class TelegramReporter:
if token is None: if token is None:
token = "864251270:AAFzZZdjspI-kIgJVk4gF3TViGFoHnf8H4o" token = "864251270:AAFzZZdjspI-kIgJVk4gF3TViGFoHnf8H4o"
self.chat_id = chat_id self.chat_id = chat_id
self.api_url = f"https://api.telegram.org/bot{token}/sendMessage" self.api_url = f"https://api.telegram.org/bot{token}"
self.player_name = player_name or "" self.player_name = player_name or ""
self.__initialized = True self.__initialized = True
self._last_time = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=-5)) self._last_time = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=-5))
@ -1017,13 +1026,21 @@ class TelegramReporter:
message = "\n\n".join(self.__queue) message = "\n\n".join(self.__queue)
if self.player_name: if self.player_name:
message = f"Player *{self.player_name}*\n\n" + message message = f"Player *{self.player_name}*\n\n" + message
response = post(self.api_url, json=dict(chat_id=self.chat_id, text=message, parse_mode='Markdown')) response = post(f"{self.api_url}/sendMessage", json=dict(chat_id=self.chat_id, text=message, parse_mode='Markdown'))
self._last_time = utils.now() self._last_time = utils.now()
if response.json().get('ok'): if response.json().get('ok'):
self.__queue.clear() self.__queue.clear()
return True return True
return False return False
def send_photos(self, photos: List[Tuple[str, BytesIO]]):
for photo_title, photo in photos:
photo.seek(0)
post(f"https://{self.api_url}/sendPhoto",
data=dict(chat_id=self.chat_id, caption=photo_title),
files=[('photo', ("f{utils.slugify(photo_title)}.png", photo))])
return
class OfferItem(NamedTuple): class OfferItem(NamedTuple):
price: float = 999_999_999. price: float = 999_999_999.

View File

@ -81,24 +81,94 @@ class Industries:
return dict(by_id=self.__by_id, by_name=self.__by_name) return dict(by_id=self.__by_id, by_name=self.__by_name)
AIR_RANKS: Dict[int, str] = { class Rank:
1: 'Airman', 2: 'Airman 1st Class', 3: 'Airman 1st Class*', 4: 'Airman 1st Class**', 5: 'Airman 1st Class***', id: int
6: 'Airman 1st Class****', 7: 'Airman 1st Class*****', 8: 'Senior Airman', 9: 'Senior Airman*', name: str
10: 'Senior Airman**', 11: 'Senior Airman***', 12: 'Senior Airman****', 13: 'Senior Airman*****', rank_points: int
14: 'Staff Sergeant', 15: 'Staff Sergeant*', 16: 'Staff Sergeant**', 17: 'Staff Sergeant***', is_air: bool
18: 'Staff Sergeant****', 19: 'Staff Sergeant*****', 20: 'Aviator', 21: 'Aviator*', 22: 'Aviator**',
23: 'Aviator***', 24: 'Aviator****', 25: 'Aviator*****', 26: 'Flight Lieutenant', 27: 'Flight Lieutenant*', def __init__(self, id: int, name: str, rank_points: int, is_air: bool = False):
28: 'Flight Lieutenant**', 29: 'Flight Lieutenant***', 30: 'Flight Lieutenant****', 31: 'Flight Lieutenant*****', self.id = id
32: 'Squadron Leader', 33: 'Squadron Leader*', 34: 'Squadron Leader**', 35: 'Squadron Leader***', self.name = name
36: 'Squadron Leader****', 37: 'Squadron Leader*****', 38: 'Chief Master Sergeant', 39: 'Chief Master Sergeant*', self.rank_points = rank_points
40: 'Chief Master Sergeant**', 41: 'Chief Master Sergeant***', 42: 'Chief Master Sergeant****', self.is_air = bool(is_air)
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', def __int__(self):
51: 'Group Captain*', 52: 'Group Captain**', 53: 'Group Captain***', 54: 'Group Captain****', return self.id
55: 'Group Captain*****', 56: 'Air Commodore', 57: 'Air Commodore*', 58: 'Air Commodore**', 59: 'Air Commodore***',
60: 'Air Commodore****', 61: 'Air Commodore*****', def __eq__(self, other):
if isinstance(other, Rank):
return self.id == other.id if other.is_air == self.is_air else False
else:
return self.id == int(other)
def __ne__(self, other):
if isinstance(other, Rank):
return not self.id == other.id if other.is_air == self.is_air else True
else:
return not self.id == int(other)
def __lt__(self, other):
if isinstance(other, Rank):
return self.id < other.id if other.is_air == self.is_air else False
else:
return self.id < int(other)
def __le__(self, other):
if isinstance(other, Rank):
return self.id <= other.id if other.is_air == self.is_air else False
else:
return self.id <= int(other)
def __gt__(self, other):
if isinstance(other, Rank):
return self.id > other.id if other.is_air == self.is_air else False
else:
return self.id > int(other)
def __ge__(self, other):
if isinstance(other, Rank):
return self.id >= other.id if other.is_air == self.is_air else False
else:
return self.id >= int(other)
@property
def as_dict(self):
return dict(id=self.id, name=self.name, rank_points=self.rank_points, is_air=self.is_air)
def __str__(self):
return f"{'Air' if self.is_air else 'Ground'}Rank<#{self.id} {self.name}>"
def __repr__(self):
return str(self)
AIR_RANK_NAMES: 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*****',
62: 'Air Vice Marshal', 63: 'Air Vice Marshal*', 64: 'Air Vice Marshal**', 65: 'Air Vice Marshal***', 66: 'Air Vice Marshal****', 67: 'Air Vice Marshal*****',
68: 'Air Marshal', 69: 'Air Marshal*', 70: 'Air Marshal**', 71: 'Air Marshal***', 72: 'Air Marshal****', 73: 'Air Marshal*****',
74: 'Air Chief Marshal', 75: 'Air Chief Marshal*', 76: 'Air Chief Marshal**', 77: 'Air Chief Marshal***', 78: 'Air Chief Marshal****', 79: 'Air Chief Marshal*****',
} }
AIR_RANK_POINTS: Dict[int, Optional[int]] = {
1: 0, 2: 10, 3: 25, 4: 45, 5: 70, 6: 100, 7: 140, 8: 190, 9: 270, 10: 380, 11: 530, 12: 850, 13: 1300, 14: 2340, 15: 3300, 16: 4200, 17: 5150, 18: 6100, 19: 7020, 20: 9100, 21: 12750, 22: 16400, 23: 20000, 24: 23650, 25: 27300,
26: 35500, 27: 48000, 28: 60000, 29: 72400, 30: 84500, 31: 97000, 32: 110000, 33: 140000, 34: 170000, 35: 210000, 36: 290000, 37: 350000, 38: 429000, 39: 601000, 40: 772000, 41: 944000, 42: 1115000, 43: 1287000,
44: 1673000, 45: 2238000, 46: 2804000, 47: 3369000, 48: 3935000, 49: 4500000, 50: 5020000, 51: 7028000, 52: 9036000, 53: 11044000, 54: 13052000, 55: 15060000,
56: 19580000, 57: 27412000, 58: 35244000, 59: 43076000, 60: 50908000, 61: 58740000, 62: 76360000, 63: 113166443, 64: 137448000, 65: None, 66: None, 67: None,
68: None, 69: None, 70: None, 71: None, 72: None, 73: None, 74: None, 75: None, 76: None, 77: None, 78: None, 79: None,
}
AIR_RANKS: Dict[int, Rank] = {i: Rank(i, AIR_RANK_NAMES[i], AIR_RANK_POINTS[i], True) for i in range(1, 80)}
COUNTRIES: Dict[int, Country] = { COUNTRIES: Dict[int, Country] = {
1: Country(1, 'Romania', 'Romania', 'ROU'), 9: Country(9, 'Brazil', 'Brazil', 'BRA'), 1: Country(1, 'Romania', 'Romania', 'ROU'), 9: Country(9, 'Brazil', 'Brazil', 'BRA'),
10: Country(10, 'Italy', 'Italy', 'ITA'), 11: Country(11, 'France', 'France', 'FRA'), 10: Country(10, 'Italy', 'Italy', 'ITA'), 11: Country(11, 'France', 'France', 'FRA'),
@ -144,28 +214,18 @@ COUNTRIES: Dict[int, Country] = {
FOOD_ENERGY: Dict[str, int] = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20) 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] = { GROUND_RANK_NAMES: Dict[int, str] = {
1: 'Recruit', 2: 'Private', 3: 'Private*', 4: 'Private**', 5: 'Private***', 1: 'Recruit', 2: 'Private', 3: 'Private*', 4: 'Private**', 5: 'Private***', 6: 'Corporal', 7: 'Corporal*', 8: 'Corporal**', 9: 'Corporal***',
6: 'Corporal', 7: 'Corporal*', 8: 'Corporal**', 9: 'Corporal***', 10: 'Sergeant', 11: 'Sergeant*', 12: 'Sergeant**', 13: 'Sergeant***', 14: 'Lieutenant', 15: 'Lieutenant*', 16: 'Lieutenant**', 17: 'Lieutenant***',
10: 'Sergeant', 11: 'Sergeant*', 12: 'Sergeant**', 13: 'Sergeant***', 18: 'Captain', 19: 'Captain*', 20: 'Captain**', 21: 'Captain***', 22: 'Major', 23: 'Major*', 24: 'Major**', 25: 'Major***',
14: 'Lieutenant', 15: 'Lieutenant*', 16: 'Lieutenant**', 17: 'Lieutenant***', 26: 'Commander', 27: 'Commander*', 28: 'Commander**', 29: 'Commander***', 30: 'Lt Colonel', 31: 'Lt Colonel*', 32: 'Lt Colonel**', 33: 'Lt Colonel***',
18: 'Captain', 19: 'Captain*', 20: 'Captain**', 21: 'Captain***', 34: 'Colonel', 35: 'Colonel*', 36: 'Colonel**', 37: 'Colonel***', 38: 'General', 39: 'General*', 40: 'General**', 41: 'General***',
22: 'Major', 23: 'Major*', 24: 'Major**', 25: 'Major***', 42: 'Field Marshal', 43: 'Field Marshal*', 44: 'Field Marshal**', 45: 'Field Marshal***', 46: 'Supreme Marshal', 47: 'Supreme Marshal*', 48: 'Supreme Marshal**', 49: 'Supreme Marshal***',
26: 'Commander', 27: 'Commander*', 28: 'Commander**', 29: 'Commander***', 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***',
30: 'Lt Colonel', 31: 'Lt Colonel*', 32: 'Lt Colonel**', 33: 'Lt Colonel***', 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***',
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***', 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', 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',
76: 'Legends VII', 77: 'Legends VIII', 78: 'Legends IX', 79: 'Legends X', 80: 'Legends XI', 81: 'Legends XII', 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'
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] = { GROUND_RANK_POINTS: Dict[int, int] = {
@ -184,6 +244,8 @@ GROUND_RANK_POINTS: Dict[int, int] = {
86: 180000000000, 87: 190000000000, 88: 200000000000, 89: 210000000000 86: 180000000000, 87: 190000000000, 88: 200000000000, 89: 210000000000
} }
GROUND_RANKS: Dict[int, Rank] = {i: Rank(i, GROUND_RANK_NAMES[i], GROUND_RANK_POINTS[i], False) for i in range(1, 90)}
INDUSTRIES = Industries() INDUSTRIES = Industries()
TERRAINS: Dict[int, str] = {0: 'Standard', 1: 'Industrial', 2: 'Urban', 3: 'Suburbs', 4: 'Airport', 5: 'Plains', TERRAINS: Dict[int, str] = {0: 'Standard', 1: 'Industrial', 2: 'Urban', 3: 'Suburbs', 4: 'Airport', 5: 'Plains',

View File

@ -7,6 +7,7 @@ import unicodedata
import warnings import warnings
from base64 import b64encode from base64 import b64encode
from decimal import Decimal from decimal import Decimal
from logging import Logger
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Union from typing import Any, Dict, List, Union
@ -14,7 +15,7 @@ import pytz
import requests import requests
from requests import Response from requests import Response
from . import __version__, constants from erepublik import __version__, constants
try: try:
import simplejson as json import simplejson as json
@ -102,27 +103,6 @@ def interactive_sleep(sleep_seconds: int):
silent_sleep = time.sleep silent_sleep = time.sleep
# def _write_log(msg, timestamp: bool = True, should_print: bool = False):
# erep_time_now = now()
# txt = f"[{erep_time_now.strftime('%F %T')}] {msg}" if timestamp else msg
# if not os.path.isdir('log'):
# os.mkdir('log')
# with open(f'log/{erep_time_now.strftime("%F")}.log', 'a', encoding='utf-8') as f:
# f.write(f'{txt}\n')
# if should_print:
# print(txt)
#
#
# def write_interactive_log(*args, **kwargs):
# kwargs.pop('should_print', None)
# _write_log(should_print=True, *args, **kwargs)
#
#
# def write_silent_log(*args, **kwargs):
# kwargs.pop('should_print', None)
# _write_log(should_print=False, *args, **kwargs)
def get_file(filepath: str) -> str: def get_file(filepath: str) -> str:
file = Path(filepath) file = Path(filepath)
if file.exists(): if file.exists():
@ -228,27 +208,6 @@ def deprecation(message):
warnings.warn(message, DeprecationWarning, stacklevel=2) warnings.warn(message, DeprecationWarning, stacklevel=2)
# def wait_for_lock(function):
# def wrapper(instance, *args, **kwargs):
# if not instance.concurrency_available.wait(600):
# e = 'Concurrency not freed in 10min!'
# instance.write_log(e)
# if instance.debug:
# instance.report_error(e)
# return None
# else:
# instance.concurrency_available.clear()
# try:
# ret = function(instance, *args, **kwargs)
# except Exception as e:
# instance.concurrency_available.set()
# raise e
# instance.concurrency_available.set()
# return ret
#
# return wrapper
def json_decode_object_hook( def json_decode_object_hook(
o: Union[Dict[str, Any], List[Any], int, float, str] o: Union[Dict[str, Any], List[Any], int, float, str]
) -> Union[Dict[str, Any], List[Any], int, float, str, datetime.date, datetime.datetime, datetime.timedelta]: ) -> Union[Dict[str, Any], List[Any], int, float, str, datetime.date, datetime.datetime, datetime.timedelta]:
@ -296,7 +255,7 @@ def json_dumps(obj, *args, **kwargs):
def b64json(obj: Union[Dict[str, Union[int, List[str]]], List[str]]): def b64json(obj: Union[Dict[str, Union[int, List[str]]], List[str]]):
if isinstance(obj, list): if isinstance(obj, list):
return b64encode(json.dumps(obj).replace(' ', '').encode('utf-8')).decode('utf-8') return b64encode(json.dumps(obj, separators=(',', ':')).encode('utf-8')).decode('utf-8')
elif isinstance(obj, (int, str)): elif isinstance(obj, (int, str)):
return obj return obj
elif isinstance(obj, dict): elif isinstance(obj, dict):
@ -305,31 +264,36 @@ def b64json(obj: Union[Dict[str, Union[int, List[str]]], List[str]]):
else: else:
from .classes import ErepublikException from .classes import ErepublikException
raise ErepublikException(f'Unhandled object type! obj is {type(obj)}') raise ErepublikException(f'Unhandled object type! obj is {type(obj)}')
return b64encode(json.dumps(obj).replace(' ', '').encode('utf-8')).decode('utf-8') return b64encode(json.dumps(obj, separators=(',', ':')).encode('utf-8')).decode('utf-8')
class ErepublikJSONEncoder(json.JSONEncoder): class ErepublikJSONEncoder(json.JSONEncoder):
def default(self, o): def default(self, o):
from erepublik.citizen import Citizen
if isinstance(o, Decimal):
return float(f"{o:.02f}")
elif isinstance(o, datetime.datetime):
return dict(__type__='datetime', date=o.strftime("%Y-%m-%d"), time=o.strftime("%H:%M:%S"),
tzinfo=str(o.tzinfo) if o.tzinfo else None)
elif isinstance(o, datetime.date):
return dict(__type__='date', date=o.strftime("%Y-%m-%d"))
elif isinstance(o, datetime.timedelta):
return dict(__type__='timedelta', days=o.days, seconds=o.seconds,
microseconds=o.microseconds, total_seconds=o.total_seconds())
elif isinstance(o, Response):
return dict(headers=dict(o.__dict__['headers']), url=o.url, text=o.text, status_code=o.status_code)
elif hasattr(o, 'as_dict'):
return o.as_dict
elif isinstance(o, set):
return list(o)
elif isinstance(o, Citizen):
return o.to_json()
try: try:
return super().default(o) from erepublik.citizen import Citizen
if isinstance(o, Decimal):
return float(f"{o:.02f}")
elif isinstance(o, datetime.datetime):
return dict(__type__='datetime', date=o.strftime("%Y-%m-%d"), time=o.strftime("%H:%M:%S"),
tzinfo=str(o.tzinfo) if o.tzinfo else None)
elif isinstance(o, datetime.date):
return dict(__type__='date', date=o.strftime("%Y-%m-%d"))
elif isinstance(o, datetime.timedelta):
return dict(__type__='timedelta', days=o.days, seconds=o.seconds,
microseconds=o.microseconds, total_seconds=o.total_seconds())
elif isinstance(o, Response):
return dict(headers=dict(o.__dict__['headers']), url=o.url, text=o.text, status_code=o.status_code)
elif hasattr(o, 'as_dict'):
return o.as_dict
elif isinstance(o, set):
return list(o)
elif isinstance(o, Citizen):
return o.to_json()
elif isinstance(o, Logger):
return str(o)
elif hasattr(o, '__dict__'):
return o.__dict__
else:
return super().default(o)
except Exception as e: # noqa except Exception as e: # noqa
return 'Object is not JSON serializable' return str(e)

View File

@ -1,20 +1,21 @@
bump2version==1.0.1 bump2version==1.0.1
coverage==5.4 coverage==5.5
edx-sphinx-theme==1.6.1 edx-sphinx-theme==2.0.0
flake8==3.8.4 flake8==3.8.4
ipython>=7.19.0 ipython>=7.21.0
jedi!=0.18.0 jedi!=0.18.0
isort==5.7.0 isort==5.7.0
pip==21.0 pip==21.0.1
pre-commit==2.9.3 pre-commit==2.10.1
pur==5.3.0 pur==5.3.0
PyInstaller==4.2 PyInstaller==4.2
PySocks==1.7.1 PySocks==1.7.1
pytest==6.2.2 pytest==6.2.2
pytz>=2020.5 pytz==2021.1
requests>=2.25.1 requests==2.25.1
requests-toolbelt==0.9.1
responses==0.12.1 responses==0.12.1
setuptools==52.0.0 setuptools==54.0.0
Sphinx==3.4.3 Sphinx==3.5.1
twine==3.3.0 twine==3.3.0
wheel==0.36.2 wheel==0.36.2

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 0.24.1 current_version = 0.24.2.4
commit = True commit = True
tag = True tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)? parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
@ -19,15 +19,15 @@ universal = 1
[flake8] [flake8]
exclude = docs,.git,log,debug,venv exclude = docs,.git,log,debug,venv
max-line-length = 120 max-line-length = 240
ignore = D100,D101,D102,D103 ignore = D100,D101,D102,D103
[pycodestyle] [pycodestyle]
max-line-length = 120 max-line-length = 240
exclude = .git,log,debug,venv, build exclude = .git,log,debug,venv, build
[mypy] [mypy]
python_version = 3.7 python_version = 3.8
check_untyped_defs = True check_untyped_defs = True
ignore_missing_imports = False ignore_missing_imports = False
warn_unused_ignores = True warn_unused_ignores = True
@ -36,4 +36,4 @@ warn_unused_configs = True
[isort] [isort]
multi_line_output = 2 multi_line_output = 2
line_length = 120 line_length = 240

View File

@ -12,9 +12,10 @@ with open('HISTORY.rst') as history_file:
history = history_file.read() history = history_file.read()
requirements = [ requirements = [
'pytz>=2020.0', 'PySocks==1.7.1',
'requests>=2.24.0,<2.26.0', 'pytz==2021.1',
'PySocks==1.7.1' 'requests==2.25.1',
'requests-toolbelt==0.9.1',
] ]
setup_requirements = [] setup_requirements = []
@ -50,6 +51,6 @@ setup(
test_suite='tests', test_suite='tests',
tests_require=test_requirements, tests_require=test_requirements,
url='https://github.com/eeriks/erepublik/', url='https://github.com/eeriks/erepublik/',
version='0.24.1', version='0.24.2.4',
zip_safe=False, zip_safe=False,
) )