Skip to content

Commit 95a93d7

Browse files
authored
Merge pull request #28 from remnawave:development
Update API versions and add new models for HWID and users
2 parents 4db922b + b087d75 commit 95a93d7

File tree

13 files changed

+220
-19
lines changed

13 files changed

+220
-19
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ pip install git+https://github.com/remnawave/python-sdk.git@development
6363

6464
| Contract Version | Remnawave Panel Version |
6565
| ---------------- | ----------------------- |
66-
| 2.3.0 | >=2.3.0 |
66+
| 2.3.2 | >=2.3.0 |
67+
| 2.3.0 | >=2.3.0, <2.3.2 |
6768
| 2.2.6 | ==2.2.6 |
6869
| 2.2.3 | >=2.2.13 |
6970
| 2.1.19 | >=2.1.19, <2.2.0 |

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "remnawave"
3-
version = "2.3.0"
4-
description = "A Python SDK for interacting with the Remnawave API v2.3.0."
3+
version = "2.3.2"
4+
description = "A Python SDK for interacting with the Remnawave API v2.3.2."
55
authors = [
66
{name = "Artem",email = "[email protected]"}
77
]

remnawave/controllers/hwid.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Annotated
1+
from typing import Annotated, Optional
22
from uuid import UUID
33

44
from remnawave.models import (
@@ -8,9 +8,10 @@
88
GetHwidStatisticsResponseDto,
99
CreateHWIDUser,
1010
HWIDDeleteRequest,
11-
DeleteUserAllHwidDeviceRequestDto
11+
DeleteUserAllHwidDeviceRequestDto,
12+
GetTopUsersByHwidDevicesResponseDto
1213
)
13-
from rapid_api_client import Path, PydanticBody
14+
from rapid_api_client import Path, PydanticBody, Query
1415
from remnawave.rapid import AttributeBody, BaseController, post, get
1516

1617

@@ -62,3 +63,12 @@ async def get_hwid_user(
6263
) -> GetUserHwidDevicesResponseDto:
6364
"""Get a user HWID device"""
6465
...
66+
67+
@get("/hwid/devices/top-users", response_class=GetTopUsersByHwidDevicesResponseDto)
68+
async def get_top_users_by_hwid_devices(
69+
self,
70+
size: Annotated[Optional[int], Query(default=None, description="Page size for pagination")] = None,
71+
start: Annotated[Optional[int], Query(default=None, description="Offset for pagination")] = None,
72+
) -> GetTopUsersByHwidDevicesResponseDto:
73+
"""Get top users by HWID devices"""
74+
...

remnawave/models/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@
8484
HWIDUserResponseDto, # Legacy alias
8585
HWIDUserResponseDtoList, # Legacy alias
8686
GetHwidStatisticsResponseDto,
87-
DeleteUserAllHwidDeviceRequestDto
87+
DeleteUserAllHwidDeviceRequestDto,
88+
GetTopUsersByHwidDevicesResponseDto,
89+
TopUserByHwidDevicesDto,
90+
TopUsersByHwidDevicesData,
8891
)
8992
from .inbounds import (
9093
AllInboundsData,
@@ -351,6 +354,7 @@
351354
CustomErrorEventDto,
352355
CrmEventDto,
353356
WebhookPayloadDto,
357+
UserTrafficDto
354358
)
355359
from .passkeys import (
356360
DeletePasskeyRequestDto,
@@ -545,6 +549,9 @@
545549
"HWIDUserResponseDtoList", # Legacy alias
546550
"GetHwidStatisticsResponseDto",
547551
"DeleteUserAllHwidDeviceRequestDto",
552+
"GetTopUsersByHwidDevicesResponseDto",
553+
"TopUserByHwidDevicesDto",
554+
"TopUsersByHwidDevicesData",
548555
# Bandwidth stats models
549556
"GetNodeUserUsageByRangeResponseDto",
550557
"GetNodesRealtimeUsageResponseDto",
@@ -712,6 +719,7 @@
712719
"BaseUserDto",
713720
"UserDto",
714721
"UserEventDto",
722+
"UserTrafficDto",
715723

716724
# HWID DEVICES
717725
"HwidUserDeviceDto",

remnawave/models/bandwidthstats.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ def __iter__(self):
2323

2424
def __getitem__(self, item):
2525
return self.root[item]
26+
27+
def __bool__(self):
28+
"""Return True if list is not empty"""
29+
return bool(self.root)
30+
31+
def __len__(self):
32+
"""Return length of list"""
33+
return len(self.root)
2634

2735

2836
class GetNodesUsageByRangeResponseDto(RootModel[List[NodeUsageResponseDto]]):
@@ -31,6 +39,14 @@ def __iter__(self):
3139

3240
def __getitem__(self, item):
3341
return self.root[item]
42+
43+
def __bool__(self):
44+
"""Return True if list is not empty"""
45+
return bool(self.root)
46+
47+
def __len__(self):
48+
"""Return length of list"""
49+
return len(self.root)
3450

3551

3652
class NodeRealtimeUsageResponseDto(BaseModel):
@@ -51,6 +67,14 @@ def __iter__(self):
5167

5268
def __getitem__(self, item):
5369
return self.root[item]
70+
71+
def __bool__(self):
72+
"""Return True if list is not empty"""
73+
return bool(self.root)
74+
75+
def __len__(self):
76+
"""Return length of list"""
77+
return len(self.root)
5478

5579

5680
class GetNodesRealtimeUsageResponseDto(RootModel[List[NodeRealtimeUsageResponseDto]]):
@@ -59,6 +83,14 @@ def __iter__(self):
5983

6084
def __getitem__(self, item):
6185
return self.root[item]
86+
87+
def __bool__(self):
88+
"""Return True if list is not empty"""
89+
return bool(self.root)
90+
91+
def __len__(self):
92+
"""Return length of list"""
93+
return len(self.root)
6294

6395

6496
class UserUsageByRangeItem(BaseModel):
@@ -75,6 +107,14 @@ def __iter__(self):
75107

76108
def __getitem__(self, item):
77109
return self.root[item]
110+
111+
def __bool__(self):
112+
"""Return True if list is not empty"""
113+
return bool(self.root)
114+
115+
def __len__(self):
116+
"""Return length of list"""
117+
return len(self.root)
78118

79119

80120
class NodeUserUsageItem(BaseModel):
@@ -91,3 +131,11 @@ def __iter__(self):
91131

92132
def __getitem__(self, item):
93133
return self.root[item]
134+
135+
def __bool__(self):
136+
"""Return True if list is not empty"""
137+
return bool(self.root)
138+
139+
def __len__(self):
140+
"""Return length of list"""
141+
return len(self.root)

remnawave/models/external_squads.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ class ExternalSquadSubscriptionSettingsDto(BaseModel):
3939
happ_routing: Optional[str] = Field(None, alias="happRouting")
4040
randomize_hosts: bool = Field(alias="randomizeHosts")
4141

42-
43-
# НОВЫЕ МОДЕЛИ
4442
class ExternalSquadHostOverridesDto(BaseModel):
4543
"""External squad host overrides"""
4644
server_description: Optional[str] = Field(None, alias="serverDescription", max_length=30)
@@ -61,7 +59,7 @@ class ExternalSquadDto(BaseModel):
6159

6260

6361
# Request/Response models
64-
class GetExternalSquadsResponseDto(ExternalSquadDto):
62+
class GetExternalSquadsResponseDto(BaseModel):
6563
"""Response with all external squads"""
6664
total: int = Field(alias="total")
6765
external_squads: List[ExternalSquadDto] = Field(alias="externalSquads")

remnawave/models/hosts.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class UpdateHostRequestDto(BaseModel):
4343
tag: Optional[Annotated[str, StringConstraints(max_length=32, pattern=r"^[A-Z0-9_:]+$")]] = None
4444
is_hidden: Optional[bool] = Field(None, serialization_alias="isHidden")
4545
override_sni_from_address: Optional[bool] = Field(None, serialization_alias="overrideSniFromAddress")
46+
keep_blank_sni: Optional[bool] = Field(None, serialization_alias="keepBlankSni")
4647
vless_route_id: Optional[int] = Field(None, serialization_alias="vlessRouteId", ge=0, le=65535)
4748
shuffle_host: Optional[bool] = Field(None, serialization_alias="shuffleHost")
4849
mihomo_x25519: Optional[bool] = Field(None, serialization_alias="mihomoX25519")
@@ -83,6 +84,7 @@ class HostResponseDto(BaseModel):
8384
security_layer: SecurityLayer = Field(SecurityLayer.DEFAULT, alias="securityLayer")
8485
is_hidden: bool = Field(False, alias="isHidden")
8586
override_sni_from_address: bool = Field(False, alias="overrideSniFromAddress")
87+
keep_blank_sni: bool = Field(False, alias="keepBlankSni")
8688
allow_insecure: bool = Field(False, alias="allowInsecure")
8789
xray_json_template_uuid: Optional[UUID] = Field(None, alias="xrayJsonTemplateUuid")
8890
excluded_internal_squads: List[UUID] = Field(default_factory=list, alias="excludedInternalSquads")
@@ -116,6 +118,7 @@ class CreateHostRequestDto(BaseModel):
116118
security_layer: SecurityLayer = Field(SecurityLayer.DEFAULT, serialization_alias="securityLayer")
117119
is_hidden: bool = Field(False, serialization_alias="isHidden")
118120
override_sni_from_address: bool = Field(False, serialization_alias="overrideSniFromAddress")
121+
keep_blank_sni: bool = Field(False, serialization_alias="keepBlankSni")
119122
xray_json_template_uuid: Optional[UUID] = Field(None, serialization_alias="xrayJsonTemplateUuid")
120123
excluded_internal_squads: List[UUID] = Field(default_factory=list, serialization_alias="excludedInternalSquads")
121124

@@ -161,6 +164,14 @@ def __iter__(self):
161164

162165
def __getitem__(self, item):
163166
return self.root[item]
167+
168+
def __bool__(self):
169+
"""Return True if list is not empty"""
170+
return bool(self.root)
171+
172+
def __len__(self):
173+
"""Return length of list"""
174+
return len(self.root)
164175

165176

166177
class GetOneHostResponseDto(HostResponseDto):

remnawave/models/hwid.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,27 @@ class GetHwidStatisticsResponseDto(HwidStatisticsData):
7676

7777
class DeleteUserAllHwidDeviceRequestDto(BaseModel):
7878
user_uuid: UUID = Field(serialization_alias="userUuid")
79+
80+
class TopUserByHwidDevicesDto(BaseModel):
81+
"""Top user by HWID devices"""
82+
user_uuid: UUID = Field(alias="userUuid")
83+
id: int
84+
username: str
85+
devices_count: int = Field(alias="devicesCount")
86+
87+
88+
class TopUsersByHwidDevicesData(BaseModel):
89+
"""Top users by HWID devices data"""
90+
users: list[TopUserByHwidDevicesDto]
91+
total: int
92+
93+
94+
class GetTopUsersByHwidDevicesResponseDto(TopUsersByHwidDevicesData):
95+
"""Response for get top users by HWID devices"""
96+
pass
7997

8098
# Legacy aliases for backward compatibility
8199
CreateHWIDUser = CreateUserHwidDeviceRequestDto
82100
HWIDUserResponseDto = HwidDeviceDto
83101
HWIDUserResponseDtoList = HwidDevicesData
84-
HWIDDeleteRequest = DeleteUserHwidDeviceRequestDto
102+
HWIDDeleteRequest = DeleteUserHwidDeviceRequestDto

remnawave/models/inbounds.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ def __iter__(self):
4141

4242
def __getitem__(self, item):
4343
return self.root[item]
44+
45+
def __bool__(self):
46+
"""Return True if list is not empty"""
47+
return bool(self.root)
48+
49+
def __len__(self):
50+
"""Return length of list"""
51+
return len(self.root)
4452

4553

4654
class FullInboundStatistic(BaseModel):
@@ -66,6 +74,14 @@ def __iter__(self):
6674

6775
def __getitem__(self, item):
6876
return self.root[item]
77+
78+
def __bool__(self):
79+
"""Return True if list is not empty"""
80+
return bool(self.root)
81+
82+
def __len__(self):
83+
"""Return length of list"""
84+
return len(self.root)
6985

7086

7187
# Legacy aliases for backward compatibility

remnawave/models/nodes.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,15 @@ def __iter__(self):
169169

170170
def __getitem__(self, item):
171171
return self.root[item]
172+
173+
def __bool__(self):
174+
"""Return True if list is not empty"""
175+
return bool(self.root)
176+
177+
def __len__(self):
178+
"""Return length of list"""
179+
return len(self.root)
180+
172181

173182

174183
class EnableNodeResponseDto(NodeResponseDto):
@@ -195,6 +204,14 @@ def __iter__(self):
195204

196205
def __getitem__(self, item):
197206
return self.root[item]
207+
208+
def __bool__(self):
209+
"""Return True if list is not empty"""
210+
return bool(self.root)
211+
212+
def __len__(self):
213+
"""Return length of list"""
214+
return len(self.root)
198215

199216

200217
class DeleteNodeResponseDto(BaseModel):

0 commit comments

Comments
 (0)