Compare commits

..

33 Commits

Author SHA1 Message Date
d6e161d815 Bump version: 0.15.2 → 0.15.3 2019-08-24 15:44:22 +03:00
1285e95cec Inventory usage bugfixes 2019-08-24 10:48:04 +03:00
324db2c136 Fix new division numbering 2019-08-24 10:27:31 +03:00
1c6b41e41b Inventory updates and booster usage updates 2019-08-24 10:03:52 +03:00
a52552afb7 Use MyJSONEncoder from classes 2019-08-22 14:33:45 +03:00
28bfdc7b20 Use country ids from constant in utils 2019-08-22 14:33:07 +03:00
de1b734728 Don't check levelup when eating 2019-08-22 14:32:32 +03:00
fa5646ecfd Receive daily order 2019-08-22 14:32:00 +03:00
339392cfb0 Regex bugfix 2019-08-13 15:44:07 +03:00
7ebba458cb Max time timezones 2019-08-12 14:19:52 +03:00
4f436292af Organisation account fetcher - UNSTABLE 2019-08-12 12:54:58 +03:00
416c391d21 Improved levelup energy management 2019-08-11 01:45:49 +03:00
66c53ef985 Typo 2019-08-11 01:44:45 +03:00
3fa7d097fe Found awards switch to ASCII friendlier multiplier char - ✕ -> x
Localize max div end time
2019-08-05 11:55:39 +03:00
6af9675c39 Added gameTokensMarket endpoint 2019-08-05 10:20:36 +03:00
2343a6c6c8 WeeklyChallange end energy saver tweaks 2019-08-05 10:20:19 +03:00
7e56f01a38 Added gameTokensMarket endpoint 2019-08-05 10:13:48 +03:00
e14cbc18e9 Bump version: 0.15.1 → 0.15.2 2019-08-01 17:24:07 +03:00
048ce798dd All battle div div containing dicts where reference to the same object 2019-08-01 17:23:24 +03:00
e5b7cde044 Bump version: 0.15.0 → 0.15.1 2019-08-01 15:06:34 +03:00
6bbc7a1f64 Typehinting and battle/war stuff - last battle, attackable regions if CP, etc 2019-08-01 15:05:44 +03:00
4eccb339bb Deploy bombs 2019-08-01 15:04:16 +03:00
dbeb6e9ba5 Market scraper updates 2019-08-01 09:37:26 +03:00
9c9bb5ae40 Region attacking post updated 2019-08-01 09:37:08 +03:00
d8eb69f82a Travel bugfix and market scraper fixup 2019-07-31 22:39:54 +03:00
42c430213f Minor updates 2019-07-31 21:41:24 +03:00
39dbcaa27d Continuing work on return Response free Citizen class 2019-07-31 21:41:08 +03:00
8911adb81c Traveling improved for Citizen class 2019-07-31 21:38:34 +03:00
7927c162f8 More precise type hint 2019-07-31 21:37:29 +03:00
92b7c45a7d Report promos to erep.lv 2019-07-31 21:36:57 +03:00
53257487d8 Write on country wall update 2019-07-30 18:32:12 +03:00
8690c4d3f2 minor fix 2019-07-30 11:10:35 +03:00
43c6bce160 minor fix 2019-07-30 11:10:09 +03:00
7 changed files with 482 additions and 317 deletions

View File

@ -6,8 +6,8 @@ eRepublik script
.. image:: https://img.shields.io/pypi/v/erepublik.svg
:target: https://pypi.python.org/pypi/erepublik
.. image:: https://readthedocs.org/projects/erepublik/badge/?version=latest
:target: https://erepublik.readthedocs.io/en/latest/?badge=latest
.. image:: https://readthedocs.org/projects/erepublik_script/badge/?version=latest
:target: https://erepublik_script.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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
@ -26,11 +26,13 @@ class ErepublikNetworkException(Exception):
class MyCompanies:
work_units: int = 0
next_ot_time: datetime.datetime
holdings: Dict[int, Dict] = dict()
companies: Dict[int, Dict] = dict()
holdings: Dict[int, Dict] = None
companies: Dict[int, Dict] = None
ff_lockdown: int = 0
def __init__(self):
self.holdings = dict()
self.companies = dict()
self.next_ot_time = utils.now()
def prepare_holdings(self, holdings: dict):
@ -41,7 +43,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 +472,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})
@ -478,6 +480,9 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
def _get_country_military(self, country: str) -> Response:
return self.get("{}/country/military/{}".format(self.url, country))
def _get_economy_citizen_accounts(self, organisation_id: int) -> Response:
return self.get("{}/economy/citizen-accounts/{}".format(self.url, organisation_id))
def _get_economy_inventory_items(self) -> Response:
return self.get("{}/economy/inventory-items/".format(self.url))
@ -549,6 +554,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)
@ -612,6 +620,10 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
data = dict(_token=self.token, buttonColor=color)
return self.post("{}/main/eat".format(self.url), params=data)
def _post_economy_activate_booster(self, quality: int, duration: int, kind: str) -> Response:
data = dict(type=kind, quality=quality, duration=duration, fromInventory=True)
return self.post("{}/economy/activateBooster".format(self.url), data=data)
def _post_economy_activate_house(self, quality: int) -> Response:
data = {"action": "activate", "quality": quality, "type": "house", "_token": self.token}
return self.post("{}/economy/activateHouse".format(self.url), data=data)
@ -645,6 +657,11 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
data = dict(_token=self.token, personalOffers=int(personal), page=page, currencyId=currency)
return self.post("{}/economy/exchange/retrieve/".format(self.url), data=data)
def _post_economy_game_tokens_market(self, action: str) -> Response:
assert action in ['retrieve', ]
data = dict(_token=self.token, action=action)
return self.post("{}/economy/gameTokensMarketAjax".format(self.url), data=data)
def _post_economy_job_market_apply(self, citizen: int, salary: int) -> Response:
data = dict(_token=self.token, citizenId=citizen, salary=salary)
return self.post("{}/economy/job-market-apply".format(self.url), data=data)
@ -737,7 +754,7 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
data = dict(csrf_token=self.token, citizen_email=email, citizen_password=password, remember='on')
return self.post("{}/login".format(self.url), data=data)
def _post_messages_alert(self, notification_ids: list) -> Response:
def _post_messages_alert(self, notification_ids: List[int]) -> Response:
data = {"_token": self.token, "delete_alerts[]": notification_ids, "deleteAllAlerts": "1", "delete": "Delete"}
return self.post("{}/main/messages-alerts/1".format(self.url), data=data)
@ -745,12 +762,20 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
url_pk = 0 if len(citizens) > 1 else str(citizens[0])
data = dict(citizen_name=",".join([str(x) for x in citizens]),
citizen_subject=subject, _token=self.token, citizen_message=body)
return self.post("{}/main/messages-compose/{}}".format(self.url, url_pk), data=data)
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)
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)
@ -771,9 +796,9 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
def _post_travel_data(self, **kwargs) -> Response:
return self.post("{}/main/travelData".format(self.url), data=dict(_token=self.token, **kwargs))
def _post_wars_attack_region(self, war: int, region: int) -> Response:
data = dict(_token=self.token)
return self.post("{}/wars/attack-region/{}/{}".format(self.url, war, region), data=data)
def _post_wars_attack_region(self, war_id: int, region_id: int, region_name: str) -> Response:
data = {'_token': self.token, 'warId': war_id, 'regionName': region_name, 'regionNameConfirm': region_name}
return self.post('{}/wars/attack-region/{}/{}'.format(self.url, war_id, region_id), data=data)
def _post_weekly_challenge_reward(self, reward_id: int) -> Response:
data = dict(_token=self.token, rewardId=reward_id)
@ -959,8 +984,8 @@ class MyJSONEncoder(JSONEncoder):
class BattleSide:
id: int
points: int
deployed: List[int] = list()
allies: List[int] = list()
deployed: List[int] = None
allies: List[int] = None
def __init__(self, country_id: int, points: int, allies: List[int], deployed: List[int]):
self.id = country_id
@ -972,8 +997,8 @@ class BattleSide:
class BattleDivision:
end: datetime.datetime
epic: bool
dom_pts: Dict[str, int] = dict()
wall: Dict[str, Union[int, float]] = dict()
dom_pts: Dict[str, int] = None
wall: Dict[str, Union[int, float]] = None
@property
def div_end(self) -> bool:
@ -982,8 +1007,8 @@ class BattleDivision:
def __init__(self, end: datetime.datetime, epic: bool, inv_pts: int, def_pts: int, wall_for: int, wall_dom: float):
self.end = end
self.epic = epic
self.dom_pts.update({"inv": inv_pts, "def": def_pts})
self.wall.update({"for": wall_for, "dom": wall_dom})
self.dom_pts = dict({"inv": inv_pts, "def": def_pts})
self.wall = dict({"for": wall_for, "dom": wall_dom})
class Battle(object):
@ -995,13 +1020,13 @@ class Battle(object):
start: datetime.datetime = None
invader: BattleSide = None
defender: BattleSide = None
div: Dict[int, BattleDivision] = dict()
div: Dict[int, BattleDivision] = None
@property
def is_air(self) -> bool:
return not bool(self.zone_id % 4)
def __init__(self, battle: dict):
def __init__(self, battle: Dict[str, Any]):
self.id = int(battle.get('id', 0))
self.war_id = int(battle.get('war_id', 0))
self.zone_id = int(battle.get('zone_id', 0))
@ -1018,12 +1043,13 @@ class Battle(object):
[row.get('id') for row in battle.get('def', {}).get('ally_list')],
[row.get('id') for row in battle.get('def', {}).get('ally_list') if row['deployed']])
self.div = {}
for div, data in battle.get('div', {}).items():
div = int(div)
div = int(data.get('div'))
if data.get('end'):
end = datetime.datetime.fromtimestamp(data.get('end'), tz=utils.erep_tz)
else:
end = datetime.datetime.max
end = utils.localize_dt(datetime.datetime.max - datetime.timedelta(days=1))
battle_div = BattleDivision(
end=end, epic=data.get('epic_type') in [1, 5],

View File

@ -7,16 +7,11 @@ import sys
import time
import traceback
import unicodedata
from collections import deque
from decimal import Decimal
from json import JSONEncoder
from pathlib import Path
from typing import Union
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",
@ -24,7 +19,6 @@ __all__ = ["FOOD_ENERGY", "COMMIT_ID", "COUNTRIES", "erep_tz",
"write_silent_log", "write_interactive_log", "get_file", "write_file",
"send_email", "normalize_html_json", "process_error", ]
FOOD_ENERGY = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20)
COMMIT_ID = "7b92e19"
@ -85,40 +79,37 @@ 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'}
class MyJSONEncoder(JSONEncoder):
def default(self, o):
if isinstance(o, Decimal):
return float("{:.02f}".format(o))
elif isinstance(o, datetime.datetime):
return dict(__type__='datetime', year=o.year, month=o.month, day=o.day, hour=o.hour, minute=o.minute,
second=o.second, microsecond=o.microsecond)
elif isinstance(o, datetime.date):
return dict(__type__='date', year=o.year, month=o.month, day=o.day)
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=o.headers.__dict__, url=o.url, text=o.text)
elif hasattr(o, '__dict__'):
return o.__dict__
elif isinstance(o, deque):
return list(o)
return super().default(o)
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', }
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 +228,10 @@ 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, player=None, local_vars=dict, promo: bool = False, captcha: bool = False):
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 = {}
from erepublik import Citizen
file_content_template = "<html><head><title>{title}</title></head><body>{body}</body></html>"
@ -277,6 +271,7 @@ def send_email(name: str, content: list, player=None, local_vars=dict, promo: bo
if local_vars:
if "state_thread" in local_vars:
local_vars.pop('state_thread', None)
from erepublik.classes import MyJSONEncoder
files.append(('file', ("local_vars.json", json.dumps(local_vars, indent=2,
cls=MyJSONEncoder, sort_keys=True), "application/json")))
if isinstance(player, Citizen):
@ -319,7 +314,11 @@ def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commi
send_email(name, bugtrace, citizen, local_vars=trace)
def slugify(value, allow_unicode=False):
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) -> str:
"""
Function copied from Django2.2.1 django.utils.text.slugify
Convert to ASCII if 'allow_unicode' is False. Convert spaces to hyphens.

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.15.0
current_version = 0.15.3
commit = True
tag = True

View File

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