Compare commits

...

8 Commits

Author SHA1 Message Date
7f3bd9b864 Bump version: 0.24.0.2 → 0.24.0.3 2021-01-28 01:46:11 +02:00
9154d2e493 Captcha solving 2021-01-28 01:46:03 +02:00
8e8b882ace Bump version: 0.24.0.1 → 0.24.0.2 2021-01-28 00:37:12 +02:00
d7b020c7ea bugfix 2021-01-28 00:37:10 +02:00
cb567bf5c0 Bump version: 0.24.0 → 0.24.0.1 2021-01-28 00:31:53 +02:00
bb6d1be1b5 bugfix 2021-01-28 00:31:46 +02:00
c546c8f5eb Bump version: 0.23.4.14 → 0.24.0 2021-01-27 23:43:48 +02:00
9acc2d2e65 Lets deploy! Preperation for 8th of February 2021-01-27 23:43:29 +02:00
7 changed files with 142 additions and 25 deletions

View File

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

View File

@ -141,6 +141,25 @@ class CitizenBaseAPI:
url = f'http://{username}:{password}@{host}:{port}' if username and password else f'socks5://{host}:{port}' url = f'http://{username}:{password}@{host}:{port}' if username and password else f'socks5://{host}:{port}'
self._req.proxies = dict(http=url) self._req.proxies = dict(http=url)
def _get_main_session_captcha(self) -> Response:
return self.get(f'{self.url}/main/sessionCaptcha')
def _get_main_session_unlock_popup(self) -> Response:
return self.get(f'{self.url}/main/sessionUnlockPopup')
def _post_main_session_get_challenge(self, captcha_id: int) -> Response:
env = dict(l=['tets', ], s=[], c=["l_chathwe", "l_chatroom"], m=0)
data = dict(_token=self.token, captchaId=captcha_id, env=utils.b64json(env))
return self.post(f'{self.url}/main/sessionGetChallenge', data=data)
def _post_main_session_unlock(
self, captcha: int, image: str, challenge: str, coords: List[Dict[str, int]], src: str
) -> Response:
env = dict(l=['tets', ], s=[], c=["l_chathwe", "l_chatroom"], m=0)
data = dict(_token=self.token, captchaId=captcha, imageId=image, challengeId=challenge,
clickMatrix=coords, isMobile=0, env=utils.b64json(env), src=src)
return self.post(f'{self.url}/main/sessionUnlock', data=data)
class ErepublikAnniversaryAPI(CitizenBaseAPI): class ErepublikAnniversaryAPI(CitizenBaseAPI):
def _post_main_collect_anniversary_reward(self) -> Response: def _post_main_collect_anniversary_reward(self) -> Response:
@ -455,9 +474,21 @@ class ErepublikMilitaryAPI(CitizenBaseAPI):
data = dict(sideId=side_id, battleId=battle_id, _token=self.token, battleZoneId=zone_id) data = dict(sideId=side_id, battleId=battle_id, _token=self.token, battleZoneId=zone_id)
return self.post(f"{self.url}/military/fight-shooot/{battle_id}", data=data) return self.post(f"{self.url}/military/fight-shooot/{battle_id}", data=data)
def _post_fight_deploy_deploy_report_data(self, deployment_id: int): def _post_fight_deploy_deploy_report_data(self, deployment_id: int) -> Response:
data = dict(_token=self.token, deploymentId=deployment_id) data = dict(_token=self.token, deploymentId=deployment_id)
return self.post(f"{self.url}/military/fightDeploy-deployReportData", json=data) return self.post(f"{self.url}/military/fightDeploy-deployReportData", data=data)
def _post_fight_deploy_get_inventory(self, battle_id: int, side_id: int, battle_zone_id: int) -> Response:
data = dict(_token=self.token, battleId=battle_id, sideCountryId=side_id, battleZoneId=battle_zone_id)
return self.post(f"{self.url}/military/fightDeploy-getInventory", data=data)
def _post_fight_deploy_start_deploy(
self, battle_id: int, side_id: int, battle_zone_id: int, energy: int, weapon: int, **kwargs
) -> Response:
data = dict(_token=self.token, battleId=battle_id, battleZoneId=battle_zone_id, sideCountryId=side_id,
weaponQuality=weapon, totalEnergy=energy, **kwargs)
return self.post(f"{self.url}/military/fightDeploy-startDeploy", data=data)
class ErepublikPoliticsAPI(CitizenBaseAPI): class ErepublikPoliticsAPI(CitizenBaseAPI):

View File

@ -27,6 +27,7 @@ class BaseCitizen(access_points.CitizenAPI):
eb_normal: int = 0 eb_normal: int = 0
eb_double: int = 0 eb_double: int = 0
eb_small: int = 0 eb_small: int = 0
eb_triple: int = 0
division: int = 0 division: int = 0
maverick: bool = False maverick: bool = False
@ -241,6 +242,28 @@ class BaseCitizen(access_points.CitizenAPI):
""" """
self._update_inventory_data(self._get_economy_inventory_items().json()) self._update_inventory_data(self._get_economy_inventory_items().json())
def do_captcha_challenge(self, retry: int = 0) -> bool:
r = self._get_main_session_captcha()
data = re.search(r'\$j\.extend\(SERVER_DATA,([^)]+)\)', r.text)
if data:
data = utils.json_loads(utils.normalize_html_json(data.group(1)))
captcha_id = data.get('sessionValidation', {}).get("captchaId")
captcha_data = self._post_main_session_get_challenge(captcha_id).json()
coordinates = self.solve_captcha(captcha_data.get('src'))
r = self._post_main_session_unlock(
captcha_id, captcha_data['imageId'], captcha_data['challengeId'], coordinates, captcha_data['src']
).json()
if not r.get('error') and r.get('verified'):
return True
else:
self.report_error('Captcha failed!')
if retry < 6:
return self.do_captcha_challenge(retry+1)
return False
def solve_captcha(self, src: str) -> List[Dict[str, int]]:
raise NotImplemented
@property @property
def inventory(self) -> classes.Inventory: def inventory(self) -> classes.Inventory:
return self.get_inventory() return self.get_inventory()
@ -271,7 +294,7 @@ class BaseCitizen(access_points.CitizenAPI):
return return
self._last_inventory_update = self.now self._last_inventory_update = self.now
self.food.update(q1=0, q2=0, q3=0, q4=0, q5=0, q6=0, q7=0) self.food.update(q1=0, q2=0, q3=0, q4=0, q5=0, q6=0, q7=0)
self.eb_small = self.eb_double = self.eb_normal = 0 self.eb_triple = self.eb_small = self.eb_double = self.eb_normal = 0
active_items: types.InvFinal = {} active_items: types.InvFinal = {}
if data.get('activeEnhancements', {}).get('items', {}): if data.get('activeEnhancements', {}).get('items', {}):
for item_data in data.get('activeEnhancements', {}).get('items', {}).values(): for item_data in data.get('activeEnhancements', {}).get('items', {}).values():
@ -289,7 +312,8 @@ class BaseCitizen(access_points.CitizenAPI):
expiration_info = [_expire_value_to_python(v) for v in expire_info['value']] expiration_info = [_expire_value_to_python(v) for v in expire_info['value']]
if not item_data.get('icon') and item_data.get('isPackBooster'): if not item_data.get('icon') and item_data.get('isPackBooster'):
item_data['icon'] = f"//www.erepublik.com/images/icons/boosters/52px/{item_data.get('type')}.png" item_data['icon'] = f"//www.erepublik.com/images/icons/boosters/52px/{item_data.get('type')}.png"
icon = item_data['icon'] if item_data['icon'] else "//www.erepublik.net/images/modules/manager/tab_storage.png" icon = item_data['icon'] if item_data[
'icon'] else "//www.erepublik.net/images/modules/manager/tab_storage.png"
inv_item: types.InvFinalItem = dict( inv_item: types.InvFinalItem = dict(
name=item_data.get('name'), time_left=item_data['active']['time_left'], icon=icon, name=item_data.get('name'), time_left=item_data['active']['time_left'], icon=icon,
kind=kind, expiration=expiration_info, quality=item_data.get('quality', 0) kind=kind, expiration=expiration_info, quality=item_data.get('quality', 0)
@ -333,15 +357,12 @@ class BaseCitizen(access_points.CitizenAPI):
self.eb_normal = amount self.eb_normal = amount
elif q == 11: elif q == 11:
self.eb_double = amount self.eb_double = amount
elif q == 13: item_data.update(token='energy_bar')
self.eb_small += amount elif 11 < q < 17:
elif q == 14:
self.eb_small += amount
elif q == 15:
self.eb_small += amount self.eb_small += amount
item_data.update(token='energy_bar') item_data.update(token='energy_bar')
elif q == 16: elif q == 17:
self.eb_small += amount self.eb_triple = amount
item_data.update(token='energy_bar') item_data.update(token='energy_bar')
kind = re.sub(r'_q\d\d*', "", item_data.get('token')) kind = re.sub(r'_q\d\d*', "", item_data.get('token'))
@ -527,7 +548,7 @@ class BaseCitizen(access_points.CitizenAPI):
ret = super().as_dict ret = super().as_dict
ret.update( ret.update(
name=self.name, __str__=self.__str__(), name=self.name, __str__=self.__str__(),
ebs=dict(normal=self.eb_normal, double=self.eb_double, small=self.eb_small), ebs=dict(normal=self.eb_normal, double=self.eb_double, small=self.eb_small, triple=self.eb_triple),
promos=self.promos, inventory=self._inventory.as_dict, ot_points=self.ot_points, food=self.food, promos=self.promos, inventory=self._inventory.as_dict, ot_points=self.ot_points, food=self.food,
division=self.division, maveric=self.maverick, eday=self.eday, wheel_of_fortune=self.wheel_of_fortune, division=self.division, maveric=self.maverick, eday=self.eday, wheel_of_fortune=self.wheel_of_fortune,
debug=self.debug, debug=self.debug,
@ -697,12 +718,10 @@ class BaseCitizen(access_points.CitizenAPI):
self.eb_normal -= amount self.eb_normal -= amount
elif q == '11': elif q == '11':
self.eb_double -= amount self.eb_double -= amount
elif q == '12': elif 11 < int(q) < 17:
self.eb_small -= amount
elif q == '15':
self.eb_small -= amount
elif q == '16':
self.eb_small -= amount self.eb_small -= amount
elif q == '17':
self.eb_triple -= amount
next_recovery = r_json.get('food_remaining_reset').split(":") next_recovery = r_json.get('food_remaining_reset').split(":")
self.energy.set_reference_time( self.energy.set_reference_time(
utils.good_timedelta(self.now, timedelta(seconds=int(next_recovery[1]) * 60 + int(next_recovery[2]))) utils.good_timedelta(self.now, timedelta(seconds=int(next_recovery[1]) * 60 + int(next_recovery[2])))
@ -743,6 +762,9 @@ class BaseCitizen(access_points.CitizenAPI):
pass pass
if response.status_code >= 400: if response.status_code >= 400:
self.r = response self.r = response
if response.text == 'Please verify your account.':
self.do_captcha_challenge()
return True
if response.status_code >= 500: if response.status_code >= 500:
if self.restricted_ip: if self.restricted_ip:
self._req.cookies.clear() self._req.cookies.clear()
@ -2230,11 +2252,59 @@ class CitizenMilitary(CitizenTravel):
if division.wall['dom'] == 50 or division.wall['dom'] > 98: if division.wall['dom'] == 50 or division.wall['dom'] > 98:
yield division, division.wall['for'] == battle.invader.country.id yield division, division.wall['for'] == battle.invader.country.id
def report_fighting(self, battle: classes.Battle, invader: bool, division: classes.BattleDivision, damage: float, hits: int): def report_fighting(self, battle: classes.Battle, invader: bool, division: classes.BattleDivision, damage: float,
hits: int):
self.reporter.report_fighting(battle, invader, division, damage, hits) self.reporter.report_fighting(battle, invader, division, damage, hits)
if self.config.telegram: if self.config.telegram:
self.telegram.report_fight(battle, invader, division, damage, hits) self.telegram.report_fight(battle, invader, division, damage, hits)
def get_deploy_inventory(self, division: classes.BattleDivision, side: classes.BattleSide):
ret = self._post_fight_deploy_get_inventory(division.battle.id, side.id, division.id).json()
# if ret.get('recoverableEnergyBuyFood'):
# self.buy_food()
# return self.get_deploy_inventory(division, side)
if ret.get('captcha'):
while not self.do_captcha_challenge():
self.sleep(5)
return ret
def deploy(self, division: classes.BattleDivision, side: classes.BattleSide, energy: int):
_energy = int(energy)
deploy_inv = self.get_deploy_inventory(division, side)
if not deploy_inv['minEnergy'] <= energy <= deploy_inv['maxEnergy']:
return 0
energy_sources = {}
source_idx = 0
recoverable = deploy_inv['recoverableEnergy']
for source in reversed(sorted(deploy_inv['energySources'], key=lambda s: (s['type'], s.get('quality', 0)))):
if source['type'] == 'pool':
_energy -= source['energy']
elif source['type'] in ['food', 'energy_bar']:
recovers = source['energy'] / source['amount']
amount = recoverable // recovers
amount = amount if amount < source['amount'] else source['amount']
if amount > 0:
energy_sources.update({f'energySources[{source_idx}][quality]': source['quality']})
energy_sources.update({f'energySources[{source_idx}][amount]': amount})
source_idx += 1
used_energy = amount * recovers
recoverable -= used_energy
_energy -= used_energy
energy -= _energy
weapon_q = -1
weapon_strength = 0
if not division.is_air:
for weapon in sorted(deploy_inv['weapons'], key=lambda w: w['damageperHit']):
if (weapon['damageperHit'] or 0) > weapon_strength and (weapon['amount'] or 0) > 50:
weapon_q = weapon['quality']
r = self._post_fight_deploy_start_deploy(
division.battle.id, side.id, division.id, energy, weapon_q, **energy_sources
).json()
self.write_log(r.get('message'))
if r.get('error'):
self.report_error('Deploy failed!')
return energy
class CitizenPolitics(BaseCitizen): class CitizenPolitics(BaseCitizen):
def get_country_parties(self, country: constants.Country = None) -> dict: def get_country_parties(self, country: constants.Country = None) -> dict:

View File

@ -7,6 +7,7 @@ import time
import traceback import traceback
import unicodedata import unicodedata
import warnings import warnings
from base64 import b64encode
from decimal import Decimal from decimal import Decimal
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Union from typing import Any, Dict, List, Union
@ -429,3 +430,17 @@ def json_load(f, **kwargs):
def json_loads(s: str, **kwargs): def json_loads(s: str, **kwargs):
kwargs.update(object_hook=json_decode_object_hook) kwargs.update(object_hook=json_decode_object_hook)
return json.loads(s, **kwargs) return json.loads(s, **kwargs)
def b64json(obj: Union[Dict[str, Union[int, List[str]]], List[str]]):
if isinstance(obj, list):
return b64encode(json.dumps(obj).encode('utf-8')).decode('utf-8')
elif isinstance(obj, (int, str)):
return obj
elif isinstance(obj, dict):
for k, v in obj.items():
obj[k] = b64json(v)
else:
from .classes import ErepublikException
raise ErepublikException(f'Unhandled object type! obj is {type(obj)}')
return b64encode(json.dumps(obj).encode('utf-8')).decode('utf-8')

View File

@ -1,19 +1,20 @@
bump2version==1.0.1 bump2version==1.0.1
coverage==5.3.1 coverage==5.4
edx-sphinx-theme==1.6.1 edx-sphinx-theme==1.6.1
flake8==3.8.4 flake8==3.8.4
ipython>=7.19.0 ipython>=7.19.0
jedi!=0.18.0
isort==5.7.0 isort==5.7.0
pip==20.3.3 pip==21.0
pre-commit==2.9.3 pre-commit==2.9.3
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.1 pytest==6.2.2
pytz>=2020.5 pytz>=2020.5
requests>=2.25.1 requests>=2.25.1
responses==0.12.1 responses==0.12.1
setuptools==51.3.3 setuptools==52.0.0
Sphinx==3.4.3 Sphinx==3.4.3
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.23.4.14 current_version = 0.24.0.3
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+)?

View File

@ -50,6 +50,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.23.4.14', version='0.24.0.3',
zip_safe=False, zip_safe=False,
) )