ip_geo: russian dual-stack mismatch note alongside english

This commit is contained in:
Andrew Pimenov 2026-08-09 11:27:20 +03:00
parent ce97958cc9
commit 168cd14f51
4 changed files with 21 additions and 9 deletions

View file

@ -136,11 +136,13 @@ async def _identity(client: httpx.AsyncClient, settings: Settings, options: Opti
v6 = v6_merged if v6_merged.ip else None
except (httpx.HTTPError, OSError):
v6 = None
dual_stack = dual_stack_mismatch(merged, v6)
return {
"egress_v4": merged,
"egress_v6": v6,
"cf_trace": cf,
"dual_stack_note": dual_stack_mismatch(merged, v6),
"dual_stack_note": dual_stack[0] if dual_stack else None,
"dual_stack_note_ru": dual_stack[1] if dual_stack else None,
"_flags": flags,
}

View file

@ -315,8 +315,9 @@ def _connection(report: dict, lang: str = "en") -> list[str]:
[_t(lang, "Cloudflare colo", "Дата-центр Cloudflare"), cf.get("colo") or "—"],
]
lines = [title, ""] + _table([_t(lang, "Field", "Поле"), _t(lang, "Value", "Значение")], rows, lang) + [""]
if geo_bundle.get("dual_stack_note"):
lines += [f"> {geo_bundle['dual_stack_note']}", ""]
note = geo_bundle.get("dual_stack_note_ru" if lang == "ru" else "dual_stack_note")
if note:
lines += [f"> {note}", ""]
return lines

View file

@ -194,15 +194,20 @@ def merge_geo(candidates: list[tuple[str, IpGeo]]) -> IpGeo:
return merged
def dual_stack_mismatch(v4: IpGeo | None, v6: IpGeo | None) -> str | None:
def dual_stack_mismatch(v4: IpGeo | None, v6: IpGeo | None) -> tuple[str, str] | None:
if v4 is None or v6 is None or not v4.asn or not v6.asn:
return None
if v4.asn == v6.asn and (v4.country_code or "") == (v6.country_code or ""):
return None
return (
en = (
f"IPv4 egress {v4.ip} is {v4.asn} ({v4.country_code}) but IPv6 egress {v6.ip} "
f"is {v6.asn} ({v6.country_code}); the two stacks leave through different networks."
)
ru = (
f"Исходящий IPv4 {v4.ip} — это {v4.asn} ({v4.country_code}), а исходящий IPv6 {v6.ip} — "
f"{v6.asn} ({v6.country_code}); два стека выходят через разные сети."
)
return en, ru
import asyncio

View file

@ -183,10 +183,14 @@ def test_merge_ignores_a_provider_that_returned_nothing():
def test_dual_stack_mismatch_is_reported_not_resolved():
v4 = IpGeo(ip="203.0.113.44", asn="AS64500", country_code="NL")
v6 = IpGeo(ip="2001:db8::1", asn="AS64777", country_code="DE")
note = dual_stack_mismatch(v4, v6)
assert note is not None
assert "AS64500" in note
assert "AS64777" in note
pair = dual_stack_mismatch(v4, v6)
assert pair is not None
en, ru = pair
assert "AS64500" in en
assert "AS64777" in en
assert "AS64500" in ru
assert "AS64777" in ru
assert any(ch in ru for ch in "абвгдеёжзийклмнопрстуфхцчшщъыьэюя")
def test_dual_stack_agreement_produces_no_note():