Compare commits

...

74 Commits

Author SHA1 Message Date
9750ee1926 Bump version: 0.30.0.3 → 0.30.0.4 2022-03-21 16:41:08 +02:00
7015b6d299 CookieFix 2022-03-21 16:40:46 +02:00
288c3df4a5 Bump version: 0.30.0.2 → 0.30.0.3 2022-03-21 15:22:45 +02:00
15eb3ac8a0 remove requests 2022-03-21 15:21:28 +02:00
c4933de237 import 2022-03-21 15:13:52 +02:00
bd864a110d Bump version: 0.30.0.1 → 0.30.0.2 2022-03-21 15:10:07 +02:00
ee422c4b02 . 2022-03-21 15:10:04 +02:00
4af6101665 Bump version: 0.30.0 → 0.30.0.1 2022-03-21 15:06:29 +02:00
61a535dca5 requirements 2022-03-21 15:05:50 +02:00
6c0871cfbd Bump version: 0.29.2.3 → 0.30.0 2022-03-21 14:57:17 +02:00
c9dbf7ee1e Enable HTTP2 to overcome CloudFlare captchas 2022-03-21 14:55:07 +02:00
c86a5ffc5a Added Readme swag icons 2022-03-15 10:44:15 +02:00
72b12a5ca2 Lint 2022-03-15 10:36:39 +02:00
1831ddfb49 Bump version: 0.29.2.2 → 0.29.2.3 2022-03-11 12:01:40 +02:00
f9d2ad85c2 bugfix 2022-03-11 12:01:30 +02:00
66a05bb202 Bump version: 0.29.2.1 → 0.29.2.2 2022-03-11 11:44:54 +02:00
273e98c8d1 Update 2022-03-11 11:44:29 +02:00
03c435bf7e Bump version: 0.29.2 → 0.29.2.1 2022-03-06 21:22:36 +02:00
112150cce6 bugfix
version update
2022-03-06 21:22:26 +02:00
eb9231b0e7 Bump version: 0.29.1 → 0.29.2 2022-03-06 16:36:49 +02:00
763fbaaf9c Update wam logic 2022-03-06 16:36:35 +02:00
e9f3441678 style setting update 2022-03-06 16:35:49 +02:00
f59b47d131 Bump version: 0.29.0.6 → 0.29.1 2022-02-04 00:41:40 +02:00
6ca6d416b3 Merge branch 'master' of github.com:eeriks/erepublik 2022-02-04 00:41:20 +02:00
903d596064 Fixup 2022-02-04 00:41:13 +02:00
8a2a1ab393 autosell bugfix 2022-02-04 00:34:25 +02:00
3ee9811b8f Bump version: 0.29.0.5 → 0.29.0.6 2022-02-01 23:01:51 +02:00
f9b136faba Bugfix 2022-02-01 23:01:44 +02:00
1fb186961f Bump version: 0.29.0.4 → 0.29.0.5 2022-02-01 22:57:18 +02:00
2c1cf2fc66 Bugfix 2022-02-01 22:57:07 +02:00
75996779e1 Bump version: 0.28.3 → 0.29.0 2022-02-01 22:21:44 +02:00
d5ce52162a Updated WAM logic for cases when there are too many RAW factories 2022-02-01 22:21:29 +02:00
7e5312df18 Bump version: 0.28.2 → 0.28.3 2022-01-26 14:03:21 +02:00
8474920b72 Bugfix requests 2.27.0 contains bug which prohibits using auth with socks proxy 2022-01-26 14:03:11 +02:00
3426f60749 Bump version: 0.28.1 → 0.28.2 2022-01-26 13:10:23 +02:00
c703201ae7 Bugfix 2022-01-26 13:10:07 +02:00
0119ebe44b Bump version: 0.28.0.4 → 0.28.1 2022-01-06 10:11:50 +02:00
247d87ac5e Update UA browser versions; Remove Android as system platform
lint
2022-01-06 10:11:35 +02:00
e978c04228 Initialize headers after login 2021-11-26 09:53:00 +02:00
e925ab455d RTD yaml 2021-11-24 17:02:19 +02:00
1447b21676 Small fixes 2021-11-24 16:54:36 +02:00
575bb60f05 GitHub worklflow update 2021-11-24 14:36:15 +02:00
81c1469752 Bump version: 0.28.0.3 → 0.28.0.4 2021-11-19 08:50:31 +02:00
490809045d Merge branch 'master' of github.com:eeriks/erepublik 2021-11-19 08:49:20 +02:00
f425faea5b Bump version: 0.28.0.2 → 0.28.0.3 2021-11-19 08:41:44 +02:00
b7fe834091 Bump version: 0.28.0.1 → 0.28.0.2 2021-11-19 08:39:45 +02:00
bc09133f61 Fight removal bugfix 2021-11-19 08:39:30 +02:00
8b264e2b9c Bump version: 0.28.0.1 → 0.28.0.2 2021-11-17 00:31:08 +02:00
711a61783c Bugfix 2021-11-17 00:30:48 +02:00
c9942cd565 Bump version: 0.28.0 → 0.28.0.1 2021-11-16 23:24:52 +02:00
0f6529262e Moved Fight code to backup file 2021-11-16 23:24:36 +02:00
8243664c24 Bump version: 0.27.2 → 0.28.0 2021-11-16 22:47:32 +02:00
c8834e0f9e Fixed medal amounts, removed military related stuff 2021-11-16 22:47:02 +02:00
954e9d576b Bump version: 0.27.1 → 0.27.2 2021-11-02 18:44:50 +02:00
47ec6c03df Updated requirements 2021-11-02 18:44:40 +02:00
44360bd143 erep.lv url update 2021-11-02 18:32:13 +02:00
3e0caae041 Doc update 2021-10-23 16:55:26 +03:00
5ce4c62f22 Bump version: 0.27.0 → 0.27.1 2021-10-23 16:32:22 +03:00
b80fa43e99 Updates 2021-10-23 16:30:04 +03:00
62f53c0396 Short Maintenance message 2021-10-23 16:19:00 +03:00
051d4765a4 Correct sleeping if no energy 2021-10-23 16:19:00 +03:00
365ad9a719 Send state update and let the bg task live even if Captcha is required 2021-10-23 16:19:00 +03:00
81f00bdbf6 Create codeql-analysis.yml 2021-10-23 16:16:40 +03:00
44d221ac1b Merge branch 'master' of github.com:eeriks/erepublik 2021-10-23 16:16:17 +03:00
7054ae2b05 Bump version: 0.26.0.2 → 0.27.0 2021-08-30 16:36:35 +03:00
2288fe01ea Fixed promo reporting 2021-08-30 16:36:25 +03:00
dfa6f7e0be Changed linting make command 2021-08-30 16:36:05 +03:00
1746f27193 Bump version: 0.26.0.1 → 0.26.0.2 2021-08-28 10:41:47 +03:00
c8f9ac9768 PromoFixup 2021-08-28 10:41:44 +03:00
7773b63520 fix
fix
2021-08-27 19:35:15 +03:00
211df41969 Bump version: 0.26.0 → 0.26.0.1 2021-08-27 19:31:20 +03:00
421b6fa919 Licence changes 2021-08-27 19:29:52 +03:00
e4814cfa8e updates 2021-08-18 10:40:19 +03:00
ba336fe8ed Zero medal reward update 2021-08-18 09:28:00 +03:00
30 changed files with 1826 additions and 1435 deletions

View File

@ -21,7 +21,7 @@ insert_final_newline = false
indent_style = tab
[*.py]
max_line_length = 240
max_line_length = 120
line_length=120
multi_line_output=0
balanced_wrapping=True

5
.flake8 Normal file
View File

@ -0,0 +1,5 @@
[flake8]
ignore = E203,E722
exclude = .git,__pycache__,venv,docs,debug,log
max-line-length = 120
#max-complexity = 10

71
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,71 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '16 0 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8]
python-version: [3.8, 3.9]
steps:
- uses: actions/checkout@v2
@ -19,7 +19,15 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements_dev.txt
pip install -r requirements-dev.txt
- name: Lint with isort
run: |
pip install -U isort
isort --check erepublik
- name: Lint with Black
run: |
pip install -U black
black --check erepublik
- name: Lint with flake8
run: |
pip install flake8
@ -27,7 +35,7 @@ jobs:
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pip install pytest
pytest
# - name: Test with pytest
# run: |
# pip install pytest
# pytest

View File

@ -1,13 +0,0 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
default_language_version:
python: python3.8

15
.readthedocs.yaml Normal file
View File

@ -0,0 +1,15 @@
version: 2
build:
os: "ubuntu-20.04"
tools:
python: "3.9"
# Build from the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# Explicitly set the version of Python and its requirements
python:
install:
- requirements: requirements-dev.txt

View File

@ -58,10 +58,10 @@ Ready to contribute? Here's how to set up `erepublik` for local development.
$ git clone git@github.com:your_name_here/erepublik.git
3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development::
3. This is how you set up your fork for local development::
$ mkvirtualenv erepublik
$ cd erepublik/
$ python3 -m venv venv
$ python setup.py develop
4. Create a branch for local development::
@ -73,8 +73,7 @@ Ready to contribute? Here's how to set up `erepublik` for local development.
5. When you're done making changes, check that your changes pass flake8, isort and the
tests::
$ flake8 erepublik tests
$ isort erepublik
$ make lint
$ python setup.py test
To get flake8 and isort, just pip install them into your virtualenv.
@ -115,8 +114,6 @@ A reminder for the maintainers on how to deploy.
Make sure all your changes are committed (including an entry in HISTORY.rst).
Then run::
$ bumpversion patch # possible: major / minor / patch
$ bumpversion patch # possible: major / minor / patch / dev
$ git push
$ git push --tags
Travis will then deploy to PyPI if tests pass.

View File

@ -52,8 +52,9 @@ clean-test: ## remove test and coverage artifacts
rm -fr .pytest_cache
lint: ## check style with flake8
black erepublik tests
flake8 erepublik tests
isort erepublik examples tests
black erepublik examples tests
flake8 erepublik examples tests
test: ## run tests quickly with the default Python
python -m unittest

View File

@ -10,16 +10,20 @@ eRepublik script
:target: https://erepublik.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. image:: https://api.codacy.com/project/badge/Grade/eaa7ae43d23f4c0abab65c3bde89475a
:target: https://app.codacy.com/manual/eeriks/erepublik?utm_source=github.com&utm_medium=referral&utm_content=eeriks/erepublik&utm_campaign=Badge_Grade_Dashboard
:alt: Codacy Badge
.. image:: https://github.com/eeriks/erepublik/actions/workflows/codeql-analysis.yml/badge.svg?branch=master
:target: //github.com/eeriks/erepublik/actions/workflows/codeql-analysis.yml
:alt: CodeQL
.. image:: https://github.com/eeriks/erepublik/actions/workflows/pythonpackage.yml/badge.svg
:target: https://github.com/eeriks/erepublik/actions/workflows/pythonpackage.yml
:alt: Python package
Python package for automated eRepublik playing
* Free software: MIT license
* Free software: GPLv3.0
* Documentation: https://erepublik.readthedocs.io/en/latest/
* Source code: https://github.com/eeriks/erepublik
Features

View File

@ -20,10 +20,14 @@
#
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, os.path.abspath(".."))
import datetime
import edx_theme
import erepublik
import edx_theme
# -- General configuration ---------------------------------------------
@ -33,24 +37,24 @@ import edx_theme
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'edx_theme']
extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode", "edx_theme"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'eRepublik script'
copyright = u"2019, Eriks Karls"
author = u"Eriks Karls"
project = "eRepublik script"
copyright = "2017-%i, Eriks Karls" % datetime.date.today().year
author = "Eriks Karls"
# The version info for the project you're documenting, acts as replacement
# for |version| and |release|, also used in various other places throughout
@ -71,10 +75,10 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
@ -85,7 +89,7 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'edx_theme'
html_theme = "edx_theme"
html_theme_path = [edx_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a
@ -97,13 +101,13 @@ html_theme_path = [edx_theme.get_html_theme_path()]
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# html_static_path = ["_static"]
# -- Options for HTMLHelp output ---------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'erepublikdoc'
htmlhelp_basename = "erepublikdoc"
# -- Options for LaTeX output ------------------------------------------
@ -112,15 +116,12 @@ latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
@ -130,9 +131,7 @@ latex_elements = {
# (source start file, target name, title, author, documentclass
# [howto, manual, or own class]).
latex_documents = [
(master_doc, 'erepublik.tex',
u'eRepublik script Documentation',
u'Eriks Karls', 'manual'),
(master_doc, "erepublik.tex", "eRepublik script Documentation", "Eriks Karls", "manual"),
]
@ -140,11 +139,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'erepublik',
u'eRepublik script Documentation',
[author], 1)
]
man_pages = [(master_doc, "erepublik", "eRepublik script Documentation", [author], 1)]
# -- Options for Texinfo output ----------------------------------------
@ -153,10 +148,13 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'erepublik',
u'eRepublik script Documentation',
author,
'erepublik',
'One line description of project.',
'Miscellaneous'),
(
master_doc,
"erepublik",
"eRepublik script Documentation",
author,
"erepublik",
"One line description of project.",
"Miscellaneous",
),
]

View File

@ -44,14 +44,6 @@ erepublik.utils module
:undoc-members:
:show-inheritance:
erepublik.ws module
-------------------
.. automodule:: erepublik.ws
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -8,12 +8,10 @@
<meta name="generator" content="Jekyll v4.0.1">
<title>eBot configuration</title>
<!-- CSS only -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" 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.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
@ -120,57 +118,57 @@
</div>
<div class="col-12 col-sm-6">
<h3>Fighting</h3>
<h3><span style="text-decoration: line-through;">Fighting</span></h3>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="fight" checked>
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="fight" disabled>
<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>
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="air" disabled>
<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">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="ground" disabled>
<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">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="boosters" disabled>
<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">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="continuous_fighting" disabled>
<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>
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="next_energy" disabled>
<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">
<input type="radio" class="form-check-input" onchange="updateJson()" id="all_in" name="fight_amount" value="all_in" disabled>
<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>
<input type="radio" class="form-check-input" onchange="updateJson()" id="h_energy" name="fight_amount" value="h_energy" disabled>
<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>
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="rw_def_side" disabled>
<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>
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="travel_to_fight" disabled>
<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">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="epic_hunt" disabled>
<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">
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="epic_hunt_ebs" disabled>
<label class="custom-control-label" for="epic_hunt_ebs">Spend <small>[all]</small> EBs in epics</label>
</div>
</div>
@ -262,7 +260,13 @@
</form>
</div>
<div class="col-12">
<pre id="json-output"></pre>
<div class="card">
<div class="card-body">
<h5 class="card-title">config.json</h5>
<h6 class="card-subtitle mb-2 text-muted">Copy-paste the content below into 'config.json' file</h6>
<pre id="json-output" class="bg-light card-text"></pre>
</div>
</div>
</div>
</div>
</div>

859
erepublik/Fighting Normal file
View File

@ -0,0 +1,859 @@
# TODO: Fix fighting and reimplement CitizenMilitary module
# class CitizenMilitary(CitizenTravel):
# all_battles: Dict[int, classes.Battle] = None
# __last_war_update_data = None
#
# active_fs: bool = False
#
# @property
# def as_dict(self):
# d = super().as_dict
# d.update(active_fs=self.active_fs, all_battles=self.all_battles)
# return d
# def update_all():
# super().update_all()
# self.update_war_info()
#
# def update_war_info(self):
# if (
# self.__last_war_update_data
# and self.__last_war_update_data.get("last_updated", 0) + 30 > self.now.timestamp()
# ):
# r_json = self.__last_war_update_data
# else:
# r_json = self._get_military_campaigns_json_list().json()
# if r_json.get("countries"):
# if self.all_battles is None:
# self.all_battles = {}
# self.__last_war_update_data = r_json
# if r_json.get("battles"):
# all_battles = {}
# for battle_data in r_json.get("battles", {}).values():
# all_battles[battle_data.get("id")] = classes.Battle(battle_data)
# # old_all_battles = self.all_battles
# self.all_battles = all_battles
# # for battle in old_all_battles.values():
# # utils._clear_up_battle_memory(battle)
#
# def get_battle_for_war(self, war_id: int) -> Optional[classes.Battle]:
# self.update_war_info()
# war_info = self.get_war_status(war_id)
# return self.all_battles.get(war_info.get("battle_id"), None)
#
# def get_war_status(self, war_id: int) -> Dict[str, Union[bool, Dict[int, str]]]:
# r = self._get_wars_show(war_id)
# html = r.text
# ret = {}
# reg_re = re.compile(fr'data-war-id="{war_id}" data-region-id="(\d+)" data-region-name="([- \w]+)"')
# if reg_re.findall(html):
# ret.update(regions={}, can_attack=True)
# for reg in reg_re.findall(html):
# ret["regions"].update({int(reg[0]): reg[1]})
# elif re.search(
# r'<a href="//www.erepublik.com/en/military/battlefield/(\d+)" '
# r'class="join" title="Join"><span>Join</span></a>',
# html,
# ):
# battle_id = re.search(
# r'<a href="//www.erepublik.com/en/military/battlefield/(\d+)" '
# r'class="join" title="Join"><span>Join</span></a>',
# html,
# ).group(1)
# ret.update(can_attack=False, battle_id=int(battle_id))
# elif re.search(r"This war is no longer active.", html):
# ret.update(can_attack=False, ended=True)
# else:
# ret.update(can_attack=False)
# return ret
#
# def get_available_weapons(self, battle_id: int):
# return self._get_military_show_weapons(battle_id).json()
#
# def set_default_weapon(self, battle: classes.Battle, division: classes.BattleDivision) -> int:
# 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 division.is_air:
# for weapon in available_weapons:
# try:
# if weapon["weaponQuantity"] > 30 and weapon["weaponInfluence"] > weapon_damage:
# weapon_quality = int(weapon["weaponId"])
# weapon_damage = weapon["weaponInfluence"]
# except ValueError:
# pass
# return self.change_weapon(battle, weapon_quality, division)
#
# def change_weapon(self, battle: classes.Battle, quality: int, battle_zone: classes.BattleDivision) -> int:
# r = self._post_military_change_weapon(battle.id, battle_zone.id, quality)
# influence = r.json().get("weaponInfluence")
# self._report_action(
# "MILITARY_WEAPON", f"Switched to q{quality} weapon," f" new influence {influence}", kwargs=r.json()
# )
# return influence
#
# def sorted_battles(self, sort_by_time: bool = True, only_tp=False) -> List[classes.Battle]:
# cs_battles_priority_air: List[classes.Battle] = []
# cs_battles_priority_ground: List[classes.Battle] = []
# cs_battles_air: List[classes.Battle] = []
# cs_battles_ground: List[classes.Battle] = []
# deployed_battles_air: List[classes.Battle] = []
# deployed_battles_ground: List[classes.Battle] = []
# ally_battles_air: List[classes.Battle] = []
# ally_battles_ground: List[classes.Battle] = []
# other_battles_air: List[classes.Battle] = []
# other_battles_ground: List[classes.Battle] = []
#
# ret_battles: List[classes.Battle] = []
# if sort_by_time:
# battle_list = sorted(self.all_battles.values(), key=lambda b: b.start)
# battle_list.reverse()
# else:
# battle_list = sorted(self.all_battles.values(), key=lambda b: b.id)
#
# contribution_json = self._get_military_campaigns_json_citizen().json()
# contributions: List[Dict[str, int]] = contribution_json.get("contributions") or []
# contributions.sort(key=lambda b: -b.get("damage"))
#
# for contribution_battle in contributions:
# if contribution_battle.get("battle_id") and contribution_battle.get("battle_id") in self.all_battles:
# ret_battles.append(self.all_battles[contribution_battle.get("battle_id")])
#
# for battle in battle_list:
# battle_sides = [battle.invader.country, battle.defender.country]
# if battle.id in ret_battles:
# continue
# # CS Battles
# elif self.details.citizenship in battle_sides:
# if battle.has_air:
# if battle.defender.id == self.details.citizenship:
# cs_battles_priority_air.append(battle)
# else:
# cs_battles_air.append(battle)
# else:
# if battle.defender.id == self.details.citizenship:
# cs_battles_priority_ground.append(battle)
# else:
# cs_battles_ground.append(battle)
#
# # Current location battles:
# elif self.details.current_country in battle_sides:
# if battle.has_air:
# deployed_battles_air.append(battle)
# else:
# deployed_battles_ground.append(battle)
#
# # 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.has_air:
# deployed_battles_air.append(battle)
# else:
# deployed_battles_ground.append(battle)
# # Allied battles:
# else:
# if battle.has_air:
# ally_battles_air.append(battle)
# else:
# ally_battles_ground.append(battle)
# else:
# if battle.has_air:
# other_battles_air.append(battle)
# else:
# other_battles_ground.append(battle)
#
# cs_battles = cs_battles_priority_air + cs_battles_priority_ground + cs_battles_air + cs_battles_ground
# if only_tp:
# return cs_battles
# deployed_battles = deployed_battles_air + deployed_battles_ground
# other_battles = ally_battles_air + ally_battles_ground + other_battles_air + other_battles_ground
# ret_battles = ret_battles + cs_battles + deployed_battles + other_battles
# return ret_battles
#
# def get_cheap_tp_divisions(self) -> Dict[str, List[Tuple[int, classes.BattleDivision]]]:
# air_divs: List[Tuple[int, classes.BattleDivision]] = []
# ground_divs: List[Tuple[int, classes.BattleDivision]] = []
# check_maverick = self.maverick and self.config.maverick
# for battle in reversed(self.sorted_battles(True, True)):
# for division in battle.div.values():
# is_start_ok = utils.good_timedelta(division.battle.start, timedelta(minutes=-1)) < self.now
# if not division.terrain and is_start_ok and not division.div_end:
# if division.is_air and self.config.air:
# division_medals = self.get_battle_round_data(division)
# medal = division_medals[self.details.citizenship == division.battle.defender.country]
# if not medal:
# air_divs.append((0, division))
# else:
# air_divs.append((medal.get("1").get("raw_value"), division))
# elif not division.is_air and self.config.ground:
# if not division.div == self.division and not check_maverick:
# continue
# division_medals = self.get_battle_round_data(division)
# medal = division_medals[self.details.citizenship == division.battle.defender.country]
# if not medal:
# ground_divs.append((0, division))
# else:
# ground_divs.append((medal.get("1").get("raw_value"), division))
#
# air_divs.sort(key=lambda z: (z[0], z[1].battle.start))
# ground_divs.sort(key=lambda z: (z[0], z[1].battle.start))
# return {"air": air_divs, "ground": ground_divs}
#
# @property
# def has_battle_contribution(self):
# return bool(self.__last_war_update_data.get("citizen_contribution", []))
#
# def find_battle_to_fight(
# self, silent: bool = False
# ) -> Tuple[classes.Battle, classes.BattleDivision, classes.BattleSide]:
# self.update_war_info()
# for battle in self.sorted_battles(self.config.sort_battles_time):
# if not isinstance(battle, classes.Battle):
# continue
# if battle.is_dict_lib:
# continue
# battle_zone: Optional[classes.BattleDivision] = None
# for div in battle.div.values():
# if div.terrain == 0:
# if div.div_end:
# continue
# maverick_ok = self.maverick and self.config.maverick
# if self.config.air and div.is_air:
# battle_zone = div
# break
# elif self.config.ground and not div.is_air and (div.div == self.division or maverick_ok):
# battle_zone = div
# break
# else:
# continue
# if not battle_zone:
# continue
# allies = (
# battle.invader.deployed + battle.defender.deployed + [battle.invader.country, battle.defender.country]
# )
#
# travel_needed = self.details.current_country not in allies
#
# if battle.is_rw:
# side = battle.defender if self.config.rw_def_side else battle.invader
# else:
# defender_side = self.details.current_country in battle.defender.allies + [
# battle.defender.country,
# ]
# side = battle.defender if defender_side else battle.invader
#
# if not silent:
# self.write_log(str(battle))
#
# travel = (
# (self.config.travel_to_fight and self.should_travel_to_fight() or self.config.force_travel)
# if travel_needed
# else True
# )
#
# if not travel:
# continue
# yield battle, battle_zone, side
#
# def find_battle_and_fight(self):
# count = self.should_fight()[0]
# if count:
# self.write_log("Checking for battles to fight in...")
# for battle, division, side in self.find_battle_to_fight():
#
# allies = (
# battle.invader.deployed
# + battle.defender.deployed
# + [battle.invader.country, battle.defender.country]
# )
#
# travel_needed = self.details.current_country not in allies
#
# if battle.start > self.now:
# self.sleep(utils.get_sleep_seconds(battle.start))
#
# if travel_needed and not self.change_division(battle, division, side):
# break
#
# if self.change_division(battle, division):
# self.set_default_weapon(battle, division)
# self.fight(battle, division, side, count)
# self.travel_to_residence()
# break
#
# def fight(
# self,
# battle: classes.Battle,
# division: classes.BattleDivision,
# side: classes.BattleSide = None,
# count: int = None,
# ) -> Optional[int]:
# """Fight in a battle.
#
# Will auto activate booster and travel if allowed to do it.
# :param battle: Battle battle to fight in
# :type battle: Battle
# :param division: Division number to fight in available choices
# :type division: BattleDivision
# :param side: BattleSide or None. Battle side to fight in, If side not == invader id or not in invader deployed
# allies list, then defender's side is chosen
# :type side: BattleSide
# :param count: How many hits to do, if not specified self.should_fight() is called.
# :type count: int
# :param use_ebs: Should use energy bars if count > 0 and not enough food_fights
# :type use_ebs: bool
# :return: None if no errors while fighting, otherwise error count.
# :rtype: int
# """
# if self.restricted_ip:
# self.write_warning("Fighting is not allowed from restricted IP!")
# self._report_action("IP_BLACKLISTED", "Fighting is not allowed from restricted IP!")
# return 1
# if not division.is_air and self.config.boosters:
# self.activate_damage_booster(not division.is_air)
# if side is None:
# side = (
# battle.defender
# if self.details.citizenship in battle.defender.allies + [battle.defender.country]
# else battle.invader
# )
# if count is None:
# count = self.should_fight()[0]
#
# self.write_log(f"Fighting in battle for {battle.region_name} on {side} side in d{division.div}")
#
# if self.now < utils.localize_dt(datetime(2021, 2, 8)):
# error_count = total_damage = total_hits = 0
# ok_to_fight = True
# while ok_to_fight and error_count < 10 and count > 0:
# while all((count > 0, error_count < 10, self.energy.energy >= 50)):
# hits, error, damage = self._shoot(battle, division, side)
# count -= hits
# total_hits += hits
# total_damage += damage
# error_count += error
# else:
# if self.energy.energy < 50 or error_count >= 10 or count <= 0:
# self.write_log(f"Hits: {total_hits:>4} | Damage: {total_damage}")
# ok_to_fight = False
# if total_damage:
# self.report_fighting(battle, not side.is_defender, division, total_damage, total_hits)
# return error_count
# else:
# deployment_id = self.deploy(division, side, count * 10)
# self.sleep(count // 3) # TODO: connect to eRepublik's WS and get from there when deploy ends
# energy_used = 0
# if deployment_id:
# self.write_warning(
# "If eRepublik responds with HTTP 500 Internal Error, "
# "it is kind of ok, because deployment has not finished yet."
# )
# deployment_data = self._post_military_fight_deploy_deploy_report_data(deployment_id).json()
# if not deployment_data.get("error"):
# data = deployment_data["data"]
# total_damage = int(data["damage"].replace(",", ""))
# energy_used = int(data["energySpent"].replace(",", ""))
# self.details.pp += int(data["rewards"]["prestigePoints"].replace(",", ""))
# self.report_fighting(battle, not side.is_defender, division, total_damage, energy_used // 10)
# return energy_used
#
# def _shoot(self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide):
# if division.is_air:
# response = self._post_military_fight_air(battle.id, side.id, division.id)
# else:
# response = self._post_military_fight_ground(battle.id, side.id, division.id)
#
# if "Zone is not meant for " in response.text:
# self.sleep(5)
# return 0, 1, 0
# try:
# r_json = response.json()
# except (ValueError, HTTPError, RequestException):
# return 0, 10, 0
# hits = 0
# damage = 0
# err = False
# if r_json.get("error"):
# if r_json.get("message") == "SHOOT_LOCKOUT":
# pass
# elif r_json.get("message") == "NOT_ENOUGH_WEAPONS":
# self.set_default_weapon(battle, division)
# elif r_json.get("message") == "Cannot activate a zone with a non-native division":
# self.write_warning("Wrong division!!")
# return 0, 10, 0
# elif r_json.get("message") == "ZONE_INACTIVE":
# self.write_warning("Wrong division!!")
# return 0, 10, 0
# elif r_json.get("message") == "NON_BELLIGERENT":
# self.write_warning("Dictatorship/Liberation wars are not supported!")
# return 0, 10, 0
# elif r_json.get("message") in ["FIGHT_DISABLED", "DEPLOYMENT_MODE"]:
# self._post_main_profile_update(
# "options", params='{"optionName":"enable_web_deploy","optionValue":"off"}'
# )
# self.set_default_weapon(battle, division)
# else:
# if r_json.get("message") == "UNKNOWN_SIDE":
# self._rw_choose_side(battle, side)
# elif r_json.get("message") == "CHANGE_LOCATION":
# countries = [side.country] + side.deployed
# self.travel_to_battle(battle, countries)
# err = True
# elif r_json.get("message") == "ENEMY_KILLED":
# # Non-InfantryKit players
# if r_json["user"]["earnedXp"]:
# hits = r_json["user"]["earnedXp"]
# # InfantryKit player
# # The almost always safe way (breaks on levelup hit)
# elif self.energy.energy >= r_json["details"]["wellness"]: # Haven't reached levelup
# hits = (self.energy.energy - r_json["details"]["wellness"]) // 10
# else:
# hits = r_json["hits"]
# if r_json["user"]["epicBattle"]:
# hits /= 1 + r_json["user"]["epicBattle"]
#
# self.energy.energy = r_json["details"]["wellness"]
# self.details.xp = int(r_json["details"]["points"])
# damage = r_json["user"]["givenDamage"] * (1.1 if r_json["oldEnemy"]["isNatural"] else 1)
# else:
# err = True
#
# return hits, err, damage
#
# def deploy_bomb(
# self, battle: classes.Battle, division: classes.BattleDivision, bomb_id: int, inv_side: bool, count: int = 1
# ) -> Optional[int]:
# """Deploy bombs in a battle for given side.
#
# :param battle: Battle
# :type battle: classes.Battle
# :param division: BattleDivision
# :type division: classes.BattleDivision
# :param bomb_id: int bomb id
# :type bomb_id: int
# :param inv_side: should deploy on invader side
# :type inv_side: bool
# :param count: how many bombs to deploy
# :type count: int
# :return: Deployed count
# :rtype: int
# """
#
# if not isinstance(count, int) or count < 1:
# count = 1
# has_traveled = False
# if battle.is_rw:
# has_traveled = self.travel_to_battle(battle, [battle.defender.country])
# self._rw_choose_side(battle, battle.invader if inv_side else battle.defender)
# if inv_side:
# good_countries = [battle.invader.country] + battle.invader.deployed
# if self.details.current_country not in good_countries:
# has_traveled = self.travel_to_battle(battle, good_countries)
# else:
# involved = (
# [battle.invader.country, battle.defender.country] + battle.invader.deployed + battle.defender.deployed
# )
# if self.details.current_country not in involved:
# count = 0
# side = battle.invader if inv_side else battle.defender
# errors = deployed_count = 0
# while (not deployed_count == count) and errors < 10:
# r = self._post_military_deploy_bomb(battle.id, division.id, side.id, bomb_id).json()
# if not r.get("error"):
# deployed_count += 1
# self.sleep(0.5)
# elif r.get("message") == "LOCKED":
# self.sleep(0.5)
# elif r.get("message") == "INVALID_BOMB":
# errors = 10
# else:
# errors += 1
#
# if has_traveled:
# self.travel_to_residence()
#
# self._report_action("MILITARY_BOMB", f"Deployed {deployed_count} bombs in battle {battle.id}")
# return deployed_count
#
# def change_division(
# self, battle: classes.Battle, division: classes.BattleDivision, side: classes.BattleSide = None
# ) -> bool:
# """Change division.
#
# :param battle: classes.Battle
# :type battle: classes.Battle
# :param division: int target division to switch to
# :type division: classes.BattleDivision
# :param side: Side to choose
# :type side: classes.BattleSide
# :return:
# """
# resp = self._post_main_battlefield_change_division(battle.id, division.id, side.id if side else None)
# if resp.json().get("error"):
# self.write_warning(resp.json().get("message"))
# return False
# self._report_action(
# "MILITARY_DIV_SWITCH", f"Switched to d{division.div} in battle {battle.id}", kwargs=resp.json()
# )
# return True
#
# 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,
# ) -> Decimal:
# if not rang or not strength or elite is None:
# r = self._get_main_citizen_profile_json(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
#
# return utils.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, weapon: bool = False
# ) -> Decimal:
# if not rang or elite is None:
# r = self._get_main_citizen_profile_json(self.details.citizen_id).json()
# if not rang:
# rang = r["military"]["militaryData"]["aircraft"]["rankNumber"]
# if elite is None:
# elite = r["citizenAttributes"]["level"] > 100
#
# return utils.calculate_hit(0, rang, True, elite, ne, 0, 20 if weapon else 0)
#
# def activate_damage_booster(self, ground: bool = True) -> int:
# kind = "damage" if ground else "aircraftDamage"
# if self.config.boosters and not self.get_active_damage_booster(ground):
# booster: Optional[types.InvFinalItem] = None
# for quality, data in sorted(self.inventory.boosters.get(kind, {}).items(), key=lambda x: x[0]):
# for _duration, _booster in sorted(data.items(), key=lambda y: y[0]):
# critical_amount = 2 if quality < 10 and ground else 10
# if _booster.get("amount") > critical_amount:
# booster = _booster
# break
# break
# if booster:
# kind = "damage" if ground else "air_damage"
# self._report_action("MILITARY_BOOSTER", f"Activated {booster['name']}")
# resp = self._post_economy_activate_booster(booster["quality"], booster["durability"], kind).json()
# self._update_inventory_data(resp)
# return self.get_active_damage_booster(ground)
#
# def get_active_damage_booster(self, ground: bool = True) -> int:
# kind = "damage" if ground else "aircraftDamage"
# boosters = self.inventory.active.get(kind, {})
# quality = 0
# for q, boost in boosters.items():
# if boost["quality"] * 10 > quality:
# quality = boost["quality"] * 10
# return quality
#
# def get_active_ground_damage_booster(self) -> int:
# return self.get_active_damage_booster(True)
#
# def get_active_air_damage_booster(self) -> int:
# return self.get_active_damage_booster(False)
#
# def activate_battle_effect(self, battle_id: int, kind: str) -> bool:
# self._report_action("MILITARY_BOOSTER", f"Activated {kind} booster")
# resp = self._post_main_activate_battle_effect(battle_id, kind, self.details.citizen_id).json()
# return not resp.get("error")
#
# def activate_pp_booster(self, pp_item: types.InvFinalItem) -> bool:
# self._report_action("MILITARY_BOOSTER", f'Activated {pp_item["name"]}')
# resp = self._post_economy_activate_booster(
# pp_item["quality"], pp_item["durability"], "prestige_points"
# ).json()
# self._update_inventory_data(resp)
# return pp_item.get("kind") in self.inventory.active
#
# def _rw_choose_side(self, battle: classes.Battle, side: classes.BattleSide) -> Response:
# return self._post_main_battlefield_travel(side.id, battle.id)
#
# def should_travel_to_fight(self) -> bool:
# ret = False
# if self.config.always_travel:
# ret = True
# elif self.should_do_levelup: # Do levelup
# ret = True
# # Get to next Energy +1
# elif self.next_reachable_energy and self.config.next_energy:
# ret = True
# # 1h worth of energy
# elif self.energy.energy + self.energy.interval * 3 >= self.energy.limit:
# ret = True
# return ret
#
# def should_fight(self) -> Tuple[int, str, bool]:
# """Checks if citizen should fight at this moment
# :rtype: Tuple[int, str, bool]
# """
# count = 0
# force_fight = False
# msg = "Fighting not allowed!"
# if not self.config.fight:
# return count, msg, force_fight
#
# # Do levelup
# if self.is_levelup_reachable:
# msg = "Level up"
# if self.should_do_levelup:
# count = (self.energy.limit * 3) // 10
# force_fight = True
# else:
# self.write_log("Waiting for fully recovered energy before leveling up.")
#
# # Levelup reachable
# elif self.is_levelup_close:
# count = self.details.xp_till_level_up - (self.energy.limit // 10) + 5
# msg = "Fighting for close Levelup. Doing %i hits" % count
# force_fight = True
#
# elif self.details.pp < 75:
# count = 75 - self.details.pp
# msg = "Obligatory fighting for at least 75pp"
# force_fight = True
#
# elif self.config.continuous_fighting and self.has_battle_contribution:
# count = self.energy.food_fights
# msg = "Continuing to fight in previous battle"
#
# # All-in (type = all-in and full ff)
# elif self.config.all_in and self.energy.energy + self.energy.interval * 3 >= self.energy.limit:
# count = self.energy.food_fights
# msg = "Fighting all-in. Doing %i hits" % count
#
# # Get to next Energy +1
# elif self.config.next_energy and self.next_reachable_energy:
# count = self.next_reachable_energy
# msg = "Fighting for +1 energy. Doing %i hits" % count
#
# # 1h worth of energy
# elif self.energy.energy + self.energy.interval * 3 >= self.energy.limit:
# count = self.energy.interval
# msg = "Fighting for 1h energy. Doing %i hits" % count
# force_fight = True
#
# return (count if count > 0 else 0), msg, force_fight
#
# def get_battle_round_data(self, division: classes.BattleDivision) -> Tuple[Any, Any]:
# battle = division.battle
#
# r = self._post_military_battle_console(
# battle.id,
# "battleStatistics",
# 1,
# zoneId=battle.zone_id,
# round_id=battle.zone_id,
# division=division.div,
# battleZoneId=division.id,
# type="damage",
# )
# r_json = r.json()
# return r_json.get(str(battle.invader.id)).get("fighterData"), r_json.get(str(battle.defender.id)).get(
# "fighterData"
# )
#
# def get_battle_division_stats(self, division: classes.BattleDivision) -> Dict[str, Any]:
# battle = division.battle
# r = self._get_military_battle_stats(battle.id, division.div, division.id)
# return r.json()
#
# def get_division_max_hit(self, division: classes.BattleDivision) -> int:
# """Returns max hit in division for current side (if not on either side returns 0)
#
# :param division: BattleDivision for which to get max hit value
# :type division: classes.BattleDivision
# :return: max hit value
# :rtype: int
# """
# return self.get_battle_division_stats(division).get("maxHit", -1)
#
# def schedule_attack(self, war_id: int, region_id: int, region_name: str, at_time: datetime):
# if at_time:
# self.sleep(utils.get_sleep_seconds(at_time))
# self.get_csrf_token()
# self.launch_attack(war_id, region_id, region_name)
#
# def get_active_wars(self, country: constants.Country = None) -> List[int]:
# r = self._get_country_military(country.link if country else self.details.citizenship.link)
# all_war_ids = re.findall(r'//www\.erepublik\.com/en/wars/show/(\d+)"', r.text)
# return [int(wid) for wid in all_war_ids]
#
# def get_last_battle_of_war_end_time(self, war_id: int) -> datetime:
# r = self._get_wars_show(war_id)
# html = r.text
# 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()
# battle = console.get("list")[0]
# return utils.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):
# self._post_wars_attack_region(war_id, region_id, region_name)
# self._report_action("MILITARY_QUEUE_ATTACK", f"Battle for *{region_name}* queued")
#
# def get_country_mus(self, country: constants.Country) -> Dict[int, str]:
# ret = {}
# r = self._get_main_leaderboards_damage_rankings(country.id)
# for data in r.json()["mu_filter"]:
# if data["id"]:
# ret.update({data["id"]: data["name"]})
# r = self._get_main_leaderboards_damage_aircraft_rankings(country.id)
# for data in r.json()["mu_filter"]:
# if data["id"]:
# ret.update({data["id"]: data["name"]})
# return ret
#
# def get_mu_members(self, mu_id: int) -> Dict[int, str]:
# ret = {}
# r = self._get_military_unit_data(mu_id)
#
# for page in range(int(r.json()["panelContents"]["pages"])):
# r = self._get_military_unit_data(mu_id, currentPage=page + 1)
# for user in r.json()["panelContents"]["members"]:
# if not user["isDead"]:
# ret.update({user["citizenId"]: user["name"]})
# return ret
#
# def get_citizen_weekly_daily_orders_done(self, citizen_id: int = None, weeks_ago: int = 0) -> int:
# if citizen_id is None:
# citizen_id = self.details.citizen_id
# profile = self._get_main_citizen_profile_json(citizen_id).json()
# mu_id = profile.get("military", {}).get("militaryUnit", {}).get("id", 0)
# if mu_id:
# name = profile.get("citizen", {}).get("name")
# member = self._get_military_unit_data(
# mu_id,
# currentPage=1,
# panel="members",
# sortBy="dailyOrdersCompleted",
# weekFilter=f"week{weeks_ago}",
# search=name,
# ).json()
# return member.get("panelContents", {}).get("members", [{}])[0].get("dailyOrdersCompleted")
# return 0
#
# def get_possibly_empty_medals(self):
# self.update_war_info()
# for battle in self.all_battles.values():
# for division in battle.div.values():
# if division.wall["dom"] == 50 or division.wall["dom"] > 98:
# yield division, division.wall["for"] == battle.invader.country.id
#
# def report_fighting(
# self, battle: classes.Battle, invader: bool, division: classes.BattleDivision, damage: float, hits: int
# ):
# self.reporter.report_fighting(battle, invader, division, damage, hits)
# if self.config.telegram:
# self.telegram.report_fight(battle, invader, division, damage, hits)
#
# def get_deploy_inventory(self, division: classes.BattleDivision, side: classes.BattleSide):
# ret = self._post_fight_deploy_get_inventory(division.battle.id, side.id, division.id).json()
# # if ret.get('recoverableEnergyBuyFood'):
# # self.buy_food()
# # return self.get_deploy_inventory(division, side)
# if ret.get("captcha"):
# self.do_captcha_challenge()
# if ret.get("error"):
# if ret.get("message") == "Deployment disabled.":
# self._post_main_profile_update(
# "options", params='{"optionName":"enable_web_deploy","optionValue":"on"}'
# )
# return self.get_deploy_inventory(division, side)
# else:
# self.report_error(f"Unable to get deployment inventory because: {ret.get('message')}")
# return ret
#
# def deploy(self, division: classes.BattleDivision, side: classes.BattleSide, energy: int, _retry=0) -> int:
# _energy = int(energy)
# deploy_inv = self.get_deploy_inventory(division, side)
# if not deploy_inv["minEnergy"] <= energy <= deploy_inv["maxEnergy"]:
# return 0
# energy_sources = {}
# source_idx = 0
# recoverable = deploy_inv["recoverableEnergy"]
# for source in reversed(sorted(deploy_inv["energySources"], key=lambda s: (s["type"], s.get("quality", 0)))):
# if source["type"] == "pool":
# _energy -= source["energy"]
# elif source["type"] in ["food", "energy_bar"]:
# recovers = source["energy"] // source["amount"]
# amount = (recoverable if source["type"] == "food" else _energy) // recovers
# amount = amount if amount < source["amount"] else source["amount"]
# if amount > 0:
# energy_sources.update({f"energySources[{source_idx}][quality]": source["quality"]})
# energy_sources.update({f"energySources[{source_idx}][amount]": amount})
# source_idx += 1
# used_energy = amount * recovers
# recoverable -= used_energy
# _energy -= used_energy
# if _energy <= 0:
# break
# if _energy > 0:
# energy -= _energy
# weapon_q = -1
# weapon_strength = 0
# if not division.is_air:
# for weapon in sorted(deploy_inv["weapons"], key=lambda w: w["damageperHit"]):
# if (weapon["damageperHit"] or 0) > weapon_strength and (weapon["amount"] or 0) > 50:
# weapon_q = weapon["quality"]
# r = self._post_fight_deploy_start_deploy(
# division.battle.id, side.id, division.id, energy, weapon_q, **energy_sources
# ).json()
# if r.get("error"):
# self.report_error(f"Deploy failed: '{r.get('message')}'")
# if r.get("message") == "Deployment disabled.":
# self._post_main_profile_update(
# "options", params='{"optionName":"enable_web_deploy","optionValue":"on"}'
# )
# if _retry < 5:
# return self.deploy(division, side, energy, _retry + 1)
# else:
# self.report_error("Unable to deploy 5 times!")
# return 0
# return r.get("deploymentId")
# def should_fight(self, silent: bool = True) -> Tuple[int, str, bool]:
# if not hasattr(super, "should_fight"):
# return 0, "Unable to fight", False
# count, log_msg, force_fight = super().should_fight()
#
# if count > 0 and not force_fight:
# if self.energy.food_fights - self.my_companies.ff_lockdown < count:
# log_msg = (
# f"Fight count modified (old count: {count} | FF: {self.energy.food_fights} | "
# f"WAM ff_lockdown: {self.my_companies.ff_lockdown} |"
# f" New count: {count - self.my_companies.ff_lockdown})"
# )
# count -= self.my_companies.ff_lockdown
# if count <= 0:
# count = 0
# 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:
# max_count = (int(self.time_till_week_change.total_seconds()) // 360 * self.energy.interval) // 10
# log_msg = (
# f"End for Weekly challenge is near (Recoverable until WC end {max_count}hp | want to do {count}hits)"
# )
# count = count if max_count > count else max_count
#
# if not silent:
# self.write_log(log_msg)
#
# return count, log_msg, force_fight

View File

@ -4,7 +4,7 @@
__author__ = """Eriks Karls"""
__email__ = "eriks@72.lv"
__version__ = "0.26.0"
__version__ = "0.30.0.4"
from erepublik.citizen import Citizen

View File

@ -9,7 +9,7 @@ from logging import LogRecord, handlers
from pathlib import Path
from typing import Any, Dict, Union
import requests
from httpx import post
from erepublik.classes import Reporter
from erepublik.constants import erep_tz
@ -112,7 +112,7 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler):
def _get_last_response(self) -> Dict[str, str]:
response = self.reporter.citizen.r
url = response.url
url = str(response.url)
last_index = url.index("?") if "?" in url else len(response.url)
name = slugify(response.url[len(self.reporter.citizen.url) : last_index])
@ -133,7 +133,9 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler):
except: # noqa
resp_time = slugify(response.headers.get("date"))
return dict(
name=f"{resp_time}_{name}.{ext}", content=html.encode("utf-8"), mimetype="application/json" if ext == "json" else "text/html"
name=f"{resp_time}_{name}.{ext}",
content=html.encode("utf-8"),
mimetype="application/json" if ext == "json" else "text/html",
)
def _get_local_vars(self) -> str:
@ -207,6 +209,6 @@ class ErepublikErrorHTTTPHandler(handlers.HTTPHandler):
headers = {"Authorization": s}
data = self.mapLogRecord(record)
files = data.pop("files") if "files" in data else None
requests.post(f"{proto}://{self.host}{self.url}", headers=headers, data=data, files=files)
post(f"{proto}://{self.host}{self.url}", headers=headers, data=data, files=files)
except Exception:
self.handleError(record)

View File

@ -4,9 +4,8 @@ import random
import time
from typing import Any, Dict, List, Mapping, Union
from requests import Response, Session
from requests.exceptions import ConnectionError
from requests_toolbelt.utils import dump
from httpx import Response, Client as Session, RequestError
#from requests_toolbelt.utils import dump
from erepublik import constants, utils
@ -15,25 +14,26 @@ __all__ = ["SlowRequests", "CitizenAPI"]
class SlowRequests(Session):
last_time: datetime.datetime
timeout: datetime.timedelta = datetime.timedelta(milliseconds=500)
__timeout: datetime.timedelta = datetime.timedelta(milliseconds=500)
debug: bool = False
http2 = True
def __init__(self, proxies: Dict[str, str] = None, user_agent: str = None):
super().__init__()
super().__init__(http2=True, follow_redirects=True)
if proxies:
self.proxies = proxies
if user_agent is None:
user_agent = random.choice(self.get_random_user_agent())
user_agent = self.get_random_user_agent()
self.request_log_name = utils.get_file(utils.now().strftime("debug/requests_%Y-%m-%d.log"))
self.last_time = utils.now()
self.headers.update({"User-Agent": user_agent})
self.hooks["response"] = [self._log_response]
self.event_hooks["response"] = [self._log_response]
@property
def as_dict(self):
return dict(
last_time=self.last_time,
timeout=self.timeout,
__timeout=self.__timeout,
cookies=self.cookies.get_dict(),
debug=self.debug,
user_agent=self.headers["User-Agent"],
@ -46,14 +46,14 @@ class SlowRequests(Session):
self._log_request(url, method, **kwargs)
try:
resp = super().request(method, url, *args, **kwargs)
except ConnectionError:
except RequestError:
time.sleep(1)
return self.request(method, url, *args, **kwargs)
# self._log_response(resp)
return resp
def _slow_down_requests(self):
ltt = utils.good_timedelta(self.last_time, self.timeout)
ltt = utils.good_timedelta(self.last_time, self.__timeout)
if ltt > utils.now():
seconds = (ltt - utils.now()).total_seconds()
time.sleep(seconds if seconds > 0 else 0)
@ -81,7 +81,7 @@ class SlowRequests(Session):
def _log_response(self, response: Response, *args, **kwargs):
redirect = kwargs.get("redirect")
url = response.request.url
url = str(response.request.url)
if self.debug:
if response.history and not redirect:
for hist_resp in response.history:
@ -92,7 +92,7 @@ class SlowRequests(Session):
fd_time = self.last_time.strftime("%Y/%m/%d/%H-%M-%S")
fd_name = utils.slugify(url[len(CitizenBaseAPI.url) :])
fd_extra = "_REDIRECT" if redirect else ""
response.read()
try:
utils.json.loads(response.text)
fd_ext = "json"
@ -102,30 +102,23 @@ class SlowRequests(Session):
filename = f"{fd_path}/{fd_time}_{fd_name}{fd_extra}.{fd_ext}"
utils.write_file(filename, response.text)
if not redirect:
data = dump.dump_all(response)
utils.write_file(f"debug/dumps/{fd_time}_{fd_name}{fd_extra}.{fd_ext}.dump", data.decode("utf8"))
#if not redirect:
# data = dump.dump_all(response)
# utils.write_file(f"debug/dumps/{fd_time}_{fd_name}{fd_extra}.{fd_ext}.dump", data.decode("utf8"))
@staticmethod
def get_random_user_agent() -> str:
windows_x64 = "Windows NT 10.0; Win64; x64"
linux_x64 = "X11; Linux x86_64"
android = [f"Android {version}; Mobile" for version in range(8, 12)]
android = [] # [f"Android {version}; Mobile" for version in range(10, 13)]
firefox_template = "Mozilla/5.0 ({osystem}; rv:{version}.0) Gecko/20100101 Firefox/{version}.0"
firefox_versions = range(85, 92)
firefox_versions = range(92, 97)
chrome_template = "Mozilla/5.0 ({osystem}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version} Safari/537.36"
chrome_versions = [
"85.0.4183.121",
"86.0.4240.183",
"87.0.4280.141",
"88.0.4324.182",
"89.0.4389.128",
"90.0.4430.18",
"91.0.4472.73",
"92.0.4515.159",
]
chrome_template = (
"Mozilla/5.0 ({osystem}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version} Safari/537.36"
)
chrome_versions = ["92.0.4515.159", "93.0.4577.82", "94.0.4606.81", "95.0.4638.54", "96.0.4664.110"]
uas = []
for osystem in [windows_x64, linux_x64, *android]:
@ -152,7 +145,7 @@ class CitizenBaseAPI:
return dict(url=self.url, request=self._req.as_dict, token=self.token)
def post(self, url: str, data=None, json=None, **kwargs) -> Response:
return self._req.post(url, data, json, **kwargs)
return self._req.post(url, data=data, json=json, **kwargs)
def get(self, url: str, **kwargs) -> Response:
return self._req.get(url, **kwargs)
@ -187,15 +180,19 @@ class CitizenBaseAPI:
) -> Response:
c = [cookie.name for cookie in self._req.cookies if not cookie.has_nonstandard_attr("HttpOnly")]
env = dict(l=["tets"], s=[], c=c, m=0)
cookies = dict(
sh=hashlib.sha256(",".join(env["l"] + env["s"]).encode("utf8")).hexdigest(),
ch=hashlib.sha256(",".join(env["c"]).encode("utf8")).hexdigest(),
)
session_hash = hashlib.sha256(",".join(env["l"] + env["s"]).encode("utf8")).hexdigest()
cookies_hash = hashlib.sha256(",".join(env["c"]).encode("utf8")).hexdigest()
cookie_kwargs = dict(
expires=int(time.time()) + 120, path="/en/main/sessionUnlock", domain=".www.erepublik.com", secure=True, rest={"HttpOnly": True}
expires=int(time.time()) + 120,
path="/en/main/sessionUnlock",
domain=".www.erepublik.com",
secure=True,
rest={"HttpOnly": True},
)
self._req.cookies.set("sh", cookies["sh"], **cookie_kwargs)
self._req.cookies.set("ch", cookies["ch"], **cookie_kwargs)
self._req.cookies.set("sh", session_hash, **cookie_kwargs)
self._req.cookies.set("ch", cookies_hash, **cookie_kwargs)
b64_env = utils.b64json(env)
data = dict(
_token=self.token,
@ -271,7 +268,13 @@ class ErepublikArticleAPI(CitizenBaseAPI):
return self.post(f"{self.url}/main/donate-article", data=data)
def _post_main_write_article(self, title: str, content: str, country_id: int, kind_id: int) -> Response:
data = dict(_token=self.token, article_name=title, article_body=content, article_location=country_id, article_category=kind_id)
data = dict(
_token=self.token,
article_name=title,
article_body=content,
article_location=country_id,
article_category=kind_id,
)
return self.post(f"{self.url}/main/write-article", data=data)
def _post_main_vote_article(self, article_id: int) -> Response:
@ -286,7 +289,9 @@ class ErepublikCompanyAPI(CitizenBaseAPI):
def _post_economy_create_company(self, industry_id: int, building_type: int = 1) -> Response:
data = {"_token": self.token, "company[industry_id]": industry_id, "company[building_type]": building_type}
return self.post(f"{self.url}/economy/create-company", data=data, headers={"Referer": f"{self.url}/economy/create-company"})
return self.post(
f"{self.url}/economy/create-company", data=data, headers={"Referer": f"{self.url}/economy/create-company"}
)
def _get_economy_inventory_items(self) -> Response:
return self.get(f"{self.url}/economy/inventory-items/")
@ -366,9 +371,13 @@ class ErepublikCountryAPI(CitizenBaseAPI):
def _get_country_military(self, country_name: str) -> Response:
return self.get(f"{self.url}/country/military/{country_name}")
def _post_main_country_donate(self, country_id: int, action: str, value: Union[int, float], quality: int = None) -> Response:
def _post_main_country_donate(
self, country_id: int, action: str, value: Union[int, float], quality: int = None
) -> Response:
data = dict(countryId=country_id, action=action, _token=self.token, value=value, quality=quality)
return self.post(f"{self.url}/main/country-donate", data=data, headers={"Referer": f"{self.url}/country/economy/Latvia"})
return self.post(
f"{self.url}/main/country-donate", data=data, headers={"Referer": f"{self.url}/country/economy/Latvia"}
)
class ErepublikEconomyAPI(CitizenBaseAPI):
@ -396,13 +405,17 @@ class ErepublikEconomyAPI(CitizenBaseAPI):
def _post_economy_donate_items_action(self, citizen_id: int, amount: int, industry: int, quality: int) -> Response:
data = dict(citizen_id=citizen_id, amount=amount, industry_id=industry, quality=quality, _token=self.token)
return self.post(
f"{self.url}/economy/donate-items-action", data=data, headers={"Referer": f"{self.url}/economy/donate-items/{citizen_id}"}
f"{self.url}/economy/donate-items-action",
data=data,
headers={"Referer": f"{self.url}/economy/donate-items/{citizen_id}"},
)
def _post_economy_donate_money_action(self, citizen_id: int, amount: float = 0.0, currency: int = 62) -> Response:
data = dict(citizen_id=citizen_id, _token=self.token, currency_id=currency, amount=amount)
return self.post(
f"{self.url}/economy/donate-money-action", data=data, headers={"Referer": f"{self.url}/economy/donate-money/{citizen_id}"}
f"{self.url}/economy/donate-money-action",
data=data,
headers={"Referer": f"{self.url}/economy/donate-money/{citizen_id}"},
)
def _post_economy_exchange_purchase(self, amount: float, currency: int, offer: int) -> Response:
@ -434,7 +447,12 @@ class ErepublikEconomyAPI(CitizenBaseAPI):
def _post_economy_marketplace_actions(self, action: str, **kwargs) -> Response:
if action == "buy":
data = dict(
_token=self.token, offerId=kwargs["offer"], amount=kwargs["amount"], orderBy="price_asc", currentPage=1, buyAction=1
_token=self.token,
offerId=kwargs["offer"],
amount=kwargs["amount"],
orderBy="price_asc",
currentPage=1,
buyAction=1,
)
elif action == "sell":
data = dict(
@ -454,16 +472,24 @@ class ErepublikEconomyAPI(CitizenBaseAPI):
class ErepublikLeaderBoardAPI(CitizenBaseAPI):
def _get_main_leaderboards_damage_aircraft_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0) -> Response: # noqa
def _get_main_leaderboards_damage_aircraft_rankings(
self, country_id: int, weeks: int = 0, mu_id: int = 0
) -> Response: # noqa
return self.get(f"{self.url}/main/leaderboards-damage-aircraft-rankings/{country_id}/{weeks}/{mu_id}/0")
def _get_main_leaderboards_damage_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0, div: int = 0) -> Response: # noqa
def _get_main_leaderboards_damage_rankings(
self, country_id: int, weeks: int = 0, mu_id: int = 0, div: int = 0
) -> Response: # noqa
return self.get(f"{self.url}/main/leaderboards-damage-rankings/{country_id}/{weeks}/{mu_id}/{div}")
def _get_main_leaderboards_kills_aircraft_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0) -> Response: # noqa
def _get_main_leaderboards_kills_aircraft_rankings(
self, country_id: int, weeks: int = 0, mu_id: int = 0
) -> Response: # noqa
return self.get(f"{self.url}/main/leaderboards-kills-aircraft-rankings/{country_id}/{weeks}/{mu_id}/0")
def _get_main_leaderboards_kills_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0, div: int = 0) -> Response: # noqa
def _get_main_leaderboards_kills_rankings(
self, country_id: int, weeks: int = 0, mu_id: int = 0, div: int = 0
) -> Response: # noqa
return self.get(f"{self.url}/main/leaderboards-kills-rankings/{country_id}/{weeks}/{mu_id}/{div}")
@ -543,7 +569,14 @@ class ErepublikMilitaryAPI(CitizenBaseAPI):
return self.post(f"{self.url}/military/battle-console", data=data)
def _post_military_deploy_bomb(self, battle_id: int, division_id: int, side_id: int, bomb_id: int) -> Response:
data = dict(battleId=battle_id, battleZoneId=division_id, sideId=side_id, sideCountryId=side_id, bombId=bomb_id, _token=self.token)
data = dict(
battleId=battle_id,
battleZoneId=division_id,
sideId=side_id,
sideCountryId=side_id,
bombId=bomb_id,
_token=self.token,
)
return self.post(f"{self.url}/military/deploy-bomb", data=data)
def _post_military_fight_air(self, battle_id: int, side_id: int, zone_id: int) -> Response:
@ -599,7 +632,9 @@ class ErepublikPoliticsAPI(CitizenBaseAPI):
return self.get(f"{self.url}/main/presidential-elections/{country_id}/{timestamp}")
def _post_propose_president_candidate(self, party_slug: str, citizen_id: int) -> Response:
return self.post(f"{self.url}/propose-president-candidate/{party_slug}", data=dict(_token=self.token, citizen=citizen_id))
return self.post(
f"{self.url}/propose-president-candidate/{party_slug}", data=dict(_token=self.token, citizen=citizen_id)
)
def _get_auto_propose_president_candidate(self, party_slug: str) -> Response:
return self.get(f"{self.url}/auto-propose-president-candidate/{party_slug}")
@ -611,11 +646,24 @@ class ErepublikPresidentAPI(CitizenBaseAPI):
return self.post(f"{self.url}/wars/attack-region/{war_id}/{region_id}", data=data)
def _post_new_war(self, self_country_id: int, attack_country_id: int, debate: str = "") -> Response:
data = dict(requirments=1, _token=self.token, debate=debate, countryNameConfirm=constants.COUNTRIES[attack_country_id].link)
data = dict(
requirments=1,
_token=self.token,
debate=debate,
countryNameConfirm=constants.COUNTRIES[attack_country_id].link,
)
return self.post(f"{self.url}/{constants.COUNTRIES[self_country_id].link}/new-war", data=data)
def _post_new_donation(self, country_id: int, amount: int, org_name: str, debate: str = "") -> Response:
data = dict(requirments=1, _token=self.token, debate=debate, currency=1, value=amount, commit="Propose", type_name=org_name)
data = dict(
requirments=1,
_token=self.token,
debate=debate,
currency=1,
value=amount,
commit="Propose",
type_name=org_name,
)
return self.post(f"{self.url}/{constants.COUNTRIES[country_id].link}/new-donation", data=data)
@ -705,7 +753,12 @@ class ErepublikProfileAPI(CitizenBaseAPI):
def _post_main_messages_compose(self, subject: str, body: str, citizens: List[int]) -> Response:
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)
data = dict(
citizen_name=",".join([str(x) for x in citizens]),
citizen_subject=subject,
_token=self.token,
citizen_message=body,
)
return self.post(f"{self.url}/main/messages-compose/{url_pk}", data=data)
def _post_military_group_missions(self) -> Response:
@ -810,7 +863,8 @@ class ErepublikWallPostAPI(CitizenBaseAPI):
# ## Medal posting
def _post_main_wall_post_automatic(self, message: str, achievement_id: int) -> Response:
return self.post(
f"{self.url}/main/wall-post/automatic", data=dict(_token=self.token, message=message, achievementId=achievement_id)
f"{self.url}/main/wall-post/automatic",
data=dict(_token=self.token, message=message, achievementId=achievement_id),
)

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ from decimal import Decimal
from io import BytesIO
from typing import Any, Dict, Generator, Iterable, List, NamedTuple, NoReturn, Optional, Tuple, Union
from requests import HTTPError, Response, Session, post
from httpx import HTTPError, Response, Client as Session, post
from erepublik import _types as types
from erepublik import constants, utils
@ -22,6 +22,8 @@ __all__ = [
"Energy",
"ErepublikException",
"ErepublikNetworkException",
"CloudFlareSessionError",
"CaptchaSessionError",
"EnergyToFight",
"Holding",
"Inventory",
@ -129,7 +131,13 @@ class Holding:
@property
def as_dict(self) -> Dict[str, Union[str, int, List[Dict[str, Union[str, int, bool, float, Decimal]]]]]:
return dict(name=self.name, id=self.id, region=self.region, companies=[c.as_dict for c in self.companies], wam_count=self.wam_count)
return dict(
name=self.name,
id=self.id,
region=self.region,
companies=[c.as_dict for c in self.companies],
wam_count=self.wam_count,
)
@property
def citizen(self):
@ -290,6 +298,10 @@ class MyCompanies:
_companies: weakref.WeakSet
_citizen: weakref.ReferenceType
companies: Generator[Company, None, None]
_frm_fab_ids = (1, 7, 8, 9, 10, 11)
_wrm_fab_ids = (2, 12, 13, 14, 15, 16)
_hrm_fab_ids = (4, 18, 19, 20, 21, 22)
_arm_fab_ids = (23, 24, 25, 26, 27, 28)
def __init__(self, citizen):
self._citizen = weakref.ref(citizen)
@ -303,7 +315,13 @@ class MyCompanies:
"""
for holding in holdings.values():
if holding.get("id") not in self.holdings:
self.holdings.update({int(holding.get("id")): Holding(holding["id"], holding["region_id"], self.citizen, holding["name"])})
self.holdings.update(
{
int(holding.get("id")): Holding(
holding["id"], holding["region_id"], self.citizen, holding["name"]
)
}
)
if not self.holdings.get(0):
self.holdings.update({0: Holding(0, 0, self.citizen, "Unassigned")}) # unassigned
@ -347,14 +365,58 @@ class MyCompanies:
@staticmethod
def get_needed_inventory_usage(companies: Union[Company, Iterable[Company]]) -> Decimal:
if isinstance(companies, list):
return sum(company.products_made * 100 if company.is_raw else 1 for company in companies)
return sum(company.products_made * (100 if company.is_raw else 1) for company in companies)
else:
return companies.products_made
def remove_factory_from_wam_list(self, raw_factories, final_factories):
frm, wrm, *_ = self.get_raw_usage_for_companies(*final_factories, *raw_factories)
inv_raw = self.citizen.inventory.raw
for raw, ids, exc in [(frm, self._frm_fab_ids, False), (wrm, self._wrm_fab_ids, False), (None, None, True)]:
if exc:
if final_factories:
final_factories.sort(key=lambda c: c.raw_usage)
return final_factories.pop(-1)
elif raw_factories:
raw_factories.sort(key=lambda c: c.raw_usage)
return raw_factories.pop(-1)
else:
if raw:
raw += Decimal(
inv_raw.get(constants.INDUSTRIES[ids[1]], {}).get(0, {}).get("amount", Decimal("0.0"))
)
if raw > 0:
to_remove = sorted(raw_factories, key=lambda c: (c.industry not in ids, c.raw_usage))
if to_remove:
return raw_factories.pop(raw_factories.index(to_remove[0]))
else:
to_remove = sorted(final_factories, key=lambda c: (c.industry != ids[0], c.raw_usage))
if to_remove:
return final_factories.pop(final_factories.index(to_remove[0]))
def get_raw_usage_for_companies(self, *companies: Company) -> Tuple[Decimal, Decimal, Decimal, Decimal]:
frm = wrm = hrm = arm = Decimal("0.00")
for company in companies:
if company.industry in self._frm_fab_ids:
frm += company.raw_usage
elif company.industry in self._wrm_fab_ids:
wrm += company.raw_usage
elif company.industry in self._hrm_fab_ids:
hrm += company.raw_usage
elif company.industry in self._arm_fab_ids:
arm += company.raw_usage
return frm, wrm, hrm, arm
@property
def companies(self) -> Generator[Company, None, None]:
return (c for c in self._companies)
def get_wam_holdings(self) -> Generator[Holding, None, None]:
for holding in sorted(
self.holdings.values(), key=lambda h: (-len(h.get_wam_companies(False)), -len(h.get_wam_companies()))
):
yield holding
def __str__(self):
return f"MyCompanies: {sum(1 for _ in self.companies)} companies in {len(self.holdings)} holdings"
@ -373,7 +435,12 @@ class MyCompanies:
self,
) -> Dict[
str,
Union[str, int, datetime.datetime, Dict[str, Dict[str, Union[str, int, List[Dict[str, Union[str, int, bool, float, Decimal]]]]]]],
Union[
str,
int,
datetime.datetime,
Dict[str, Dict[str, Union[str, int, List[Dict[str, Union[str, int, bool, float, Decimal]]]]]],
],
]:
return dict(
name=str(self),
@ -399,28 +466,28 @@ class Config:
auto_sell: List[str] = None
auto_sell_all = False
employees = False
fight = False
air = False
ground = False
all_in = False
next_energy = False
boosters = False
travel_to_fight = False
always_travel = False
epic_hunt = False
epic_hunt_ebs = False
rw_def_side = False
interactive = True
continuous_fighting = False
auto_buy_raw = False
force_wam = False
sort_battles_time = True
force_travel = False
telegram = True
telegram_chat_id = 0
telegram_token = ""
maverick = False
spin_wheel_of_fortune = False
# fight = False
# air = False
# ground = False
# all_in = False
# next_energy = False
# boosters = False
# travel_to_fight = False
# always_travel = False
# epic_hunt = False
# epic_hunt_ebs = False
# rw_def_side = False
# continuous_fighting = False
# sort_battles_time = True
# force_travel = False
# maverick = False
def __init__(self):
self.auto_sell = []
@ -433,28 +500,28 @@ class Config:
self.auto_sell = list()
self.auto_sell_all = False
self.employees = False
self.fight = False
self.air = False
self.ground = False
self.all_in = False
self.next_energy = False
self.boosters = False
self.travel_to_fight = False
self.always_travel = False
self.epic_hunt = False
self.epic_hunt_ebs = False
self.rw_def_side = False
self.interactive = True
self.continuous_fighting = False
self.auto_buy_raw = False
self.force_wam = False
self.sort_battles_time = True
self.force_travel = False
self.telegram = True
self.telegram_chat_id = 0
self.telegram_token = ""
self.maverick = False
self.spin_wheel_of_fortune = False
# self.fight = False
# self.air = False
# self.ground = False
# self.all_in = False
# self.next_energy = False
# self.boosters = False
# self.travel_to_fight = False
# self.always_travel = False
# self.epic_hunt = False
# self.epic_hunt_ebs = False
# self.rw_def_side = False
# self.continuous_fighting = False
# self.sort_battles_time = True
# self.force_travel = False
# self.maverick = False
@property
def as_dict(self) -> Dict[str, Union[bool, int, str, List[str]]]:
@ -467,27 +534,27 @@ class Config:
auto_sell=self.auto_sell,
auto_sell_all=self.auto_sell_all,
employees=self.employees,
fight=self.fight,
air=self.air,
ground=self.ground,
all_in=self.all_in,
next_energy=self.next_energy,
travel_to_fight=self.travel_to_fight,
always_travel=self.always_travel,
epic_hunt=self.epic_hunt,
epic_hunt_ebs=self.epic_hunt_ebs,
rw_def_side=self.rw_def_side,
interactive=self.interactive,
maverick=self.maverick,
continuous_fighting=self.continuous_fighting,
auto_buy_raw=self.auto_buy_raw,
force_wam=self.force_wam,
sort_battles_time=self.sort_battles_time,
force_travel=self.force_travel,
telegram=self.telegram,
telegram_chat_id=self.telegram_chat_id,
telegram_token=self.telegram_token,
spin_wheel_of_fortune=self.spin_wheel_of_fortune,
# fight=self.fight,
# air=self.air,
# ground=self.ground,
# all_in=self.all_in,
# next_energy=self.next_energy,
# travel_to_fight=self.travel_to_fight,
# always_travel=self.always_travel,
# epic_hunt=self.epic_hunt,
# epic_hunt_ebs=self.epic_hunt_ebs,
# rw_def_side=self.rw_def_side,
# maverick=self.maverick,
# continuous_fighting=self.continuous_fighting,
# sort_battles_time=self.sort_battles_time,
# force_travel=self.force_travel,
)
@ -530,12 +597,16 @@ class Energy:
@property
def is_recoverable_full(self):
warnings.warn("Deprecated since auto auto-eat! Will be removed soon. Use Energy.is_energy_full", DeprecationWarning)
warnings.warn(
"Deprecated since auto auto-eat! Will be removed soon. Use Energy.is_energy_full", DeprecationWarning
)
return self.is_energy_full
@property
def is_recovered_full(self):
warnings.warn("Deprecated since auto auto-eat! Will be removed soon. Use Energy.is_energy_full", DeprecationWarning)
warnings.warn(
"Deprecated since auto auto-eat! Will be removed soon. Use Energy.is_energy_full", DeprecationWarning
)
return self.is_energy_full
@property
@ -691,27 +762,26 @@ class Reporter:
@property
def as_dict(self) -> Dict[str, Union[bool, int, str, List[Dict[Any, Any]]]]:
return dict(
name=self.name, email=self.email, citizen_id=self.citizen_id, key=self.key, allowed=self.allowed, queue=self.__to_update
name=self.name,
email=self.email,
citizen_id=self.citizen_id,
key=self.key,
allowed=self.allowed,
queue=self.__to_update,
)
def __init__(self, citizen):
self._citizen = weakref.ref(citizen)
self._req = Session()
self.url = "https://api.erep.lv"
self._req.headers.update(
{
"user-agent": "eRepublik Script Reporter v3",
"erep-version": utils.__version__,
"erep-user-id": str(self.citizen_id),
"erep-user-name": self.citizen.name,
}
)
self._req = Session(follow_redirects=True)
self.url = "https://erep.lv"
self._req.headers.update({"user-agent": "eRepublik Script Reporter v3", "erep-version": utils.__version__})
self.__to_update = []
self.__registered: bool = False
def do_init(self):
self.key: str = ""
self.__update_key()
self._req.headers.update({"erep-user-id": str(self.citizen_id), "erep-user-name": self.name})
self.register_account()
self.allowed = True
@ -751,7 +821,10 @@ class Reporter:
r = self.__bot_update(dict(key=self.key, check=True, player_id=self.citizen_id))
if r:
if not r.json().get("status"):
self._req.post(f"{self.url}/bot/register", json=dict(name=self.name, email=self.email, player_id=self.citizen_id))
self._req.post(
f"{self.url}/bot/register",
json=dict(name=self.name, email=self.email, player_id=self.citizen_id),
)
self.__registered = True
self.allowed = True
self.report_action("STARTED", value=utils.now().strftime("%F %T"))
@ -790,7 +863,9 @@ class Reporter:
self._bot_update(data)
def report_action(self, action: str, json_val: Dict[Any, Any] = None, value: str = None):
json_data = dict(player_id=getattr(self, "citizen_id", None), log={"action": action}, key=getattr(self, "key", None))
json_data = dict(
player_id=getattr(self, "citizen_id", None), log={"action": action}, key=getattr(self, "key", None)
)
if json_val:
json_data["log"].update(dict(json=json_val))
if value:
@ -836,14 +911,18 @@ class Reporter:
try:
battle_response = self._req.get(f"{self.url}/api/v1/battles/{country.id}")
return [
self.citizen.all_battles[bid] for bid in battle_response.json().get("battle_ids", []) if bid in self.citizen.all_battles
self.citizen.all_battles[bid]
for bid in battle_response.json().get("battle_ids", [])
if bid in self.citizen.all_battles
]
except: # noqa
return []
def fetch_tasks(self) -> List[Dict[str, Any]]:
try:
task_response = self._req.post(f"{self.url}/api/v1/command", data=dict(citizen=self.citizen_id, key=self.key)).json()
task_response = self._req.post(
f"{self.url}/api/v1/command", data=dict(citizen=self.citizen_id, key=self.key)
).json()
if task_response.get("status"):
return task_response.get("data")
else:
@ -894,7 +973,13 @@ class BattleSide:
@property
def as_dict(self) -> Dict[str, Union[int, constants.Country, bool, List[constants.Country]]]:
return dict(points=self.points, country=self.country, is_defender=self.is_defender, allies=self.allies, deployed=self.deployed)
return dict(
points=self.points,
country=self.country,
is_defender=self.is_defender,
allies=self.allies,
deployed=self.deployed,
)
@property
def battle(self):
@ -917,7 +1002,12 @@ class BattleDivision:
@property
def as_dict(self):
return dict(
id=self.id, division=self.div, terrain=(self.terrain, self.terrain_display), wall=self.wall, epic=self.epic, end=self.div_end
id=self.id,
division=self.div,
terrain=(self.terrain, self.terrain_display),
wall=self.wall,
epic=self.epic,
end=self.div_end,
)
@property
@ -1210,7 +1300,8 @@ class TelegramReporter:
def report_item_donation(self, citizen_id: int, amount: float, product: str):
self.send_message(
f"*Donation*: {amount} x {product} to citizen " f"[{citizen_id}](https://www.erepublik.com/en/citizen/profile/{citizen_id})"
f"*Donation*: {amount} x {product} to citizen "
f"[{citizen_id}](https://www.erepublik.com/en/citizen/profile/{citizen_id})"
)
def report_money_donation(self, citizen_id: int, amount: float, is_currency: bool = True):
@ -1228,7 +1319,9 @@ class TelegramReporter:
message = "\n\n".join(self.__queue)
if self.player_name:
message = f"Player *{self.player_name}*\n\n" + message
response = post(f"{self.api_url}/sendMessage", json=dict(chat_id=self.chat_id, text=message, parse_mode="Markdown"))
response = post(
f"{self.api_url}/sendMessage", json=dict(chat_id=self.chat_id, text=message, parse_mode="Markdown")
)
self._last_time = utils.now()
if response.json().get("ok"):
self.__queue.clear()
@ -1275,5 +1368,11 @@ class Inventory:
@property
def as_dict(self) -> Dict[str, Union[types.InvFinal, types.InvRaw, int]]:
return dict(
active=self.active, final=self.final, boosters=self.boosters, raw=self.raw, offers=self.offers, total=self.total, used=self.used
active=self.active,
final=self.final,
boosters=self.boosters,
raw=self.raw,
offers=self.offers,
total=self.total,
used=self.used,
)

View File

@ -8,12 +8,15 @@ __all__ = [
"min_datetime",
"max_datetime",
"Country",
"Industries",
"Rank",
"AIR_RANKS",
"AIR_RANK_NAMES",
"AIR_RANK_POINTS",
"COUNTRIES",
"FOOD_ENERGY",
"GROUND_RANKS",
"GROUND_RANK_NAMES",
"GROUND_RANK_POINTS",
"INDUSTRIES",
"TERRAINS",
@ -118,7 +121,6 @@ class Industries:
14: "WRM q3",
15: "WRM q4",
16: "WRM q5",
17: "houseRaw",
18: "houseRaw",
19: "HRM q2",
20: "HRM q3",
@ -131,7 +133,7 @@ class Industries:
28: "ARM q5",
}
def __getitem__(self, item) -> Optional[Union[int, str]]:
def __getitem__(self, item: Union[int, str]) -> Optional[Union[int, str]]:
if isinstance(item, int):
return self.__by_id.get(item, None)
elif isinstance(item, str):

View File

@ -12,8 +12,8 @@ from pathlib import Path
from typing import Any, Dict, List, Union
import pytz
import requests
from requests import Response
import httpx
from httpx import Response
from erepublik import __version__, constants
@ -93,7 +93,7 @@ def eday_from_date(date: Union[datetime.date, datetime.datetime] = None) -> int:
return (date - datetime.datetime(2007, 11, 20, 0, 0, 0)).days
def date_from_eday(eday: int) -> datetime.date:
def date_from_eday(eday: int) -> datetime.datetime:
return localize_dt(datetime.date(2007, 11, 20)) + datetime.timedelta(days=eday)
@ -182,7 +182,14 @@ def slugify(value, allow_unicode=False) -> str:
def calculate_hit(
strength: float, rang: int, tp: bool, elite: bool, ne: bool, booster: int = 0, weapon: int = 200, is_deploy: bool = False
strength: float,
rang: int,
tp: bool,
elite: bool,
ne: bool,
booster: int = 0,
weapon: int = 200,
is_deploy: bool = False,
) -> Decimal:
dec = 3 if is_deploy else 0
base_str = 1 + Decimal(str(round(strength, 3))) / 400
@ -197,7 +204,7 @@ def calculate_hit(
def get_ground_hit_dmg_value(
citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, weapon_power: int = 200
) -> Decimal:
r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json()
r = httpx.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json()
rang = r["military"]["militaryData"]["ground"]["rankNumber"]
strength = r["military"]["militaryData"]["ground"]["strength"]
elite = r["citizenAttributes"]["level"] > 100
@ -210,14 +217,19 @@ def get_ground_hit_dmg_value(
def get_air_hit_dmg_value(
citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0, weapon_power: int = 0
) -> Decimal:
r = requests.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json()
r = httpx.get(f"https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}").json()
rang = r["military"]["militaryData"]["aircraft"]["rankNumber"]
elite = r["citizenAttributes"]["level"] > 100
return calculate_hit(0, rang, true_patriot, elite, natural_enemy, booster, weapon_power)
def get_final_hit_dmg(
base_dmg: Union[Decimal, float, str], rang: int, tp: bool = False, elite: bool = False, ne: bool = False, booster: int = 0
base_dmg: Union[Decimal, float, str],
rang: int,
tp: bool = False,
elite: bool = False,
ne: bool = False,
booster: int = 0,
) -> Decimal:
dmg = Decimal(str(base_dmg))
@ -313,7 +325,11 @@ class ErepublikJSONEncoder(json.JSONEncoder):
return dict(__type__="date", date=o.strftime("%Y-%m-%d"))
elif isinstance(o, datetime.timedelta):
return dict(
__type__="timedelta", days=o.days, seconds=o.seconds, microseconds=o.microseconds, total_seconds=o.total_seconds()
__type__="timedelta",
days=o.days,
seconds=o.seconds,
microseconds=o.microseconds,
total_seconds=o.total_seconds(),
)
elif isinstance(o, Response):
return dict(headers=dict(o.__dict__["headers"]), url=o.url, text=o.text, status_code=o.status_code)

View File

@ -4,18 +4,18 @@ from datetime import timedelta
from erepublik import Citizen, utils
CONFIG = {
'email': 'player@email.com',
'password': 'Pa$5w0rd!',
'interactive': True,
'fight': True,
'debug': True,
'battle_launcher': {
"email": "player@email.com",
"password": "Pa$5w0rd!",
"interactive": True,
"fight": True,
"debug": True,
"battle_launcher": {
# War id: {auto_attack: bool (attack asap when region is available), regions: [region_ids allowed to attack]}
121672: {"auto_attack": False, "regions": [661]},
125530: {"auto_attack": False, "regions": [259]},
125226: {"auto_attack": True, "regions": [549]},
124559: {"auto_attack": True, "regions": [176]}
}
124559: {"auto_attack": True, "regions": [176]},
},
}
@ -29,7 +29,7 @@ def _battle_launcher(player: Citizen):
"""
global CONFIG
finished_war_ids = {*[]}
war_data = CONFIG.get('battle_launcher', {})
war_data = CONFIG.get("battle_launcher", {})
war_ids = {int(war_id) for war_id in war_data.keys()}
next_attack_time = player.now
next_attack_time = next_attack_time.replace(minute=next_attack_time.minute // 5 * 5, second=0)
@ -40,23 +40,23 @@ def _battle_launcher(player: Citizen):
running_wars = {b.war_id for b in player.all_battles.values()}
for war_id in war_ids - finished_war_ids - running_wars:
war = war_data[war_id]
war_regions = set(war.get('regions'))
auto_attack = war.get('auto_attack')
war_regions = set(war.get("regions"))
auto_attack = war.get("auto_attack")
status = player.get_war_status(war_id)
if status.get('ended', False):
CONFIG['battle_launcher'].pop(war_id, None)
if status.get("ended", False):
CONFIG["battle_launcher"].pop(war_id, None)
finished_war_ids.add(war_id)
continue
elif not status.get('can_attack'):
elif not status.get("can_attack"):
continue
if auto_attack or (player.now.hour > 20 or player.now.hour < 2):
for reg in war_regions:
if attacked:
break
if reg in status.get('regions', {}).keys():
player.launch_attack(war_id, reg, status.get('regions', {}).get(reg))
if reg in status.get("regions", {}).keys():
player.launch_attack(war_id, reg, status.get("regions", {}).get(reg))
attacked = True
hits = 100
if player.energy.food_fights >= hits and player.config.fight:
@ -91,12 +91,12 @@ def _battle_launcher(player: Citizen):
# noinspection DuplicatedCode
def main():
player = Citizen(email=CONFIG['email'], password=CONFIG['password'], auto_login=False)
player.config.interactive = CONFIG['interactive']
player.config.fight = CONFIG['fight']
player.set_debug(CONFIG.get('debug', False))
player = Citizen(email=CONFIG["email"], password=CONFIG["password"], auto_login=False)
player.config.interactive = CONFIG["interactive"]
player.config.fight = CONFIG["fight"]
player.set_debug(CONFIG.get("debug", False))
player.login()
if CONFIG.get('battle_launcher'):
if CONFIG.get("battle_launcher"):
name = f"{player.name}-battle_launcher-{threading.active_count() - 1}"
state_thread = threading.Thread(target=_battle_launcher, args=(player,), name=name)
state_thread.start()

View File

@ -3,71 +3,72 @@ from datetime import timedelta
from erepublik import Citizen, constants, utils
CONFIG = {
'email': 'player@email.com',
'password': 'Pa$5w0rd!',
'interactive': True,
'debug': True,
'work': True,
'ot': True, # Work OverTime
'wam': True, # WorkAsManager
'train': True
"email": "player@email.com",
"password": "Pa$5w0rd!",
"interactive": True,
"debug": True,
"work": True,
"ot": True, # Work OverTime
"wam": True, # WorkAsManager
"train": True,
}
# noinspection DuplicatedCode
def main():
player = Citizen(email=CONFIG['email'], password=CONFIG['password'], auto_login=False)
player.config.interactive = CONFIG['interactive']
player.config.work = CONFIG['work']
player.config.train = CONFIG['train']
player.config.ot = CONFIG['ot']
player.config.wam = CONFIG['wam']
player.set_debug(CONFIG.get('debug', False))
player = Citizen(email=CONFIG["email"], password=CONFIG["password"], auto_login=False)
player.config.interactive = CONFIG["interactive"]
player.config.work = CONFIG["work"]
player.config.train = CONFIG["train"]
player.config.ot = CONFIG["ot"]
player.config.wam = CONFIG["wam"]
player.set_debug(CONFIG.get("debug", False))
player.login()
now = player.now.replace(second=0, microsecond=0)
dt_max = constants.max_datetime
tasks = {}
if player.config.work:
tasks.update({'work': now})
tasks.update({"work": now})
if player.config.train:
tasks.update({'train': now})
tasks.update({"train": now})
if player.config.ot:
tasks.update({'ot': now})
tasks.update({"ot": now})
if player.config.wam:
tasks.update({'wam': now.replace(hour=14, minute=0)})
tasks.update({"wam": now.replace(hour=14, minute=0)})
while True:
try:
player.update_all()
if tasks.get('work', dt_max) <= now:
if tasks.get("work", dt_max) <= now:
player.write_log("Doing task: work")
player.update_citizen_info()
player.work()
if player.config.ot:
tasks['ot'] = now
tasks["ot"] = now
player.collect_daily_task()
next_time = utils.good_timedelta(now.replace(hour=0, minute=0, second=0), timedelta(days=1))
tasks.update({'work': next_time})
tasks.update({"work": next_time})
if tasks.get('train', dt_max) <= now:
if tasks.get("train", dt_max) <= now:
player.write_log("Doing task: train")
player.update_citizen_info()
player.train()
player.collect_daily_task()
next_time = utils.good_timedelta(now.replace(hour=0, minute=0, second=0), timedelta(days=1))
tasks.update({'train': next_time})
tasks.update({"train": next_time})
if tasks.get('wam', dt_max) <= now:
if tasks.get("wam", dt_max) <= now:
player.write_log("Doing task: Work as manager")
success = player.work_as_manager()
if success:
next_time = utils.good_timedelta(now.replace(hour=14, minute=0, second=0, microsecond=0),
timedelta(days=1))
next_time = utils.good_timedelta(
now.replace(hour=14, minute=0, second=0, microsecond=0), timedelta(days=1)
)
else:
next_time = utils.good_timedelta(now.replace(second=0, microsecond=0), timedelta(minutes=30))
tasks.update({'wam': next_time})
tasks.update({"wam": next_time})
if tasks.get('ot', dt_max) <= now:
if tasks.get("ot", dt_max) <= now:
player.update_job_info()
player.write_log("Doing task: work overtime")
if now > player.my_companies.next_ot_time:
@ -75,7 +76,7 @@ def main():
next_time = now + timedelta(minutes=60)
else:
next_time = player.my_companies.next_ot_time
tasks.update({'ot': next_time})
tasks.update({"ot": next_time})
closest_next_time = dt_max
next_tasks = []
@ -87,8 +88,9 @@ def main():
if sleep_seconds <= 0:
player.write_log(f"Loop detected! Offending task: '{next_tasks[0]}'")
player.write_log("My next Tasks and there time:\n" + "\n".join(sorted(next_tasks)))
player.write_log(f"Sleeping until (eRep): {closest_next_time.strftime('%F %T')}"
f" (sleeping for {sleep_seconds}s)")
player.write_log(
f"Sleeping until (eRep): {closest_next_time.strftime('%F %T')}" f" (sleeping for {sleep_seconds}s)"
)
seconds_to_sleep = sleep_seconds if sleep_seconds > 0 else 0
player.sleep(seconds_to_sleep)
except Exception as e:

View File

@ -1,4 +1,11 @@
[tool.black]
line-length = 140
line-length = 120
target-version = ['py38', 'py39']
extend-exclude = 'venv'
workers = 4
[tool.isort]
profile = "black"
multi_line_output = 3
line_length = 120

16
requirements-dev.txt Normal file
View File

@ -0,0 +1,16 @@
-r requirements.txt
-r requirements-tests.txt
bump2version==1.0.1
coverage==6.3.2
edx-sphinx-theme==3.0.0
flake8==4.0.1
ipython>=8.1.1
jedi!=0.18.0
isort==5.10.1
pre-commit==2.17.0
pur==6.0.1
responses==0.18.0
Sphinx==4.4.0
twine==3.8.0
wheel==0.37.1
black==22.1.0

2
requirements-tests.txt Normal file
View File

@ -0,0 +1,2 @@
-r requirements.txt
pytest==7.0.1

8
requirements.txt Normal file
View File

@ -0,0 +1,8 @@
PySocks==1.7.1
pytz>2021.0
httpx==0.22.0
h2==4.1.0
socksio==1.0.0
brotli==1.0.9
#requests>2.25.0,!=2.27.0
#requests-toolbelt==0.9.1

View File

@ -1,22 +0,0 @@
bump2version==1.0.1
coverage==5.5
edx-sphinx-theme==3.0.0
flake8==3.9.2
ipython>=7.26.0
jedi!=0.18.0
isort==5.9.3
pip==21.2.4
pre-commit==2.14.0
pur==5.4.2
PyInstaller==4.5.1
PySocks==1.7.1
pytest==6.2.4
pytz==2021.1
requests==2.26.0
requests-toolbelt==0.9.1
responses==0.13.4
setuptools==57.4.0
Sphinx==4.1.2
twine==3.4.2
wheel==0.37.0
black==21.7b0

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.26.0
current_version = 0.30.0.4
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
@ -19,8 +19,8 @@ universal = 1
[flake8]
exclude = docs,.git,log,debug,venv
line_length = 140
max-line-length = 140
line_length = 120
max-line-length = 120
ignore = D100,D101,D102,D103,E203
[pycodestyle]
@ -29,7 +29,7 @@ max-line-length = 140
exclude = .git,log,debug,venv, build
[mypy]
python_version = 3.8
python_version = 3.9
check_untyped_defs = True
ignore_missing_imports = False
warn_unused_ignores = True
@ -38,4 +38,4 @@ warn_unused_configs = True
[isort]
multi_line_output = 2
line_length = 140
line_length = 120

View File

@ -11,18 +11,17 @@ with open("README.rst") as readme_file:
with open("HISTORY.rst") as history_file:
history = history_file.read()
requirements = [
"PySocks==1.7.1",
"pytz==2021.1",
"requests==2.26.0",
"requests-toolbelt==0.9.1",
]
with open("requirements.txt") as requirements_file:
requirements = requirements_file.read()
requirements = requirements.split()
setup_requirements = []
test_requirements = [
"pytest==6.2.4",
]
with open("requirements-tests.txt") as test_req_file:
test_requirements = test_req_file.read()
test_requirements = [
line.strip() for line in test_requirements.split() if line.strip()[:2].strip() not in ("#", "-r")
]
setup(
author="Eriks Karls",
@ -30,7 +29,7 @@ setup(
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Natural Language :: English",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
@ -39,7 +38,7 @@ setup(
description="Python package for automated eRepublik playing",
entry_points={},
install_requires=requirements,
license="MIT license",
license="GPLv3",
long_description=readme + "\n\n" + history,
include_package_data=True,
keywords="erepublik",
@ -50,6 +49,6 @@ setup(
test_suite="tests",
tests_require=test_requirements,
url="https://github.com/eeriks/erepublik/",
version="0.26.0",
version="0.30.0.4",
zip_safe=False,
)

View File

@ -3,10 +3,10 @@
"""Tests for `erepublik` package."""
from erepublik import Citizen
import unittest
from erepublik import Citizen
class TestErepublik(unittest.TestCase):
"""Tests for `erepublik` package."""
@ -26,111 +26,114 @@ class TestErepublik(unittest.TestCase):
self.citizen.energy.energy = 1000
self.assertFalse(self.citizen.should_do_levelup)
def test_should_travel_to_fight(self):
self.citizen.config.always_travel = True
self.assertTrue(self.citizen.should_travel_to_fight())
self.citizen.config.always_travel = False
self.assertFalse(self.citizen.should_travel_to_fight())
self.citizen.energy.energy = 5960
self.citizen.energy.interval = 30
self.citizen.energy.limit = 6000
self.citizen.details.xp = 14850
self.assertTrue(self.citizen.should_travel_to_fight())
self.citizen.details.xp = 15000
self.citizen.energy.energy = 5000
self.assertFalse(self.citizen.should_travel_to_fight())
self.citizen.energy.energy = 5910
self.assertTrue(self.citizen.should_travel_to_fight())
self.citizen.energy.energy = 5900
self.assertFalse(self.citizen.should_travel_to_fight())
# self.citizen.next_reachable_energy and self.citizen.config.next_energy
self.citizen.config.next_energy = True
self.citizen.energy.limit = 10000
self.citizen.details.next_pp = [5000, 5250, 5750, 6250, 6750]
self.citizen.details.pp = 4900
self.citizen.energy.energy = 8510
self.assertEqual(self.citizen.next_reachable_energy, 850)
self.citizen.energy.energy = 8490
self.assertTrue(self.citizen.should_travel_to_fight())
self.assertEqual(self.citizen.next_reachable_energy, 350)
self.citizen.energy.energy = 250
self.assertFalse(self.citizen.should_travel_to_fight())
self.assertEqual(self.citizen.next_reachable_energy, 0)
def test_should_fight(self):
def is_wc_close():
return self.citizen.max_time_till_full_ff > self.citizen.time_till_week_change
self.citizen.config.fight = False
self.assertEqual(self.citizen.should_fight(), (0, "Fighting not allowed!", False))
self.citizen.config.fight = True
# Level up
self.citizen.energy.limit = 3000
self.citizen.details.xp = 24705
if not is_wc_close:
self.assertEqual(self.citizen.should_fight(), (0, "Level up", False))
self.citizen.energy.energy = 5950
self.citizen.energy.interval = 30
self.assertEqual(self.citizen.should_fight(), (900, "Level up", True))
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), (900, "Level up", True))
self.citizen.my_companies.ff_lockdown = 0
# Level up reachable
self.citizen.details.xp = 24400
self.assertEqual(self.citizen.should_fight(), (305, "Fighting for close Levelup. Doing 305 hits", True))
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), (305, "Fighting for close Levelup. Doing 305 hits", True))
self.citizen.my_companies.ff_lockdown = 0
self.citizen.details.xp = 21000
self.assertEqual(self.citizen.should_fight(), (75, "Obligatory fighting for at least 75pp", True))
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), (75, "Obligatory fighting for at least 75pp", True))
self.citizen.my_companies.ff_lockdown = 0
self.citizen.details.pp = 80
# All-in (type = all-in and full ff)
self.citizen.config.all_in = True
self.assertEqual(self.citizen.should_fight(), (595, "Fighting all-in. Doing 595 hits", False))
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(
self.citizen.should_fight(),
(435, "Fight count modified (old count: 595 | FF: 595 | WAM ff_lockdown: 160 | New count: 435)", False),
)
self.citizen.my_companies.ff_lockdown = 0
self.citizen.config.air = True
self.citizen.energy.energy = 4000
self.assertEqual(self.citizen.should_fight(), (400, "Fighting all-in in AIR. Doing 400 hits", False))
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(
self.citizen.should_fight(),
(240, "Fight count modified (old count: 400 | FF: 400 | WAM ff_lockdown: 160 | New count: 240)", False),
)
self.citizen.my_companies.ff_lockdown = 0
self.citizen.config.all_in = False
self.citizen.config.next_energy = True
self.citizen.energy.limit = 10000
self.citizen.details.next_pp = [100, 150, 250, 400, 500]
self.assertEqual(self.citizen.should_fight(), (320, "Fighting for +1 energy. Doing 320 hits", False))
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(
self.citizen.should_fight(),
(160, "Fight count modified (old count: 320 | FF: 400 | WAM ff_lockdown: 160 | New count: 160)", False),
)
self.citizen.my_companies.ff_lockdown = 0
self.citizen.energy.limit = 3000
self.citizen.details.next_pp = [19250, 20000]
self.citizen.config.next_energy = False
# 1h worth of energy
self.citizen.energy.energy = 5910
self.assertEqual(self.citizen.should_fight(), (30, "Fighting for 1h energy. Doing 30 hits", True))
# def deprecated_test_should_travel_to_fight(self):
# self.citizen.config.always_travel = True
# self.assertTrue(self.citizen.should_travel_to_fight())
# self.citizen.config.always_travel = False
# self.assertFalse(self.citizen.should_travel_to_fight())
#
# self.citizen.energy.energy = 5960
# self.citizen.energy.interval = 30
# self.citizen.energy.limit = 6000
# self.citizen.details.xp = 14850
# self.assertTrue(self.citizen.should_travel_to_fight())
# self.citizen.details.xp = 15000
# self.citizen.energy.energy = 5000
# self.assertFalse(self.citizen.should_travel_to_fight())
#
# self.citizen.energy.energy = 5910
# self.assertTrue(self.citizen.should_travel_to_fight())
# self.citizen.energy.energy = 5900
# self.assertFalse(self.citizen.should_travel_to_fight())
#
# # self.citizen.next_reachable_energy and self.citizen.config.next_energy
# self.citizen.config.next_energy = True
# self.citizen.energy.limit = 10000
# self.citizen.details.next_pp = [5000, 5250, 5750, 6250, 6750]
# self.citizen.details.pp = 4900
# self.citizen.energy.energy = 8510
# self.assertEqual(self.citizen.next_reachable_energy, 850)
# self.citizen.energy.energy = 8490
# self.assertTrue(self.citizen.should_travel_to_fight())
# self.assertEqual(self.citizen.next_reachable_energy, 350)
# self.citizen.energy.energy = 250
# self.assertFalse(self.citizen.should_travel_to_fight())
# self.assertEqual(self.citizen.next_reachable_energy, 0)
#
# def deprecated_test_should_fight(self):
# def is_wc_close():
# return self.citizen.max_time_till_full_ff > self.citizen.time_till_week_change
#
# self.citizen.config.fight = False
# self.assertEqual(self.citizen.should_fight(), (0, "Fighting not allowed!", False))
#
# self.citizen.config.fight = True
#
# # Level up
# self.citizen.energy.limit = 3000
# self.citizen.details.xp = 24705
# if not is_wc_close:
# self.assertEqual(self.citizen.should_fight(), (0, "Level up", False))
#
# self.citizen.energy.energy = 5950
# self.citizen.energy.interval = 30
# self.assertEqual(self.citizen.should_fight(), (900, "Level up", True))
# self.citizen.my_companies.ff_lockdown = 160
# self.assertEqual(self.citizen.should_fight(), (900, "Level up", True))
# self.citizen.my_companies.ff_lockdown = 0
#
# # Level up reachable
# self.citizen.details.xp = 24400
# self.assertEqual(self.citizen.should_fight(), (305, "Fighting for close Levelup. Doing 305 hits", True))
# self.citizen.my_companies.ff_lockdown = 160
# self.assertEqual(self.citizen.should_fight(), (305, "Fighting for close Levelup. Doing 305 hits", True))
# self.citizen.my_companies.ff_lockdown = 0
#
# self.citizen.details.xp = 21000
# self.assertEqual(self.citizen.should_fight(), (75, "Obligatory fighting for at least 75pp", True))
# self.citizen.my_companies.ff_lockdown = 160
# self.assertEqual(self.citizen.should_fight(), (75, "Obligatory fighting for at least 75pp", True))
# self.citizen.my_companies.ff_lockdown = 0
# self.citizen.details.pp = 80
#
# # All-in (type = all-in and full ff)
# self.citizen.config.all_in = True
# self.assertEqual(self.citizen.should_fight(), (595, "Fighting all-in. Doing 595 hits", False))
# self.citizen.my_companies.ff_lockdown = 160
# self.assertEqual(
# self.citizen.should_fight(),
# (435, "Fight count modified (old count: 595 | FF: 595
# | WAM ff_lockdown: 160 | New count: 435)", False),
# )
# self.citizen.my_companies.ff_lockdown = 0
#
# self.citizen.config.air = True
# self.citizen.energy.energy = 4000
# self.assertEqual(self.citizen.should_fight(), (400, "Fighting all-in in AIR. Doing 400 hits", False))
# self.citizen.my_companies.ff_lockdown = 160
# self.assertEqual(
# self.citizen.should_fight(),
# (240, "Fight count modified (old count: 400 | FF: 400
# | WAM ff_lockdown: 160 | New count: 240)", False),
# )
# self.citizen.my_companies.ff_lockdown = 0
# self.citizen.config.all_in = False
#
# self.citizen.config.next_energy = True
# self.citizen.energy.limit = 10000
# self.citizen.details.next_pp = [100, 150, 250, 400, 500]
# self.assertEqual(self.citizen.should_fight(), (320, "Fighting for +1 energy. Doing 320 hits", False))
# self.citizen.my_companies.ff_lockdown = 160
# self.assertEqual(
# self.citizen.should_fight(),
# (160, "Fight count modified (old count: 320 | FF: 400
# | WAM ff_lockdown: 160 | New count: 160)", False),
# )
# self.citizen.my_companies.ff_lockdown = 0
# self.citizen.energy.limit = 3000
# self.citizen.details.next_pp = [19250, 20000]
# self.citizen.config.next_energy = False
#
# # 1h worth of energy
# self.citizen.energy.energy = 5910
# self.assertEqual(self.citizen.should_fight(), (30, "Fighting for 1h energy. Doing 30 hits", True))