Compare commits

...

19 Commits

Author SHA1 Message Date
d07334f602 Bump version: 0.20.1.7 → 0.20.2 2020-07-04 00:31:33 +03:00
bcb27da54f Summer challange shanged battle list API 2020-07-04 00:31:26 +03:00
7ac22b5e11 Bump version: 0.20.1.6 → 0.20.1.7 2020-07-03 23:56:07 +03:00
5bd3d72a63 Quickfix for summer terrains 2020-07-03 23:54:32 +03:00
33d2c641df Config.json creator HTML 2020-07-03 15:32:13 +03:00
d1e078e443 Bump version: 0.20.1.5 → 0.20.1.6 2020-06-29 14:58:27 +03:00
71c64b0cf5 minor tweaks 2020-06-29 14:57:40 +03:00
3b5b15553d Bomb deploy bugfix where deploying in RW could leave player stranded in non residency region. Battle update bugfix where self.all_battles could be empty while re-parsing every battle 2020-06-29 14:48:37 +03:00
d077e10f15 Bump version: 0.20.1.4 → 0.20.1.5 2020-06-26 17:27:11 +03:00
8eb5235f12 Deploy bombs in RWs bugfix 2020-06-26 17:27:02 +03:00
a873d223c1 Bump version: 0.20.1.3 → 0.20.1.4 2020-06-25 14:18:41 +03:00
ed96143c80 Flake8
Battle.div is no longer a defaultdict, but should raise error if trying to access division which isn't assigned
2020-06-25 14:17:05 +03:00
b49e4ab594 Bump version: 0.20.1.2 → 0.20.1.3 2020-06-19 13:44:39 +03:00
2f69090c03 bugfix 2020-06-19 13:44:33 +03:00
df170048af Bump version: 0.20.1.1 → 0.20.1.2 2020-06-19 13:37:02 +03:00
8ca845cf17 Add damage amount to inventory bomb 2020-06-19 13:36:45 +03:00
ce7874fbf5 Bump version: 0.20.1 → 0.20.1.1 2020-06-18 10:14:50 +03:00
6abfc98fbd Test requirements 2020-06-18 10:13:55 +03:00
66f0e648df Citizen.to_json() bugfixed and optimised 2020-06-18 10:10:22 +03:00
9 changed files with 631 additions and 181 deletions

387
docs/index.html Normal file
View File

@ -0,0 +1,387 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="Eriks Karls">
<meta name="generator" content="Jekyll v4.0.1">
<title>eBot configuration</title>
<!-- CSS only -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk"
crossorigin="anonymous">
<!-- JS, Popper.js, and jQuery -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI"
crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<div class="p-3 text-center">
<h1>eBot configuration file generator</h1>
<!-- <h2>-->
<!-- <span class="d-inline d-sm-none d-md-none d-lg-none d-xl-none">XS</span>-->
<!-- <span class="d-none d-sm-inline d-md-none d-lg-none d-xl-none">SM</span>-->
<!-- <span class="d-none d-sm-none d-md-inline d-lg-none d-xl-none">MD</span>-->
<!-- <span class="d-none d-sm-none d-md-none d-lg-inline d-xl-none">LG</span>-->
<!-- <span class="d-none d-sm-none d-md-none d-lg-none d-xl-inline">XL</span>-->
<!-- </h2>-->
</div>
<div class="row pt-4">
<div class="col-12">
<form>
<div class="row">
<div class="col-12 col-sm-8 col-md-6">
<h3>Login data</h3>
<div class="form-group">
<input type="email" class="form-control form-control-sm" onchange="updateJson()" id="email" placeholder="E-mail...">
<input type="password" class="form-control form-control-sm mt-3" onchange="updateJson()" id="password" disabled placeholder="Password..."
aria-describedby="passwordHelpBlock">
<small id="passwordHelpBlock" class="form-text text-muted"><strong>NEVER</strong> enter Your passwords on 3rd party sites and <strong class="text-upper">DO NOT</strong> reuse Your
password!</small>
</div>
</div>
<div class="col-6 col-md-3">
<h3>Basic tasks</h3>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="work" checked>
<label class="custom-control-label" for="work">Work</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="train" checked>
<label class="custom-control-label" for="train">Train</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="ot" checked>
<label class="custom-control-label" for="ot">Work overtime</label>
</div>
</div>
<div class="col-6 col-md-3">
<h3>Misc</h3>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="renew_houses" checked>
<label class="custom-control-label" for="renew_houses">Auto renew houses</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="random_sleep" checked>
<label class="custom-control-label" for="random_sleep">Random sleep</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="buy_gold">
<label class="custom-control-label" for="buy_gold">Auto buy 10g</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="interactive" checked>
<label class="custom-control-label" for="interactive">Interactive</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="debug">
<label class="custom-control-label" for="debug">Debug</label>
</div>
</div>
</div>
<div class="row">
<div class="col-12 col-sm-6">
<h3>Production</h3>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="wam" checked>
<label class="custom-control-label" for="wam">Work as manager</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="employ">
<label class="custom-control-label" for="employ">Employ employees</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_buy_raw" checked>
<label class="custom-control-label" for="auto_buy_raw">Auto buy missing RAW</label>
</div>
</div>
<div class="form-group">
<h6 class="">Auto sell produced products</h6>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_frm">
<label class="custom-control-label" for="auto_sell_frm">Food Raw</label>
</div>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_food">
<label class="custom-control-label" for="auto_sell_food">Food</label>
</div>
<div class="clearfix"></div>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_wrm">
<label class="custom-control-label" for="auto_sell_wrm">Weapon Raw</label>
</div>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_weapons">
<label class="custom-control-label" for="auto_sell_weapons">Weapon</label>
</div>
<div class="clearfix"></div>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_hrm">
<label class="custom-control-label" for="auto_sell_hrm">House Raw</label>
</div>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_house">
<label class="custom-control-label" for="auto_sell_house">House</label>
</div>
<div class="clearfix"></div>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_arm">
<label class="custom-control-label" for="auto_sell_arm">Aircraft Raw</label>
</div>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_air">
<label class="custom-control-label" for="auto_sell_air">Aircraft Weapon</label>
</div>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_all">
<label class="custom-control-label" for="auto_sell_all">Auto sell all (also from inventory)</label>
</div>
</div>
<div class="col-12 col-sm-6">
<h3>Fighting</h3>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="fight" checked>
<label class="custom-control-label" for="fight">Fight</label>
</div>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="air" checked>
<label class="custom-control-label" for="air">Air</label>
</div>
<div class="custom-control custom-switch custom-control-inline">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="ground">
<label class="custom-control-label" for="ground">Ground</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="boosters">
<label class="custom-control-label" for="boosters">Use ground boosters</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="continuous_fighting">
<label class="custom-control-label" for="continuous_fighting">Continue fighting all FF in round</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="next_energy" checked>
<label class="custom-control-label" for="next_energy">Fight for next WC +1hp/6min if reachable by FF</label>
</div>
</div>
<div class="form-group">
<div class="form-check form-check-inline">
<input type="radio" class="form-check-input" onchange="updateJson()" id="all_in" name="fight_amount" value="all_in">
<label class="form-check-label" for="all_in">All energy</label>
</div>
<div class="form-check form-check-inline">
<input type="radio" class="form-check-input" onchange="updateJson()" id="h_energy" name="fight_amount" value="h_energy" checked>
<label class="form-check-label" for="h_energy">1h energy</label>
</div>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="rw_def_side" checked>
<label class="custom-control-label" for="rw_def_side">In RWs fight on right side (occupier/defender)</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="travel_to_fight" checked>
<label class="custom-control-label" for="travel_to_fight">Travel to fight</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="epic_hunt">
<label class="custom-control-label" for="epic_hunt">Hunt epics</label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="epic_hunt_ebs">
<label class="custom-control-label" for="epic_hunt_ebs">Spend <small>[all]</small> EBs in epics</label>
</div>
</div>
</div>
</form>
</div>
<div class="col-12">
<pre id="json-output"></pre>
</div>
</div>
</div>
<script>
function disable(element){
element.checked = false;
element.disabled = true;
}
function updateJson() {
let config = {};
let email = document.getElementById('email'); // Generated
config.email = email.value;
config.password = "";
let work = document.getElementById('work'); // Generated
config.work = work.checked;
let train = document.getElementById('train'); // Generated
config.train = train.checked;
let ot = document.getElementById('ot'); // Generated
config.ot = ot.checked;
let renew_houses = document.getElementById('renew_houses'); // Generated
config.renew_houses = renew_houses.checked;
let random_sleep = document.getElementById('random_sleep'); // Generated
config.random_sleep = random_sleep.checked;
let buy_gold = document.getElementById('buy_gold'); // Generated
config.buy_gold = buy_gold.checked;
let interactive = document.getElementById('interactive'); // Generated
config.interactive = interactive.checked;
let debug = document.getElementById('debug'); // Generated
config.debug = debug.checked;
let wam = document.getElementById('wam'); // Generated
config.wam = wam.checked;
let employ = document.getElementById('employ'); // Generated
config.employ = employ.checked;
let auto_buy_raw = document.getElementById('auto_buy_raw'); // Generated
let auto_sell_all = document.getElementById('auto_sell_all'); // Generated
let auto_sell_frm = document.getElementById('auto_sell_frm'); // Generated
let auto_sell_food = document.getElementById('auto_sell_food'); // Generated
let auto_sell_wrm = document.getElementById('auto_sell_wrm'); // Generated
let auto_sell_weapons = document.getElementById('auto_sell_weapons'); // Generated
let auto_sell_hrm = document.getElementById('auto_sell_hrm'); // Generated
let auto_sell_house = document.getElementById('auto_sell_house'); // Generated
let auto_sell_arm = document.getElementById('auto_sell_arm'); // Generated
let auto_sell_air = document.getElementById('auto_sell_air'); // Generated
if (config.wam || config.employ) {
auto_buy_raw.disabled = false;
auto_sell_all.disabled = false;
auto_sell_frm.disabled = false;
auto_sell_food.disabled = false;
auto_sell_wrm.disabled = false;
auto_sell_weapons.disabled = false;
auto_sell_hrm.disabled = false;
auto_sell_house.disabled = false;
auto_sell_arm.disabled = false;
auto_sell_air.disabled = false;
} else {
disable(auto_buy_raw);
disable(auto_sell_all);
disable(auto_sell_food);
disable(auto_sell_weapons);
disable(auto_sell_house);
disable(auto_sell_air);
disable(auto_sell_frm);
disable(auto_sell_wrm);
disable(auto_sell_hrm);
disable(auto_sell_arm);
}
config.auto_buy_raw = auto_buy_raw.checked;
config.auto_sell_all = auto_sell_all.checked;
config.auto_sell = [];
if (auto_sell_food.checked) config.auto_sell.push("food");
if (auto_sell_weapons.checked) config.auto_sell.push("weapon");
if (auto_sell_house.checked) config.auto_sell.push("house");
if (auto_sell_air.checked) config.auto_sell.push("airplane");
if (auto_sell_frm.checked) config.auto_sell.push("foodRaw");
if (auto_sell_wrm.checked) config.auto_sell.push("weaponRaw");
if (auto_sell_hrm.checked) config.auto_sell.push("houseRaw");
if (auto_sell_arm.checked) config.auto_sell.push("airplaneRaw");
let fight = document.getElementById('fight'); // Generated
config.fight = fight.checked;
let air = document.getElementById('air'); // Generated
let ground = document.getElementById('ground'); // Generated
let boosters = document.getElementById('boosters'); // Generated
let continuous_fighting = document.getElementById('continuous_fighting'); // Generated
let next_energy = document.getElementById('next_energy'); // Generated
let all_in = document.getElementById('all_in'); // Generated
let h_energy = document.getElementById('h_energy'); // Generated
let rw_def_side = document.getElementById('rw_def_side'); // Generated
let travel_to_fight = document.getElementById('travel_to_fight'); // Generated
let epic_hunt = document.getElementById('epic_hunt'); // Generated
let epic_hunt_ebs = document.getElementById('epic_hunt_ebs'); // Generated
if (config.fight){
air.disabled = false;
ground.disabled = false;
boosters.disabled = false;
continuous_fighting.disabled = false;
next_energy.disabled = false;
all_in.disabled = false;
h_energy.disabled = false;
rw_def_side.disabled = false;
travel_to_fight.disabled = false;
epic_hunt.disabled = false;
epic_hunt_ebs.disabled = false;
if (!epic_hunt.checked) disable(epic_hunt_ebs);
} else {
disable(air);
disable(ground);
disable(boosters);
disable(continuous_fighting);
disable(next_energy);
disable(all_in);
disable(h_energy);
disable(rw_def_side);
disable(travel_to_fight);
disable(epic_hunt);
disable(epic_hunt_ebs);
}
config.air = air.checked;
config.ground = ground.cehcked;
config.boosters = boosters.checked;
config.continuous_fighting = continuous_fighting.checked;
config.next_energy = next_energy.checked;
config.all_in = all_in.checked;
config.rw_def_side = rw_def_side.checked;
config.travel_to_fight = travel_to_fight.checked;
config.epic_hunt = epic_hunt.checked;
config.epic_hunt_ebs = config.epic_hunt ? epic_hunt_ebs.checked : config.epic_hunt;
let pre = document.getElementById('json-output');
pre.textContent = JSON.stringify(config, null, 2);
}
updateJson();
</script>
</body>
</html>
<!--
{
"email": "",
"password": "",
"work": true,
"train": true,
"ot": true,
"renew_houses": true,
"random_sleep": true,
"buy_gold": true,
"interactive": true,
"debug": true,
"wam": true,
"employ": true,
"auto_buy_raw": true,
"auto_sell_all": true,
"auto_sell": [
"food",
"weapon",
"house",
"airplane",
"foodRaw",
"weaponRaw",
"houseRaw",
"airplaneRaw"
],
"fight": true,
"air": true,
"boosters": true,
"continuous_fighting": true,
"next_energy": true,
"all_in": false,
"rw_def_side": true,
"travel_to_fight": true,
"epic_hunt": true,
"epic_hunt_ebs": true
}
-->

View File

@ -4,8 +4,8 @@
__author__ = """Eriks Karls"""
__email__ = 'eriks@72.lv'
__version__ = '0.20.1'
__commit_id__ = "a825917"
__version__ = '0.20.2'
__commit_id__ = "bcb27da"
from erepublik import classes, utils
from erepublik.citizen import Citizen

View File

@ -12,8 +12,8 @@ from requests import HTTPError, RequestException, Response
from erepublik import utils
from erepublik.access_points import CitizenAPI
from erepublik.classes import (Battle, BattleDivision, Config, Details, Energy, ErepublikException,
MyCompanies, MyJSONEncoder, OfferItem, Politics, Reporter, TelegramBot)
from erepublik.classes import Battle, Config, Details, Energy, \
ErepublikException, MyCompanies, MyJSONEncoder, OfferItem, Politics, Reporter, TelegramBot
class BaseCitizen(CitizenAPI):
@ -322,6 +322,14 @@ class BaseCitizen(CitizenAPI):
if item.get('type') in ('damageBoosters', "aircraftDamageBoosters"):
data = {data['durability']: data}
else:
if item.get('type') == 'bomb':
firepower = 0
try:
firepower = item.get('attributes').get('firePower').get('value', 0)
except AttributeError:
pass
finally:
data.update(fire_power=firepower)
data = {data['quality']: data}
final_items[kind].update(data)
@ -427,7 +435,7 @@ class BaseCitizen(CitizenAPI):
def __dict__(self):
ret = super().__dict__.copy()
ret.pop('stop_threads', None)
ret.pop('_Citizen__last_war_update_data', None)
ret.pop('_CitizenMilitary__last_war_update_data', None)
return ret
@ -656,7 +664,16 @@ class BaseCitizen(CitizenAPI):
return bool(re.search(r'body id="error"|Internal Server Error|'
r'CSRF attack detected|meta http-equiv="refresh"|not_authenticated', response.text))
def _report_action(self, action: str, msg: str, **kwargs):
def _report_action(self, action: str, msg: str, **kwargs: Optional[Dict[str, Any]]):
""" Report action to all available reporting channels
:type action: str
:type msg: str
:type kwargs: Optional[Dict[str, Any]]
:param action: Action taken
:param msg: Message about the action
:param kwargs: Extra information regarding action
"""
kwargs = utils.json.loads(utils.json.dumps(kwargs or {}, cls=MyJSONEncoder))
action = action[:32]
self.write_log(msg)
@ -830,8 +847,8 @@ class CitizenCompanies(BaseCitizen):
raw_factories = True
free_inventory = self.inventory["total"] - self.inventory["used"]
wam_list = self.my_companies.get_holding_wam_companies(wam_holding_id,
raw_factory=raw_factories)[:self.energy.food_fights]
wam_list = self.my_companies.get_holding_wam_companies(
wam_holding_id, raw_factory=raw_factories)[:self.energy.food_fights]
has_space = False
while not has_space and wam_list:
extra_needed = self.my_companies.get_needed_inventory_usage(companies=wam_list)
@ -851,7 +868,8 @@ class CitizenCompanies(BaseCitizen):
if sum(employ_factories.values()) > self.my_companies.work_units:
employ_factories = {}
response = self._post_economy_work("production", wam=[c.id for c in wam_list], employ=employ_factories).json()
response = self._post_economy_work("production", wam=[c.id for c in wam_list],
employ=employ_factories).json()
return response
def update_companies(self):
@ -890,8 +908,8 @@ class CitizenCompanies(BaseCitizen):
18: "HRM q1", 19: "HRM q2", 20: "HRM q3", 21: "HRM q4", 22: "HRM q5",
24: "ARM q1", 25: "ARM q2", 26: "ARM q3", 27: "ARM q4", 28: "ARM q5", }[industry_id]
if building_type == 2:
company_name = f"Storage"
self.write_log(f"{company_name} created!")
company_name = "Storage"
self.write_log(f'{company_name} created!')
return self._post_economy_create_company(industry_id, building_type)
def dissolve_factory(self, factory_id: int) -> Response:
@ -1141,7 +1159,8 @@ class CitizenEconomy(CitizenTravel):
self.stop_threads.wait()
return False
else:
self._report_action("BUY_GOLD", f"New amount {self.details.cc}cc, {self.details.gold}g", kwargs=response.json())
self._report_action('BUY_GOLD', f'New amount {self.details.cc}cc, {self.details.gold}g',
kwargs=response.json())
return True
def donate_money(self, citizen_id: int = 1620414, amount: float = 0.0, currency: int = 62) -> bool:
@ -1166,25 +1185,25 @@ class CitizenEconomy(CitizenTravel):
if re.search(rf"Successfully transferred {amount} item\(s\) to", response.text):
msg = (f"Successfully donated {amount}q{quality} {self.get_industry_name(industry_id)} "
f"to citizen with id {citizen_id}!")
self._report_action("DONATE_ITEMS", msg, success=True)
self._report_action("DONATE_ITEMS", msg)
return amount
elif re.search('You must wait 5 seconds before donating again', response.text):
self.write_log(f"Previous donation failed! Must wait at least 5 seconds before next donation!")
self.write_log('Previous donation failed! Must wait at least 5 seconds before next donation!')
self.sleep(5)
return self.donate_items(citizen_id, int(amount), industry_id, quality)
else:
if re.search(r"You do not have enough items in your inventory to make this donation", response.text):
if re.search(r'You do not have enough items in your inventory to make this donation', response.text):
self._report_action("DONATE_ITEMS",
f"Unable to donate {amount}q{quality} "
f"{self.get_industry_name(industry_id)}, not enough left!", success=False)
f"{self.get_industry_name(industry_id)}, not enough left!")
return 0
available = re.search(
rf"Cannot transfer the items because the user has only (\d+) free slots in (his|her) storage.",
r'Cannot transfer the items because the user has only (\d+) free slots in (his|her) storage.',
response.text
).group(1)
self._report_action("DONATE_ITEMS",
f"Unable to donate {amount}q{quality}{self.get_industry_name(industry_id)}"
f", receiver has only {available} storage left!", success=False)
self._report_action('DONATE_ITEMS',
f'Unable to donate {amount}q{quality}{self.get_industry_name(industry_id)}'
f', receiver has only {available} storage left!')
self.sleep(5)
return self.donate_items(citizen_id, int(available), industry_id, quality)
@ -1196,8 +1215,7 @@ class CitizenEconomy(CitizenTravel):
data = dict(country=country_id, action='currency', value=amount)
r = self._post_main_country_donate(**data)
if r.json().get('status') or not r.json().get('error'):
self._report_action("CONTRIBUTE_CC", f"Contributed {amount}cc to {utils.COUNTRIES[country_id]}'s treasury",
success=True)
self._report_action("CONTRIBUTE_CC", f'Contributed {amount}cc to {utils.COUNTRIES[country_id]}\'s treasury')
return True
else:
self._report_action("CONTRIBUTE_CC", f"Unable to contribute {amount}cc to {utils.COUNTRIES[country_id]}'s"
@ -1214,7 +1232,7 @@ class CitizenEconomy(CitizenTravel):
if r.json().get('status') or not r.json().get('error'):
self._report_action("CONTRIBUTE_FOOD", f"Contributed {amount}q{quality} food to "
f"{utils.COUNTRIES[country_id]}'s treasury", success=True)
f"{utils.COUNTRIES[country_id]}'s treasury")
return True
else:
self._report_action("CONTRIBUTE_FOOD", f"Unable to contribute {amount}q{quality} food to "
@ -1231,8 +1249,7 @@ class CitizenEconomy(CitizenTravel):
r = self._post_main_country_donate(**data)
if r.json().get('status') or not r.json().get('error'):
self._report_action("CONTRIBUTE_GOLD", f"Contributed {amount}g to {utils.COUNTRIES[country_id]}'s treasury",
success=True)
self._report_action("CONTRIBUTE_GOLD", f"Contributed {amount}g to {utils.COUNTRIES[country_id]}'s treasury")
return True
else:
self._report_action("CONTRIBUTE_GOLD", f"Unable to contribute {amount}g to {utils.COUNTRIES[country_id]}'s"
@ -1259,7 +1276,7 @@ class CitizenMedia(BaseCitizen):
if amount in (5, 50, 100):
resp = self._post_main_donate_article(article_id, amount).json()
if not bool(resp.get('error')):
self._report_action("ARTICLE_ENDORSE", f"Endorsed article ({article_id}) with {amount}cc", success=True)
self._report_action("ARTICLE_ENDORSE", f"Endorsed article ({article_id}) with {amount}cc")
return True
else:
self._report_action("ARTICLE_ENDORSE", f"Unable to endorse article ({article_id}) with {amount}cc",
@ -1272,7 +1289,7 @@ class CitizenMedia(BaseCitizen):
resp = self._post_main_vote_article(article_id).json()
if not bool(resp.get('error')):
self._report_action("ARTICLE_VOTE", f"Voted article {article_id}", success=True)
self._report_action("ARTICLE_VOTE", f"Voted article {article_id}")
return True
else:
self._report_action("ARTICLE_VOTE", f"Unable to vote for article {article_id}", kwargs=resp)
@ -1325,25 +1342,23 @@ class CitizenMilitary(CitizenTravel):
if r_json.get("countries"):
if self.all_battles is None:
self.all_battles = {}
else:
self.all_battles.clear()
if self.countries is None:
self.countries = {}
else:
self.countries.clear()
countries = {}
for c_id, c_data in r_json.get("countries").items():
if int(c_id) not in self.countries:
self.countries.update({
if int(c_id) not in countries:
countries.update({
int(c_id): {"name": c_data.get("name"), "allies": c_data.get("allies")}
})
else:
self.countries[int(c_id)].update(allies=c_data.get("allies"))
countries[int(c_id)].update(allies=c_data.get("allies"))
self.countries = countries
self.__last_war_update_data = r_json
if r_json.get("battles"):
all_battles = {}
for battle_data in r_json.get("battles", {}).values():
self.all_battles[battle_data.get('id')] = Battle(battle_data)
all_battles[battle_data.get('id')] = Battle(battle_data)
self.all_battles = all_battles
def get_battle_for_war(self, war_id: int) -> Optional[Battle]:
self.update_war_info()
@ -1373,74 +1388,73 @@ class CitizenMilitary(CitizenTravel):
def get_available_weapons(self, battle_id: int):
return self._get_military_show_weapons(battle_id).json()
def set_default_weapon(self, battle_id: int) -> int:
def set_default_weapon(self, battle_id: int, battle_zone: int) -> int:
battle = self.all_battles.get(battle_id)
available_weapons = self._get_military_show_weapons(battle_id).json()
while not isinstance(available_weapons, list):
available_weapons = self._get_military_show_weapons(battle_id).json()
weapon_quality = -1
weapon_damage = 0
if not battle.is_air:
if not battle.div[battle_zone].div == 11:
for weapon in available_weapons:
try:
if weapon['weaponQuantity'] > 30 and weapon['weaponInfluence'] > weapon_damage:
weapon_quality = int(weapon['weaponId'])
except ValueError:
pass
return self.change_weapon(battle_id, weapon_quality)
return self.change_weapon(battle_id, weapon_quality, battle_zone)
def change_weapon(self, battle_id: int, quality: int) -> int:
battle = self.all_battles.get(battle_id)
battle_zone = battle.div[11 if battle.is_air else self.division].battle_zone_id
def change_weapon(self, battle_id: int, quality: int, battle_zone: int) -> int:
r = self._post_military_change_weapon(battle_id, battle_zone, quality)
influence = r.json().get('weaponInfluence')
self._report_action("MILITARY_WEAPON", f"Switched to q{quality} weapon, new influence {influence}", kwargs=r.json())
self._report_action("MILITARY_WEAPON", f"Switched to q{quality} weapon,"
f" new influence {influence}", kwargs=r.json())
return influence
def check_epic_battles(self):
active_fs = False
for battle_id in self.sorted_battles(self.config.sort_battles_time):
battle = self.all_battles.get(battle_id)
if not battle.is_air:
my_div: BattleDivision = battle.div.get(self.division)
if my_div.epic and my_div.end > self.now:
if self.energy.food_fights > 50:
inv_allies = battle.invader.deployed + [battle.invader.id]
def_allies = battle.defender.deployed + [battle.defender.id]
all_allies = inv_allies + def_allies
if self.details.current_country not in all_allies:
if self.details.current_country in battle.invader.allies:
allies = battle.invader.deployed
side = battle.invader.id
else:
allies = battle.defender.deployed
side = battle.defender.id
self.travel_to_battle(battle.id, allies)
else:
if self.details.current_country in inv_allies:
side = battle.invader.id
elif self.details.current_country in def_allies:
side = battle.defender.id
else:
self.write_log(
f"Country {self.details.current_country} not in all allies list ({all_allies}) and "
f"also not in inv allies ({inv_allies}) nor def allies ({def_allies})")
break
error_count = 0
while self.energy.food_fights > 5 and error_count < 20:
errors = self.fight(battle_id, side_id=side, count=self.energy.food_fights - 5)
if errors:
error_count += errors
if self.config.epic_hunt_ebs:
self._eat('orange')
self.travel_to_residence()
break
elif bool(my_div.epic):
active_fs = True
self.active_fs = active_fs
# def check_epic_battles(self):
# active_fs = False
# for battle_id in self.sorted_battles(self.config.sort_battles_time):
# battle = self.all_battles.get(battle_id)
# if not battle.is_air:
# my_div: BattleDivision = battle.div.get(self.division)
# if my_div.epic and my_div.end > self.now:
# if self.energy.food_fights > 50:
# inv_allies = battle.invader.deployed + [battle.invader.id]
# def_allies = battle.defender.deployed + [battle.defender.id]
# all_allies = inv_allies + def_allies
# if self.details.current_country not in all_allies:
# if self.details.current_country in battle.invader.allies:
# allies = battle.invader.deployed
# side = battle.invader.id
# else:
# allies = battle.defender.deployed
# side = battle.defender.id
#
# self.travel_to_battle(battle.id, allies)
#
# else:
# if self.details.current_country in inv_allies:
# side = battle.invader.id
# elif self.details.current_country in def_allies:
# side = battle.defender.id
# else:
# self.write_log(
# f"Country {self.details.current_country} not in all allies list ({all_allies}) and "
# f"also not in inv allies ({inv_allies}) nor def allies ({def_allies})")
# break
# error_count = 0
# while self.energy.food_fights > 5 and error_count < 20:
# errors = self.fight(battle_id, side_id=side, count=self.energy.food_fights - 5)
# if errors:
# error_count += errors
# if self.config.epic_hunt_ebs:
# self._eat('orange')
# self.travel_to_residence()
# break
# elif bool(my_div.epic):
# active_fs = True
#
# self.active_fs = active_fs
def sorted_battles(self, sort_by_time: bool = True) -> List[int]:
cs_battles_priority_air: List[int] = []
@ -1472,7 +1486,7 @@ class CitizenMilitary(CitizenTravel):
continue
# CS Battles
elif self.details.citizenship in battle_sides:
if battle.is_air:
if battle.has_air:
if battle.defender.id == self.details.citizenship:
cs_battles_priority_air.append(battle.id)
else:
@ -1485,7 +1499,7 @@ class CitizenMilitary(CitizenTravel):
# Current location battles:
elif self.details.current_country in battle_sides:
if battle.is_air:
if battle.has_air:
deployed_battles_air.append(battle.id)
else:
deployed_battles_ground.append(battle.id)
@ -1493,18 +1507,18 @@ class CitizenMilitary(CitizenTravel):
# Deployed battles and allied battles:
elif self.details.current_country in battle.invader.allies + battle.defender.allies + battle_sides:
if self.details.current_country in battle.invader.deployed + battle.defender.deployed:
if battle.is_air:
if battle.has_air:
deployed_battles_air.append(battle.id)
else:
deployed_battles_ground.append(battle.id)
# Allied battles:
else:
if battle.is_air:
if battle.has_air:
ally_battles_air.append(battle.id)
else:
ally_battles_ground.append(battle.id)
else:
if battle.is_air:
if battle.has_air:
other_battles_air.append(battle.id)
else:
other_battles_ground.append(battle.id)
@ -1526,8 +1540,17 @@ class CitizenMilitary(CitizenTravel):
battle = self.all_battles.get(battle_id)
if not isinstance(battle, Battle):
continue
div = 11 if battle.is_air else self.division
battle_zone = 0
for div in battle.div.values():
if div.terrain == 0:
if self.config.air and div.is_air:
battle_zone = div.id
break
elif self.config.ground and not div.is_air and div.div == self.division:
battle_zone = div.id
break
else:
return
allies = battle.invader.deployed + battle.defender.deployed + [battle.invader.id, battle.defender.id]
travel_needed = self.details.current_country not in allies
@ -1537,20 +1560,13 @@ class CitizenMilitary(CitizenTravel):
else:
side = self.details.current_country in battle.defender.allies + [battle.defender.id, ]
side_id = battle.defender.id if side else battle.invader.id
try:
def_points = battle.div.get(div).dom_pts.get('def')
inv_points = battle.div.get(div).dom_pts.get('inv')
except KeyError:
self.report_error(f"Division {div} not available for battle {battle.id}!")
def_points = inv_points = 3600
self.write_log(battle)
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
travel = (self.config.travel_to_fight and self.should_travel_to_fight() or self.config.force_travel) \
if travel_needed else True
if not (points and b_type and travel):
if not travel:
continue
if battle.start > self.now:
@ -1568,12 +1584,12 @@ class CitizenMilitary(CitizenTravel):
if not self.travel_to_battle(battle_id, country_ids_to_travel):
break
self.set_default_weapon(battle_id)
self.fight(battle_id, side_id)
self.set_default_weapon(battle_id, battle_zone)
self.fight(battle_id, battle_zone, side_id)
self.travel_to_residence()
break
def fight(self, battle_id: int, side_id: int = None, count: int = None, division: int = None) -> int:
def fight(self, battle_id: int, battle_zone: int, side_id: int = None, count: int = None) -> int:
"""Fight in a battle.
Will auto activate booster and travel if allowed to do it.
@ -1584,8 +1600,8 @@ class CitizenMilitary(CitizenTravel):
:type side_id: int
:param count: How many hits to do, if not specified self.should_fight() is called.
:type count: int
:param division: int Division number to fight in available choices 1,2,3,4,11
:type division: int
:param battle_zone: int Division number to fight in available choices
:type battle_zone: int
:return: None if no errors while fighting, otherwise error count.
:rtype: int
"""
@ -1598,9 +1614,7 @@ class CitizenMilitary(CitizenTravel):
if battle_id not in self.all_battles:
self.update_war_info()
battle = self.all_battles.get(battle_id)
division = self.division if division is None else division
zone_id = battle.div[11 if battle.is_air else division].battle_zone_id
if not battle.is_air and self.config.boosters:
if not battle.div[battle_zone].is_air and self.config.boosters:
self.activate_dmg_booster()
error_count = 0
ok_to_fight = True
@ -1612,7 +1626,7 @@ class CitizenMilitary(CitizenTravel):
side = battle.invader.id == side_id or side_id in battle.invader.deployed
while ok_to_fight and error_count < 10 and count > 0:
while all((count > 0, error_count < 10, self.energy.recovered >= 50)):
hits, error, damage = self._shoot(battle_id, side, zone_id)
hits, error, damage = self._shoot(battle_id, side, battle_zone)
count -= hits
total_hits += hits
total_damage += damage
@ -1624,13 +1638,13 @@ class CitizenMilitary(CitizenTravel):
ok_to_fight = False
if total_damage:
self.reporter.report_action("FIGHT", dict(battle=battle_id, side=side_id, dmg=total_damage,
air=battle.is_air, hits=total_hits))
air=battle.has_air, hits=total_hits))
return error_count
def _shoot(self, battle_id: int, inv_side: bool, zone_id: int):
battle = self.all_battles.get(battle_id)
side_id = battle.invader.id if inv_side else battle.defender.id
if battle.is_air:
if battle.div[zone_id].is_air:
response = self._post_military_fight_air(battle_id, side_id, zone_id)
else:
response = self._post_military_fight_ground(battle_id, side_id, zone_id)
@ -1649,7 +1663,7 @@ class CitizenMilitary(CitizenTravel):
if r_json.get("message") == "SHOOT_LOCKOUT" or r_json.get("message") == "ZONE_INACTIVE":
pass
elif r_json.get("message") == "NOT_ENOUGH_WEAPONS":
self.set_default_weapon(battle_id)
self.set_default_weapon(battle_id, zone_id)
else:
if r_json.get("message") == "UNKNOWN_SIDE":
self._rw_choose_side(battle_id, side_id)
@ -1681,6 +1695,9 @@ class CitizenMilitary(CitizenTravel):
count = 1
has_traveled = False
battle = self.all_battles.get(battle_id)
if battle.is_rw:
has_traveled = self.travel_to_battle(battle_id, [battle.defender.id])
self._rw_choose_side(battle.id, battle.invader.id if inv_side else battle.defender.id)
if inv_side:
good_countries = [battle.invader.id] + battle.invader.deployed
if self.details.current_country not in good_countries:
@ -1768,11 +1785,11 @@ class CitizenMilitary(CitizenTravel):
return quality
def activate_battle_effect(self, battle_id: int, kind: str) -> Response:
self._report_action("MILITARY_BOOSTER", f"Activated {kind} booster")
self._report_action('MILITARY_BOOSTER', f'Activated {kind} booster')
return self._post_main_activate_battle_effect(battle_id, kind, self.details.citizen_id)
def activate_pp_booster(self, battle_id: int) -> Response:
self._report_action("MILITARY_BOOSTER", f"Activated PrestigePoint booster")
self._report_action('MILITARY_BOOSTER', 'Activated PrestigePoint booster')
return self._post_military_fight_activate_booster(battle_id, 1, 180, "prestige_points")
def _rw_choose_side(self, battle_id: int, side_id: int) -> Response:
@ -1949,29 +1966,29 @@ class CitizenPolitics(BaseCitizen):
return ret
def candidate_for_congress(self, presentation: str = "") -> Response:
self._report_action("POLITIC_CONGRESS", f"Applied for congress elections")
self._report_action('POLITIC_CONGRESS', 'Applied for congress elections')
return self._post_candidate_for_congress(presentation)
def candidate_for_party_presidency(self) -> Response:
self._report_action("POLITIC_PARTY_PRESIDENT", f"Applied for party president elections")
self._report_action('POLITIC_PARTY_PRESIDENT', 'Applied for party president elections')
return self._get_candidate_party(self.politics.party_slug)
class CitizenSocial(BaseCitizen):
def send_mail_to_owner(self):
if not self.details.citizen_id == 1620414:
self.send_mail("Started", "time {}".format(self.now.strftime("%Y-%m-%d %H-%M-%S")), [1620414, ])
self.send_mail('Started', f'time {self.now.strftime("%Y-%m-%d %H-%M-%S")}', [1620414, ])
self.sleep(1)
msg_id = re.search(r"<input type=\"hidden\" value=\"(\d+)\" "
r"id=\"delete_message_(\d+)\" name=\"delete_message\[]\">", self.r.text).group(1)
msg_id = re.search(r'<input type="hidden" value="(\d+)" '
r'id="delete_message_(\d+)" name="delete_message\[]">', self.r.text).group(1)
self._post_delete_message([msg_id])
def send_mail(self, subject: str, msg: str, ids: List[int] = None):
if ids is None:
ids = [1620414, ]
for player_id in ids:
self._report_action("SOCIAL_MESSAGE", f"Sent a message to {player_id}", kwargs=dict(subject=subject, msg=msg,
id=player_id))
self._report_action('SOCIAL_MESSAGE', f'Sent a message to {player_id}',
kwargs=dict(subject=subject, msg=msg, id=player_id))
self._post_main_messages_compose(subject, msg, [player_id])
def write_on_country_wall(self, message: str) -> bool:
@ -1980,16 +1997,16 @@ class CitizenSocial(BaseCitizen):
self.r.text, re.S | re.M)
r = self._post_main_country_post_create(message, max(post_to_wall_as, key=int) if post_to_wall_as else 0)
self._report_action("SOCIAL_WRITE_WALL_COUNTRY", f"Wrote a message to the country wall", kwargs=message)
self._report_action('SOCIAL_WRITE_WALL_COUNTRY', 'Wrote a message to the country wall')
return r.json()
def add_friend(self, player_id: int) -> Response:
resp = self._get_main_citizen_hovercard(player_id)
r_json = resp.json()
if not any([r_json["isBanned"], r_json["isDead"], r_json["isFriend"], r_json["isOrg"], r_json["isSelf"]]):
if not any([r_json['isBanned'], r_json['isDead'], r_json['isFriend'], r_json['isOrg'], r_json['isSelf']]):
r = self._post_main_citizen_add_remove_friend(int(player_id), True)
self.write_log(f"{r_json['name']:<64} (id:{player_id:>11}) added as friend")
self._report_action("SOCIAL_ADD_FRIEND", f"{r_json['name']:<64} (id:{player_id:>11}) added as friend")
self._report_action('SOCIAL_ADD_FRIEND', f"{r_json['name']:<64} (id:{player_id:>11}) added as friend")
return r
return resp
@ -2156,7 +2173,7 @@ class CitizenTasks(BaseCitizen):
def resign_from_employer(self) -> bool:
r = self.update_job_info()
if r.json().get("isEmployee"):
self._report_action("ECONOMY_RESIGN", f"Resigned from employer!", kwargs=r.json())
self._report_action('ECONOMY_RESIGN', 'Resigned from employer!', kwargs=r.json())
self._post_economy_resign()
return True
return False
@ -2167,7 +2184,7 @@ class CitizenTasks(BaseCitizen):
extra = ret.json()
except: # noqa
extra = {}
self._report_action("ECONOMY_TG_CONTRACT", f"Bought TG Contract", kwargs=extra)
self._report_action('ECONOMY_TG_CONTRACT', 'Bought TG Contract', kwargs=extra)
return ret
def find_new_job(self) -> Response:
@ -2441,7 +2458,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
if response is None:
return
if response.get("status"):
self._report_action("WORK_AS_MANAGER", f"Worked as manager", kwargs=response)
self._report_action('WORK_AS_MANAGER', 'Worked as manager', kwargs=response)
if self.config.auto_sell:
for kind, data in response.get("result", {}).get("production", {}).items():
if data and kind in self.config.auto_sell:
@ -2519,7 +2536,7 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
self.travel_to_residence()
return bool(wam_count)
else:
self.write_log("Did not WAM because I would mess up levelup!")
self.write_log('Did not WAM because I would mess up levelup!')
self.my_companies.ff_lockdown = 0
self.update_companies()
@ -2542,4 +2559,4 @@ class Citizen(CitizenAnniversary, CitizenCompanies, CitizenEconomy, CitizenLeade
continue
self.stop_threads.wait(90)
except: # noqa
self.report_error("Command central has broken")
self.report_error('Command central is broken')

View File

@ -1,9 +1,8 @@
import datetime
import hashlib
import threading
from collections import defaultdict, deque
from decimal import Decimal
from typing import Any, Dict, List, NamedTuple, Tuple, Union, Optional
from typing import Any, Dict, List, NamedTuple, Optional, Tuple, Union
from requests import Response, Session, post
@ -65,11 +64,20 @@ class Holding:
return dict(frm=frm, wrm=wrm)
def __str__(self):
return f"Holding (#{self.id}) with {len(self.companies)} compan{'y' if len(self.companies) % 10 == 1 else 'ies'}"
name = f"Holding (#{self.id}) with {len(self.companies)} "
if len(self.companies) % 10 == 1:
name += "company"
else:
name += "companies"
return name
def __repr__(self):
return str(self)
@property
def __dict__(self):
return dict(name=str(self), id=self.id, region=self.region, companies=self.companies, wam_count=self.wam_count)
class Company:
holding: Holding
@ -172,6 +180,13 @@ class Company:
def __repr__(self):
return str(self)
@property
def __dict__(self):
return dict(name=str(self), holding=self.holding.id, id=self.id, quality=self.quality, is_raw=self.is_raw,
raw_usage=self.raw_usage, products_made=self.products_made, wam_enabled=self.wam_enabled,
can_wam=self.can_wam, cannot_wam_reason=self.cannot_wam_reason, industry=self.industry,
already_worked=self.already_worked, preset_works=self.preset_works)
class MyCompanies:
work_units: int = 0
@ -278,6 +293,11 @@ class MyCompanies:
holding.companies.clear()
self.companies.clear()
@property
def __dict__(self):
return dict(name=str(self), work_units=self.work_units, next_ot_time=self.next_ot_time,
ff_lockdown=self.ff_lockdown, holdings=self.holdings, company_count=len(self.companies))
class Config:
email = ""
@ -576,7 +596,7 @@ class MyJSONEncoder(json.JSONEncoder):
return float(f"{o:.02f}")
elif isinstance(o, datetime.datetime):
return dict(__type__='datetime', date=o.strftime("%Y-%m-%d"), time=o.strftime("%H:%M:%S"),
tzinfo=o.tzinfo.tzname if o.tzinfo else None)
tzinfo=str(o.tzinfo) if o.tzinfo else None)
elif isinstance(o, datetime.date):
return dict(__type__='date', date=o.strftime("%Y-%m-%d"))
elif isinstance(o, datetime.timedelta):
@ -586,18 +606,14 @@ class MyJSONEncoder(json.JSONEncoder):
return dict(headers=o.headers.__dict__, url=o.url, text=o.text)
elif hasattr(o, '__dict__'):
return o.__dict__
elif isinstance(o, (deque, set)):
elif isinstance(o, set):
return list(o)
elif isinstance(o, Citizen):
return o.to_json()
try:
return super().default(o)
except TypeError as e:
name = None
for ___, ____ in globals().copy().items():
if id(o) == id(____):
name = ___
return dict(__error__=str(e), __type__=str(type(o)), __name__=name)
except Exception as e: # noqa
return 'Object is not JSON serializable'
class BattleSide:
@ -621,26 +637,36 @@ class BattleDivision:
battle_zone_id: int
def_medal: Dict[str, int]
inv_medal: Dict[str, int]
terrain: int
div: int
@property
def is_air(self):
return self.div == 11
@property
def div_end(self) -> bool:
return utils.now() >= self.end
def __init__(self, **kwargs):
def __init__(self, div_id: int, end: datetime.datetime, epic: bool, div: int, wall_for: int, wall_dom: float,
terrain_id: int = 0):
"""Battle division helper class
:param kwargs: must contain keys:
div_id: int, end: datetime.datetime, epic: bool, inv_pts: int, def_pts: int,
wall_for: int, wall_dom: float, def_medal: Tuple[int, int], inv_medal: Tuple[int, int]
:type div_id: int
:type end: datetime.datetime
:type epic: bool
:type div: int
:type terrain_id: int
:type wall_for: int
:type wall_dom: float
"""
self.battle_zone_id = kwargs.get("div_id", 0)
self.end = kwargs.get("end", 0)
self.epic = kwargs.get("epic", 0)
self.dom_pts = dict({"inv": kwargs.get("inv_pts", 0), "def": kwargs.get("def_pts", 0)})
self.wall = dict({"for": kwargs.get("wall_for", 0), "dom": kwargs.get("wall_dom", 0)})
self.def_medal = {"id": kwargs.get("def_medal", 0)[0], "dmg": kwargs.get("def_medal", 0)[1]}
self.inv_medal = {"id": kwargs.get("inv_medal", 0)[0], "dmg": kwargs.get("inv_medal", 0)[1]}
self.battle_zone_id = div_id
self.end = end
self.epic = epic
self.wall = dict({"for": wall_for, "dom": wall_dom})
self.terrain = terrain_id
self.div = div
@property
def id(self):
@ -659,9 +685,19 @@ class Battle:
div: Dict[int, BattleDivision]
@property
def is_air(self) -> bool:
def has_air(self) -> bool:
for div in self.div.values():
if div.div == 11:
return True
return not bool(self.zone_id % 4)
@property
def has_ground(self) -> bool:
for div in self.div.values():
if div.div != 11:
return True
return bool(self.zone_id % 4)
def __init__(self, battle: Dict[str, Any]):
"""Object representing eRepublik battle.
@ -688,7 +724,7 @@ class Battle:
[row.get('id') for row in battle.get('def', {}).get('ally_list') if row['deployed']]
)
self.div = defaultdict(BattleDivision)
self.div = {}
for div, data in battle.get('div', {}).items():
div = int(data.get('div'))
if data.get('end'):
@ -696,18 +732,11 @@ class Battle:
else:
end = utils.localize_dt(datetime.datetime.max - datetime.timedelta(days=1))
if not data['stats']['def']:
def_medal = (0, 0)
else:
def_medal = (data['stats']['def']['citizenId'], data['stats']['def']['damage'])
if not data['stats']['inv']:
inv_medal = (0, 0)
else:
inv_medal = (data['stats']['inv']['citizenId'], data['stats']['inv']['damage'])
battle_div = BattleDivision(end=end, epic=data.get('epic_type') in [1, 5], div_id=data.get('id'),
inv_pts=data.get('dom_pts').get("inv"), def_pts=data.get('dom_pts').get("def"),
wall_for=data.get('wall').get("for"), wall_dom=data.get('wall').get("dom"),
def_medal=def_medal, inv_medal=inv_medal)
battle_div = BattleDivision(div_id=data.get('id'), div=data.get('dev'), end=end,
epic=data.get('epic_type') in [1, 5],
wall_for=data.get('wall').get("for"),
wall_dom=data.get('wall').get("dom"),
terrain_id=data.get('terrain', 0))
self.div.update({div: battle_div})

View File

@ -9,7 +9,7 @@ import traceback
import unicodedata
from decimal import Decimal
from pathlib import Path
from typing import Any, List, Mapping, Optional, Union, Dict
from typing import Any, Dict, List, Mapping, Optional, Union
import pytz
import requests
@ -323,7 +323,8 @@ def send_email(name: str, content: List[Any], player=None, local_vars: Mapping[A
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, cls=MyJSONEncoder, sort_keys=True), "application/json")))
files.append(('file', ("local_vars.json", json.dumps(local_vars, cls=MyJSONEncoder, sort_keys=True),
"application/json")))
if isinstance(player, Citizen):
files.append(('file', ("instance.json", player.to_json(indent=True), "application/json")))
requests.post('https://pasts.72.lv', data=data, files=files)

View File

@ -8,6 +8,7 @@ pip==20.1.1
PyInstaller==3.6
pytz==2020.1
requests==2.23.0
responses==0.10.15
setuptools==47.1.1
Sphinx==3.1.1
tox==3.15.2

View File

@ -1,10 +1,9 @@
[bumpversion]
current_version = 0.20.1
current_version = 0.20.2
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
serialize =
{major}.{minor}.{patch}.{dev}
serialize = {major}.{minor}.{patch}.{dev}
{major}.{minor}.{patch}
[bumpversion:file:setup.py]
@ -19,8 +18,24 @@ replace = __version__ = '{new_version}'
universal = 1
[flake8]
exclude = docs
exclude = docs,.tox,.git,log,debug,venv
max-line-length = 120
ignore = E722 F401
ignore = D100,D101,D102,D103
[aliases]
[pycodestyle]
max-line-length = 120
exclude = .tox,.git,log,debug,venv, build
[mypy]
python_version = 3.7
check_untyped_defs = True
ignore_missing_imports = False
warn_unused_ignores = True
warn_redundant_casts = True
warn_unused_configs = True
plugins = mypy_django_plugin.main
[isort]
multi_line_output = 2
line_length = 120
not_skip = __init__.py

View File

@ -3,7 +3,7 @@
"""The setup script."""
from setuptools import setup, find_packages
from setuptools import find_packages, setup
with open('README.rst') as readme_file:
readme = readme_file.read()
@ -43,6 +43,6 @@ setup(
test_suite='tests',
tests_require=test_requirements,
url='https://github.com/eeriks/erepublik/',
version='0.20.1',
version='0.20.2',
zip_safe=False,
)

View File

@ -3,10 +3,10 @@
"""Tests for `erepublik` package."""
import unittest
from erepublik import Citizen
import unittest
class TestErepublik(unittest.TestCase):
"""Tests for `erepublik` package."""