no message

This commit is contained in:
Eriks Karls 2019-10-30 19:35:40 +02:00
parent c48af9a891
commit ef27960ff1
3 changed files with 143 additions and 155 deletions

View File

@ -1,35 +1,34 @@
import datetime
import itertools
import re import re
import sys import sys
import threading from threading import Event
import time
from json import loads, dumps from itertools import product
from typing import Dict, List, Tuple, Any, Union from datetime import datetime, timedelta
from json import loads, dumps
from time import sleep
from typing import Dict, List, Tuple, Any, Union, Set
import requests
from requests import Response, RequestException from requests import Response, RequestException
from erepublik import classes, utils from erepublik.classes import (CitizenAPI, Battle, Reporter, Config, Energy, Details, Politics, MyCompanies,
TelegramBot, ErepublikException, BattleDivision, MyJSONEncoder)
from erepublik.utils import *
class Citizen(classes.CitizenAPI): class Citizen(CitizenAPI):
division = 0 division = 0
all_battles: Dict[int, classes.Battle] = None all_battles: Dict[int, Battle] = None
countries: Dict[int, Dict[str, Union[str, List[int]]]] = None countries: Dict[int, Dict[str, Union[str, List[int]]]] = None
__last_war_update_data = None __last_war_update_data = None
__last_full_update: datetime.datetime = utils.now().min __last_full_update: datetime = now().min
active_fs: bool = False active_fs: bool = False
food = {"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0, "total": 0} food = {"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0, "total": 0}
inventory = {"used": 0, "total": 0} inventory = {"used": 0, "total": 0}
boosters = { boosters = {100: {}, 50: {}}
100: {
}, 50: {
}
}
eb_normal = 0 eb_normal = 0
eb_double = 0 eb_double = 0
@ -43,28 +42,26 @@ class Citizen(classes.CitizenAPI):
eday = 0 eday = 0
r: requests.Response r: Response
reporter: classes.Reporter
name = "Not logged in!" name = "Not logged in!"
debug = False debug = False
__registered = False __registered = False
logged_in = False logged_in = False
telegram = None
def __init__(self, email: str = "", password: str = "", auto_login: bool = True): def __init__(self, email: str = "", password: str = "", auto_login: bool = True):
super().__init__() super().__init__()
self.commit_id = utils.COMMIT_ID self.commit_id = COMMIT_ID
self.config = classes.Config() self.config = Config()
self.config.email = email self.config.email = email
self.config.password = password self.config.password = password
self.energy = classes.Energy() self.energy = Energy()
self.details = classes.Details() self.details = Details()
self.politics = classes.Politics() self.politics = Politics()
self.my_companies = classes.MyCompanies() self.my_companies = MyCompanies()
self.set_debug(True) self.set_debug(True)
self.reporter = classes.Reporter() self.reporter = Reporter()
self.stop_threads = threading.Event() self.stop_threads = Event()
self.telegram = classes.TelegramBot(stop_event=self.stop_threads) self.telegram = TelegramBot(stop_event=self.stop_threads)
if auto_login: if auto_login:
self.login() self.login()
@ -85,27 +82,26 @@ class Citizen(classes.CitizenAPI):
self.telegram.do_init(self.config.telegram_chat_id or 620981703, self.telegram.do_init(self.config.telegram_chat_id or 620981703,
self.config.telegram_token or "864251270:AAFzZZdjspI-kIgJVk4gF3TViGFoHnf8H4o", self.config.telegram_token or "864251270:AAFzZZdjspI-kIgJVk4gF3TViGFoHnf8H4o",
"" if self.config.telegram_chat_id or self.config.telegram_token else self.name) "" if self.config.telegram_chat_id or self.config.telegram_token else self.name)
self.telegram.send_message("*Started* {:%F %T}".format(utils.now())) self.telegram.send_message(f"*Started* {now():%F %T}")
self.__last_full_update = utils.good_timedelta(self.now, - datetime.timedelta(minutes=5)) self.__last_full_update = good_timedelta(self.now, - timedelta(minutes=5))
def write_log(self, *args, **kwargs): def write_log(self, *args, **kwargs):
if self.config.interactive: if self.config.interactive:
utils.write_interactive_log(*args, **kwargs) write_interactive_log(*args, **kwargs)
else: else:
utils.write_silent_log(*args, **kwargs) write_silent_log(*args, **kwargs)
def sleep(self, seconds: int): def sleep(self, seconds: int):
if seconds < 0: if seconds < 0:
seconds = 0 seconds = 0
if self.config.interactive: if self.config.interactive:
utils.interactive_sleep(seconds) interactive_sleep(seconds)
else: else:
time.sleep(seconds) sleep(seconds)
def __str__(self) -> str: def __str__(self) -> str:
return "Citizen {}".format(self.name) return f"Citizen {self.name}"
@property
def __dict__(self): def __dict__(self):
ret = super().__dict__.copy() ret = super().__dict__.copy()
ret.pop('stop_threads', None) ret.pop('stop_threads', None)
@ -142,7 +138,7 @@ class Citizen(classes.CitizenAPI):
self.token = re_login_token.group(1) self.token = re_login_token.group(1)
self._login() self._login()
else: else:
raise classes.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:
@ -153,7 +149,7 @@ class Citizen(classes.CitizenAPI):
r = self._post_login(self.config.email, self.config.password) r = self._post_login(self.config.email, self.config.password)
self.r = r self.r = r
if r.url == "{}/login".format(self.url): if r.url == f"{self.url}/login":
self.write_log("Citizen email and/or password is incorrect!") self.write_log("Citizen email and/or password is incorrect!")
raise KeyboardInterrupt raise KeyboardInterrupt
else: else:
@ -162,18 +158,18 @@ class Citizen(classes.CitizenAPI):
self.name = re_name_id.group(2) self.name = re_name_id.group(2)
self.details.citizen_id = re_name_id.group(1) self.details.citizen_id = re_name_id.group(1)
self.write_log("Logged in as: {}".format(self.name)) self.write_log(f"Logged in as: {self.name}")
self.get_csrf_token() self.get_csrf_token()
self.logged_in = True self.logged_in = True
def _errors_in_response(self, response: requests.Response): def _errors_in_response(self, response: Response):
if response.status_code >= 400: if response.status_code >= 400:
self.r = response self.r = response
if response.status_code >= 500: if response.status_code >= 500:
self.write_log("eRepublik servers are having internal troubles. Sleeping for 5 minutes") self.write_log("eRepublik servers are having internal troubles. Sleeping for 5 minutes")
self.sleep(5 * 60) self.sleep(5 * 60)
else: else:
raise classes.ErepublikException("HTTP {} error!".format(response.status_code)) raise ErepublikException(f"HTTP {response.status_code} error!")
return bool(re.search(r'body id="error"|Internal Server Error|' return bool(re.search(r'body id="error"|Internal Server Error|'
r'CSRF attack detected|meta http-equiv="refresh"|not_authenticated', response.text)) r'CSRF attack detected|meta http-equiv="refresh"|not_authenticated', response.text))
@ -229,7 +225,7 @@ class Citizen(classes.CitizenAPI):
try: try:
resp_data = response.json() resp_data = response.json()
if (resp_data.get("error") or not resp_data.get("status")) and resp_data.get("message", "") == "captcha": if (resp_data.get("error") or not resp_data.get("status")) and resp_data.get("message", "") == "captcha":
utils.send_email(self.name, [response.text, ], player=self, captcha=True) send_email(self.name, [response.text, ], player=self, captcha=True)
except: except:
pass pass
@ -278,21 +274,21 @@ class Citizen(classes.CitizenAPI):
msgs = "\n".join(msgs) msgs = "\n".join(msgs)
self.telegram.report_medal(msgs) self.telegram.report_medal(msgs)
self.write_log("Found awards:\n{}".format(msgs)) self.write_log(f"Found awards:\n{msgs}")
for info in data.values(): for info in data.values():
self.reporter.report_action("NEW_MEDAL", info) self.reporter.report_action("NEW_MEDAL", info)
levelup = re.search(r"<p>Congratulations, you have reached experience <strong>level (\d+)</strong></p>", html) levelup = re.search(r"<p>Congratulations, you have reached experience <strong>level (\d+)</strong></p>", html)
if levelup: if levelup:
level = levelup.group(1) level = levelup.group(1)
msg = "Level up! Current level {}".format(level) msg = f"Level up! Current level {level}"
self.write_log(msg) self.write_log(msg)
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 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 utils.good_timedelta(self.__last_full_update, datetime.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:
return return
else: else:
self.__last_full_update = self.now self.__last_full_update = self.now
@ -312,12 +308,12 @@ class Citizen(classes.CitizenAPI):
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)
promos = loads(utils.normalize_html_json(ugly_js)) promos = loads(normalize_html_json(ugly_js))
self.promos = {k: v for k, v in (self.promos.items() if self.promos else {}) if v > self.now} self.promos = {k: v for k, v in (self.promos.items() if self.promos else {}) if v > self.now}
send_mail = False send_mail = False
for promo in promos: for promo in promos:
promo_name = promo.get("id") promo_name = promo.get("id")
expire = utils.localize_timestamp(int(promo.get("expiresAt"))) expire = localize_timestamp(int(promo.get("expiresAt")))
if promo_name not in self.promos: if promo_name not in self.promos:
send_mail = True send_mail = True
self.promos.update({promo_name: expire}) self.promos.update({promo_name: expire})
@ -328,20 +324,19 @@ class Citizen(classes.CitizenAPI):
if self.details.gold >= 54: if self.details.gold >= 54:
self.buy_tg_contract() self.buy_tg_contract()
else: else:
self.write_log("Training ground contract active but don't have enough gold ({}g {}cc)".format( self.write_log(f"Training ground contract active but "
self.details.gold, self.details.cc f"don't have enough gold ({self.details.gold}g {self.details.cc}cc)")
))
if send_mail: if send_mail:
active_promos = [] active_promos = []
for kind, time_until in self.promos.items(): for kind, time_until in self.promos.items():
active_promos.append(f"{kind} active until {time_until}") active_promos.append(f"{kind} active until {time_until}")
utils.report_promo(kind, time_until) report_promo(kind, time_until)
utils.send_email(self.name, active_promos, player=self, promo=True) send_email(self.name, active_promos, player=self, promo=True)
new_date = re.search(r"var new_date = '(\d*)';", html) new_date = re.search(r"var new_date = '(\d*)';", html)
if new_date: if new_date:
self.energy.set_reference_time( self.energy.set_reference_time(
utils.good_timedelta(self.now, datetime.timedelta(seconds=int(new_date.group(1)))) good_timedelta(self.now, timedelta(seconds=int(new_date.group(1))))
) )
ugly_js = re.search(r"var erepublik = ({.*}),\s+", html).group(1) ugly_js = re.search(r"var erepublik = ({.*}),\s+", html).group(1)
@ -379,7 +374,7 @@ class Citizen(classes.CitizenAPI):
self.politics.is_party_member = True self.politics.is_party_member = True
self.politics.party_id = party.get('party_id') self.politics.party_id = party.get('party_id')
self.politics.is_party_president = bool(party.get('is_party_president')) self.politics.is_party_president = bool(party.get('is_party_president'))
self.politics.party_slug = "{}-{}".format(party.get("stripped_title"), party.get('party_id')) self.politics.party_slug = f"{party.get('stripped_title')}-{party.get('party_id')}"
def update_money(self, page: int = 0, currency: int = 62) -> Dict[str, Any]: def update_money(self, page: int = 0, currency: int = 62) -> Dict[str, Any]:
""" """
@ -396,7 +391,7 @@ class Citizen(classes.CitizenAPI):
def update_job_info(self): def update_job_info(self):
ot = self._get_main_job_data().json().get("overTime", {}) ot = self._get_main_job_data().json().get("overTime", {})
if ot: if ot:
self.my_companies.next_ot_time = utils.localize_timestamp(int(ot.get("nextOverTime", 0))) self.my_companies.next_ot_time = localize_timestamp(int(ot.get("nextOverTime", 0)))
self.ot_points = ot.get("points", 0) self.ot_points = ot.get("points", 0)
def update_companies(self): def update_companies(self):
@ -447,7 +442,7 @@ class Citizen(classes.CitizenAPI):
if item.get('type'): if item.get('type'):
if item.get('type') in ['damageBoosters', "aircraftDamageBoosters"]: if item.get('type') in ['damageBoosters', "aircraftDamageBoosters"]:
kind = "{}{}".format(item['type'], item['quality']) kind = f"{item['type']}{item['quality']}"
if item['quality'] == 5: if item['quality'] == 5:
self.boosters[50].update({item['duration']: item['amount']}) self.boosters[50].update({item['duration']: item['amount']})
elif item['quality'] == 10: elif item['quality'] == 10:
@ -518,7 +513,7 @@ class Citizen(classes.CitizenAPI):
self.inventory.update({"used": j.get("inventoryStatus").get("usedStorage"), self.inventory.update({"used": j.get("inventoryStatus").get("usedStorage"),
"total": j.get("inventoryStatus").get("totalStorage")}) "total": j.get("inventoryStatus").get("totalStorage")})
inventory = dict(items=dict(active=active_items, final=final_items, raw=raw_materials), status=self.inventory) inventory = dict(items=dict(active=active_items, final=final_items, raw=raw_materials), status=self.inventory)
self.food["total"] = sum([self.food[q] * utils.FOOD_ENERGY[q] for q in utils.FOOD_ENERGY]) self.food["total"] = sum([self.food[q] * FOOD_ENERGY[q] for q in FOOD_ENERGY])
return inventory return inventory
def update_weekly_challenge(self): def update_weekly_challenge(self):
@ -555,7 +550,7 @@ class Citizen(classes.CitizenAPI):
self.__last_war_update_data = resp_json self.__last_war_update_data = resp_json
if resp_json.get("battles"): if resp_json.get("battles"):
for battle_id, battle_data in resp_json.get("battles", {}).items(): for battle_id, battle_data in resp_json.get("battles", {}).items():
self.all_battles.update({int(battle_id): classes.Battle(battle_data)}) self.all_battles.update({int(battle_id): Battle(battle_data)})
def eat(self): def eat(self):
""" """
@ -567,7 +562,7 @@ class Citizen(classes.CitizenAPI):
else: else:
self.write_log("I don't want to eat right now!") self.write_log("I don't want to eat right now!")
else: else:
self.write_log("I'm out of food! But I'll try to buy some!\n{}".format(self.food)) self.write_log(f"I'm out of food! But I'll try to buy some!\n{self.food}")
self.buy_food() self.buy_food()
if self.food["total"] > self.energy.interval: if self.food["total"] > self.energy.interval:
self.eat() self.eat()
@ -587,14 +582,14 @@ class Citizen(classes.CitizenAPI):
r_json = response.json() r_json = response.json()
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, good_timedelta(self.now,
datetime.timedelta(seconds=int(next_recovery[1]) * 60 + int(next_recovery[2]))) timedelta(seconds=int(next_recovery[1]) * 60 + int(next_recovery[2])))
) )
self.energy.recovered = r_json.get("health") self.energy.recovered = r_json.get("health")
self.energy.recoverable = r_json.get("food_remaining") self.energy.recoverable = r_json.get("food_remaining")
for q, amount in r_json.get("units_consumed").items(): for q, amount in r_json.get("units_consumed").items():
if "q{}".format(q) in self.food: if f"q{q}" in self.food:
self.food["q{}".format(q)] -= amount self.food[f"q{q}"] -= amount
elif q == "10": elif q == "10":
self.eb_normal -= amount self.eb_normal -= amount
elif q == "11": elif q == "11":
@ -605,29 +600,24 @@ class Citizen(classes.CitizenAPI):
@property @property
def health_info(self): def health_info(self):
ret = "{}/{} + {}, {}hp/6m. {}xp until level up".format( ret = f"{self.energy.recovered}/{self.energy.limit} + {self.energy.recoverable}, " \
self.energy.recovered, f"{self.energy.interval}hp/6m. {self.details.xp_till_level_up}xp until level up"
self.energy.limit,
self.energy.recoverable,
self.energy.interval,
self.details.xp_till_level_up
)
return ret return ret
@property @property
def now(self) -> datetime.datetime: def now(self) -> datetime:
""" """
Returns aware datetime object localized to US/Pacific (eRepublik time) Returns aware datetime object localized to US/Pacific (eRepublik time)
:return: datetime.datetime :return: datetime.datetime
""" """
return utils.now() return now()
def check_epic_battles(self): def check_epic_battles(self):
active_fs = False active_fs = False
for battle_id in self.sorted_battles(self.config.sort_battles_time): for battle_id in self.sorted_battles(self.config.sort_battles_time):
battle = self.all_battles.get(battle_id) battle = self.all_battles.get(battle_id)
if not battle.is_air: if not battle.is_air:
my_div: classes.BattleDivision = battle.div.get(self.division) my_div: BattleDivision = battle.div.get(self.division)
if my_div.epic and my_div.end > self.now: if my_div.epic and my_div.end > self.now:
if self.energy.food_fights > 50: if self.energy.food_fights > 50:
inv_allies = battle.invader.deployed + [battle.invader.id] inv_allies = battle.invader.deployed + [battle.invader.id]
@ -650,9 +640,8 @@ class Citizen(classes.CitizenAPI):
side = battle.defender.id side = battle.defender.id
else: else:
self.write_log( self.write_log(
"Country {} not in all allies list ({}) and also not in inv allies ({}) nor def " f"Country {self.details.current_country} not in all allies list ({all_allies}) and "
"allies ({})".format(self.details.current_country, all_allies, f"also not in inv allies ({inv_allies}) nor def allies ({def_allies})")
inv_allies, def_allies))
break break
error_count = 0 error_count = 0
while self.energy.food_fights > 5 and error_count < 20: while self.energy.food_fights > 5 and error_count < 20:
@ -737,6 +726,8 @@ class Citizen(classes.CitizenAPI):
self.write_log("Checking for battles to fight in...") self.write_log("Checking for battles to fight in...")
for battle_id in self.sorted_battles(self.config.sort_battles_time): for battle_id in self.sorted_battles(self.config.sort_battles_time):
battle = self.all_battles.get(battle_id) battle = self.all_battles.get(battle_id)
if not isinstance(battle, Battle):
continue
div = 11 if battle.is_air else self.division div = 11 if battle.is_air else self.division
allies = battle.invader.deployed + battle.defender.deployed + [battle.invader.id, battle.defender.id] allies = battle.invader.deployed + battle.defender.deployed + [battle.invader.id, battle.defender.id]
@ -762,8 +753,7 @@ class Citizen(classes.CitizenAPI):
"inv": inv_points, "inv": inv_points,
"travel": "(TRAVEL)" if travel_needed else "", "travel": "(TRAVEL)" if travel_needed else "",
} }
self.write_log("Battle {bid}, type: {air:6}, rw: {rw:5}, " self.write_log(battle)
"points: {def:4}:{inv:<4} {travel}".format(**kwargs))
points = def_points <= 1700 and inv_points <= 1700 points = def_points <= 1700 and inv_points <= 1700
b_type = battle.is_air and self.config.air or not battle.is_air and self.config.ground b_type = battle.is_air and self.config.air or not battle.is_air and self.config.ground
@ -774,7 +764,7 @@ class Citizen(classes.CitizenAPI):
continue continue
if battle.start > self.now: if battle.start > self.now:
self.sleep(utils.get_sleep_seconds(battle.start)) self.sleep(get_sleep_seconds(battle.start))
if travel_needed: if travel_needed:
if battle.is_rw: if battle.is_rw:
@ -1034,7 +1024,7 @@ class Citizen(classes.CitizenAPI):
self.sell_produced_product(kind, 1) self.sell_produced_product(kind, 1)
else: else:
raise classes.ErepublikException("Unknown kind produced '{kind}'".format(kind=kind)) raise ErepublikException("Unknown kind produced '{kind}'".format(kind=kind))
elif self.config.auto_buy_raw and re.search(r"not_enough_[^_]*_raw", response.get("message")): elif self.config.auto_buy_raw and re.search(r"not_enough_[^_]*_raw", response.get("message")):
raw_kind = re.search(r"not_enough_(\w+)_raw", response.get("message")) raw_kind = re.search(r"not_enough_(\w+)_raw", response.get("message"))
if raw_kind: if raw_kind:
@ -1074,7 +1064,7 @@ class Citizen(classes.CitizenAPI):
if amount >= 1: if amount >= 1:
lowest_price = self.get_market_offers(country_id=self.details.citizenship, lowest_price = self.get_market_offers(country_id=self.details.citizenship,
product=kind, quality=int(quality)) product_name=kind, quality=int(quality))
if lowest_price["citizen_id"] == self.details.citizen_id: if lowest_price["citizen_id"] == self.details.citizen_id:
price = lowest_price["price"] price = lowest_price["price"]
@ -1179,12 +1169,12 @@ class Citizen(classes.CitizenAPI):
d = self._post_main_travel_data(holdingId=holding_id, battleId=battle_id, countryId=country_id).json() d = self._post_main_travel_data(holdingId=holding_id, battleId=battle_id, countryId=country_id).json()
return d.get('regions', []) return d.get('regions', [])
def get_travel_countries(self) -> List[int]: def get_travel_countries(self) -> Set[int]:
response_json = self._post_main_travel_data().json() response_json = self._post_main_travel_data().json()
return_list = [] return_list = {*[]}
for country_data in response_json['countries'].values(): for country_data in response_json['countries'].values():
if country_data['currentRegions']: if country_data['currentRegions']:
return_list.append(country_data['id']) return_list.add(country_data['id'])
return return_list return return_list
def parse_notifications(self, page: int = 1) -> list: def parse_notifications(self, page: int = 1) -> list:
@ -1219,19 +1209,19 @@ class Citizen(classes.CitizenAPI):
r"id=\"delete_message_(\d+)\" name=\"delete_message\[]\">", self.r.text).group(1) r"id=\"delete_message_(\d+)\" name=\"delete_message\[]\">", self.r.text).group(1)
self._post_delete_message([msg_id]) self._post_delete_message([msg_id])
def get_market_offers(self, country_id: int = None, product: str = None, quality: int = None) -> dict: def get_market_offers(self, country_id: int = None, product_name: str = None, quality: int = None) -> dict:
raw_short_names = dict(frm="foodRaw", wrm="weaponRaw", hrm="houseRaw", arm="airplaneRaw") raw_short_names = dict(frm="foodRaw", wrm="weaponRaw", hrm="houseRaw", arm="airplaneRaw")
q1_industries = ["aircraft"] + list(raw_short_names.values()) q1_industries = ["aircraft"] + list(raw_short_names.values())
if product: if product_name:
if product not in self.available_industries and product not in raw_short_names: if product_name not in self.available_industries and product_name not in raw_short_names:
self.write_log("Industry '{}' not implemented".format(product)) self.write_log(f"Industry '{product_name}' not implemented")
raise classes.ErepublikException("Industry '{}' not implemented".format(product)) raise ErepublikException(f"Industry '{product_name}' not implemented")
elif product in raw_short_names: elif product_name in raw_short_names:
quality = 1 quality = 1
product = raw_short_names.get(product) product_name = raw_short_names[product_name]
product = [product] product_name = [product_name]
elif quality: elif quality:
raise classes.ErepublikException("Quality without product not allowed") raise ErepublikException("Quality without product not allowed")
item_data = dict(price=999999., country=0, amount=0, offer_id=0, citizen_id=0) item_data = dict(price=999999., country=0, amount=0, offer_id=0, citizen_id=0)
@ -1247,19 +1237,16 @@ class Citizen(classes.CitizenAPI):
if country_id: if country_id:
countries = [country_id] countries = [country_id]
else: else:
good_countries = self.get_travel_countries() countries = self.get_travel_countries()
countries = {cid for cid in utils.COUNTRIES.keys() if cid in good_countries}
start_dt = self.now start_dt = self.now
iterable = [countries, product or items, [quality] if quality else range(1, 8)] iterable = [countries, product_name or items, [quality] if quality else range(1, 8)]
for country, industry, q in itertools.product(*iterable): for country, industry, q in product(*iterable):
if (q > 1 and industry in q1_industries) or (q > 5 and industry == "house"): if (q > 1 and industry in q1_industries) or (q > 5 and industry == "house"):
continue continue
str_q = "q%i" % q
r = self._post_economy_marketplace(country, self.available_industries[industry], q).json() r = self._post_economy_marketplace(country, self.available_industries[industry], q).json()
obj = items[industry][str_q] obj = items[industry][f"q{q}"]
if not r.get("error", False): if not r.get("error", False):
for offer in r["offers"]: for offer in r["offers"]:
if obj["price"] > float(offer["priceWithTaxes"]): if obj["price"] > float(offer["priceWithTaxes"]):
@ -1272,15 +1259,15 @@ class Citizen(classes.CitizenAPI):
obj["country"] = int(offer["country_id"]) obj["country"] = int(offer["country_id"])
obj["amount"] = int(offer["amount"]) obj["amount"] = int(offer["amount"])
obj["offer_id"] = int(offer["id"]) obj["offer_id"] = int(offer["id"])
self.write_log("Scraped market in {}!".format(self.now - start_dt)) self.write_log(f"Scraped market in {self.now - start_dt}!")
if quality: if quality:
ret = items[product[0]]["q%i" % quality] ret = items[product_name[0]]["q%i" % quality]
elif product: elif product_name:
if product[0] in raw_short_names.values(): if product_name[0] in raw_short_names.values():
ret = items[product[0]]["q1"] ret = items[product_name[0]]["q1"]
else: else:
ret = items[product[0]] ret = items[product_name[0]]
else: else:
ret = items ret = items
return ret return ret
@ -1288,7 +1275,7 @@ class Citizen(classes.CitizenAPI):
def buy_food(self): def buy_food(self):
hp_per_quality = {"q1": 2, "q2": 4, "q3": 6, "q4": 8, "q5": 10, "q6": 12, "q7": 20} hp_per_quality = {"q1": 2, "q2": 4, "q3": 6, "q4": 8, "q5": 10, "q6": 12, "q7": 20}
hp_needed = 48 * self.energy.interval * 10 - self.food["total"] hp_needed = 48 * self.energy.interval * 10 - self.food["total"]
local_offers = self.get_market_offers(country_id=self.details.current_country, product="food") local_offers = self.get_market_offers(country_id=self.details.current_country, product_name="food")
cheapest_q, cheapest = sorted(local_offers.items(), key=lambda v: v[1]["price"] / hp_per_quality[v[0]])[0] cheapest_q, cheapest = sorted(local_offers.items(), key=lambda v: v[1]["price"] / hp_per_quality[v[0]])[0]
@ -1304,7 +1291,7 @@ class Citizen(classes.CitizenAPI):
self.buy_from_market(cheapest["offer_id"], amount) self.buy_from_market(cheapest["offer_id"], amount)
self.update_inventory() self.update_inventory()
else: else:
s = "Don't have enough money! Needed: {}cc, Have: {}cc".format(amount * cheapest["price"], self.details.cc) s = f"Don't have enough money! Needed: {amount * cheapest['price']}cc, Have: {self.details.cc}cc"
self.write_log(s) self.write_log(s)
self.reporter.report_action("BUY_FOOD", value=s) self.reporter.report_action("BUY_FOOD", value=s)
@ -1327,7 +1314,7 @@ class Citizen(classes.CitizenAPI):
self.details.cc = float(response.json().get("ecash").get("value")) self.details.cc = float(response.json().get("ecash").get("value"))
self.details.gold = float(response.json().get("gold").get("value")) self.details.gold = float(response.json().get("gold").get("value"))
self.reporter.report_action("BUY_GOLD", json_val=response.json(), self.reporter.report_action("BUY_GOLD", json_val=response.json(),
value="New amount {o.cc}cc, {o.gold}g".format(o=self.details)) value=f"New amount {self.details.cc}cc, {self.details.gold}g")
return not response.json().get("error", False) return not response.json().get("error", False)
def activate_dmg_booster(self): def activate_dmg_booster(self):
@ -1366,7 +1353,7 @@ class Citizen(classes.CitizenAPI):
if amount < 1: if amount < 1:
return 0 return 0
ind = {v: k for k, v in self.available_industries.items()} ind = {v: k for k, v in self.available_industries.items()}
self.write_log("D,{},q{},{},{}".format(amount, quality, ind[industry_id], citizen_id)) self.write_log(f"Donate: {amount:4d}q{quality} {ind[industry_id]} to {citizen_id}")
response = self._post_economy_donate_items_action(citizen_id, amount, industry_id, quality) response = self._post_economy_donate_items_action(citizen_id, amount, industry_id, quality)
if re.search(rf"Successfully transferred {amount} item\(s\) to", response.text): if re.search(rf"Successfully transferred {amount} item\(s\) to", response.text):
return amount return amount
@ -1479,12 +1466,12 @@ class Citizen(classes.CitizenAPI):
count -= self.my_companies.ff_lockdown count -= self.my_companies.ff_lockdown
if count <= 0: if count <= 0:
count = 0 count = 0
log_msg = "Not fighting because WAM needs {} food fights".format(self.my_companies.ff_lockdown) log_msg = f"Not fighting because WAM needs {self.my_companies.ff_lockdown} food fights"
if self.max_time_till_full_ff > self.time_till_week_change: if self.max_time_till_full_ff > self.time_till_week_change:
max_count = (int(self.time_till_week_change.total_seconds()) // 360 * self.energy.interval) // 10 max_count = (int(self.time_till_week_change.total_seconds()) // 360 * self.energy.interval) // 10
log_msg = "End for Weekly challenge is near (Recoverable until WC end {}hp | want to do {}hits)".format( log_msg = ("End for Weekly challenge is near "
max_count, count) f"(Recoverable until WC end {max_count}hp | want to do {count}hits)")
count = count if max_count > count else max_count count = count if max_count > count else max_count
self.write_log(log_msg, False) self.write_log(log_msg, False)
@ -1504,30 +1491,30 @@ class Citizen(classes.CitizenAPI):
return max_pp - self.details.pp if max_pp else 0 return max_pp - self.details.pp if max_pp else 0
@property @property
def next_wc_start(self) -> datetime.datetime: def next_wc_start(self) -> datetime:
days = 1 - self.now.weekday() if 1 - self.now.weekday() > 0 else 1 - self.now.weekday() + 7 days = 1 - self.now.weekday() if 1 - self.now.weekday() > 0 else 1 - self.now.weekday() + 7
return utils.good_timedelta(self.now.replace(hour=0, minute=0, second=0, microsecond=0), return good_timedelta(self.now.replace(hour=0, minute=0, second=0, microsecond=0),
datetime.timedelta(days=days)) timedelta(days=days))
@property @property
def time_till_week_change(self) -> datetime.timedelta: def time_till_week_change(self) -> timedelta:
return self.next_wc_start - self.now return self.next_wc_start - self.now
@property @property
def time_till_full_ff(self) -> datetime.timedelta: def time_till_full_ff(self) -> timedelta:
energy = self.energy.recoverable + self.energy.recovered energy = self.energy.recoverable + self.energy.recovered
if energy >= self.energy.limit * 2: if energy >= self.energy.limit * 2:
return datetime.timedelta(0) return timedelta(0)
minutes_needed = round((self.energy.limit * 2 - energy) / self.energy.interval) * 6 minutes_needed = round((self.energy.limit * 2 - energy) / self.energy.interval) * 6
return (self.energy.reference_time - self.now) + datetime.timedelta(minutes=minutes_needed) return (self.energy.reference_time - self.now) + timedelta(minutes=minutes_needed)
@property @property
def max_time_till_full_ff(self) -> datetime.timedelta: def max_time_till_full_ff(self) -> timedelta:
""" """
Max required time for 0 to full energy (0/0 -> limit/limit) (last interval rounded up) Max required time for 0 to full energy (0/0 -> limit/limit) (last interval rounded up)
:return: :return:
""" """
return datetime.timedelta(minutes=round((self.energy.limit * 2 / self.energy.interval) + 0.49) * 6) return timedelta(minutes=round((self.energy.limit * 2 / self.energy.interval) + 0.49) * 6)
@property @property
def is_levelup_close(self) -> bool: def is_levelup_close(self) -> bool:
@ -1577,7 +1564,7 @@ class Citizen(classes.CitizenAPI):
if kind in kinds: if kind in kinds:
return self._post_main_write_article(title, content, self.details.citizenship, kind) return self._post_main_write_article(title, content, self.details.citizenship, kind)
else: else:
raise classes.ErepublikException( raise ErepublikException(
"Article kind must be one of:\n{}\n'{}' is not supported".format( "Article kind must be one of:\n{}\n'{}' is not supported".format(
"\n".join(["{}: {}".format(k, v) for k, v in kinds.items()]), "\n".join(["{}: {}".format(k, v) for k, v in kinds.items()]),
kind kind
@ -1586,7 +1573,7 @@ class Citizen(classes.CitizenAPI):
def post_market_offer(self, industry: int, quality: int, amount: int, price: float) -> Response: def post_market_offer(self, industry: int, quality: int, amount: int, price: float) -> Response:
if industry not in self.available_industries.values(): if industry not in self.available_industries.values():
self.write_log("Trying to sell unsupported industry {}".format(industry)) self.write_log(f"Trying to sell unsupported industry {industry}")
data = { data = {
"country": self.details.citizenship, "country": self.details.citizenship,
@ -1706,7 +1693,7 @@ class Citizen(classes.CitizenAPI):
rjson = resp.json() rjson = resp.json()
if not any([rjson["isBanned"], rjson["isDead"], rjson["isFriend"], rjson["isOrg"], rjson["isSelf"]]): if not any([rjson["isBanned"], rjson["isDead"], rjson["isFriend"], rjson["isOrg"], rjson["isSelf"]]):
r = self._post_main_citizen_add_remove_friend(int(player_id), True) r = self._post_main_citizen_add_remove_friend(int(player_id), True)
self.write_log("{:<64} (id:{:>11}) added as friend".format(rjson["name"], player_id)) self.write_log(f"{rjson['name']:<64} (id:{player_id:>11}) added as friend")
return r return r
return resp return resp
@ -1767,7 +1754,7 @@ class Citizen(classes.CitizenAPI):
cities.sort(key=int) cities.sort(key=int)
for city_id in cities: for city_id in cities:
self.write_log("Adding friends from {} (id: {})".format(cities_dict[city_id], city_id)) self.write_log(f"Adding friends from {cities_dict[city_id]} (id: {city_id})")
resp = self._get_main_city_data_residents(city_id).json() resp = self._get_main_city_data_residents(city_id).json()
for resident in resp["widgets"]["residents"]["residents"]: for resident in resp["widgets"]["residents"]["residents"]:
self.add_friend(resident["citizenId"]) self.add_friend(resident["citizenId"])
@ -1777,14 +1764,14 @@ class Citizen(classes.CitizenAPI):
for resident in resp["widgets"]["residents"]["residents"]: for resident in resp["widgets"]["residents"]["residents"]:
self.add_friend(resident["citizenId"]) self.add_friend(resident["citizenId"])
def schedule_attack(self, war_id: int, region_id: int, region_name: str, at_time: datetime) -> None: def schedule_attack(self, war_id: int, region_id: int, region_name: str, at_time: datetime):
if at_time: if at_time:
self.sleep(utils.get_sleep_seconds(at_time)) self.sleep(get_sleep_seconds(at_time))
self.get_csrf_token() self.get_csrf_token()
self.launch_attack(war_id, region_id, region_name) self.launch_attack(war_id, region_id, region_name)
def get_active_wars(self, country_id: int = None) -> List[int]: def get_active_wars(self, country_id: int = None) -> List[int]:
r = self._get_country_military(utils.COUNTRY_LINK.get(country_id or self.details.citizenship)) r = self._get_country_military(COUNTRY_LINK.get(country_id or self.details.citizenship))
all_war_ids = re.findall(r'//www\.erepublik\.com/en/wars/show/(\d+)"', r.text) all_war_ids = re.findall(r'//www\.erepublik\.com/en/wars/show/(\d+)"', r.text)
return [int(wid) for wid in all_war_ids] return [int(wid) for wid in all_war_ids]
@ -1814,7 +1801,7 @@ class Citizen(classes.CitizenAPI):
last_battle_id = int(re.search(r'<a href="//www.erepublik.com/en/military/battlefield/(\d+)">', html).group(1)) last_battle_id = int(re.search(r'<a href="//www.erepublik.com/en/military/battlefield/(\d+)">', html).group(1))
console = self._post_military_battle_console(last_battle_id, 'warList', 1).json() console = self._post_military_battle_console(last_battle_id, 'warList', 1).json()
battle = console.get('list')[0] battle = console.get('list')[0]
return utils.localize_dt(datetime.datetime.strptime(battle.get('result').get('end'), "%Y-%m-%d %H:%M:%S")) return localize_dt(datetime.strptime(battle.get('result').get('end'), "%Y-%m-%d %H:%M:%S"))
def launch_attack(self, war_id: int, region_id: int, region_name: str): def launch_attack(self, war_id: int, region_id: int, region_name: str):
self._post_wars_attack_region(war_id, region_id, region_name) self._post_wars_attack_region(war_id, region_id, region_name)
@ -1826,10 +1813,10 @@ class Citizen(classes.CitizenAPI):
if start_time.minute <= 30: if start_time.minute <= 30:
start_time = start_time.replace(minute=30) start_time = start_time.replace(minute=30)
else: else:
start_time = start_time.replace(minute=0) start_time = good_timedelta(start_time.replace(minute=0), timedelta(hours=1))
while not self.stop_threads.is_set(): while not self.stop_threads.is_set():
self.update_citizen_info() self.update_citizen_info()
start_time = utils.good_timedelta(start_time, datetime.timedelta(minutes=30)) start_time = good_timedelta(start_time, timedelta(minutes=30))
self.send_state_update() self.send_state_update()
self.send_inventory_update() self.send_inventory_update()
sleep_seconds = (start_time - self.now).total_seconds() sleep_seconds = (start_time - self.now).total_seconds()
@ -1847,15 +1834,15 @@ class Citizen(classes.CitizenAPI):
to_report = self.update_inventory() to_report = self.update_inventory()
self.reporter.report_action("INVENTORY", json_val=to_report) self.reporter.report_action("INVENTORY", json_val=to_report)
def check_house_durability(self) -> Dict[int, datetime.datetime]: def check_house_durability(self) -> Dict[int, datetime]:
ret = {} ret = {}
inv = self.update_inventory() inv = self.update_inventory()
for house_quality, active_house in inv['items']['active'].get('house', {}).items(): for house_quality, active_house in inv['items']['active'].get('house', {}).items():
till = utils.good_timedelta(self.now, datetime.timedelta(seconds=active_house['time_left'])) till = good_timedelta(self.now, timedelta(seconds=active_house['time_left']))
ret.update({house_quality: till}) ret.update({house_quality: till})
return ret return ret
def buy_and_activate_house(self, q: int) -> Dict[int, datetime.datetime]: def buy_and_activate_house(self, q: int) -> Dict[int, datetime]:
inventory = self.update_inventory() inventory = self.update_inventory()
ok_to_activate = False ok_to_activate = False
if not [h for h in inventory['items']['final'].get('house', []) if h['quality'] == q]: if not [h for h in inventory['items']['final'].get('house', []) if h['quality'] == q]:
@ -1865,7 +1852,7 @@ class Citizen(classes.CitizenAPI):
countries.append(self.details.current_country) countries.append(self.details.current_country)
for country in countries: for country in countries:
offers += [self.get_market_offers(country, "house", q)] offers += [self.get_market_offers(country, "house", q)]
global_cheapest = self.get_market_offers(product="house", quality=q) global_cheapest = self.get_market_offers(product_name="house", quality=q)
cheapest_offer = sorted(offers, key=lambda o: o["price"])[0] cheapest_offer = sorted(offers, key=lambda o: o["price"])[0]
region = self.get_country_travel_region(global_cheapest['country']) region = self.get_country_travel_region(global_cheapest['country'])
if global_cheapest['price'] + 200 < cheapest_offer['price'] and region: if global_cheapest['price'] + 200 < cheapest_offer['price'] and region:
@ -1874,7 +1861,7 @@ class Citizen(classes.CitizenAPI):
else: else:
buy = self.buy_from_market(cheapest_offer['offer_id'], 1) buy = self.buy_from_market(cheapest_offer['offer_id'], 1)
if buy["error"]: if buy["error"]:
msg = "Unable to buy q{} house! \n{}".format(q, buy['message']) msg = f"Unable to buy q{q} house! \n{buy['message']}"
self.write_log(msg) self.write_log(msg)
else: else:
ok_to_activate = True ok_to_activate = True
@ -1884,7 +1871,7 @@ class Citizen(classes.CitizenAPI):
self.activate_house(q) self.activate_house(q)
return self.check_house_durability() return self.check_house_durability()
def renew_houses(self, forced: bool = False) -> Dict[int, datetime.datetime]: def renew_houses(self, forced: bool = False) -> Dict[int, datetime]:
""" """
Renew all houses which endtime is in next 48h Renew all houses which endtime is in next 48h
:param forced: if true - renew all houses :param forced: if true - renew all houses
@ -1892,17 +1879,17 @@ class Citizen(classes.CitizenAPI):
""" """
house_durability = self.check_house_durability() house_durability = self.check_house_durability()
for q, active_till in house_durability.items(): for q, active_till in house_durability.items():
if utils.good_timedelta(active_till, - datetime.timedelta(hours=48)) <= self.now or forced: if good_timedelta(active_till, - timedelta(hours=48)) <= self.now or forced:
house_durability = self.buy_and_activate_house(q) house_durability = self.buy_and_activate_house(q)
self.travel_to_residence() self.travel_to_residence()
return house_durability return house_durability
def activate_house(self, quality: int) -> datetime.datetime: def activate_house(self, quality: int) -> datetime:
active_until = self.now active_until = self.now
r = self._post_economy_activate_house(quality) r = self._post_economy_activate_house(quality)
if r.json().get("status") and not r.json().get("error"): if r.json().get("status") and not r.json().get("error"):
house = r.json()["inventoryItems"]["activeEnhancements"]["items"]["4_%i_active" % quality] house = r.json()["inventoryItems"]["activeEnhancements"]["items"]["4_%i_active" % quality]
active_until = utils.good_timedelta(active_until, datetime.timedelta(seconds=house["active"]["time_left"])) active_until = good_timedelta(active_until, timedelta(seconds=house["active"]["time_left"]))
return active_until return active_until
def collect_anniversary_reward(self) -> Response: def collect_anniversary_reward(self) -> Response:
@ -1925,7 +1912,7 @@ class Citizen(classes.CitizenAPI):
if self.details.cc < amount or amount < 20: if self.details.cc < amount or amount < 20:
return False return False
data = dict(country=71, action='currency', value=amount) data = dict(country=71, action='currency', value=amount)
self.telegram.send_message(f"Donated {amount}cc to {utils.COUNTRIES[71]}") self.telegram.send_message(f"Donated {amount}cc to {COUNTRIES[71]}")
self.reporter.report_action("CONTRIBUTE_CC", data) self.reporter.report_action("CONTRIBUTE_CC", data)
r = self._post_main_country_donate(**data) r = self._post_main_country_donate(**data)
return r.json().get('status') or not r.json().get('error') return r.json().get('status') or not r.json().get('error')
@ -1958,7 +1945,7 @@ class Citizen(classes.CitizenAPI):
return r.json() return r.json()
def report_error(self, msg: str = ""): def report_error(self, msg: str = ""):
utils.process_error(msg, self.name, sys.exc_info(), self, self.commit_id, False) process_error(msg, self.name, sys.exc_info(), self, self.commit_id, False)
def get_battle_top_10(self, battle_id: int) -> Dict[int, List[Tuple[int, int]]]: def get_battle_top_10(self, battle_id: int) -> Dict[int, List[Tuple[int, int]]]:
battle = self.all_battles.get(battle_id) battle = self.all_battles.get(battle_id)
@ -1976,7 +1963,7 @@ class Citizen(classes.CitizenAPI):
return ret return ret
def to_json(self, indent: bool = False) -> str: def to_json(self, indent: bool = False) -> str:
return dumps(self.__dict__, cls=classes.MyJSONEncoder, indent=4 if indent else None, sort_keys=True) return dumps(self.__dict__, cls=MyJSONEncoder, indent=4 if indent else None, sort_keys=True)
def get_game_token_offers(self): def get_game_token_offers(self):
r = self._post_economy_game_tokens_market('retrieve').json() r = self._post_economy_game_tokens_market('retrieve').json()
@ -2005,7 +1992,7 @@ class Citizen(classes.CitizenAPI):
if ne: if ne:
tp = True tp = True
return utils.calculate_hit(strength, rang, tp, elite, ne, 50 if booster_50 else 100 if booster_100 else 0) return calculate_hit(strength, rang, tp, elite, ne, 50 if booster_50 else 100 if booster_100 else 0)
def get_air_hit_dmg_value(self, rang: int = None, elite: bool = None, ne: bool = False, def get_air_hit_dmg_value(self, rang: int = None, elite: bool = None, ne: bool = False,
weapon: bool = False) -> float: weapon: bool = False) -> float:
@ -2016,7 +2003,7 @@ class Citizen(classes.CitizenAPI):
if elite is None: if elite is None:
elite = r['citizenAttributes']['level'] > 100 elite = r['citizenAttributes']['level'] > 100
return utils.calculate_hit(0, rang, True, elite, ne, 0, 20 if weapon else 0) return calculate_hit(0, rang, True, elite, ne, 0, 20 if weapon else 0)
def endorse_article(self, article_id: int, amount: int) -> bool: def endorse_article(self, article_id: int, amount: int) -> bool:
if amount in (5, 50, 100): if amount in (5, 50, 100):

View File

@ -18,7 +18,7 @@ class ErepublikException(Exception):
super().__init__(message) super().__init__(message)
class ErepublikNetworkException(Exception): class ErepublikNetworkException(ErepublikException):
def __init__(self, message, request): def __init__(self, message, request):
super().__init__(message) super().__init__(message)
self.request = request self.request = request
@ -373,7 +373,7 @@ class Energy:
return self.recovered + self.recoverable return self.recovered + self.recoverable
class Details(object): class Details:
xp = 0 xp = 0
cc = 0 cc = 0
pp = 0 pp = 0
@ -427,7 +427,7 @@ class Politics:
is_country_president: bool = False is_country_president: bool = False
class House(object): class House:
quality = None quality = None
unactivated_count = 0 unactivated_count = 0
active_untill = utils.good_timedelta(utils.now(), -datetime.timedelta(days=1)) active_untill = utils.good_timedelta(utils.now(), -datetime.timedelta(days=1))
@ -1031,7 +1031,7 @@ class BattleDivision:
self.wall = dict({"for": wall_for, "dom": wall_dom}) self.wall = dict({"for": wall_for, "dom": wall_dom})
class Battle(object): class Battle:
id: int = 0 id: int = 0
war_id: int = 0 war_id: int = 0
zone_id: int = 0 zone_id: int = 0
@ -1086,9 +1086,10 @@ class Battle(object):
time_part = "{}".format(now - self.start) time_part = "{}".format(now - self.start)
else: else:
time_part = "- {}".format(self.start - now) time_part = "- {}".format(self.start - now)
return "Battle {} | {:>21.21}:{:<21.21} | Round {:2} | Start {}".format( return f"Battle {self.id} | " \
self.id, utils.COUNTRIES[self.invader.id], utils.COUNTRIES[self.defender.id], self.zone_id, time_part f"{utils.COUNTRIES[self.invader.id]:>21.21}:{utils.COUNTRIES[self.defender.id]:<21.21} | " \
) f"Round {self.zone_id:2} | " \
f"Time since start {time_part}"
class EnergyToFight: class EnergyToFight:

View File

@ -13,11 +13,11 @@ from typing import Union, Any, List, NoReturn, Mapping
import pytz import pytz
import requests import requests
__all__ = ["FOOD_ENERGY", "COMMIT_ID", "COUNTRIES", "erep_tz", __all__ = ["FOOD_ENERGY", "COMMIT_ID", "COUNTRIES", "erep_tz", 'COUNTRY_LINK',
"now", "localize_dt", "localize_timestamp", "good_timedelta", "eday_from_date", "date_from_eday", "now", "localize_dt", "localize_timestamp", "good_timedelta", "eday_from_date", "date_from_eday",
"get_sleep_seconds", "interactive_sleep", "silent_sleep", "get_sleep_seconds", "interactive_sleep", "silent_sleep",
"write_silent_log", "write_interactive_log", "get_file", "write_file", "write_silent_log", "write_interactive_log", "get_file", "write_file",
"send_email", "normalize_html_json", "process_error", ] "send_email", "normalize_html_json", "process_error", 'report_promo', 'calculate_hit']
FOOD_ENERGY = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20) FOOD_ENERGY = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20)
COMMIT_ID = "7b92e19" COMMIT_ID = "7b92e19"