repo hygiene: track the PINK launcher import closure
67 production .py modules that the running PINK service imports but which were never committed: prod/bingx/ (HTTP client, market/user streams, journal, config), prod/clean_arch/ adapters/persistence/runtime/dita/dita_v2 production modules and their co-located tests. Rule going forward: every module imported by launch_dolphin_pink.py / pink_direct.py must appear in git ls-files. Excludes _backup dirs, __pycache__, and non-code files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
128
prod/bingx/dns_cache.py
Normal file
128
prod/bingx/dns_cache.py
Normal file
@@ -0,0 +1,128 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import socket
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Mapping
|
||||
|
||||
|
||||
_STATIC_ENDPOINT_IPS: dict[str, tuple[str, ...]] = {
|
||||
"open-api.bingx.com": (
|
||||
"18.66.122.103",
|
||||
"18.66.122.71",
|
||||
"18.66.122.3",
|
||||
"18.66.122.36",
|
||||
"2600:9000:2250:4800:1a:4f2d:b440:93a1",
|
||||
"2600:9000:2250:c400:1a:4f2d:b440:93a1",
|
||||
"2600:9000:2250:6c00:1a:4f2d:b440:93a1",
|
||||
"2600:9000:2250:7200:1a:4f2d:b440:93a1",
|
||||
"2600:9000:2250:4600:1a:4f2d:b440:93a1",
|
||||
"2600:9000:2250:ee00:1a:4f2d:b440:93a1",
|
||||
"2600:9000:2250:fc00:1a:4f2d:b440:93a1",
|
||||
"2600:9000:2250:6800:1a:4f2d:b440:93a1",
|
||||
),
|
||||
"open-api-vst.bingx.com": (
|
||||
"18.165.122.31",
|
||||
"18.165.122.22",
|
||||
"18.165.122.47",
|
||||
"18.165.122.63",
|
||||
"2600:9000:2375:a200:14:7788:7980:93a1",
|
||||
"2600:9000:2375:9000:14:7788:7980:93a1",
|
||||
"2600:9000:2375:2c00:14:7788:7980:93a1",
|
||||
"2600:9000:2375:7800:14:7788:7980:93a1",
|
||||
"2600:9000:2375:f600:14:7788:7980:93a1",
|
||||
"2600:9000:2375:ce00:14:7788:7980:93a1",
|
||||
"2600:9000:2375:e800:14:7788:7980:93a1",
|
||||
"2600:9000:2375:e200:14:7788:7980:93a1",
|
||||
),
|
||||
"open-api-swap.bingx.com": (
|
||||
"3.164.68.61",
|
||||
"3.164.68.42",
|
||||
"3.164.68.75",
|
||||
"3.164.68.125",
|
||||
"2600:9000:278c:7400:1f:3dec:7600:93a1",
|
||||
"2600:9000:278c:3e00:1f:3dec:7600:93a1",
|
||||
"2600:9000:278c:5800:1f:3dec:7600:93a1",
|
||||
"2600:9000:278c:3400:1f:3dec:7600:93a1",
|
||||
"2600:9000:278c:ce00:1f:3dec:7600:93a1",
|
||||
"2600:9000:278c:9a00:1f:3dec:7600:93a1",
|
||||
"2600:9000:278c:9000:1f:3dec:7600:93a1",
|
||||
"2600:9000:278c:c800:1f:3dec:7600:93a1",
|
||||
),
|
||||
"open-api.bingx.pro": (
|
||||
"2606:4700:4403::ac40:9313",
|
||||
"2a06:98c1:310d::6812:28ed",
|
||||
),
|
||||
"open-api-vst.bingx.pro": (
|
||||
"2a06:98c1:310d::6812:28ed",
|
||||
"2606:4700:4403::ac40:9313",
|
||||
),
|
||||
"open-api-swap.bingx.pro": (
|
||||
"2a06:98c1:310d::6812:28ed",
|
||||
"2606:4700:4403::ac40:9313",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class BingxDnsRecord:
|
||||
hostname: str
|
||||
ips: tuple[str, ...]
|
||||
source: str
|
||||
updated_at_ns: int
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class BingxDnsFallbackCache:
|
||||
default_ips: Mapping[str, tuple[str, ...]] = field(default_factory=lambda: dict(_STATIC_ENDPOINT_IPS))
|
||||
_records: dict[str, BingxDnsRecord] = field(default_factory=dict, init=False, repr=False)
|
||||
|
||||
def resolve(self, hostname: str) -> tuple[str, ...]:
|
||||
record = self._records.get(hostname)
|
||||
if record is not None:
|
||||
return record.ips
|
||||
return tuple(self.default_ips.get(hostname, ()))
|
||||
|
||||
def record_static(self, hostname: str) -> tuple[str, ...]:
|
||||
ips = self.resolve(hostname)
|
||||
if ips:
|
||||
self._records[hostname] = BingxDnsRecord(
|
||||
hostname=hostname,
|
||||
ips=ips,
|
||||
source="static",
|
||||
updated_at_ns=time.monotonic_ns(),
|
||||
)
|
||||
return ips
|
||||
|
||||
def refresh_from_dns(self, hostname: str) -> tuple[str, ...]:
|
||||
seen: list[str] = []
|
||||
for family in (socket.AF_UNSPEC,):
|
||||
infos = socket.getaddrinfo(hostname, None, family=family, type=socket.SOCK_STREAM)
|
||||
for info in infos:
|
||||
address = info[4][0]
|
||||
if address not in seen:
|
||||
seen.append(address)
|
||||
ips = tuple(seen)
|
||||
if ips:
|
||||
self._records[hostname] = BingxDnsRecord(
|
||||
hostname=hostname,
|
||||
ips=ips,
|
||||
source="dns",
|
||||
updated_at_ns=time.monotonic_ns(),
|
||||
)
|
||||
return ips
|
||||
|
||||
def maybe_refresh_from_dns(self, hostname: str, *, min_interval_secs: int = 300) -> tuple[str, ...] | None:
|
||||
record = self._records.get(hostname)
|
||||
if record is not None:
|
||||
age_secs = (time.monotonic_ns() - record.updated_at_ns) / 1_000_000_000
|
||||
if age_secs < min_interval_secs:
|
||||
return record.ips
|
||||
try:
|
||||
return self.refresh_from_dns(hostname)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def is_bingx_hostname(hostname: str | None) -> bool:
|
||||
return hostname in _STATIC_ENDPOINT_IPS
|
||||
Reference in New Issue
Block a user