ddapi 0.9.3__tar.gz → 0.12.0__tar.gz

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.
@@ -1,21 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddapi
3
- Version: 0.9.3
3
+ Version: 0.12.0
4
4
  Summary: DDnet api
5
5
  Home-page: https://github.com/ByFox213/ddapi
6
6
  Author: ByFox
7
+ License: MIT
7
8
  Project-URL: Github, https://github.com/ByFox213/ddapi
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
9
  Requires-Python: >=3.8
12
10
  Description-Content-Type: text/markdown
13
11
  License-File: LICENSE
14
12
  Dynamic: author
15
- Dynamic: classifier
16
13
  Dynamic: description
17
14
  Dynamic: description-content-type
18
15
  Dynamic: home-page
16
+ Dynamic: license
19
17
  Dynamic: license-file
20
18
  Dynamic: project-url
21
19
  Dynamic: requires-python
@@ -41,28 +39,28 @@ DDnet
41
39
 
42
40
  ```python
43
41
  import asyncio
42
+
44
43
  from ddapi import DDnetApi, DDPlayer
45
44
 
46
45
 
47
46
  async def main():
48
- obj = DDnetApi()
49
- nickname = "Cor"
50
- user: DDPlayer = await obj.player(nickname)
51
- if user is None:
52
- return "Player not found"
53
-
54
- print(f"{user.player}: {user.points.points}")
55
- # Cor: 31473
56
- await obj.close() # Closing client Not necessary
57
- assert isinstance(user, DDPlayer)
47
+ async with DDnetApi() as obj:
48
+ user: DDPlayer = await obj.player("Cor")
49
+ if user is not None:
50
+ text = f"{user.player}: {user.points.points}/{user.points.total}"
51
+ percent = round(user.points.total / user.points.points * 100)
52
+ print(f"{text}({percent}%)")
53
+ # Cor: 32950/32950(100%)
54
+ else:
55
+ print("Player not found")
58
56
 
59
57
 
60
58
  asyncio.run(main())
61
59
  ```
62
60
 
63
61
  Tested on Python
64
- | 3.9 | 3.10 | 3.11 | 3.12 |
65
- |-----|------|------|------|
66
- | ? | ? | ✅ | ✅ |
62
+ | 3.10 | 3.11 | 3.12 |
63
+ |------|------|------|
64
+ | ✅ | ✅ | ✅ |
67
65
 
68
66
  [other examples](https://github.com/ByFox213/ddapi/tree/main/example)
ddapi-0.12.0/README.md ADDED
@@ -0,0 +1,45 @@
1
+ ddapi is an api from the ddnet and qwik website
2
+
3
+ ## Installation
4
+
5
+ ```bash
6
+ pip install ddapi
7
+ ```
8
+
9
+ or
10
+
11
+ ```bash
12
+ pip install git+https://github.com/ByFox213/ddapi
13
+ ```
14
+
15
+ ## Usage/Examples
16
+
17
+ DDnet
18
+
19
+ ```python
20
+ import asyncio
21
+
22
+ from ddapi import DDnetApi, DDPlayer
23
+
24
+
25
+ async def main():
26
+ async with DDnetApi() as obj:
27
+ user: DDPlayer = await obj.player("Cor")
28
+ if user is not None:
29
+ text = f"{user.player}: {user.points.points}/{user.points.total}"
30
+ percent = round(user.points.total / user.points.points * 100)
31
+ print(f"{text}({percent}%)")
32
+ # Cor: 32950/32950(100%)
33
+ else:
34
+ print("Player not found")
35
+
36
+
37
+ asyncio.run(main())
38
+ ```
39
+
40
+ Tested on Python
41
+ | 3.10 | 3.11 | 3.12 |
42
+ |------|------|------|
43
+ | ✅ | ✅ | ✅ |
44
+
45
+ [other examples](https://github.com/ByFox213/ddapi/tree/main/example)
@@ -1,7 +1,7 @@
1
1
  """the module is made for the convenience of users to download and use"""
2
2
  from .main import DDnetApi, DDstats, Status
3
- from .scheme import *
3
+ from .scheme import * # noqa: F403
4
4
 
5
- __version__ = "0.9.3"
6
- __author__ = 'ByFox'
5
+ __version__ = "0.12.0"
6
+ __author__ = "ByFox"
7
7
  __LICENSE__ = "MIT"
@@ -1,43 +1,66 @@
1
+ import asyncio
1
2
  from abc import ABC
2
- from typing import Optional, TypeVar, Type, Any
3
+ from typing import Optional, Type, Any
3
4
 
4
5
  import aiohttp
5
- from aiohttp import ClientSession, ClientConnectorError
6
- from aiohttp.typedefs import DEFAULT_JSON_DECODER
6
+ from aiohttp import ClientSession
7
7
 
8
- ModelType = TypeVar('ModelType', bound='Model')
8
+ from ddapi.types import T
9
+
10
+ try:
11
+ import orjson
12
+
13
+ DEFAULT_JSON_DECODER = orjson.loads
14
+ except ImportError:
15
+ from aiohttp.typedefs import DEFAULT_JSON_DECODER
9
16
 
10
17
 
11
18
  class API(ABC):
12
19
  def __init__(
13
20
  self,
14
21
  session: ClientSession = None,
22
+ autoclose: bool = True,
15
23
  json_loads: Any = DEFAULT_JSON_DECODER
16
24
  ):
17
25
  self.__session = session
18
26
  self.json_loads = json_loads
27
+ self.autoclose = autoclose
19
28
 
20
- @staticmethod
21
- def powered() -> None:
22
- return None
29
+ if self.__session is None:
30
+ self.__session = aiohttp.ClientSession()
23
31
 
24
- async def _get(self, url: str) -> Optional[dict]:
25
- """Send a GET request to the given URL and return the response as JSON."""
32
+ async def __aenter__(self):
26
33
  if self.__session is None:
27
34
  self.__session = aiohttp.ClientSession()
35
+ return self
28
36
 
29
- try:
30
- async with self.__session.get(url) as req:
31
- return await req.json(loads=self.json_loads) if req.status == 200 else None
32
- except ClientConnectorError:
33
- return
37
+ async def __aexit__(self, _exc_type, _exc_val, _exc_tb):
38
+ if self.autoclose:
39
+ await self.close()
40
+
41
+ def __del__(self):
42
+ if self.__session is not None and not self.__session.closed:
43
+ loop = asyncio.get_event_loop()
44
+ if loop.is_running():
45
+ loop.create_task(self.close())
46
+ else:
47
+ loop.run_until_complete(self.close())
48
+
49
+ async def _get(self, url: str) -> Optional[dict]:
50
+ """Send a GET request to the given URL and return the response as JSON."""
51
+ async with self.__session.get(url) as req:
52
+ return (
53
+ await req.json(loads=self.json_loads)
54
+ if req.status == 200
55
+ else None
56
+ )
34
57
 
35
58
  async def _generate_model_instance(
36
59
  self,
37
60
  url: str,
38
- model: Type[ModelType],
61
+ model: Type[T],
39
62
  k: Optional[str] = None
40
- ) -> Optional[ModelType]:
63
+ ) -> Optional[T]:
41
64
  """Generate a model instance from the given URL and optional keyword arguments.
42
65
 
43
66
  Args:
@@ -64,3 +87,11 @@ class API(ABC):
64
87
  if self.__session is not None:
65
88
  await self.__session.close()
66
89
  self.__session = None
90
+
91
+ @property
92
+ def domain(self) -> str:
93
+ return "ddnet.org"
94
+
95
+ @staticmethod
96
+ def powered() -> None:
97
+ return None
@@ -1,9 +1,13 @@
1
+ from enum import StrEnum
1
2
  from urllib.parse import quote
2
3
 
3
4
  from .api import API
4
- from .scheme import *
5
+ from .scheme import DDPlayer, DDStatus, ReleasesMaps, Query, QueryMap, QueryMapper, \
6
+ Master, DMap, DProfile, SMap, Maps, Player, Info, BannedMaster, List, ListData, \
7
+ ListPl, ListPlData, MasterTw, ServerTwOne, Charts, ChartEnum, Stats
5
8
 
6
9
  __all__ = (
10
+ "MasterEnum",
7
11
  "DDnetApi",
8
12
  "DDstats",
9
13
  "Status"
@@ -11,16 +15,16 @@ __all__ = (
11
15
 
12
16
 
13
17
  class MasterEnum(StrEnum):
14
- master1 = "master1"
15
- master2 = "master2"
16
- master3 = "master3"
17
- master4 = "master4"
18
+ master1 = "1"
19
+ master2 = "2"
20
+ master3 = "3"
21
+ master4 = "4"
18
22
 
19
23
 
20
24
  class DDnetApi(API):
21
- def __init__(self):
22
- super().__init__()
23
- self.domain = "ddnet.org"
25
+ @property
26
+ def domain(self) -> str:
27
+ return "ddnet.org"
24
28
 
25
29
  @staticmethod
26
30
  def powered() -> str:
@@ -57,19 +61,19 @@ class DDnetApi(API):
57
61
  return await self._generate_model_instance(
58
62
  f"https://{self.domain}/maps/?query={quote(map_name)}",
59
63
  QueryMap,
60
- 'maps',
64
+ "maps",
61
65
  )
62
66
 
63
67
  async def query_mapper(self, player: str) -> QueryMapper:
64
68
  return await self._generate_model_instance(
65
69
  f"https://{self.domain}/maps/?qmapper={quote(player)}",
66
70
  QueryMapper,
67
- 'players',
71
+ "players",
68
72
  )
69
73
 
70
- async def master(self, master: MasterEnum = MasterEnum.master1) -> Master:
74
+ async def master(self, number: MasterEnum = MasterEnum.master1) -> Master:
71
75
  return await self._generate_model_instance(
72
- f"https://{master}.{self.domain}/ddnet/15/servers.json",
76
+ f"https://master{number}.{self.domain}/ddnet/15/servers.json",
73
77
  Master
74
78
  )
75
79
 
@@ -81,14 +85,34 @@ class DDnetApi(API):
81
85
 
82
86
 
83
87
  class DDstats(API):
84
- def __init__(self):
85
- super().__init__()
86
- self.domain = 'ddstats.tw'
88
+ @property
89
+ def domain(self) -> str:
90
+ return "ddstats.tw"
87
91
 
88
92
  @staticmethod
89
93
  def powered() -> str:
90
94
  return DDstats().domain
91
95
 
96
+ async def profile(self, player_name: str) -> DProfile:
97
+ return await self._generate_model_instance(
98
+ f"https://{self.domain}/profile/json?player={quote(player_name)}",
99
+ DProfile,
100
+ "profile"
101
+ )
102
+
103
+ async def map(self, map_name: str) -> SMap:
104
+ return await self._generate_model_instance(
105
+ f"https://{self.domain}/map/json?map={quote(map_name)}",
106
+ SMap
107
+ )
108
+
109
+ async def maps(self) -> Maps:
110
+ return await self._generate_model_instance(
111
+ f"https://{self.domain}/maps/json",
112
+ Maps,
113
+ "maps"
114
+ )
115
+
92
116
  async def player(self, player_name: str) -> Player:
93
117
  """Fetch player data from the API and return it as a dictionary."""
94
118
  return await self._generate_model_instance(
@@ -98,9 +122,9 @@ class DDstats(API):
98
122
 
99
123
 
100
124
  class Status(API):
101
- def __init__(self):
102
- super().__init__()
103
- self.domain = 'status.tw'
125
+ @property
126
+ def domain(self) -> str:
127
+ return "status.tw"
104
128
 
105
129
  @staticmethod
106
130
  def powered() -> str:
@@ -172,7 +196,12 @@ class Status(API):
172
196
  ServerTwOne,
173
197
  )
174
198
 
175
- async def server_charts(self, ip: str, port: int | str, chart: ChartEnum = ChartEnum.day) -> Charts:
199
+ async def server_charts(
200
+ self,
201
+ ip: str,
202
+ port: int | str,
203
+ chart: ChartEnum = ChartEnum.day
204
+ ) -> Charts:
176
205
  return await self._generate_model_instance(
177
206
  f"https://api.{self.domain}/server/{quote(ip)}/{port}/chart/{chart}",
178
207
  Charts,
@@ -180,11 +209,11 @@ class Status(API):
180
209
  )
181
210
 
182
211
  async def server_history(self, ip: str, port: int | str) -> None:
183
- # https://api.status.tw/server/{ip}/{port}/history
212
+ # TODO: https://api.status.tw/server/{ip}/{port}/history
184
213
  raise NotImplementedError()
185
214
 
186
215
  async def server_stats(self, ip: str, port: int | str) -> None:
187
- # https://api.status.tw/server/{ip}/{port}/stats
216
+ # TODO: https://api.status.tw/server/{ip}/{port}/stats
188
217
  raise NotImplementedError()
189
218
 
190
219
  async def clan_list(self) -> ListPl:
@@ -214,7 +243,7 @@ class Status(API):
214
243
  )
215
244
 
216
245
  async def player_history(self, name: str) -> None:
217
- # https://api.status.tw/player/name/{name}/history
246
+ # TODO: https://api.status.tw/player/name/{name}/history
218
247
  raise NotImplementedError()
219
248
 
220
249
  async def stats(self) -> Stats:
@@ -224,9 +253,9 @@ class Status(API):
224
253
  )
225
254
 
226
255
  async def stats_players(self, t: str) -> None:
227
- # https://api.status.tw/stats/players/day | week | month
256
+ # TODO: https://api.status.tw/stats/players/day | week | month
228
257
  raise NotImplementedError()
229
258
 
230
259
  async def stats_servers(self, t: str) -> None:
231
- # https://api.status.tw/stats/servers/week | month
260
+ # TODO: https://api.status.tw/stats/servers/week | month
232
261
  raise NotImplementedError()
@@ -0,0 +1,4 @@
1
+ from .base import * # noqa: F403
2
+ from .ddnet import * # noqa: F403
3
+ from .ddstats_tw import * # noqa: F403
4
+ from .status_tw import * # noqa: F403
@@ -0,0 +1,21 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from ddapi import DData
6
+
7
+
8
+ class DDType(BaseModel):
9
+ Novice: Optional[DData] = None
10
+ Moderate: Optional[DData] = None
11
+ Brutal: Optional[DData] = None
12
+ Insane: Optional[DData] = None
13
+ Dummy: Optional[DData] = None
14
+ DDmaX_Easy: Optional[DData] = Field(default=None, validation_alias="DDmaX.Easy")
15
+ DDmaX_Next: Optional[DData] = Field(default=None, validation_alias="DDmaX.Next")
16
+ DDmaX_Nut: Optional[DData] = Field(default=None, validation_alias="DDmaX.Nut")
17
+ DDmaX_Pro: Optional[DData] = Field(default=None, validation_alias="DDmaX.Pro")
18
+ Oldschool: Optional[DData] = Field(default=None)
19
+ Race: Optional[DData] = None
20
+ Solo: Optional[DData] = None
21
+ total: Optional[DData] = None
@@ -5,7 +5,9 @@ from typing import Optional, Any
5
5
 
6
6
  from pydantic import BaseModel, Field
7
7
 
8
- rm_list = ["DD-Persian", "/vDQMHSss8W", '']
8
+ from .base import DDType
9
+
10
+ rm_list = ["DD-Persian", "/vDQMHSss8W", ""]
9
11
 
10
12
 
11
13
  class DPoints(BaseModel):
@@ -94,22 +96,6 @@ class DData(BaseModel):
94
96
  maps: Optional[dict] = Field(default=None)
95
97
 
96
98
 
97
- class DDType(BaseModel):
98
- Novice: Optional[DData] = None
99
- Moderate: Optional[DData] = None
100
- Brutal: Optional[DData] = None
101
- Insane: Optional[DData] = None
102
- Dummy: Optional[DData] = None
103
- DDmaX_Easy: Optional[DData] = Field(default=None, validation_alias='DDmaX.Easy')
104
- DDmaX_Next: Optional[DData] = Field(default=None, validation_alias='DDmaX.Next')
105
- DDmaX_Nut: Optional[DData] = Field(default=None, validation_alias='DDmaX.Nut')
106
- DDmaX_Pro: Optional[DData] = Field(default=None, validation_alias='DDmaX.Pro')
107
- Oldschool: Optional[DData] = Field(default=None)
108
- Race: Optional[DData] = None
109
- Solo: Optional[DData] = None
110
- total: Optional[DData] = None
111
-
112
-
113
99
  class Activity(BaseModel):
114
100
  date: str
115
101
  hours_played: int
@@ -220,7 +206,7 @@ class Master(BaseModel):
220
206
  client.clan
221
207
  for server in self.servers
222
208
  for client in server.info.clients
223
- if client != ''
209
+ if client != ""
224
210
  )
225
211
 
226
212
  for i in remove_list:
@@ -297,3 +283,29 @@ class QueryMapData(BaseModel):
297
283
 
298
284
  class QueryMap(BaseModel):
299
285
  maps: list[QueryMapData]
286
+
287
+
288
+ class DDStatusData(BaseModel):
289
+ name: str = None
290
+ type: str = None
291
+ host: str = None
292
+ location: str = None
293
+ online4: bool = None
294
+ online6: bool = None
295
+ uptime: str = None
296
+ load: float = None
297
+ network_rx: int = None
298
+ network_tx: int = None
299
+ packets_rx: int = None
300
+ packets_tx: int = None
301
+ cpu: int = None
302
+ memory_total: int = None
303
+ memory_used: int = None
304
+ swap_total: int = None
305
+ swap_used: int = None
306
+ hdd_total: int = None
307
+ hdd_used: int = None
308
+
309
+
310
+ class DDStatus(BaseModel):
311
+ servers: list[DDStatusData]
@@ -4,7 +4,7 @@ from typing import Optional
4
4
 
5
5
  from pydantic import BaseModel, Field
6
6
 
7
- rm_list = ["DD-Persian", "/vDQMHSss8W", '']
7
+ from .base import DDType
8
8
 
9
9
 
10
10
  class PData(BaseModel):
@@ -12,22 +12,6 @@ class PData(BaseModel):
12
12
  points: int
13
13
 
14
14
 
15
- class PPoints(BaseModel):
16
- Novice: Optional[PData] = None
17
- Moderate: Optional[PData] = None
18
- Brutal: Optional[PData] = None
19
- Insane: Optional[PData] = None
20
- Dummy: Optional[PData] = None
21
- DDmaX_Easy: Optional[PData] = Field(default=None, validation_alias='DDmaX.Easy')
22
- DDmaX_Next: Optional[PData] = Field(default=None, validation_alias='DDmaX.Next')
23
- DDmaX_Nut: Optional[PData] = Field(default=None, validation_alias='DDmaX.Nut')
24
- DDmaX_Pro: Optional[PData] = Field(default=None, validation_alias='DDmaX.Pro')
25
- Oldschool: Optional[PData] = None
26
- Race: Optional[PData] = None
27
- Solo: Optional[PData] = None
28
- total: Optional[PData] = None
29
-
30
-
31
15
  class PointG(BaseModel):
32
16
  date: str
33
17
  points: int
@@ -45,6 +29,10 @@ class Profile(BaseModel):
45
29
  skin_color_feet: Optional[int] = None
46
30
 
47
31
 
32
+ class DProfile(BaseModel):
33
+ profile: Profile
34
+
35
+
48
36
  class DDMap(BaseModel):
49
37
  map: str
50
38
  server: str
@@ -113,9 +101,9 @@ class Points(BaseModel):
113
101
  weekly_points: Optional[PData] = None
114
102
  monthly_points: Optional[PData] = None
115
103
  yearly_points: Optional[PData] = None
116
- points: PPoints
117
- rank_points: PPoints
118
- team_points: PPoints
104
+ points: DDType
105
+ rank_points: DDType
106
+ team_points: DDType
119
107
 
120
108
 
121
109
  class RecentActivity(BaseModel):
@@ -173,7 +161,7 @@ class RecentTop10s(BaseModel):
173
161
  map: str
174
162
  time: float
175
163
  rank: int
176
- timestamp: Optional[datetime]
164
+ timestamp: Optional[datetime] = None
177
165
  server: str
178
166
 
179
167
 
@@ -205,27 +193,74 @@ class Player(BaseModel):
205
193
  recent_top_10s: Optional[list[RecentTop10s]] = None
206
194
 
207
195
 
208
- class DDStatusData(BaseModel):
209
- name: str = None
210
- type: str = None
211
- host: str = None
212
- location: str = None
213
- online4: bool = None
214
- online6: bool = None
215
- uptime: str = None
216
- load: float = None
217
- network_rx: int = None
218
- network_tx: int = None
219
- packets_rx: int = None
220
- packets_tx: int = None
221
- cpu: int = None
222
- memory_total: int = None
223
- memory_used: int = None
224
- swap_total: int = None
225
- swap_used: int = None
226
- hdd_total: int = None
227
- hdd_used: int = None
228
-
229
-
230
- class DDStatus(BaseModel):
231
- servers: list[DDStatusData]
196
+ class Maps(BaseModel):
197
+ maps: list[DDMap]
198
+
199
+
200
+ class InfoSMap(BaseModel):
201
+ map: DDMap
202
+ finishes: int
203
+ finishes_rank: int
204
+ median_time: float
205
+
206
+
207
+ class RankingSMap(BaseModel):
208
+ rank: int
209
+ timestamp: Optional[datetime] = None
210
+ name: str
211
+ time: float
212
+ map: str
213
+ server: str
214
+
215
+
216
+ class TeamRankingSMap(BaseModel):
217
+ rank: int
218
+ timestamp: Optional[datetime] = None
219
+ id: list[int]
220
+ players: list[str]
221
+ time: float
222
+ map: str
223
+ server: str
224
+
225
+
226
+ class TimeCpsSMap(BaseModel):
227
+ name: str
228
+ cp1: float
229
+ cp2: float
230
+ cp3: float
231
+ cp4: float
232
+ cp5: float
233
+ cp6: float
234
+ cp7: float
235
+ cp8: float
236
+ cp9: float
237
+ cp10: float
238
+ cp11: float
239
+ cp12: float
240
+ cp13: float
241
+ cp14: float
242
+ cp15: float
243
+ cp16: float
244
+ cp17: float
245
+ cp18: float
246
+ cp19: float
247
+ cp20: float
248
+ cp21: float
249
+ cp22: float
250
+ cp23: float
251
+ cp24: float
252
+ cp25: float
253
+ time: float
254
+
255
+
256
+ class PlaytimeSMap(BaseModel):
257
+ name: str
258
+ seconds_played: int
259
+
260
+
261
+ class SMap(BaseModel):
262
+ info: InfoSMap
263
+ rankings: list[RankingSMap]
264
+ team_rankings: list[TeamRankingSMap]
265
+ time_cps: list[TimeCpsSMap]
266
+ playtime: list[PlaytimeSMap]
@@ -0,0 +1,131 @@
1
+ # Status.tw
2
+ from datetime import datetime
3
+ from enum import StrEnum
4
+ from typing import Optional
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class ChartEnum(StrEnum):
10
+ day = "day"
11
+ week = "week"
12
+ month = "month"
13
+
14
+
15
+ class DataTw(BaseModel):
16
+ name: str
17
+ created_at: datetime = Field(serialization_alias="createdAt")
18
+
19
+
20
+ class VersionTw(BaseModel):
21
+ version: str
22
+ created_at: datetime = Field(serialization_alias="createdAt")
23
+
24
+
25
+ class CountryTw(BaseModel):
26
+ identifier: str
27
+ code: int
28
+ icon_url: str = Field(serialization_alias="iconUrl")
29
+
30
+
31
+ class ClientTw(BaseModel):
32
+ name: str
33
+ country: CountryTw
34
+ score: int
35
+ is_player: bool = Field(serialization_alias="isPlayer")
36
+ is_afk: bool = Field(serialization_alias="isAfk")
37
+ team: int
38
+ created_at: datetime = Field(serialization_alias="createdAt")
39
+ clan: DataTw | None
40
+ server: "ServerTw" = None
41
+
42
+
43
+ class ServerTw(BaseModel):
44
+ ip: str
45
+ port: int
46
+ name: str
47
+ num_clients: int = Field(serialization_alias="numClients")
48
+ num_players: int = Field(serialization_alias="numPlayers")
49
+ max_clients: int = Field(serialization_alias="maxClients")
50
+ max_players: int = Field(serialization_alias="maxPlayers")
51
+ has_password: bool = Field(serialization_alias="hasPassword")
52
+ supports6: bool
53
+ supports7: bool
54
+ created_at: datetime = Field(serialization_alias="createdAt")
55
+ relevance_score: int = Field(serialization_alias="relevanceScore")
56
+ client_score_kind: str | None = Field(serialization_alias="clientScoreKind")
57
+ logo_url: str = Field(serialization_alias="logoUrl")
58
+ map: DataTw
59
+ website: Optional[str] = None
60
+ discord_invite: Optional[str] = Field(None, serialization_alias="discordInvite")
61
+ description: Optional[str] = None
62
+ game_type: DataTw = Field(serialization_alias="gameType")
63
+ version: VersionTw
64
+ clients: list[ClientTw]
65
+
66
+
67
+ class ServerTwOne(BaseModel):
68
+ ip: str
69
+ port: int
70
+ online: ServerTw
71
+
72
+
73
+ class Chart(BaseModel):
74
+ avg_players: float = Field(serialization_alias="avgPlayers")
75
+ max_players: int = Field(serialization_alias="maxPlayers")
76
+ min_players: int = Field(serialization_alias="minPlayers")
77
+ timestamp: datetime
78
+
79
+
80
+ class Charts(BaseModel):
81
+ charts: list[Chart]
82
+
83
+
84
+ class MasterTw(BaseModel):
85
+ servers: list[ServerTw]
86
+
87
+
88
+ class Info(BaseModel):
89
+ app: str
90
+ version: str
91
+
92
+
93
+ class BannedMasterData(BaseModel):
94
+ ip: str
95
+ is_active: bool = Field(serialization_alias="isActive")
96
+ reason: str | None
97
+ unban_date: datetime | None = Field(serialization_alias="unbanDate")
98
+ created_at: datetime = Field(serialization_alias="createdAt")
99
+
100
+
101
+ class BannedMaster(BaseModel):
102
+ servers: list[BannedMasterData]
103
+
104
+
105
+ class ListData(BaseModel):
106
+ name: str
107
+ created_at: datetime = Field(serialization_alias="createdAt")
108
+ servers: list[ServerTw]
109
+
110
+
111
+ class List(BaseModel):
112
+ data: list[ListData]
113
+
114
+
115
+ class ListPlData(BaseModel):
116
+ name: str
117
+ created_at: datetime = Field(serialization_alias="createdAt")
118
+ players: list[ClientTw]
119
+
120
+
121
+ class ListPl(BaseModel):
122
+ data: list[ListPlData]
123
+
124
+
125
+ class Stats(BaseModel):
126
+ num_players: int = Field(serialization_alias="numPlayers")
127
+ num_clans: int = Field(serialization_alias="numClans")
128
+ num_servers: int = Field(serialization_alias="numServers")
129
+ num_maps: int = Field(serialization_alias="numMaps")
130
+ num_game_types: int = Field(serialization_alias="numGameTypes")
131
+ num_versions: int = Field(serialization_alias="numVersions")
@@ -0,0 +1,3 @@
1
+ from typing import TypeVar
2
+
3
+ T = TypeVar("T")
@@ -1,21 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddapi
3
- Version: 0.9.3
3
+ Version: 0.12.0
4
4
  Summary: DDnet api
5
5
  Home-page: https://github.com/ByFox213/ddapi
6
6
  Author: ByFox
7
+ License: MIT
7
8
  Project-URL: Github, https://github.com/ByFox213/ddapi
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
9
  Requires-Python: >=3.8
12
10
  Description-Content-Type: text/markdown
13
11
  License-File: LICENSE
14
12
  Dynamic: author
15
- Dynamic: classifier
16
13
  Dynamic: description
17
14
  Dynamic: description-content-type
18
15
  Dynamic: home-page
16
+ Dynamic: license
19
17
  Dynamic: license-file
20
18
  Dynamic: project-url
21
19
  Dynamic: requires-python
@@ -41,28 +39,28 @@ DDnet
41
39
 
42
40
  ```python
43
41
  import asyncio
42
+
44
43
  from ddapi import DDnetApi, DDPlayer
45
44
 
46
45
 
47
46
  async def main():
48
- obj = DDnetApi()
49
- nickname = "Cor"
50
- user: DDPlayer = await obj.player(nickname)
51
- if user is None:
52
- return "Player not found"
53
-
54
- print(f"{user.player}: {user.points.points}")
55
- # Cor: 31473
56
- await obj.close() # Closing client Not necessary
57
- assert isinstance(user, DDPlayer)
47
+ async with DDnetApi() as obj:
48
+ user: DDPlayer = await obj.player("Cor")
49
+ if user is not None:
50
+ text = f"{user.player}: {user.points.points}/{user.points.total}"
51
+ percent = round(user.points.total / user.points.points * 100)
52
+ print(f"{text}({percent}%)")
53
+ # Cor: 32950/32950(100%)
54
+ else:
55
+ print("Player not found")
58
56
 
59
57
 
60
58
  asyncio.run(main())
61
59
  ```
62
60
 
63
61
  Tested on Python
64
- | 3.9 | 3.10 | 3.11 | 3.12 |
65
- |-----|------|------|------|
66
- | ? | ? | ✅ | ✅ |
62
+ | 3.10 | 3.11 | 3.12 |
63
+ |------|------|------|
64
+ | ✅ | ✅ | ✅ |
67
65
 
68
66
  [other examples](https://github.com/ByFox213/ddapi/tree/main/example)
@@ -5,11 +5,13 @@ setup.py
5
5
  ddapi/ddapi/__init__.py
6
6
  ddapi/ddapi/api.py
7
7
  ddapi/ddapi/main.py
8
+ ddapi/ddapi/types.py
8
9
  ddapi/ddapi.egg-info/PKG-INFO
9
10
  ddapi/ddapi.egg-info/SOURCES.txt
10
11
  ddapi/ddapi.egg-info/dependency_links.txt
11
12
  ddapi/ddapi.egg-info/top_level.txt
12
13
  ddapi/ddapi/scheme/__init__.py
14
+ ddapi/ddapi/scheme/base.py
13
15
  ddapi/ddapi/scheme/ddnet.py
14
16
  ddapi/ddapi/scheme/ddstats_tw.py
15
17
  ddapi/ddapi/scheme/status_tw.py
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = ddapi
3
- version = 0.9.3
3
+ version = 0.12.0
4
4
  author = ByFox
5
5
  description = DDnet api
6
6
  long_description = file: README.md
@@ -8,16 +8,13 @@ long_description_content_type = text/markdown
8
8
  url = https://github.com/ByFox213/ddapi
9
9
  project_urls =
10
10
  github = https://github.com/ByFox213/ddapi
11
- classifiers =
12
- Programming Language :: Python :: 3
13
- License :: OSI Approved :: MIT License
14
- Operating System :: OS Independent
11
+ license_files = LICENSE
15
12
 
16
13
  [options]
17
14
  package_dir =
18
15
  = ddapi
19
16
  packages = find:
20
- python_requires = >=3.8
17
+ python_requires = >=3.9
21
18
 
22
19
  [options.packages.find]
23
20
  where = ddapi
@@ -5,20 +5,16 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="ddapi",
8
- version="0.9.3",
8
+ version="0.12.0",
9
9
  author="ByFox",
10
10
  description="DDnet api",
11
11
  long_description=long_description,
12
12
  long_description_content_type="text/markdown",
13
13
  url="https://github.com/ByFox213/ddapi",
14
+ license="MIT",
14
15
  project_urls={
15
16
  "Github": "https://github.com/ByFox213/ddapi"
16
17
  },
17
- classifiers=[
18
- "Programming Language :: Python :: 3",
19
- "License :: OSI Approved :: MIT License",
20
- "Operating System :: OS Independent",
21
- ],
22
18
  package_dir={"": "ddapi"},
23
19
  packages=setuptools.find_packages(where="ddapi"),
24
20
  python_requires=">=3.8"
ddapi-0.9.3/README.md DELETED
@@ -1,45 +0,0 @@
1
- ddapi is an api from the ddnet and qwik website
2
-
3
- ## Installation
4
-
5
- ```bash
6
- pip install ddapi
7
- ```
8
-
9
- or
10
-
11
- ```bash
12
- pip install git+https://github.com/ByFox213/ddapi
13
- ```
14
-
15
- ## Usage/Examples
16
-
17
- DDnet
18
-
19
- ```python
20
- import asyncio
21
- from ddapi import DDnetApi, DDPlayer
22
-
23
-
24
- async def main():
25
- obj = DDnetApi()
26
- nickname = "Cor"
27
- user: DDPlayer = await obj.player(nickname)
28
- if user is None:
29
- return "Player not found"
30
-
31
- print(f"{user.player}: {user.points.points}")
32
- # Cor: 31473
33
- await obj.close() # Closing client Not necessary
34
- assert isinstance(user, DDPlayer)
35
-
36
-
37
- asyncio.run(main())
38
- ```
39
-
40
- Tested on Python
41
- | 3.9 | 3.10 | 3.11 | 3.12 |
42
- |-----|------|------|------|
43
- | ? | ? | ✅ | ✅ |
44
-
45
- [other examples](https://github.com/ByFox213/ddapi/tree/main/example)
@@ -1,3 +0,0 @@
1
- from .ddnet import *
2
- from .ddstats_tw import *
3
- from .status_tw import *
@@ -1,131 +0,0 @@
1
- # Status.tw
2
- from datetime import datetime
3
- from enum import StrEnum
4
- from typing import Optional
5
-
6
- from pydantic import BaseModel
7
-
8
-
9
- class ChartEnum(StrEnum):
10
- day = "day"
11
- week = "week"
12
- month = "month"
13
-
14
-
15
- class DataTw(BaseModel):
16
- name: str
17
- createdAt: datetime
18
-
19
-
20
- class VersionTw(BaseModel):
21
- version: str
22
- createdAt: datetime
23
-
24
-
25
- class CountryTw(BaseModel):
26
- identifier: str
27
- code: int
28
- iconUrl: str
29
-
30
-
31
- class ClientTw(BaseModel):
32
- name: str
33
- country: CountryTw
34
- score: int
35
- isPlayer: bool
36
- isAfk: bool
37
- team: int
38
- createdAt: datetime
39
- clan: DataTw | None
40
- server: "ServerTw" = None
41
-
42
-
43
- class ServerTw(BaseModel):
44
- ip: str
45
- port: int
46
- name: str
47
- numClients: int
48
- numPlayers: int
49
- maxClients: int
50
- maxPlayers: int
51
- hasPassword: bool
52
- supports6: bool
53
- supports7: bool
54
- createdAt: datetime
55
- relevanceScore: int
56
- clientScoreKind: str | None
57
- logoUrl: str
58
- map: DataTw
59
- website: Optional[str] = None
60
- discordInvite: Optional[str] = None
61
- description: Optional[str] = None
62
- gameType: DataTw
63
- version: VersionTw
64
- clients: list[ClientTw]
65
-
66
-
67
- class ServerTwOne(BaseModel):
68
- ip: str
69
- port: int
70
- online: ServerTw
71
-
72
-
73
- class Chart(BaseModel):
74
- avgPlayers: float
75
- maxPlayers: int
76
- minPlayers: int
77
- timestamp: datetime
78
-
79
-
80
- class Charts(BaseModel):
81
- charts: list[Chart]
82
-
83
-
84
- class MasterTw(BaseModel):
85
- servers: list[ServerTw]
86
-
87
-
88
- class Info(BaseModel):
89
- app: str
90
- version: str
91
-
92
-
93
- class BannedMasterData(BaseModel):
94
- ip: str
95
- isActive: bool
96
- reason: str | None
97
- unbanDate: datetime | None
98
- createdAt: datetime
99
-
100
-
101
- class BannedMaster(BaseModel):
102
- servers: list[BannedMasterData]
103
-
104
-
105
- class ListData(BaseModel):
106
- name: str
107
- createdAt: datetime
108
- servers: list[ServerTw]
109
-
110
-
111
- class List(BaseModel):
112
- data: list[ListData]
113
-
114
-
115
- class ListPlData(BaseModel):
116
- name: str
117
- createdAt: datetime
118
- players: list[ClientTw]
119
-
120
-
121
- class ListPl(BaseModel):
122
- data: list[ListPlData]
123
-
124
-
125
- class Stats(BaseModel):
126
- numPlayers: int
127
- numClans: int
128
- numServers: int
129
- numMaps: int
130
- numGameTypes: int
131
- numVersions: int
File without changes