From 30d027090124c93c0cc4f9d561bed83c0f4374dc Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sat, 2 Mar 2024 18:18:28 +0300 Subject: [PATCH] refactor files filters/outputs --- pddnsc/filters/files.py | 80 +++++++++++++++++++++++++---------------- pddnsc/outputs/files.py | 67 ++++++++++++++++++++++++---------- 2 files changed, 98 insertions(+), 49 deletions(-) diff --git a/pddnsc/filters/files.py b/pddnsc/filters/files.py index 67192d2..290c6d2 100644 --- a/pddnsc/filters/files.py +++ b/pddnsc/filters/files.py @@ -1,4 +1,3 @@ -import asyncio import hashlib import json import aiofiles @@ -7,7 +6,41 @@ from os.path import isfile from pddnsc.base import BaseFilterProvider -class StateHashFilter(BaseFilterProvider): +class GenericTextFileFilter(BaseFilterProvider): + def post_init(self): + super().post_init() + self.filepath = self.config["filepath"] + self.encoding = self.config.get("encoding", "utf-8") + self.mode = self.config.get("mode", "r") + self.check_ipv4 = self.config.get("check_ipv4", False) + self.check_ipv6 = self.config.get("check_ipv6", False) + if "check_ipv4" not in self.config and "check_ipv4" not in self.config: + self.check_ipv4 = self.check_ipv6 = True + self.content = "" + + async def read(self) -> str: + async with aiofiles.open( + self.filepath, mode=self.mode, encoding=self.encoding + ) as f: + self.content = await f.read() + + async def check_imp(self, source_provider: str, addr_v4: str, addr_v6: str) -> bool: + lst = [] + if self.check_ipv4: + lst.append(addr_v4) + if self.check_ipv4: + lst.append(addr_v6) + new_content = "\n".join(lst) + return new_content == self.content + + async def check(self, source_provider: str, addr_v4: str, addr_v6: str) -> bool: + if not isfile(self.filepath): + return True + await self.read() + return await self.check_imp(source_provider, addr_v4, addr_v6) + + +class StateHashFilter(GenericTextFileFilter): """Проверка на то что хотябы один IP адрес изменился по хешу сохраненному в файле Конфигурация: @@ -16,20 +49,14 @@ class StateHashFilter(BaseFilterProvider): """ async def check_imp(self, source_provider: str, addr_v4: str, addr_v6: str) -> bool: - if not isfile(self.config["filepath"]): - return True - - new_state_str = (addr_v4 or "") + (addr_v6 or "") - new_sha = hashlib.sha256(new_state_str.encode(encoding="utf-8")) - async with aiofiles.open( - self.config["filepath"], mode="r", encoding="utf-8" - ) as f: - old_state_hash = await f.read() - - return old_state_hash != new_sha.hexdigest() + new_state_str = (self.check_ipv4 and addr_v4 or "") + ( + self.check_ipv6 and addr_v6 or "" + ) + new_sha = hashlib.sha256(new_state_str.encode(encoding=self.encoding)) + return self.content != new_sha.hexdigest() -class StateFileFilter(BaseFilterProvider): +class StateFileFilter(GenericTextFileFilter): """Проверка на то что хотябы один IP адрес изменился по сравнению с данными в json файле Конфигурация: @@ -42,28 +69,19 @@ class StateFileFilter(BaseFilterProvider): """ async def check_imp(self, source_provider: str, addr_v4: str, addr_v6: str) -> bool: - if not isfile(self.config["filepath"]): - return True - - new_state = { - "ipv4": addr_v4, - "ipv6": addr_v6, - } - - async with aiofiles.open( - self.config["filepath"], mode="r", encoding="utf-8" - ) as f: - old_state = json.loads(await f.read()) + new_state = {} + if self.check_ipv4: + new_state["ipv4"] = addr_v4 or "" + if self.check_ipv6: + new_state["ipv6"] = addr_v6 or "" + old_state = json.loads(self.content) result = True - if "check_ipv4" not in self.config and "check_ipv4" not in self.config: - return new_state != old_state - - if self.config.get("check_ipv4", False): + if self.check_ipv4: result = result and new_state["ipv4"] != old_state["ipv4"] - if self.config.get("check_ipv6", False): + if self.check_ipv6: result = result and new_state["ipv6"] != old_state["ipv6"] return result diff --git a/pddnsc/outputs/files.py b/pddnsc/outputs/files.py index b4c04f5..07a2e6f 100644 --- a/pddnsc/outputs/files.py +++ b/pddnsc/outputs/files.py @@ -5,7 +5,42 @@ import hashlib from pddnsc.base import BaseOutputProvider -class StateFile(BaseOutputProvider): +class GenericTextFile(BaseOutputProvider): + def post_init(self): + super().post_init() + self.filepath = self.config["filepath"] + self.encoding = self.config.get("encoding", "utf-8") + self.mode = self.config.get("mode", "w") + self.save_ipv4 = self.config.get("save_ipv4", False) + self.save_ipv6 = self.config.get("save_ipv6", False) + if "save_ipv4" not in self.config and "save_ipv4" not in self.config: + self.save_ipv4 = self.save_ipv6 = True + self.content = "" + + async def read(self): + async with aiofiles.open(self.filepath, mode="r", encoding=self.encoding) as f: + self.content = await f.read() + + def set_content(self, ipv4: str, ipv6: str): + lst = [] + if self.save_ipv4: + lst.append(ipv4) + if self.save_ipv6: + lst.append(ipv6) + self.content = "\n".join(lst) + + async def write(self): + async with aiofiles.open( + self.filepath, mode=self.mode, encoding=self.encoding + ) as f: + await f.write(self.content) + + async def set_addrs_imp(self, source_provider: str, addr_v4: str, addr_v6: str): + await self.set_content(addr_v4, addr_v6) + await self.write() + + +class StateFile(GenericTextFile): """Схранение всех IP адресов в json файл Конфигурация: @@ -13,19 +48,16 @@ class StateFile(BaseOutputProvider): - filepath: имя файла """ - async def set_addrs_imp(self, source_provider: str, addr_v4: str, addr_v6: str): - state = { - "ipv4": addr_v4 or "", - "ipv6": addr_v6 or "", - } - state_str = json.dumps(state) - async with aiofiles.open( - self.config["filepath"], mode="w", encoding="utf-8" - ) as f: - await f.write(state_str) + async def set_content(self, addr_v4: str, addr_v6: str): + state = {} + if self.save_ipv4: + state["ipv4"] = addr_v4 or "" + if self.save_ipv6: + state["ipv6"] = addr_v6 or "" + self.content = json.dumps(state) -class StateHashFile(BaseOutputProvider): +class StateHashFile(GenericTextFile): """Сохранение хеша от всех IP адресов в файл Конфигурация: @@ -33,10 +65,9 @@ class StateHashFile(BaseOutputProvider): - filepath: имя файла """ - async def set_addrs_imp(self, source_provider: str, addr_v4: str, addr_v6: str): - state_str = (addr_v4 or "") + (addr_v6 or "") + async def set_content(self, addr_v4: str, addr_v6: str): + state_str = (self.save_ipv4 and addr_v4 or "") + ( + self.save_ipv6 and addr_v6 or "" + ) sha = hashlib.sha256(state_str.encode(encoding="utf-8")) - async with aiofiles.open( - self.config["filepath"], mode="w", encoding="utf-8" - ) as f: - await f.write(sha.hexdigest()) + self.content = sha.hexdigest()