Compare commits

...

21 Commits

Author SHA1 Message Date
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
20 changed files with 274 additions and 214 deletions

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 runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: [3.7, 3.8] python-version: [3.8, 3.9]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

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 $ 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/ $ cd erepublik/
$ python3 -m venv venv
$ python setup.py develop $ python setup.py develop
4. Create a branch for local development:: 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 5. When you're done making changes, check that your changes pass flake8, isort and the
tests:: tests::
$ flake8 erepublik tests $ make lint
$ isort erepublik
$ python setup.py test $ python setup.py test
To get flake8 and isort, just pip install them into your virtualenv. 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). Make sure all your changes are committed (including an entry in HISTORY.rst).
Then run:: Then run::
$ bumpversion patch # possible: major / minor / patch $ bumpversion patch # possible: major / minor / patch / dev
$ git push $ git push
$ git push --tags $ 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 rm -fr .pytest_cache
lint: ## check style with flake8 lint: ## check style with flake8
black erepublik tests isort erepublik examples tests
flake8 erepublik tests black erepublik examples tests
flake8 erepublik examples tests
test: ## run tests quickly with the default Python test: ## run tests quickly with the default Python
python -m unittest python -m unittest

View File

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

View File

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

View File

@ -8,12 +8,10 @@
<meta name="generator" content="Jekyll v4.0.1"> <meta name="generator" content="Jekyll v4.0.1">
<title>eBot configuration</title> <title>eBot configuration</title>
<!-- CSS only --> <!-- 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 --> <!-- 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://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.6.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" 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>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
@ -120,57 +118,57 @@
</div> </div>
<div class="col-12 col-sm-6"> <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="form-group">
<div class="custom-control custom-switch"> <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> <label class="custom-control-label" for="fight">Fight</label>
</div> </div>
<div class="custom-control custom-switch custom-control-inline"> <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> <label class="custom-control-label" for="air">Air</label>
</div> </div>
<div class="custom-control custom-switch custom-control-inline"> <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> <label class="custom-control-label" for="ground">Ground</label>
</div> </div>
<div class="custom-control custom-switch"> <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> <label class="custom-control-label" for="boosters">Use ground boosters</label>
</div> </div>
<div class="custom-control custom-switch"> <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> <label class="custom-control-label" for="continuous_fighting">Continue fighting all FF in round</label>
</div> </div>
<div class="custom-control custom-switch"> <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> <label class="custom-control-label" for="next_energy">Fight for next WC +1hp/6min if reachable by FF</label>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="form-check form-check-inline"> <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> <label class="form-check-label" for="all_in">All energy</label>
</div> </div>
<div class="form-check form-check-inline"> <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> <label class="form-check-label" for="h_energy">1h energy</label>
</div> </div>
</div> </div>
<div class="custom-control custom-switch"> <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> <label class="custom-control-label" for="rw_def_side">In RWs fight on right side (occupier/defender)</label>
</div> </div>
<div class="custom-control custom-switch"> <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> <label class="custom-control-label" for="travel_to_fight">Travel to fight</label>
</div> </div>
<div class="custom-control custom-switch"> <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> <label class="custom-control-label" for="epic_hunt">Hunt epics</label>
</div> </div>
<div class="custom-control custom-switch"> <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> <label class="custom-control-label" for="epic_hunt_ebs">Spend <small>[all]</small> EBs in epics</label>
</div> </div>
</div> </div>
@ -262,7 +260,13 @@
</form> </form>
</div> </div>
<div class="col-12"> <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> </div>
</div> </div>

View File

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

View File

@ -187,15 +187,15 @@ class CitizenBaseAPI:
) -> Response: ) -> Response:
c = [cookie.name for cookie in self._req.cookies if not cookie.has_nonstandard_attr("HttpOnly")] 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) env = dict(l=["tets"], s=[], c=c, m=0)
cookies = dict(
sh=hashlib.sha256(",".join(env["l"] + env["s"]).encode("utf8")).hexdigest(), session_hash = hashlib.sha256(",".join(env["l"] + env["s"]).encode("utf8")).hexdigest()
ch=hashlib.sha256(",".join(env["c"]).encode("utf8")).hexdigest(), cookies_hash = hashlib.sha256(",".join(env["c"]).encode("utf8")).hexdigest()
)
cookie_kwargs = dict( 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("sh", session_hash, **cookie_kwargs)
self._req.cookies.set("ch", cookies["ch"], **cookie_kwargs) self._req.cookies.set("ch", cookies_hash, **cookie_kwargs)
b64_env = utils.b64json(env) b64_env = utils.b64json(env)
data = dict( data = dict(
_token=self.token, _token=self.token,

View File

@ -7,7 +7,7 @@ from decimal import Decimal
from itertools import product from itertools import product
from threading import Event from threading import Event
from time import sleep from time import sleep
from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, Union from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, TypedDict, Union
from requests import HTTPError, RequestException, Response from requests import HTTPError, RequestException, Response
@ -180,12 +180,21 @@ class BaseCitizen(access_points.CitizenAPI):
ugly_js_match = re.search(r'"promotions":\s*(\[{?.*?}?])', html) ugly_js_match = re.search(r'"promotions":\s*(\[{?.*?}?])', html)
ugly_js = ugly_js_match.group(1) if ugly_js_match else "null" ugly_js = ugly_js_match.group(1) if ugly_js_match else "null"
promos = utils.json_loads(utils.normalize_html_json(ugly_js)) promos = utils.json_loads(utils.normalize_html_json(ugly_js))
self.promos = {} if self.promos:
if promos: self.promos = {k: v for k, v in self.promos.items() if v > self.now}
self.promos = {k: v for k, v in promos.items() if v > self.now} else:
for kind, time_until in self.promos.items(): self.promos = {}
self.reporter.report_promo(kind, time_until)
try:
if promos:
for promo in promos:
kind = promo["typeId"]
time_until = utils.localize_timestamp(promo["expiresAt"])
if kind not in self.promos:
self.reporter.report_promo(kind, time_until)
self.promos[kind] = time_until
except Exception: # noqa
self.report_error()
new_date = re.search(r"var new_date = '(\d*)';", html) new_date = re.search(r"var new_date = '(\d*)';", html)
if new_date: if new_date:
self.energy.set_reference_time(utils.good_timedelta(self.now, timedelta(seconds=int(new_date.group(1))))) self.energy.set_reference_time(utils.good_timedelta(self.now, timedelta(seconds=int(new_date.group(1)))))
@ -270,20 +279,22 @@ class BaseCitizen(access_points.CitizenAPI):
return self._post_main_session_get_challenge(captcha_id, image_id).json() return self._post_main_session_get_challenge(captcha_id, image_id).json()
def solve_captcha(self, src: str) -> Optional[List[Dict[str, int]]]: def solve_captcha(self, src: str) -> Optional[List[Dict[str, int]]]:
class _API_RESULT(dict): class ApiResult(TypedDict):
x: int x: int
y: int y: int
class _API_RETURN(dict): class ApiReturn(TypedDict):
status: bool status: bool
message: str message: str
result: Optional[List[_API_RESULT]] result: Optional[List[ApiResult]]
solve_data: _API_RETURN = self.post( solve_data = ApiReturn(
"https://api.erep.lv/captcha/api", data=dict(citizen_id=self.details.citizen_id, src=src, key="CaptchaDevAPI") **self.post(
).json() "https://erep.lv/captcha/api", data=dict(citizen_id=self.details.citizen_id, src=src, password="CaptchaDevAPI")
).json()
)
if solve_data["status"]: if solve_data["status"]:
return solve_data.get("result") return solve_data["result"]
@property @property
def inventory(self) -> classes.Inventory: def inventory(self) -> classes.Inventory:
@ -470,12 +481,8 @@ class BaseCitizen(access_points.CitizenAPI):
else: else:
icon = "//www.erepublik.net/" + item_data["icon"] icon = "//www.erepublik.net/" + item_data["icon"]
raw_materials[constants.INDUSTRIES[item_data.get("industryId")]].update( raw_materials[constants.INDUSTRIES[item_data.get("industryId")]][0] = dict(
{ name=item_data.get("name"), amount=item_data["amount"] + (item_data.get("underCostruction", 0) / 100), icon=icon
0: dict(
name=item_data.get("name"), amount=item_data["amount"] + (item_data.get("underCostruction", 0) / 100), icon=icon
)
}
) )
offers: Dict[str, Dict[int, Dict[str, Union[str, int]]]] = {} offers: Dict[str, Dict[int, Dict[str, Union[str, int]]]] = {}
@ -783,7 +790,7 @@ class BaseCitizen(access_points.CitizenAPI):
reward = reward[:-1] reward = reward[:-1]
if (title, reward) not in data: if (title, reward) not in data:
data[(title, reward)] = dict(about=about, kind=title, reward=reward, count=1, currency=currency) data[(title, reward)] = dict(about=about, kind=title, reward=reward or 0, count=1, currency=currency)
else: else:
data[(title, reward)]["count"] += 1 data[(title, reward)]["count"] += 1
except AttributeError: except AttributeError:
@ -864,8 +871,8 @@ class BaseCitizen(access_points.CitizenAPI):
if re.search( if re.search(
r"Occasionally there are a couple of things which we need to check or to implement in order make " r"Occasionally there are a couple of things which we need to check or to implement in order make "
r"your experience in eRepublik more pleasant. <strong>Don\'t worry about ongoing battles, timer " r"your experience in eRepublik more pleasant\. <strong>Don't worry about ongoing battles, timer "
r"will be stopped during maintenance.</strong>", r"will be stopped during maintenance\.</strong>|Maintenance\. We&rsquo;ll be back any second now\.",
response.text, response.text,
): ):
self.write_warning("eRepublik is having maintenance. Sleeping for 5 mi#nutes") self.write_warning("eRepublik is having maintenance. Sleeping for 5 mi#nutes")
@ -1588,6 +1595,12 @@ class CitizenEconomy(CitizenTravel):
if self.config.telegram: if self.config.telegram:
self.telegram.report_item_donation(citizen_id, amount, f"{industry} q{quality}") self.telegram.report_item_donation(citizen_id, amount, f"{industry} q{quality}")
def _update_inventory_data(self, *args, **kwargs):
super()._update_inventory_data(*args, **kwargs)
if self.food["total"] < 240 * self.energy.interval:
self.buy_food()
class CitizenLeaderBoard(BaseCitizen): class CitizenLeaderBoard(BaseCitizen):
def get_aircraft_damage_rankings(self, country: int, weeks: int = 0, mu: int = 0) -> Dict[str, any]: def get_aircraft_damage_rankings(self, country: int, weeks: int = 0, mu: int = 0) -> Dict[str, any]:
@ -1993,7 +2006,7 @@ class CitizenMilitary(CitizenTravel):
energy_used = 0 energy_used = 0
if deployment_id: if deployment_id:
self.write_warning( self.write_warning(
"If erepublik responds with HTTP 500 Internal Error, it is kind of ok, because deployment has not finished yet." "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() deployment_data = self._post_military_fight_deploy_deploy_report_data(deployment_id).json()
if not deployment_data.get("error"): if not deployment_data.get("error"):
@ -2296,7 +2309,7 @@ class CitizenMilitary(CitizenTravel):
type="damage", type="damage",
) )
r_json = r.json() r_json = r.json()
return (r_json.get(str(battle.invader.id)).get("fighterData"), r_json.get(str(battle.defender.id)).get("fighterData")) 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]: def get_battle_division_stats(self, division: classes.BattleDivision) -> Dict[str, Any]:
battle = division.battle battle = division.battle
@ -2616,10 +2629,9 @@ class CitizenTasks(CitizenEconomy):
else: else:
self.reporter.report_action("WORK", json_val=js) self.reporter.report_action("WORK", json_val=js)
else: else:
if self.energy.food_fights < 1: seconds = self.now.timestamp() % 360
seconds = (self.energy.reference_time - self.now).total_seconds() self.write_warning(f"I don't have energy to work. Will sleep for {seconds}s")
self.write_warning(f"I don't have energy to work. Will sleep for {seconds}s") self.sleep(seconds)
self.sleep(seconds)
self.work() self.work()
def train(self): def train(self):
@ -2643,11 +2655,9 @@ class CitizenTasks(CitizenEconomy):
else: else:
self.reporter.report_action("TRAIN", response.json()) self.reporter.report_action("TRAIN", response.json())
else: else:
if self.energy.food_fights < len(tgs): seconds = self.now.timestamp() % 360
large = max(self.energy.reference_time, self.now) self.write_warning(f"I don't have energy to train. Will sleep for {seconds}s")
sleep_seconds = utils.get_sleep_seconds(large) self.sleep(seconds)
self.write_warning(f"I don't have energy to train. Will sleep for {sleep_seconds} seconds")
self.sleep(sleep_seconds)
self.train() self.train()
def work_ot(self): def work_ot(self):
@ -2665,11 +2675,9 @@ class CitizenTasks(CitizenEconomy):
self.buy_food(120) self.buy_food(120)
self.reporter.report_action("WORK_OT", r.json()) self.reporter.report_action("WORK_OT", r.json())
elif self.energy.food_fights < 1 and self.ot_points >= 24: elif self.energy.food_fights < 1 and self.ot_points >= 24:
if self.energy.food_fights < 1: seconds = self.now.timestamp() % 360
large = max(self.energy.reference_time, self.now) self.write_warning(f"I don't have energy to work OT. Will sleep for {seconds}s")
sleep_seconds = utils.get_sleep_seconds(large) self.sleep(seconds)
self.write_warning(f"I don't have energy to work OT. Will sleep for {sleep_seconds}s")
self.sleep(sleep_seconds)
self.work_ot() self.work_ot()
def resign_from_employer(self) -> bool: def resign_from_employer(self) -> bool:
@ -2791,15 +2799,18 @@ class _Citizen(
if award_id and title and medal.get("details").get("isWallMaterial"): if award_id and title and medal.get("details").get("isWallMaterial"):
self._post_main_wall_post_automatic(title.lower(), award_id) self._post_main_wall_post_automatic(title.lower(), award_id)
if params.get("ccValue"): if "ccValue" in params:
reward = params.get("ccValue") reward = params.get("ccValue") or 0
currency = "Currency" currency = "Currency"
elif params.get("goldValue"): elif "goldValue" in params:
reward = params.get("goldValue") reward = params.get("goldValue") or 0
currency = "Gold" currency = "Gold"
else: elif "energyValue" in params:
reward = params.get("energyValue") reward = params.get("energyValue") or 0
currency = "Energy" currency = "Energy"
else:
reward = 0
currency = "Unknown"
if (title, reward) not in data: if (title, reward) not in data:
data[(title, reward)] = { data[(title, reward)] = {
@ -2836,8 +2847,8 @@ class _Citizen(
self.update_companies() self.update_companies()
self.update_money() self.update_money()
self.update_weekly_challenge() self.update_weekly_challenge()
self.send_state_update()
self.check_for_notification_medals() self.check_for_notification_medals()
self.send_state_update()
def update_weekly_challenge(self): def update_weekly_challenge(self):
data = self._get_main_weekly_challenge_data().json() data = self._get_main_weekly_challenge_data().json()
@ -2902,11 +2913,12 @@ class _Citizen(
start_time = utils.good_timedelta(start_time.replace(minute=0), timedelta(hours=1)) start_time = utils.good_timedelta(start_time.replace(minute=0), timedelta(hours=1))
while not self.stop_threads.is_set(): while not self.stop_threads.is_set():
start_time = utils.good_timedelta(start_time, timedelta(minutes=10 if self.restricted_ip else 30)) start_time = utils.good_timedelta(start_time, timedelta(minutes=10 if self.restricted_ip else 30))
self.update_citizen_info() try:
self.update_weekly_challenge() self.update_all()
self.send_state_update() except classes.CaptchaSessionError:
self.send_state_update()
pass
self.send_inventory_update() self.send_inventory_update()
self.update_companies()
self.send_my_companies_update() self.send_my_companies_update()
sleep_seconds = (start_time - self.now).total_seconds() sleep_seconds = (start_time - self.now).total_seconds()
self.stop_threads.wait(sleep_seconds if sleep_seconds > 0 else 0) self.stop_threads.wait(sleep_seconds if sleep_seconds > 0 else 0)

View File

@ -697,7 +697,7 @@ class Reporter:
def __init__(self, citizen): def __init__(self, citizen):
self._citizen = weakref.ref(citizen) self._citizen = weakref.ref(citizen)
self._req = Session() self._req = Session()
self.url = "https://api.erep.lv" self.url = "https://erep.lv"
self._req.headers.update( self._req.headers.update(
{ {
"user-agent": "eRepublik Script Reporter v3", "user-agent": "eRepublik Script Reporter v3",

View File

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

View File

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

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.1.1
edx-sphinx-theme==3.0.0
flake8==4.0.1
ipython>=7.29.0
jedi!=0.18.0
isort==5.9.3
pre-commit==2.15.0
pur==5.4.2
responses==0.15.0
Sphinx==4.2.0
twine==3.4.2
wheel==0.37.0
black==21.7b0

2
requirements-tests.txt Normal file
View File

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

4
requirements.txt Normal file
View File

@ -0,0 +1,4 @@
PySocks==1.7.1
pytz==2021.3
requests==2.26.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] [bumpversion]
current_version = 0.26.0 current_version = 0.27.2
commit = True commit = True
tag = True tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)? parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
@ -29,7 +29,7 @@ max-line-length = 140
exclude = .git,log,debug,venv, build exclude = .git,log,debug,venv, build
[mypy] [mypy]
python_version = 3.8 python_version = 3.9
check_untyped_defs = True check_untyped_defs = True
ignore_missing_imports = False ignore_missing_imports = False
warn_unused_ignores = True warn_unused_ignores = True

View File

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

View File

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