Compare commits
590 Commits
Author | SHA1 | Date | |
---|---|---|---|
8cf86fb9d3 | |||
cf927df6e6 | |||
a6f5dbd05f | |||
967afa472f | |||
a65568cd0c | |||
6e45334d99 | |||
936a1010a6 | |||
acc528cb1d | |||
614d273104 | |||
95966764e8 | |||
f52b078e6a | |||
3af27f6512 | |||
6276242260 | |||
45623de97b | |||
25f932121c | |||
61be2b1edf | |||
ce9034ad24 | |||
69b2073b74 | |||
eb048bf9f8 | |||
fe1206dc84 | |||
a2a1ed3dad | |||
39c8f6913e | |||
d7b15b3708 | |||
4fe3efa045 | |||
14bcb46735 | |||
b04cc896d8 | |||
f07062788b | |||
4504bdaa97 | |||
ac135614cc | |||
41752e1f2e | |||
a1739e627e | |||
12ff11deea | |||
4e3a16b8d4 | |||
5f56f59ab8 | |||
50c66efbda | |||
47b3154c6a | |||
632e4e8ad2 | |||
7c0d66f126 | |||
842fb64dae | |||
b22349cb1a | |||
a9ced91741 | |||
e374562189 | |||
e0b64e09b1 | |||
c51337d249 | |||
f4896e0b79 | |||
13f5c673ad | |||
e95ffbd505 | |||
5e638806b5 | |||
7860fa3669 | |||
e38f603e8b | |||
0e1c42a8fb | |||
ddc412b348 | |||
7f3bd9b864 | |||
9154d2e493 | |||
8e8b882ace | |||
d7b020c7ea | |||
cb567bf5c0 | |||
bb6d1be1b5 | |||
c546c8f5eb | |||
9acc2d2e65 | |||
51a15874f2 | |||
af8f764396 | |||
65b555f2bd | |||
319b4414df | |||
b71cb0c263 | |||
0936dee06c | |||
a1c6fb06a0 | |||
c57bf99976 | |||
995b45464f | |||
ebe28c948a | |||
34d7230faf | |||
3235991cce | |||
3fba1d6b3d | |||
88c8d5a9a0 | |||
382749a8d8 | |||
0c877e315b | |||
c95f642fee | |||
da0276f9a6 | |||
f89f91e969 | |||
56c2ca1b6e | |||
3cee2d1f0f | |||
0ed03877ce | |||
491c9f5efe | |||
8445b556e7 | |||
d02c5c2969 | |||
bff4183cb6 | |||
47f5142837 | |||
a32fd039dd | |||
311e684c0c | |||
52038a86d5 | |||
c35a107641 | |||
b53b2f0fae | |||
8da9b6221a | |||
caa41c85f6 | |||
da3b5d5768 | |||
f96d0233f9 | |||
c693a5182e | |||
0dfba6a9ff | |||
ee3eca9658 | |||
0e8680daca | |||
8e3606f1a3 | |||
bd0bcc9ac7 | |||
c6f2226e64 | |||
308807d800 | |||
91565d840e | |||
b4a9dd88f8 | |||
8435aa23ba | |||
09cd275a69 | |||
e23a67231e | |||
6a03d99abf | |||
2e344749a6 | |||
5aecefbd9d | |||
8b9ee5042d | |||
1e93006c3d | |||
b13bfcdbf3 | |||
771dbdf826 | |||
9646d112d2 | |||
a09c37a065 | |||
ba75e961fa | |||
3b5780dbd6 | |||
fccd0134b5 | |||
b9010fa856 | |||
3e5410289e | |||
661a019b0a | |||
23d682959d | |||
5806ccb6ca | |||
a9bc78b701 | |||
c458eb4b1c | |||
4af4d284c9 | |||
104c1a0b16 | |||
86f820771b | |||
2a7af0cb7d | |||
94de509026 | |||
82d913bc47 | |||
c462eac369 | |||
d522a4faeb | |||
084a47b07a | |||
5082855440 | |||
c9971f3570 | |||
c0122eccdf | |||
2524173da0 | |||
4a4b991faf | |||
e87c48124c | |||
f0f47566a0 | |||
f88eaccf67 | |||
7be129a781 | |||
32546505b9 | |||
00ceabf8e3 | |||
19113da8e6 | |||
3ad7172925 | |||
179f1a0892 | |||
65adf707e2 | |||
29f9ce5ccc | |||
fa3881bf10 | |||
c1e8e94cba | |||
8133461fd7 | |||
c87333411a | |||
d679006b34 | |||
fa308db074 | |||
a080163af9 | |||
8aa159edf7 | |||
1211a98227 | |||
734a5ef2b6 | |||
5c3b405ca8 | |||
75de8fce96 | |||
01e5e44350 | |||
500409f74a | |||
9d79bb713c | |||
684b2a6ba4 | |||
447aee8134 | |||
64249fc3d9 | |||
95a78b6e7e | |||
b338ea598a | |||
b88e7973e8 | |||
c671425531 | |||
08937d06e4 | |||
cec4510831 | |||
cfb9501647 | |||
d419211955 | |||
0ea144db17 | |||
1418f580cd | |||
49575bddf5 | |||
c874247335 | |||
6e9def4394 | |||
d6fbaa7945 | |||
eb740c60c7 | |||
240c409739 | |||
f348a315c5 | |||
7a09eea2b4 | |||
81b1069cf0 | |||
429d43df15 | |||
c81986d65e | |||
7bb988f716 | |||
16cae24712 | |||
3e051fe906 | |||
aa9cda9314 | |||
fc66db8cab | |||
6bbc4f8768 | |||
160b32a914 | |||
a51c3c620e | |||
a1c26468eb | |||
4895ae3663 | |||
b8d7cc8d7c | |||
1d0645b490 | |||
30cf6203b7 | |||
a32e88218d | |||
a031da0ee7 | |||
bdb13fa4ae | |||
e1e3b33d46 | |||
e09ca143b1 | |||
61d0599295 | |||
1ef600492a | |||
377eda6445 | |||
fd13667ca8 | |||
0479afcabe | |||
486f022f35 | |||
638373e452 | |||
04f357cc70 | |||
ed4ffe5af6 | |||
8aa90a7dbf | |||
e798859105 | |||
241f1642ce | |||
a4128b5d89 | |||
22c2a0ffd2 | |||
38f0335354 | |||
889435b94e | |||
bb16c27674 | |||
963d7ca11a | |||
36c7fefdf7 | |||
d9fa30b06e | |||
b53dc447f4 | |||
233d8d83f8 | |||
ec62d90aa2 | |||
0c433a56da | |||
ad24338f4d | |||
6f4bc65d1b | |||
cc09ba7ee7 | |||
9e1166a460 | |||
fb0042c00d | |||
bb800578e7 | |||
7025f750dc | |||
bf77f21b60 | |||
6b7639d7fb | |||
3b1c1928fd | |||
2e26c2db79 | |||
78c055fee2 | |||
fe41c4cdc6 | |||
123b6cf4ed | |||
f652b02443 | |||
73537e4742 | |||
955432e0d2 | |||
1d93864dca | |||
c472d688be | |||
bff9a2bec9 | |||
973ea40e00 | |||
52c85fdf28 | |||
a889e9efa1 | |||
a9a0cdc6d5 | |||
1c102488b6 | |||
c38acef2a0 | |||
48b5e473aa | |||
7fadeb1a49 | |||
b723660f23 | |||
f10eeec498 | |||
230167f93d | |||
d5ed989e80 | |||
6fc24b8adf | |||
cf797f2f60 | |||
ad29045ace | |||
c919e46af5 | |||
644b4d70e1 | |||
6dbbd054ba | |||
0ee952e504 | |||
bb9b198a53 | |||
cb22e631ca | |||
c43e20c8f6 | |||
c8f41b97af | |||
d483bcbcb9 | |||
a316f277fb | |||
e8c81d17e6 | |||
edb4d8851b | |||
76edd6bb7d | |||
8da376b852 | |||
b6b0be7442 | |||
ed434b605d | |||
2bd311def6 | |||
4b437c2ba6 | |||
cc8a1450d4 | |||
0c9f3756ea | |||
9de07950b8 | |||
766b7b2d95 | |||
c730981865 | |||
d70c3e2c9e | |||
d044af6d2f | |||
dd75b10d2f | |||
a45dd7e0c3 | |||
316a826c93 | |||
c9710733e2 | |||
9e678d6b51 | |||
88517cb076 | |||
01c8aef012 | |||
d7ac66bd69 | |||
e8739caca1 | |||
9df9c1cd87 | |||
1b004f163a | |||
72bd2787d3 | |||
bfa6cb1e78 | |||
1db7c98b47 | |||
1f21a71c74 | |||
0531c8997b | |||
5a8a0a3920 | |||
d938908986 | |||
ffbbd25e54 | |||
81bd09e13e | |||
fc4295d8bd | |||
7bbf7cb34a | |||
8ce56a31f7 | |||
ef23b3b8db | |||
cd861ea29b | |||
5b580f7c79 | |||
0061503581 | |||
c78dbae925 | |||
6a9b574454 | |||
cde97c6259 | |||
7fa02be7d3 | |||
098bfe5f3f | |||
c26689a8fa | |||
94c416faa5 | |||
ba840765be | |||
3927b9ba6b | |||
cd5c0b161f | |||
39defc0fd7 | |||
6c8dbcd99e | |||
d07334f602 | |||
bcb27da54f | |||
7ac22b5e11 | |||
5bd3d72a63 | |||
33d2c641df | |||
d1e078e443 | |||
71c64b0cf5 | |||
3b5b15553d | |||
d077e10f15 | |||
8eb5235f12 | |||
a873d223c1 | |||
ed96143c80 | |||
b49e4ab594 | |||
2f69090c03 | |||
df170048af | |||
8ca845cf17 | |||
ce7874fbf5 | |||
6abfc98fbd | |||
66f0e648df | |||
7cf6cf0e12 | |||
a825917a98 | |||
603604213d | |||
f83df449ae | |||
b480ed7230 | |||
67677f356f | |||
ff869e0403 | |||
845cd8d174 | |||
4e337177f2 | |||
eed244deb5 | |||
1e7c9a395e | |||
7aa353bc06 | |||
6642839af5 | |||
c216d98287 | |||
588475d554 | |||
ab3ce2b8c3 | |||
a208c8bcf0 | |||
09c6255b84 | |||
ff2a0e02dc | |||
5712007e3f | |||
f64a9dc709 | |||
4cfe25b0b1 | |||
c094ef26b4 | |||
3cac1cf041 | |||
ac4fc9baab | |||
b760a2f66c | |||
5c47b70ea6 | |||
05964f6c58 | |||
d95c472ede | |||
49726b8cec | |||
9a0cbf77da | |||
904fd4efc8 | |||
1bbe72f3e1 | |||
2aa1cbd79e | |||
2eecd9fd4d | |||
2efc9496a0 | |||
1b5b5f736f | |||
4b4ed18cdb | |||
d928ffc8df | |||
260344bcbe | |||
d4a3719645 | |||
98947e6bbe | |||
24d81bbadf | |||
93f2f2887f | |||
7ec15a9645 | |||
e0c09672b1 | |||
d6a0d5a704 | |||
22dc18d80d | |||
772c09a2af | |||
bf5f3d74b3 | |||
70e78023eb | |||
8dcebdecd2 | |||
77bcfb3df6 | |||
a01b85154f | |||
04bb0be837 | |||
62e265e7e1 | |||
dd2c20cc41 | |||
b76ea2c4ae | |||
b7211b7c75 | |||
07ba1795d3 | |||
ce02a39158 | |||
a5dfc07018 | |||
e6ce02fc09 | |||
149071ae86 | |||
72375f40ca | |||
fd1880c50f | |||
f73f2b7b9f | |||
f6433908b4 | |||
2fd317153f | |||
256a180bd6 | |||
c7dbeb2078 | |||
8e5ae0320a | |||
5c258d7aae | |||
75b43fc455 | |||
2362dc51e8 | |||
a2cf479135 | |||
00b87dc832 | |||
0dd1ae9ac5 | |||
76bd40c655 | |||
15e6deebda | |||
69d0e7df0a | |||
4f92894ab6 | |||
9c64bfac0f | |||
1f07f2e270 | |||
71d204843d | |||
d9305214eb | |||
5556d5f772 | |||
1c47d169d2 | |||
ef44787bad | |||
42431134e1 | |||
bedaeeefd1 | |||
bbf304aa99 | |||
a2447959e7 | |||
700bd8d98e | |||
3599dc40fc | |||
6ba727a781 | |||
7f1829a5d7 | |||
e374aa8a54 | |||
7edfa3b004 | |||
12aee23739 | |||
b7f8182ef5 | |||
39093accd0 | |||
aba8c15fd3 | |||
f294506a2d | |||
fd56c6c389 | |||
4f613ee5ac | |||
a7dd528597 | |||
24c755d414 | |||
13b639dc5a | |||
ec1141a46e | |||
77170433c2 | |||
4736f70203 | |||
48a27997ac | |||
90bec82630 | |||
aedfbf4465 | |||
66f459c692 | |||
ef27960ff1 | |||
c48af9a891 | |||
1abfdb71ac | |||
e060f67666 | |||
06d8d1c0b5 | |||
adda8dcb54 | |||
c7f084436d | |||
94a87091a4 | |||
c0b97f112d | |||
3d895bd085 | |||
d548d1bbf1 | |||
b1eefcc662 | |||
41798c446c | |||
074da3adbe | |||
6c9a9e920d | |||
ffa2fc109c | |||
f7f4028f32 | |||
e91705ce90 | |||
ca65a1ffe1 | |||
07c8881092 | |||
25e534f783 | |||
daa071f0f5 | |||
2f8120bd0d | |||
6b2c073abe | |||
c298d66086 | |||
bf971972bf | |||
50f3d291ca | |||
b03140f2b8 | |||
fe9a118875 | |||
17c73c79a7 | |||
7533608316 | |||
71a7f55338 | |||
0ca0f49f92 | |||
3f1b0018b2 | |||
2aaa4aad65 | |||
b44a3a4b62 | |||
3a7dd9a6fa | |||
630c7cbc76 | |||
1ee47dfdcf | |||
ab34135b73 | |||
20bba4b9f9 | |||
8db4ab1f0f | |||
acbf1590d7 | |||
63dd2d4f77 | |||
896b1b2432 | |||
60543e02c8 | |||
b06e9a2933 | |||
4eb96d7f1e | |||
c8a1d8c8e8 | |||
c85f0417f2 | |||
7573f29950 | |||
597d27117c | |||
95570b7c17 | |||
bc4ba671d6 | |||
088219a60a | |||
53266b1c94 | |||
065a0ff3ec | |||
c8e90ca910 | |||
bbab84bf5b | |||
fff17469e0 | |||
0d208a8d32 | |||
86004eb81b | |||
7e05e35ebf | |||
411b158487 | |||
d6e161d815 | |||
1285e95cec | |||
324db2c136 | |||
1c6b41e41b | |||
a52552afb7 | |||
28bfdc7b20 | |||
de1b734728 | |||
fa5646ecfd | |||
339392cfb0 | |||
7ebba458cb | |||
4f436292af | |||
416c391d21 | |||
66c53ef985 | |||
3fa7d097fe | |||
6af9675c39 | |||
2343a6c6c8 | |||
7e56f01a38 | |||
e14cbc18e9 | |||
048ce798dd | |||
e5b7cde044 | |||
6bbc7a1f64 | |||
4eccb339bb | |||
dbeb6e9ba5 | |||
9c9bb5ae40 | |||
d8eb69f82a | |||
42c430213f | |||
39dbcaa27d | |||
8911adb81c | |||
7927c162f8 | |||
92b7c45a7d | |||
53257487d8 | |||
8690c4d3f2 | |||
43c6bce160 | |||
c4f598c1ba | |||
c48d90dec3 | |||
953902476f | |||
156cbbb61c | |||
b72039c865 | |||
9587538fdc | |||
e775679581 | |||
3aa305ea74 | |||
6b2e5ffb68 | |||
bb8634fe56 | |||
25a0d8993e | |||
a1d10bb427 | |||
ec701396d9 | |||
d8035b42e3 | |||
42320a14a4 | |||
de4b059b7d | |||
dc106cc87d | |||
bb2c13d63a | |||
ea48fbe7e1 | |||
65a3a9f678 | |||
0757345e17 | |||
6f4b32b12c | |||
69265a35e8 | |||
f12bd0ed57 | |||
2d246cbf4b |
@ -19,3 +19,9 @@ insert_final_newline = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.py]
|
||||
max_line_length = 240
|
||||
line_length=120
|
||||
multi_line_output=0
|
||||
balanced_wrapping=True
|
||||
|
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -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.
|
||||
```
|
||||
|
33
.github/workflows/pythonpackage.yml
vendored
Normal file
33
.github/workflows/pythonpackage.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
name: Python package
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7, 3.8]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements_dev.txt
|
||||
- name: Lint with flake8
|
||||
run: |
|
||||
pip install flake8
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
pip install pytest
|
||||
pytest
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -37,7 +37,6 @@ pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
@ -85,7 +84,7 @@ celerybeat-schedule
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
venv/
|
||||
venv*/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
@ -103,4 +102,4 @@ ENV/
|
||||
|
||||
debug/
|
||||
log/
|
||||
docs/
|
||||
*dump.json
|
||||
|
13
.pre-commit-config.yaml
Normal file
13
.pre-commit-config.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
|
||||
default_language_version:
|
||||
python: python3.8
|
16
.travis.yml
16
.travis.yml
@ -1,16 +0,0 @@
|
||||
# Config file for automatic testing at travis-ci.org
|
||||
|
||||
language: python
|
||||
python:
|
||||
- 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
|
||||
|
||||
# Command to run tests, e.g. python setup.py test
|
||||
script: tox
|
||||
|
||||
|
@ -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:
|
||||
|
||||
@ -23,12 +23,6 @@ If you are reporting a bug, please include:
|
||||
* Any details about your local setup that might be helpful in troubleshooting.
|
||||
* Detailed steps to reproduce the bug.
|
||||
|
||||
Fix Bugs
|
||||
~~~~~~~~
|
||||
|
||||
Look through the GitHub issues for bugs. Anything tagged with "bug" and "help
|
||||
wanted" is open to whoever wants to implement it.
|
||||
|
||||
Implement Features
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -45,7 +39,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 +51,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.
|
||||
1. Fork the `erepublik` 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::
|
||||
@ -76,14 +70,14 @@ Ready to contribute? Here's how to set up `erepublik_script` for local developme
|
||||
|
||||
Now you can make your changes locally.
|
||||
|
||||
5. When you're done making changes, check that your changes pass flake8 and the
|
||||
tests, including testing other Python versions with tox::
|
||||
5. When you're done making changes, check that your changes pass flake8, isort and the
|
||||
tests::
|
||||
|
||||
$ flake8 erepublik_script tests
|
||||
$ python setup.py test or py.test
|
||||
$ tox
|
||||
$ flake8 erepublik tests
|
||||
$ isort erepublik
|
||||
$ python setup.py test
|
||||
|
||||
To get flake8 and tox, just pip install them into your virtualenv.
|
||||
To get flake8 and isort, just pip install them into your virtualenv.
|
||||
|
||||
6. Commit your changes and push your branch to GitHub::
|
||||
|
||||
@ -102,8 +96,8 @@ 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
|
||||
https://travis-ci.org/eeriks/erepublik_script/pull_requests
|
||||
3. The pull request should work for Python 3.7.1. Check
|
||||
https://travis-ci.org/eeriks/erepublik/pull_requests
|
||||
and make sure that the tests pass for all supported Python versions.
|
||||
|
||||
Tips
|
||||
@ -112,7 +106,7 @@ Tips
|
||||
To run a subset of tests::
|
||||
|
||||
|
||||
$ python -m unittest tests.test_erepublik_script
|
||||
$ python -m unittest tests.test_erepublik
|
||||
|
||||
Deploying
|
||||
---------
|
||||
|
134
HISTORY.rst
134
HISTORY.rst
@ -2,6 +2,140 @@
|
||||
History
|
||||
=======
|
||||
|
||||
0.23.4 (2021-01-05)
|
||||
-------------------
|
||||
* Added expiration data to inventory items
|
||||
* Inventory is now based on `classes.Inventory`
|
||||
* Requirement update to make them more flexible regarding versions required
|
||||
* Restructured inventory
|
||||
|
||||
0.23.3 (2020-12-17)
|
||||
-------------------
|
||||
* Fixed carpet bombing
|
||||
* Fixed hits done amount when fighting on ground
|
||||
* Minor requirement updates
|
||||
* Minor tweaks to method signatures
|
||||
* Fixed buy food if unable to work or train because not enough energy and not enough food
|
||||
* Fixed applications for party presidency and congress if not a party member
|
||||
* Removed tox
|
||||
* Updates to github.io config generator
|
||||
* Fixed `Citizen.concurrency_available` stuck in unset state if exception is being raised while doing concurrency task
|
||||
|
||||
0.23.2 (2020-12-01)
|
||||
-------------------
|
||||
* Added concurrency checks to guard against simultaneous fighting/wam'ing/traveling
|
||||
* For concurrency checking use `utils.wait_for_lock` decorator
|
||||
|
||||
0.23.1 (2020-12-01)
|
||||
-------------------
|
||||
* Separated battle finding logic from CitizenMilitary.find_battle_and_fight method
|
||||
* Base dmg calculations
|
||||
* Get max hit value for divisions on current side
|
||||
* Added method to get division stats
|
||||
* Wheel of fortune updates
|
||||
|
||||
0.23.0 (2020-11-26)
|
||||
-------------------
|
||||
* ***0.23 - last officially supported version for Python 3.7.***
|
||||
* Added `Config.maverick` switch, to allow/deny automated fighting in non native divisions if the player has MaverickPack
|
||||
* Added `CitizenMedia.get_article(article_id:int)` method to get article data
|
||||
* Added `CitizenMedia.delete_article(article_id:int)` method to delete article
|
||||
* Fixed `CitizenTravel.travel_to_region(region_id:int)` method
|
||||
* Added `CitizenAnniversary.collect_map_quest_node(node_id:int, extra:bool=False)` to collect also extra rewards
|
||||
* Fixed `CitizenTasks.work()` when employer out of money - resign and find a new job
|
||||
* Fixed `CitizenEconomy.post_market_offer()`
|
||||
|
||||
0.22.3 (2020-11-16)
|
||||
-------------------
|
||||
* Fixed round to even bug when doing wam and not enough raw.
|
||||
* Added meta industry airplaneRaw
|
||||
* Added method `Citizen.buy_market_offer(OfferItem, amount=None)` to directly buy market offer with included travel to country and back.
|
||||
|
||||
0.22.2 (2020-11-09)
|
||||
-------------------
|
||||
* Allow querying market offers for q2-q5 aircrafts
|
||||
* Added "Ticket" industry
|
||||
|
||||
0.22.1 (2020-11-04)
|
||||
-------------------
|
||||
* Requirement update
|
||||
* Unified product naming in inventory and other places based on `erepublik.constants.INDUSTRIES` values
|
||||
* `erepublik.Citizen` parameter `auto_login` now defaults to `False`
|
||||
* Continued work on more verbose action and result logging
|
||||
|
||||
0.22.0 (2020-10-22)
|
||||
-------------------
|
||||
* Ability to dump session and restore from file
|
||||
* Proxy support
|
||||
* Inventory updates
|
||||
* Remove market offers
|
||||
* Memory and network optimizations
|
||||
* Python 3.6 supported
|
||||
|
||||
0.20.0 (2020-06-15)
|
||||
-------------------
|
||||
* Massive restructuring
|
||||
* Restricted IP check
|
||||
* Bomb deploy improvements
|
||||
* More verbose action logging
|
||||
* Division switching for maverick scripts
|
||||
* New medal endpoint is correctly parsed
|
||||
* WAM/Employ modularized
|
||||
|
||||
|
||||
0.19.0 (2020-01-13)
|
||||
-------------------
|
||||
* Created method for current products on sale.
|
||||
* Updated inventory to also include products on sale
|
||||
* set_default_weapon() - eRepublik should return list with all available weapon qualities, but when a battle is just launched, they return only dict with barehands
|
||||
* fight() - no longer calls self.set_default_weapon()
|
||||
* find_battle_and_fight() - now calls self.set_default_weapon() just before fighting
|
||||
* update_war_info() - returns previous battle list if responses 'last_updated' isn't more than 30s old
|
||||
* get_battle_for_war(war_id) - returns Battle instance for specific war, if battle is active for given war
|
||||
* Citizen.get_raw_surplus() fixed and moved to Citizen.my_companies.get_wam_raw_usage()
|
||||
* Implemented division switching
|
||||
* improved multi bomb deploy with auto traveling,
|
||||
* Citizen.fight() simplified battle data gathering logic -> Citizen.shoot logic improved
|
||||
|
||||
|
||||
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)
|
||||
------------------
|
||||
|
||||
|
1
LICENSE
1
LICENSE
@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
14
Makefile
14
Makefile
@ -43,32 +43,30 @@ clean-pyc: ## remove Python file artifacts
|
||||
find . -name '*.pyo' -exec rm -f {} +
|
||||
find . -name '*~' -exec rm -f {} +
|
||||
find . -name '__pycache__' -exec rm -fr {} +
|
||||
rm -rf debug/
|
||||
rm -rf log/
|
||||
|
||||
clean-test: ## remove test and coverage artifacts
|
||||
rm -fr .tox/
|
||||
rm -f .coverage
|
||||
rm -fr htmlcov/
|
||||
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
|
||||
|
||||
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
|
||||
|
23
README.rst
23
README.rst
@ -3,28 +3,23 @@ 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
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/eaa7ae43d23f4c0abab65c3bde89475a
|
||||
:target: https://app.codacy.com/manual/eeriks/erepublik?utm_source=github.com&utm_medium=referral&utm_content=eeriks/erepublik&utm_campaign=Badge_Grade_Dashboard
|
||||
:alt: Codacy Badge
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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
@ -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
@ -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
|
||||
|
||||
|
27
docs/conf.py
27
docs/conf.py
@ -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'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
61
docs/erepublik.rst
Normal file
61
docs/erepublik.rst
Normal file
@ -0,0 +1,61 @@
|
||||
erepublik package
|
||||
=================
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
erepublik.access\_points module
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: erepublik.access_points
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
erepublik.citizen module
|
||||
------------------------
|
||||
|
||||
.. automodule:: erepublik.citizen
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
erepublik.classes module
|
||||
------------------------
|
||||
|
||||
.. automodule:: erepublik.classes
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
erepublik.constants module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: erepublik.constants
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
erepublik.utils module
|
||||
----------------------
|
||||
|
||||
.. automodule:: erepublik.utils
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
erepublik.ws module
|
||||
-------------------
|
||||
|
||||
.. automodule:: erepublik.ws
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: erepublik
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
508
docs/index.html
Normal file
508
docs/index.html
Normal file
@ -0,0 +1,508 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Eriks Karls">
|
||||
<meta name="generator" content="Jekyll v4.0.1">
|
||||
<title>eBot configuration</title>
|
||||
<!-- CSS only -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
|
||||
|
||||
<!-- JS, Popper.js, and jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="p-3 text-center">
|
||||
<h1>eBot configuration file generator</h1>
|
||||
<!-- <h2>-->
|
||||
<!-- <span class="d-inline d-sm-none d-md-none d-lg-none d-xl-none">XS</span>-->
|
||||
<!-- <span class="d-none d-sm-inline d-md-none d-lg-none d-xl-none">SM</span>-->
|
||||
<!-- <span class="d-none d-sm-none d-md-inline d-lg-none d-xl-none">MD</span>-->
|
||||
<!-- <span class="d-none d-sm-none d-md-none d-lg-inline d-xl-none">LG</span>-->
|
||||
<!-- <span class="d-none d-sm-none d-md-none d-lg-none d-xl-inline">XL</span>-->
|
||||
<!-- </h2>-->
|
||||
</div>
|
||||
<div class="row pt-4">
|
||||
<div class="col-12">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-8 col-md-6">
|
||||
<h3>Login data</h3>
|
||||
<div class="form-group">
|
||||
<label for="email" class="hidden"></label><input type="email" class="form-control" onchange="updateJson()" id="email" placeholder="E-mail...">
|
||||
<label for="password" class="hidden"></label><input type="password" class="form-control" onchange="updateJson()" id="password" disabled placeholder="Password..."
|
||||
aria-describedby="passwordHelpBlock">
|
||||
<small id="passwordHelpBlock" class="form-text text-muted"><strong>NEVER</strong> enter Your passwords on 3rd party sites and <strong class="text-upper">DO NOT</strong> reuse Your
|
||||
password!</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-sm-4 col-md-3">
|
||||
<h3>Basic tasks</h3>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="work" checked>
|
||||
<label class="custom-control-label" for="work">Work</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="train" checked>
|
||||
<label class="custom-control-label" for="train">Train</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="ot" checked>
|
||||
<label class="custom-control-label" for="ot">Work overtime</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6">
|
||||
<h3>Production</h3>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="wam" checked>
|
||||
<label class="custom-control-label" for="wam">Work as manager</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="employees">
|
||||
<label class="custom-control-label" for="employees">Employ employees</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_buy_raw" checked>
|
||||
<label class="custom-control-label" for="auto_buy_raw">Auto buy missing RAW</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<h6 class="">Auto sell produced products</h6>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_frm">
|
||||
<label class="custom-control-label" for="auto_sell_frm">Food Raw</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_food">
|
||||
<label class="custom-control-label" for="auto_sell_food">Food</label>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_wrm">
|
||||
<label class="custom-control-label" for="auto_sell_wrm">Weapon Raw</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_weapons">
|
||||
<label class="custom-control-label" for="auto_sell_weapons">Weapon</label>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_hrm">
|
||||
<label class="custom-control-label" for="auto_sell_hrm">House Raw</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_house">
|
||||
<label class="custom-control-label" for="auto_sell_house">House</label>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_arm">
|
||||
<label class="custom-control-label" for="auto_sell_arm">Aircraft Raw</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_air">
|
||||
<label class="custom-control-label" for="auto_sell_air">Aircraft Weapon</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="auto_sell_all">
|
||||
<label class="custom-control-label" for="auto_sell_all">Auto sell all (also from inventory)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6">
|
||||
<h3>Fighting</h3>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="fight" checked>
|
||||
<label class="custom-control-label" for="fight">Fight</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="air" checked>
|
||||
<label class="custom-control-label" for="air">Air</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch custom-control-inline">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="ground">
|
||||
<label class="custom-control-label" for="ground">Ground</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="boosters">
|
||||
<label class="custom-control-label" for="boosters">Use ground boosters</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="continuous_fighting">
|
||||
<label class="custom-control-label" for="continuous_fighting">Continue fighting all FF in round</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="next_energy" checked>
|
||||
<label class="custom-control-label" for="next_energy">Fight for next WC +1hp/6min if reachable by FF</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check form-check-inline">
|
||||
<input type="radio" class="form-check-input" onchange="updateJson()" id="all_in" name="fight_amount" value="all_in">
|
||||
<label class="form-check-label" for="all_in">All energy</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input type="radio" class="form-check-input" onchange="updateJson()" id="h_energy" name="fight_amount" value="h_energy" checked>
|
||||
<label class="form-check-label" for="h_energy">1h energy</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="rw_def_side" checked>
|
||||
<label class="custom-control-label" for="rw_def_side">In RWs fight on right side (occupier/defender)</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="travel_to_fight" checked>
|
||||
<label class="custom-control-label" for="travel_to_fight">Travel to fight</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="epic_hunt">
|
||||
<label class="custom-control-label" for="epic_hunt">Hunt epics</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="epic_hunt_ebs">
|
||||
<label class="custom-control-label" for="epic_hunt_ebs">Spend <small>[all]</small> EBs in epics</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6">
|
||||
<h3 class="mt-4">Misc</h3>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="renew_houses" checked>
|
||||
<label class="custom-control-label" for="renew_houses">Auto renew houses</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="random_sleep" checked>
|
||||
<label class="custom-control-label" for="random_sleep">Random sleep</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="buy_gold">
|
||||
<label class="custom-control-label" for="buy_gold">Auto buy 10g</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="interactive" checked>
|
||||
<label class="custom-control-label" for="interactive">Interactive</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="debug">
|
||||
<label class="custom-control-label" for="debug">Debug</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="spin_wheel_of_fortune">
|
||||
<label class="custom-control-label" for="spin_wheel_of_fortune">Auto spin 10% of cc in WheelOfFortune</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="congress">
|
||||
<label class="custom-control-label" for="congress">Auto candidate for congress</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="party_president">
|
||||
<label class="custom-control-label" for="party_president">Auto candidate for party presidency</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="contribute_cc">
|
||||
<label class="custom-control-label" for="contribute_cc">Contribute cc to country's account (weekly)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6">
|
||||
<h3 class="mt-4">Advanced</h3>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="telegram">
|
||||
<label class="custom-control-label" for="telegram">Notify trough Telegram</label>
|
||||
</div>
|
||||
|
||||
<label for="telegram_chat_id">Telegram's chat ID</label>
|
||||
<input type="text" class="form-control" onchange="updateJson()" id="telegram_chat_id" placeholder="Chat ID">
|
||||
<label for="telegram_token">Telegram Bot token</label>
|
||||
<input type="text" class="form-control" onchange="updateJson()" id="telegram_token" placeholder="864251270:AAFzZZdjspI-kIgJVk4gF3TViGFoHnf8H4o">
|
||||
<small id="telegramTokenHelp" class="form-text text-muted">Only enter token if You want to use your own Telegram bot for notification sending</small>
|
||||
</div>
|
||||
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" onchange="updateJson()" id="proxy">
|
||||
<label class="custom-control-label" for="proxy">Use proxy</label>
|
||||
</div>
|
||||
<label for="proxy_kind">Proxy kind</label>
|
||||
<div class="form-group">
|
||||
<select class="form-control custom-select" id="proxy_kind">
|
||||
<option value="socks" selected>SOCKS5</option>
|
||||
<option value="http">HTTP</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="proxy_host">Proxy hostname or IP address</label>
|
||||
<input type="text" class="form-control" onchange="updateJson()" id="proxy_host" placeholder="localhost">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="proxy_port">Proxy port</label>
|
||||
<input type="text" class="form-control" onchange="updateJson()" id="proxy_port" placeholder="8080">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="proxy_user">Proxy username (optional)</label>
|
||||
<input type="text" class="form-control" onchange="updateJson()" id="proxy_user" placeholder="user">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="proxy_password">Proxy password (optional)</label>
|
||||
<input type="password" class="form-control" onchange="updateJson()" id="proxy_password" placeholder="password" disabled>
|
||||
<small id="proxyHelpBlock" class="form-text text-muted"><strong>NEVER</strong> enter Your passwords on 3rd party sites and <strong class="text-upper">DO NOT</strong> reuse Your password!</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<pre id="json-output"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function disable(element){
|
||||
element.checked = false;
|
||||
element.disabled = true;
|
||||
element.value = null;
|
||||
}
|
||||
|
||||
function updateJson() {
|
||||
let config = {};
|
||||
let email = document.getElementById('email'); // Generated
|
||||
config.email = email.value;
|
||||
config.password = "";
|
||||
let work = document.getElementById('work'); // Generated
|
||||
config.work = work.checked;
|
||||
let train = document.getElementById('train'); // Generated
|
||||
config.train = train.checked;
|
||||
let ot = document.getElementById('ot'); // Generated
|
||||
config.ot = ot.checked;
|
||||
let renew_houses = document.getElementById('renew_houses'); // Generated
|
||||
config.renew_houses = renew_houses.checked;
|
||||
let random_sleep = document.getElementById('random_sleep'); // Generated
|
||||
config.random_sleep = random_sleep.checked;
|
||||
let spin_wheel_of_fortune = document.getElementById('spin_wheel_of_fortune'); // Generated
|
||||
config.spin_wheel_of_fortune = spin_wheel_of_fortune.checked;
|
||||
let congress = document.getElementById('congress'); // Generated
|
||||
config.congress = congress.checked;
|
||||
let party_president = document.getElementById('party_president'); // Generated
|
||||
config.party_president = party_president.checked;
|
||||
let contribute_cc = document.getElementById('contribute_cc'); // Generated
|
||||
config.contribute_cc = contribute_cc.checked ? 10000 : false;
|
||||
let buy_gold = document.getElementById('buy_gold'); // Generated
|
||||
config.buy_gold = buy_gold.checked;
|
||||
let interactive = document.getElementById('interactive'); // Generated
|
||||
config.interactive = interactive.checked;
|
||||
let debug = document.getElementById('debug'); // Generated
|
||||
config.debug = debug.checked;
|
||||
let wam = document.getElementById('wam'); // Generated
|
||||
config.wam = wam.checked;
|
||||
let employees = document.getElementById('employees'); // Generated
|
||||
config.employees = employees.checked;
|
||||
|
||||
let auto_buy_raw = document.getElementById('auto_buy_raw'); // Generated
|
||||
let auto_sell_all = document.getElementById('auto_sell_all'); // Generated
|
||||
let auto_sell_frm = document.getElementById('auto_sell_frm'); // Generated
|
||||
let auto_sell_food = document.getElementById('auto_sell_food'); // Generated
|
||||
let auto_sell_wrm = document.getElementById('auto_sell_wrm'); // Generated
|
||||
let auto_sell_weapons = document.getElementById('auto_sell_weapons'); // Generated
|
||||
let auto_sell_hrm = document.getElementById('auto_sell_hrm'); // Generated
|
||||
let auto_sell_house = document.getElementById('auto_sell_house'); // Generated
|
||||
let auto_sell_arm = document.getElementById('auto_sell_arm'); // Generated
|
||||
let auto_sell_air = document.getElementById('auto_sell_air'); // Generated
|
||||
if (config.wam || config.employees) {
|
||||
auto_buy_raw.disabled = false;
|
||||
auto_sell_all.disabled = false;
|
||||
auto_sell_frm.disabled = false;
|
||||
auto_sell_food.disabled = false;
|
||||
auto_sell_wrm.disabled = false;
|
||||
auto_sell_weapons.disabled = false;
|
||||
auto_sell_hrm.disabled = false;
|
||||
auto_sell_house.disabled = false;
|
||||
auto_sell_arm.disabled = false;
|
||||
auto_sell_air.disabled = false;
|
||||
} else {
|
||||
disable(auto_buy_raw);
|
||||
disable(auto_sell_all);
|
||||
disable(auto_sell_food);
|
||||
disable(auto_sell_weapons);
|
||||
disable(auto_sell_house);
|
||||
disable(auto_sell_air);
|
||||
disable(auto_sell_frm);
|
||||
disable(auto_sell_wrm);
|
||||
disable(auto_sell_hrm);
|
||||
disable(auto_sell_arm);
|
||||
}
|
||||
config.auto_buy_raw = auto_buy_raw.checked;
|
||||
config.auto_sell_all = auto_sell_all.checked;
|
||||
config.auto_sell = [];
|
||||
if (auto_sell_food.checked) config.auto_sell.push("food");
|
||||
if (auto_sell_weapons.checked) config.auto_sell.push("weapon");
|
||||
if (auto_sell_house.checked) config.auto_sell.push("house");
|
||||
if (auto_sell_air.checked) config.auto_sell.push("airplane");
|
||||
if (auto_sell_frm.checked) config.auto_sell.push("foodRaw");
|
||||
if (auto_sell_wrm.checked) config.auto_sell.push("weaponRaw");
|
||||
if (auto_sell_hrm.checked) config.auto_sell.push("houseRaw");
|
||||
if (auto_sell_arm.checked) config.auto_sell.push("airplaneRaw");
|
||||
|
||||
let fight = document.getElementById('fight'); // Generated
|
||||
config.fight = fight.checked;
|
||||
let air = document.getElementById('air'); // Generated
|
||||
let ground = document.getElementById('ground'); // Generated
|
||||
let boosters = document.getElementById('boosters'); // Generated
|
||||
let continuous_fighting = document.getElementById('continuous_fighting'); // Generated
|
||||
let next_energy = document.getElementById('next_energy'); // Generated
|
||||
let all_in = document.getElementById('all_in'); // Generated
|
||||
let h_energy = document.getElementById('h_energy'); // Generated
|
||||
let rw_def_side = document.getElementById('rw_def_side'); // Generated
|
||||
let travel_to_fight = document.getElementById('travel_to_fight'); // Generated
|
||||
let epic_hunt = document.getElementById('epic_hunt'); // Generated
|
||||
let epic_hunt_ebs = document.getElementById('epic_hunt_ebs'); // Generated
|
||||
if (config.fight) {
|
||||
air.disabled = false;
|
||||
ground.disabled = false;
|
||||
boosters.disabled = false;
|
||||
continuous_fighting.disabled = false;
|
||||
next_energy.disabled = false;
|
||||
all_in.disabled = false;
|
||||
h_energy.disabled = false;
|
||||
rw_def_side.disabled = false;
|
||||
travel_to_fight.disabled = false;
|
||||
epic_hunt.disabled = false;
|
||||
epic_hunt_ebs.disabled = false;
|
||||
if (!epic_hunt.checked) disable(epic_hunt_ebs);
|
||||
} else {
|
||||
disable(air);
|
||||
disable(ground);
|
||||
disable(boosters);
|
||||
disable(continuous_fighting);
|
||||
disable(next_energy);
|
||||
disable(all_in);
|
||||
disable(h_energy);
|
||||
disable(rw_def_side);
|
||||
disable(travel_to_fight);
|
||||
disable(epic_hunt);
|
||||
disable(epic_hunt_ebs);
|
||||
}
|
||||
|
||||
config.air = air.checked;
|
||||
config.ground = ground.checked;
|
||||
config.boosters = boosters.checked;
|
||||
config.continuous_fighting = continuous_fighting.checked;
|
||||
config.next_energy = next_energy.checked;
|
||||
config.all_in = all_in.checked;
|
||||
config.rw_def_side = rw_def_side.checked;
|
||||
config.travel_to_fight = travel_to_fight.checked;
|
||||
config.epic_hunt = epic_hunt.checked;
|
||||
config.epic_hunt_ebs = config.epic_hunt ? epic_hunt_ebs.checked : config.epic_hunt;
|
||||
config.maverick = false;
|
||||
|
||||
// Advanced
|
||||
let telegram = document.getElementById('telegram'); // Generated
|
||||
config.telegram = telegram.checked;
|
||||
let telegram_chat_id = document.getElementById('telegram_chat_id'); // Generated
|
||||
let telegram_token = document.getElementById('telegram_token'); // Generated
|
||||
if (config.telegram) {
|
||||
telegram_chat_id.disabled = false;
|
||||
telegram_token.disabled = false;
|
||||
} else {
|
||||
disable(telegram_chat_id);
|
||||
disable(telegram_token);
|
||||
}
|
||||
config.telegram_chat_id = telegram_chat_id.value;
|
||||
config.telegram_token = telegram_token.value;
|
||||
|
||||
let _proxy = {};
|
||||
let proxy = document.getElementById('proxy'); // Generated
|
||||
let proxy_kind = document.getElementById('proxy_kind'); // Generated
|
||||
let proxy_host = document.getElementById('proxy_host'); // Generated
|
||||
let proxy_port = document.getElementById('proxy_port'); // Generated
|
||||
let proxy_user = document.getElementById('proxy_user'); // Generated
|
||||
|
||||
if (proxy.checked) {
|
||||
proxy_kind.disabled = false;
|
||||
proxy_host.disabled = false;
|
||||
proxy_port.disabled = false;
|
||||
proxy_user.disabled = false;
|
||||
} else {
|
||||
disable(proxy_kind);
|
||||
disable(proxy_host);
|
||||
disable(proxy_port);
|
||||
disable(proxy_user);
|
||||
}
|
||||
_proxy.kind = proxy_kind.value;
|
||||
_proxy.host = proxy_host.value;
|
||||
_proxy.port = proxy_port.value;
|
||||
_proxy.username = proxy_user.value;
|
||||
_proxy.password = ""
|
||||
if (proxy.checked) {
|
||||
delete config._proxy;
|
||||
config.proxy = _proxy;
|
||||
} else {
|
||||
delete config.proxy;
|
||||
config._proxy = {
|
||||
'kind': 'socks or http',
|
||||
'host': 'localhost',
|
||||
'port': 8080,
|
||||
'username': 'optional',
|
||||
'password': 'optional'
|
||||
}
|
||||
}
|
||||
let pre = document.getElementById('json-output');
|
||||
pre.textContent = JSON.stringify(config, null, 2);
|
||||
}
|
||||
updateJson();
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
{
|
||||
"email": "",
|
||||
"password": "",
|
||||
"work": true,
|
||||
"train": true,
|
||||
"ot": true,
|
||||
"renew_houses": true,
|
||||
"random_sleep": true,
|
||||
"buy_gold": true,
|
||||
"interactive": true,
|
||||
"debug": true,
|
||||
"wam": true,
|
||||
"employees": true,
|
||||
"auto_buy_raw": true,
|
||||
"auto_sell_all": true,
|
||||
"auto_sell": [
|
||||
"food",
|
||||
"weapon",
|
||||
"house",
|
||||
"airplane",
|
||||
"foodRaw",
|
||||
"weaponRaw",
|
||||
"houseRaw",
|
||||
"airplaneRaw"
|
||||
],
|
||||
"fight": true,
|
||||
"air": true,
|
||||
"boosters": true,
|
||||
"continuous_fighting": true,
|
||||
"next_energy": true,
|
||||
"all_in": false,
|
||||
"rw_def_side": true,
|
||||
"travel_to_fight": true,
|
||||
"epic_hunt": true,
|
||||
"epic_hunt_ebs": true
|
||||
}
|
||||
|
||||
-->
|
@ -1,5 +1,5 @@
|
||||
Welcome to eRepublik script's documentation!
|
||||
======================================
|
||||
============================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
@ -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
|
||||
|
@ -9,7 +9,7 @@ if "%SPHINXBUILD%" == "" (
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set SPHINXPROJ=erepublik_script
|
||||
set SPHINXPROJ=erepublik
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
|
7
docs/modules.rst
Normal file
7
docs/modules.rst
Normal file
@ -0,0 +1,7 @@
|
||||
erepublik
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
erepublik
|
@ -4,4 +4,6 @@ Usage
|
||||
|
||||
To use eRepublik script in a project::
|
||||
|
||||
import erepublik_script
|
||||
from erepublik import Citizen
|
||||
player = Citizen('email@domain.com', 'password')
|
||||
player.update_all()
|
||||
|
11
erepublik/__init__.py
Normal file
11
erepublik/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Top-level package for eRepublik script."""
|
||||
|
||||
__author__ = """Eriks Karls"""
|
||||
__email__ = 'eriks@72.lv'
|
||||
__version__ = '0.24.2.4'
|
||||
|
||||
from erepublik.citizen import Citizen
|
||||
|
||||
__all__ = ['Citizen', '__version__']
|
201
erepublik/_logging.py
Normal file
201
erepublik/_logging.py
Normal file
@ -0,0 +1,201 @@
|
||||
import base64
|
||||
import datetime
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import weakref
|
||||
from logging import LogRecord, handlers
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Union
|
||||
|
||||
import requests
|
||||
|
||||
from erepublik.classes import Reporter
|
||||
from erepublik.constants import erep_tz
|
||||
from erepublik.utils import json, json_dumps, json_loads, slugify
|
||||
|
||||
|
||||
class ErepublikFileHandler(handlers.TimedRotatingFileHandler):
|
||||
_file_path: Path
|
||||
|
||||
def __init__(self, filename: str = 'log/erepublik.log', *args, **kwargs):
|
||||
log_path = Path(filename)
|
||||
self._file_path = log_path
|
||||
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
at_time = erep_tz.localize(datetime.datetime.now()).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
kwargs.update(atTime=at_time)
|
||||
super().__init__(filename, when='d', *args, **kwargs)
|
||||
|
||||
def doRollover(self) -> None:
|
||||
self._file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
super().doRollover()
|
||||
|
||||
def emit(self, record: LogRecord) -> None:
|
||||
self._file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
super().emit(record)
|
||||
|
||||
|
||||
class ErepublikLogConsoleHandler(logging.StreamHandler):
|
||||
def __init__(self, *_):
|
||||
super().__init__(sys.stdout)
|
||||
|
||||
|
||||
class ErepublikFormatter(logging.Formatter):
|
||||
"""override logging.Formatter to use an aware datetime object"""
|
||||
|
||||
dbg_fmt = "[%(asctime)s] DEBUG: %(module)s: %(lineno)d: %(msg)s"
|
||||
info_fmt = "[%(asctime)s] %(msg)s"
|
||||
default_fmt = "[%(asctime)s] %(levelname)s: %(msg)s"
|
||||
|
||||
def converter(self, timestamp: Union[int, float]) -> datetime.datetime:
|
||||
return datetime.datetime.fromtimestamp(timestamp).astimezone(erep_tz)
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
"""
|
||||
Format the specified record as text.
|
||||
|
||||
The record's attribute dictionary is used as the operand to a
|
||||
string formatting operation which yields the returned string.
|
||||
Before formatting the dictionary, a couple of preparatory steps
|
||||
are carried out. The message attribute of the record is computed
|
||||
using LogRecord.getMessage(). If the formatting string uses the
|
||||
time (as determined by a call to usesTime(), formatTime() is
|
||||
called to format the event time. If there is exception information,
|
||||
it is formatted using formatException() and appended to the message.
|
||||
"""
|
||||
if record.levelno == logging.DEBUG:
|
||||
self._fmt = self.dbg_fmt
|
||||
elif record.levelno == logging.INFO:
|
||||
self._fmt = self.info_fmt
|
||||
else:
|
||||
self._fmt = self.default_fmt
|
||||
self._style = logging.PercentStyle(self._fmt)
|
||||
|
||||
record.message = record.getMessage()
|
||||
if self.usesTime():
|
||||
record.asctime = self.formatTime(record, self.datefmt)
|
||||
s = self.formatMessage(record)
|
||||
if record.exc_info:
|
||||
# Cache the traceback text to avoid converting it multiple times
|
||||
# (it's constant anyway)
|
||||
if not record.exc_text:
|
||||
record.exc_text = self.formatException(record.exc_info)
|
||||
return s
|
||||
|
||||
def formatTime(self, record, datefmt=None):
|
||||
dt = self.converter(record.created)
|
||||
if datefmt:
|
||||
s = dt.strftime(datefmt)
|
||||
else:
|
||||
s = dt.strftime('%Y-%m-%d %H:%M:%S')
|
||||
return s
|
||||
|
||||
def usesTime(self):
|
||||
return self._style.usesTime()
|
||||
|
||||
|
||||
class ErepublikErrorHTTTPHandler(handlers.HTTPHandler):
|
||||
def __init__(self, reporter: Reporter):
|
||||
logging.Handler.__init__(self, level=logging.ERROR)
|
||||
self._reporter = weakref.ref(reporter)
|
||||
self.host = 'erep.lv'
|
||||
self.url = '/ebot/error/'
|
||||
self.method = 'POST'
|
||||
self.secure = True
|
||||
self.credentials = (str(reporter.citizen_id), reporter.key)
|
||||
self.context = None
|
||||
|
||||
@property
|
||||
def reporter(self):
|
||||
return self._reporter()
|
||||
|
||||
def _get_last_response(self) -> Dict[str, str]:
|
||||
response = self.reporter.citizen.r
|
||||
url = response.url
|
||||
last_index = url.index("?") if "?" in url else len(response.url)
|
||||
|
||||
name = slugify(response.url[len(self.reporter.citizen.url):last_index])
|
||||
html = response.text
|
||||
|
||||
try:
|
||||
json_loads(html)
|
||||
ext = 'json'
|
||||
except json.decoder.JSONDecodeError:
|
||||
ext = 'html'
|
||||
try:
|
||||
resp_time = datetime.datetime.strptime(
|
||||
response.headers.get('date'), '%a, %d %b %Y %H:%M:%S %Z'
|
||||
).replace(tzinfo=datetime.timezone.utc).astimezone(erep_tz).strftime('%F_%H-%M-%S')
|
||||
except:
|
||||
resp_time = slugify(response.headers.get('date'))
|
||||
return dict(name=f"{resp_time}_{name}.{ext}", content=html.encode('utf-8'),
|
||||
mimetype="application/json" if ext == 'json' else "text/html")
|
||||
|
||||
def _get_local_vars(self) -> str:
|
||||
trace = inspect.trace()
|
||||
local_vars = {}
|
||||
if trace:
|
||||
local_vars = trace[-1][0].f_locals
|
||||
if local_vars.get('__name__') == '__main__':
|
||||
local_vars.update(commit_id=local_vars.get('COMMIT_ID'), interactive=local_vars.get('INTERACTIVE'),
|
||||
version=local_vars.get('__version__'), config=local_vars.get('CONFIG'))
|
||||
else:
|
||||
stack = inspect.stack()
|
||||
report_error_caller_found = False
|
||||
for frame in stack:
|
||||
if report_error_caller_found:
|
||||
local_vars = frame.frame.f_locals
|
||||
break
|
||||
if 'report_error' in str(frame.frame):
|
||||
report_error_caller_found = True
|
||||
|
||||
if 'state_thread' in local_vars:
|
||||
local_vars.pop('state_thread', None)
|
||||
from erepublik import Citizen
|
||||
if isinstance(local_vars.get('self'), Citizen):
|
||||
local_vars['self'] = repr(local_vars['self'])
|
||||
if isinstance(local_vars.get('player'), Citizen):
|
||||
local_vars['player'] = repr(local_vars['player'])
|
||||
if isinstance(local_vars.get('citizen'), Citizen):
|
||||
local_vars['citizen'] = repr(local_vars['citizen'])
|
||||
return json_dumps(local_vars)
|
||||
|
||||
def _get_instance_json(self) -> str:
|
||||
if self.reporter:
|
||||
return self.reporter.citizen.to_json(False)
|
||||
return ""
|
||||
|
||||
def mapLogRecord(self, record: logging.LogRecord) -> Dict[str, Any]:
|
||||
data = super().mapLogRecord(record)
|
||||
|
||||
# Log last response
|
||||
resp = self._get_last_response()
|
||||
files = [('file', (resp.get('name'), resp.get('content'), resp.get('mimetype'))), ]
|
||||
|
||||
files += list(('file', (f, open(f'log/{f}', 'rb'))) for f in os.listdir('log') if f.endswith('.log'))
|
||||
local_vars_json = self._get_local_vars()
|
||||
if local_vars_json:
|
||||
files.append(('file', ('local_vars.json', local_vars_json, "application/json")))
|
||||
instance_json = self._get_instance_json()
|
||||
if instance_json:
|
||||
files.append(('file', ('instance.json', instance_json, "application/json")))
|
||||
data.update(files=files)
|
||||
return data
|
||||
|
||||
def emit(self, record):
|
||||
"""
|
||||
Emit a record.
|
||||
|
||||
Send the record to the Web server as a percent-encoded dictionary
|
||||
"""
|
||||
try:
|
||||
proto = 'https' if self.secure else 'http'
|
||||
u, p = self.credentials
|
||||
s = 'Basic ' + base64.b64encode(f'{u}:{p}'.encode('utf-8')).strip().decode('ascii')
|
||||
headers = {'Authorization': s}
|
||||
data = self.mapLogRecord(record)
|
||||
files = data.pop('files') if 'files' in data else None
|
||||
requests.post(f"{proto}://{self.host}{self.url}", headers=headers, data=data, files=files)
|
||||
except Exception:
|
||||
self.handleError(record)
|
7
erepublik/_types.py
Normal file
7
erepublik/_types.py
Normal file
@ -0,0 +1,7 @@
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Union
|
||||
|
||||
InvFinalItem = Dict[str, Union[str, int, List[Dict[str, Union[int, datetime]]]]]
|
||||
InvBooster = Dict[str, Dict[int, Dict[int, InvFinalItem]]]
|
||||
InvFinal = Dict[str, Dict[int, InvFinalItem]]
|
||||
InvRaw = Dict[str, Dict[int, Dict[str, Union[str, int]]]]
|
758
erepublik/access_points.py
Normal file
758
erepublik/access_points.py
Normal file
@ -0,0 +1,758 @@
|
||||
import datetime
|
||||
import hashlib
|
||||
import random
|
||||
import time
|
||||
from typing import Any, Dict, List, Mapping, Union
|
||||
|
||||
from requests import Response, Session
|
||||
from requests_toolbelt.utils import dump
|
||||
|
||||
from erepublik import constants, utils
|
||||
|
||||
__all__ = ['SlowRequests', 'CitizenAPI']
|
||||
|
||||
|
||||
class SlowRequests(Session):
|
||||
last_time: datetime.datetime
|
||||
timeout: datetime.timedelta = datetime.timedelta(milliseconds=500)
|
||||
_uas: List[str] = [
|
||||
# Chrome
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36',
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
|
||||
|
||||
# FireFox
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0',
|
||||
]
|
||||
debug: bool = False
|
||||
|
||||
def __init__(self, proxies: Dict[str, str] = None, user_agent: str = None):
|
||||
super().__init__()
|
||||
if proxies:
|
||||
self.proxies = proxies
|
||||
if user_agent is None:
|
||||
user_agent = random.choice(self._uas)
|
||||
self.request_log_name = utils.get_file(utils.now().strftime("debug/requests_%Y-%m-%d.log"))
|
||||
self.last_time = utils.now()
|
||||
self.headers.update({'User-Agent': user_agent})
|
||||
self.hooks["response"] = [self._log_response]
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
return dict(last_time=self.last_time, timeout=self.timeout, cookies=self.cookies.get_dict(), debug=self.debug,
|
||||
user_agent=self.headers['User-Agent'], request_log_name=self.request_log_name, proxies=self.proxies)
|
||||
|
||||
def request(self, method, url, *args, **kwargs):
|
||||
self._slow_down_requests()
|
||||
self._log_request(url, method, **kwargs)
|
||||
resp = super().request(method, url, *args, **kwargs)
|
||||
# self._log_response(resp)
|
||||
return resp
|
||||
|
||||
def _slow_down_requests(self):
|
||||
ltt = utils.good_timedelta(self.last_time, self.timeout)
|
||||
if ltt > utils.now():
|
||||
seconds = (ltt - utils.now()).total_seconds()
|
||||
time.sleep(seconds if seconds > 0 else 0)
|
||||
self.last_time = utils.now()
|
||||
|
||||
def _log_request(self, url, method, data=None, json=None, params=None, **kwargs):
|
||||
if self.debug:
|
||||
args = {}
|
||||
kwargs.pop('allow_redirects', None)
|
||||
if kwargs:
|
||||
args.update({'kwargs': kwargs})
|
||||
|
||||
if data:
|
||||
args.update({'data': data})
|
||||
|
||||
if json:
|
||||
args.update({'json': json})
|
||||
|
||||
if params:
|
||||
args.update({'params': params})
|
||||
|
||||
body = f"[{utils.now().strftime('%F %T')}]\tURL: '{url}'\tMETHOD: {method}\tARGS: {args}\n"
|
||||
with open(self.request_log_name, 'ab') as file:
|
||||
file.write(body.encode("UTF-8"))
|
||||
|
||||
def _log_response(self, response: Response, *args, **kwargs):
|
||||
redirect = kwargs.get('redirect')
|
||||
url = response.request.url
|
||||
if self.debug:
|
||||
if response.history and not redirect:
|
||||
for hist_resp in response.history:
|
||||
self._log_request(hist_resp.request.url, 'REDIRECT')
|
||||
self._log_response(hist_resp, redirect=True)
|
||||
|
||||
fd_path = 'debug/requests'
|
||||
fd_time = self.last_time.strftime('%Y/%m/%d/%H-%M-%S')
|
||||
fd_name = utils.slugify(url[len(CitizenBaseAPI.url):])
|
||||
fd_extra = '_REDIRECT' if redirect else ""
|
||||
|
||||
try:
|
||||
utils.json.loads(response.text)
|
||||
fd_ext = 'json'
|
||||
except utils.json.JSONDecodeError:
|
||||
fd_ext = 'html'
|
||||
|
||||
filename = f'{fd_path}/{fd_time}_{fd_name}{fd_extra}.{fd_ext}'
|
||||
utils.write_file(filename, response.text)
|
||||
|
||||
if not redirect:
|
||||
data = dump.dump_all(response)
|
||||
utils.write_file(f'debug/dumps/{fd_time}_{fd_name}{fd_extra}.{fd_ext}.dump', data.decode('utf8'))
|
||||
|
||||
|
||||
class CitizenBaseAPI:
|
||||
url: str = "https://www.erepublik.com/en"
|
||||
_req: SlowRequests
|
||||
token: str
|
||||
|
||||
def __init__(self):
|
||||
""" Class for unifying eRepublik known endpoints and their required/optional parameters """
|
||||
self._req = SlowRequests()
|
||||
self.token = ""
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
return dict(url=self.url, request=self._req.as_dict, token=self.token)
|
||||
|
||||
def post(self, url: str, data=None, json=None, **kwargs) -> Response:
|
||||
return self._req.post(url, data, json, **kwargs)
|
||||
|
||||
def get(self, url: str, **kwargs) -> Response:
|
||||
return self._req.get(url, **kwargs)
|
||||
|
||||
def _get_main(self) -> Response:
|
||||
return self.get(self.url)
|
||||
|
||||
def set_socks_proxy(self, host: str, port: int, username: str = None, password: str = None):
|
||||
url = f'socks5://{username}:{password}@{host}:{port}' if username and password else f'socks5://{host}:{port}'
|
||||
self._req.proxies = dict(http=url, https=url)
|
||||
|
||||
def set_http_proxy(self, host: str, port: int, username: str = None, password: str = None):
|
||||
url = f'http://{username}:{password}@{host}:{port}' if username and password else f'socks5://{host}:{port}'
|
||||
self._req.proxies = dict(http=url)
|
||||
|
||||
def _get_main_session_captcha(self) -> Response:
|
||||
return self.get(f'{self.url}/main/sessionCaptcha')
|
||||
|
||||
def _get_main_session_unlock_popup(self) -> Response:
|
||||
return self.get(f'{self.url}/main/sessionUnlockPopup')
|
||||
|
||||
def _post_main_session_get_challenge(self, captcha_id: int, image_id: str = "") -> Response:
|
||||
env = dict(l=['tets', ], s=[], c=["l_chathwe", "l_chatroom"], m=0)
|
||||
data = dict(_token=self.token, captchaId=captcha_id, env=utils.b64json(env))
|
||||
if image_id:
|
||||
data.update(imageId=image_id, isRefresh=True)
|
||||
return self.post(f'{self.url}/main/sessionGetChallenge', data=data)
|
||||
|
||||
def _post_main_session_unlock(
|
||||
self, captcha_id: int, image_id: str, challenge_id: str, coords: List[Dict[str, int]], src: str
|
||||
) -> Response:
|
||||
c = [cookie.name for cookie in self._req.cookies if not cookie.has_nonstandard_attr('HttpOnly')]
|
||||
env = dict(l=['tets'], s=[], c=c, m=0)
|
||||
cookies = dict(sh=hashlib.sha256(','.join(env['l']+env['s']).encode('utf8')).hexdigest(),
|
||||
ch=hashlib.sha256(','.join(env['c']).encode('utf8')).hexdigest())
|
||||
cookie_kwargs = dict(expires=int(time.time())+120, path="/en/main/sessionUnlock", domain='.www.erepublik.com',
|
||||
secure=True, rest={'HttpOnly': True})
|
||||
self._req.cookies.set('sh', cookies['sh'], **cookie_kwargs)
|
||||
self._req.cookies.set('ch', cookies['ch'], **cookie_kwargs)
|
||||
b64_env = utils.b64json(env)
|
||||
data = dict(_token=self.token, captchaId=captcha_id, imageId=image_id, challengeId=challenge_id,
|
||||
clickMatrix=utils.json_dumps(coords).replace(' ', ''), isMobile=0, env=b64_env, src=src)
|
||||
return self.post(f'{self.url}/main/sessionUnlock', data=data, json=data,
|
||||
headers={'X-Requested-With': 'XMLHttpRequest', 'Referer': 'https://www.erepublik.com/en'})
|
||||
|
||||
|
||||
class ErepublikAnniversaryAPI(CitizenBaseAPI):
|
||||
def _post_main_collect_anniversary_reward(self) -> Response:
|
||||
return self.post(f"{self.url}/main/collect-anniversary-reward", data={'_token': self.token})
|
||||
|
||||
# 12th anniversary endpoints
|
||||
def _get_anniversary_quest_data(self) -> Response:
|
||||
return self.get(f"{self.url}/main/anniversaryQuestData")
|
||||
|
||||
def _post_map_rewards_unlock(self, node_id: int) -> Response:
|
||||
data = {'nodeId': node_id, '_token': self.token}
|
||||
return self.post(f"{self.url}/main/map-rewards-unlock", data=data)
|
||||
|
||||
def _post_map_rewards_speedup(self, node_id: int, currency_amount: int) -> Response:
|
||||
data = {'nodeId': node_id, '_token': self.token, 'currencyCost': currency_amount}
|
||||
return self.post(f"{self.url}/main/map-rewards-speedup", data=data)
|
||||
|
||||
def _post_map_rewards_claim(self, node_id: int, extra: bool = False) -> Response:
|
||||
data = {'nodeId': node_id, '_token': self.token}
|
||||
if extra:
|
||||
data['claimExtra'] = 1
|
||||
return self.post(f"{self.url}/main/map-rewards-claim", data=data)
|
||||
|
||||
def _post_main_wheel_of_fortune_spin(self, cost) -> Response:
|
||||
return self.post(f"{self.url}/main/wheeloffortune-spin", data={'_token': self.token, '_currentCost': cost})
|
||||
|
||||
def _post_main_wheel_of_fortune_build(self) -> Response:
|
||||
return self.post(f"{self.url}/main/wheeloffortune-build", data={'_token': self.token})
|
||||
|
||||
|
||||
class ErepublikArticleAPI(CitizenBaseAPI):
|
||||
def _get_main_article_json(self, article_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/articleJson/{article_id}")
|
||||
|
||||
def _get_main_delete_article(self, article_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/delete-article/{article_id}/1")
|
||||
|
||||
def _post_main_article_comments(self, article_id: int, page: int = 1) -> Response:
|
||||
data = dict(_token=self.token, articleId=article_id, page=page)
|
||||
if page:
|
||||
data.update({'page': page})
|
||||
return self.post(f"{self.url}/main/articleComments", data=data)
|
||||
|
||||
def _post_main_article_comments_create(self, message: str, article_id: int, parent: int = 0) -> Response:
|
||||
data = dict(_token=self.token, message=message, articleId=article_id)
|
||||
if parent:
|
||||
data.update({'parentId': parent})
|
||||
return self.post(f"{self.url}/main/articleComments/create", data=data)
|
||||
|
||||
def _post_main_donate_article(self, article_id: int, amount: int) -> Response:
|
||||
data = dict(_token=self.token, articleId=article_id, amount=amount)
|
||||
return self.post(f"{self.url}/main/donate-article", data=data)
|
||||
|
||||
def _post_main_write_article(self, title: str, content: str, country_id: int, kind_id: int) -> Response:
|
||||
data = dict(_token=self.token, article_name=title, article_body=content, article_location=country_id,
|
||||
article_category=kind_id)
|
||||
return self.post(f"{self.url}/main/write-article", data=data)
|
||||
|
||||
def _post_main_vote_article(self, article_id: int) -> Response:
|
||||
data = dict(_token=self.token, articleId=article_id)
|
||||
return self.post(f"{self.url}/main/vote-article", data=data)
|
||||
|
||||
|
||||
class ErepublikCompanyAPI(CitizenBaseAPI):
|
||||
def _post_economy_assign_to_holding(self, factory_id: int, holding_id: int) -> Response:
|
||||
data = dict(_token=self.token, factoryId=factory_id, action='assign', holdingCompanyId=holding_id)
|
||||
return self.post(f"{self.url}/economy/assign-to-holding", data=data)
|
||||
|
||||
def _post_economy_create_company(self, industry_id: int, building_type: int = 1) -> Response:
|
||||
data = {'_token': self.token, "company[industry_id]": industry_id, "company[building_type]": building_type}
|
||||
return self.post(f"{self.url}/economy/create-company", data=data,
|
||||
headers={'Referer': f"{self.url}/economy/create-company"})
|
||||
|
||||
def _get_economy_inventory_items(self) -> Response:
|
||||
return self.get(f"{self.url}/economy/inventory-items/")
|
||||
|
||||
def _get_economy_job_market_json(self, country_id: int) -> Response:
|
||||
return self.get(f"{self.url}/economy/job-market-json/{country_id}/1/desc")
|
||||
|
||||
def _get_economy_my_companies(self) -> Response:
|
||||
return self.get(f"{self.url}/economy/myCompanies")
|
||||
|
||||
def _post_economy_train(self, tg_ids: List[int]) -> Response:
|
||||
data: Dict[str, Union[int, str]] = {}
|
||||
for idx, tg_id in enumerate(tg_ids):
|
||||
data["grounds[%i][id]" % idx] = tg_id
|
||||
data["grounds[%i][train]" % idx] = 1
|
||||
if data:
|
||||
data['_token'] = self.token
|
||||
return self.post(f"{self.url}/economy/train", data=data)
|
||||
|
||||
def _post_economy_upgrade_company(self, factory: int, level: int, pin: str = None) -> Response:
|
||||
data = dict(_token=self.token, type='upgrade', companyId=factory, level=level, pin="" if pin is None else pin)
|
||||
return self.post(f"{self.url}/economy/upgrade-company", data=data)
|
||||
|
||||
def _post_economy_work(self, action_type: str, wam: List[int] = None, employ: Dict[int, int] = None) -> Response:
|
||||
data: Dict[str, Union[int, str]] = dict(action_type=action_type, _token=self.token)
|
||||
if action_type == 'production':
|
||||
if employ is None:
|
||||
employ = {}
|
||||
if wam is None:
|
||||
wam = []
|
||||
max_idx = 0
|
||||
for company_id in sorted(wam or []):
|
||||
data.update({
|
||||
f"companies[{max_idx}][id]": company_id,
|
||||
f"companies[{max_idx}][employee_works]": employ.pop(company_id, 0),
|
||||
f"companies[{max_idx}][own_work]": 1
|
||||
})
|
||||
max_idx += 1
|
||||
for company_id in sorted(employ or []):
|
||||
data.update({
|
||||
f"companies[{max_idx}][id]": company_id,
|
||||
f"companies[{max_idx}][employee_works]": employ.pop(company_id, 0),
|
||||
f"companies[{max_idx}][own_work]": 0
|
||||
})
|
||||
max_idx += 1
|
||||
return self.post(f"{self.url}/economy/work", data=data)
|
||||
|
||||
def _post_economy_work_overtime(self) -> Response:
|
||||
data = dict(action_type='workOvertime', _token=self.token)
|
||||
return self.post(f"{self.url}/economy/workOvertime", data=data)
|
||||
|
||||
def _post_economy_job_market_apply(self, citizen_id: int, salary: float) -> Response:
|
||||
data = dict(_token=self.token, citizenId=citizen_id, salary=salary)
|
||||
return self.post(f"{self.url}/economy/job-market-apply", data=data)
|
||||
|
||||
def _post_economy_resign(self) -> Response:
|
||||
return self.post(f"{self.url}/economy/resign",
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||
data={'_token': self.token, 'action_type': 'resign'})
|
||||
|
||||
def _post_economy_sell_company(self, factory_id: int, pin: int = None, sell: bool = True) -> Response:
|
||||
data = dict(_token=self.token, pin="" if pin is None else pin)
|
||||
if sell:
|
||||
data.update({'sell': 'sell'})
|
||||
else:
|
||||
data.update({'dissolve': factory_id})
|
||||
return self.post(f"{self.url}/economy/sell-company/{factory_id}",
|
||||
data=data, headers={'Referer': self.url})
|
||||
|
||||
|
||||
class ErepublikCountryAPI(CitizenBaseAPI):
|
||||
def _get_country_military(self, country_name: str) -> Response:
|
||||
return self.get(f"{self.url}/country/military/{country_name}")
|
||||
|
||||
def _post_main_country_donate(self, country_id: int, action: str, value: Union[int, float],
|
||||
quality: int = None) -> Response:
|
||||
data = dict(countryId=country_id, action=action, _token=self.token, value=value, quality=quality)
|
||||
return self.post(f"{self.url}/main/country-donate", data=data,
|
||||
headers={'Referer': f"{self.url}/country/economy/Latvia"})
|
||||
|
||||
|
||||
class ErepublikEconomyAPI(CitizenBaseAPI):
|
||||
def _get_economy_citizen_accounts(self, organisation_id: int) -> Response:
|
||||
return self.get(f"{self.url}/economy/citizen-accounts/{organisation_id}")
|
||||
|
||||
def _get_economy_my_market_offers(self) -> Response:
|
||||
return self.get(f"{self.url}/economy/myMarketOffers")
|
||||
|
||||
def _get_main_job_data(self) -> Response:
|
||||
return self.get(f"{self.url}/main/job-data")
|
||||
|
||||
def _post_main_buy_gold_items(self, currency: str, item: str, amount: int) -> Response:
|
||||
data = dict(itemId=item, currency=currency, amount=amount, _token=self.token)
|
||||
return self.post(f"{self.url}/main/buyGoldItems", data=data)
|
||||
|
||||
def _post_economy_activate_booster(self, quality: int, duration: int, kind: str) -> Response:
|
||||
data = dict(type=kind, quality=quality, duration=duration, fromInventory=True, _token=self.token)
|
||||
return self.post(f"{self.url}/economy/activateBooster", data=data)
|
||||
|
||||
def _post_economy_activate_house(self, quality: int) -> Response:
|
||||
data = dict(action='activate', quality=quality, type='house', _token=self.token)
|
||||
return self.post(f"{self.url}/economy/activateHouse", data=data)
|
||||
|
||||
def _post_economy_donate_items_action(self, citizen_id: int, amount: int, industry: int,
|
||||
quality: int) -> Response:
|
||||
data = dict(citizen_id=citizen_id, amount=amount, industry_id=industry, quality=quality, _token=self.token)
|
||||
return self.post(f"{self.url}/economy/donate-items-action", data=data,
|
||||
headers={'Referer': f"{self.url}/economy/donate-items/{citizen_id}"})
|
||||
|
||||
def _post_economy_donate_money_action(self, citizen_id: int, amount: float = 0.0,
|
||||
currency: int = 62) -> Response:
|
||||
data = dict(citizen_id=citizen_id, _token=self.token, currency_id=currency, amount=amount)
|
||||
return self.post(f"{self.url}/economy/donate-money-action", data=data,
|
||||
headers={'Referer': f"{self.url}/economy/donate-money/{citizen_id}"})
|
||||
|
||||
def _post_economy_exchange_purchase(self, amount: float, currency: int, offer: int) -> Response:
|
||||
data = dict(_token=self.token, amount=amount, currencyId=currency, offerId=offer)
|
||||
return self.post(f"{self.url}/economy/exchange/purchase/", data=data)
|
||||
|
||||
def _post_economy_exchange_retrieve(self, personal: bool, page: int, currency: int) -> Response:
|
||||
data = dict(_token=self.token, personalOffers=int(personal), page=page, currencyId=currency)
|
||||
return self.post(f"{self.url}/economy/exchange/retrieve/", data=data)
|
||||
|
||||
def _post_economy_game_tokens_market(self, action: str) -> Response:
|
||||
assert action in ['retrieve', ]
|
||||
data = dict(_token=self.token, action=action)
|
||||
return self.post(f"{self.url}/economy/gameTokensMarketAjax", data=data)
|
||||
|
||||
def _post_economy_marketplace(self, country: int, industry: int, quality: int,
|
||||
order_asc: bool = True) -> Response:
|
||||
data = dict(countryId=country, industryId=industry, quality=quality, ajaxMarket=1,
|
||||
orderBy='price_asc' if order_asc else 'price_desc', _token=self.token)
|
||||
return self.post(f"{self.url}/economy/marketplaceAjax", data=data)
|
||||
|
||||
def _post_economy_marketplace_actions(self, action: str, **kwargs) -> Response:
|
||||
if action == 'buy':
|
||||
data = dict(_token=self.token, offerId=kwargs['offer'], amount=kwargs['amount'],
|
||||
orderBy='price_asc', currentPage=1, buyAction=1)
|
||||
elif action == 'sell':
|
||||
data = dict(_token=self.token, countryId=kwargs['country_id'], price=kwargs['price'],
|
||||
industryId=kwargs['industry'], quality=kwargs['quality'], amount=kwargs['amount'],
|
||||
sellAction='postOffer')
|
||||
elif action == 'delete':
|
||||
data = dict(_token=self.token, offerId=kwargs['offer_id'], sellAction='deleteOffer')
|
||||
else:
|
||||
raise ValueError(f"Action '{action}' is not supported! Only 'buy/sell/delete' actions are available")
|
||||
return self.post(f"{self.url}/economy/marketplaceActions", data=data)
|
||||
|
||||
|
||||
class ErepublikLeaderBoardAPI(CitizenBaseAPI):
|
||||
def _get_main_leaderboards_damage_aircraft_rankings(self, country_id: int, weeks: int = 0,
|
||||
mu_id: int = 0) -> Response: # noqa
|
||||
return self.get(f"{self.url}/main/leaderboards-damage-aircraft-rankings/{country_id}/{weeks}/{mu_id}/0")
|
||||
|
||||
def _get_main_leaderboards_damage_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0,
|
||||
div: int = 0) -> Response: # noqa
|
||||
return self.get(f"{self.url}/main/leaderboards-damage-rankings/{country_id}/{weeks}/{mu_id}/{div}")
|
||||
|
||||
def _get_main_leaderboards_kills_aircraft_rankings(self, country_id: int, weeks: int = 0,
|
||||
mu_id: int = 0) -> Response: # noqa
|
||||
return self.get(f"{self.url}/main/leaderboards-kills-aircraft-rankings/{country_id}/{weeks}/{mu_id}/0")
|
||||
|
||||
def _get_main_leaderboards_kills_rankings(self, country_id: int, weeks: int = 0, mu_id: int = 0,
|
||||
div: int = 0) -> Response: # noqa
|
||||
return self.get(f"{self.url}/main/leaderboards-kills-rankings/{country_id}/{weeks}/{mu_id}/{div}")
|
||||
|
||||
|
||||
class ErepublikLocationAPI(CitizenBaseAPI):
|
||||
def _get_main_city_data_residents(self, city_id: int, page: int = 1, params: Mapping[str, Any] = None) -> Response:
|
||||
if params is None:
|
||||
params = {}
|
||||
return self.get(f"{self.url}/main/city-data/{city_id}/residents", params={'currentPage': page, **params})
|
||||
|
||||
|
||||
class ErepublikMilitaryAPI(CitizenBaseAPI):
|
||||
def _get_military_battle_stats(self, battle_id: int, division: int, division_id: int):
|
||||
return self.get(f"{self.url}/military/battle-stats/{battle_id}/{division}/{division_id}")
|
||||
|
||||
def _get_military_battlefield_choose_side(self, battle_id: int, side_id: int) -> Response:
|
||||
return self.get(f"{self.url}/military/battlefield-choose-side/{battle_id}/{side_id}")
|
||||
|
||||
def _get_military_show_weapons(self, battle_id: int) -> Response:
|
||||
return self.get(f"{self.url}/military/show-weapons", params={'_token': self.token, 'battleId': battle_id})
|
||||
|
||||
def _get_military_campaigns(self) -> Response:
|
||||
return self.get(f"{self.url}/military/campaigns-new/")
|
||||
|
||||
def _get_military_campaigns_json_list(self) -> Response:
|
||||
return self.get(f"{self.url}/military/campaignsJson/list")
|
||||
|
||||
def _get_military_campaigns_json_citizen(self) -> Response:
|
||||
return self.get(f"{self.url}/military/campaignsJson/citizen")
|
||||
|
||||
def _get_military_unit_data(self, unit_id: int, **kwargs) -> Response:
|
||||
params = {'groupId': unit_id, 'panel': 'members', **kwargs}
|
||||
return self.get(f"{self.url}/military/military-unit-data/", params=params)
|
||||
|
||||
def _post_main_activate_battle_effect(self, battle_id: int, kind: str, citizen_id: int) -> Response:
|
||||
data = dict(battleId=battle_id, citizenId=citizen_id, type=kind, _token=self.token)
|
||||
return self.post(f"{self.url}/main/fight-activateBattleEffect", data=data)
|
||||
|
||||
def _post_main_battlefield_travel(self, side_id: int, battle_id: int) -> Response:
|
||||
data = dict(_token=self.token, sideCountryId=side_id, battleId=battle_id)
|
||||
return self.post(f"{self.url}/main/battlefieldTravel", data=data)
|
||||
|
||||
def _post_main_battlefield_change_division(self, battle_id: int, division_id: int, side_id: int = None) -> Response:
|
||||
data = dict(_token=self.token, battleZoneId=division_id, battleId=battle_id)
|
||||
if side_id is not None:
|
||||
data.update(sideCountryId=side_id)
|
||||
return self.post(f"{self.url}/main/battlefieldTravel", data=data)
|
||||
|
||||
def _get_wars_show(self, war_id: int) -> Response:
|
||||
return self.get(f"{self.url}/wars/show/{war_id}")
|
||||
|
||||
def _post_military_fight_activate_booster(self, battle_id: int, quality: int, duration: int, kind: str) -> Response:
|
||||
data = dict(type=kind, quality=quality, duration=duration, battleId=battle_id, _token=self.token)
|
||||
return self.post(f"{self.url}/military/fight-activateBooster", data=data)
|
||||
|
||||
def _post_military_change_weapon(self, battle_id: int, battle_zone: int, weapon_level: int, ) -> Response:
|
||||
data = dict(battleId=battle_id, _token=self.token, battleZoneId=battle_zone, customizationLevel=weapon_level)
|
||||
return self.post(f"{self.url}/military/change-weapon", data=data)
|
||||
|
||||
def _post_military_battle_console(self, battle_id: int, action: str, page: int = 1, **kwargs) -> Response:
|
||||
data = dict(battleId=battle_id, action=action, _token=self.token)
|
||||
if action == 'battleStatistics':
|
||||
data.update(round=kwargs['round_id'], zoneId=kwargs['round_id'], leftPage=page, rightPage=page,
|
||||
division=kwargs['division'], type=kwargs.get('type', 'damage'), )
|
||||
elif action == 'warList':
|
||||
data.update(page=page)
|
||||
return self.post(f"{self.url}/military/battle-console", data=data)
|
||||
|
||||
def _post_military_deploy_bomb(self, battle_id: int, division_id: int, side_id: int, bomb_id: int) -> Response:
|
||||
data = dict(battleId=battle_id, battleZoneId=division_id, sideId=side_id, sideCountryId=side_id,
|
||||
bombId=bomb_id, _token=self.token)
|
||||
return self.post(f"{self.url}/military/deploy-bomb", data=data)
|
||||
|
||||
def _post_military_fight_air(self, battle_id: int, side_id: int, zone_id: int) -> Response:
|
||||
data = dict(sideId=side_id, battleId=battle_id, _token=self.token, battleZoneId=zone_id)
|
||||
return self.post(f"{self.url}/military/fight-shoooot/{battle_id}", data=data)
|
||||
|
||||
def _post_military_fight_ground(self, battle_id: int, side_id: int, zone_id: int) -> Response:
|
||||
data = dict(sideId=side_id, battleId=battle_id, _token=self.token, battleZoneId=zone_id)
|
||||
return self.post(f"{self.url}/military/fight-shooot/{battle_id}", data=data)
|
||||
|
||||
def _post_fight_deploy_deploy_report_data(self, deployment_id: int) -> Response:
|
||||
data = dict(_token=self.token, deploymentId=deployment_id)
|
||||
return self.post(f"{self.url}/military/fightDeploy-deployReportData", data=data)
|
||||
|
||||
def _post_fight_deploy_get_inventory(self, battle_id: int, side_id: int, battle_zone_id: int) -> Response:
|
||||
data = dict(_token=self.token, battleId=battle_id, sideCountryId=side_id, battleZoneId=battle_zone_id)
|
||||
return self.post(f"{self.url}/military/fightDeploy-getInventory", data=data)
|
||||
|
||||
def _post_fight_deploy_start_deploy(
|
||||
self, battle_id: int, side_id: int, battle_zone_id: int, energy: int, weapon: int, **kwargs
|
||||
) -> Response:
|
||||
data = dict(_token=self.token, battleId=battle_id, battleZoneId=battle_zone_id, sideCountryId=side_id,
|
||||
weaponQuality=weapon, totalEnergy=energy, **kwargs)
|
||||
return self.post(f"{self.url}/military/fightDeploy-startDeploy", data=data)
|
||||
|
||||
def _post_military_fight_deploy_deploy_report_data(self, deployment_id: int) -> Response:
|
||||
data = dict(_token=self.token, deploymentId=deployment_id)
|
||||
return self.post(f"{self.url}/military/fightDeploy-deployReportData", data=data)
|
||||
|
||||
|
||||
class ErepublikPoliticsAPI(CitizenBaseAPI):
|
||||
def _get_candidate_party(self, party_slug: str) -> Response:
|
||||
return self.get(f"{self.url}/candidate/{party_slug}")
|
||||
|
||||
def _get_main_party_members(self, party_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/party-members/{party_id}")
|
||||
|
||||
def _get_main_rankings_parties(self, country_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/rankings-parties/1/{country_id}")
|
||||
|
||||
def _post_candidate_for_congress(self, presentation: str = "") -> Response:
|
||||
data = dict(_token=self.token, presentation=presentation)
|
||||
return self.post(f"{self.url}/candidate-for-congress", data=data)
|
||||
|
||||
def _get_presidential_elections(self, country_id: int, timestamp: int) -> Response:
|
||||
return self.get(f"{self.url}/main/presidential-elections/{country_id}/{timestamp}")
|
||||
|
||||
def _post_propose_president_candidate(self, party_slug: str, citizen_id: int) -> Response:
|
||||
return self.post(f"{self.url}/propose-president-candidate/{party_slug}",
|
||||
data=dict(_token=self.token, citizen=citizen_id))
|
||||
|
||||
def _get_auto_propose_president_candidate(self, party_slug: str) -> Response:
|
||||
return self.get(f"{self.url}/auto-propose-president-candidate/{party_slug}")
|
||||
|
||||
|
||||
class ErepublikPresidentAPI(CitizenBaseAPI):
|
||||
def _post_wars_attack_region(self, war_id: int, region_id: int, region_name: str) -> Response:
|
||||
data = {'_token': self.token, 'warId': war_id, 'regionName': region_name, 'regionNameConfirm': region_name}
|
||||
return self.post(f'{self.url}/wars/attack-region/{war_id}/{region_id}', data=data)
|
||||
|
||||
def _post_new_war(self, self_country_id: int, attack_country_id: int, debate: str = "") -> Response:
|
||||
data = dict(requirments=1, _token=self.token, debate=debate,
|
||||
countryNameConfirm=constants.COUNTRIES[attack_country_id].link)
|
||||
return self.post(f"{self.url}/{constants.COUNTRIES[self_country_id].link}/new-war", data=data)
|
||||
|
||||
def _post_new_donation(self, country_id: int, amount: int, org_name: str, debate: str = "") -> Response:
|
||||
data = dict(requirments=1, _token=self.token, debate=debate, currency=1, value=amount, commit='Propose',
|
||||
type_name=org_name)
|
||||
return self.post(f"{self.url}/{constants.COUNTRIES[country_id].link}/new-donation", data=data)
|
||||
|
||||
|
||||
class ErepublikProfileAPI(CitizenBaseAPI):
|
||||
def _get_main_citizen_hovercard(self, citizen_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/citizen-hovercard/{citizen_id}")
|
||||
|
||||
def _get_main_citizen_profile_json(self, citizen_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/citizen-profile-json/{citizen_id}")
|
||||
|
||||
def _get_main_citizen_notifications(self) -> Response:
|
||||
return self.get(f"{self.url}/main/citizenDailyAssistant")
|
||||
|
||||
def _get_main_citizen_daily_assistant(self) -> Response:
|
||||
return self.get(f"{self.url}/main/citizenNotifications")
|
||||
|
||||
def _get_main_messages_paginated(self, page: int = 1) -> Response:
|
||||
return self.get(f"{self.url}/main/messages-paginated/{page}")
|
||||
|
||||
def _get_main_money_donation_accept(self, donation_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/money-donation/accept/{donation_id}", params={'_token': self.token})
|
||||
|
||||
def _get_main_money_donation_reject(self, donation_id: int) -> Response:
|
||||
return self.get(f"{self.url}/main/money-donation/reject/{donation_id}", params={'_token': self.token})
|
||||
|
||||
def _get_main_notifications_ajax_community(self, page: int = 1) -> Response:
|
||||
return self.get(f"{self.url}/main/notificationsAjax/community/{page}")
|
||||
|
||||
def _get_main_notifications_ajax_system(self, page: int = 1) -> Response:
|
||||
return self.get(f"{self.url}/main/notificationsAjax/system/{page}")
|
||||
|
||||
def _get_main_notifications_ajax_report(self, page: int = 1) -> Response:
|
||||
return self.get(f"{self.url}/main/notificationsAjax/report/{page}")
|
||||
|
||||
def _get_main_training_grounds_json(self) -> Response:
|
||||
return self.get(f"{self.url}/main/training-grounds-json")
|
||||
|
||||
def _get_main_weekly_challenge_data(self) -> Response:
|
||||
return self.get(f"{self.url}/main/weekly-challenge-data")
|
||||
|
||||
def _post_main_citizen_add_remove_friend(self, citizen: int, add: bool) -> Response:
|
||||
data = dict(_token=self.token, citizenId=citizen, url="//www.erepublik.com/en/main/citizen-addRemoveFriend")
|
||||
if add:
|
||||
data.update({'action': 'addFriend'})
|
||||
else:
|
||||
data.update({'action': 'removeFriend'})
|
||||
return self.post(f"{self.url}/main/citizen-addRemoveFriend", data=data)
|
||||
|
||||
def _post_main_daily_task_reward(self) -> Response:
|
||||
return self.post(f"{self.url}/main/daily-tasks-reward", data=dict(_token=self.token))
|
||||
|
||||
def _post_delete_message(self, msg_id: list) -> Response:
|
||||
data = {'_token': self.token, "delete_message[]": msg_id}
|
||||
return self.post(f"{self.url}/main/messages-delete", data)
|
||||
|
||||
def _post_eat(self, color: str) -> Response:
|
||||
data = dict(_token=self.token, buttonColor=color)
|
||||
return self.post(f"{self.url}/main/eat", params=data)
|
||||
|
||||
def _post_main_global_alerts_close(self, alert_id: int) -> Response:
|
||||
data = dict(_token=self.token, alert_id=alert_id)
|
||||
return self.post(f"{self.url}/main/global-alerts/close", data=data)
|
||||
|
||||
def _post_forgot_password(self, email: str) -> Response:
|
||||
data = dict(_token=self.token, email=email, commit='Reset password')
|
||||
return self.post(f"{self.url}/forgot-password", data=data)
|
||||
|
||||
def _post_login(self, email: str, password: str) -> Response:
|
||||
data = dict(csrf_token=self.token, citizen_email=email, citizen_password=password, remember='on')
|
||||
return self.post(f"{self.url}/login", data=data)
|
||||
|
||||
def _post_main_messages_alert(self, notification_ids: List[int]) -> Response:
|
||||
data = {'_token': self.token, "delete_alerts[]": notification_ids, 'deleteAllAlerts': '1', 'delete': 'Delete'}
|
||||
return self.post(f"{self.url}/main/messages-alerts/1", data=data)
|
||||
|
||||
def _post_main_notifications_ajax_community(self, notification_ids: List[int], page: int = 1) -> Response:
|
||||
data = {'_token': self.token, "delete_alerts[]": notification_ids}
|
||||
return self.post(f"{self.url}/main/notificationsAjax/community/{page}", data=data)
|
||||
|
||||
def _post_main_notifications_ajax_system(self, notification_ids: List[int], page: int = 1) -> Response:
|
||||
data = {'_token': self.token, "delete_alerts[]": notification_ids}
|
||||
return self.post(f"{self.url}/main/notificationsAjax/system/{page}", data=data)
|
||||
|
||||
def _post_main_notifications_ajax_report(self, notification_ids: List[int], page: int = 1) -> Response:
|
||||
data = {'_token': self.token, "delete_alerts[]": notification_ids}
|
||||
return self.post(f"{self.url}/main/notificationsAjax/report/{page}", data=data)
|
||||
|
||||
def _post_main_messages_compose(self, subject: str, body: str, citizens: List[int]) -> Response:
|
||||
url_pk = 0 if len(citizens) > 1 else str(citizens[0])
|
||||
data = dict(citizen_name=",".join([str(x) for x in citizens]),
|
||||
citizen_subject=subject, _token=self.token, citizen_message=body)
|
||||
return self.post(f"{self.url}/main/messages-compose/{url_pk}", data=data)
|
||||
|
||||
def _post_military_group_missions(self) -> Response:
|
||||
data = dict(action='check', _token=self.token)
|
||||
return self.post(f"{self.url}/military/group-missions", data=data)
|
||||
|
||||
def _post_main_weekly_challenge_reward(self, reward_id: int) -> Response:
|
||||
data = dict(_token=self.token, rewardId=reward_id)
|
||||
return self.post(f"{self.url}/main/weekly-challenge-collect-reward", data=data)
|
||||
|
||||
def _post_main_weekly_challenge_collect_all(self, max_reward_id: int) -> Response:
|
||||
data = dict(_token=self.token, maxRewardId=max_reward_id)
|
||||
return self.post(f"{self.url}/main/weekly-challenge-collect-all", data=data)
|
||||
|
||||
def _post_main_profile_update(self, action: str, params: str):
|
||||
data = {'action': action, 'params': params, '_token': self.token}
|
||||
return self.post(f"{self.url}/main/profile-update", data=data)
|
||||
|
||||
|
||||
class ErepublikTravelAPI(CitizenBaseAPI):
|
||||
def _post_main_travel(self, check: str, **kwargs) -> Response:
|
||||
data = dict(_token=self.token, check=check, **kwargs)
|
||||
return self.post(f"{self.url}/main/travel", data=data)
|
||||
|
||||
def _post_main_travel_data(self, **kwargs) -> Response:
|
||||
return self.post(f"{self.url}/main/travelData", data=dict(_token=self.token, **kwargs))
|
||||
|
||||
|
||||
class ErepublikWallPostAPI(CitizenBaseAPI):
|
||||
# ## Country
|
||||
|
||||
def _post_main_country_comment_retrieve(self, post_id: int) -> Response:
|
||||
data = {'_token': self.token, 'postId': post_id}
|
||||
return self.post(f"{self.url}/main/country-comment/retrieve/json", data=data)
|
||||
|
||||
def _post_main_country_comment_create(self, post_id: int, comment_message: str) -> Response:
|
||||
data = {'_token': self.token, 'postId': post_id, 'comment_message': comment_message}
|
||||
return self.post(f"{self.url}/main/country-comment/create/json", data=data)
|
||||
|
||||
def _post_main_country_post_create(self, body: str, post_as: int) -> Response:
|
||||
data = {'_token': self.token, 'post_message': body, 'post_as': post_as}
|
||||
return self.post(f"{self.url}/main/country-post/create/json", data=data)
|
||||
|
||||
def _post_main_country_post_retrieve(self) -> Response:
|
||||
data = {'_token': self.token, 'page': 1, 'switchedFrom': False}
|
||||
return self.post(f"{self.url}/main/country-post/retrieve/json", data=data)
|
||||
|
||||
# ## Military Unit
|
||||
|
||||
def _post_main_military_unit_comment_retrieve(self, post_id: int) -> Response:
|
||||
data = {'_token': self.token, 'postId': post_id}
|
||||
return self.post(f"{self.url}/main/military-unit-comment/retrieve/json", data=data)
|
||||
|
||||
def _post_main_military_unit_comment_create(self, post_id: int, comment_message: str) -> Response:
|
||||
data = {'_token': self.token, 'postId': post_id, 'comment_message': comment_message}
|
||||
return self.post(f"{self.url}/main/military-unit-comment/create/json", data=data)
|
||||
|
||||
def _post_main_military_unit_post_create(self, body: str, post_as: int) -> Response:
|
||||
data = {'_token': self.token, 'post_message': body, 'post_as': post_as}
|
||||
return self.post(f"{self.url}/main/military-unit-post/create/json", data=data)
|
||||
|
||||
def _post_main_military_unit_post_retrieve(self) -> Response:
|
||||
data = {'_token': self.token, 'page': 1, 'switchedFrom': False}
|
||||
return self.post(f"{self.url}/main/military-unit-post/retrieve/json", data=data)
|
||||
|
||||
# ## Party
|
||||
|
||||
def _post_main_party_comment_retrieve(self, post_id: int) -> Response:
|
||||
data = {'_token': self.token, 'postId': post_id}
|
||||
return self.post(f"{self.url}/main/party-comment/retrieve/json", data=data)
|
||||
|
||||
def _post_main_party_comment_create(self, post_id: int, comment_message: str) -> Response:
|
||||
data = {'_token': self.token, 'postId': post_id, 'comment_message': comment_message}
|
||||
return self.post(f"{self.url}/main/party-comment/create/json", data=data)
|
||||
|
||||
def _post_main_party_post_create(self, body: str) -> Response:
|
||||
data = {'_token': self.token, 'post_message': body}
|
||||
return self.post(f"{self.url}/main/party-post/create/json", data=data)
|
||||
|
||||
def _post_main_party_post_retrieve(self) -> Response:
|
||||
data = {'_token': self.token, 'page': 1, 'switchedFrom': False}
|
||||
return self.post(f"{self.url}/main/party-post/retrieve/json", data=data)
|
||||
|
||||
# ## Friend's Wall
|
||||
|
||||
def _post_main_wall_comment_retrieve(self, post_id: int) -> Response:
|
||||
data = {'_token': self.token, 'postId': post_id}
|
||||
return self.post(f"{self.url}/main/wall-comment/retrieve/json", data=data)
|
||||
|
||||
def _post_main_wall_comment_create(self, post_id: int, comment_message: str) -> Response:
|
||||
data = {'_token': self.token, 'postId': post_id, 'comment_message': comment_message}
|
||||
return self.post(f"{self.url}/main/wall-comment/create/json", data=data)
|
||||
|
||||
def _post_main_wall_post_create(self, body: str) -> Response:
|
||||
data = {'_token': self.token, 'post_message': body}
|
||||
return self.post(f"{self.url}/main/wall-post/create/json", data=data)
|
||||
|
||||
def _post_main_wall_post_retrieve(self) -> Response:
|
||||
data = {'_token': self.token, 'page': 1, 'switchedFrom': False}
|
||||
return self.post(f"{self.url}/main/wall-post/retrieve/json", data=data)
|
||||
|
||||
# ## Medal posting
|
||||
def _post_main_wall_post_automatic(self, message: str, achievement_id: int) -> Response:
|
||||
return self.post(f"{self.url}/main/wall-post/automatic", data=dict(_token=self.token, message=message,
|
||||
achievementId=achievement_id))
|
||||
|
||||
|
||||
class CitizenAPI(
|
||||
ErepublikArticleAPI, ErepublikCountryAPI, ErepublikCompanyAPI, ErepublikEconomyAPI,
|
||||
ErepublikLeaderBoardAPI, ErepublikLocationAPI, ErepublikMilitaryAPI, ErepublikProfileAPI,
|
||||
ErepublikPresidentAPI, ErepublikPoliticsAPI, ErepublikAnniversaryAPI, ErepublikWallPostAPI,
|
||||
ErepublikTravelAPI
|
||||
):
|
||||
pass
|
3201
erepublik/citizen.py
Normal file
3201
erepublik/citizen.py
Normal file
File diff suppressed because it is too large
Load Diff
1074
erepublik/classes.py
Normal file
1074
erepublik/classes.py
Normal file
File diff suppressed because it is too large
Load Diff
253
erepublik/constants.py
Normal file
253
erepublik/constants.py
Normal file
@ -0,0 +1,253 @@
|
||||
import datetime
|
||||
from typing import Dict, Optional, Union
|
||||
|
||||
import pytz
|
||||
|
||||
__all__ = ['erep_tz', 'min_datetime', 'max_datetime', 'Country', 'AIR_RANKS', 'COUNTRIES', 'FOOD_ENERGY',
|
||||
'GROUND_RANKS', 'GROUND_RANK_POINTS', 'INDUSTRIES', 'TERRAINS']
|
||||
|
||||
erep_tz = pytz.timezone('US/Pacific')
|
||||
min_datetime = erep_tz.localize(datetime.datetime(2007, 11, 20))
|
||||
max_datetime = erep_tz.localize(datetime.datetime(2281, 9, 4))
|
||||
|
||||
|
||||
class Country:
|
||||
id: int
|
||||
name: str
|
||||
link: str
|
||||
iso: str
|
||||
|
||||
def __init__(self, country_id: int, name: str, link: str, iso: str):
|
||||
self.id = country_id
|
||||
self.name = name
|
||||
self.link = link
|
||||
self.iso = iso
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.id, self.name))
|
||||
|
||||
def __repr__(self):
|
||||
return f"Country({self.id}, '{self.name}', '{self.link}', '{self.iso}')"
|
||||
|
||||
def __str__(self):
|
||||
return f"#{self.id} {self.name}"
|
||||
|
||||
def __format__(self, format_spec):
|
||||
return self.iso
|
||||
|
||||
def __int__(self):
|
||||
return self.id
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, (int, float)):
|
||||
return self.id == int(other)
|
||||
else:
|
||||
try:
|
||||
return self.id == int(other)
|
||||
except ValueError:
|
||||
return self == other
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
return dict(id=self.id, name=self.name, iso=self.iso)
|
||||
|
||||
|
||||
class Industries:
|
||||
__by_name = {'food': 1, 'weapon': 2, 'ticket': 3, 'house': 4, 'aircraft': 23,
|
||||
'foodraw': 7, 'weaponraw': 12, 'houseraw': 18, 'aircraftraw': 24, 'airplaneraw': 24,
|
||||
'frm': 7, 'wrm': 12, 'hrm': 18, 'arm': 24,
|
||||
'frm q1': 7, 'frm q2': 8, 'frm q3': 9, 'frm q4': 10, 'frm q5': 11,
|
||||
'wrm q1': 12, 'wrm q2': 13, 'wrm q3': 14, 'wrm q4': 15, 'wrm q5': 16,
|
||||
'hrm q1': 18, 'hrm q2': 19, 'hrm q3': 20, 'hrm q4': 21, 'hrm q5': 22,
|
||||
'arm q1': 24, 'arm q2': 25, 'arm q3': 26, 'arm q4': 27, 'arm q5': 28}
|
||||
__by_id = {1: 'Food', 2: 'Weapon', 3: 'Ticket', 4: 'House', 23: 'Aircraft',
|
||||
7: 'foodRaw', 8: 'FRM q2', 9: 'FRM q3', 10: 'FRM q4', 11: 'FRM q5',
|
||||
12: 'weaponRaw', 13: 'WRM q2', 14: 'WRM q3', 15: 'WRM q4', 16: 'WRM q5',
|
||||
17: 'houseRaw', 18: 'houseRaw', 19: 'HRM q2', 20: 'HRM q3', 21: 'HRM q4', 22: 'HRM q5',
|
||||
24: 'aircraftRaw', 25: 'ARM q2', 26: 'ARM q3', 27: 'ARM q4', 28: 'ARM q5'}
|
||||
|
||||
def __getitem__(self, item) -> Optional[Union[int, str]]:
|
||||
if isinstance(item, int):
|
||||
return self.__by_id.get(item, None)
|
||||
elif isinstance(item, str):
|
||||
return self.__by_name.get(item.lower(), None)
|
||||
return
|
||||
|
||||
def __getattr__(self, item) -> Optional[Union[int, str]]:
|
||||
return self[item]
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
return dict(by_id=self.__by_id, by_name=self.__by_name)
|
||||
|
||||
|
||||
class Rank:
|
||||
id: int
|
||||
name: str
|
||||
rank_points: int
|
||||
is_air: bool
|
||||
|
||||
def __init__(self, id: int, name: str, rank_points: int, is_air: bool = False):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.rank_points = rank_points
|
||||
self.is_air = bool(is_air)
|
||||
|
||||
def __int__(self):
|
||||
return self.id
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Rank):
|
||||
return self.id == other.id if other.is_air == self.is_air else False
|
||||
else:
|
||||
return self.id == int(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
if isinstance(other, Rank):
|
||||
return not self.id == other.id if other.is_air == self.is_air else True
|
||||
else:
|
||||
return not self.id == int(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, Rank):
|
||||
return self.id < other.id if other.is_air == self.is_air else False
|
||||
else:
|
||||
return self.id < int(other)
|
||||
|
||||
def __le__(self, other):
|
||||
if isinstance(other, Rank):
|
||||
return self.id <= other.id if other.is_air == self.is_air else False
|
||||
else:
|
||||
return self.id <= int(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
if isinstance(other, Rank):
|
||||
return self.id > other.id if other.is_air == self.is_air else False
|
||||
else:
|
||||
return self.id > int(other)
|
||||
|
||||
def __ge__(self, other):
|
||||
if isinstance(other, Rank):
|
||||
return self.id >= other.id if other.is_air == self.is_air else False
|
||||
else:
|
||||
return self.id >= int(other)
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
return dict(id=self.id, name=self.name, rank_points=self.rank_points, is_air=self.is_air)
|
||||
|
||||
def __str__(self):
|
||||
return f"{'Air' if self.is_air else 'Ground'}Rank<#{self.id} {self.name}>"
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
AIR_RANK_NAMES: Dict[int, str] = {
|
||||
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*****',
|
||||
62: 'Air Vice Marshal', 63: 'Air Vice Marshal*', 64: 'Air Vice Marshal**', 65: 'Air Vice Marshal***', 66: 'Air Vice Marshal****', 67: 'Air Vice Marshal*****',
|
||||
68: 'Air Marshal', 69: 'Air Marshal*', 70: 'Air Marshal**', 71: 'Air Marshal***', 72: 'Air Marshal****', 73: 'Air Marshal*****',
|
||||
74: 'Air Chief Marshal', 75: 'Air Chief Marshal*', 76: 'Air Chief Marshal**', 77: 'Air Chief Marshal***', 78: 'Air Chief Marshal****', 79: 'Air Chief Marshal*****',
|
||||
}
|
||||
|
||||
AIR_RANK_POINTS: Dict[int, Optional[int]] = {
|
||||
1: 0, 2: 10, 3: 25, 4: 45, 5: 70, 6: 100, 7: 140, 8: 190, 9: 270, 10: 380, 11: 530, 12: 850, 13: 1300, 14: 2340, 15: 3300, 16: 4200, 17: 5150, 18: 6100, 19: 7020, 20: 9100, 21: 12750, 22: 16400, 23: 20000, 24: 23650, 25: 27300,
|
||||
26: 35500, 27: 48000, 28: 60000, 29: 72400, 30: 84500, 31: 97000, 32: 110000, 33: 140000, 34: 170000, 35: 210000, 36: 290000, 37: 350000, 38: 429000, 39: 601000, 40: 772000, 41: 944000, 42: 1115000, 43: 1287000,
|
||||
44: 1673000, 45: 2238000, 46: 2804000, 47: 3369000, 48: 3935000, 49: 4500000, 50: 5020000, 51: 7028000, 52: 9036000, 53: 11044000, 54: 13052000, 55: 15060000,
|
||||
56: 19580000, 57: 27412000, 58: 35244000, 59: 43076000, 60: 50908000, 61: 58740000, 62: 76360000, 63: 113166443, 64: 137448000, 65: None, 66: None, 67: None,
|
||||
68: None, 69: None, 70: None, 71: None, 72: None, 73: None, 74: None, 75: None, 76: None, 77: None, 78: None, 79: None,
|
||||
}
|
||||
|
||||
AIR_RANKS: Dict[int, Rank] = {i: Rank(i, AIR_RANK_NAMES[i], AIR_RANK_POINTS[i], True) for i in range(1, 80)}
|
||||
|
||||
COUNTRIES: Dict[int, Country] = {
|
||||
1: Country(1, 'Romania', 'Romania', 'ROU'), 9: Country(9, 'Brazil', 'Brazil', 'BRA'),
|
||||
10: Country(10, 'Italy', 'Italy', 'ITA'), 11: Country(11, 'France', 'France', 'FRA'),
|
||||
12: Country(12, 'Germany', 'Germany', 'DEU'), 13: Country(13, 'Hungary', 'Hungary', 'HUN'),
|
||||
14: Country(14, 'China', 'China', 'CHN'), 15: Country(15, 'Spain', 'Spain', 'ESP'),
|
||||
23: Country(23, 'Canada', 'Canada', 'CAN'), 24: Country(24, 'USA', 'USA', 'USA'),
|
||||
26: Country(26, 'Mexico', 'Mexico', 'MEX'), 27: Country(27, 'Argentina', 'Argentina', 'ARG'),
|
||||
28: Country(28, 'Venezuela', 'Venezuela', 'VEN'), 29: Country(29, 'United Kingdom', 'United-Kingdom', 'GBR'),
|
||||
30: Country(30, 'Switzerland', 'Switzerland', 'CHE'), 31: Country(31, 'Netherlands', 'Netherlands', 'NLD'),
|
||||
32: Country(32, 'Belgium', 'Belgium', 'BEL'), 33: Country(33, 'Austria', 'Austria', 'AUT'),
|
||||
34: Country(34, 'Czech Republic', 'Czech-Republic', 'CZE'), 35: Country(35, 'Poland', 'Poland', 'POL'),
|
||||
36: Country(36, 'Slovakia', 'Slovakia', 'SVK'), 37: Country(37, 'Norway', 'Norway', 'NOR'),
|
||||
38: Country(38, 'Sweden', 'Sweden', 'SWE'), 39: Country(39, 'Finland', 'Finland', 'FIN'),
|
||||
40: Country(40, 'Ukraine', 'Ukraine', 'UKR'), 41: Country(41, 'Russia', 'Russia', 'RUS'),
|
||||
42: Country(42, 'Bulgaria', 'Bulgaria', 'BGR'), 43: Country(43, 'Turkey', 'Turkey', 'TUR'),
|
||||
44: Country(44, 'Greece', 'Greece', 'GRC'), 45: Country(45, 'Japan', 'Japan', 'JPN'),
|
||||
47: Country(47, 'South Korea', 'South-Korea', 'KOR'), 48: Country(48, 'India', 'India', 'IND'),
|
||||
49: Country(49, 'Indonesia', 'Indonesia', 'IDN'), 50: Country(50, 'Australia', 'Australia', 'AUS'),
|
||||
51: Country(51, 'South Africa', 'South-Africa', 'ZAF'),
|
||||
52: Country(52, 'Republic of Moldova', 'Republic-of-Moldova', 'MDA'),
|
||||
53: Country(53, 'Portugal', 'Portugal', 'PRT'), 54: Country(54, 'Ireland', 'Ireland', 'IRL'),
|
||||
55: Country(55, 'Denmark', 'Denmark', 'DNK'), 56: Country(56, 'Iran', 'Iran', 'IRN'),
|
||||
57: Country(57, 'Pakistan', 'Pakistan', 'PAK'), 58: Country(58, 'Israel', 'Israel', 'ISR'),
|
||||
59: Country(59, 'Thailand', 'Thailand', 'THA'), 61: Country(61, 'Slovenia', 'Slovenia', 'SVN'),
|
||||
63: Country(63, 'Croatia', 'Croatia', 'HRV'), 64: Country(64, 'Chile', 'Chile', 'CHL'),
|
||||
65: Country(65, 'Serbia', 'Serbia', 'SRB'), 66: Country(66, 'Malaysia', 'Malaysia', 'MYS'),
|
||||
67: Country(67, 'Philippines', 'Philippines', 'PHL'), 68: Country(68, 'Singapore', 'Singapore', 'SGP'),
|
||||
69: Country(69, 'Bosnia and Herzegovina', 'Bosnia-Herzegovina', 'BiH'),
|
||||
70: Country(70, 'Estonia', 'Estonia', 'EST'), 80: Country(80, 'Montenegro', 'Montenegro', 'MNE'),
|
||||
71: Country(71, 'Latvia', 'Latvia', 'LVA'), 72: Country(72, 'Lithuania', 'Lithuania', 'LTU'),
|
||||
73: Country(73, 'North Korea', 'North-Korea', 'PRK'), 74: Country(74, 'Uruguay', 'Uruguay', 'URY'),
|
||||
75: Country(75, 'Paraguay', 'Paraguay', 'PRY'), 76: Country(76, 'Bolivia', 'Bolivia', 'BOL'),
|
||||
77: Country(77, 'Peru', 'Peru', 'PER'), 78: Country(78, 'Colombia', 'Colombia', 'COL'),
|
||||
79: Country(79, 'Republic of Macedonia (FYROM)', 'Republic-of-Macedonia-FYROM', 'MKD'),
|
||||
81: Country(81, 'Republic of China (Taiwan)', 'Republic-of-China-Taiwan', 'TWN'),
|
||||
82: Country(82, 'Cyprus', 'Cyprus', 'CYP'), 167: Country(167, 'Albania', 'Albania', 'ALB'),
|
||||
83: Country(83, 'Belarus', 'Belarus', 'BLR'), 84: Country(84, 'New Zealand', 'New-Zealand', 'NZL'),
|
||||
164: Country(164, 'Saudi Arabia', 'Saudi-Arabia', 'SAU'), 165: Country(165, 'Egypt', 'Egypt', 'EGY'),
|
||||
166: Country(166, 'United Arab Emirates', 'United-Arab-Emirates', 'UAE'),
|
||||
168: Country(168, 'Georgia', 'Georgia', 'GEO'), 169: Country(169, 'Armenia', 'Armenia', 'ARM'),
|
||||
170: Country(170, 'Nigeria', 'Nigeria', 'NGA'), 171: Country(171, 'Cuba', 'Cuba', 'CUB')
|
||||
}
|
||||
|
||||
FOOD_ENERGY: Dict[str, int] = dict(q1=2, q2=4, q3=6, q4=8, q5=10, q6=12, q7=20)
|
||||
|
||||
GROUND_RANK_NAMES: Dict[int, str] = {
|
||||
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'
|
||||
}
|
||||
|
||||
GROUND_RANK_POINTS: Dict[int, int] = {
|
||||
1: 0, 2: 15, 3: 45, 4: 80, 5: 120, 6: 170, 7: 250, 8: 350, 9: 450, 10: 600, 11: 800, 12: 1000,
|
||||
13: 1400, 14: 1850, 15: 2350, 16: 3000, 17: 3750, 18: 5000, 19: 6500, 20: 9000, 21: 12000,
|
||||
22: 15500, 23: 20000, 24: 25000, 25: 31000, 26: 40000, 27: 52000, 28: 67000, 29: 85000,
|
||||
30: 110000, 31: 140000, 32: 180000, 33: 225000, 34: 285000, 35: 355000, 36: 435000, 37: 540000,
|
||||
38: 660000, 39: 800000, 40: 950000, 41: 1140000, 42: 1350000, 43: 1600000, 44: 1875000,
|
||||
45: 2185000, 46: 2550000, 47: 3000000, 48: 3500000, 49: 4150000, 50: 4900000, 51: 5800000,
|
||||
52: 7000000, 53: 9000000, 54: 11500000, 55: 14500000, 56: 18000000, 57: 22000000, 58: 26500000,
|
||||
59: 31500000, 60: 37000000, 61: 43000000, 62: 50000000, 63: 100000000, 64: 200000000,
|
||||
65: 500000000, 66: 1000000000, 67: 2000000000, 68: 4000000000, 69: 10000000000, 70: 20000000000,
|
||||
71: 30000000000, 72: 40000000000, 73: 50000000000, 74: 60000000000, 75: 70000000000,
|
||||
76: 80000000000, 77: 90000000000, 78: 100000000000, 79: 110000000000, 80: 120000000000,
|
||||
81: 130000000000, 82: 140000000000, 83: 150000000000, 84: 160000000000, 85: 170000000000,
|
||||
86: 180000000000, 87: 190000000000, 88: 200000000000, 89: 210000000000
|
||||
}
|
||||
|
||||
GROUND_RANKS: Dict[int, Rank] = {i: Rank(i, GROUND_RANK_NAMES[i], GROUND_RANK_POINTS[i], False) for i in range(1, 90)}
|
||||
|
||||
INDUSTRIES = Industries()
|
||||
|
||||
TERRAINS: Dict[int, str] = {0: 'Standard', 1: 'Industrial', 2: 'Urban', 3: 'Suburbs', 4: 'Airport', 5: 'Plains',
|
||||
6: 'Wasteland', 7: 'Mountains', 8: 'Beach', 9: 'Swamp', 10: 'Mud', 11: 'Hills',
|
||||
12: 'Jungle', 13: 'Forest', 14: 'Desert'}
|
299
erepublik/utils.py
Normal file
299
erepublik/utils.py
Normal file
@ -0,0 +1,299 @@
|
||||
import datetime
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import unicodedata
|
||||
import warnings
|
||||
from base64 import b64encode
|
||||
from decimal import Decimal
|
||||
from logging import Logger
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
import pytz
|
||||
import requests
|
||||
from requests import Response
|
||||
|
||||
from erepublik import __version__, constants
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
__all__ = [
|
||||
'VERSION', 'calculate_hit', 'date_from_eday', 'eday_from_date', 'deprecation', 'get_final_hit_dmg', 'write_file',
|
||||
'get_air_hit_dmg_value', 'get_file', 'get_ground_hit_dmg_value', 'get_sleep_seconds', 'good_timedelta', 'slugify',
|
||||
'interactive_sleep', 'json', 'localize_dt', 'localize_timestamp', 'normalize_html_json', 'now', 'silent_sleep',
|
||||
'json_decode_object_hook', 'json_load', 'json_loads', 'json_dump', 'json_dumps', 'b64json', 'ErepublikJSONEncoder',
|
||||
]
|
||||
|
||||
VERSION: str = __version__
|
||||
|
||||
|
||||
def now() -> datetime.datetime:
|
||||
return datetime.datetime.now(constants.erep_tz).replace(microsecond=0)
|
||||
|
||||
|
||||
def localize_timestamp(timestamp: int) -> datetime.datetime:
|
||||
return datetime.datetime.fromtimestamp(timestamp, constants.erep_tz)
|
||||
|
||||
|
||||
def localize_dt(dt: Union[datetime.date, datetime.datetime]) -> datetime.datetime:
|
||||
if isinstance(dt, datetime.datetime):
|
||||
return constants.erep_tz.localize(dt)
|
||||
elif isinstance(dt, datetime.date):
|
||||
return constants.erep_tz.localize(datetime.datetime.combine(dt, datetime.time(0, 0, 0)))
|
||||
else:
|
||||
raise TypeError(f"Argument dt must be and instance of datetime.datetime or datetime.date not {type(dt)}")
|
||||
|
||||
|
||||
def good_timedelta(dt: datetime.datetime, td: datetime.timedelta) -> datetime.datetime:
|
||||
"""Normalize timezone aware datetime object after timedelta to correct jumps over DST switches
|
||||
|
||||
:param dt: Timezone aware datetime object
|
||||
:type dt: datetime.datetime
|
||||
:param td: timedelta object
|
||||
:type td: datetime.timedelta
|
||||
:return: datetime object with correct timezone when jumped over DST
|
||||
:rtype: datetime.datetime
|
||||
"""
|
||||
return constants.erep_tz.normalize(dt + td)
|
||||
|
||||
|
||||
def eday_from_date(date: Union[datetime.date, datetime.datetime] = None) -> int:
|
||||
if date is None:
|
||||
date = now()
|
||||
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_until: datetime.datetime) -> int:
|
||||
""" time_until aware datetime object Wrapper for sleeping until """
|
||||
sleep_seconds = int((time_until - 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(f"\rSleeping for {sleep_seconds:4} more seconds")
|
||||
sys.stdout.flush()
|
||||
time.sleep(seconds)
|
||||
sleep_seconds -= seconds
|
||||
sys.stdout.write("\r")
|
||||
|
||||
|
||||
silent_sleep = time.sleep
|
||||
|
||||
|
||||
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:
|
||||
ret = f.write(content.encode("utf-8"))
|
||||
return ret
|
||||
|
||||
|
||||
def normalize_html_json(js: str) -> str:
|
||||
js = re.sub(r' \'(.*?)\'', lambda a: f'"{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 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, is_deploy: bool = False) -> Decimal:
|
||||
dec = 3 if is_deploy else 0
|
||||
base_str = (1 + Decimal(str(round(strength, 3))) / 400)
|
||||
base_rnk = (1 + Decimal(str(rang / 5)))
|
||||
base_wpn = (1 + Decimal(str(weapon / 100)))
|
||||
dmg = 10 * base_str * base_rnk * base_wpn
|
||||
|
||||
dmg = get_final_hit_dmg(dmg, rang, tp=tp, elite=elite, ne=ne, booster=booster)
|
||||
return Decimal(round(dmg, dec))
|
||||
|
||||
|
||||
def get_ground_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False,
|
||||
booster: int = 0, weapon_power: int = 200) -> Decimal:
|
||||
r = requests.get(f'https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}').json()
|
||||
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 get_air_hit_dmg_value(citizen_id: int, natural_enemy: bool = False, true_patriot: bool = False, booster: int = 0,
|
||||
weapon_power: int = 0) -> Decimal:
|
||||
r = requests.get(f'https://www.erepublik.com/en/main/citizen-profile-json/{citizen_id}').json()
|
||||
rang = r['military']['militaryData']['aircraft']['rankNumber']
|
||||
elite = r['citizenAttributes']['level'] > 100
|
||||
return calculate_hit(0, rang, true_patriot, elite, natural_enemy, booster, weapon_power)
|
||||
|
||||
|
||||
def get_final_hit_dmg(base_dmg: Union[Decimal, float, str], rang: int,
|
||||
tp: bool = False, elite: bool = False, ne: bool = False, booster: int = 0) -> Decimal:
|
||||
dmg = Decimal(str(base_dmg))
|
||||
|
||||
if elite:
|
||||
dmg = dmg * 11 / 10
|
||||
if tp and rang >= 70:
|
||||
dmg = dmg * (1 + Decimal((rang - 69) / 10))
|
||||
dmg = dmg * (100 + booster) / 100
|
||||
if ne:
|
||||
dmg = dmg * 11 / 10
|
||||
return Decimal(dmg)
|
||||
|
||||
|
||||
def deprecation(message):
|
||||
warnings.warn(message, DeprecationWarning, stacklevel=2)
|
||||
|
||||
|
||||
def json_decode_object_hook(
|
||||
o: Union[Dict[str, Any], List[Any], int, float, str]
|
||||
) -> Union[Dict[str, Any], List[Any], int, float, str, datetime.date, datetime.datetime, datetime.timedelta]:
|
||||
""" Convert classes.ErepublikJSONEncoder datetime, date and timedelta to their python objects
|
||||
|
||||
:param o:
|
||||
:return: Union[Dict[str, Any], List[Any], int, float, str, datetime.date, datetime.datetime, datetime.timedelta]
|
||||
"""
|
||||
if o.get('__type__'):
|
||||
_type = o.get('__type__')
|
||||
if _type == 'datetime':
|
||||
dt = datetime.datetime.strptime(f"{o['date']} {o['time']}", '%Y-%m-%d %H:%M:%S')
|
||||
if o.get('tzinfo'):
|
||||
dt = pytz.timezone(o['tzinfo']).localize(dt)
|
||||
return dt
|
||||
elif _type == 'date':
|
||||
dt = datetime.datetime.strptime(f"{o['date']}", '%Y-%m-%d')
|
||||
return dt.date()
|
||||
elif _type == 'timedelta':
|
||||
return datetime.timedelta(seconds=o['total_seconds'])
|
||||
return o
|
||||
|
||||
|
||||
def json_load(f, **kwargs):
|
||||
kwargs.update(object_hook=json_decode_object_hook)
|
||||
return json.load(f, **kwargs)
|
||||
|
||||
|
||||
def json_loads(s: str, **kwargs):
|
||||
kwargs.update(object_hook=json_decode_object_hook)
|
||||
return json.loads(s, **kwargs)
|
||||
|
||||
|
||||
def json_dump(obj, fp, *args, **kwargs):
|
||||
if not kwargs.get('cls'):
|
||||
kwargs.update(cls=ErepublikJSONEncoder)
|
||||
return json.dump(obj, fp, *args, **kwargs)
|
||||
|
||||
|
||||
def json_dumps(obj, *args, **kwargs):
|
||||
if not kwargs.get('cls'):
|
||||
kwargs.update(cls=ErepublikJSONEncoder)
|
||||
return json.dumps(obj, *args, **kwargs)
|
||||
|
||||
|
||||
def b64json(obj: Union[Dict[str, Union[int, List[str]]], List[str]]):
|
||||
if isinstance(obj, list):
|
||||
return b64encode(json.dumps(obj, separators=(',', ':')).encode('utf-8')).decode('utf-8')
|
||||
elif isinstance(obj, (int, str)):
|
||||
return obj
|
||||
elif isinstance(obj, dict):
|
||||
for k, v in obj.items():
|
||||
obj[k] = b64json(v)
|
||||
else:
|
||||
from .classes import ErepublikException
|
||||
raise ErepublikException(f'Unhandled object type! obj is {type(obj)}')
|
||||
return b64encode(json.dumps(obj, separators=(',', ':')).encode('utf-8')).decode('utf-8')
|
||||
|
||||
|
||||
class ErepublikJSONEncoder(json.JSONEncoder):
|
||||
def default(self, o):
|
||||
try:
|
||||
from erepublik.citizen import Citizen
|
||||
if isinstance(o, Decimal):
|
||||
return float(f"{o:.02f}")
|
||||
elif isinstance(o, datetime.datetime):
|
||||
return dict(__type__='datetime', date=o.strftime("%Y-%m-%d"), time=o.strftime("%H:%M:%S"),
|
||||
tzinfo=str(o.tzinfo) if o.tzinfo else None)
|
||||
elif isinstance(o, datetime.date):
|
||||
return dict(__type__='date', date=o.strftime("%Y-%m-%d"))
|
||||
elif isinstance(o, datetime.timedelta):
|
||||
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=dict(o.__dict__['headers']), url=o.url, text=o.text, status_code=o.status_code)
|
||||
elif hasattr(o, 'as_dict'):
|
||||
return o.as_dict
|
||||
elif isinstance(o, set):
|
||||
return list(o)
|
||||
elif isinstance(o, Citizen):
|
||||
return o.to_json()
|
||||
elif isinstance(o, Logger):
|
||||
return str(o)
|
||||
elif hasattr(o, '__dict__'):
|
||||
return o.__dict__
|
||||
else:
|
||||
return super().default(o)
|
||||
except Exception as e: # noqa
|
||||
return str(e)
|
@ -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
File diff suppressed because it is too large
Load Diff
@ -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)
|
@ -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))
|
106
examples/battle_launcher.py
Normal file
106
examples/battle_launcher.py
Normal file
@ -0,0 +1,106 @@
|
||||
import threading
|
||||
from datetime import timedelta
|
||||
|
||||
from erepublik import Citizen, utils
|
||||
|
||||
CONFIG = {
|
||||
'email': 'player@email.com',
|
||||
'password': 'Pa$5w0rd!',
|
||||
'interactive': True,
|
||||
'fight': True,
|
||||
'debug': True,
|
||||
'battle_launcher': {
|
||||
# War id: {auto_attack: bool (attack asap when region is available), regions: [region_ids allowed to attack]}
|
||||
121672: {"auto_attack": False, "regions": [661]},
|
||||
125530: {"auto_attack": False, "regions": [259]},
|
||||
125226: {"auto_attack": True, "regions": [549]},
|
||||
124559: {"auto_attack": True, "regions": [176]}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def _battle_launcher(player: Citizen):
|
||||
"""Launch battles. Check every 5th minute (0,5,10...45,50,55) if any battle could be started on specified regions
|
||||
and after launching wait for 90 minutes before starting next attack so that all battles aren't launched at the same
|
||||
time. If player is allowed to fight, do 100 hits on the first round in players division.
|
||||
|
||||
:param player: Logged in Citizen instance
|
||||
:type player: Citizen
|
||||
"""
|
||||
global CONFIG
|
||||
finished_war_ids = {*[]}
|
||||
war_data = CONFIG.get('battle_launcher', {})
|
||||
war_ids = {int(war_id) for war_id in war_data.keys()}
|
||||
next_attack_time = player.now
|
||||
next_attack_time = next_attack_time.replace(minute=next_attack_time.minute // 5 * 5, second=0)
|
||||
while not player.stop_threads.is_set():
|
||||
try:
|
||||
attacked = False
|
||||
player.update_war_info()
|
||||
running_wars = {b.war_id for b in player.all_battles.values()}
|
||||
for war_id in war_ids - finished_war_ids - running_wars:
|
||||
war = war_data[war_id]
|
||||
war_regions = set(war.get('regions'))
|
||||
auto_attack = war.get('auto_attack')
|
||||
|
||||
status = player.get_war_status(war_id)
|
||||
if status.get('ended', False):
|
||||
CONFIG['battle_launcher'].pop(war_id, None)
|
||||
finished_war_ids.add(war_id)
|
||||
continue
|
||||
elif not status.get('can_attack'):
|
||||
continue
|
||||
|
||||
if auto_attack or (player.now.hour > 20 or player.now.hour < 2):
|
||||
for reg in war_regions:
|
||||
if attacked:
|
||||
break
|
||||
if reg in status.get('regions', {}).keys():
|
||||
player.launch_attack(war_id, reg, status.get('regions', {}).get(reg))
|
||||
attacked = True
|
||||
hits = 100
|
||||
if player.energy.food_fights >= hits and player.config.fight:
|
||||
for _ in range(120):
|
||||
player.update_war_info()
|
||||
battle_id = player.get_war_status(war_id).get("battle_id")
|
||||
if battle_id is not None and battle_id in player.all_battles:
|
||||
battle = player.all_battles.get(battle_id)
|
||||
for division in battle.div.values():
|
||||
if division.div == player.division:
|
||||
div = division
|
||||
break
|
||||
else:
|
||||
player.report_error("Players division not found in the first round!")
|
||||
break
|
||||
player.fight(battle, div, battle.invader, hits)
|
||||
break
|
||||
player.sleep(1)
|
||||
if attacked:
|
||||
break
|
||||
if attacked:
|
||||
break
|
||||
war_ids -= finished_war_ids
|
||||
if attacked:
|
||||
next_attack_time = utils.good_timedelta(next_attack_time, timedelta(hours=1, minutes=30))
|
||||
else:
|
||||
next_attack_time = utils.good_timedelta(next_attack_time, timedelta(minutes=5))
|
||||
player.stop_threads.wait(utils.get_sleep_seconds(next_attack_time))
|
||||
except Exception as e:
|
||||
player.report_error(f"Task battle launcher ran into error {e}")
|
||||
|
||||
|
||||
# noinspection DuplicatedCode
|
||||
def main():
|
||||
player = Citizen(email=CONFIG['email'], password=CONFIG['password'], auto_login=False)
|
||||
player.config.interactive = CONFIG['interactive']
|
||||
player.config.fight = CONFIG['fight']
|
||||
player.set_debug(CONFIG.get('debug', False))
|
||||
player.login()
|
||||
if CONFIG.get('battle_launcher'):
|
||||
name = f"{player.name}-battle_launcher-{threading.active_count() - 1}"
|
||||
state_thread = threading.Thread(target=_battle_launcher, args=(player,), name=name)
|
||||
state_thread.start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
113
examples/eat_work_train.py
Normal file
113
examples/eat_work_train.py
Normal file
@ -0,0 +1,113 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from erepublik import Citizen, constants, utils
|
||||
|
||||
CONFIG = {
|
||||
'email': 'player@email.com',
|
||||
'password': 'Pa$5w0rd!',
|
||||
'interactive': True,
|
||||
'debug': True,
|
||||
'work': True,
|
||||
'ot': True, # Work OverTime
|
||||
'wam': True, # WorkAsManager
|
||||
'train': True
|
||||
}
|
||||
|
||||
|
||||
# noinspection DuplicatedCode
|
||||
def main():
|
||||
player = Citizen(email=CONFIG['email'], password=CONFIG['password'], auto_login=False)
|
||||
player.config.interactive = CONFIG['interactive']
|
||||
player.config.work = CONFIG['work']
|
||||
player.config.train = CONFIG['train']
|
||||
player.config.ot = CONFIG['ot']
|
||||
player.config.wam = CONFIG['wam']
|
||||
player.set_debug(CONFIG.get('debug', False))
|
||||
player.login()
|
||||
now = player.now.replace(second=0, microsecond=0)
|
||||
dt_max = constants.max_datetime
|
||||
tasks = {
|
||||
'eat': now,
|
||||
}
|
||||
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.wam:
|
||||
tasks.update({'wam': now.replace(hour=14, minute=0)})
|
||||
while True:
|
||||
try:
|
||||
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 = utils.good_timedelta(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 = utils.good_timedelta(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_as_manager()
|
||||
player.eat()
|
||||
if success:
|
||||
next_time = utils.good_timedelta(now.replace(hour=14, minute=0, second=0, microsecond=0),
|
||||
timedelta(days=1))
|
||||
else:
|
||||
next_time = utils.good_timedelta(now.replace(second=0, microsecond=0), timedelta(minutes=30))
|
||||
|
||||
tasks.update({'wam': next_time})
|
||||
|
||||
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('ot', dt_max) <= now:
|
||||
player.write_log("Doing task: work overtime")
|
||||
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})
|
||||
|
||||
closest_next_time = dt_max
|
||||
next_tasks = []
|
||||
for task, next_time in sorted(tasks.items(), key=lambda s: s[1]):
|
||||
next_tasks.append(f"{next_time.strftime('%F %T')}: {task}")
|
||||
if next_time < closest_next_time:
|
||||
closest_next_time = next_time
|
||||
sleep_seconds = int(utils.get_sleep_seconds(closest_next_time))
|
||||
if sleep_seconds <= 0:
|
||||
player.write_log(f"Loop detected! Offending task: '{next_tasks[0]}'")
|
||||
player.write_log("My next Tasks and there time:\n" + "\n".join(sorted(next_tasks)))
|
||||
player.write_log(f"Sleeping until (eRep): {closest_next_time.strftime('%F %T')}"
|
||||
f" (sleeping for {sleep_seconds}s)")
|
||||
seconds_to_sleep = sleep_seconds if sleep_seconds > 0 else 0
|
||||
player.sleep(seconds_to_sleep)
|
||||
except Exception as e:
|
||||
player.report_error(f"Task main loop ran into error: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -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
|
@ -1,10 +1,21 @@
|
||||
pip==19.1.1
|
||||
bumpversion==0.5.3
|
||||
wheel==0.32.1
|
||||
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
|
||||
bump2version==1.0.1
|
||||
coverage==5.5
|
||||
edx-sphinx-theme==2.0.0
|
||||
flake8==3.8.4
|
||||
ipython>=7.21.0
|
||||
jedi!=0.18.0
|
||||
isort==5.7.0
|
||||
pip==21.0.1
|
||||
pre-commit==2.10.1
|
||||
pur==5.3.0
|
||||
PyInstaller==4.2
|
||||
PySocks==1.7.1
|
||||
pytest==6.2.2
|
||||
pytz==2021.1
|
||||
requests==2.25.1
|
||||
requests-toolbelt==0.9.1
|
||||
responses==0.12.1
|
||||
setuptools==54.0.0
|
||||
Sphinx==3.5.1
|
||||
twine==3.3.0
|
||||
wheel==0.36.2
|
||||
|
28
setup.cfg
28
setup.cfg
@ -1,13 +1,16 @@
|
||||
[bumpversion]
|
||||
current_version = 0.1.2
|
||||
current_version = 0.24.2.4
|
||||
commit = True
|
||||
tag = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.?(?P<dev>\d+)?
|
||||
serialize = {major}.{minor}.{patch}.{dev}
|
||||
{major}.{minor}.{patch}
|
||||
|
||||
[bumpversion:file:setup.py]
|
||||
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}'
|
||||
|
||||
@ -15,9 +18,22 @@ replace = __version__ = '{new_version}'
|
||||
universal = 1
|
||||
|
||||
[flake8]
|
||||
exclude = docs
|
||||
max-line-length = 120
|
||||
ignore = E722
|
||||
exclude = docs,.git,log,debug,venv
|
||||
max-line-length = 240
|
||||
ignore = D100,D101,D102,D103
|
||||
|
||||
[aliases]
|
||||
[pycodestyle]
|
||||
max-line-length = 240
|
||||
exclude = .git,log,debug,venv, build
|
||||
|
||||
[mypy]
|
||||
python_version = 3.8
|
||||
check_untyped_defs = True
|
||||
ignore_missing_imports = False
|
||||
warn_unused_ignores = True
|
||||
warn_redundant_casts = True
|
||||
warn_unused_configs = True
|
||||
|
||||
[isort]
|
||||
multi_line_output = 2
|
||||
line_length = 240
|
||||
|
38
setup.py
38
setup.py
@ -3,7 +3,7 @@
|
||||
|
||||
"""The setup script."""
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
with open('README.rst') as readme_file:
|
||||
readme = readme_file.read()
|
||||
@ -11,40 +11,46 @@ 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 = [
|
||||
'PySocks==1.7.1',
|
||||
'pytz==2021.1',
|
||||
'requests==2.25.1',
|
||||
'requests-toolbelt==0.9.1',
|
||||
]
|
||||
|
||||
setup_requirements = [ ]
|
||||
setup_requirements = []
|
||||
|
||||
test_requirements = [ ]
|
||||
test_requirements = [
|
||||
"pytest==6.1.2",
|
||||
"responses==0.12.1"
|
||||
]
|
||||
|
||||
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',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
],
|
||||
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.24.2.4',
|
||||
zip_safe=False,
|
||||
)
|
||||
|
@ -1,3 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Unit test package for erepublik_script."""
|
||||
"""Unit test package for erepublik."""
|
||||
|
@ -1,34 +1,137 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Tests for `erepublik_script` package."""
|
||||
"""Tests for `erepublik` package."""
|
||||
|
||||
from erepublik import Citizen
|
||||
|
||||
import unittest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from erepublik_script import Citizen
|
||||
from erepublik_script import cli
|
||||
|
||||
|
||||
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_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
|
||||
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):
|
||||
def is_wc_close():
|
||||
return self.citizen.max_time_till_full_ff > self.citizen.time_till_week_change
|
||||
self.citizen.config.fight = False
|
||||
self.assertEqual(self.citizen.should_fight(), (0, "Fighting not allowed!", False))
|
||||
|
||||
self.citizen.config.fight = True
|
||||
|
||||
# Level up
|
||||
self.citizen.energy.limit = 3000
|
||||
self.citizen.details.xp = 24705
|
||||
if not is_wc_close:
|
||||
self.assertEqual(self.citizen.should_fight(), (0, 'Level up', False))
|
||||
|
||||
self.citizen.energy.recovered = 3000
|
||||
self.citizen.energy.recoverable = 2950
|
||||
self.citizen.energy.interval = 30
|
||||
self.assertEqual(self.citizen.should_fight(), (900, 'Level up', True))
|
||||
self.citizen.my_companies.ff_lockdown = 160
|
||||
self.assertEqual(self.citizen.should_fight(), (900, 'Level up', True))
|
||||
self.citizen.my_companies.ff_lockdown = 0
|
||||
|
||||
# Level up reachable
|
||||
self.citizen.details.xp = 24400
|
||||
self.assertEqual(self.citizen.should_fight(), (305, 'Fighting for close Levelup. Doing 305 hits', True))
|
||||
self.citizen.my_companies.ff_lockdown = 160
|
||||
self.assertEqual(self.citizen.should_fight(), (305, 'Fighting for close Levelup. Doing 305 hits', True))
|
||||
self.citizen.my_companies.ff_lockdown = 0
|
||||
|
||||
self.citizen.details.xp = 21000
|
||||
self.assertEqual(self.citizen.should_fight(), (75, 'Obligatory fighting for at least 75pp', True))
|
||||
self.citizen.my_companies.ff_lockdown = 160
|
||||
self.assertEqual(self.citizen.should_fight(), (75, 'Obligatory fighting for at least 75pp', True))
|
||||
self.citizen.my_companies.ff_lockdown = 0
|
||||
self.citizen.details.pp = 80
|
||||
|
||||
# All-in (type = all-in and full ff)
|
||||
self.citizen.config.all_in = True
|
||||
self.assertEqual(self.citizen.should_fight(), (595, 'Fighting all-in. Doing 595 hits', False))
|
||||
self.citizen.my_companies.ff_lockdown = 160
|
||||
self.assertEqual(self.citizen.should_fight(), (
|
||||
435, 'Fight count modified (old count: 595 | FF: 595 | WAM ff_lockdown: 160 | New count: 435)', False
|
||||
))
|
||||
self.citizen.my_companies.ff_lockdown = 0
|
||||
|
||||
self.citizen.config.air = True
|
||||
self.citizen.energy.recoverable = 1000
|
||||
self.assertEqual(self.citizen.should_fight(), (400, 'Fighting all-in in AIR. Doing 400 hits', False))
|
||||
self.citizen.my_companies.ff_lockdown = 160
|
||||
self.assertEqual(self.citizen.should_fight(), (
|
||||
240, 'Fight count modified (old count: 400 | FF: 400 | WAM ff_lockdown: 160 | New count: 240)', False
|
||||
))
|
||||
self.citizen.my_companies.ff_lockdown = 0
|
||||
self.citizen.config.all_in = False
|
||||
|
||||
self.citizen.config.next_energy = True
|
||||
self.citizen.energy.limit = 5000
|
||||
self.citizen.details.next_pp = [100, 150, 250, 400, 500]
|
||||
self.assertEqual(self.citizen.should_fight(), (320, 'Fighting for +1 energy. Doing 320 hits', False))
|
||||
self.citizen.my_companies.ff_lockdown = 160
|
||||
self.assertEqual(self.citizen.should_fight(), (
|
||||
160, 'Fight count modified (old count: 320 | FF: 400 | WAM ff_lockdown: 160 | New count: 160)', False
|
||||
))
|
||||
self.citizen.my_companies.ff_lockdown = 0
|
||||
self.citizen.energy.limit = 3000
|
||||
self.citizen.details.next_pp = [19250, 20000]
|
||||
self.citizen.config.next_energy = False
|
||||
|
||||
# 1h worth of energy
|
||||
self.citizen.energy.recoverable = 2910
|
||||
self.assertEqual(self.citizen.should_fight(), (30, 'Fighting for 1h energy. Doing 30 hits', True))
|
||||
|
Reference in New Issue
Block a user