Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
d6e161d815 | |||
1285e95cec | |||
324db2c136 | |||
1c6b41e41b | |||
a52552afb7 | |||
28bfdc7b20 | |||
de1b734728 | |||
fa5646ecfd | |||
339392cfb0 | |||
7ebba458cb | |||
4f436292af | |||
416c391d21 | |||
66c53ef985 | |||
3fa7d097fe | |||
6af9675c39 | |||
2343a6c6c8 | |||
7e56f01a38 | |||
e14cbc18e9 | |||
048ce798dd |
@ -4,7 +4,7 @@
|
||||
|
||||
__author__ = """Eriks Karls"""
|
||||
__email__ = 'eriks@72.lv'
|
||||
__version__ = '0.15.1'
|
||||
__version__ = '0.15.3'
|
||||
|
||||
from erepublik import classes, utils
|
||||
from erepublik.citizen import Citizen
|
||||
|
@ -5,7 +5,7 @@ import sys
|
||||
import threading
|
||||
import time
|
||||
from json import loads, dumps
|
||||
from typing import Dict, List, Tuple, Any, Union, Mapping
|
||||
from typing import Dict, List, Tuple, Any, Union
|
||||
|
||||
import requests
|
||||
from requests import Response, RequestException
|
||||
@ -18,9 +18,9 @@ class Citizen(classes.CitizenAPI):
|
||||
|
||||
division = 0
|
||||
|
||||
all_battles: Dict[int, classes.Battle] = dict()
|
||||
countries: Dict[int, Dict[str, Union[str, List[int]]]] = dict()
|
||||
__last_war_update_data = {}
|
||||
all_battles: Dict[int, classes.Battle] = None
|
||||
countries: Dict[int, Dict[str, Union[str, List[int]]]] = None
|
||||
__last_war_update_data = None
|
||||
__last_full_update: datetime.datetime = utils.now().min
|
||||
|
||||
active_fs: bool = False
|
||||
@ -28,24 +28,20 @@ class Citizen(classes.CitizenAPI):
|
||||
food = {"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0, "total": 0}
|
||||
inventory = {"used": 0, "total": 0}
|
||||
boosters = {
|
||||
"100_damageBoosters_5_7200": 0, # 2h × 60min × 60sec, +50%
|
||||
"100_damageBoosters_5_28800": 0, # 8h × 60min × 60sec, +50%
|
||||
"100_damageBoosters_5_86400": 0, # 1d × 24h × 60min × 60sec, +50%
|
||||
"100_speedBoosters_1_180": 0, # 3min × 60sec, ×2 kills
|
||||
"100_speedBoosters_2_600": 0, # 10min × 60sec, ×5 kills
|
||||
"100_catchupBoosters_30_60": 0, # 60sec, +30%
|
||||
100: {
|
||||
}, 50: {
|
||||
}
|
||||
}
|
||||
|
||||
candies_normal = 0
|
||||
candies_double = 0
|
||||
candies_small = 0
|
||||
tickets = 0
|
||||
eb_normal = 0
|
||||
eb_double = 0
|
||||
eb_small = 0
|
||||
|
||||
work_units = 0
|
||||
ot_points = 0
|
||||
|
||||
tg_contract = {}
|
||||
promos = {}
|
||||
tg_contract = None
|
||||
promos = None
|
||||
|
||||
eday = 0
|
||||
|
||||
@ -82,6 +78,8 @@ class Citizen(classes.CitizenAPI):
|
||||
utils.write_silent_log(*args, **kwargs)
|
||||
|
||||
def sleep(self, seconds: int):
|
||||
if seconds < 0:
|
||||
seconds = 0
|
||||
if self.config.interactive:
|
||||
utils.interactive_sleep(seconds)
|
||||
else:
|
||||
@ -256,7 +254,7 @@ class Citizen(classes.CitizenAPI):
|
||||
continue
|
||||
if data:
|
||||
|
||||
msgs = ["{count} ✕ {kind}, totaling {} {currency}\n"
|
||||
msgs = ["{count} x {kind}, totaling {} {currency}\n"
|
||||
"{about}".format(d["count"] * d["reward"], **d) for d in data.values()]
|
||||
|
||||
self.write_log("Found awards: {}".format("\n".join(msgs)))
|
||||
@ -293,7 +291,7 @@ class Citizen(classes.CitizenAPI):
|
||||
html = self.r.text
|
||||
ugly_js = re.search(r"promotions: (\[{?.*}?]),\s+", html).group(1)
|
||||
promos = loads(utils.normalize_html_json(ugly_js))
|
||||
self.promos = {k: v for k, v in self.promos.items() 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
|
||||
for promo in promos:
|
||||
promo_name = promo.get("id")
|
||||
@ -345,9 +343,8 @@ class Citizen(classes.CitizenAPI):
|
||||
self.details.xp = citizen.get("currentExperiencePoints", 0)
|
||||
self.details.daily_task_done = citizen.get("dailyTasksDone", False)
|
||||
self.details.daily_task_reward = citizen.get("hasReward", False)
|
||||
# if citizen.get("dailyOrderDone", False) and not citizen.get("hasDailyOrderReward", False):
|
||||
# self.post_military_group_missions(self.token)
|
||||
# self.get_citizen_daily_assistant()
|
||||
if citizen.get("dailyOrderDone", False) and not citizen.get("hasDailyOrderReward", False):
|
||||
self._post_military_group_missions()
|
||||
|
||||
self.details.next_pp.sort()
|
||||
for id_, skill in citizen.get("mySkills", {}).items():
|
||||
@ -391,36 +388,65 @@ class Citizen(classes.CitizenAPI):
|
||||
self.my_companies.update_holding_companies()
|
||||
|
||||
def update_inventory(self) -> dict:
|
||||
self.food.update({"q1": 0, "q2": 0, "q3": 0, "q4": 0, "q5": 0, "q6": 0, "q7": 0})
|
||||
self.eb_small = 0
|
||||
j = self._get_economy_inventory_items().json()
|
||||
active_items = {}
|
||||
for item in j.get("inventoryItems", {}).get("activeEnhancements", {}).get("items", {}).values():
|
||||
active_items.update({item['name']: item['active']['time_left']})
|
||||
final_items = {}
|
||||
|
||||
for item in j.get("inventoryItems", {}).get("finalProducts", {}).get("items", {}).values():
|
||||
name = item['name']
|
||||
if item.get('type') == 'damageBoosters':
|
||||
if item['quality'] == 5:
|
||||
self.boosters[50].update({item['duration']: item['amount']})
|
||||
elif item['quality'] == 10:
|
||||
self.boosters[100].update({item['duration']: item['amount']})
|
||||
delta = item['duration']
|
||||
if delta // 3600:
|
||||
name += f" {delta // 3600}h"
|
||||
if delta // 60 % 60:
|
||||
name += f" {delta // 60 % 60}m"
|
||||
if delta % 60:
|
||||
name += f" {delta % 60}s"
|
||||
elif item['industryId'] == 1:
|
||||
amount = item['amount']
|
||||
q = item['quality']
|
||||
if 1 <= q <= 7:
|
||||
self.food.update({f"q{q}": item['amount']})
|
||||
else:
|
||||
if q == 10:
|
||||
self.eb_normal = amount
|
||||
elif q == 11:
|
||||
self.eb_double = amount
|
||||
elif q == 13:
|
||||
self.eb_small += amount
|
||||
elif q == 14:
|
||||
self.eb_small += amount
|
||||
|
||||
elif item['industryId'] == 3 and item['quality'] == 5:
|
||||
self.ot_points = item['amount']
|
||||
|
||||
elif item['industryId'] == 4 and item['quality'] == 100:
|
||||
self.ot_points = item['amount']
|
||||
|
||||
if item['amount']:
|
||||
final_items.update({name: item['amount']})
|
||||
|
||||
raw_materials = {}
|
||||
for item in j.get("inventoryItems", {}).get("rawMaterials", {}).get("items", {}).values():
|
||||
name = item['name']
|
||||
if item['isPartial']:
|
||||
continue
|
||||
if item['amount']:
|
||||
raw_materials.update({name: item['amount']})
|
||||
|
||||
self.inventory.update({"used": j.get("inventoryStatus").get("usedStorage"),
|
||||
"total": j.get("inventoryStatus").get("totalStorage")})
|
||||
final = j.get("inventoryItems").get("finalProducts").get("items")
|
||||
self.food.update({
|
||||
"q1": final.get("1_1", {"amount": 0}).get("amount", 0),
|
||||
"q2": final.get("1_2", {"amount": 0}).get("amount", 0),
|
||||
"q3": final.get("1_3", {"amount": 0}).get("amount", 0),
|
||||
"q4": final.get("1_4", {"amount": 0}).get("amount", 0),
|
||||
"q5": final.get("1_5", {"amount": 0}).get("amount", 0),
|
||||
"q6": final.get("1_6", {"amount": 0}).get("amount", 0),
|
||||
"q7": final.get("1_7", {"amount": 0}).get("amount", 0),
|
||||
})
|
||||
self.boosters.update({
|
||||
"100_damageBoosters_5_7200": final.get("100_damageBoosters_5_7200", {"amount": 0}).get("amount", 0),
|
||||
"100_damageBoosters_5_28800": final.get("100_damageBoosters_5_28800", {"amount": 0}).get("amount", 0),
|
||||
"100_damageBoosters_5_86400": final.get("100_damageBoosters_5_86400", {"amount": 0}).get("amount", 0),
|
||||
"100_speedBoosters_1_180": final.get("100_speedBoosters_1_180", {"amount": 0}).get("amount", 0),
|
||||
"100_speedBoosters_2_600": final.get("100_speedBoosters_2_600", {"amount": 0}).get("amount", 0),
|
||||
"100_catchupBoosters_30_60": final.get("100_catchupBoosters_30_60", {"amount": 0}).get("amount", 0),
|
||||
})
|
||||
self.candies_normal = final.get("1_10", {"amount": 0}).get("amount", 0)
|
||||
self.candies_double = final.get("1_11", {"amount": 0}).get("amount", 0)
|
||||
self.candies_small = final.get("1_12", {"amount": 0}).get("amount", 0)
|
||||
self.ot_points = final.get("4_100", {"amount": 0}).get("amount", 0)
|
||||
self.tickets = final.get("3_5", {"amount": 0}).get("amount", 0)
|
||||
|
||||
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])
|
||||
return j
|
||||
return inventory
|
||||
|
||||
def update_weekly_challenge(self):
|
||||
data = self._get_weekly_challenge_data().json()
|
||||
@ -443,8 +469,9 @@ class Citizen(classes.CitizenAPI):
|
||||
self.update_citizen_info()
|
||||
|
||||
resp_json = self._get_military_campaigns().json()
|
||||
self.all_battles = {}
|
||||
if resp_json.get("countries"):
|
||||
self.all_battles = {}
|
||||
self.countries = {}
|
||||
for c_id, c_data in resp_json.get("countries").items():
|
||||
if int(c_id) not in self.countries:
|
||||
self.countries.update({
|
||||
@ -463,27 +490,23 @@ class Citizen(classes.CitizenAPI):
|
||||
"""
|
||||
self.update_citizen_info()
|
||||
self.update_inventory()
|
||||
if self.details.xp_till_level_up > (self.energy.recovered - 50) // 10:
|
||||
if self.food["total"] > self.energy.interval:
|
||||
if self.energy.limit - self.energy.recovered > self.energy.interval or not self.energy.recoverable % 2:
|
||||
self._eat("blue")
|
||||
else:
|
||||
self.write_log("I don't want to eat right now!")
|
||||
if self.food["total"] > self.energy.interval:
|
||||
if self.energy.limit - self.energy.recovered > self.energy.interval or not self.energy.recoverable % 2:
|
||||
self._eat("blue")
|
||||
else:
|
||||
self.write_log("I'm out of food! But I'll try to buy some!\n{}".format(self.food))
|
||||
self.buy_food()
|
||||
self.update_inventory()
|
||||
if self.food["total"] > self.energy.interval:
|
||||
self.eat()
|
||||
else:
|
||||
self.write_log("I failed to buy food")
|
||||
self.write_log("I don't want to eat right now!")
|
||||
else:
|
||||
self.write_log("I'm not allowed to eat because I have levelup coming up!")
|
||||
self.write_log("I'm out of food! But I'll try to buy some!\n{}".format(self.food))
|
||||
self.buy_food()
|
||||
self.update_inventory()
|
||||
if self.food["total"] > self.energy.interval:
|
||||
self.eat()
|
||||
else:
|
||||
self.write_log("I failed to buy food")
|
||||
self.write_log(self.health_info)
|
||||
|
||||
def eat_ebs(self):
|
||||
self.write_log("Eating energy bar")
|
||||
self.update_citizen_info()
|
||||
if self.energy.recoverable:
|
||||
self._eat("blue")
|
||||
self._eat("orange")
|
||||
@ -503,11 +526,11 @@ class Citizen(classes.CitizenAPI):
|
||||
if "q{}".format(q) in self.food:
|
||||
self.food["q{}".format(q)] -= amount
|
||||
elif q == "10":
|
||||
self.candies_normal -= amount
|
||||
self.eb_normal -= amount
|
||||
elif q == "11":
|
||||
self.candies_double -= amount
|
||||
self.eb_double -= amount
|
||||
elif q == "12":
|
||||
self.candies_small -= amount
|
||||
self.eb_small -= amount
|
||||
return response
|
||||
|
||||
@property
|
||||
@ -700,6 +723,8 @@ class Citizen(classes.CitizenAPI):
|
||||
break
|
||||
|
||||
def fight(self, battle_id: int, side_id: int, is_air: bool = False, count: int = None):
|
||||
if not is_air and self.config.boosters:
|
||||
self.activate_dmg_booster()
|
||||
data = dict(sideId=side_id, battleId=battle_id)
|
||||
error_count = 0
|
||||
ok_to_fight = True
|
||||
@ -796,10 +821,9 @@ class Citizen(classes.CitizenAPI):
|
||||
else:
|
||||
self._eat("blue")
|
||||
if self.energy.food_fights < 1:
|
||||
large = max(self.energy.reference_time, self.now)
|
||||
small = min(self.energy.reference_time, self.now)
|
||||
self.write_log("I don't have energy to work. Will sleep for {}s".format((large - small).seconds))
|
||||
self.sleep(int((large - small).total_seconds()))
|
||||
seconds = (self.energy.reference_time - self.now).total_seconds()
|
||||
self.write_log("I don't have energy to work. Will sleep for {}s".format(seconds))
|
||||
self.sleep(seconds)
|
||||
self._eat("blue")
|
||||
self.work()
|
||||
|
||||
@ -807,7 +831,7 @@ class Citizen(classes.CitizenAPI):
|
||||
r = self._get_training_grounds_json()
|
||||
tg_json = r.json()
|
||||
self.details.gold = tg_json["page_details"]["gold"]
|
||||
self.tg_contract.update({"free_train": tg_json["hasFreeTrain"]})
|
||||
self.tg_contract = {"free_train": tg_json["hasFreeTrain"]}
|
||||
if tg_json["contracts"]:
|
||||
self.tg_contract.update(**tg_json["contracts"][0])
|
||||
|
||||
@ -965,7 +989,7 @@ class Citizen(classes.CitizenAPI):
|
||||
|
||||
def sell_produced_product(self, kind: str, quality: int = 1, amount: int = 0):
|
||||
if not amount:
|
||||
inv_resp = self.update_inventory()
|
||||
inv_resp = self._get_economy_inventory_items().json()
|
||||
category = "rawMaterials" if kind.endswith("Raw") else "finalProducts"
|
||||
item = "{}_{}".format(self.available_industries[kind], quality)
|
||||
amount = inv_resp.get("inventoryItems").get(category).get("items").get(item).get("amount", 0)
|
||||
@ -1146,7 +1170,7 @@ class Citizen(classes.CitizenAPI):
|
||||
countries = [country_id]
|
||||
else:
|
||||
good_countries = self.get_travel_countries()
|
||||
countries = {cid for cid in self.countries.keys() if cid in good_countries}
|
||||
countries = {cid for cid in utils.COUNTRIES.keys() if cid in good_countries}
|
||||
|
||||
start_dt = self.now
|
||||
iterable = [countries, product or items, [quality] if quality else range(1, 8)]
|
||||
@ -1228,18 +1252,17 @@ class Citizen(classes.CitizenAPI):
|
||||
value="New amount {o.cc}cc, {o.gold}g".format(o=self.details))
|
||||
return not response.json().get("error", False)
|
||||
|
||||
def activate_dmg_booster(self, battle_id: int):
|
||||
def activate_dmg_booster(self):
|
||||
if self.config.boosters:
|
||||
duration = 0
|
||||
if self.boosters.get("100_damageBoosters_5_600", 0) > 0:
|
||||
duration = 600
|
||||
elif self.boosters.get("100_damageBoosters_5_7200", 0) > 1:
|
||||
duration = 7200
|
||||
elif self.boosters.get("100_damageBoosters_5_28800", 0) > 2:
|
||||
duration = 28800
|
||||
elif self.boosters.get("100_damageBoosters_5_86400", 0) > 2:
|
||||
duration = 86400
|
||||
self._post_fight_activate_booster(battle_id, 5, duration, "damage")
|
||||
inventory = self.update_inventory()
|
||||
if not ("+100% Damage" in inventory['items']['active'] or "+50% Damage" in inventory['items']['active']):
|
||||
duration = 0
|
||||
for length, amount in self.boosters[50].items():
|
||||
if amount > 1:
|
||||
duration = length
|
||||
break
|
||||
if duration:
|
||||
self._post_economy_activate_booster(5, duration, "damage")
|
||||
|
||||
def activate_battle_effect(self, battle_id: int, kind: str) -> Response:
|
||||
return self._post_activate_battle_effect(battle_id, kind, self.details.citizen_id)
|
||||
@ -1289,8 +1312,8 @@ class Citizen(classes.CitizenAPI):
|
||||
def should_travel_to_fight(self) -> bool:
|
||||
ret = False
|
||||
if self.config.always_travel:
|
||||
return True
|
||||
if self.should_do_levelup: # Do levelup
|
||||
ret = True
|
||||
elif self.should_do_levelup: # Do levelup
|
||||
ret = True
|
||||
elif self.config.all_in and self.energy.available > self.energy.limit * 2 - self.energy.interval * 3:
|
||||
ret = True
|
||||
@ -1312,7 +1335,7 @@ class Citizen(classes.CitizenAPI):
|
||||
if self.is_levelup_reachable:
|
||||
log_msg = "Level up"
|
||||
if self.should_do_levelup:
|
||||
count = (self.energy.limit * 3) // 10
|
||||
count = (self.energy.available + self.energy.limit) // 10
|
||||
force_fight = True
|
||||
else:
|
||||
self.write_log("Waiting for fully recovered energy before leveling up.", False)
|
||||
@ -1355,7 +1378,7 @@ class Citizen(classes.CitizenAPI):
|
||||
|
||||
if count > 0 and not force_fight:
|
||||
if self.my_companies.ff_lockdown and self.details.pp > 75:
|
||||
if count - self.my_companies.ff_lockdown > 0:
|
||||
if self.energy.food_fights - self.my_companies.ff_lockdown < count:
|
||||
log_msg = ("Fight count modified (old count: {} | FF: {} | "
|
||||
"WAM ff_lockdown: {} | New count: {})").format(
|
||||
count, self.energy.food_fights, self.my_companies.ff_lockdown,
|
||||
@ -1367,9 +1390,12 @@ class Citizen(classes.CitizenAPI):
|
||||
log_msg = "Not fighting because WAM needs {} food fights".format(self.my_companies.ff_lockdown)
|
||||
|
||||
if self.max_time_till_full_ff > self.time_till_week_change:
|
||||
max_count = int((self.time_till_week_change - self.time_till_full_ff).total_seconds()) // 60
|
||||
log_msg = "End for Weekly challenge is near ({} | {})".format(max_count, count)
|
||||
count = count if max_count > count else max_count
|
||||
max_count = int((self.time_till_week_change -
|
||||
self.time_till_full_ff).total_seconds()) // 360 * self.energy.interval
|
||||
log_msg = "End for Weekly challenge is near (Recoverable until WC end {}hp | want to do {}hits)".format(
|
||||
max_count, count)
|
||||
max_usable_energy = max_count - self.energy.limit * 2
|
||||
count = count if max_usable_energy > count * 10 else max_usable_energy // 10
|
||||
|
||||
if not silent:
|
||||
self.write_log(log_msg, False)
|
||||
@ -1436,14 +1462,8 @@ class Citizen(classes.CitizenAPI):
|
||||
If Energy limit >= xp till levelup * 10
|
||||
:return: bool
|
||||
"""
|
||||
if self.energy.limit - self.energy.interval <= self.energy.recoverable:
|
||||
if self.is_levelup_reachable:
|
||||
# 45xp till levelup, 50hp/6min, 500+500/500 energy = 500+450 >= 1000
|
||||
# 40xp till levelup, 50hp/6min, 450+500/500 energy = 500+400 >= 950
|
||||
# 25xp till levelup, 50hp/6min, 300+500/500 energy = 500+250 >= 800
|
||||
# 25xp till levelup, 50hp/6min, 300+200/500 energy = 500+250 >= 500
|
||||
return self.energy.limit + self.details.xp_till_level_up * 10 <= self.energy.available
|
||||
return False
|
||||
return (self.energy.recovered >= self.details.xp_till_level_up * 10 and # can reach next level
|
||||
self.energy.recoverable + 2 * self.energy.interval >= self.energy.limit) # can do max amount of dmg
|
||||
|
||||
def get_article_comments(self, article_id: int = 2645676, page_id: int = 1) -> Response:
|
||||
return self._post_article_comments(article_id, page_id)
|
||||
@ -1733,52 +1753,23 @@ class Citizen(classes.CitizenAPI):
|
||||
self.reporter.send_state_update(**data)
|
||||
|
||||
def send_inventory_update(self):
|
||||
j = self.update_inventory()
|
||||
active_items = {}
|
||||
for item in j.get("inventoryItems", {}).get("activeEnhancements", {}).get("items", {}).values():
|
||||
active_items.update({item['name']: item['active']['time_left']})
|
||||
final_items = {}
|
||||
|
||||
for item in j.get("inventoryItems", {}).get("finalProducts", {}).get("items", {}).values():
|
||||
name = item['name']
|
||||
if item.get('type') == 'damageBoosters':
|
||||
delta = item['duration']
|
||||
if delta // 3600:
|
||||
name += f" {delta // 3600}h"
|
||||
if delta // 60 % 60:
|
||||
name += f" {delta // 60 % 60}m"
|
||||
if delta % 60:
|
||||
name += f" {delta % 60}s"
|
||||
if item['amount']:
|
||||
final_items.update({name: item['amount']})
|
||||
|
||||
raw_materials = {}
|
||||
for item in j.get("inventoryItems", {}).get("rawMaterials", {}).get("items", {}).values():
|
||||
name = item['name']
|
||||
if item['isPartial']:
|
||||
continue
|
||||
if item['amount']:
|
||||
raw_materials.update({name: item['amount']})
|
||||
|
||||
to_report = dict(items=dict(active=active_items, final=final_items, raw=raw_materials), status=self.inventory)
|
||||
to_report = self.update_inventory()
|
||||
self.reporter.report_action("INVENTORY", json_val=to_report)
|
||||
|
||||
def check_house_durability(self) -> Dict[int, datetime.datetime]:
|
||||
ret = {}
|
||||
inv = self.update_inventory()
|
||||
if "activeEnhancements" in inv.get("inventoryItems", {}):
|
||||
active = inv.get("inventoryItems", {}).get("activeEnhancements", {}).get("items", {})
|
||||
for q in range(1, 6):
|
||||
if "4_%i_active" % q in active:
|
||||
till = utils.good_timedelta(self.now, datetime.timedelta(
|
||||
seconds=active["4_%i_active" % q]["active"]["time_left"]))
|
||||
ret.update({q: till})
|
||||
active = inv["items"]['active']
|
||||
for q in range(1, 6):
|
||||
if f"House Q{q}" in active:
|
||||
till = utils.good_timedelta(self.now, datetime.timedelta(seconds=active[f"House Q{q}"]))
|
||||
ret.update({q: till})
|
||||
return ret
|
||||
|
||||
def buy_and_activate_house(self, q: int) -> Dict[int, datetime.datetime]:
|
||||
inventory = self.update_inventory()
|
||||
ok_to_activate = False
|
||||
if not inventory.get("inventoryItems").get("finalProducts", {}).get("items", {}).get("4_{}".format(q)):
|
||||
if not inventory["items"]["final"].get("House Q{}".format(q)):
|
||||
offers = []
|
||||
countries = [self.details.citizenship, ]
|
||||
if self.details.current_country != self.details.citizenship:
|
||||
@ -1868,7 +1859,7 @@ class Citizen(classes.CitizenAPI):
|
||||
|
||||
def write_on_country_wall(self, message: str) -> bool:
|
||||
self._get_main()
|
||||
post_to_wall_as = re.findall(r"""id="post_to_country_as".*?<option value="(\d?)">.*?</option>.*</select>""",
|
||||
post_to_wall_as = re.findall(r'id="post_to_country_as".*?<option value="(\d?)">.*?</option>.*</select>',
|
||||
self.r.text, re.S | re.M)
|
||||
r = self._post_country_post_create(message, max(post_to_wall_as, key=int) if post_to_wall_as else 0)
|
||||
return r.json()
|
||||
@ -1892,4 +1883,53 @@ class Citizen(classes.CitizenAPI):
|
||||
return ret
|
||||
|
||||
def to_json(self, indent: bool = False) -> str:
|
||||
return dumps(self.__dict__, cls=utils.MyJSONEncoder, indent=4 if indent else None, sort_keys=True)
|
||||
return dumps(self.__dict__, cls=classes.MyJSONEncoder, indent=4 if indent else None, sort_keys=True)
|
||||
|
||||
def get_game_token_offers(self):
|
||||
r = self._post_economy_game_tokens_market('retrieve').json()
|
||||
return {v.get('id'): dict(amount=v.get('amount'), price=v.get('price')) for v in r.get("topOffers")}
|
||||
|
||||
def fetch_organisation_account(self, org_id: int):
|
||||
r = self._get_economy_citizen_accounts(org_id)
|
||||
table = re.search(r'(<table class="holder racc" .*</table>)', r.text, re.I | re.M | re.S)
|
||||
if table:
|
||||
account = re.findall(r'>\s*(\d+.\d+)\s*</td>', table.group(1))
|
||||
if account:
|
||||
return {"gold": account[0], "cc": account[1], 'ok': True}
|
||||
|
||||
return {"gold": 0, "cc": 0, 'ok': False}
|
||||
|
||||
def get_ground_hit_dmg_value(self, rang: int = None, strength: float = None, elite: bool = None, ne: bool = False,
|
||||
booster_50: bool = False, booster_100: bool = False, tp: bool = True) -> float:
|
||||
if not rang or strength or elite is None:
|
||||
r = self._get_citizen_profile(self.details.citizen_id).json()
|
||||
if not rang:
|
||||
rang = r['military']['militaryData']['ground']['rankNumber']
|
||||
if not strength:
|
||||
strength = r['military']['militaryData']['ground']['strength']
|
||||
if elite is None:
|
||||
elite = r['citizenAttributes']['level'] > 100
|
||||
if ne:
|
||||
tp = True
|
||||
|
||||
dmg = int(10 * (1 + strength / 400) * (1 + rang / 5) * 3)
|
||||
booster = 1.5 if booster_50 else 2 if booster_100 else 1
|
||||
elite = 1.1 if elite else 1
|
||||
dmg = int(dmg * booster * elite)
|
||||
legend = 1 if (not tp or rang < 70) else 1 + (rang - 69) / 10
|
||||
dmg = int(dmg * legend)
|
||||
return dmg * (1.1 if ne else 1)
|
||||
|
||||
def get_air_hit_dmg_value(self, rang: int = None, elite: bool = None, ne: bool = False,
|
||||
weapon: bool = False) -> float:
|
||||
if not rang or elite is None:
|
||||
r = self._get_citizen_profile(self.details.citizen_id).json()
|
||||
if not rang:
|
||||
rang = r['military']['militaryData']['air']['rankNumber']
|
||||
if elite is None:
|
||||
elite = r['citizenAttributes']['level'] > 100
|
||||
|
||||
dmg = int(10 * (1 + rang / 5) * (1.2 if weapon else 1))
|
||||
elite = 1.1 if elite else 1
|
||||
dmg = int(dmg * elite)
|
||||
return dmg * (1.1 if ne else 1.)
|
||||
|
@ -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):
|
||||
@ -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))
|
||||
|
||||
@ -615,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)
|
||||
@ -648,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)
|
||||
@ -748,7 +762,7 @@ 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, action: str, page: int = 1, **kwargs) -> Response:
|
||||
data = dict(battleId=battle_id, action=action, _token=self.token)
|
||||
@ -970,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
|
||||
@ -983,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:
|
||||
@ -993,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):
|
||||
@ -1006,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))
|
||||
@ -1029,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],
|
||||
|
@ -7,15 +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, 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",
|
||||
@ -23,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"
|
||||
|
||||
@ -86,7 +81,8 @@ COUNTRIES = {1: 'Romania', 9: 'Brazil', 10: 'Italy', 11: 'France', 12: 'Germany'
|
||||
|
||||
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',
|
||||
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',
|
||||
@ -98,27 +94,6 @@ COUNTRY_LINK = {1: 'Romania', 9: 'Brazil', 11: 'France', 12: 'Germany', 13: 'Hun
|
||||
169: 'Armenia', 83: 'Belarus', 84: 'New-Zealand', 164: 'Saudi-Arabia', 170: 'Nigeria', }
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def now() -> datetime.datetime:
|
||||
return datetime.datetime.now(erep_tz).replace(microsecond=0)
|
||||
|
||||
@ -296,6 +271,7 @@ def send_email(name: str, content: List[Any], player=None, local_vars: Mapping[A
|
||||
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):
|
||||
|
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 0.15.1
|
||||
current_version = 0.15.3
|
||||
commit = True
|
||||
tag = True
|
||||
|
||||
|
Reference in New Issue
Block a user