Compare commits

..

No commits in common. "058b9a9cf8234da90bb7676ba0f93b24c437093d" and "bbfb4f92af498ada25ab9279c37629c1b19695d3" have entirely different histories.

4 changed files with 33 additions and 48 deletions

View File

@ -1,13 +1,6 @@
import httpx import httpx
import asyncio import asyncio
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import NamedTuple
class IPAddreses(NamedTuple):
source_name: str
ipv4: str
ipv6: str
class BaseSourceProvider(ABC): class BaseSourceProvider(ABC):
@ -25,13 +18,12 @@ class BaseSourceProvider(ABC):
def __str__(self): def __str__(self):
return f"{self.__class__.__name__}: {self.name}" return f"{self.__class__.__name__}: {self.name}"
async def fetch_all(self) -> IPAddreses: async def fetch_all(self) -> tuple[str, str, str]:
results = await asyncio.gather( results = await asyncio.gather(
self.fetch_v4(), self.fetch_v6(), return_exceptions=True self.fetch_v4(), self.fetch_v6(), return_exceptions=True
) )
return (self.name,) + tuple(
return IPAddreses( None if isinstance(i, Exception) else i for i in results
self.name, *("" if isinstance(i, Exception) else i for i in results)
) )
def __init_subclass__(cls) -> None: def __init_subclass__(cls) -> None:

View File

@ -2,21 +2,20 @@ import httpx
import asyncio import asyncio
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import toml import toml
from .base import BaseFilterProvider, BaseSourceProvider, BaseOutputProvider, IPAddreses from .base import BaseFilterProvider, BaseSourceProvider, BaseOutputProvider
from .plugins import use_plugins from .plugins import use_plugins
from typing import Optional
async def get_ip_addresses() -> Optional[IPAddreses]: async def source_task():
providers = BaseSourceProvider.registred.values() providers = BaseSourceProvider.registred.values()
ip_addresses = None result = None
is_done = False is_done = False
pending = [asyncio.create_task(p.fetch_all(), name=p.name) for p in providers] pending = [asyncio.create_task(p.fetch_all(), name=p.name) for p in providers]
while not is_done and pending: while not is_done and pending:
done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
for x in done: for x in done:
ip_addresses = x.result() result = x.result()
if ip_addresses.ipv4 or ip_addresses.ipv6: if len(result) == 3 and result[1] or result[2]:
is_done = True is_done = True
break break
@ -27,16 +26,14 @@ async def get_ip_addresses() -> Optional[IPAddreses]:
await gather await gather
except asyncio.CancelledError: except asyncio.CancelledError:
pass pass
return ip_addresses return result
async def check_ip_addresses(ip_addresses): async def filter_task(ip_result):
providers = BaseFilterProvider.registred.values() providers = BaseFilterProvider.registred.values()
result = True result = True
failed = "" failed = ""
pending = [ pending = [asyncio.create_task(p.check(*ip_result), name=p.name) for p in providers]
asyncio.create_task(p.check(*ip_addresses), name=p.name) for p in providers
]
while result and pending: while result and pending:
done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
for x in done: for x in done:
@ -58,17 +55,17 @@ async def check_ip_addresses(ip_addresses):
return result return result
async def send_ip_addreses(ip_addresses): async def output_task(result):
providers = BaseOutputProvider.registred.values() providers = BaseOutputProvider.registred.values()
await asyncio.gather( await asyncio.gather(
*( *(asyncio.create_task(p.set_addrs(*result), name=p.name) for p in providers)
asyncio.create_task(p.set_addrs(*ip_addresses), name=p.name)
for p in providers
)
) )
def print_debug_info(config): async def app(ipv4t, ipv6t):
config = toml.load("settings/config.toml")
use_plugins(config, ipv4t, ipv6t)
debug = config.get("debug", False) debug = config.get("debug", False)
if debug: if debug:
print("DEBUG info:") print("DEBUG info:")
@ -91,31 +88,19 @@ def print_debug_info(config):
f"output providers: {[*BaseOutputProvider.registred]}, {[*map(str, BaseOutputProvider.registred.values())]}" f"output providers: {[*BaseOutputProvider.registred]}, {[*map(str, BaseOutputProvider.registred.values())]}"
) )
result = await source_task()
async def app(config, ipv4t, ipv6t): if not await filter_task(result):
use_plugins(config, ipv4t, ipv6t)
print_debug_info(config)
ip_addreses = await get_ip_addresses()
if ip_addreses is None:
print("no IP addresses")
return
if not await check_ip_addresses(ip_addreses):
print("stop by filters") print("stop by filters")
return return
await send_ip_addreses(ip_addreses) await output_task(result)
print("done") print("done")
async def main(): async def main():
config = toml.load("settings/config.toml")
async with httpx.AsyncHTTPTransport( async with httpx.AsyncHTTPTransport(
local_address="0.0.0.0", proxy=config.get("proxy_v4") local_address="0.0.0.0",
) as ipv4t, httpx.AsyncHTTPTransport( ) as ipv4t, httpx.AsyncHTTPTransport(local_address="::") as ipv6t:
local_address="::", proxy=config.get("proxy_v6") await app(ipv4t, ipv6t)
) as ipv6t:
await app(config, ipv4t, ipv6t)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -12,7 +12,11 @@ class StateHashFilter(BaseFilterProvider):
if not isfile(self.config["filepath"]): if not isfile(self.config["filepath"]):
return True return True
new_state_str = (addr_v4 or "") + (addr_v6 or "") new_state = {
"ipv4": addr_v4 or "",
"ipv6": addr_v6 or "",
}
new_state_str = json.dumps(new_state)
new_sha = hashlib.sha256(new_state_str.encode(encoding="utf-8")) new_sha = hashlib.sha256(new_state_str.encode(encoding="utf-8"))
async with aiofiles.open( async with aiofiles.open(
self.config["filepath"], mode="r", encoding="utf-8" self.config["filepath"], mode="r", encoding="utf-8"

View File

@ -21,7 +21,11 @@ class StateFile(BaseOutputProvider):
class StateHashFile(BaseOutputProvider): class StateHashFile(BaseOutputProvider):
async def set_addrs_imp(self, source_provider, addr_v4, addr_v6): async def set_addrs_imp(self, source_provider, addr_v4, addr_v6):
state_str = (addr_v4 or "") + (addr_v6 or "") state = {
"ipv4": addr_v4 or "",
"ipv6": addr_v6 or "",
}
state_str = json.dumps(state)
sha = hashlib.sha256(state_str.encode(encoding="utf-8")) sha = hashlib.sha256(state_str.encode(encoding="utf-8"))
async with aiofiles.open( async with aiofiles.open(
self.config["filepath"], mode="w", encoding="utf-8" self.config["filepath"], mode="w", encoding="utf-8"