Compare commits

..

15 Commits

Author SHA1 Message Date
149071ae86 Bump version: 0.19.4 → 0.19.4.1 2020-02-20 12:32:13 +02:00
72375f40ca Dev versioning 2020-02-20 09:58:03 +02:00
fd1880c50f Some notifications are still being displayed the old way 2020-02-19 18:52:40 +02:00
f73f2b7b9f Bump version: 0.19.3 → 0.19.4 2020-02-18 20:12:02 +02:00
f6433908b4 New notifications API 2020-02-18 20:11:34 +02:00
2fd317153f Bump version: 0.19.2 → 0.19.3 2020-01-27 01:54:03 +02:00
256a180bd6 if error occures on main thread - simplify error logging 2020-01-27 01:53:52 +02:00
c7dbeb2078 Bump version: 0.19.1 → 0.19.2 2020-01-26 20:45:18 +02:00
8e5ae0320a Merge branch 'bugfix'
* bugfix:
  Hey Plato! If You're reading this - fix your variable types and there will be 90% less bugs in Your code!!!
  Some TelegramBot tweaks
  when full energy update citizen info would stop working because trying to get timedelta from now - _last_full_energy_report
  Too broad exception was cought without notifying about actual error - when Telegram isn't enabled
2020-01-26 20:44:43 +02:00
5c258d7aae Hey Plato! If You're reading this - fix your variable types and there will be 90% less bugs in Your code!!!
{'weaponId': 6, 'weaponQuantity': 0, 'damage': 120}
{'weaponId': '7', 'weaponQuantity': 1185, 'inClip': 7, 'damage': 200}
{'weaponId': 10, 'weaponQuantity': 0, 'damage': 100}
2020-01-26 20:44:21 +02:00
75b43fc455 Merge pull request #2 from eeriks/eeriks-patch-1
GHSA-7fcj-pq9j-wh2r
2020-01-17 15:41:02 +02:00
2362dc51e8 GHSA-7fcj-pq9j-wh2r
https://github.com/advisories/GHSA-7fcj-pq9j-wh2r
2020-01-17 15:40:34 +02:00
a2cf479135 Some TelegramBot tweaks 2020-01-16 13:49:24 +02:00
00b87dc832 when full energy update citizen info would stop working because trying to get timedelta from now - _last_full_energy_report 2020-01-14 13:43:10 +02:00
0dd1ae9ac5 Too broad exception was cought without notifying about actual error - when Telegram isn't enabled 2020-01-14 13:42:34 +02:00
7 changed files with 103 additions and 39 deletions

View File

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

View File

@ -144,7 +144,7 @@ class Citizen(CitizenAPI):
return return
html = resp.text html = resp.text
self.check_for_new_medals(html) self.check_for_medals(html)
re_token = re.search(r'var csrfToken = \'(\w{32})\'', html) re_token = re.search(r'var csrfToken = \'(\w{32})\'', html)
re_login_token = re.search(r'<input type="hidden" id="_token" name="_token" value="(\w{32})">', html) re_login_token = re.search(r'<input type="hidden" id="_token" name="_token" value="(\w{32})">', html)
if re_token: if re_token:
@ -156,7 +156,7 @@ class Citizen(CitizenAPI):
raise ErepublikException("Something went wrong! Can't find token in page! Exiting!") raise ErepublikException("Something went wrong! Can't find token in page! Exiting!")
try: try:
self.update_citizen_info(resp.text) self.update_citizen_info(resp.text)
except: except (AttributeError, json.JSONDecodeError, ValueError, KeyError):
pass pass
def _login(self): def _login(self):
@ -212,15 +212,15 @@ class Citizen(CitizenAPI):
return self.get(url, **kwargs) return self.get(url, **kwargs)
try: try:
self.update_citizen_info(html=response.text) self.update_citizen_info(response.text)
except: except (AttributeError, json.JSONDecodeError, ValueError, KeyError):
pass pass
if self._errors_in_response(response): if self._errors_in_response(response):
self.get_csrf_token() self.get_csrf_token()
self.get(url, **kwargs) self.get(url, **kwargs)
else: else:
self.check_for_new_medals(response.text) self.check_for_medals(response.text)
self.r = response self.r = response
return response return response
@ -259,12 +259,12 @@ class Citizen(CitizenAPI):
json.update({"_token": self.token}) json.update({"_token": self.token})
response = self.post(url, data=data, json=json, **kwargs) response = self.post(url, data=data, json=json, **kwargs)
else: else:
self.check_for_new_medals(response.text) self.check_for_medals(response.text)
self.r = response self.r = response
return response return response
def check_for_new_medals(self, html: str): def check_for_medals(self, html: str):
new_medals = re.findall(r'(<div class="home_reward reward achievement">.*?<div class="bottom"></div>\s*</div>)', new_medals = re.findall(r'(<div class="home_reward reward achievement">.*?<div class="bottom"></div>\s*</div>)',
html, re.M | re.S | re.I) html, re.M | re.S | re.I)
data: Dict[Tuple[str, Union[float, str]], Dict[str, Union[int, str, float]]] = {} data: Dict[Tuple[str, Union[float, str]], Dict[str, Union[int, str, float]]] = {}
@ -311,6 +311,44 @@ class Citizen(CitizenAPI):
self.telegram.report_medal(f"Level *{level}*") self.telegram.report_medal(f"Level *{level}*")
self.reporter.report_action("LEVEL_UP", value=level) self.reporter.report_action("LEVEL_UP", value=level)
def check_for_notification_medals(self):
notifications = self._get_main_citizen_daily_assistant().json()
data: Dict[Tuple[str, Union[float, str]], Dict[str, Union[int, str, float]]] = {}
for medal in notifications.get('notifications', []):
if medal.get('details', {}).get('type') == "citizenAchievement":
params = medal.get('details', {}).get('achievement')
about = medal.get('details').get('description')
title = medal.get('title')
# 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)})
if params.get('ccValue'):
reward = params.get('ccValue')
currency = "Currency"
elif params.get('goldValue'):
reward = params.get('goldValue')
currency = "Gold"
else:
reward = params.get('energyValue')
currency = "Energy"
if (title, reward) not in data:
data[(title, reward)] = {'about': about, 'kind': title, 'reward': reward, "count": 1,
"currency": currency, "params": params}
else:
data[(title, reward)]['count'] += 1
self._post_main_global_alerts_close(medal.get('id'))
if data:
msgs = ["{count} x {kind},"
" totaling {} {currency}".format(d["count"] * d["reward"], **d) for d in data.values()]
msgs = "\n".join(msgs)
self.telegram.report_medal(msgs)
self.write_log(f"Found awards:\n{msgs}")
for info in data.values():
self.reporter.report_action("NEW_MEDAL", info)
def update_all(self, force_update=False): def update_all(self, force_update=False):
# Do full update max every 5 min # Do full update max every 5 min
if good_timedelta(self.__last_full_update, timedelta(minutes=5)) > self.now and not force_update: if good_timedelta(self.__last_full_update, timedelta(minutes=5)) > self.now and not force_update:
@ -324,12 +362,14 @@ class Citizen(CitizenAPI):
self.update_money() self.update_money()
self.update_weekly_challenge() self.update_weekly_challenge()
self.send_state_update() self.send_state_update()
self.check_for_notification_medals()
def update_citizen_info(self, html: str = None): def update_citizen_info(self, html: str = None):
""" """
Gets main page and updates most information about player Gets main page and updates most information about player
""" """
if html is None: if html is None:
self.check_for_notification_medals()
self._get_main() self._get_main()
return return
ugly_js = re.search(r'"promotions":\s*(\[{?.*?}?])', html).group(1) ugly_js = re.search(r'"promotions":\s*(\[{?.*?}?])', html).group(1)
@ -2195,11 +2235,14 @@ class Citizen(CitizenAPI):
while not isinstance(available_weapons, list): while not isinstance(available_weapons, list):
available_weapons = self._get_military_show_weapons(battle_id).json() available_weapons = self._get_military_show_weapons(battle_id).json()
weapon_quality = -1 weapon_quality = -1
weapon_damage = 0
if not battle.is_air: if not battle.is_air:
for weapon in available_weapons: for weapon in available_weapons:
if weapon['weaponId'] == 7 and weapon['weaponQuantity'] > 30: try:
weapon_quality = 7 if weapon['weaponQuantity'] > 30 and weapon['damage'] > weapon_damage:
break weapon_quality = int(weapon['weaponId'])
except ValueError:
pass
return self.change_weapon(battle_id, weapon_quality) return self.change_weapon(battle_id, weapon_quality)
def change_weapon(self, battle_id: int, weapon_quality: int) -> int: def change_weapon(self, battle_id: int, weapon_quality: int) -> int:

View File

@ -524,9 +524,12 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
def _get_main_citizen_profile_json(self, player_id: int) -> Response: def _get_main_citizen_profile_json(self, player_id: int) -> Response:
return self.get("{}/main/citizen-profile-json/{}".format(self.url, player_id)) return self.get("{}/main/citizen-profile-json/{}".format(self.url, player_id))
def _get_main_citizen_daily_assistant(self) -> Response: def _get_main_citizen_notifications(self) -> Response:
return self.get("{}/main/citizenDailyAssistant".format(self.url)) return self.get("{}/main/citizenDailyAssistant".format(self.url))
def _get_main_citizen_daily_assistant(self) -> Response:
return self.get("{}/main/citizenNotifications".format(self.url))
def _get_main_city_data_residents(self, city: int, page: int = 1, params: Mapping[str, Any] = None) -> Response: def _get_main_city_data_residents(self, city: int, page: int = 1, params: Mapping[str, Any] = None) -> Response:
if params is None: if params is None:
params = {} params = {}
@ -673,6 +676,10 @@ Class for unifying eRepublik known endpoints and their required/optional paramet
data = dict(_token=self.token, articleId=article_id, amount=amount) data = dict(_token=self.token, articleId=article_id, amount=amount)
return self.post("{}/main/donate-article".format(self.url), data=data) return self.post("{}/main/donate-article".format(self.url), data=data)
def _post_main_global_alerts_close(self, alert_id: int) -> Response:
data = dict(_token=self.token, alert_id=alert_id)
return self.post("{}/main/global-alerts/close".format(self.url), data=data)
def _post_delete_message(self, msg_id: list) -> Response: def _post_delete_message(self, msg_id: list) -> Response:
data = {"_token": self.token, "delete_message[]": msg_id} data = {"_token": self.token, "delete_message[]": msg_id}
return self.post("{}/main/messages-delete".format(self.url), data) return self.post("{}/main/messages-delete".format(self.url), data)
@ -1242,11 +1249,11 @@ class EnergyToFight:
class TelegramBot: class TelegramBot:
__initialized = False __initialized: bool = False
__queue: List[str] __queue: List[str]
chat_id = 0 chat_id: int = 0
api_url = "" api_url: str = ""
player_name = "" player_name: str = ""
__thread_stopper: threading.Event __thread_stopper: threading.Event
_last_time: datetime.datetime _last_time: datetime.datetime
_last_full_energy_report: datetime.datetime _last_full_energy_report: datetime.datetime
@ -1257,12 +1264,13 @@ class TelegramBot:
self._threads = [] self._threads = []
self.__queue = [] self.__queue = []
self.__thread_stopper = threading.Event() if stop_event is None else stop_event self.__thread_stopper = threading.Event() if stop_event is None else stop_event
self._last_full_energy_report = self._next_time = self._last_time = utils.good_timedelta(utils.now(), datetime.timedelta(hours=1))
@property @property
def __dict__(self): def __dict__(self):
return dict(chat_id=self.chat_id, api_url=self.api_url, player=self.player_name, last_time=self._last_time, return {'chat_id': self.chat_id, 'api_url': self.api_url, 'player': self.player_name,
next_time=self._next_time, queue=self.__queue, initialized=self.__initialized, 'last_time': self._last_time, 'next_time': self._next_time, 'queue': self.__queue,
has_threads=bool(len(self._threads))) 'initialized': self.__initialized, 'has_threads': bool(len(self._threads))}
def do_init(self, chat_id: int, token: str, player_name: str = ""): def do_init(self, chat_id: int, token: str, player_name: str = ""):
self.chat_id = chat_id self.chat_id = chat_id
@ -1277,6 +1285,8 @@ class TelegramBot:
def send_message(self, message: str) -> bool: def send_message(self, message: str) -> bool:
self.__queue.append(message) self.__queue.append(message)
if not self.__initialized: if not self.__initialized:
if self._last_time < utils.now():
self.__queue.clear()
return True return True
self._threads = [t for t in self._threads if t.is_alive()] self._threads = [t for t in self._threads if t.is_alive()]
self._next_time = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=1)) self._next_time = utils.good_timedelta(utils.now(), datetime.timedelta(minutes=1))

View File

@ -6,6 +6,7 @@ import sys
import time import time
import traceback import traceback
import unicodedata import unicodedata
from decimal import Decimal
from pathlib import Path from pathlib import Path
from typing import Any, List, Mapping, NoReturn, Optional, Union from typing import Any, List, Mapping, NoReturn, Optional, Union
@ -344,6 +345,11 @@ def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commi
trace = inspect.trace() trace = inspect.trace()
if trace: if trace:
trace = trace[-1][0].f_locals trace = trace[-1][0].f_locals
if trace.get('__name__') == '__main__':
trace = {'commit_id': trace.get('COMMIT_ID'),
'interactive': trace.get('INTERACTIVE'),
'version': trace.get('__version__'),
'config': trace.get('CONFIG')}
else: else:
trace = dict() trace = dict()
send_email(name, content, citizen, local_vars=trace) send_email(name, content, citizen, local_vars=trace)
@ -393,27 +399,28 @@ def slugify(value, allow_unicode=False) -> str:
def calculate_hit(strength: float, rang: int, tp: bool, elite: bool, ne: bool, booster: int = 0, def calculate_hit(strength: float, rang: int, tp: bool, elite: bool, ne: bool, booster: int = 0,
weapon: int = 200) -> float: weapon: int = 200, is_deploy: bool = False) -> float:
base_dmg = 10 * (1 + strength / 400) * (1 + rang / 5) * (1 + weapon / 100) dec = 3 if is_deploy else 0
dmg = int(base_dmg * 10 + 5) // 10 base_str = (1 + Decimal(str(round(strength, 3))) / 400)
base_rnk = (1 + Decimal(str(rang / 5)))
base_wpn = (1 + Decimal(str(weapon / 100)))
dmg = 10 * base_str * base_rnk * base_wpn
booster_multiplier = (100 + booster) / 100 if elite:
booster_dmg = dmg * booster_multiplier dmg = dmg * 11 / 10
dmg = int(booster_dmg * 10 + 5) // 10
elite = 1.1 if elite else 1 if tp and rang >= 70:
elite_dmg = dmg * elite dmg = dmg * (1 + Decimal((rang - 69) / 10))
dmg = int(elite_dmg)
legend = 1 if (not tp or rang < 70) else 1 + (rang - 69) / 10 dmg = dmg * (100 + booster) / 100
legend_dmg = dmg * legend
dmg = int(legend_dmg)
return dmg * (1.1 if ne else 1) if ne:
dmg = dmg * 11 / 10
return round(dmg, dec)
def ground_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, def get_ground_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False,
booster: int = 0, weapon_power: int = 200) -> float: booster: int = 0, weapon_power: int = 200) -> float:
r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json() r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json()
rang = r['military']['militaryData']['ground']['rankNumber'] rang = r['military']['militaryData']['ground']['rankNumber']
strength = r['military']['militaryData']['ground']['strength'] strength = r['military']['militaryData']['ground']['strength']
@ -424,8 +431,8 @@ def ground_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patr
return calculate_hit(strength, rang, true_patriot, elite, natural_enemy, booster, weapon_power) return calculate_hit(strength, rang, true_patriot, elite, natural_enemy, booster, weapon_power)
def air_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, def get_air_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0,
weapon_power: int = 0) -> float: weapon_power: int = 0) -> float:
r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json() r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json()
rang = r['military']['militaryData']['aircraft']['rankNumber'] rang = r['military']['militaryData']['aircraft']['rankNumber']
elite = r['citizenAttributes']['level'] > 100 elite = r['citizenAttributes']['level'] > 100

View File

@ -5,7 +5,7 @@ flake8==3.7.9
ipython==7.11.1 ipython==7.11.1
isort==4.3.21 isort==4.3.21
pip==19.3.1 pip==19.3.1
PyInstaller==3.5 PyInstaller==3.6
pytz==2019.3 pytz==2019.3
requests==2.22.0 requests==2.22.0
setuptools==44.0.0 setuptools==44.0.0

View File

@ -1,7 +1,11 @@
[bumpversion] [bumpversion]
current_version = 0.19.1 current_version = 0.19.4.1
commit = True commit = True
tag = True tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
serialize =
{major}.{minor}.{patch}.{dev}
{major}.{minor}.{patch}
[bumpversion:file:setup.py] [bumpversion:file:setup.py]
search = version='{current_version}' search = version='{current_version}'

View File

@ -43,6 +43,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.19.1', version='0.19.4.1',
zip_safe=False, zip_safe=False,
) )