JoostSijm 5 éve
commit
3c82376392
11 módosított fájl, 729 hozzáadás és 0 törlés
  1. 7 0
      .gitignore
  2. 19 0
      Pipfile
  3. 261 0
      Pipfile.lock
  4. 83 0
      app/__init__.py
  5. 33 0
      app/__main__.py
  6. 88 0
      app/api.py
  7. 105 0
      app/app.py
  8. 47 0
      app/database.py
  9. 8 0
      app/jobs.py
  10. 74 0
      app/models.py
  11. 4 0
      example.env

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+.venv/
+.env
+__pycache__
+*.html
+*.log
+*.png
+jobs.json

+ 19 - 0
Pipfile

@@ -0,0 +1,19 @@
+[[source]]
+name = "pypi"
+url = "https://pypi.org/simple"
+verify_ssl = true
+
+[dev-packages]
+
+[packages]
+requests = "*"
+beautifulsoup4 = "*"
+sqlalchemy = "*"
+python-dotenv = "*"
+psycopg2-binary = "*"
+apscheduler = "*"
+python-telegram-bot = "*"
+python-dateutil = "*"
+
+[requires]
+python_version = "3"

+ 261 - 0
Pipfile.lock

@@ -0,0 +1,261 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "d30abf65dac00c961cad98851fb0582b0b0322d8f04dc6eff4f853ea7695b05c"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "apscheduler": {
+            "hashes": [
+                "sha256:3bb5229eed6fbbdafc13ce962712ae66e175aa214c69bed35a06bffcf0c5e244",
+                "sha256:e8b1ecdb4c7cb2818913f766d5898183c7cb8936680710a4d3a966e02262e526"
+            ],
+            "index": "pypi",
+            "version": "==3.6.3"
+        },
+        "beautifulsoup4": {
+            "hashes": [
+                "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a",
+                "sha256:9fbb4d6e48ecd30bcacc5b63b94088192dcda178513b2ae3c394229f8911b887",
+                "sha256:e1505eeed31b0f4ce2dbb3bc8eb256c04cc2b3b72af7d551a4ab6efd5cbe5dae"
+            ],
+            "index": "pypi",
+            "version": "==4.8.2"
+        },
+        "certifi": {
+            "hashes": [
+                "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
+                "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
+            ],
+            "version": "==2019.11.28"
+        },
+        "cffi": {
+            "hashes": [
+                "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff",
+                "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b",
+                "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac",
+                "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0",
+                "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384",
+                "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26",
+                "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6",
+                "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b",
+                "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e",
+                "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd",
+                "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2",
+                "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66",
+                "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc",
+                "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8",
+                "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55",
+                "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4",
+                "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5",
+                "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d",
+                "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78",
+                "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa",
+                "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793",
+                "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f",
+                "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a",
+                "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f",
+                "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30",
+                "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f",
+                "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3",
+                "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"
+            ],
+            "version": "==1.14.0"
+        },
+        "chardet": {
+            "hashes": [
+                "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+                "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+            ],
+            "version": "==3.0.4"
+        },
+        "cryptography": {
+            "hashes": [
+                "sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c",
+                "sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595",
+                "sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad",
+                "sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651",
+                "sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2",
+                "sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff",
+                "sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d",
+                "sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42",
+                "sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d",
+                "sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e",
+                "sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912",
+                "sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793",
+                "sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13",
+                "sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7",
+                "sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0",
+                "sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879",
+                "sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f",
+                "sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9",
+                "sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2",
+                "sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf",
+                "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"
+            ],
+            "version": "==2.8"
+        },
+        "decorator": {
+            "hashes": [
+                "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce",
+                "sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"
+            ],
+            "version": "==4.4.1"
+        },
+        "future": {
+            "hashes": [
+                "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
+            ],
+            "version": "==0.18.2"
+        },
+        "idna": {
+            "hashes": [
+                "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
+                "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
+            ],
+            "version": "==2.8"
+        },
+        "psycopg2-binary": {
+            "hashes": [
+                "sha256:040234f8a4a8dfd692662a8308d78f63f31a97e1c42d2480e5e6810c48966a29",
+                "sha256:086f7e89ec85a6704db51f68f0dcae432eff9300809723a6e8782c41c2f48e03",
+                "sha256:18ca813fdb17bc1db73fe61b196b05dd1ca2165b884dd5ec5568877cabf9b039",
+                "sha256:19dc39616850342a2a6db70559af55b22955f86667b5f652f40c0e99253d9881",
+                "sha256:2166e770cb98f02ed5ee2b0b569d40db26788e0bf2ec3ae1a0d864ea6f1d8309",
+                "sha256:3a2522b1d9178575acee4adf8fd9f979f9c0449b00b4164bb63c3475ea6528ed",
+                "sha256:3aa773580f85a28ffdf6f862e59cb5a3cc7ef6885121f2de3fca8d6ada4dbf3b",
+                "sha256:3b5deaa3ee7180585a296af33e14c9b18c218d148e735c7accf78130765a47e3",
+                "sha256:407af6d7e46593415f216c7f56ba087a9a42bd6dc2ecb86028760aa45b802bd7",
+                "sha256:4c3c09fb674401f630626310bcaf6cd6285daf0d5e4c26d6e55ca26a2734e39b",
+                "sha256:4c6717962247445b4f9e21c962ea61d2e884fc17df5ddf5e35863b016f8a1f03",
+                "sha256:50446fae5681fc99f87e505d4e77c9407e683ab60c555ec302f9ac9bffa61103",
+                "sha256:5057669b6a66aa9ca118a2a860159f0ee3acf837eda937bdd2a64f3431361a2d",
+                "sha256:5dd90c5438b4f935c9d01fcbad3620253da89d19c1f5fca9158646407ed7df35",
+                "sha256:659c815b5b8e2a55193ede2795c1e2349b8011497310bb936da7d4745652823b",
+                "sha256:69b13fdf12878b10dc6003acc8d0abf3ad93e79813fd5f3812497c1c9fb9be49",
+                "sha256:7a1cb80e35e1ccea3e11a48afe65d38744a0e0bde88795cc56a4d05b6e4f9d70",
+                "sha256:7e6e3c52e6732c219c07bd97fff6c088f8df4dae3b79752ee3a817e6f32e177e",
+                "sha256:7f42a8490c4fe854325504ce7a6e4796b207960dabb2cbafe3c3959cb00d1d7e",
+                "sha256:84156313f258eafff716b2961644a4483a9be44a5d43551d554844d15d4d224e",
+                "sha256:8578d6b8192e4c805e85f187bc530d0f52ba86c39172e61cd51f68fddd648103",
+                "sha256:890167d5091279a27e2505ff0e1fb273f8c48c41d35c5b92adbf4af80e6b2ed6",
+                "sha256:98e10634792ac0e9e7a92a76b4991b44c2325d3e7798270a808407355e7bb0a1",
+                "sha256:9aadff9032e967865f9778485571e93908d27dab21d0fdfdec0ca779bb6f8ad9",
+                "sha256:9f24f383a298a0c0f9b3113b982e21751a8ecde6615494a3f1470eb4a9d70e9e",
+                "sha256:a73021b44813b5c84eda4a3af5826dd72356a900bac9bd9dd1f0f81ee1c22c2f",
+                "sha256:afd96845e12638d2c44d213d4810a08f4dc4a563f9a98204b7428e567014b1cd",
+                "sha256:b73ddf033d8cd4cc9dfed6324b1ad2a89ba52c410ef6877998422fcb9c23e3a8",
+                "sha256:b8f490f5fad1767a1331df1259763b3bad7d7af12a75b950c2843ba319b2415f",
+                "sha256:dbc5cd56fff1a6152ca59445178652756f4e509f672e49ccdf3d79c1043113a4",
+                "sha256:eac8a3499754790187bb00574ab980df13e754777d346f85e0ff6df929bcd964",
+                "sha256:eaed1c65f461a959284649e37b5051224f4db6ebdc84e40b5e65f2986f101a08"
+            ],
+            "index": "pypi",
+            "version": "==2.8.4"
+        },
+        "pycparser": {
+            "hashes": [
+                "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
+            ],
+            "version": "==2.19"
+        },
+        "python-dateutil": {
+            "hashes": [
+                "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
+                "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
+            ],
+            "index": "pypi",
+            "version": "==2.8.1"
+        },
+        "python-dotenv": {
+            "hashes": [
+                "sha256:8429f459fc041237d98c9ff32e1938e7e5535b5ff24388876315a098027c3a57",
+                "sha256:ca9f3debf2262170d6f46571ce4d6ca1add60bb93b69c3a29dcb3d1a00a65c93"
+            ],
+            "index": "pypi",
+            "version": "==0.11.0"
+        },
+        "python-telegram-bot": {
+            "hashes": [
+                "sha256:0a97cbca638f949582b4ee326170d2f8d7f4bf559a4e511132bb2203623e04ad",
+                "sha256:d3cffd020af4094d07c11783f875e5c682072ba7f5bc21ce89ff0222f4e6d742"
+            ],
+            "index": "pypi",
+            "version": "==12.4.2"
+        },
+        "pytz": {
+            "hashes": [
+                "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
+                "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
+            ],
+            "version": "==2019.3"
+        },
+        "requests": {
+            "hashes": [
+                "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
+                "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
+            ],
+            "index": "pypi",
+            "version": "==2.22.0"
+        },
+        "six": {
+            "hashes": [
+                "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
+                "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
+            ],
+            "version": "==1.14.0"
+        },
+        "soupsieve": {
+            "hashes": [
+                "sha256:bdb0d917b03a1369ce964056fc195cfdff8819c40de04695a80bc813c3cfa1f5",
+                "sha256:e2c1c5dee4a1c36bcb790e0fabd5492d874b8ebd4617622c4f6a731701060dda"
+            ],
+            "version": "==1.9.5"
+        },
+        "sqlalchemy": {
+            "hashes": [
+                "sha256:64a7b71846db6423807e96820993fa12a03b89127d278290ca25c0b11ed7b4fb"
+            ],
+            "index": "pypi",
+            "version": "==1.3.13"
+        },
+        "tornado": {
+            "hashes": [
+                "sha256:349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c",
+                "sha256:398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60",
+                "sha256:4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281",
+                "sha256:559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5",
+                "sha256:abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7",
+                "sha256:c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9",
+                "sha256:c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5"
+            ],
+            "version": "==6.0.3"
+        },
+        "tzlocal": {
+            "hashes": [
+                "sha256:11c9f16e0a633b4b60e1eede97d8a46340d042e67b670b290ca526576e039048",
+                "sha256:949b9dd5ba4be17190a80c0268167d7e6c92c62b30026cf9764caf3e308e5590"
+            ],
+            "version": "==2.0.0"
+        },
+        "urllib3": {
+            "hashes": [
+                "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
+                "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
+            ],
+            "version": "==1.25.8"
+        }
+    },
+    "develop": {}
+}

+ 83 - 0
app/__init__.py

@@ -0,0 +1,83 @@
+"""Deep Exploration Planner"""
+
+import os
+import logging
+
+import telegram
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from dotenv import load_dotenv
+from apscheduler.schedulers.background import BackgroundScheduler
+
+
+load_dotenv()
+
+# Telegram
+TELEGRAM_BOT = telegram.Bot(os.environ['TELEGRAM_KEY'])
+
+# database
+ENGINE = create_engine(os.environ["DATABASE_URI"])
+SESSION = sessionmaker(bind=ENGINE)
+
+# scheduler
+SCHEDULER = BackgroundScheduler(
+    daemon=True,
+    job_defaults={'misfire_grace_time': 300},
+)
+SCHEDULER.start()
+
+# get logger
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.INFO)
+SCHEDULER_LOGGER = logging.getLogger('apscheduler')
+SCHEDULER_LOGGER.setLevel(logging.DEBUG)
+
+# create file handler
+FILE_HANDLER = logging.FileHandler('output.log')
+FILE_HANDLER.setLevel(logging.DEBUG)
+
+# create console handler
+STREAM_HANDLER = logging.StreamHandler()
+STREAM_HANDLER.setLevel(logging.INFO)
+
+# create formatter and add it to the handlers
+FORMATTER = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+STREAM_HANDLER.setFormatter(FORMATTER)
+FILE_HANDLER.setFormatter(FORMATTER)
+
+# add the handlers to logger
+LOGGER.addHandler(STREAM_HANDLER)
+LOGGER.addHandler(FILE_HANDLER)
+SCHEDULER_LOGGER.addHandler(STREAM_HANDLER)
+SCHEDULER_LOGGER.addHandler(FILE_HANDLER)
+
+# api
+BASE_URL = os.environ["API_URL"]
+HEADERS = {
+    'Authorization': os.environ["AUTHORIZATION"]
+}
+
+RESOURCE_IDS = {
+    0: 'gold',
+    3: 'oil',
+    4: 'ore',
+    11: 'uranium',
+    15: 'diamond',
+}
+
+RESOURCE_NAMES = {
+    'gold': 0,
+    'oil': 3,
+    'ore': 4,
+    'uranium': 11,
+    'diamond': 15,
+    'diamonds': 15,
+}
+
+RESOURCE_MAX = {
+    0: 2500,
+    3: 600,
+    4: 500,
+    11: 60,
+    15: 75,
+}

+ 33 - 0
app/__main__.py

@@ -0,0 +1,33 @@
+"""Main app"""
+
+import time
+import sys
+
+from app import SCHEDULER, LOGGER, RESOURCE_NAMES, jobs
+
+
+if __name__ == '__main__':
+    jobs.check_deep_exploration(4002)
+    sys.exit()
+
+    add_telegram_update_job(2788, '@vn_resources', 'gold')
+    add_telegram_update_job(2788, '@vn_uranium_resources', 'uranium')
+
+    try:
+        while True:
+            time.sleep(100)
+    except KeyboardInterrupt:
+        LOGGER.info('Exiting application')
+        SCHEDULER.shutdown()
+        sys.exit()
+
+def add_telegram_update_job(state_id, telegram_id, resource_type):
+    """Add telegram update job"""
+    SCHEDULER.add_job(
+        jobs.send_telegram_update,
+        'cron',
+        args=[state_id, telegram_id, resource_type],
+        id='{}_send_telegram_update_{}'.format(state_id, resource_type),
+        replace_existing=True,
+        minute='5'
+    )

+ 88 - 0
app/api.py

@@ -0,0 +1,88 @@
+"""Main application"""
+
+import re
+
+import requests
+from bs4 import BeautifulSoup
+from dateutil import parser
+
+from app import BASE_URL, HEADERS, LOGGER, RESOURCE_IDS, RESOURCE_NAMES 
+
+
+def download_deep_explorations(region_id):
+    """Download the deep explorations list"""
+    # return read_deep_explorations()
+    response = requests.get(
+        '{}listed/upgrades/{}'.format(BASE_URL, region_id),
+        headers=HEADERS
+    )
+    return parse_deep_explorations(response.text)
+
+def read_deep_explorations():
+    """Read deep_exploration file"""
+    with open('deep_explorations.html') as file:
+        return parse_deep_explorations(file)
+
+def parse_deep_explorations(html):
+    """Read the deep_explorations left"""
+    soup = BeautifulSoup(html, 'html.parser')
+    deep_explorations_tree = soup.find_all(class_='list_link')
+
+    deep_explorations = {}
+    for deep_exploration_tree in deep_explorations_tree:
+        deep_exploration_id = int(deep_exploration_tree['user'])
+        columns = deep_exploration_tree.find_all('td')
+        deep_explorations[deep_exploration_id] = {
+            'resource_type': RESOURCE_NAMES[columns[1].text.replace(' resources', '').lower()],
+            'until_date_time': parser.parse(columns[2].string),
+        }
+    return deep_explorations
+
+def deep_explorate(state_id, capital_id, deep_exploration_id, amount, alt):
+    """Main function"""
+    # Check location
+    # response = requests.get(
+    #     '{}main/content'.format(BASE_URL),
+    #     headers=HEADERS
+    # )
+    # soup = BeautifulSoup(response.text, 'html.parser')
+    # state_div = soup.find_all('div', {'class': 'index_case_50'})[1]
+    # action = state_div.findChild()['action']
+    # current_state_id = int(re.sub('.*/', '', action))
+    # LOGGER.info('Current state %s', current_state_id)
+
+    params = {}
+    # if current_state_id != state_id:
+    #     params['alt'] = True
+    if alt:
+        params['alt'] = True
+
+    json_data = {
+        'tmp_gov': '{}_{}'.format(deep_exploration_id, amount)
+    }
+
+    requests.post(
+        '{}parliament/donew/34/{}_{}/{}'.format(BASE_URL, deep_exploration_id, amount, capital_id),
+        headers=HEADERS,
+        params=params,
+        json=json_data
+    )
+
+    response = requests.get(
+        '{}parliament/index/{}'.format(BASE_URL, capital_id),
+        headers=HEADERS
+    )
+    soup = BeautifulSoup(response.text, 'html.parser')
+    active_laws = soup.find('div', {'id': 'parliament_active_laws'})
+    deep_exploration_name = RESOURCE_IDS[deep_exploration_id]
+    exploration_laws = active_laws.findAll(text='Deep exploration,')
+    LOGGER.info('Resources exploration: state, %s deep_explorations', deep_exploration_name)
+    for exploration_law in exploration_laws:
+        action = exploration_law.parent.parent['action']
+        action = action.replace('law', 'votelaw')
+        result = requests.post(
+            '{}{}/pro'.format(BASE_URL, action),
+            params=params,
+            headers=HEADERS
+        )
+        LOGGER.info('Response: %s', result.text)

+ 105 - 0
app/app.py

@@ -0,0 +1,105 @@
+"""General function module"""
+
+import random
+from datetime import datetime, timedelta
+
+from telegram import ParseMode
+
+from app import LOGGER, SCHEDULER, TELEGRAM_BOT, RESOURCE_NAMES, jobs, api, database
+
+
+def check_deep_exploration(region_id):
+    """Check resources and refill if necessary"""
+    deep_explorations = api.download_deep_explorations(region_id)
+    database.save_deep_explorations(region_id, deep_explorations)
+    deep_exploration = database.get_active_deep_exploration(region_id)
+    print(deep_exploration)
+    return
+    if do_refill and need_refill(regions, refill_percentage):
+        max_seconds = max_refill_seconds(regions, refill_percentage, 900)
+        random_seconds = random.randint(0, max_seconds)
+        random_time_delta = timedelta(seconds=random_seconds)
+        scheduled_date = datetime.now() + random_time_delta
+        job_id = 'refill_{}_{}'.format(capital_id, resource_id)
+        LOGGER.info(
+            'Refil resource %s at %s (%s minutes)',
+            resource_id,
+            scheduled_date,
+            round(random_time_delta.seconds / 60)
+        )
+        job = SCHEDULER.get_job(job_id)
+        if not job:
+            SCHEDULER.add_job(
+                jobs.refill_resource,
+                'date',
+                args=[state_id, capital_id, resource_id, alt],
+                id=job_id,
+                run_date=scheduled_date
+            )
+
+def print_resources(regions):
+    """print resources"""
+    if regions:
+        print('region                        expl max   D left    c %    t %')
+        for region in regions.values():
+            region['explored_percentage'] = 100 / region['maximum'] * region['explored']
+            region['total_left'] = region['explored'] + region['limit_left']
+            region['total_percentage'] = 100 / 2500 * region['total_left']
+            print('{:25}: {:7.2f}{:4}{:4}{:5}{:7.2f}{:7.2f}'.format(
+                region['region_name'],
+                region['explored'],
+                region['maximum'],
+                region['deep_exploration'],
+                region['limit_left'],
+                region['explored_percentage'],
+                region['total_percentage'],
+            ))
+    else:
+        LOGGER.error('no region to print data')
+
+def need_refill(regions, limit):
+    """Check if refill is needed"""
+    for region in regions.values():
+        percentage = 100 / region['maximum'] * region['explored']
+        if percentage < limit and region['limit_left']:
+            return True
+    return False
+
+def max_refill_seconds(regions, limit, max_time):
+    """Give random seconds for next refill"""
+    lowest_percentage = limit
+    for region in regions.values():
+        percentage = 100 / region['maximum'] * region['explored']
+        if percentage < lowest_percentage:
+            lowest_percentage = percentage
+    return int(max_time / limit * lowest_percentage)
+
+def send_telegram_update(state_id, group_id, resource_name):
+    """Send resource update to telegram"""
+    resource_id = RESOURCE_NAMES[resource_name]
+    # date = datetime.now()
+    date = datetime.today().replace(hour=18, minute=5) - timedelta(1)
+    print(date)
+    message = database.get_work_percentage(state_id, resource_id, date, 1, 1)
+    date = datetime.today().replace(hour=19, minute=5) - timedelta(1)
+    print(date)
+    message = database.get_work_percentage(state_id, resource_id, date, 1, 1)
+    date = datetime.today().replace(hour=20, minute=5) - timedelta(1)
+    print(date)
+    message = database.get_work_percentage(state_id, resource_id, date, 1, 1)
+    date = datetime.today().replace(hour=21, minute=5) - timedelta(1)
+    print(date)
+    message = database.get_work_percentage(state_id, resource_id, date, 1, 1)
+    date = datetime.today().replace(hour=22, minute=5) - timedelta(1)
+    print(date)
+    message = database.get_work_percentage(state_id, resource_id, date, 1, 1)
+    return
+    if message:
+        print(message)
+        TELEGRAM_BOT.sendMessage(
+            chat_id=group_id,
+            text='```\n{}```'.format(message),
+            parse_mode=ParseMode.MARKDOWN
+        )
+    else:
+        LOGGER.error('no data for Telegram message')

+ 47 - 0
app/database.py

@@ -0,0 +1,47 @@
+"""Main application"""
+
+from datetime import datetime, timedelta, timezone
+
+from sqlalchemy.orm import joinedload
+
+from app import SESSION, RESOURCE_MAX
+from app.models import Region, DeepExploration
+
+
+def save_deep_explorations(region_id, deep_explorations):
+    """Save resources to database"""
+    session = SESSION()
+    for deep_exploration_id, deep_exploration_dict in deep_explorations.items():
+        deep_exploration = session.query(DeepExploration).get(deep_exploration_id)
+        if deep_exploration:
+            break
+        deep_exploration = DeepExploration()
+        deep_exploration.id = deep_exploration_id
+        region = session.query(Region).get(region_id)
+        if not region:
+            region = save_region(session, region_id)
+        deep_exploration.region_id = region_id
+        deep_exploration.resource_type = deep_exploration_dict['resource_type']
+        deep_exploration.until_date_time = deep_exploration_dict['until_date_time']
+        session.add(deep_exploration)
+    session.commit()
+    session.close()
+
+def get_active_deep_exploration(region_id):
+    """Get active deep exploration in a region"""
+    session = SESSION()
+    deep_exploration = session.query(DeepExploration) \
+        .filter(DeepExploration.region_id == region_id) \
+        .filter(DeepExploration.until_date_time >= datetime.now()) \
+        .first()
+    session.close()
+    return deep_exploration
+
+
+def save_region(session, region_id):
+    """Save player to database"""
+    region = Region()
+    region.id = region_id
+    region.name = 'UNKNOWN'
+    session.add(region)
+    return region

+ 8 - 0
app/jobs.py

@@ -0,0 +1,8 @@
+"""Jobs for scheduler module"""
+
+from app import app, api
+
+
+def check_deep_exploration(region_id):
+    """Check resources and refill if necessary"""
+    app.check_deep_exploration(region_id)

+ 74 - 0
app/models.py

@@ -0,0 +1,74 @@
+"""Database models"""
+
+import datetime
+
+from sqlalchemy import Column, ForeignKey, Integer, String, SmallInteger, DateTime
+from sqlalchemy.orm import relationship, backref
+from sqlalchemy.ext.declarative import declarative_base
+
+
+Base = declarative_base()
+
+class State(Base):
+    """Model for state"""
+    __tablename__ = 'state'
+    id = Column(Integer, primary_key=True)
+    name = Column(String)
+
+class Region(Base):
+    """Model for region"""
+    __tablename__ = 'region'
+    id = Column(Integer, primary_key=True)
+    name = Column(String)
+    gold_limit = Column(SmallInteger)
+    oil_limit = Column(SmallInteger)
+    ore_limit = Column(SmallInteger)
+    uranium_limit = Column(SmallInteger)
+    diamond_limit = Column(SmallInteger)
+
+
+class StateRegion(Base):
+    """Model for state region"""
+    __tablename__ = 'state_region'
+    state_id = Column(Integer, ForeignKey('state.id'), primary_key=True)
+    region_id = Column(Integer, ForeignKey('region.id'), primary_key=True)
+    from_date_time = Column(DateTime, primary_key=True)
+    until_date_time = Column(DateTime)
+
+
+class DeepExploration(Base):
+    """Model for deep exploration"""
+    __tablename__ = 'deep_exploration'
+    id = Column(Integer, primary_key=True)
+    until_date_time = Column(DateTime)
+    points = Column(Integer)
+    resource_type = Column(SmallInteger)
+    region_id = Column(Integer, ForeignKey('region.id'))
+    region = relationship(
+        'Region',
+        backref=backref('deep_explorations', lazy='dynamic')
+    )
+
+
+class DeepExplorationOrder(Base):
+    """Model for deep exploration order"""
+    __tablename__ = 'deep_exploration_order'
+    id = Column(Integer, primary_key=True)
+    resource_type = Column(SmallInteger, nullable=False)
+    order_type = Column(SmallInteger, nullable=False)
+    amount = Column(Integer)
+    from_date_time = Column(DateTime)
+    until_date_time = Column(DateTime)
+
+    order_types = {
+        0: 'max',
+        1: 'fixed',
+        2: 'percentage',
+        3: 'auto',
+    }
+
+    def order_type_name(self):
+        """Type name"""
+        if self.order_type in self.order_types:
+            return self.order_types[self.type]
+        return 'unknown'

+ 4 - 0
example.env

@@ -0,0 +1,4 @@
+AUTHORIZATION=PLACEHOLDER
+DATABASE_URI='postgresql://hvs@localhost/hvs'
+API_URL='http://localhost:5000/api/request/'
+TELEGRAM_KEY=PLACEHOLDER