asockslib 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asockslib/__init__.py +117 -0
- asockslib/_console.py +18 -0
- asockslib/_port_utils.py +132 -0
- asockslib/benchmark.py +365 -0
- asockslib/cli/__init__.py +72 -0
- asockslib/cli/_helpers.py +162 -0
- asockslib/cli/_output.py +52 -0
- asockslib/cli/api_commands.py +377 -0
- asockslib/cli/commands.py +485 -0
- asockslib/client.py +634 -0
- asockslib/exceptions.py +89 -0
- asockslib/geo.json +649269 -0
- asockslib/geo_picker/__init__.py +30 -0
- asockslib/geo_picker/_completer.py +90 -0
- asockslib/geo_picker/_messages.py +89 -0
- asockslib/geo_picker/_types.py +89 -0
- asockslib/geo_picker/picker.py +667 -0
- asockslib/models/__init__.py +72 -0
- asockslib/models/directory.py +65 -0
- asockslib/models/enums.py +65 -0
- asockslib/models/port.py +187 -0
- asockslib/models/requests.py +89 -0
- asockslib/models/responses.py +20 -0
- asockslib/proxy_pool.py +659 -0
- asockslib/quick.py +174 -0
- asockslib/smart_proxy.py +251 -0
- asockslib-0.1.0.dist-info/METADATA +228 -0
- asockslib-0.1.0.dist-info/RECORD +30 -0
- asockslib-0.1.0.dist-info/WHEEL +4 -0
- asockslib-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Pydantic models for ASocks API v2 requests and responses.
|
|
2
|
+
|
|
3
|
+
Covers all 25 endpoints documented at https://docs.asocks.com/en/.
|
|
4
|
+
|
|
5
|
+
Modules:
|
|
6
|
+
- :mod:`.enums` — port statuses and connection/proxy type identifiers.
|
|
7
|
+
- :mod:`.directory` — countries, states, cities, ASN entries.
|
|
8
|
+
- :mod:`.port` — proxy port models and list responses.
|
|
9
|
+
- :mod:`.requests` — bodies and query parameters for mutations.
|
|
10
|
+
- :mod:`.responses` — typed wrappers for API responses.
|
|
11
|
+
|
|
12
|
+
Every model uses ``extra="allow"`` so that new fields added by the API
|
|
13
|
+
are accepted without breaking existing code.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from asockslib.models.directory import (
|
|
19
|
+
ASNInfo,
|
|
20
|
+
ASNListResponse,
|
|
21
|
+
CityInfo,
|
|
22
|
+
CountryInfo,
|
|
23
|
+
StateInfo,
|
|
24
|
+
)
|
|
25
|
+
from asockslib.models.enums import (
|
|
26
|
+
AuthType,
|
|
27
|
+
ConnectionType,
|
|
28
|
+
ConnectionTypeId,
|
|
29
|
+
PortStatus,
|
|
30
|
+
ProxyType,
|
|
31
|
+
ProxyTypeId,
|
|
32
|
+
ServerPortType,
|
|
33
|
+
)
|
|
34
|
+
from asockslib.models.port import (
|
|
35
|
+
PortInfo,
|
|
36
|
+
PortListResponse,
|
|
37
|
+
)
|
|
38
|
+
from asockslib.models.requests import (
|
|
39
|
+
CreatePortRequest,
|
|
40
|
+
CreateTemplateRequest,
|
|
41
|
+
PortFilterParams,
|
|
42
|
+
UpdatePortRequest,
|
|
43
|
+
UpdateTemplateRequest,
|
|
44
|
+
WhitelistAddRequest,
|
|
45
|
+
)
|
|
46
|
+
from asockslib.models.responses import (
|
|
47
|
+
BalanceResponse,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"ASNInfo",
|
|
52
|
+
"ASNListResponse",
|
|
53
|
+
"AuthType",
|
|
54
|
+
"BalanceResponse",
|
|
55
|
+
"CityInfo",
|
|
56
|
+
"ConnectionType",
|
|
57
|
+
"ConnectionTypeId",
|
|
58
|
+
"CountryInfo",
|
|
59
|
+
"CreatePortRequest",
|
|
60
|
+
"CreateTemplateRequest",
|
|
61
|
+
"PortFilterParams",
|
|
62
|
+
"PortInfo",
|
|
63
|
+
"PortListResponse",
|
|
64
|
+
"PortStatus",
|
|
65
|
+
"ProxyType",
|
|
66
|
+
"ProxyTypeId",
|
|
67
|
+
"ServerPortType",
|
|
68
|
+
"StateInfo",
|
|
69
|
+
"UpdatePortRequest",
|
|
70
|
+
"UpdateTemplateRequest",
|
|
71
|
+
"WhitelistAddRequest",
|
|
72
|
+
]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Directory (geo) models for the ASocks API v2.
|
|
2
|
+
|
|
3
|
+
Country, state, city, and ASN data structures returned by the
|
|
4
|
+
``/v2/dir/*`` endpoints.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pydantic import AliasChoices, BaseModel, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CountryInfo(BaseModel, extra="allow"):
|
|
13
|
+
"""Country entry from ``GET /v2/dir/countries``."""
|
|
14
|
+
|
|
15
|
+
id: int = Field(description="Internal country ID")
|
|
16
|
+
name: str = Field(default="", description="Full country name")
|
|
17
|
+
code: str = Field(
|
|
18
|
+
default="",
|
|
19
|
+
description="ISO country code (e.g. US, DE)",
|
|
20
|
+
validation_alias=AliasChoices("code", "short_name"),
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def short_name(self) -> str:
|
|
25
|
+
"""Alias for :pyattr:`code` (backward-compatible)."""
|
|
26
|
+
return self.code
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class StateInfo(BaseModel, extra="allow"):
|
|
30
|
+
"""State/region entry from ``GET /v2/dir/states``."""
|
|
31
|
+
|
|
32
|
+
id: int = Field(description="Internal state ID")
|
|
33
|
+
name: str = Field(default="", description="State/region name")
|
|
34
|
+
dir_country_id: int | None = Field(default=None, description="Parent country ID")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CityInfo(BaseModel, extra="allow"):
|
|
38
|
+
"""City entry from ``GET /v2/dir/cities``."""
|
|
39
|
+
|
|
40
|
+
id: int = Field(description="Internal city ID")
|
|
41
|
+
name: str = Field(default="", description="City name")
|
|
42
|
+
dir_country_id: int | None = Field(default=None, description="Parent country ID")
|
|
43
|
+
dir_state_id: int | None = Field(default=None, description="Parent state ID")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ASNInfo(BaseModel, extra="allow"):
|
|
47
|
+
"""ASN (Autonomous System Number) entry from ``GET /v2/dir/asns``."""
|
|
48
|
+
|
|
49
|
+
asn: int = Field(description="Autonomous System Number")
|
|
50
|
+
name: str = Field(default="", description="ISP / provider name")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ASNListResponse(BaseModel, extra="allow"):
|
|
54
|
+
"""Paginated response for ``GET /v2/dir/asns``."""
|
|
55
|
+
|
|
56
|
+
data: list[ASNInfo] = Field(default_factory=list[ASNInfo], description="ASN entries")
|
|
57
|
+
current_page: int = Field(default=1, description="Current page number")
|
|
58
|
+
per_page: int = Field(default=1000, description="Items per page")
|
|
59
|
+
total: int = Field(default=0, description="Total number of ASN entries")
|
|
60
|
+
last_page: int = Field(default=1, description="Last page number")
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def items(self) -> list[ASNInfo]:
|
|
64
|
+
"""Alias for ``data``."""
|
|
65
|
+
return self.data
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Enum types for the ASocks API v2.
|
|
2
|
+
|
|
3
|
+
Centralizes all enumeration types used across the library:
|
|
4
|
+
port statuses, proxy types, connection types, and identifiers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from enum import IntEnum, StrEnum
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PortStatus(IntEnum):
|
|
13
|
+
"""Port status codes used by the ASocks API."""
|
|
14
|
+
|
|
15
|
+
ACTIVE = 1
|
|
16
|
+
INACTIVE = 0
|
|
17
|
+
EXPIRED = 2
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ProxyType(StrEnum):
|
|
21
|
+
"""Proxy category."""
|
|
22
|
+
|
|
23
|
+
RESIDENTIAL = "residential"
|
|
24
|
+
MOBILE = "mobile"
|
|
25
|
+
CORPORATE = "corporate"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ConnectionType(StrEnum):
|
|
29
|
+
"""Connection mode for a proxy port."""
|
|
30
|
+
|
|
31
|
+
KEEP_PROXY = "keep-proxy"
|
|
32
|
+
KEEP_CONNECTION = "keep-connection"
|
|
33
|
+
ROTATE_CONNECTION = "rotate-connection"
|
|
34
|
+
KEEP_CONNECTION_LOW_TRUST = "keep-connection-low-trust"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AuthType(StrEnum):
|
|
38
|
+
"""Authentication method."""
|
|
39
|
+
|
|
40
|
+
LOGIN_PASSWORD = "login-and-password"
|
|
41
|
+
IP_WHITELIST = "ip-whitelist"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ProxyTypeId(IntEnum):
|
|
45
|
+
"""Numeric proxy-type identifiers for API requests."""
|
|
46
|
+
|
|
47
|
+
RESIDENTIAL = 1
|
|
48
|
+
ALL = 2
|
|
49
|
+
MOBILE = 3
|
|
50
|
+
CORPORATE = 4
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ConnectionTypeId(IntEnum):
|
|
54
|
+
"""Numeric connection-type identifiers for API requests."""
|
|
55
|
+
|
|
56
|
+
KEEP_PROXY = 1
|
|
57
|
+
KEEP_CONNECTION = 2
|
|
58
|
+
ROTATE_CONNECTION = 3
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ServerPortType(IntEnum):
|
|
62
|
+
"""Server port type."""
|
|
63
|
+
|
|
64
|
+
SHARED = 0
|
|
65
|
+
DEDICATED = 1
|
asockslib/models/port.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Port models for the ASocks API v2.
|
|
2
|
+
|
|
3
|
+
Defines :class:`PortInfo` (single proxy port) and
|
|
4
|
+
:class:`PortListResponse` (paginated port list wrapper).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import ClassVar, cast
|
|
10
|
+
|
|
11
|
+
from beartype import beartype
|
|
12
|
+
from pydantic import AliasChoices, BaseModel, Field, field_validator, model_validator
|
|
13
|
+
|
|
14
|
+
from asockslib.models.enums import PortStatus
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PortInfo(BaseModel, extra="allow"):
|
|
18
|
+
"""A single proxy port returned by the API.
|
|
19
|
+
|
|
20
|
+
Produced by ``GET /v2/proxy/ports``, ``GET /v2/proxy/port-info``
|
|
21
|
+
and ``POST /v2/proxy/create-port``.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
id: Unique port identifier.
|
|
25
|
+
host: Proxy server address.
|
|
26
|
+
port: Port number.
|
|
27
|
+
login: Authentication login.
|
|
28
|
+
password: Authentication password.
|
|
29
|
+
protocol: Proxy protocol (``socks5``, ``http``, …).
|
|
30
|
+
proxy_type: Proxy category.
|
|
31
|
+
country_code: ISO country code.
|
|
32
|
+
status: Port status code (see :class:`PortStatus`).
|
|
33
|
+
|
|
34
|
+
Example::
|
|
35
|
+
|
|
36
|
+
print(port.proxy_url) # socks5://user:pass@host:10001
|
|
37
|
+
print(port.is_active) # True
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
id: int = Field(description="Unique port identifier")
|
|
41
|
+
host: str = Field(
|
|
42
|
+
default="",
|
|
43
|
+
description="Proxy host address",
|
|
44
|
+
validation_alias=AliasChoices("host", "server"),
|
|
45
|
+
)
|
|
46
|
+
port: int = Field(default=0, description="Proxy port number")
|
|
47
|
+
login: str = Field(default="", description="Authentication login")
|
|
48
|
+
password: str = Field(default="", description="Authentication password")
|
|
49
|
+
protocol: str = Field(default="socks5", description="Proxy protocol")
|
|
50
|
+
proxy_type: str | None = Field(default=None, description="Proxy type category")
|
|
51
|
+
country: str = Field(default="", description="Country name")
|
|
52
|
+
country_code: str = Field(default="", description="ISO country code")
|
|
53
|
+
city: str = Field(default="", description="City name")
|
|
54
|
+
state: str = Field(default="", description="State name")
|
|
55
|
+
status: int | str = Field(default=1, description="Port status code")
|
|
56
|
+
name: str = Field(default="", description="User-defined port name")
|
|
57
|
+
asn: int | None = Field(default=None, description="Autonomous System Number")
|
|
58
|
+
external_ip: str = Field(default="", description="External IP address")
|
|
59
|
+
|
|
60
|
+
@model_validator(mode="before")
|
|
61
|
+
@classmethod
|
|
62
|
+
def _coerce_none_strings(cls, data: dict[str, object] | object) -> dict[str, object] | object:
|
|
63
|
+
"""Coerce ``None`` values to empty strings for str fields."""
|
|
64
|
+
if not isinstance(data, dict):
|
|
65
|
+
return data
|
|
66
|
+
data = cast("dict[str, object]", data)
|
|
67
|
+
_str_fields = (
|
|
68
|
+
"host",
|
|
69
|
+
"login",
|
|
70
|
+
"password",
|
|
71
|
+
"protocol",
|
|
72
|
+
"country",
|
|
73
|
+
"country_code",
|
|
74
|
+
"city",
|
|
75
|
+
"state",
|
|
76
|
+
"name",
|
|
77
|
+
"external_ip",
|
|
78
|
+
)
|
|
79
|
+
for fld in _str_fields:
|
|
80
|
+
if fld in data and data[fld] is None:
|
|
81
|
+
data[fld] = ""
|
|
82
|
+
return data
|
|
83
|
+
|
|
84
|
+
expires_at: str | None = Field(default=None, description="Expiration timestamp")
|
|
85
|
+
created_at: str | None = Field(default=None, description="Creation timestamp")
|
|
86
|
+
ttl: int | None = Field(default=None, description="Time-to-live in days")
|
|
87
|
+
traffic_limit: float | None = Field(default=None, description="Traffic limit in GB")
|
|
88
|
+
traffic_used: float | None = Field(default=None, description="Traffic used in GB")
|
|
89
|
+
type_id: int | None = Field(default=None, description="Connection type ID")
|
|
90
|
+
proxy_type_id: int | None = Field(default=None, description="Proxy type ID")
|
|
91
|
+
server_port_type_id: int | None = Field(default=None, description="Server port type ID")
|
|
92
|
+
|
|
93
|
+
_STATUS_MAP: ClassVar[dict[str, int]] = {
|
|
94
|
+
"active": 1,
|
|
95
|
+
"inactive": 0,
|
|
96
|
+
"expired": 2,
|
|
97
|
+
"stopped": 0,
|
|
98
|
+
"error": 0,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@field_validator("status", mode="before")
|
|
102
|
+
@classmethod
|
|
103
|
+
def _coerce_status(cls, v: object) -> int:
|
|
104
|
+
"""Accept both int and string status from the API.
|
|
105
|
+
|
|
106
|
+
Typed ``object`` (not ``int | str``): a ``mode="before"`` validator
|
|
107
|
+
receives the raw, not-yet-validated JSON value, which may be any type.
|
|
108
|
+
"""
|
|
109
|
+
if isinstance(v, int):
|
|
110
|
+
return v
|
|
111
|
+
if isinstance(v, str):
|
|
112
|
+
low = v.strip().lower()
|
|
113
|
+
if low.isdigit():
|
|
114
|
+
return int(low)
|
|
115
|
+
return cls._STATUS_MAP.get(low, 0)
|
|
116
|
+
return int(cast("int", v))
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def proxy_url(self) -> str:
|
|
120
|
+
"""Full proxy URL (``protocol://login:password@host:port``)."""
|
|
121
|
+
auth = f"{self.login}:{self.password}@" if self.login else ""
|
|
122
|
+
proto = self.protocol or "socks5"
|
|
123
|
+
return f"{proto}://{auth}{self.host}:{self.port}"
|
|
124
|
+
|
|
125
|
+
@beartype
|
|
126
|
+
def format_with_template(self, template: str) -> str:
|
|
127
|
+
"""Format the proxy using a template string.
|
|
128
|
+
|
|
129
|
+
Supported placeholders: ``{protocol}``, ``{id}``, ``{login}``,
|
|
130
|
+
``{password}``, ``{ip}``, ``{port}``, ``{refresh_link}``,
|
|
131
|
+
``{name}``, ``{external_ip}``.
|
|
132
|
+
|
|
133
|
+
Example::
|
|
134
|
+
|
|
135
|
+
port.format_with_template("{ip}:{port}:{login}:{password}")
|
|
136
|
+
# "1.2.3.4:8080:u:p"
|
|
137
|
+
"""
|
|
138
|
+
refresh = f"https://api.asocks.com/v2/proxy/refresh-ip/{self.id}"
|
|
139
|
+
return (
|
|
140
|
+
template.replace("{protocol}", self.protocol or "socks5")
|
|
141
|
+
.replace("{id}", str(self.id))
|
|
142
|
+
.replace("{login}", self.login)
|
|
143
|
+
.replace("{password}", self.password)
|
|
144
|
+
.replace("{ip}", self.host)
|
|
145
|
+
.replace("{port}", str(self.port))
|
|
146
|
+
.replace("{refresh_link}", refresh)
|
|
147
|
+
.replace("{name}", self.name)
|
|
148
|
+
.replace("{external_ip}", self.external_ip)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def is_active(self) -> bool:
|
|
153
|
+
"""``True`` when the port status is :attr:`PortStatus.ACTIVE`."""
|
|
154
|
+
return self.status == PortStatus.ACTIVE # type: ignore[comparison-overlap]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class PortListResponse(BaseModel, extra="allow"):
|
|
158
|
+
"""Response wrapper for ``GET /v2/proxy/ports``.
|
|
159
|
+
|
|
160
|
+
The API may return either a flat list or a paginated envelope
|
|
161
|
+
(``{data: [...], total: N, ...}``); this model handles both.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
success: bool = Field(default=True)
|
|
165
|
+
message: list[PortInfo] | dict[str, object] = Field(default_factory=list[PortInfo])
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def items(self) -> list[PortInfo]:
|
|
169
|
+
"""Port list regardless of the envelope format."""
|
|
170
|
+
if isinstance(self.message, list):
|
|
171
|
+
return self.message
|
|
172
|
+
data = self.message.get("data", [])
|
|
173
|
+
if isinstance(data, list):
|
|
174
|
+
return [PortInfo.model_validate(d) for d in cast("list[object]", data)]
|
|
175
|
+
return []
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def total(self) -> int:
|
|
179
|
+
"""Total number of ports."""
|
|
180
|
+
if isinstance(self.message, dict):
|
|
181
|
+
raw_total = self.message.get("total", 0)
|
|
182
|
+
if isinstance(raw_total, int):
|
|
183
|
+
return raw_total
|
|
184
|
+
if isinstance(raw_total, str) and raw_total.strip().lstrip("-").isdigit():
|
|
185
|
+
return int(raw_total)
|
|
186
|
+
return 0
|
|
187
|
+
return len(self.message)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Request models for the ASocks API v2.
|
|
2
|
+
|
|
3
|
+
Bodies and query parameters for mutations: port creation, updates,
|
|
4
|
+
template management, and whitelist operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CreatePortRequest(BaseModel):
|
|
13
|
+
"""Body for ``POST /v2/proxy/create-port``.
|
|
14
|
+
|
|
15
|
+
Example::
|
|
16
|
+
|
|
17
|
+
req = CreatePortRequest(country_code="US", count=5, ttl=7)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
country_code: str = Field(default="", description="ISO country code (e.g. US)")
|
|
21
|
+
state: str = Field(default="", description="State name")
|
|
22
|
+
city: str = Field(default="", description="City name")
|
|
23
|
+
asn: int | None = Field(default=None, description="Autonomous System Number")
|
|
24
|
+
type_id: int = Field(default=1, description="Connection type ID")
|
|
25
|
+
proxy_type_id: int = Field(default=1, description="Proxy type ID")
|
|
26
|
+
name: str = Field(default="", description="Port display name")
|
|
27
|
+
server_port_type_id: int = Field(
|
|
28
|
+
default=0,
|
|
29
|
+
description="Server port type (0=shared, 1=dedicated)",
|
|
30
|
+
)
|
|
31
|
+
count: int = Field(default=1, ge=1, le=1000, description="Number of ports to create")
|
|
32
|
+
ttl: int = Field(default=1, ge=1, description="Time-to-live in days")
|
|
33
|
+
traffic_limit: int = Field(default=10, ge=1, description="Traffic limit in GB")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PortFilterParams(BaseModel):
|
|
37
|
+
"""Query parameters for ``GET /v2/proxy/ports``.
|
|
38
|
+
|
|
39
|
+
All fields are optional; omitted filters return all ports.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
id: int | None = Field(default=None, description="Filter by port ID")
|
|
43
|
+
proxy: str | None = Field(default=None, description="Filter by proxy address")
|
|
44
|
+
# Field names mirror the ASocks API's camelCase query params exactly
|
|
45
|
+
# (sent verbatim by ASocksClient.list_ports) — not a style violation.
|
|
46
|
+
countryName: str | None = Field(default=None, description="Filter by country name") # noqa: N815
|
|
47
|
+
stateName: str | None = Field(default=None, description="Filter by state name") # noqa: N815
|
|
48
|
+
cityName: str | None = Field(default=None, description="Filter by city name") # noqa: N815
|
|
49
|
+
asn: int | None = Field(default=None, description="Filter by ASN")
|
|
50
|
+
status: int | None = Field(default=None, description="Filter by status")
|
|
51
|
+
template_id: int | None = Field(default=None, description="Filter by template ID")
|
|
52
|
+
page: int = Field(default=1, ge=1)
|
|
53
|
+
per_page: int = Field(default=50, ge=1, le=200)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class UpdatePortRequest(BaseModel):
|
|
57
|
+
"""Body for ``PATCH /v2/proxy/update-port/{id}``."""
|
|
58
|
+
|
|
59
|
+
geo_country_ids: list[int] | None = Field(default=None, description="Country IDs")
|
|
60
|
+
geo_state_id: int | None = Field(default=None, description="State ID")
|
|
61
|
+
geo_city_id: int | None = Field(default=None, description="City ID")
|
|
62
|
+
asns: list[int] | None = Field(default=None, description="ASN list")
|
|
63
|
+
connection_type: str | None = Field(default=None, description="Connection type")
|
|
64
|
+
auth_type: str | None = Field(default=None, description="Auth type")
|
|
65
|
+
proxy_types: list[str] | None = Field(default=None, description="Proxy type list")
|
|
66
|
+
name: str | None = Field(default=None, description="Port name")
|
|
67
|
+
ttl: int | None = Field(default=None, description="TTL in days")
|
|
68
|
+
traffic_limit: int | None = Field(default=None, description="Traffic limit in GB")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class CreateTemplateRequest(BaseModel):
|
|
72
|
+
"""Body for ``POST /v2/proxy-template/create-template``."""
|
|
73
|
+
|
|
74
|
+
label: str = Field(description="Template label")
|
|
75
|
+
template: str = Field(description="Template pattern, e.g. {ip}:{port}")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class UpdateTemplateRequest(BaseModel):
|
|
79
|
+
"""Body for ``PATCH /v2/proxy-template/update-template``."""
|
|
80
|
+
|
|
81
|
+
label: str | None = Field(default=None, description="Template label")
|
|
82
|
+
template: str | None = Field(default=None, description="Template pattern")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class WhitelistAddRequest(BaseModel):
|
|
86
|
+
"""Body for ``POST /v2/whitelist/add``."""
|
|
87
|
+
|
|
88
|
+
ip: str = Field(description="IP address to whitelist")
|
|
89
|
+
description: str = Field(default="", description="Description for the IP entry")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Response models for the ASocks API v2.
|
|
2
|
+
|
|
3
|
+
Typed wrappers for API response payloads that don't fit into
|
|
4
|
+
the port or directory categories.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BalanceResponse(BaseModel, extra="allow"):
|
|
13
|
+
"""Response for ``GET /v2/user/balance``."""
|
|
14
|
+
|
|
15
|
+
success: bool = Field(default=True)
|
|
16
|
+
balance: float = Field(default=0, description="Account balance in USD")
|
|
17
|
+
balance_traffic: float = Field(default=0, description="Traffic balance")
|
|
18
|
+
all_available_traffic: float = Field(default=0, description="Total available traffic")
|
|
19
|
+
prepared_traffic_balance: float = Field(default=0, description="Prepared traffic balance")
|
|
20
|
+
balance_hold: float = Field(default=0, description="Held balance")
|