Compare commits

...

140 Commits

Author SHA1 Message Date
7edfa3b004 Bump version: 0.17.2 → 0.17.3 2019-12-18 11:45:02 +02:00
12aee23739 Variable and method redeclaration 2019-12-18 11:44:18 +02:00
b7f8182ef5 Bump version: 0.17.1 → 0.17.2 2019-12-13 19:32:29 +02:00
39093accd0 Type hinting. Class parameter defined lists where shared accross instances. 2019-12-13 19:30:43 +02:00
aba8c15fd3 AutoPost medals 2019-12-03 15:22:35 +02:00
f294506a2d Updated wars list, added default weapon choosing (q7 - ground, bare hands - air) 2019-12-03 09:52:53 +02:00
fd56c6c389 By default sort battles by time 2019-12-03 09:44:14 +02:00
4f613ee5ac remvoed unused variables 2019-12-03 09:43:55 +02:00
a7dd528597 Citizen get() and post() signature update, check if server isn't compaining about request flooding 2019-12-03 09:42:51 +02:00
24c755d414 code style 2019-12-03 09:41:07 +02:00
13b639dc5a Bump version: 0.17.0 → 0.17.1 2019-11-21 14:04:49 +02:00
ec1141a46e set serialization 2019-11-21 14:04:43 +02:00
77170433c2 Bump version: 0.16.1 → 0.17.0 2019-11-21 11:12:49 +02:00
4736f70203 History update 2019-11-21 11:12:43 +02:00
48a27997ac Bump version: 0.16.0 → 0.16.1 2019-11-21 11:10:15 +02:00
90bec82630 12th anniversary minimal methods 2019-11-21 11:06:29 +02:00
aedfbf4465 12th anniversary endpoints 2019-11-21 10:42:55 +02:00
66f459c692 Inventory structure update 2019-10-30 19:42:05 +02:00
ef27960ff1 no message 2019-10-30 19:35:40 +02:00
c48af9a891 Thread stopping 2019-10-30 18:16:18 +02:00
1abfdb71ac Code cleanup and serialization improvements 2019-10-30 16:55:33 +02:00
e060f67666 More inventory structure updates 2019-10-29 16:06:07 +02:00
06d8d1c0b5 Telegram threading queue has been messing with error reporting 2019-10-29 16:05:22 +02:00
adda8dcb54 Structure requests by year/month/date folders, to keep requests in cleaner format.
The same medal kind (Maverick div BHs) can have different reward value - group them by kind-reward.
Citizen.post bugfix (with no data and json arguments) TODO: Must check where post is called without data or json
2019-10-28 14:17:45 +02:00
c7f084436d Inventory structure update 2019-10-18 18:18:39 +03:00
94a87091a4 Allow full energy reports once every half an hour 2019-10-17 19:15:17 +03:00
c0b97f112d TelegramBot.send_message should always append to send queue 2019-10-16 15:10:38 +03:00
3d895bd085 Damage calculation 2019-10-16 15:09:28 +03:00
d548d1bbf1 Hit calculation can be static 2019-10-15 20:03:38 +03:00
b1eefcc662 CSRF Attack Detecked loop on POST requests. 2019-10-15 20:03:22 +03:00
41798c446c Return successfully transfered item count 2019-10-15 11:28:56 +03:00
074da3adbe Telegram reporter queue bug 2019-10-14 19:21:00 +03:00
6c9a9e920d Delay telegram notification sending by appending multiple messages to queue and after minute of inactivity clear the queue by sending all messages 2019-10-14 13:44:31 +03:00
ffa2fc109c Travel for fighting fixed 2019-10-14 13:14:30 +03:00
f7f4028f32 Revert "Travel for fighting"
This reverts commit 07c8881092.
2019-10-14 13:03:36 +03:00
e91705ce90 no message 2019-10-14 13:03:19 +03:00
ca65a1ffe1 iPython indexer infinite loop and crash 2019-10-14 12:54:59 +03:00
07c8881092 Travel for fighting 2019-10-13 23:50:45 +03:00
25e534f783 Always print should_fight message 2019-10-08 09:32:00 +03:00
daa071f0f5 RTD build image url fixed, setup.py typo 2019-10-08 09:31:09 +03:00
2f8120bd0d Telegram formatting 2019-10-01 09:59:37 +03:00
6b2c073abe Damage booster activisation update and bugfix 2019-10-01 09:59:22 +03:00
c298d66086 Don't allow to fight before WeekChange even if force_fight (levelup, 75pp etc) 2019-10-01 09:58:37 +03:00
bf971972bf History update with version number 2019-09-29 09:47:15 +03:00
50f3d291ca Bump version: 0.15.3 → 0.16.0 2019-09-29 09:45:46 +03:00
b03140f2b8 RTD build image url fixed, setup.py typo 2019-09-29 09:42:24 +03:00
fe9a118875 Telegram loop traceback formatting
Fixed double code execution in Citizen.update_citizen_info(None) calling Citizen.update_citizen_info(html) and executing code twice.
2019-09-29 09:40:25 +03:00
17c73c79a7 Trying to find how and where Telegram spam happens 2019-09-28 16:03:31 +03:00
7533608316 Version updates 2019-09-27 13:51:09 +03:00
71a7f55338 Changes in eRepublik html 2019-09-27 11:50:51 +03:00
0ca0f49f92 bugfix 2019-09-27 11:20:00 +03:00
3f1b0018b2 Misplaced telegram queueing 2019-09-27 11:11:18 +03:00
2aaa4aad65 cleanup 2019-09-27 11:03:22 +03:00
b44a3a4b62 Init update 2019-09-27 11:03:02 +03:00
3a7dd9a6fa Init update 2019-09-27 11:02:26 +03:00
630c7cbc76 Was trying to send messages before citizen has been initialized 2019-09-27 10:26:53 +03:00
1ee47dfdcf Telegram integration 2019-09-26 15:53:21 +03:00
ab34135b73 Telegram integration 2019-09-26 15:44:21 +03:00
20bba4b9f9 Telegram integration 2019-09-26 15:42:16 +03:00
8db4ab1f0f Company sorting for wam: Raw factories, food, weapon, house, air, q7...q1, Final factories, food, weapon, house, air, q7...q1 2019-09-25 09:38:27 +03:00
acbf1590d7 If not enough food for wam - buy food and wam 2019-09-25 09:36:42 +03:00
63dd2d4f77 Optimisation and test cleanup 2019-09-05 15:36:01 +03:00
896b1b2432 Bugfix, cleanup and optimisation 2019-09-05 15:01:54 +03:00
60543e02c8 Merge branch 'master' into tests
* master:
  Update citizen.py
  Update citizen.py
2019-09-03 11:56:00 +03:00
b06e9a2933 Merge branch 'master' of github.com:eeriks/erepublik_script
* 'master' of github.com:eeriks/erepublik_script:
  Update citizen.py
  Update citizen.py
2019-09-03 11:55:44 +03:00
4eb96d7f1e Testing should travel and should fight 2019-09-03 11:55:34 +03:00
c8a1d8c8e8 Update citizen.py
If not employee find new job
2019-09-01 19:58:52 +03:00
c85f0417f2 Update citizen.py
Apply for work bugfix
2019-09-01 19:47:17 +03:00
7573f29950 Freshening up documentation 2019-08-28 17:16:46 +03:00
597d27117c Donating money now return status true or false 2019-08-27 11:16:36 +03:00
95570b7c17 Article voting and endorsing 2019-08-27 11:03:08 +03:00
bc4ba671d6 Endpoint method names renamed to better represent urls 2019-08-27 11:02:52 +03:00
088219a60a Endpoint method names renamed to better represent urls 2019-08-27 10:51:57 +03:00
53266b1c94 Merge branch 'master' of github.com:eeriks/erepublik_script 2019-08-27 10:17:34 +03:00
065a0ff3ec bugfix 2019-08-27 10:13:37 +03:00
c8e90ca910 bugfix 2019-08-27 10:13:14 +03:00
bbab84bf5b Update utils.py 2019-08-26 22:42:06 +03:00
fff17469e0 More verbose Citizen class serialization 2019-08-26 10:06:18 +03:00
0d208a8d32 Add timezone to datetime serialization 2019-08-26 10:05:52 +03:00
86004eb81b Don't serialize password nor email 2019-08-26 10:05:22 +03:00
7e05e35ebf More verbose Object representation in json format 2019-08-26 09:47:04 +03:00
411b158487 Citizen.buy_food() now buys 48h worth of food
When doing WAM and fialing because not enough food - buy food
Integrated new RW side chooser
2019-08-26 09:32:12 +03:00
d6e161d815 Bump version: 0.15.2 → 0.15.3 2019-08-24 15:44:22 +03:00
1285e95cec Inventory usage bugfixes 2019-08-24 10:48:04 +03:00
324db2c136 Fix new division numbering 2019-08-24 10:27:31 +03:00
1c6b41e41b Inventory updates and booster usage updates 2019-08-24 10:03:52 +03:00
a52552afb7 Use MyJSONEncoder from classes 2019-08-22 14:33:45 +03:00
28bfdc7b20 Use country ids from constant in utils 2019-08-22 14:33:07 +03:00
de1b734728 Don't check levelup when eating 2019-08-22 14:32:32 +03:00
fa5646ecfd Receive daily order 2019-08-22 14:32:00 +03:00
339392cfb0 Regex bugfix 2019-08-13 15:44:07 +03:00
7ebba458cb Max time timezones 2019-08-12 14:19:52 +03:00
4f436292af Organisation account fetcher - UNSTABLE 2019-08-12 12:54:58 +03:00
416c391d21 Improved levelup energy management 2019-08-11 01:45:49 +03:00
66c53ef985 Typo 2019-08-11 01:44:45 +03:00
3fa7d097fe Found awards switch to ASCII friendlier multiplier char - ✕ -> x
Localize max div end time
2019-08-05 11:55:39 +03:00
6af9675c39 Added gameTokensMarket endpoint 2019-08-05 10:20:36 +03:00
2343a6c6c8 WeeklyChallange end energy saver tweaks 2019-08-05 10:20:19 +03:00
7e56f01a38 Added gameTokensMarket endpoint 2019-08-05 10:13:48 +03:00
e14cbc18e9 Bump version: 0.15.1 → 0.15.2 2019-08-01 17:24:07 +03:00
048ce798dd All battle div div containing dicts where reference to the same object 2019-08-01 17:23:24 +03:00
e5b7cde044 Bump version: 0.15.0 → 0.15.1 2019-08-01 15:06:34 +03:00
6bbc7a1f64 Typehinting and battle/war stuff - last battle, attackable regions if CP, etc 2019-08-01 15:05:44 +03:00
4eccb339bb Deploy bombs 2019-08-01 15:04:16 +03:00
dbeb6e9ba5 Market scraper updates 2019-08-01 09:37:26 +03:00
9c9bb5ae40 Region attacking post updated 2019-08-01 09:37:08 +03:00
d8eb69f82a Travel bugfix and market scraper fixup 2019-07-31 22:39:54 +03:00
42c430213f Minor updates 2019-07-31 21:41:24 +03:00
39dbcaa27d Continuing work on return Response free Citizen class 2019-07-31 21:41:08 +03:00
8911adb81c Traveling improved for Citizen class 2019-07-31 21:38:34 +03:00
7927c162f8 More precise type hint 2019-07-31 21:37:29 +03:00
92b7c45a7d Report promos to erep.lv 2019-07-31 21:36:57 +03:00
53257487d8 Write on country wall update 2019-07-30 18:32:12 +03:00
8690c4d3f2 minor fix 2019-07-30 11:10:35 +03:00
43c6bce160 minor fix 2019-07-30 11:10:09 +03:00
c4f598c1ba Bump version: 0.14.7 → 0.15.0 2019-07-30 10:22:38 +03:00
c48d90dec3 First step towards removing manual response parsing in Citizen class 2019-07-30 10:22:29 +03:00
953902476f Bump version: 0.14.6.1 → 0.14.7 2019-07-30 09:32:10 +03:00
156cbbb61c Typehinting changes 2019-07-30 09:32:03 +03:00
b72039c865 PyPi needs new version number, even if accidentaly published version has been removed 2019-07-29 15:17:14 +03:00
9587538fdc Merge branch 'master' of github.com:eeriks/erepublik_script
* 'master' of github.com:eeriks/erepublik_script:
  Bump version: 0.14.5 → 0.14.6
2019-07-29 13:54:09 +03:00
e775679581 Bump version: 0.14.5 → 0.14.6 2019-07-29 13:53:17 +03:00
3aa305ea74 Switch from python-slugify to self hosted (Taken from Django 2.2.1 django.utils.text.slugify) 2019-07-29 13:52:33 +03:00
6b2e5ffb68 Bump version: 0.14.5 → 0.14.6 2019-07-29 13:33:02 +03:00
bb8634fe56 After deleting debug directory next request failed if SlowRequests.debug == True 2019-07-29 13:31:07 +03:00
25a0d8993e eRepublikAPI now has it's own token property 2019-07-29 13:19:19 +03:00
a1d10bb427 Bump version: 0.14.4 → 0.14.5 2019-07-23 17:55:08 +03:00
ec701396d9 project name migration 2019-07-23 17:55:04 +03:00
d8035b42e3 Underline too short 2019-07-23 17:53:00 +03:00
42320a14a4 no message 2019-07-23 17:50:14 +03:00
de4b059b7d Package name update: erepublik_script → eRepublik 2019-07-23 16:41:21 +03:00
dc106cc87d Bump version: 0.14.3 → 0.14.4 2019-07-23 14:37:35 +03:00
bb2c13d63a requirement update 2019-07-23 14:37:26 +03:00
ea48fbe7e1 Wall post comment creation endpoints 2019-07-23 14:37:07 +03:00
65a3a9f678 Possible memory leak addressed 2019-07-23 14:36:27 +03:00
0757345e17 . 2019-07-23 14:34:49 +03:00
6f4b32b12c code cleanup 2019-07-22 11:54:33 +03:00
69265a35e8 Bump version: 0.14.2 → 0.14.3 2019-07-22 11:03:44 +03:00
f12bd0ed57 requirements 2019-07-22 11:03:14 +03:00
2d246cbf4b Continuation from previous project's version 2019-07-19 11:06:58 +03:00
40 changed files with 2723 additions and 18513 deletions

View File

@ -1,6 +1,6 @@
* eRepublik script version:
* Python version:
* Operating System:
* eRepublik script version:
* Python version:
* Operating System:
### Description
@ -9,7 +9,7 @@ Tell us what happened, what went wrong, and what you expected to happen.
### What I Did
```
``` python
Paste the command(s) you ran and the output.
If there was a crash, please include the traceback here.
```

View File

@ -2,10 +2,8 @@
language: python
python:
- 3.7
- 3.6
- 3.5
- 3.4
- 2.7
# Command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
install: pip install -U tox-travis

View File

@ -15,7 +15,7 @@ Types of Contributions
Report Bugs
~~~~~~~~~~~
Report bugs at https://github.com/eeriks/erepublik_script/issues.
Report bugs at https://github.com/eeriks/erepublik/issues.
If you are reporting a bug, please include:
@ -45,7 +45,7 @@ articles, and such.
Submit Feedback
~~~~~~~~~~~~~~~
The best way to send feedback is to file an issue at https://github.com/eeriks/erepublik_script/issues.
The best way to send feedback is to file an issue at https://github.com/eeriks/erepublik/issues.
If you are proposing a feature:
@ -57,17 +57,17 @@ If you are proposing a feature:
Get Started!
------------
Ready to contribute? Here's how to set up `erepublik_script` for local development.
Ready to contribute? Here's how to set up `erepublik` for local development.
1. Fork the `erepublik_script` repo on GitHub.
2. Clone your fork locally::
$ git clone git@github.com:your_name_here/erepublik_script.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::
$ mkvirtualenv erepublik_script
$ cd erepublik_script/
$ mkvirtualenv erepublik
$ cd erepublik/
$ python setup.py develop
4. Create a branch for local development::
@ -79,7 +79,7 @@ Ready to contribute? Here's how to set up `erepublik_script` for local developme
5. When you're done making changes, check that your changes pass flake8 and the
tests, including testing other Python versions with tox::
$ flake8 erepublik_script tests
$ flake8 erepublik tests
$ python setup.py test or py.test
$ tox
@ -102,7 +102,7 @@ Before you submit a pull request, check that it meets these guidelines:
2. If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in README.rst.
3. The pull request should work for Python 2.7, 3.4, 3.5 and 3.6, and for PyPy. Check
3. The pull request should work for Python 3.7.1. Check
https://travis-ci.org/eeriks/erepublik_script/pull_requests
and make sure that the tests pass for all supported Python versions.

View File

@ -2,6 +2,45 @@
History
=======
0.17.0 (2019-11-21)
-------------------
* 12th anniversary's endpoints added
* Telegram message queue optimisation
* WC end fighting energy bugfix
* More strict fighting limiting before week change
* Improved and fixed ground damage booster usage
0.16.0 (2019-09-29)
-------------------
* Telegram notification integration
* Improved serialization to JSON
* When failing to do WAM because of not enough food - buy food
* Buy food buys 48h worth instead of 24h energy
0.15.3 (2019-08-24)
-------------------
* Update after eRepublik changed campaign apis
0.15.0 (2019-07-30)
-------------------
* CitizenAPI class methods renamed to "private", they are intended to be used internally.
* TODO: None of the Citizen class's methods should return Response object - CitizenAPI is meant for that.
0.14.4 (2019-07-23)
-------------------
* Wall post comment endpoints updated with comment create endpoints.
0.1.0 (2019-07-19)
------------------

View File

@ -51,7 +51,7 @@ clean-test: ## remove test and coverage artifacts
rm -fr .pytest_cache
lint: ## check style with flake8
flake8 erepublik_script tests
flake8 erepublik tests
test: ## run tests quickly with the default Python
python setup.py test
@ -60,15 +60,15 @@ test-all: ## run tests on every Python version with tox
tox
coverage: ## check code coverage quickly with the default Python
coverage run --source erepublik_script setup.py test
coverage run --source erepublik setup.py test
coverage report -m
coverage html
$(BROWSER) htmlcov/index.html
docs: ## generate Sphinx HTML documentation, including API docs
rm -f docs/erepublik_script.rst
rm -f docs/erepublik.rst
rm -f docs/modules.rst
sphinx-apidoc -o docs/ erepublik_script
sphinx-apidoc -o docs/ erepublik
$(MAKE) -C docs clean
$(MAKE) -C docs html
$(BROWSER) docs/_build/html/index.html

View File

@ -3,28 +3,19 @@ eRepublik script
================
.. image:: https://img.shields.io/pypi/v/erepublik_script.svg
:target: https://pypi.python.org/pypi/erepublik_script
.. image:: https://img.shields.io/pypi/v/erepublik.svg
:target: https://pypi.python.org/pypi/erepublik
.. image:: https://img.shields.io/travis/eeriks/erepublik_script.svg
:target: https://travis-ci.org/eeriks/erepublik_script
.. image:: https://readthedocs.org/projects/erepublik-script/badge/?version=latest
:target: https://erepublik-script.readthedocs.io/en/latest/?badge=latest
.. image:: https://readthedocs.org/projects/erepublik/badge/?version=latest
:target: https://erepublik.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. image:: https://pyup.io/repos/github/eeriks/erepublik_script/shield.svg
:target: https://pyup.io/repos/github/eeriks/erepublik_script/
:alt: Updates
Python package for eRepublik automated playing
Python package for automated eRepublik playing
* Free software: MIT license
* Documentation: https://erepublik-script.readthedocs.io.
* Documentation: https://erepublik.readthedocs.io/en/latest/
Features

View File

@ -33,4 +33,4 @@
"travel_to_fight": true,
"wam": true,
"work": true
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
<html><head><meta http-equiv="refresh" content="0;url=https://www.erepublik.com/en"/></head></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"error":false,"enabled":true,"type":{"anniversary":false,"flavorPacks":false,"springChallenge":false,"summerChallenge":false,"halloweenChallenge":false},"timeLeft":346765,"nextReward":{"maxReward":false,"type":"icon_energy_booster","text":"+1 Energy recovery until the end of Day 4,262"},"maxRewardId":0,"player":{"avatar":"//cdnt.erepublik.net/7efiav4XZ4SMvXAtgEk1NciUmAg=/55x55/smart/avatars/Citizens/2009/07/08/4b57b9ebb0232f0d6c3f6f2c21b8ab95.jpg?c022b6df6f643263dba839cb35b7a9ab","name":"inpoc1","prestigePoints":14170},"progress":0.9141935483871,"rewards":{"normal":[{"id":59,"collectedBefore":58,"percent":0.74193548387097,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_bars"},{"id":60,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_booster"},{"id":61,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_bars"},{"id":62,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_booster"},{"id":63,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_bars"},{"id":64,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_booster"},{"id":65,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_bars"},{"id":66,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_booster"},{"id":67,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_bars"},{"id":68,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_booster"},{"id":69,"collectedBefore":0,"percent":0.016129032258065,"label":"You have already collected this reward","tooltip":"","status":"rewarded","icon":"energy_bars"},{"id":70,"collectedBefore":0,"percent":0.016129032258065,"label":"Reach 14,250 Prestige Points to unlock the following reward: +1 Energy recovery until the end of Day 4,262","tooltip":"Reach 14250 Prestige Points to unlock the following reward: +1 Energy recovery until the end of Day 4,262","status":"","icon":"energy_booster"},{"id":71,"collectedBefore":0,"percent":0.016129032258065,"label":"Reach 14,500 Prestige Points to unlock the following reward: 10 Energy Bars","tooltip":"Reach 14500 Prestige Points to unlock the following reward: 10 Energy Bars","status":"","icon":"energy_bars"},{"id":72,"collectedBefore":0,"percent":0.032258064516129,"label":"Reach 15,000 Prestige Points to unlock the following reward: +1 Energy recovery until the end of Day 4,262","tooltip":"Reach 15000 Prestige Points to unlock the following reward: +1 Energy recovery until the end of Day 4,262","status":"","icon":"energy_booster"},{"id":73,"collectedBefore":0,"percent":0.016129032258065,"label":"Reach 15,250 Prestige Points to unlock the following reward: +1 Energy recovery until the end of Day 4,262","tooltip":"Reach 15250 Prestige Points to unlock the following reward: +1 Energy recovery until the end of Day 4,262","status":"","icon":"energy_booster"},{"id":74,"collectedBefore":0,"percent":0.016129032258065,"label":"Reach 15,500 Prestige Points to unlock the following reward: 15 Energy Bars","tooltip":"Reach 15500 Prestige Points to unlock the following reward: 15 Energy Bars","status":"","icon":"energy_bars"}],"extra":[]}}

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -msphinx
SPHINXPROJ = erepublik_script
SPHINXPROJ = erepublik
SOURCEDIR = .
BUILDDIR = _build

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# erepublik_script documentation build configuration file, created by
# erepublik documentation build configuration file, created by
# sphinx-quickstart on Fri Jun 9 13:47:02 2017.
#
# This file is execfile()d with the current directory set to its
@ -22,7 +22,8 @@ import os
import sys
sys.path.insert(0, os.path.abspath('..'))
import erepublik_script
import erepublik
import edx_theme
# -- General configuration ---------------------------------------------
@ -32,7 +33,7 @@ import erepublik_script
# 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']
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'edx_theme']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -56,9 +57,9 @@ author = u"Eriks Karls"
# the built documents.
#
# The short X.Y version.
version = erepublik_script.__version__
version = erepublik.__version__
# The full version, including alpha/beta/rc tags.
release = erepublik_script.__version__
release = erepublik.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@ -84,7 +85,8 @@ 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 = 'alabaster'
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
# theme further. For a list of options available for each theme, see the
@ -101,7 +103,7 @@ html_static_path = ['_static']
# -- Options for HTMLHelp output ---------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'erepublik_scriptdoc'
htmlhelp_basename = 'erepublikdoc'
# -- Options for LaTeX output ------------------------------------------
@ -128,7 +130,7 @@ latex_elements = {
# (source start file, target name, title, author, documentclass
# [howto, manual, or own class]).
latex_documents = [
(master_doc, 'erepublik_script.tex',
(master_doc, 'erepublik.tex',
u'eRepublik script Documentation',
u'Eriks Karls', 'manual'),
]
@ -139,7 +141,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'erepublik_script',
(master_doc, 'erepublik',
u'eRepublik script Documentation',
[author], 1)
]
@ -151,13 +153,10 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'erepublik_script',
(master_doc, 'erepublik',
u'eRepublik script Documentation',
author,
'erepublik_script',
'erepublik',
'One line description of project.',
'Miscellaneous'),
]

View File

@ -1,5 +1,5 @@
Welcome to eRepublik script's documentation!
======================================
============================================
.. toctree::
:maxdepth: 2

View File

@ -12,7 +12,7 @@ To install eRepublik script, run this command in your terminal:
.. code-block:: console
$ pip install erepublik_script
$ pip install erepublik
This is the preferred method to install eRepublik script, as it will always install the most recent stable release.
@ -32,13 +32,13 @@ You can either clone the public repository:
.. code-block:: console
$ git clone git://github.com/eeriks/erepublik_script
$ git clone git://github.com/eeriks/erepublik
Or download the `tarball`_:
.. code-block:: console
$ curl -OL https://github.com/eeriks/erepublik_script/tarball/master
$ curl -OL https://github.com/eeriks/erepublik/tarball/master
Once you have a copy of the source, you can install it with:
@ -47,5 +47,5 @@ Once you have a copy of the source, you can install it with:
$ python setup.py install
.. _Github repo: https://github.com/eeriks/erepublik_script
.. _tarball: https://github.com/eeriks/erepublik_script/tarball/master
.. _Github repo: https://github.com/eeriks/erepublik
.. _tarball: https://github.com/eeriks/erepublik/tarball/master

View File

@ -1,36 +1,36 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=python -msphinx
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=erepublik_script
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The Sphinx module was not found. Make sure you have Sphinx installed,
echo.then set the SPHINXBUILD environment variable to point to the full
echo.path of the 'sphinx-build' executable. Alternatively you may add the
echo.Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=python -msphinx
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=erepublik
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The Sphinx module was not found. Make sure you have Sphinx installed,
echo.then set the SPHINXBUILD environment variable to point to the full
echo.path of the 'sphinx-build' executable. Alternatively you may add the
echo.Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd

View File

@ -4,4 +4,7 @@ Usage
To use eRepublik script in a project::
import erepublik_script
from erepublik import Citizen
player = Citizen('email@domain.com', 'password')
player.update_all()

10
erepublik/__init__.py Normal file
View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
"""Top-level package for eRepublik script."""
__author__ = """Eriks Karls"""
__email__ = 'eriks@72.lv'
__version__ = '0.17.3'
from erepublik import classes, utils
from erepublik.citizen import Citizen

File diff suppressed because it is too large Load Diff

1269
erepublik/classes.py Normal file

File diff suppressed because it is too large Load Diff

374
erepublik/utils.py Normal file
View File

@ -0,0 +1,374 @@
import datetime
import inspect
import json
import os
import re
import sys
import time
import traceback
import unicodedata
from pathlib import Path
from typing import Union, Any, List, NoReturn, Mapping
import pytz
import requests
__all__ = ["FOOD_ENERGY", "COMMIT_ID", "COUNTRIES", "erep_tz", 'COUNTRY_LINK',
"now", "localize_dt", "localize_timestamp", "good_timedelta", "eday_from_date", "date_from_eday",
"get_sleep_seconds", "interactive_sleep", "silent_sleep",
"write_silent_log", "write_interactive_log", "get_file", "write_file",
"send_email", "normalize_html_json", "process_error", 'report_promo', 'calculate_hit']
FOOD_ENERGY = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20)
COMMIT_ID = "7b92e19"
erep_tz = pytz.timezone('US/Pacific')
AIR_RANKS = {1: "Airman", 2: "Airman 1st Class", 3: "Airman 1st Class*", 4: "Airman 1st Class**",
5: "Airman 1st Class***", 6: "Airman 1st Class****", 7: "Airman 1st Class*****",
8: "Senior Airman", 9: "Senior Airman*", 10: "Senior Airman**", 11: "Senior Airman***",
12: "Senior Airman****", 13: "Senior Airman*****",
14: "Staff Sergeant", 15: "Staff Sergeant*", 16: "Staff Sergeant**", 17: "Staff Sergeant***",
18: "Staff Sergeant****", 19: "Staff Sergeant*****",
20: "Aviator", 21: "Aviator*", 22: "Aviator**", 23: "Aviator***", 24: "Aviator****", 25: "Aviator*****",
26: "Flight Lieutenant", 27: "Flight Lieutenant*", 28: "Flight Lieutenant**", 29: "Flight Lieutenant***",
30: "Flight Lieutenant****", 31: "Flight Lieutenant*****",
32: "Squadron Leader", 33: "Squadron Leader*", 34: "Squadron Leader**", 35: "Squadron Leader***",
36: "Squadron Leader****", 37: "Squadron Leader*****",
38: "Chief Master Sergeant", 39: "Chief Master Sergeant*", 40: "Chief Master Sergeant**",
41: "Chief Master Sergeant***", 42: "Chief Master Sergeant****", 43: "Chief Master Sergeant*****",
44: "Wing Commander", 45: "Wing Commander*", 46: "Wing Commander**", 47: "Wing Commander***",
48: "Wing Commander****", 49: "Wing Commander*****",
50: "Group Captain", 51: "Group Captain*", 52: "Group Captain**", 53: "Group Captain***",
54: "Group Captain****", 55: "Group Captain*****",
56: "Air Commodore", 57: "Air Commodore*", 58: "Air Commodore**", 59: "Air Commodore***",
60: "Air Commodore****", 61: "Air Commodore*****", }
GROUND_RANKS = {1: "Recruit", 2: "Private", 3: "Private*", 4: "Private**", 5: "Private***", 6: "Corporal",
7: "Corporal*", 8: "Corporal**", 9: "Corporal***",
10: "Sergeant", 11: "Sergeant*", 12: "Sergeant**", 13: "Sergeant***", 14: "Lieutenant",
15: "Lieutenant*", 16: "Lieutenant**", 17: "Lieutenant***",
18: "Captain", 19: "Captain*", 20: "Captain**", 21: "Captain***", 22: "Major", 23: "Major*",
24: "Major**", 25: "Major***",
26: "Commander", 27: "Commander*", 28: "Commander**", 29: "Commander***", 30: "Lt Colonel",
31: "Lt Colonel*", 32: "Lt Colonel**", 33: "Lt Colonel***",
34: "Colonel", 35: "Colonel*", 36: "Colonel**", 37: "Colonel***", 38: "General", 39: "General*",
40: "General**", 41: "General***",
42: "Field Marshal", 43: "Field Marshal*", 44: "Field Marshal**", 45: "Field Marshal***",
46: "Supreme Marshal", 47: "Supreme Marshal*", 48: "Supreme Marshal**", 49: "Supreme Marshal***",
50: "National Force", 51: "National Force*", 52: "National Force**", 53: "National Force***",
54: "World Class Force", 55: "World Class Force*", 56: "World Class Force**",
57: "World Class Force***", 58: "Legendary Force", 59: "Legendary Force*", 60: "Legendary Force**",
61: "Legendary Force***",
62: "God of War", 63: "God of War*", 64: "God of War**", 65: "God of War***", 66: "Titan", 67: "Titan*",
68: "Titan**", 69: "Titan***",
70: "Legends I", 71: "Legends II", 72: "Legends III", 73: "Legends IV", 74: "Legends V",
75: "Legends VI", 76: "Legends VII", 77: "Legends VIII", 78: "Legends IX", 79: "Legends X",
80: "Legends XI", 81: "Legends XII", 82: "Legends XIII", 83: "Legends XIV", 84: "Legends XV",
85: "Legends XVI", 86: "Legends XVII", 87: "Legends XVIII", 88: "Legends XIX", 89: "Legends XX", }
COUNTRIES = {1: 'Romania', 9: 'Brazil', 10: 'Italy', 11: 'France', 12: 'Germany', 13: 'Hungary', 14: 'China',
15: 'Spain', 23: 'Canada', 24: 'USA', 26: 'Mexico', 27: 'Argentina', 28: 'Venezuela', 29: 'United Kingdom',
30: 'Switzerland', 31: 'Netherlands', 32: 'Belgium', 33: 'Austria', 34: 'Czech Republic', 35: 'Poland',
36: 'Slovakia', 37: 'Norway', 38: 'Sweden', 39: 'Finland', 40: 'Ukraine', 41: 'Russia', 42: 'Bulgaria',
43: 'Turkey', 44: 'Greece', 45: 'Japan', 47: 'South Korea', 48: 'India', 49: 'Indonesia', 50: 'Australia',
51: 'South Africa', 52: 'Republic of Moldova', 53: 'Portugal', 54: 'Ireland', 55: 'Denmark', 56: 'Iran',
57: 'Pakistan', 58: 'Israel', 59: 'Thailand', 61: 'Slovenia', 63: 'Croatia', 64: 'Chile', 65: 'Serbia',
66: 'Malaysia', 67: 'Philippines', 68: 'Singapore', 69: 'Bosnia and Herzegovina', 70: 'Estonia',
71: 'Latvia', 72: 'Lithuania', 73: 'North Korea', 74: 'Uruguay', 75: 'Paraguay', 76: 'Bolivia', 77: 'Peru',
78: 'Colombia', 79: 'Republic of Macedonia (FYROM)', 80: 'Montenegro', 81: 'Republic of China (Taiwan)',
82: 'Cyprus', 83: 'Belarus', 84: 'New Zealand', 164: 'Saudi Arabia', 165: 'Egypt',
166: 'United Arab Emirates', 167: 'Albania', 168: 'Georgia', 169: 'Armenia', 170: 'Nigeria', 171: 'Cuba'}
COUNTRY_LINK = {1: 'Romania', 9: 'Brazil', 11: 'France', 12: 'Germany', 13: 'Hungary', 82: 'Cyprus', 168: 'Georgia',
15: 'Spain', 23: 'Canada', 26: 'Mexico', 27: 'Argentina', 28: 'Venezuela', 80: 'Montenegro', 24: 'USA',
29: 'United-Kingdom', 50: 'Australia', 47: 'South-Korea', 171: 'Cuba',
79: 'Republic-of-Macedonia-FYROM',
30: 'Switzerland', 31: 'Netherlands', 32: 'Belgium', 33: 'Austria', 34: 'Czech-Republic', 35: 'Poland',
36: 'Slovakia', 37: 'Norway', 38: 'Sweden', 39: 'Finland', 40: 'Ukraine', 41: 'Russia', 42: 'Bulgaria',
43: 'Turkey', 44: 'Greece', 45: 'Japan', 48: 'India', 49: 'Indonesia', 78: 'Colombia', 68: 'Singapore',
51: 'South Africa', 52: 'Republic-of-Moldova', 53: 'Portugal', 54: 'Ireland', 55: 'Denmark', 56: 'Iran',
57: 'Pakistan', 58: 'Israel', 59: 'Thailand', 61: 'Slovenia', 63: 'Croatia', 64: 'Chile', 65: 'Serbia',
66: 'Malaysia', 67: 'Philippines', 70: 'Estonia', 165: 'Egypt', 14: 'China', 77: 'Peru', 10: 'Italy',
71: 'Latvia', 72: 'Lithuania', 73: 'North-Korea', 74: 'Uruguay', 75: 'Paraguay', 76: 'Bolivia',
81: 'Republic-of-China-Taiwan', 166: 'United-Arab-Emirates', 167: 'Albania', 69: 'Bosnia-Herzegovina',
169: 'Armenia', 83: 'Belarus', 84: 'New-Zealand', 164: 'Saudi-Arabia', 170: 'Nigeria', }
def now() -> datetime.datetime:
return datetime.datetime.now(erep_tz).replace(microsecond=0)
def localize_timestamp(timestamp: int) -> datetime.datetime:
return datetime.datetime.fromtimestamp(timestamp, erep_tz)
def localize_dt(dt: Union[datetime.date, datetime.datetime]) -> datetime.datetime:
try:
try:
return erep_tz.localize(dt)
except AttributeError:
return erep_tz.localize(datetime.datetime.combine(dt, datetime.time(0, 0, 0)))
except ValueError:
return dt.astimezone(erep_tz)
def good_timedelta(dt: datetime.datetime, td: datetime.timedelta) -> datetime.datetime:
return erep_tz.normalize(dt + td)
def eday_from_date(date: Union[datetime.date, datetime.datetime] = now()) -> int:
if isinstance(date, datetime.date):
date = datetime.datetime.combine(date, datetime.time(0, 0, 0))
return (date - datetime.datetime(2007, 11, 20, 0, 0, 0)).days
def date_from_eday(eday: int) -> datetime.date:
return localize_dt(datetime.date(2007, 11, 20)) + datetime.timedelta(days=eday)
def get_sleep_seconds(time_untill: datetime.datetime) -> int:
""" time_until aware datetime object Wrapper for sleeping until """
sleep_seconds = int((time_untill - now()).total_seconds())
return sleep_seconds if sleep_seconds > 0 else 0
def interactive_sleep(sleep_seconds: int):
while sleep_seconds > 0:
seconds = sleep_seconds
if (seconds - 1) // 1800:
seconds = seconds % 1800 if seconds % 1800 else 1800
elif (seconds - 1) // 300:
seconds = seconds % 300 if seconds % 300 else 300
elif (seconds - 1) // 60:
seconds = seconds % 60 if seconds % 60 else 60
# elif (seconds - 1) // 30:
# seconds = seconds % 30 if seconds % 30 else 30
else:
seconds = 1
sys.stdout.write("\rSleeping for {:4} more seconds".format(sleep_seconds))
sys.stdout.flush()
time.sleep(seconds)
sleep_seconds -= seconds
sys.stdout.write("\r")
silent_sleep = time.sleep
def _write_log(msg, timestamp: bool = True, should_print: bool = False):
erep_time_now = now()
txt = "[{}] {}".format(erep_time_now.strftime('%F %T'), msg) if timestamp else msg
if not os.path.isdir('log'):
os.mkdir('log')
with open("log/%s.log" % erep_time_now.strftime('%F'), 'a', encoding="utf-8") as f:
f.write("%s\n" % txt)
if should_print:
print(txt)
def write_interactive_log(*args, **kwargs):
_write_log(should_print=True, *args, **kwargs)
def write_silent_log(*args, **kwargs):
_write_log(should_print=False, *args, **kwargs)
def get_file(filepath: str) -> str:
file = Path(filepath)
if file.exists():
if file.is_dir():
return str(file / "new_file.txt")
else:
version = 1
try:
version = int(file.suffix[1:]) + 1
basename = file.stem
except ValueError:
basename = file.name
version += 1
full_name = file.parent / f"{basename}.{version}"
while full_name.exists():
version += 1
full_name = file.parent / f"{basename}.{version}"
return str(full_name)
else:
os.makedirs(file.parent, exist_ok=True)
return str(file)
def write_file(filename: str, content: str) -> int:
filename = get_file(filename)
with open(filename, 'ab') as f:
return f.write(content.encode("utf-8"))
def write_request(response: requests.Response, is_error: bool = False):
from erepublik import Citizen
# Remove GET args from url name
url = response.url
last_index = url.index("?") if "?" in url else len(response.url)
name = slugify(response.url[len(Citizen.url):last_index])
html = response.text
try:
json.loads(html)
ext = "json"
except json.decoder.JSONDecodeError:
ext = "html"
if not is_error:
filename = "debug/requests/{}_{}.{}".format(now().strftime('%F_%H-%M-%S'), name, ext)
write_file(filename, html)
else:
return {"name": "{}_{}.{}".format(now().strftime('%F_%H-%M-%S'), name, ext),
"content": html.encode('utf-8'),
"mimetype": "application/json" if ext == "json" else "text/html"}
def send_email(name: str, content: List[Any], player=None, local_vars: Mapping[Any, Any] = None,
promo: bool = False, captcha: bool = False):
if local_vars is None:
local_vars = {}
from erepublik import Citizen
file_content_template = "<html><head><title>{title}</title></head><body>{body}</body></html>"
if isinstance(player, Citizen):
resp = write_request(player.r, is_error=True)
else:
resp = {"name": "None.html", "mimetype": "text/html",
"content": file_content_template.format(body="<br/>".join(content), title="Error"), }
if promo:
resp = {"name": "%s.html" % name, "mimetype": "text/html",
"content": file_content_template.format(title="Promo", body="<br/>".join(content))}
subject = "[eBot][{}] Promos: {}".format(now().strftime('%F %T'), name)
elif captcha:
resp = {"name": "%s.html" % name, "mimetype": "text/html",
"content": file_content_template.format(title="ReCaptcha", body="<br/>".join(content))}
subject = "[eBot][{}] RECAPTCHA: {}".format(now().strftime('%F %T'), name)
else:
subject = "[eBot][%s] Bug trace: %s" % (now().strftime('%F %T'), name)
body = "".join(traceback.format_stack()) + \
"\n\n" + \
"\n".join(content)
data = dict(send_mail=True, subject=subject, bugtrace=body)
if promo:
data.update({'promo': True})
elif captcha:
data.update({'captcha': True})
else:
data.update({"bug": True})
files = [('file', (resp.get("name"), resp.get("content"), resp.get("mimetype"))), ]
filename = "log/%s.log" % now().strftime('%F')
if os.path.isfile(filename):
files.append(('file', (filename[4:], open(filename, 'rb'), "text/plain")))
if local_vars:
if "state_thread" in local_vars:
local_vars.pop('state_thread', None)
from erepublik.classes import MyJSONEncoder
files.append(('file', ("local_vars.json", json.dumps(local_vars, indent=2,
cls=MyJSONEncoder, sort_keys=True), "application/json")))
if isinstance(player, Citizen):
files.append(('file', ("instance.json", player.to_json(indent=True), "application/json")))
requests.post('https://pasts.72.lv', data=data, files=files)
def normalize_html_json(js: str) -> str:
js = re.sub(r' \'(.*?)\'', lambda a: '"%s"' % a.group(1), js)
js = re.sub(r'(\d\d):(\d\d):(\d\d)', r'\1\2\3', js)
js = re.sub(r'([{\s,])(\w+)(:)(?!"})', r'\1"\2"\3', js)
js = re.sub(r',\s*}', '}', js)
return js
def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commit_id: str = None,
interactive: bool = False):
"""
Process error logging and email sending to developer
:param interactive: Should print interactively
:param log_info: String to be written in output
:param name: String Instance name
:param exc_info: tuple output from sys.exc_info()
:param citizen: Citizen instance
:param commit_id: Code's version by commit id
"""
type_, value_, traceback_ = exc_info
bugtrace = [] if not commit_id else ["Commit id: %s" % commit_id, ]
bugtrace += [str(value_), str(type_), ''.join(traceback.format_tb(traceback_))]
if interactive:
write_interactive_log(log_info)
else:
write_silent_log(log_info)
trace = inspect.trace()
if trace:
trace = trace[-1][0].f_locals
else:
trace = dict()
send_email(name, bugtrace, citizen, local_vars=trace)
def report_promo(kind: str, time_untill: datetime.datetime) -> NoReturn:
requests.post('https://api.erep.lv/promos/add/', data=dict(kind=kind, time_untill=time_untill))
def slugify(value, allow_unicode=False) -> str:
"""
Function copied from Django2.2.1 django.utils.text.slugify
Convert to ASCII if 'allow_unicode' is False. Convert spaces to hyphens.
Remove characters that aren't alphanumerics, underscores, or hyphens.
Convert to lowercase. Also strip leading and trailing whitespace.
"""
value = str(value)
if allow_unicode:
value = unicodedata.normalize('NFKC', value)
else:
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
value = re.sub(r'[^\w\s-]', '_', value).strip().lower()
return re.sub(r'[-\s]+', '-', value)
def calculate_hit(strength: float, rang: int, tp: bool, elite: bool, ne: bool, booster: int = 0,
weapon: int = 200) -> float:
base_dmg = 10 * (1 + strength / 400) * (1 + rang / 5) * (1 + weapon / 100)
dmg = int(base_dmg * 10 + 5) // 10
booster_multiplier = (100 + booster) / 100
booster_dmg = dmg * booster_multiplier
dmg = int(booster_dmg * 10 + 5) // 10
elite = 1.1 if elite else 1
elite_dmg = dmg * elite
dmg = int(elite_dmg)
legend = 1 if (not tp or rang < 70) else 1 + (rang - 69) / 10
legend_dmg = dmg * legend
dmg = int(legend_dmg)
return dmg * (1.1 if ne else 1)
def ground_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False,
booster: int = 0, weapon_power: int = 200) -> float:
r = requests.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
if natural_enemy:
true_patriot = True
return calculate_hit(strength, rang, true_patriot, elite, natural_enemy, booster, weapon_power)
def air_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0,
weapon_power: int = 0) -> float:
r = requests.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)

View File

@ -1,12 +0,0 @@
# -*- coding: utf-8 -*-
"""Top-level package for eRepublik script."""
__author__ = """Eriks Karls"""
__email__ = 'eriks@72.lv'
__version__ = '0.1.2'
__all__ = ["Citizen"]
from erepublik_script import classes, utils
from erepublik_script.citizen import Citizen

File diff suppressed because it is too large Load Diff

View File

@ -1,422 +0,0 @@
# -*- coding: utf-8 -*-
"""Console script for erepublik_script."""
import json
import os
import random
import sys
import threading
from collections import defaultdict
from datetime import timedelta
from typing import List, Tuple
import click
from erepublik_script import classes, utils
from erepublik_script.citizen import Citizen
__all__ = ["Citizen"]
CONFIG = defaultdict(bool)
@click.command()
@click.option('--silent', help='Run silently', type=bool, is_flag=True)
def main(silent):
global CONFIG
assert sys.version_info >= (3, 7, 1)
if silent:
write_log = utils.write_silent_log
else:
write_log = utils.write_interactive_log
try:
with open('config.json', 'r') as f:
CONFIG = json.load(f)
write_log('Config file found. Checking...')
CONFIG = utils.parse_config(CONFIG)
except:
CONFIG = utils.parse_config()
with open('config.json', 'w') as f:
json.dump(CONFIG, f, indent=True, sort_keys=True)
if CONFIG['interactive']:
write_log = utils.write_interactive_log
else:
write_log = utils.write_silent_log
write_log('\nTo quit press [ctrl] + [c]', False)
os.chdir(os.path.dirname(os.path.realpath(__file__)))
write_log('Version: ' + utils.VERSION)
player = None
try: # If errors before player is initialized
while True:
player = Citizen(email=CONFIG['email'], password=CONFIG['password'])
if player.logged_in:
break
utils.silent_sleep(2)
player.config.work = CONFIG['work']
player.config.train = CONFIG['train']
player.config.ot = CONFIG['ot']
player.config.wam = bool(CONFIG['wam'])
player.config.employees = bool(CONFIG['employ'])
player.config.auto_sell = CONFIG.get('auto_sell', [])
player.config.auto_sell_all = CONFIG.get('auto_sell_all', False)
player.config.auto_buy_raw = CONFIG.get('auto_buy_raw', False)
player.config.force_wam = CONFIG.get('force_wam', False)
player.config.fight = CONFIG['fight']
player.config.air = CONFIG['air']
player.config.ground = CONFIG['ground']
player.config.all_in = CONFIG['all_in']
player.config.next_energy = CONFIG['next_energy']
player.config.boosters = CONFIG['boosters']
player.config.travel_to_fight = CONFIG['travel_to_fight']
player.config.always_travel = CONFIG.get('always_travel', False)
player.config.epic_hunt = CONFIG['epic_hunt']
player.config.epic_hunt_ebs = CONFIG['epic_hunt_ebs']
player.config.rw_def_side = CONFIG['rw_def_side']
player.config.random_sleep = CONFIG['random_sleep']
player.config.continuous_fighting = CONFIG['continuous_fighting']
player.config.interactive = CONFIG['interactive']
player.reporter.allowed = not CONFIG.get('reporting_is_not_allowed')
player.set_debug(CONFIG.get('debug', False))
while True:
try:
player.update_all()
break
except:
utils.silent_sleep(2)
now = utils.now()
dt_max = now.replace(year=9999)
tasks = {
'eat': now,
}
wam_hour = employ_hour = 14
if player.config.work:
tasks.update({'work': now})
if player.config.train:
tasks.update({'train': now})
if player.config.ot:
tasks.update({'ot': now})
if player.config.fight:
tasks.update({'fight': now})
if player.config.wam:
wam_hour = 14
if not isinstance(CONFIG['wam'], bool):
try:
wam_hour = abs(int(CONFIG['wam'])) % 24
except ValueError:
pass
tasks.update({'wam': now.replace(hour=wam_hour, minute=0, second=0, microsecond=0)})
if player.config.employees:
employ_hour = 8
if not isinstance(CONFIG['employ'], bool):
try:
employ_hour = abs(int(CONFIG['employ'])) % 24
except ValueError:
pass
tasks.update({'employ': now.replace(hour=employ_hour, minute=0, second=0, microsecond=0)})
if player.config.epic_hunt:
tasks['epic_hunt'] = now
if CONFIG.get("renew_houses", True):
tasks['renew_houses'] = now
if CONFIG.get('start_battles'):
""" {'start_battle': {war_id: {'regions': [region_id, ],
'timing': ['at', 'hh:mm' | 'before', 'hh:mm' (before autoattack) |
'auto' (after round for citizenship country's oldest battle or at 00:00)
'rw', (after first round of RW if you are occupying)]}} """
player.allowed_battles = CONFIG.get('start_battles', dict())
raise classes.ErepublikException("Battle starting is not implemented")
if player.reporter.allowed:
report = dict(CONFIG)
report.pop("email", None)
report.pop("password", None)
report.update(
VERSION=utils.VERSION,
COMMIT_ID=utils.COMMIT_ID
)
player.reporter.report_action("ACTIVE_CONFIG", json_val=report)
# -1 because main thread is counted in
name = "{}-state_updater-{}".format(player.name, threading.active_count() - 1)
state_thread = threading.Thread(target=player.state_update_repeater, name=name)
state_thread.start()
if CONFIG.get("congress", True):
tasks['congress'] = now.replace(hour=1, minute=30, second=0)
if CONFIG.get("party_president", False):
tasks['party_president'] = now.replace(hour=1, minute=30, second=0)
contribute_cc = int(CONFIG.get("contribute_cc", 0))
if contribute_cc:
tasks['contribute_cc'] = now.replace(hour=2, minute=0, second=0)
if CONFIG.get("gold_buy"):
tasks['gold_buy'] = now.replace(hour=23, minute=57, second=0, microsecond=0)
error_count = 0
while error_count < 3:
try:
now = utils.now()
player.update_all()
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
player.collect_daily_task()
next_time = now.replace(hour=0, minute=0, second=0) + timedelta(days=1)
tasks.update({'work': next_time})
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 = now.replace(hour=0, minute=0, second=0) + timedelta(days=1)
tasks.update({'train': next_time})
if tasks.get('wam', dt_max) <= now:
player.write_log("Doing task: Work as manager")
success = player.work_wam()
player.eat()
if success:
next_time = now.replace(hour=wam_hour, minute=0, second=0, microsecond=0) + timedelta(days=1)
else:
next_time = now.replace(second=0, microsecond=0) + timedelta(minutes=30)
tasks.update({'wam': next_time})
if tasks.get('eat', dt_max) <= now:
player.write_log("Doing task: eat")
player.eat()
if player.energy.food_fights > player.energy.limit // 10:
next_minutes = 12
else:
next_minutes = (player.energy.limit - 5 * player.energy.interval) // player.energy.interval * 6
next_time = player.energy.reference_time + timedelta(minutes=next_minutes)
tasks.update({'eat': next_time})
if tasks.get('fight', dt_max) <= now or player.energy.is_energy_full:
fight_energy_debug_log: List[Tuple[int, str]] = []
player.write_log("Doing task: fight")
player.write_log(player.health_info)
if player.should_fight():
player.find_battle_and_fight()
else:
player.collect_weekly_reward()
energy = classes.EnergyToFight(player.details.xp_till_level_up * 10 - player.energy.limit + 50)
fight_energy_debug_log.append((
energy.i,
f"Levelup reachable {player.details.xp_till_level_up} * 10 - {player.energy.limit} + 50"
))
# Do levelup
energy.check(player.details.xp_till_level_up * 10 + 50)
fight_energy_debug_log.append((
energy.i, f"Levelup {player.details.xp_till_level_up} * 10 + 50"
))
# if levelup is close stop queueing other fighting
if not player.is_levelup_close:
# Obligatory need 75pp
if player.details.pp < 75:
energy.check(75 - player.details.pp)
fight_energy_debug_log.append((energy.i, f"Obligatory need 75pp: 75 - {player.details.pp}"))
if player.config.continuous_fighting and player.has_battle_contribution:
energy.check(player.energy.interval)
fight_energy_debug_log.append((energy.i, f"continuous_fighting: {player.energy.interval}"))
# All-in
if player.config.all_in:
energy.check(player.energy.limit * 2 - 3 * player.energy.interval)
fight_energy_debug_log.append((
energy.i, f"All-in: {player.energy.limit} * 2 - 3 * {player.energy.interval}"
))
elif player.energy.limit * 2 - 3 * player.energy.interval >= player.energy.recovered:
# 1h worth of energy
energy.check(player.energy.limit * 2 - 3 * player.energy.interval)
fight_energy_debug_log.append(
(energy.i, f"1h worth of energy: {player.energy.interval} * 10"
))
# All-in for AIR battles
if all([player.config.air, player.config.all_in,
player.energy.available >= player.energy.limit]):
energy.check(player.energy.limit)
fight_energy_debug_log.append((
energy.i, f"All-in for AIR battles: {player.energy.limit}"
))
# Get to next Energy +1
if player.next_reachable_energy and player.config.next_energy:
energy.check(player.next_reachable_energy * 10)
fight_energy_debug_log.append((
energy.i, f"Get to next Energy +1: {player.next_reachable_energy} * 10"
))
energy = energy.i - player.energy.available
next_minutes = max([6, abs(energy) // player.energy.interval * 6])
# utils.write_silent_log("\n".join([f"{energy} {info}" for energy, info in fight_energy_debug_log]))
next_time = player.energy.reference_time + timedelta(minutes=next_minutes)
tasks.update({'fight': next_time})
if tasks.get('ot', dt_max) <= now:
player.write_log("Doing task: ot")
if now > player.my_companies.next_ot_time:
player.work_ot()
next_time = now + timedelta(minutes=60)
else:
next_time = player.my_companies.next_ot_time
tasks.update({'ot': next_time})
if tasks.get('employ', dt_max) <= now:
player.write_log("Doing task: Employee work")
next_time = utils.now().replace(hour=employ_hour, minute=0, second=0) + timedelta(days=1)
next_time = next_time if player.work_employees() else tasks.get('employ') + timedelta(minutes=30)
tasks.update({'employ': next_time})
if tasks.get('epic_hunt', dt_max) <= now:
player.write_log("Doing task: EPIC check")
player.check_epic_battles()
if player.active_fs:
next_time = now + timedelta(minutes=1)
else:
next_time = tasks.get('eat')
tasks.update({'epic_hunt': next_time})
if tasks.get('gold_buy', dt_max) <= now:
player.write_log("Doing task: auto buy 10g")
for offer in player.get_monetary_offers():
if offer['amount'] >= 10 and player.details.cc >= 20 * offer["price"]:
# TODO: check allowed amount to buy
if player.buy_monetary_market_offer(offer=offer['offer_id'], amount=10, currency=62):
break
next_time = tasks.get('gold_buy') + timedelta(days=1)
tasks.update({'gold_buy': next_time})
if tasks.get('congress', dt_max) <= now:
if 1 <= now.day < 16:
next_time = now.replace(day=16)
elif 16 <= now.day < 24:
player.write_log("Doing task: candidate for congress")
player.candidate_for_congress()
if not now.month == 12:
next_time = now.replace(month=now.month + 1, day=16)
else:
next_time = now.replace(year=now.year + 1, month=1, day=16)
else:
if not now.month == 12:
next_time = now.replace(month=now.month + 1, day=16)
else:
next_time = now.replace(year=now.year + 1, month=1, day=16)
tasks.update({'congress': next_time.replace(hour=1, minute=30, second=0, microsecond=0)})
if tasks.get('party_president', dt_max) <= now:
if not now.day == 15:
player.write_log("Doing task: candidate for party president")
player.candidate_for_party_presidency()
if not now.month == 12:
next_time = now.replace(month=now.month + 1)
else:
next_time = now.replace(year=now.year + 1, month=1)
else:
if not now.month == 12:
next_time = now.replace(month=now.month + 1)
else:
next_time = now.replace(year=now.year + 1, month=1)
tasks.update(party_president=next_time.replace(day=16, hour=0, minute=0, second=0, microsecond=0))
if tasks.get('contribute_cc', dt_max) <= now:
if not now.weekday():
player.update_money()
cc = (player.details.cc // contribute_cc) * contribute_cc
player.write_log("Doing task: Contribute {}cc to Latvia".format(cc))
player.contribute_cc_to_country(cc)
next_time = now + timedelta(days=7 - now.weekday())
next_time = next_time.replace(hour=2, minute=0, second=0)
tasks.update({'contribute_cc': next_time})
if tasks.get('renew_houses', dt_max) <= now:
player.write_log("Doing task: Renew houses")
end_times = player.renew_houses()
if end_times:
tasks.update(renew_houses=min(end_times.values()) - timedelta(hours=24))
else:
player.write_log("No houses found! Forcing q1 usage...")
end_times = player.buy_and_activate_house(1)
if not end_times:
tasks.update(renew_houses=now + timedelta(hours=6))
else:
tasks.update(renew_houses=min(end_times.values()) - timedelta(hours=24))
closest_next_time = dt_max
next_tasks = []
for task, next_time in sorted(tasks.items(), key=lambda s: s[1]):
next_tasks.append("{}: {}".format(next_time.strftime('%F %T'), task))
if next_time < closest_next_time:
closest_next_time = next_time
random_seconds = random.randint(0, 121) if player.config.random_sleep else 0
sleep_seconds = int(utils.get_sleep_seconds(closest_next_time))
if sleep_seconds <= 0:
raise classes.ErepublikException(f"Loop detected! Offending task: '{next_tasks[0]}'")
closest_next_time += timedelta(seconds=random_seconds)
player.write_log("My next Tasks and there time:\n" + "\n".join(sorted(next_tasks)))
player.write_log("Sleeping until (eRep): {} (sleeping for {}s + random {}s)".format(
closest_next_time.strftime("%F %T"), sleep_seconds, random_seconds))
seconds_to_sleep = sleep_seconds + random_seconds if sleep_seconds > 0 else 0
player.sleep(seconds_to_sleep)
except classes.ErepublikNetworkException:
player.write_log('Network ERROR detected. Sleeping for 1min...')
player.sleep(60)
except (KeyboardInterrupt, SystemExit):
sys.exit(1)
except classes.ErepublikException as e:
utils.process_error(f"Known error detected! {e}", player.name, sys.exc_info(), player, utils.COMMIT_ID)
except:
utils.process_error("Unknown error!", player.name, sys.exc_info(), player, utils.COMMIT_ID)
error_count += 1
if error_count < 3:
player.sleep(60)
finally:
if error_count >= 3:
player.stop_threads.set()
player.stop_threads.set()
player.write_log('Too many errors.')
except (KeyboardInterrupt, SystemExit):
sys.exit(1)
except classes.ErepublikException:
utils.process_error("[{}] To many errors.".format(utils.COMMIT_ID), player.name, sys.exc_info(), player,
utils.COMMIT_ID)
except:
if isinstance(player, Citizen):
name = player.name
elif CONFIG.get('email', None):
name = CONFIG['email']
else:
name = "Uninitialized"
utils.process_error("[{}] Fatal error.".format(utils.COMMIT_ID), name, sys.exc_info(), player, utils.COMMIT_ID)
sys.exit(1)
if __name__ == "__main__":
while True:
main()
utils.write_interactive_log("Restarting after 1h")
utils.interactive_sleep(60 * 60)

View File

@ -1,730 +0,0 @@
import datetime
import inspect
import json
import os
import re
import sys
import time
import traceback
from collections import deque
from decimal import Decimal
from json import JSONEncoder
from pathlib import Path
from typing import Union
import pytz
import requests
from requests import Response
from slugify import slugify
__all__ = ["FOOD_ENERGY", "VERSION", "COMMIT_ID", "COUNTRIES", "erep_tz",
"now", "localize_dt", "localize_timestamp", "good_timedelta", "eday_from_date", "date_from_eday",
"get_sleep_seconds", "interactive_sleep", "silent_sleep",
"write_silent_log", "write_interactive_log", "get_file", "write_file",
"send_email", "normalize_html_json", "process_error", ]
FOOD_ENERGY = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20)
VERSION = "v0.14.1"
COMMIT_ID = "7b92e19"
erep_tz = pytz.timezone('US/Pacific')
AIR_RANKS = {1: "Airman", 2: "Airman 1st Class", 3: "Airman 1st Class*", 4: "Airman 1st Class**",
5: "Airman 1st Class***", 6: "Airman 1st Class****", 7: "Airman 1st Class*****",
8: "Senior Airman", 9: "Senior Airman*", 10: "Senior Airman**", 11: "Senior Airman***",
12: "Senior Airman****", 13: "Senior Airman*****",
14: "Staff Sergeant", 15: "Staff Sergeant*", 16: "Staff Sergeant**", 17: "Staff Sergeant***",
18: "Staff Sergeant****", 19: "Staff Sergeant*****",
20: "Aviator", 21: "Aviator*", 22: "Aviator**", 23: "Aviator***", 24: "Aviator****", 25: "Aviator*****",
26: "Flight Lieutenant", 27: "Flight Lieutenant*", 28: "Flight Lieutenant**", 29: "Flight Lieutenant***",
30: "Flight Lieutenant****", 31: "Flight Lieutenant*****",
32: "Squadron Leader", 33: "Squadron Leader*", 34: "Squadron Leader**", 35: "Squadron Leader***",
36: "Squadron Leader****", 37: "Squadron Leader*****",
38: "Chief Master Sergeant", 39: "Chief Master Sergeant*", 40: "Chief Master Sergeant**",
41: "Chief Master Sergeant***", 42: "Chief Master Sergeant****", 43: "Chief Master Sergeant*****",
44: "Wing Commander", 45: "Wing Commander*", 46: "Wing Commander**", 47: "Wing Commander***",
48: "Wing Commander****", 49: "Wing Commander*****",
50: "Group Captain", 51: "Group Captain*", 52: "Group Captain**", 53: "Group Captain***",
54: "Group Captain****", 55: "Group Captain*****",
56: "Air Commodore", 57: "Air Commodore*", 58: "Air Commodore**", 59: "Air Commodore***",
60: "Air Commodore****", 61: "Air Commodore*****", }
GROUND_RANKS = {1: "Recruit", 2: "Private", 3: "Private*", 4: "Private**", 5: "Private***", 6: "Corporal",
7: "Corporal*", 8: "Corporal**", 9: "Corporal***",
10: "Sergeant", 11: "Sergeant*", 12: "Sergeant**", 13: "Sergeant***", 14: "Lieutenant",
15: "Lieutenant*", 16: "Lieutenant**", 17: "Lieutenant***",
18: "Captain", 19: "Captain*", 20: "Captain**", 21: "Captain***", 22: "Major", 23: "Major*",
24: "Major**", 25: "Major***",
26: "Commander", 27: "Commander*", 28: "Commander**", 29: "Commander***", 30: "Lt Colonel",
31: "Lt Colonel*", 32: "Lt Colonel**", 33: "Lt Colonel***",
34: "Colonel", 35: "Colonel*", 36: "Colonel**", 37: "Colonel***", 38: "General", 39: "General*",
40: "General**", 41: "General***",
42: "Field Marshal", 43: "Field Marshal*", 44: "Field Marshal**", 45: "Field Marshal***",
46: "Supreme Marshal", 47: "Supreme Marshal*", 48: "Supreme Marshal**", 49: "Supreme Marshal***",
50: "National Force", 51: "National Force*", 52: "National Force**", 53: "National Force***",
54: "World Class Force", 55: "World Class Force*", 56: "World Class Force**",
57: "World Class Force***", 58: "Legendary Force", 59: "Legendary Force*", 60: "Legendary Force**",
61: "Legendary Force***",
62: "God of War", 63: "God of War*", 64: "God of War**", 65: "God of War***", 66: "Titan", 67: "Titan*",
68: "Titan**", 69: "Titan***",
70: "Legends I", 71: "Legends II", 72: "Legends III", 73: "Legends IV", 74: "Legends V",
75: "Legends VI", 76: "Legends VII", 77: "Legends VIII", 78: "Legends IX", 79: "Legends X",
80: "Legends XI", 81: "Legends XII", 82: "Legends XIII", 83: "Legends XIV", 84: "Legends XV",
85: "Legends XVI", 86: "Legends XVII", 87: "Legends XVIII", 88: "Legends XIX", 89: "Legends XX", }
COUNTRIES = {1: 'Romania', 9: 'Brazil', 10: 'Italy', 11: 'France', 12: 'Germany', 13: 'Hungary', 14: 'China',
15: 'Spain', 23: 'Canada', 24: 'USA', 26: 'Mexico', 27: 'Argentina', 28: 'Venezuela', 29: 'United Kingdom',
30: 'Switzerland', 31: 'Netherlands', 32: 'Belgium', 33: 'Austria', 34: 'Czech Republic', 35: 'Poland',
36: 'Slovakia', 37: 'Norway', 38: 'Sweden', 39: 'Finland', 40: 'Ukraine', 41: 'Russia', 42: 'Bulgaria',
43: 'Turkey', 44: 'Greece', 45: 'Japan', 47: 'South Korea', 48: 'India', 49: 'Indonesia', 50: 'Australia',
51: 'South Africa', 52: 'Republic of Moldova', 53: 'Portugal', 54: 'Ireland', 55: 'Denmark', 56: 'Iran',
57: 'Pakistan', 58: 'Israel', 59: 'Thailand', 61: 'Slovenia', 63: 'Croatia', 64: 'Chile', 65: 'Serbia',
66: 'Malaysia', 67: 'Philippines', 68: 'Singapore', 69: 'Bosnia and Herzegovina', 70: 'Estonia',
71: 'Latvia', 72: 'Lithuania', 73: 'North Korea', 74: 'Uruguay', 75: 'Paraguay', 76: 'Bolivia', 77: 'Peru',
78: 'Colombia', 79: 'Republic of Macedonia (FYROM)', 80: 'Montenegro', 81: 'Republic of China (Taiwan)',
82: 'Cyprus', 83: 'Belarus', 84: 'New Zealand', 164: 'Saudi Arabia', 165: 'Egypt',
166: 'United Arab Emirates', 167: 'Albania', 168: 'Georgia', 169: 'Armenia', 170: 'Nigeria', 171: 'Cuba'}
class MyJSONEncoder(JSONEncoder):
def default(self, o):
if isinstance(o, Decimal):
return float("{:.02f}".format(o))
elif isinstance(o, datetime.datetime):
return dict(__type__='datetime', year=o.year, month=o.month, day=o.day, hour=o.hour, minute=o.minute,
second=o.second, microsecond=o.microsecond)
elif isinstance(o, datetime.date):
return dict(__type__='date', year=o.year, month=o.month, day=o.day)
elif isinstance(o, datetime.timedelta):
return dict(__type__='timedelta', days=o.days, seconds=o.seconds,
microseconds=o.microseconds, total_seconds=o.total_seconds())
elif isinstance(o, Response):
return dict(headers=o.headers.__dict__, url=o.url, text=o.text)
elif hasattr(o, '__dict__'):
return o.__dict__
elif isinstance(o, deque):
return list(o)
return super().default(o)
def now():
return datetime.datetime.now(erep_tz).replace(microsecond=0)
def localize_timestamp(timestamp: int):
return datetime.datetime.fromtimestamp(timestamp, erep_tz)
def localize_dt(dt: Union[datetime.date, datetime.datetime]):
if isinstance(dt, datetime.date):
dt = datetime.datetime.combine(dt, datetime.time(0, 0, 0))
return erep_tz.localize(dt)
def good_timedelta(dt: datetime.datetime, td: datetime.timedelta) -> datetime.datetime:
return erep_tz.normalize(dt + td)
def eday_from_date(date: Union[datetime.date, datetime.datetime] = now()) -> int:
if isinstance(date, datetime.date):
date = datetime.datetime.combine(date, datetime.time(0, 0, 0))
return (date - datetime.datetime(2007, 11, 20, 0, 0, 0)).days
def date_from_eday(eday: int) -> datetime.date:
return localize_dt(datetime.date(2007, 11, 20)) + datetime.timedelta(days=eday)
def get_sleep_seconds(time_untill: datetime.datetime) -> int:
""" time_until aware datetime object Wrapper for sleeping until """
sleep_seconds = int((time_untill - now()).total_seconds())
return sleep_seconds if sleep_seconds > 0 else 0
def interactive_sleep(sleep_seconds: int):
while sleep_seconds > 0:
seconds = sleep_seconds
if (seconds - 1) // 1800:
seconds = seconds % 1800 if seconds % 1800 else 1800
elif (seconds - 1) // 300:
seconds = seconds % 300 if seconds % 300 else 300
elif (seconds - 1) // 60:
seconds = seconds % 60 if seconds % 60 else 60
# elif (seconds - 1) // 30:
# seconds = seconds % 30 if seconds % 30 else 30
else:
seconds = 1
sys.stdout.write("\rSleeping for {:4} more seconds".format(sleep_seconds))
sys.stdout.flush()
time.sleep(seconds)
sleep_seconds -= seconds
sys.stdout.write("\r")
silent_sleep = time.sleep
def _write_log(msg, timestamp: bool = True, should_print: bool = False):
erep_time_now = now()
txt = "[{}] {}".format(erep_time_now.strftime('%F %T'), msg) if timestamp else msg
if not os.path.isdir('log'):
os.mkdir('log')
with open("log/%s.log" % erep_time_now.strftime('%F'), 'a', encoding="utf-8") as f:
f.write("%s\n" % txt)
if should_print:
print(txt)
def write_interactive_log(*args, **kwargs):
_write_log(should_print=True, *args, **kwargs)
def write_silent_log(*args, **kwargs):
_write_log(should_print=False, *args, **kwargs)
def get_file(filepath: str) -> str:
file = Path(filepath)
if file.exists():
if file.is_dir():
return str(file / "new_file.txt")
else:
version = 1
try:
version = int(file.suffix[1:]) + 1
basename = file.stem
except ValueError:
basename = file.name
version += 1
full_name = file.parent / f"{basename}.{version}"
while full_name.exists():
version += 1
full_name = file.parent / f"{basename}.{version}"
return str(full_name)
else:
os.makedirs(file.parent, exist_ok=True)
return str(file)
def write_file(filename: str, content: str) -> int:
filename = get_file(filename)
with open(filename, 'ab') as f:
return f.write(content.encode("utf-8"))
def write_request(response: requests.Response, is_error: bool = False):
from erepublik_script import Citizen
# Remove GET args from url name
url = response.url
last_index = url.index("?") if "?" in url else len(response.url)
name = slugify(response.url[len(Citizen.url):last_index])
html = response.text
try:
json.loads(html)
ext = "json"
except json.decoder.JSONDecodeError:
ext = "html"
if not is_error:
filename = "debug/requests/{}_{}.{}".format(now().strftime('%F_%H-%M-%S'), name, ext)
write_file(filename, html)
else:
return {"name": "{}_{}.{}".format(now().strftime('%F_%H-%M-%S'), name, ext),
"content": html.encode('utf-8'),
"mimetype": "application/json" if ext == "json" else "text/html"}
def send_email(name, content: list, player=None, local_vars=dict, promo: bool = False, captcha: bool = False):
from erepublik_script import Citizen
file_content_template = "<html><head><title>{title}</title></head><body>{body}</body></html>"
if isinstance(player, Citizen):
resp = write_request(player.r, is_error=True)
else:
resp = {"name": "None.html", "mimetype": "text/html",
"content": file_content_template.format(body="<br/>".join(content), title="Error"), }
if promo:
resp = {"name": "%s.html" % name, "mimetype": "text/html",
"content": file_content_template.format(title="Promo", body="<br/>".join(content))}
subject = "[eBot][{}] Promos: {}".format(now().strftime('%F %T'), name)
elif captcha:
resp = {"name": "%s.html" % name, "mimetype": "text/html",
"content": file_content_template.format(title="ReCaptcha", body="<br/>".join(content))}
subject = "[eBot][{}] RECAPTCHA: {}".format(now().strftime('%F %T'), name)
else:
subject = "[eBot][%s] Bug trace: %s" % (now().strftime('%F %T'), name)
body = "".join(traceback.format_stack()) + \
"\n\n" + \
"\n".join(content)
data = dict(send_mail=True, subject=subject, bugtrace=body)
if promo:
data.update({'promo': True})
elif captcha:
data.update({'captcha': True})
else:
data.update({"bug": True})
files = [('file', (resp.get("name"), resp.get("content"), resp.get("mimetype"))), ]
filename = "log/%s.log" % now().strftime('%F')
if os.path.isfile(filename):
files.append(('file', (filename[4:], open(filename, 'rb'), "text/plain")))
if local_vars:
if "state_thread" in local_vars:
local_vars.pop('state_thread', None)
files.append(('file', ("local_vars.json", json.dumps(local_vars, indent=2,
cls=MyJSONEncoder, sort_keys=True), "application/json")))
if isinstance(player, Citizen):
files.append(('file', ("instance.json", player.to_json(indent=True), "application/json")))
requests.post('https://pasts.72.lv', data=data, files=files)
def parse_input(msg: str) -> bool:
msg += " (y|n):"
data = None
while data not in ['', 'y', 'Y', 'n', 'N']:
try:
data = input(msg)
except EOFError:
data = 'n'
return data in ['', 'y', 'Y']
def parse_config(config=None) -> dict:
if config is None:
config = {}
if not config.get('email'):
config['email'] = input("Player email: ")
if not config.get('password'):
config['password'] = input("Player password: ")
if 'wt' in config:
config['work'] = config['wt']
config['train'] = config['wt']
if 'work' not in config:
config['work'] = parse_input('Should I work')
if 'train' not in config:
config['train'] = parse_input('Should I train')
if 'ot' not in config:
config['ot'] = parse_input('Should I work overtime')
if 'wam' not in config:
config['wam'] = parse_input('Should I WAM')
if 'employ' not in config:
config['employ'] = parse_input('Should I employ employees')
if config['wam'] or config['employ']:
if "autosell" in config:
config.pop("autosell")
if "autosell_raw" in config:
config.pop("autosell_raw")
if "autosell_final" in config:
config.pop("autosell_final")
if 'auto_sell' not in config or not isinstance(config['auto_sell'], list):
if parse_input('Should I auto sell produced products'):
config['auto_sell'] = []
if parse_input("Should I auto sell Food products"):
if parse_input("Should I auto sell Food products"):
config['auto_sell'].append("food")
if parse_input("Should I auto sell Weapon products"):
config['auto_sell'].append("weapon")
if parse_input("Should I auto sell House products"):
config['auto_sell'].append("house")
if parse_input("Should I auto sell Aircraft products"):
config['auto_sell'].append("airplane")
if parse_input("Should I auto sell raw products"):
if parse_input("Should I auto sell Food raw"):
config['auto_sell'].append("foodRaw")
if parse_input("Should I auto sell Weapon raw"):
config['auto_sell'].append("weaponRaw")
if parse_input("Should I auto sell House raw"):
config['auto_sell'].append("houseRaw")
if parse_input("Should I auto sell Airplane raw"):
config['auto_sell'].append("airplaneRaw")
if config['auto_sell']:
if 'auto_sell_all' not in config:
print("When selling produced items should I also sell items already in inventory?")
config['auto_sell_all'] = parse_input('Y - sell all, N - only just produced')
else:
config['auto_sell_all'] = False
if 'auto_buy_raw' not in config:
config['auto_buy_raw'] = parse_input('Should I auto buy raw deficit at WAM or employ')
else:
config['auto_sell'] = []
config['auto_sell_all'] = False
config['auto_buy_raw'] = False
if 'fight' not in config:
config['fight'] = parse_input('Should I fight')
if config.get('fight'):
if 'air' not in config:
config['air'] = parse_input('Should I fight in AIR')
if 'ground' not in config:
config['ground'] = parse_input('Should I fight in GROUND')
if 'all_in' not in config:
print("When full energy should I go all in")
config['all_in'] = parse_input('Y - all in, N - 1h worth of energy')
if 'next_energy' not in config:
config['next_energy'] = parse_input('Should I fight when next pp +1 energy available')
if 'boosters' not in config:
config['boosters'] = parse_input('Should I use +50% dmg boosters')
if 'travel_to_fight' not in config:
config['travel_to_fight'] = parse_input('Should I travel to fight')
if 'epic_hunt' not in config:
config['epic_hunt'] = parse_input('Should I check for epic battles')
if not config['epic_hunt']:
config['epic_hunt_ebs'] = False
if not config['epic_hunt']:
config['epic_hunt_ebs'] = False
elif 'epic_hunt_ebs' not in config:
config['epic_hunt_ebs'] = parse_input('Should I eat EBs when fighting in epic battle')
if 'rw_def_side' not in config:
config['rw_def_side'] = parse_input('Should I fight on defenders side in RWs')
if 'continuous_fighting' not in config:
config['continuous_fighting'] = parse_input('If already fought in any battle, \n'
'should I continue to fight all FF in that battle')
else:
config['air'] = False
config['ground'] = False
config['all_in'] = False
config['next_energy'] = False
config['boosters'] = False
config['travel_to_fight'] = False
config['epic_hunt'] = False
config['epic_hunt_ebs'] = False
config['rw_def_side'] = False
config['continuous_fighting'] = False
if 'debug' not in config:
config['debug'] = parse_input('Should I generate debug files')
if 'random_sleep' not in config:
config['random_sleep'] = parse_input('Should I add random amount (0-120sec) to sleep time')
if 'gold_buy' not in config:
config['gold_buy'] = parse_input('Should I auto buy 10g every day')
if 'interactive' not in config:
config['interactive'] = parse_input('Should I print output to console?')
return config
def normalize_html_json(js: str) -> str:
js = re.sub(r' \'(.*?)\'', lambda a: '"%s"' % a.group(1), js)
js = re.sub(r'(\d\d):(\d\d):(\d\d)', r'\1\2\3', js)
js = re.sub(r'([{\s,])(\w+)(:)(?!"})', r'\1"\2"\3', js)
js = re.sub(r',\s*}', '}', js)
return js
def process_error(log_info: str, name: str, exc_info: tuple, citizen=None, commit_id: str = None,
interactive: bool = False):
"""
Process error logging and email sending to developer
:param error:
:param interactive: Should print interactively
:param log_info: String to be written in output
:param name: String Instance name
:param exc_info: tuple output from sys.exc_info()
:param citizen: Citizen instance
:param commit_id: Code's version by commit id
"""
type_, value_, traceback_ = exc_info
bugtrace = [] if not commit_id else ["Commit id: %s" % commit_id, ]
bugtrace += [str(value_), str(type_), ''.join(traceback.format_tb(traceback_))]
if interactive:
write_interactive_log(log_info)
else:
write_silent_log(log_info)
send_email(name, bugtrace, citizen, local_vars=inspect.trace()[-1][0].f_locals)
def aviator_support(citizen, send_food=False, free_food=None):
forbidden_ids = []
if free_food is None:
free_food = {} # {"q1": 0, "q2": 1000, ...}
context = {'PLAYER_COUNT': 0, 'TABLE': "",
'STARTING_ENERGY': sum([amount * FOOD_ENERGY[q] for q, amount in free_food.items()]),
'TOTAL_CC': 0, 'TOTAL_ENERGY': 0, 'END_ENERGY': 0}
from erepublik_script import Citizen
if not isinstance(citizen, Citizen):
from .classes import ErepublikException
raise ErepublikException("\"citizen\" must be instance of erepublik.Citizen")
citizen.config.interactive = True
aviators = dict()
time_string = "%Y-%m-%d %H:%M:%S"
latest_article = requests.get('https://erep.lv/aviator/latest_article/').json()
for quality, amount in latest_article.get('free_food', {}).items():
free_food[quality] = free_food.get(quality, 0) + amount
if not latest_article.get('status'):
from .classes import ErepublikException
raise ErepublikException('Article ID and week problem')
context.update(WEEK=latest_article.get('week', 0) + 1)
comments = citizen.post_article_comments(citizen.token, latest_article.get('article_id'), 1).json()
ranking = citizen.get_leaderboards_kills_aircraft_rankings(71, 1, 0).json()
if not comments.get("comments", {}):
from .classes import ErepublikException
raise ErepublikException("No comments found")
for comment_data in comments.get("comments", {}).values():
if comment_data.get('authorId') == 1954361:
start_dt = localize_dt(datetime.datetime.strptime(comment_data.get('createdAt'), time_string))
days_ahead = 1 - start_dt.weekday()
if days_ahead <= 0:
days_ahead += 7
end_dt = (good_timedelta(start_dt, datetime.timedelta(days_ahead))).replace(hour=0, minute=0, second=0)
if not comment_data.get('replies', {}):
from .classes import ErepublikException
raise ErepublikException("No replies found")
for reply_data in comment_data.get('replies').values():
if localize_dt(datetime.datetime.strptime(reply_data.get('createdAt'), time_string)) > end_dt:
continue
if re.search(r'piesakos', reply_data.get('message'), re.I):
aviators.update({int(reply_data.get('authorId')): dict(
id=reply_data.get('authorId'), name="", kills=0, rank=0, residency=None, health=0, extra=[],
factories=0
)})
context['PLAYER_COUNT'] = len(aviators)
write_interactive_log("{:^9} | {:<28} | {:4} | {:26} | {:6} | {}".format(
"ID", "Vārds", "Kili", "Gaisa rangs", "Energy", "Aktivizētās mājas"
))
for player_top_data in ranking.get('top'):
player_id = int(player_top_data.get('id'))
if player_id in aviators:
aviators[player_id]["kills"] = int(player_top_data['values'])
for aviator_id, aviator_data in aviators.items():
aviator_info = citizen.get_citizen_profile(aviator_id).json()
aviator_data.update({
'rank': aviator_info['military']['militaryData']['aircraft']['rankNumber'],
'name': aviator_info['citizen']['name'],
'residency': aviator_info['city']['residenceCityId']
})
if aviator_info.get("isBanned"):
aviator_data.update({'health': 0, 'extra': ["BANNED", ]})
else:
if aviator_data['rank'] < 44:
if aviator_data['rank'] < 38:
health = aviator_data['kills'] * 30
else:
health = aviator_data['kills'] * 20
has_pp = False
if aviator_info.get("activePacks"):
has_pp = bool(aviator_info.get("activePacks").get("power_pack"))
max_health = 7 * 24 * (500 if has_pp else 300)
if health < max_health:
aviator_data['health'] = health
else:
aviator_data['health'] = max_health
if not aviator_data["residency"]:
aviator_data['health'] = 0
aviator_data['extra'].append("No residency set")
else:
residency = citizen.get_city_data_residents(
aviator_data["residency"], params={"search": aviator_data['name']}
).json()
for resident in residency.get('widgets', {}).get('residents', {}).get('residents'):
if int(resident.get('citizenId')) == aviator_id:
if resident['numFactories']:
aviator_data['factories'] = resident['numFactories']
else:
aviator_data['factories'] = 0
if not resident.get('activeHouses'):
aviator_data['health'] = 0
if resident['numHouses']:
aviator_data['extra'].append(", ".join(resident['activeHouses']))
else:
aviator_data['extra'].append("Nav māja")
aviator_data['health'] = 0
else:
aviator_data['extra'].append("Rank")
write_interactive_log("{id:>9} | {name:<28} | {kills:4} | {:26} | {health:6} | {}".format(
AIR_RANKS[aviator_data['rank']],
", ".join(aviator_data["extra"]),
**aviator_data)
)
db_post_data = []
for aviator_id, aviator_data in aviators.items():
db_post_data.append(dict(id=aviator_id, name=aviator_data['name'],
rank=aviator_data['rank'], factory_count=aviator_data['factories']))
requests.post('https://erep.lv/aviator/set/', json=db_post_data)
for aviator_id, new in aviators.items():
resp = requests.get('https://erep.lv/aviator/check/{}/'.format(aviator_id))
if not resp.json()['status']:
aviators[aviator_id]['health'] = 0
aviators[aviator_id]['extra'] = ["Nav izmaiņas fabriku skaitā", ]
for player_id in forbidden_ids:
if player_id in aviators:
aviators[player_id]['health'] = 0
if "BANNED" not in aviators[player_id]['extra']:
aviators[player_id]['extra'] = ["Aizliegta pieteikšanās", ]
sent_data = []
if send_food:
for aviator_data in sorted(aviators.values(), key=lambda t: (-t["health"], -t['kills'])):
remaining = aviator_data['health']
if not remaining:
sent_data.append({
"player_id": aviator_data['id'], "name": aviator_data['name'], "quality": 0,
"amount": 0, "energy": 0, "price": 0, "cost": 0,
})
while remaining > 0:
o = []
if free_food:
# Reversed because need to start with higher qualities so that q1 stays available
for quality in reversed(list(free_food.keys())):
if free_food[quality]:
o.append((quality, {'price': 0., 'amount': free_food[quality]}))
else:
free_food.pop(quality)
if not free_food:
offers = citizen.get_market_offers(71, product="food")
o += sorted(offers.items(), key=lambda v: (v[1]['price'] / FOOD_ENERGY[v[0]],
-v[1]['amount'] * FOOD_ENERGY[v[0]]))
for _o in o:
q, q_data = _o
if FOOD_ENERGY[q] <= remaining:
break
else:
write_interactive_log(
"{name} needs to receive extra {remaining}hp".format(name=aviator_data['name'],
remaining=remaining))
break
if q_data['amount'] * FOOD_ENERGY[q] <= remaining:
amount = q_data['amount']
else:
amount = remaining // FOOD_ENERGY[q]
if q_data['price']:
# print(f"citizen._buy_market_offer(offer={q_data['offer_id']}, amount={amount})")
citizen.post_economy_marketplace_actions(citizen.token, amount=amount, buy=True,
offer=q_data["offer_id"])
else:
free_food[q] -= amount
# print(f"citizen.donate_items(citizen_id={aviator_data['id']},
# amount={amount}, industry_id=1, quality={int(q[1])})")
citizen.donate_items(citizen_id=aviator_data['id'], amount=amount, industry_id=1, quality=int(q[1]))
remaining -= amount * FOOD_ENERGY[q]
context['TOTAL_CC'] += q_data['price'] * amount
context['TOTAL_ENERGY'] += amount * FOOD_ENERGY[q]
sent_data.append(
{"player_id": aviator_data['id'], "name": aviator_data['name'], "quality": q, "amount": amount,
"energy": amount * FOOD_ENERGY[q], "price": q_data['price'],
"cost": q_data['price'] * amount, })
with open(get_file("{eday}.csv".format(eday=eday_from_date(now()))), 'a') as f:
f.write('PlayerID, Quality, Amount, Energy, Price, Cost\n')
for player_data in sent_data:
f.write('{player_id}, {quality}, {amount}, {energy}, {price}, {cost}\n'.format(**player_data))
columns = ('[columns][b]Spēlētajs[/b]\n'
'{players}[nextcol][b]Kili[/b]\n'
'{kills}\n'
'[nextcol][right][b]Enerģija[/b]\n'
'{health}\n'
'[/right][/columns]')
player_template = '[b][url=https://www.erepublik.com/en/citizen/profile/{id}]{name}[/url][/b]'
players = []
kills = []
health = []
write_interactive_log("\n".join(["{}: {}".format(q, a) for q, a in free_food.items()]))
context['TOTAL_CC'] = round(context['TOTAL_CC'], 2)
context["END_ENERGY"] = sum([amount * FOOD_ENERGY[q] for q, amount in free_food.items()])
data = {}
for row in sent_data:
pid = int(row['player_id'])
if pid not in data:
data.update({pid: dict(id=pid, name=row['name'], energy=0, cost=0, kills=aviators[pid]['kills'])})
data[pid]["energy"] += row['energy']
data[pid]["cost"] += row['cost']
for pid, player_data in sorted(aviators.items(), key=lambda t: (-t[1]["health"], -t[1]['kills'])):
players.append(player_template.format(id=pid, name=player_data['name']))
kills.append(str(player_data['kills']))
health.append(str(player_data['health'] or ", ".join(player_data['extra'])))
else:
context['TABLE'] = columns.format(
players="\n".join(players),
kills="\n".join(kills),
health="\n".join(health)
)
if os.path.isfile("scripts/KM_piloti.txt"):
with open("scripts/KM_piloti.txt") as f:
template = f.read()
article = template.format(**context)
with open(get_file("{eday}.txt".format(eday=eday_from_date(now()))), "w") as f:
f.write(article)
if send_food:
article_data = dict(
title="[KM] Gaisa maizītes [d{} {}]".format(citizen.eday, citizen.now.strftime("%H:%M")),
content=article,
kind=3
)
from_eday = eday_from_date(good_timedelta(now(), - datetime.timedelta(days=now().weekday() + 6)))
till_eday = eday_from_date(good_timedelta(now(), - datetime.timedelta(days=now().weekday())))
comment_data = dict(
message="★★★★ MAIZE PAR NEDĒĻU [DAY {}-{}] IZDALĪTA ★★★★\n★ Apgādei piesakāmies šī komentāra reply "
"komentāros ar saucienu - piesakos! ★".format(from_eday, till_eday))
total_cc = int(round(context['TOTAL_CC']))
wall_body = ("★★★ [ KONGRESA BALSOJUMS ] ★★★\n\nDotācija pilotiem par d{}-{} {}cc apmērā.\n\n"
"Balsot ar Par/Pret\nBalsošanas laiks 24h līdz d{} {}").format(
from_eday, till_eday, total_cc, citizen.eday + 1, citizen.now.strftime("%H:%M"))
citizen.write_log("Publishing info:\n\n### Article ###\n{}\n\n{}\n\n### Wall ###\n{}".format(
article_data['title'], comment_data['message'], wall_body
))
KM_account: Citizen = Citizen("kara-ministrija@erep.lv", "KMPar0le")
KM_account.set_debug(True)
KM_account.update_citizen_info()
resp = KM_account.publish_article(**article_data)
article_id = resp.history[1].url.split("/")[-3]
comment_data.update({"article_id": article_id})
KM_account.write_article_comment(**comment_data)
citizen.write_on_country_wall(wall_body)
requests.post('https://erep.lv/aviator/latest_article/',
data=dict(week=context["WEEK"], article_id=article_id))

View File

@ -1,7 +0,0 @@
ipython==7.3.0
pycryptodome==3.7.3
PyInstaller==3.4
python-slugify==2.0.1
pytz==2018.9
requests==2.21.0
Click>=6.0

View File

@ -1,10 +1,14 @@
pip==19.1.1
bumpversion==0.5.3
wheel==0.32.1
wheel==0.33.4
watchdog==0.9.0
flake8==3.5.0
tox==3.5.2
coverage==4.5.1
Sphinx==1.8.1
twine==1.12.1
ipython==7.3.0
flake8==3.7.8
tox==3.13.2
coverage==4.5.3
Sphinx==2.2.0
twine==2.0.0
ipython
PyInstaller
pytz==2019.1
requests==2.22.0
edx-sphinx-theme

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.1.2
current_version = 0.17.3
commit = True
tag = True
@ -7,7 +7,7 @@ tag = True
search = version='{current_version}'
replace = version='{new_version}'
[bumpversion:file:erepublik_script/__init__.py]
[bumpversion:file:erepublik/__init__.py]
search = __version__ = '{current_version}'
replace = __version__ = '{new_version}'
@ -17,7 +17,7 @@ universal = 1
[flake8]
exclude = docs
max-line-length = 120
ignore = E722
ignore = E722 F401
[aliases]

View File

@ -11,40 +11,37 @@ with open('README.rst') as readme_file:
with open('HISTORY.rst') as history_file:
history = history_file.read()
requirements = ['Click>=6.0', 'pytz==2018.9', 'requests==2.21.0', 'python-slugify==2.0.1']
requirements = ['pytz>=2019.2', 'requests>=2.22']
setup_requirements = [ ]
setup_requirements = []
test_requirements = [ ]
test_requirements = []
setup(
author="Eriks Karls",
author_email='eriks@72.lv',
classifiers=[
'Development Status :: 2 - Pre-Alpha',
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Natural Language :: English',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
],
description="Python package for eRepublik automated playing",
entry_points={
'console_scripts': [
'erepublik_script=erepublik_script.cli:main',
],
},
description="Python package for automated eRepublik playing",
entry_points={},
install_requires=requirements,
license="MIT license",
long_description=readme + '\n\n' + history,
include_package_data=True,
keywords='erepublik_script',
name='erepublik_script',
packages=find_packages(include=['erepublik_script']),
keywords='erepublik',
name='eRepublik',
packages=find_packages(include=['erepublik']),
python_requires='>=3.7.*, <4',
setup_requires=setup_requirements,
test_suite='tests',
tests_require=test_requirements,
url='https://github.com/eeriks/erepublik_script',
version='0.1.2',
url='https://github.com/eeriks/erepublik/',
version='0.17.3',
zip_safe=False,
)

View File

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Unit test package for erepublik_script."""
"""Unit test package for erepublik."""

View File

@ -1,34 +1,130 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Tests for `erepublik_script` package."""
"""Tests for `erepublik` package."""
import unittest
from click.testing import CliRunner
from erepublik_script import Citizen
from erepublik_script import cli
from erepublik import Citizen
class TestErepublik_script(unittest.TestCase):
"""Tests for `erepublik_script` package."""
class TestErepublik(unittest.TestCase):
"""Tests for `erepublik` package."""
def setUp(self):
"""Set up test fixtures, if any."""
self.citizen = Citizen("email", "password", False)
self.citizen.config.interactive = False
def tearDown(self):
"""Tear down test fixtures, if any."""
def test_should_do_levelup(self):
self.citizen.energy.recovered = 1900
self.citizen.energy.recoverable = 2940
self.citizen.energy.interval = 30
self.citizen.energy.limit = 3000
self.citizen.details.xp = 14850
self.assertTrue(self.citizen.should_do_levelup)
def test_000_something(self):
"""Test something."""
self.citizen.energy.recoverable = 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.recovered = 1900
self.citizen.energy.recoverable = 2940
self.citizen.energy.interval = 30
self.citizen.energy.limit = 3000
self.citizen.details.xp = 14850
self.assertTrue(self.citizen.should_travel_to_fight())
self.citizen.details.xp = 15000
self.assertFalse(self.citizen.should_travel_to_fight())
self.citizen.energy.recovered = 3000
self.citizen.energy.recoverable = 2910
self.assertTrue(self.citizen.should_travel_to_fight())
self.citizen.energy.recoverable = 2900
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 = 5000
self.citizen.details.next_pp = [5000, 5250, 5750, 6250, 6750]
self.citizen.details.pp = 4900
self.citizen.energy.recovered = 4000
self.citizen.energy.recoverable = 4510
self.assertEqual(self.citizen.next_reachable_energy, 850)
self.citizen.energy.recoverable = 4490
self.assertTrue(self.citizen.should_travel_to_fight())
self.assertEqual(self.citizen.next_reachable_energy, 350)
self.citizen.energy.recovered = 100
self.citizen.energy.recoverable = 150
self.assertFalse(self.citizen.should_travel_to_fight())
self.assertEqual(self.citizen.next_reachable_energy, 0)
def test_should_fight(self):
self.citizen.config.fight = False
self.assertEqual(self.citizen.should_fight(), 0)
self.citizen.config.fight = True
# Level up
self.citizen.energy.limit = 3000
self.citizen.details.xp = 24705
self.assertEqual(self.citizen.should_fight(), 0)
self.citizen.energy.recovered = 3000
self.citizen.energy.recoverable = 2950
self.citizen.energy.interval = 30
self.assertEqual(self.citizen.should_fight(), 900)
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), 900)
self.citizen.my_companies.ff_lockdown = 0
# Level up reachable
self.citizen.details.xp = 24400
self.assertEqual(self.citizen.should_fight(), 305)
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), 305)
self.citizen.my_companies.ff_lockdown = 0
self.citizen.details.xp = 21000
self.assertEqual(self.citizen.should_fight(), 75)
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), 75)
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)
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), 435)
self.citizen.my_companies.ff_lockdown = 0
self.citizen.config.air = True
self.citizen.energy.recoverable = 1000
self.assertEqual(self.citizen.should_fight(), 400)
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), 240)
self.citizen.my_companies.ff_lockdown = 0
self.citizen.config.all_in = False
self.citizen.config.next_energy = True
self.citizen.energy.limit = 5000
self.citizen.details.next_pp = [100, 150, 250, 400, 500]
self.assertEqual(self.citizen.should_fight(), 320)
self.citizen.my_companies.ff_lockdown = 160
self.assertEqual(self.citizen.should_fight(), 160)
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.recoverable = 2910
self.assertEqual(self.citizen.should_fight(), 30)
def test_command_line_interface(self):
"""Test the CLI."""
runner = CliRunner()
result = runner.invoke(cli.main)
assert result.exit_code == 0
assert 'erepublik_script.cli.main' in result.output
help_result = runner.invoke(cli.main, ['--help'])
assert help_result.exit_code == 0
assert '--help Show this message and exit.' in help_result.output

View File

@ -1,10 +1,9 @@
[tox]
envlist = py36, py37, flake8
envlist = py37, flake8
[travis]
python =
3.7: py37
3.6: py36
[testenv:flake8]
basepython = python