From 4c810f003bf6af06c07742ecfb4a4eeb8233ed67 Mon Sep 17 00:00:00 2001 From: 9FS Date: Sun, 12 May 2024 11:50:40 +0000 Subject: [PATCH] updated dependencies, modified config loading --- .dockerignore | 1 + .gitignore | 1 + config/discord_bot_channel_names.txt | 3 - poetry.lock | 86 ++++++++++++++----------- pyproject.toml | 18 +++--- requirements.txt | 54 ++++++++-------- src/init_DB.py | 2 +- src/main.py | 96 +++++++++++++++------------- src/process_METAR_TAF.py | 2 +- 9 files changed, 137 insertions(+), 126 deletions(-) delete mode 100644 config/discord_bot_channel_names.txt diff --git a/.dockerignore b/.dockerignore index 456d26e..94a0739 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,5 +10,6 @@ database/ doc_templates/ log/ +.env docker-image.tar tests/test.py \ No newline at end of file diff --git a/.gitignore b/.gitignore index 456d26e..94a0739 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,6 @@ database/ doc_templates/ log/ +.env docker-image.tar tests/test.py \ No newline at end of file diff --git a/config/discord_bot_channel_names.txt b/config/discord_bot_channel_names.txt deleted file mode 100644 index 3ad9d5e..0000000 --- a/config/discord_bot_channel_names.txt +++ /dev/null @@ -1,3 +0,0 @@ -bots -botspam -metar \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index b9d7809..7024e3f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -371,13 +371,13 @@ files = [ [[package]] name = "hypothesis" -version = "6.100.2" +version = "6.100.7" description = "A library for property-based testing" optional = false python-versions = ">=3.8" files = [ - {file = "hypothesis-6.100.2-py3-none-any.whl", hash = "sha256:4ae5918f5a47f979e53fe9e8be4ba6edf17d7a133c13542938d8a6e18f890a2a"}, - {file = "hypothesis-6.100.2.tar.gz", hash = "sha256:7d68e45d371cee5b9d2236516341aca57fb7f2cfabef6cb9e5b3a7ec62219cf2"}, + {file = "hypothesis-6.100.7-py3-none-any.whl", hash = "sha256:45d31834346f911c7a3184addb2f244eaea649298c531ec4a1eebe99225e82bb"}, + {file = "hypothesis-6.100.7.tar.gz", hash = "sha256:64106ba1358f6e5205d89696d3f2b033c6c4f3ec2252a128abae6466d3f4ef47"}, ] [package.dependencies] @@ -441,69 +441,69 @@ testing = ["bson", "ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", [[package]] name = "kfsconfig" -version = "1.2.0" +version = "2.1.5" description = "" optional = false -python-versions = "<4.0.0,>=3.11.0" +python-versions = "<4.0.0,>=3.12.0" files = [ - {file = "kfsconfig-1.2.0-py3-none-any.whl", hash = "sha256:ab1bba467eb0597ed11b3fb6f8a58aef6bd168dc4a779724768d6c724b1f057d"}, - {file = "kfsconfig-1.2.0.tar.gz", hash = "sha256:3d4c2afc3c94afa158b4f594764813ff55fde3227e0f669b0705d3a4fc1168d9"}, + {file = "kfsconfig-2.1.5-py3-none-any.whl", hash = "sha256:1b2334636df62fc23d098b18a9fa41c7e3c40da45b169524925222579361138f"}, + {file = "kfsconfig-2.1.5.tar.gz", hash = "sha256:f2d2bb14ea9940946882783b367e2af0e8cbd8a62a71c7fed22207f51d24dc40"}, ] [package.dependencies] -KFSlog = ">=1.0.0,<2.0.0" +kfslog = ">=2.0.0,<3.0.0" [[package]] name = "kfsconvert-to-si" -version = "1.0.1" +version = "1.0.2" description = "" optional = false -python-versions = ">=3.11.0,<4.0.0" +python-versions = "<4.0.0,>=3.12.0" files = [ - {file = "kfsconvert_to_si-1.0.1-py3-none-any.whl", hash = "sha256:3f82287f4d2ccb591080325b2405ac9fe5300f195a168cd5b26f978349986066"}, - {file = "kfsconvert_to_si-1.0.1.tar.gz", hash = "sha256:32d32f0d3117e10dbf51db86f89e7eb587625b5a4721b28819bca6507a4cad8a"}, + {file = "kfsconvert_to_si-1.0.2-py3-none-any.whl", hash = "sha256:f55b6ba4f78036482ff021e5939513f9435dec714005b6f6baaeb82c8729ddb8"}, + {file = "kfsconvert_to_si-1.0.2.tar.gz", hash = "sha256:018d64de878334cc153b91562cd7478415cca6fe8aff9beea86d0aae6c7e952b"}, ] [[package]] name = "kfsfstr" -version = "1.1.0" +version = "1.1.2" description = "" optional = false -python-versions = ">=3.11.0,<4.0.0" +python-versions = "<4.0.0,>=3.12.0" files = [ - {file = "kfsfstr-1.1.0-py3-none-any.whl", hash = "sha256:ee96067a97df0607a6d547ebb5b9228a8d61273451451f1e276d4c8b318db836"}, - {file = "kfsfstr-1.1.0.tar.gz", hash = "sha256:5db73b876efb481ca2d1d26ac129a35d5524e1fe3d275aaa3c513a396cb04621"}, + {file = "kfsfstr-1.1.2-py3-none-any.whl", hash = "sha256:effb79562013d3a84c878ec94252d52f5af4d9eb02183073b04fc584e5a958eb"}, + {file = "kfsfstr-1.1.2.tar.gz", hash = "sha256:8d4f2d5fa2240f58276f9048e0a6bafcaa3b9e4742db159631ee6d85fab23a2c"}, ] [package.dependencies] -colorama = ">=0.4.6,<0.5.0" -kfslog = ">=1.0.0,<2.0.0" +colorama = ">=0.4.0,<0.5.0" kfsmath = ">=1.0.0,<2.0.0" [[package]] name = "kfslog" -version = "1.0.1" +version = "2.0.1" description = "" optional = false -python-versions = ">=3.11.0,<4.0.0" +python-versions = "<4.0.0,>=3.12.0" files = [ - {file = "kfslog-1.0.1-py3-none-any.whl", hash = "sha256:e9c5b59282c453cc9b38c251220281fba28fb955c0e9fadf920bb29c30bd6cd9"}, - {file = "kfslog-1.0.1.tar.gz", hash = "sha256:db1237d6704d09e7adc7ec798df01a281fcc5282bc3b81104d87b9607a42f03f"}, + {file = "kfslog-2.0.1-py3-none-any.whl", hash = "sha256:142a934a01eaeca786b446c16b725bf0388ff3087184ceefe807dfe82a4cecc5"}, + {file = "kfslog-2.0.1.tar.gz", hash = "sha256:df2f0f852a92a4db8316c4d4f7208fc9d6cd9b68e4b44111887bc602d4328500"}, ] [package.dependencies] colorama = ">=0.4.6,<0.5.0" -KFSfstr = ">=1.0.0,<2.0.0" +kfsfstr = ">=1.1.0,<2.0.0" +result = ">=0.16.0,<0.17.0" [[package]] name = "kfsmath" -version = "1.0.1" +version = "1.0.2" description = "" optional = false -python-versions = ">=3.11.0,<4.0.0" +python-versions = "<4.0.0,>=3.12.0" files = [ - {file = "kfsmath-1.0.1-py3-none-any.whl", hash = "sha256:c5cccef537e10293cd1ac6f0e952fb31e97f5d60d133e43a6a34a2f3122457e8"}, - {file = "kfsmath-1.0.1.tar.gz", hash = "sha256:bd28030eeffcad0880ea7955ca7e4fb94f23496714a4f54a75867268eba25e55"}, + {file = "kfsmath-1.0.2-py3-none-any.whl", hash = "sha256:03f4fefb420e014faa822e6af5a231023616a8d3d863138fd0f2b8bcbee68b4e"}, + {file = "kfsmath-1.0.2.tar.gz", hash = "sha256:f2e74102132500c6738c918d0029cd3810c3473cec1ebaebe2df082a66fcde7a"}, ] [[package]] @@ -698,10 +698,7 @@ files = [ ] [package.dependencies] -numpy = [ - {version = ">=1.23.2", markers = "python_version == \"3.11\""}, - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, -] +numpy = {version = ">=1.26.0", markers = "python_version >= \"3.12\""} python-dateutil = ">=2.8.2" pytz = ">=2020.1" tzdata = ">=2022.7" @@ -748,23 +745,23 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pytest" -version = "7.4.4" +version = "8.2.0" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, + {file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"}, + {file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" +pluggy = ">=1.5,<2.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "python-dateutil" @@ -812,6 +809,17 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "result" +version = "0.16.1" +description = "A Rust-like result type for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "result-0.16.1-py3-none-any.whl", hash = "sha256:47598bb9bde342251f5b0f49e3637cd30dc144c086b29dc58fff458477650669"}, + {file = "result-0.16.1.tar.gz", hash = "sha256:379f0233cdf8cf157588c77bb2ab1ac367431d9bf6e8456a336b026d528cee0d"}, +] + [[package]] name = "six" version = "1.16.0" @@ -967,5 +975,5 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" -python-versions = "^3.11.0" -content-hash = "c36c63615b02be44899243d77441208b2d8a465a43ed9b68adc9d911f45e232c" +python-versions = "^3.12.0" +content-hash = "7b9b6401bdea6b3afdd7aefc2229ef00ed5d3ea2aa27d334f75cd7d8a7d967d3" diff --git a/pyproject.toml b/pyproject.toml index 28e95a3..c9eec54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,22 +6,22 @@ name = "" package-mode = false # this is an application, not library readme = "readme.md" repository = "https://github.com/9-FS/2022-01-26-Metric-METAR-for-Discord" -version = "1.1.5" +version = "1.2.0" [tool.poetry.dependencies] -discord = "^2.3.2" -jsonpickle = "^3.0.2" -kfsconfig = "^1.0.0" +discord = "^2.3.0" +jsonpickle = "^3.0.0" +kfsconfig = "^2.0.0" kfsconvert-to-si = "^1.0.0" kfsfstr = "^1.0.0" -kfslog = "^1.0.0" -pandas = "^2.1.0" -python = "^3.11.0" +kfslog = "^2.0.0" +pandas = "^2.2.0" +python = "^3.12.0" requests = "^2.31.0" [tool.poetry.group.dev.dependencies] -hypothesis = "^6.87.0" -pytest = "^7.4.2" +hypothesis = "^6.0.0" +pytest = "^8.0.0" [build-system] build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt index 401111c..89a4045 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,27 +1,27 @@ -aiohttp==3.8.6 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -aiosignal==1.3.1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -async-timeout==4.0.3 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -attrs==23.1.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -certifi==2023.7.22 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -charset-normalizer==3.3.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -colorama==0.4.6 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -discord-py==2.3.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -discord==2.3.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -frozenlist==1.4.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -idna==3.4 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -jsonpickle==3.0.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -kfsconfig==1.0.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -kfsconvert-to-si==1.0.1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -kfsfstr==1.1.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -kfslog==1.0.1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -kfsmath==1.0.1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -multidict==6.0.4 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -numpy==1.26.1 ; python_version == "3.11" -pandas==2.1.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -python-dateutil==2.8.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -pytz==2023.3.post1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -requests==2.31.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -six==1.16.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -tzdata==2023.3 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -urllib3==2.0.7 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" -yarl==1.9.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0" +aiohttp==3.9.5 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +aiosignal==1.3.1 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +attrs==23.2.0 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +certifi==2024.2.2 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +charset-normalizer==3.3.2 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +colorama==0.4.6 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +discord-py==2.3.2 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +discord==2.3.2 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +frozenlist==1.4.1 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +idna==3.7 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +jsonpickle==3.0.4 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +kfsconfig==2.1.5 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +kfsconvert-to-si==1.0.2 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +kfsfstr==1.1.2 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +kfslog==2.0.1 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +kfsmath==1.0.2 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +multidict==6.0.5 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +numpy==1.26.4 ; python_version >= "3.12" and python_full_version < "4.0.0" +pandas==2.2.2 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +python-dateutil==2.9.0.post0 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +pytz==2024.1 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +requests==2.31.0 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +result==0.16.1 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +six==1.16.0 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +tzdata==2024.1 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +urllib3==2.2.1 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" +yarl==1.9.4 ; python_full_version >= "3.12.0" and python_full_version < "4.0.0" diff --git a/src/init_DB.py b/src/init_DB.py index a7a99fe..8fef237 100644 --- a/src/init_DB.py +++ b/src/init_DB.py @@ -10,7 +10,7 @@ from DB_Type import DB_Type -def init_DB(DB_type: DB_Type, DB: pandas.DataFrame, now_DT: dt.datetime, DOWNLOAD_TIMEOUT: int) -> pandas.DataFrame: +def init_DB(DB_type: DB_Type, DB: pandas.DataFrame, now_DT: dt.datetime, DOWNLOAD_TIMEOUT: float) -> pandas.DataFrame: DB_filenames: list[str] # databases existing filenames DB_filepath: str # filepath to database, date can be in past DB_TODAY_FILEPATH: str=f"./database/{now_DT.strftime('%Y-%m-%d')} {DB_type.name} DB.csv" # filepath to database diff --git a/src/main.py b/src/main.py index c1f3f98..277e2e9 100644 --- a/src/main.py +++ b/src/main.py @@ -13,6 +13,7 @@ import pandas import re import requests +import typing from DB_Type import DB_Type from Doc_Type import Doc_Type from init_DB import init_DB @@ -28,47 +29,52 @@ # navaid_DB: pandas.DataFrame =pandas.DataFrame() # navaid database for information command RWY_DB: pandas.DataFrame =pandas.DataFrame() # runway database for cross wind components and information command servers: list[Server] # all variables for 1 server instance -SERVERS_FILENAME: str="./config/servers.json" # save filename for all servers, so subscription is remembered beyond restarts -@KFSlog.timeit_async +@KFSlog.timeit_async() async def main(DEBUG: bool) -> None: global servers # keep over runtime whole, but read-only in sub-functions - DOWNLOAD_TIMEOUT: int=50 # METAR and TAF download timeouts [s] - COMMANDS_ALLOWED: tuple=( # commands allowed + COMMANDS_ALLOWED: tuple[str, ...]=\ + ( # commands allowed "^(?P[0-9A-Z]{4})$", "^(?P[0-9A-Z]{4}) TAF$", - # "^(?P[0-9A-Z]{4}) INFO$" # TODO ) - discord_bot: discord.Client # discord client instance - discord_bot_channel_names: list[str] # bot channel names - discord_bot_token: str # discord bot token - intents: discord.Intents # client permissions - if logging.root.level<=logging.DEBUG: # if level debug or lower: - UPDATE_FREQUENCY: float=200e-3 # update subscription with 200mHz (every 5s) - else: - UPDATE_FREQUENCY: float=10e-3 # but usually update subscription with 10mHz (every 100s) + config: dict[str, typing.Any] # config file + CONFIG_DEFAULT: dict[str, typing.Any]=\ + { + "DOWNLOAD_TIMEOUT": 50, # METAR and TAF download timeouts [s] + "SERVERS_FILENAME": "./config/servers.json", # save filename for all servers, so subscription is remembered beyond restarts + "UPDATE_FREQUENCY": 10e-3, # update subscription with 10mHz (every 100s) + } + env: dict[str, str] # environment variables + ENV_DEFAULT: dict[str, str]=\ + { + "DISCORD_BOT_CHANNEL_NAME": "metar", # bot channel names + "DISCORD_BOT_TOKEN": "", # discord bot token + } + discord_bot: discord.Client # discord client instance + intents: discord.Intents # client permissions try: - discord_bot_channel_names=[bot_channel_name for bot_channel_name in KFSconfig.load_config("./config/discord_bot_channel_names.txt", "bots\nbotspam\nmetar").split("\n") if bot_channel_name!=""] # load bot channel names, remove empty lines - discord_bot_token=KFSconfig.load_config("./config/discord_bot.token") # load discord bot token - except FileNotFoundError: + config=KFSconfig.load_config(env=False, config_filepaths=["./config/config.json"], config_default=CONFIG_DEFAULT) # load configuration + env =KFSconfig.load_config( config_filepaths=["./.env"], config_default=ENV_DEFAULT) # load environment variables + except ValueError: return - intents=discord.Intents.default() # standard permissions - intents.message_content=True # in addition with message contents - discord_bot=discord.Client(intents=intents) # create client instance + intents=discord.Intents.default() # standard permissions + intents.message_content=True # in addition with message contents + discord_bot=discord.Client(intents=intents) # create client instance - logging.info(f"Restoring server states from \"{SERVERS_FILENAME}\"...") + logging.info(f"Restoring server states from \"{config["SERVERS_FILENAME"]}\"...") try: - with open(SERVERS_FILENAME, "rt") as servers_file: # try to restore server states - servers=jsonpickle.decode(servers_file.read()) # type:ignore - except FileNotFoundError as e: # if file not created yet: no server states available yet - logging.warning(f"\rRestoring server states from \"{SERVERS_FILENAME}\" failed with {KFSfstr.full_class_name(e)}.") + with open(config["SERVERS_FILENAME"], "rt") as servers_file: # try to restore server states + servers=jsonpickle.decode(servers_file.read()) # type:ignore + except FileNotFoundError as e: # if file not created yet: no server states available yet + logging.warning(f"\rRestoring server states from \"{config["SERVERS_FILENAME"]}\" failed with {KFSfstr.full_class_name(e)}.") servers=[] else: - logging.info(f"\rRestored server states from \"{SERVERS_FILENAME}\".") + logging.info(f"\rRestored server states from \"{config["SERVERS_FILENAME"]}\".") @discord_bot.event @@ -89,8 +95,6 @@ async def on_message(message: discord.Message|Server): # either discord.Message - \"{ICAO code}\" requests the current METAR and changes the subscribed station. - \"{ICAO code} TAF\" requests the current METAR and TAF and changes the subscribed station. - - - \"{ICAO code} INFO\" requests information and does not change the subscribed station. TODO: CURRENTLY UNUSABLE. """ global servers @@ -125,18 +129,18 @@ def __enter__(self): logging.critical("message: discord.Message|Server has invalid type \"{type(message)}\".") raise RuntimeError(f"Error in {main.__name__}{inspect.signature(main)}: message: discord.Message|Server has invalid type \"{type(message)}\".") return self - def __exit__(self, exc_type, exc_value, exc_traceback): # upon exit, force print by default + def __exit__(self, exc_type, exc_value, exc_traceback): # upon exit, force print by default self.server.force_print=True - if self.save_server_states==True: # save server states? only if command was valid - logging.info(f"Saving server states in \"{SERVERS_FILENAME}\"...") - with open(SERVERS_FILENAME, "wt") as servers_file: # save servers state so subscription is remembered beyond restarts - servers_file.write(jsonpickle.encode(servers, indent=4)) # recursively convert everything in the list to a dict so json can save it # type:ignore - logging.info(f"\rSaved server states in \"{SERVERS_FILENAME}\".") + if self.save_server_states==True: # save server states? only if command was valid + logging.info(f"Saving server states in \"{config["SERVERS_FILENAME"]}\"...") + with open(config["SERVERS_FILENAME"], "wt") as servers_file: # save servers state so subscription is remembered beyond restarts + servers_file.write(jsonpickle.encode(servers, indent=4)) # recursively convert everything in the list to a dict so json can save it # type:ignore + logging.info(f"\rSaved server states in \"{config["SERVERS_FILENAME"]}\".") return with ContextManager() as context: # upon exit, force print by default server=context.server # get current server from context, SHALLOW COPY which is desired if isinstance(message, discord.message.Message): - if message.author==discord_bot.user or message.channel.name not in discord_bot_channel_names: # if message from bot itself or outside dedicated bot channel: do nothing # type:ignore + if message.author==discord_bot.user or message.channel.name!=env["DISCORD_BOT_CHANNEL_NAME"]: # if message from bot itself or outside dedicated bot channel: do nothing # type:ignore return channel_id=message.channel.id # save active channel id command=message.content.upper() # save active command @@ -202,7 +206,7 @@ def __exit__(self, exc_type, exc_value, exc_traceback): # up station.elev=None else: # if elevation available: station.elev=aerodrome.at[0, "elevation_ft"]*KFSconvert_to_SI.LENGTH["ft"] # save elevation [m] - logging.info(f"Elevation: {KFSfstr.notation_abs(station.elev, 0, round_static=True)}m") + logging.info(f"Elevation: {KFSfstr.notation_abs(station.elev, 0, round_static=True)}m") # type:ignore # information command # if INFO_command==True: # if information command: execute that, then return without downloading METAR, TAF etc. @@ -212,13 +216,13 @@ def __exit__(self, exc_type, exc_value, exc_traceback): # up # download and convert METAR and TAF try: - METAR_o, METAR=process_METAR_TAF(Doc_Type.METAR, station, RWY_DB, now_DT, server, DOWNLOAD_TIMEOUT) + METAR_o, METAR=process_METAR_TAF(Doc_Type.METAR, station, RWY_DB, now_DT, server, config["DOWNLOAD_TIMEOUT"]) except (requests.ConnectTimeout, requests.ConnectionError, ValueError): # if unsuccessful: abort return if append_TAF==True: # if append TAF: download and process TAF try: - TAF_o, TAF=process_METAR_TAF(Doc_Type.TAF, station, RWY_DB, now_DT, server, DOWNLOAD_TIMEOUT) + TAF_o, TAF=process_METAR_TAF(Doc_Type.TAF, station, RWY_DB, now_DT, server, config["DOWNLOAD_TIMEOUT"]) except (requests.ConnectTimeout, requests.ConnectionError, ValueError): # if unsuccessful: just no TAF logging.warning(f"Continuing without TAF...") append_TAF=False @@ -325,16 +329,16 @@ def __exit__(self, exc_type, exc_value, exc_traceback): # up try: await discord_bot.get_channel(channel_id).send(message_send) # send message to discord # type:ignore - except AttributeError as e: # get_channel already returned None, bot has probably been removed from server + except AttributeError as e: # get_channel already returned None, bot has probably been removed from server logging.error(f"Sending message to discord failed with {KFSfstr.full_class_name(e)}. Assuming bot has been removed from server.") logging.info(f"Deleting server {server.name} ({server.id}) from servers list...") servers=[s for s in servers if s.id!=server.id] # delete from list logging.info(f"\rDeleted server {server.name} ({server.id}) from servers list.") return - except discord.errors.Forbidden as e: # bot has no permission to send message + except discord.errors.Forbidden as e: # bot has no permission to send message logging.error(f"Sending message to discord failed with {KFSfstr.full_class_name(e)}. Bot has no permission to send message.") return - except discord.errors.DiscordServerError as e: # send failed + except discord.errors.DiscordServerError as e: # send failed logging.error(f"Sending message to discord failed with {KFSfstr.full_class_name(e)}. Error message: {e.args}") return if append_TAF==False: @@ -347,7 +351,7 @@ def __exit__(self, exc_type, exc_value, exc_traceback): # up return - @discord.ext.tasks.loop(seconds=1/UPDATE_FREQUENCY) # every 100s look for updates + @discord.ext.tasks.loop(seconds=1/config["UPDATE_FREQUENCY"]) # every 100s look for updates async def station_subscription(): """ Executed every 100s for subscription logic. @@ -361,10 +365,10 @@ async def station_subscription(): # refresh databases now_DT=dt.datetime.now(dt.timezone.utc) - aerodrome_DB=init_DB(DB_Type.aerodrome, aerodrome_DB, now_DT, DOWNLOAD_TIMEOUT) - country_DB =init_DB(DB_Type.country, country_DB, now_DT, DOWNLOAD_TIMEOUT) - # navaid_DB =init_DB(DB_Type.navaid, navaid_DB, now_DT, DOWNLOAD_TIMEOUT) - RWY_DB =init_DB(DB_Type.runway, RWY_DB, now_DT, DOWNLOAD_TIMEOUT) + aerodrome_DB=init_DB(DB_Type.aerodrome, aerodrome_DB, now_DT, config["DOWNLOAD_TIMEOUT"]) + country_DB =init_DB(DB_Type.country, country_DB, now_DT, config["DOWNLOAD_TIMEOUT"]) + # navaid_DB =init_DB(DB_Type.navaid, navaid_DB, now_DT, config["DOWNLOAD_TIMEOUT"]) + RWY_DB =init_DB(DB_Type.runway, RWY_DB, now_DT, config["DOWNLOAD_TIMEOUT"]) for server in servers: @@ -376,7 +380,7 @@ async def station_subscription(): while True: logging.info("Starting discord client...") try: - await discord_bot.start(discord_bot_token) # start discord client now + await discord_bot.start(env["DISCORD_BOT_TOKEN"]) # start discord client now except aiohttp.client_exceptions.ClientConnectorError: # if temporary internet failure: retry connection logging.error("Starting discord client failed, because client could not connect. Retrying in 10s...") await asyncio.sleep(10) diff --git a/src/process_METAR_TAF.py b/src/process_METAR_TAF.py index c305798..3b82aee 100644 --- a/src/process_METAR_TAF.py +++ b/src/process_METAR_TAF.py @@ -13,7 +13,7 @@ from Station import Station -def process_METAR_TAF(doc_type: Doc_Type, station: Station, RWY_DB: pandas.DataFrame, now_DT: dt.datetime, server: Server, DOWNLOAD_TIMEOUT: int) -> tuple[str|None, str|None]: +def process_METAR_TAF(doc_type: Doc_Type, station: Station, RWY_DB: pandas.DataFrame, now_DT: dt.datetime, server: Server, DOWNLOAD_TIMEOUT: float) -> tuple[str|None, str|None]: """ Processes whole METAR or TAF, from downloading, cleaning up, looking whether it is expired or not, changing format, to returning in both original format and my format. """