botonomus.network¶
Proxy tunnelling, the credential forwarder, exit geolocation and proxy checks.
botonomus.network
¶
Proxy tunnelling, the credential forwarder, exit geolocation and proxy checks.
COUNTRY_LOCALE
module-attribute
¶
COUNTRY_LOCALE: Final = {'US': 'en-US', 'GB': 'en-GB', 'CA': 'en-CA', 'AU': 'en-AU', 'IE': 'en-IE', 'DE': 'de-DE', 'AT': 'de-AT', 'CH': 'de-CH', 'FR': 'fr-FR', 'BE': 'fr-BE', 'NL': 'nl-NL', 'ES': 'es-ES', 'MX': 'es-MX', 'IT': 'it-IT', 'PT': 'pt-PT', 'BR': 'pt-BR', 'PL': 'pl-PL', 'SE': 'sv-SE', 'NO': 'nb-NO', 'DK': 'da-DK', 'FI': 'fi-FI', 'CZ': 'cs-CZ', 'RO': 'ro-RO', 'UA': 'uk-UA', 'RU': 'ru-RU', 'TR': 'tr-TR', 'GE': 'ka-GE', 'JP': 'ja-JP', 'KR': 'ko-KR', 'CN': 'zh-CN', 'TW': 'zh-TW', 'IN': 'en-IN', 'IL': 'he-IL', 'GR': 'el-GR', 'HU': 'hu-HU'}
Most common primary browser locale per ISO 3166-1 alpha-2 country.
DEFAULT_PROVIDERS
module-attribute
¶
DEFAULT_PROVIDERS: Final[tuple[GeoProvider, ...]] = (IpApiProvider(), IpInfoProvider())
ip-api.com first for its risk flags, then ipinfo.io over TLS.
ProxyForwarder
¶
ProxyForwarder(upstream: ProxySpec)
A per-browser loopback SOCKS5 server tunnelling through one upstream proxy.
Every accepted connection is registered synchronously, as soon as its
transport exists, so close can abort all of them, including ones
accepted in the instant before the listener closed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
upstream
|
ProxySpec
|
The authenticated upstream proxy. |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
port |
The bound loopback port, valid after |
active_connections
property
¶
Client connections accepted and not yet finished.
start
async
¶
Bind to an ephemeral loopback port and start accepting connections.
Raises:
| Type | Description |
|---|---|
OSError
|
If the loopback socket cannot be bound. |
close
async
¶
Stop accepting, abort every client connection and wait for them. Idempotent.
Client transports are aborted rather than closed gracefully: a graceful close waits for buffered writes, which never drain once the peer has stopped reading.
ExitCache
¶
ExitCache(ttl: float = 3600.0, *, lookup: Callable[[ProxySpec], Awaitable[ExitInfo]] | None = None, clock: Callable[[], float] = monotonic)
Per-proxy exit lookups, cached in memory with a time to live.
Concurrent get calls for the same proxy share one lookup. Failures
are not cached. Proxies with different credentials are distinct keys, since
providers often pin a sticky exit to the username.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ttl
|
float
|
Seconds a successful result stays valid. |
3600.0
|
lookup
|
Callable[[ProxySpec], Awaitable[ExitInfo]] | None
|
The lookup coroutine; defaults to |
None
|
clock
|
Callable[[], float]
|
Monotonic time source, replaceable in tests. |
monotonic
|
get
async
¶
Return the cached exit for spec, looking it up if absent or expired.
Cancelling one caller does not cancel a lookup other callers share.
Raises:
| Type | Description |
|---|---|
ExitLookupError
|
Or whatever the configured lookup raises, for every caller waiting on the failed lookup. |
ExitInfo
dataclass
¶
ExitInfo(ip: str, country_code: str, region: str, city: str, timezone: str, isp: str, hosting: bool, flagged_proxy: bool, latency: float, source: str = '')
A proxy exit as seen by the lookup service.
Providers differ in what they report; fields a provider does not supply are
empty strings, and flags it does not supply are False.
Attributes:
| Name | Type | Description |
|---|---|---|
ip |
str
|
Public exit address. |
country_code |
str
|
ISO 3166-1 alpha-2 code, or empty if unknown. |
region |
str
|
Region or state name. |
city |
str
|
City name. |
timezone |
str
|
IANA zone such as |
isp |
str
|
Network operator (ipinfo.io reports the AS organisation). |
hosting |
bool
|
The address belongs to a data-centre range (ip-api.com only). |
flagged_proxy |
bool
|
The address is a known proxy, VPN or Tor exit (ip-api.com only). |
latency |
float
|
Seconds for the lookup round trip through the proxy. |
source |
str
|
Name of the provider that answered, such as |
ExitLookupError
¶
Bases: UpstreamError
Every exit lookup provider failed.
Attributes:
| Name | Type | Description |
|---|---|---|
causes |
|
GeoProvider
¶
Bases: Protocol
An exit lookup service reachable with one HTTP request through the tunnel.
tls_context
¶
The TLS context for the service, or None for plain HTTP.
request
¶
The complete HTTP request, which must ask the server to close afterwards.
parse
¶
parse(response: bytes, *, latency: float) -> ExitInfo
Turn the raw HTTP response into an ExitInfo.
Raises:
| Type | Description |
|---|---|
UpstreamError
|
If the response is not a successful lookup. |
IpApiProvider
dataclass
¶
ip-api.com over plain HTTP: location, ISP and data-centre / proxy flags.
The free endpoint has no TLS and allows about 45 requests per minute per exit
address; beyond that it answers 429.
Attributes:
| Name | Type | Description |
|---|---|---|
host |
str
|
Service hostname. |
port |
int
|
Service port. |
parse
¶
parse(response: bytes, *, latency: float) -> ExitInfo
Parse an ip-api.com JSON response.
Raises:
| Type | Description |
|---|---|
UpstreamError
|
On a non-200 status (including rate limiting), malformed
JSON, or a |
IpInfoProvider
dataclass
¶
ipinfo.io over TLS: location, timezone and AS organisation, no risk flags.
Attributes:
| Name | Type | Description |
|---|---|---|
host |
str
|
Service hostname, also used for SNI and certificate verification. |
port |
int
|
Service port. |
context |
SSLContext | None
|
TLS context; |
parse
¶
parse(response: bytes, *, latency: float) -> ExitInfo
Parse an ipinfo.io JSON response.
Raises:
| Type | Description |
|---|---|
UpstreamError
|
On a non-200 status (including rate limiting), malformed
JSON, an |
ProxyCheck
dataclass
¶
ProxyCheck(address: str, ok: bool, error: str | None, exit: ExitInfo | None)
The outcome of checking one proxy. Never contains credentials.
Attributes:
| Name | Type | Description |
|---|---|---|
address |
str
|
|
ok |
bool
|
Whether an exit lookup through the proxy succeeded. |
error |
str | None
|
Failure category, |
exit |
ExitInfo | None
|
The exit as seen by the lookup service, |
UpstreamError
¶
Bases: Exception
The upstream proxy refused or failed the tunnel.
exit_info
async
¶
exit_info(spec: ProxySpec, timeout: float = 15.0, providers: Sequence[GeoProvider] = DEFAULT_PROVIDERS) -> ExitInfo
Look up the proxy's exit address and location through the proxy itself.
Providers are tried in order until one succeeds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spec
|
ProxySpec
|
The proxy to test. |
required |
timeout
|
float
|
Seconds allowed for each provider attempt, so the whole call is
bounded by |
15.0
|
providers
|
Sequence[GeoProvider]
|
Lookup services in order of preference. |
DEFAULT_PROVIDERS
|
Returns:
| Type | Description |
|---|---|
ExitInfo
|
The exit as seen by the first provider that answered. |
Raises:
| Type | Description |
|---|---|
ExitLookupError
|
If every provider failed; |
check_proxies
async
¶
check_proxies(urls: Iterable[str], *, parallel: int = 16, timeout: float = 20.0, providers: Sequence[GeoProvider] = DEFAULT_PROVIDERS) -> list[ProxyCheck]
Check proxies concurrently by looking up each one's exit through it.
A failing proxy never fails the batch; it is reported in its result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
urls
|
Iterable[str]
|
Proxy URLs, for example from |
required |
parallel
|
int
|
Maximum number of checks in flight. |
16
|
timeout
|
float
|
Seconds allowed per lookup provider attempt. |
20.0
|
providers
|
Sequence[GeoProvider]
|
Exit lookup services, tried in order for each proxy. |
DEFAULT_PROVIDERS
|
Returns:
| Type | Description |
|---|---|
list[ProxyCheck]
|
One result per URL, in input order. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
load_proxies
¶
Read one proxy URL per line.
Blank lines and lines starting with # are ignored. Every entry is validated
up front, without echoing credentials in errors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
A UTF-8 text file. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
The proxy URLs in file order. |
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If any entry is not a valid proxy URL. |
OSError
|
If the file cannot be read. |
open_tunnel
async
¶
open_tunnel(spec: ProxySpec, host: str, port: int) -> tuple[StreamReader, StreamWriter]
Open a TCP stream to host:port through the upstream proxy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spec
|
ProxySpec
|
The upstream proxy. |
required |
host
|
str
|
Destination hostname or IP literal. |
required |
port
|
int
|
Destination port. |
required |
Returns:
| Type | Description |
|---|---|
tuple[StreamReader, StreamWriter]
|
A connected |
Raises:
| Type | Description |
|---|---|
UpstreamError
|
If the proxy refuses, fails authentication or misbehaves. |
OSError
|
If the proxy cannot be reached. |