ddapi 0.7.2__tar.gz → 0.8.1__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,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ddapi
3
- Version: 0.7.2
3
+ Version: 0.8.1
4
4
  Summary: DDnet api
5
5
  Home-page: https://github.com/ByFox213/ddapi
6
6
  Author: ByFox
@@ -21,19 +21,23 @@ Dynamic: requires-python
21
21
  Dynamic: summary
22
22
 
23
23
  ddapi is an api from the ddnet and qwik website
24
+
24
25
  ## Installation
25
26
 
26
27
  ```bash
27
28
  pip install ddapi
28
29
  ```
30
+
29
31
  or
32
+
30
33
  ```bash
31
34
  pip install git+https://github.com/ByFox213/ddapi
32
35
  ```
33
-
36
+
34
37
  ## Usage/Examples
35
38
 
36
39
  DDnet
40
+
37
41
  ```python
38
42
  import asyncio
39
43
  from ddapi import DDnetApi, DDPlayer
@@ -45,6 +49,7 @@ async def main():
45
49
  user: DDPlayer = await obj.player(nickname)
46
50
  if user is None:
47
51
  return "Player not found"
52
+
48
53
  print(f"{user.player}: {user.points.points}")
49
54
  # Cor: 31473
50
55
  await obj.close() # Closing client Not necessary
@@ -53,10 +58,10 @@ async def main():
53
58
 
54
59
  asyncio.run(main())
55
60
  ```
61
+
56
62
  Tested on Python
57
63
  | 3.9 | 3.10 | 3.11 | 3.12 |
58
64
  |-----|------|------|------|
59
- | ? | ? | ✅ | ? |
60
-
65
+ | ? | ? | ✅ | ✅ |
61
66
 
62
67
  [other examples](https://github.com/ByFox213/ddapi/tree/main/example)
@@ -1,17 +1,21 @@
1
1
  ddapi is an api from the ddnet and qwik website
2
+
2
3
  ## Installation
3
4
 
4
5
  ```bash
5
6
  pip install ddapi
6
7
  ```
8
+
7
9
  or
10
+
8
11
  ```bash
9
12
  pip install git+https://github.com/ByFox213/ddapi
10
13
  ```
11
-
14
+
12
15
  ## Usage/Examples
13
16
 
14
17
  DDnet
18
+
15
19
  ```python
16
20
  import asyncio
17
21
  from ddapi import DDnetApi, DDPlayer
@@ -23,6 +27,7 @@ async def main():
23
27
  user: DDPlayer = await obj.player(nickname)
24
28
  if user is None:
25
29
  return "Player not found"
30
+
26
31
  print(f"{user.player}: {user.points.points}")
27
32
  # Cor: 31473
28
33
  await obj.close() # Closing client Not necessary
@@ -31,10 +36,10 @@ async def main():
31
36
 
32
37
  asyncio.run(main())
33
38
  ```
39
+
34
40
  Tested on Python
35
41
  | 3.9 | 3.10 | 3.11 | 3.12 |
36
42
  |-----|------|------|------|
37
- | ? | ? | ✅ | ? |
38
-
43
+ | ? | ? | ✅ | ✅ |
39
44
 
40
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 .dataclass import *
3
+ from .scheme import *
4
4
 
5
- __version__ = "0.6.0"
5
+ __version__ = "0.8.1"
6
6
  __author__ = 'ByFox'
7
7
  __LICENSE__ = "MIT"
@@ -1,5 +1,5 @@
1
1
  from abc import ABC
2
- from typing import Union, Optional, TypeVar, Type, Any
2
+ from typing import Optional, TypeVar, Type, Any
3
3
 
4
4
  import aiohttp
5
5
  from aiohttp import ClientSession, ClientConnectorError
@@ -14,23 +14,21 @@ class API(ABC):
14
14
  session: ClientSession = None,
15
15
  json_loads: Any = DEFAULT_JSON_DECODER
16
16
  ):
17
- self.session = session
17
+ self.__session = session
18
18
  self.json_loads = json_loads
19
19
 
20
20
  @staticmethod
21
- def powered() -> str:
22
- """Return an empty string."""
23
- return ''
21
+ def powered() -> None:
22
+ return None
24
23
 
25
- async def _get(self, url: str) -> Union[dict, None]:
24
+ async def _get(self, url: str) -> Optional[dict]:
26
25
  """Send a GET request to the given URL and return the response as JSON."""
27
- if self.session is None:
28
- self.session = aiohttp.ClientSession()
26
+ if self.__session is None:
27
+ self.__session = aiohttp.ClientSession()
28
+
29
29
  try:
30
- async with self.session.get(url) as req:
31
- if req.status == 200:
32
- return await req.json(loads=self.json_loads)
33
- return
30
+ async with self.__session.get(url) as req:
31
+ return await req.json(loads=self.json_loads) if req.status == 200 else None
34
32
  except ClientConnectorError:
35
33
  return
36
34
 
@@ -38,7 +36,7 @@ class API(ABC):
38
36
  self,
39
37
  url: str,
40
38
  model: Type[ModelType],
41
- k: str = None
39
+ k: Optional[str] = None
42
40
  ) -> Optional[ModelType]:
43
41
  """Generate a model instance from the given URL and optional keyword arguments.
44
42
 
@@ -54,11 +52,15 @@ class API(ABC):
54
52
  if dat is None or not dat:
55
53
  return
56
54
 
57
- data_to_pass = {k: dat} if k else dat
55
+ data_to_pass = {k: dat} if k is not None else dat
58
56
  return model(**data_to_pass)
59
57
 
58
+ async def is_closed(self) -> bool:
59
+ """True is closed, False otherwise."""
60
+ return self.__session is None or self.__session.closed
61
+
60
62
  async def close(self) -> None:
61
63
  """Close the aiohttp session."""
62
- if self.session:
63
- await self.session.close()
64
- self.session = None
64
+ if self.__session is not None:
65
+ await self.__session.close()
66
+ self.__session = None
@@ -0,0 +1,173 @@
1
+ from typing import Union
2
+ from urllib.parse import quote
3
+
4
+ from .api import API
5
+ from .scheme import DDPlayer, Master, Query, DMap, DDStatus, Player, MasterTw, ServerTwOne, ChartEnum, Charts
6
+
7
+ __all__ = (
8
+ "DDnetApi",
9
+ "DDstats",
10
+ "Status"
11
+ )
12
+
13
+
14
+ class DDnetApi(API):
15
+ def __init__(self):
16
+ super().__init__()
17
+ self.domain = "ddnet.org"
18
+
19
+ @staticmethod
20
+ def powered() -> str:
21
+ return DDnetApi().domain
22
+
23
+ async def player(self, player_name: str) -> DDPlayer:
24
+ """Fetch player data from the API and return it as a dictionary."""
25
+ return await self._generate_model_instance(
26
+ f"https://{self.domain}/players/?json2={quote(player_name)}",
27
+ DDPlayer
28
+ )
29
+
30
+ async def status(self) -> Union[DDStatus, None]:
31
+ return await self._generate_model_instance(
32
+ f"https://{self.domain}/status/json/stats.json",
33
+ DDStatus
34
+ )
35
+
36
+ async def query(self, player: str) -> Union[Query, None]:
37
+ return await self._generate_model_instance(
38
+ f"https://{self.domain}/players/?query={quote(player)}",
39
+ Query,
40
+ "data"
41
+ )
42
+
43
+ async def master(self) -> Union[Master, None]:
44
+ return await self._generate_model_instance(
45
+ f"https://master1.{self.domain}/ddnet/15/servers.json",
46
+ Master
47
+ )
48
+
49
+ async def map(self, map_name: str) -> Union[DMap, None]:
50
+ return await self._generate_model_instance(
51
+ f"https://{self.domain}/maps/?json={quote(map_name)}",
52
+ DMap
53
+ )
54
+
55
+
56
+ class DDstats(API):
57
+ def __init__(self):
58
+ super().__init__()
59
+ self.domain = 'ddstats.tw'
60
+
61
+ @staticmethod
62
+ def powered() -> str:
63
+ return DDstats().domain
64
+
65
+ async def player(self, player_name: str) -> Player:
66
+ """Fetch player data from the API and return it as a dictionary."""
67
+ return await self._generate_model_instance(
68
+ f"https://{self.domain}/player/json?player={quote(player_name)}",
69
+ Player
70
+ )
71
+
72
+
73
+ class Status(API):
74
+ def __init__(self):
75
+ super().__init__()
76
+ self.domain = 'status.tw'
77
+
78
+ @staticmethod
79
+ def powered() -> str:
80
+ return Status().domain
81
+
82
+ async def info(self) -> None:
83
+ # https://api.status.tw/info
84
+ raise NotImplementedError()
85
+
86
+ async def master_bans(self) -> None:
87
+ # https://api.status.tw/master/bans/list
88
+ raise NotImplementedError()
89
+
90
+ async def map_list(self) -> None:
91
+ # https://api.status.tw/map/list
92
+ raise NotImplementedError()
93
+
94
+ async def map(self, name: str) -> None:
95
+ # https://api.status.tw/map/name/{name}
96
+ raise NotImplementedError()
97
+
98
+ async def gametype_list(self) -> None:
99
+ # https://api.status.tw/gametype/list
100
+ raise NotImplementedError()
101
+
102
+ async def gametype(self, name: str) -> None:
103
+ # https://api.status.tw/gametype/name/{name}
104
+ raise NotImplementedError()
105
+
106
+ async def version_list(self) -> None:
107
+ # https://api.status.tw/version/list
108
+ raise NotImplementedError()
109
+
110
+ async def version(self, name: str) -> None:
111
+ # https://api.status.tw/version/name/{name}
112
+ raise NotImplementedError()
113
+
114
+ async def server_list(self) -> MasterTw:
115
+ """Fetch player data from the API and return it as a dictionary."""
116
+ return await self._generate_model_instance(
117
+ f"https://api.{self.domain}/server/list",
118
+ MasterTw,
119
+ "servers"
120
+ )
121
+
122
+ async def server(self, ip: str, port: Union[int, str]) -> ServerTwOne:
123
+ return await self._generate_model_instance(
124
+ f"https://api.{self.domain}/server/{quote(ip)}/{port}",
125
+ ServerTwOne,
126
+ )
127
+
128
+ async def server_charts(self, ip: str, port: Union[int, str], chart: ChartEnum = ChartEnum.day) -> Charts:
129
+ return await self._generate_model_instance(
130
+ f"https://api.{self.domain}/server/{quote(ip)}/{port}/chart/{chart}",
131
+ Charts,
132
+ "charts"
133
+ )
134
+
135
+ async def server_history(self, ip: str, port: Union[int, str]) -> None:
136
+ # https://api.status.tw/server/{ip}/{port}/history
137
+ raise NotImplementedError()
138
+
139
+ async def server_stats(self, ip: str, port: Union[int, str]) -> None:
140
+ # https://api.status.tw/server/{ip}/{port}/stats
141
+ raise NotImplementedError()
142
+
143
+ async def clan_list(self) -> None:
144
+ # https://api.status.tw/clan/list
145
+ raise NotImplementedError()
146
+
147
+ async def clan(self, name: str) -> None:
148
+ # https://api.status.tw/clan/name/{name}
149
+ raise NotImplementedError()
150
+
151
+ async def player_list(self) -> None:
152
+ # https://api.status.tw/player/list
153
+ raise NotImplementedError()
154
+
155
+ async def player(self, name: str) -> None:
156
+ # https://api.status.tw/player/name/{name}
157
+ raise NotImplementedError()
158
+
159
+ async def player_history(self, name: str) -> None:
160
+ # https://api.status.tw/player/name/{name}/history
161
+ raise NotImplementedError()
162
+
163
+ async def stats(self) -> None:
164
+ # https://api.status.tw/stats
165
+ raise NotImplementedError()
166
+
167
+ async def stats_players(self, t: str) -> None:
168
+ # https://api.status.tw/stats/players/day week month
169
+ raise NotImplementedError()
170
+
171
+ async def stats_servers(self, t: str) -> None:
172
+ # https://api.status.tw/stats/servers/week month
173
+ raise NotImplementedError()
@@ -0,0 +1,3 @@
1
+ from .ddnet import *
2
+ from .status_tw import *
3
+ from .ddstats_tw import *
@@ -0,0 +1,132 @@
1
+ # ddnet
2
+ from typing import Optional
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class DPoints(BaseModel):
8
+ total: int
9
+ points: Optional[int] = Field(default=None)
10
+ rank: Optional[int] = Field(default=None)
11
+
12
+
13
+ class MaxFinishes(BaseModel):
14
+ rank: int
15
+ player: str
16
+ num: int
17
+ time: float
18
+ min_timestamp: int
19
+ max_timestamp: int
20
+
21
+
22
+ class DDRanks(BaseModel):
23
+ rank: int
24
+ player: str
25
+ time: float
26
+ timestamp: int
27
+ country: str
28
+
29
+
30
+ class DDTeamRanks(BaseModel):
31
+ rank: int
32
+ players: list[str]
33
+ time: float
34
+ timestamp: int
35
+ country: str
36
+
37
+
38
+ class DMap(BaseModel):
39
+ name: str
40
+ website: str
41
+ thumbnail: str
42
+ web_preview: str
43
+ type: str
44
+ points: int
45
+ difficulty: int
46
+ mapper: str
47
+ release: Optional[int] = None
48
+ median_time: float
49
+ first_finish: int
50
+ last_finish: int
51
+ finishes: int
52
+ finishers: int
53
+ biggest_team: int
54
+ width: int
55
+ height: int
56
+ tiles: list[str]
57
+ team_ranks: list[DDTeamRanks]
58
+ ranks: list[DDRanks]
59
+ max_finishes: list[MaxFinishes]
60
+
61
+
62
+ class DRank(BaseModel):
63
+ points: Optional[int] = None
64
+ rank: Optional[int] = None
65
+
66
+
67
+ class FirstFinish(BaseModel):
68
+ timestamp: float
69
+ map: str
70
+ time: float
71
+
72
+
73
+ class LastFinish(BaseModel):
74
+ timestamp: float
75
+ map: str
76
+ time: float
77
+ country: str
78
+ type: Optional[str] = None
79
+
80
+
81
+ class FavoritePartner(BaseModel):
82
+ name: str
83
+ finishes: int
84
+
85
+
86
+ class DData(BaseModel):
87
+ points: Optional[DPoints] = Field(default=None)
88
+ team_rank: Optional[DRank] = Field(default=None)
89
+ rank: Optional[DRank] = Field(default=None)
90
+ maps: Optional[dict] = Field(default=None)
91
+
92
+
93
+ class DDType(BaseModel):
94
+ Novice: Optional[DData] = None
95
+ Moderate: Optional[DData] = None
96
+ Brutal: Optional[DData] = None
97
+ Insane: Optional[DData] = None
98
+ Dummy: Optional[DData] = None
99
+ DDmaX_Easy: Optional[DData] = Field(default=None, validation_alias='DDmaX.Easy')
100
+ DDmaX_Next: Optional[DData] = Field(default=None, validation_alias='DDmaX.Next')
101
+ DDmaX_Nut: Optional[DData] = Field(default=None, validation_alias='DDmaX.Nut')
102
+ DDmaX_Pro: Optional[DData] = Field(default=None, validation_alias='DDmaX.Pro')
103
+ Oldschool: Optional[DData] = Field(default=None)
104
+ Race: Optional[DData] = None
105
+ Solo: Optional[DData] = None
106
+ total: Optional[DData] = None
107
+
108
+
109
+ class Activity(BaseModel):
110
+ date: str
111
+ hours_played: int
112
+
113
+
114
+ class FavoriteServer(BaseModel):
115
+ server: str
116
+
117
+
118
+ class DDPlayer(BaseModel):
119
+ player: str
120
+ points: DPoints
121
+ team_rank: Optional[DRank] = None
122
+ rank: Optional[DRank] = None
123
+ points_last_year: Optional[DRank] = None
124
+ points_last_month: Optional[DRank] = None
125
+ points_last_week: Optional[DRank] = None
126
+ favorite_server: FavoriteServer
127
+ first_finish: FirstFinish
128
+ last_finishes: list[LastFinish]
129
+ favorite_partners: Optional[list[FavoritePartner]] = None
130
+ types: DDType
131
+ activity: list[Activity]
132
+ hours_played_past_365_days: int
@@ -1,143 +1,13 @@
1
- # pylint: disable-all
1
+ # ddstats.tw
2
2
  from collections import Counter
3
3
  from datetime import datetime
4
- from typing import Optional, Union, Any
4
+ from typing import Any, Union, Optional
5
+
5
6
  from pydantic import BaseModel, Field
6
7
 
7
8
  rm_list = ["DD-Persian", "/vDQMHSss8W", '']
8
9
 
9
10
 
10
- # ddnet
11
- class DPoints(BaseModel):
12
- total: int
13
- points: Optional[int] = Field(default=None)
14
- rank: Optional[int] = Field(default=None)
15
-
16
-
17
- class MaxFinishes(BaseModel):
18
- rank: int
19
- player: str
20
- num: int
21
- time: float
22
- min_timestamp: int
23
- max_timestamp: int
24
-
25
-
26
- class DDRanks(BaseModel):
27
- rank: int
28
- player: str
29
- time: float
30
- timestamp: int
31
- country: str
32
-
33
-
34
- class DDTeamRanks(BaseModel):
35
- rank: int
36
- players: list[str]
37
- time: float
38
- timestamp: int
39
- country: str
40
-
41
-
42
- class DMap(BaseModel):
43
- name: str
44
- website: str
45
- thumbnail: str
46
- web_preview: str
47
- type: str
48
- points: int
49
- difficulty: int
50
- mapper: str
51
- release: Optional[int] = None
52
- median_time: float
53
- first_finish: int
54
- last_finish: int
55
- finishes: int
56
- finishers: int
57
- biggest_team: int
58
- width: int
59
- height: int
60
- tiles: list[str]
61
- team_ranks: list[DDTeamRanks]
62
- ranks: list[DDRanks]
63
- max_finishes: list[MaxFinishes]
64
-
65
-
66
- class DRank(BaseModel):
67
- points: Optional[int] = None
68
- rank: Optional[int] = None
69
-
70
-
71
- class FirstFinish(BaseModel):
72
- timestamp: float
73
- map: str
74
- time: float
75
-
76
-
77
- class LastFinish(BaseModel):
78
- timestamp: float
79
- map: str
80
- time: float
81
- country: str
82
- type: Optional[str] = None
83
-
84
-
85
- class FavoritePartner(BaseModel):
86
- name: str
87
- finishes: int
88
-
89
-
90
- class DData(BaseModel):
91
- points: Optional[DPoints] = Field(default=None)
92
- team_rank: Optional[DRank] = Field(default=None)
93
- rank: Optional[DRank] = Field(default=None)
94
- maps: Optional[dict] = Field(default=None)
95
-
96
-
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
- class Activity(BaseModel):
114
- date: str
115
- hours_played: int
116
-
117
-
118
- class FavoriteServer(BaseModel):
119
- server: str
120
-
121
-
122
- class DDPlayer(BaseModel):
123
- player: str
124
- points: DPoints
125
- team_rank: Optional[DRank] = None
126
- rank: Optional[DRank] = None
127
- points_last_year: Optional[DRank] = None
128
- points_last_month: Optional[DRank] = None
129
- points_last_week: Optional[DRank] = None
130
- favorite_server: FavoriteServer
131
- first_finish: FirstFinish
132
- last_finishes: list[LastFinish]
133
- favorite_partners: Optional[list[FavoritePartner]] = None
134
- types: DDType
135
- activity: list[Activity]
136
- hours_played_past_365_days: int
137
-
138
-
139
- # ddstats.tw
140
-
141
11
  class PData(BaseModel):
142
12
  rank: int
143
13
  points: int
@@ -169,9 +39,9 @@ class PointG(BaseModel):
169
39
  class Profile(BaseModel):
170
40
  name: str
171
41
  points: int
172
- clan: str
173
- country: int
174
- skin_name: str
42
+ clan: Optional[str]
43
+ country: Optional[int]
44
+ skin_name: Optional[str]
175
45
  skin_color_body: Optional[int] = None
176
46
  skin_color_feet: Optional[int] = None
177
47
 
@@ -391,6 +261,13 @@ class Server(BaseModel):
391
261
  return 0
392
262
 
393
263
 
264
+ class CountServers(BaseModel):
265
+ name: str
266
+ address: str
267
+ game_type: str
268
+ count_clients: int
269
+
270
+
394
271
  class Master(BaseModel):
395
272
  servers: list[Server]
396
273
 
@@ -401,25 +278,37 @@ class Master(BaseModel):
401
278
  remove_list = rm_list.copy() if rm is None else rm
402
279
  if not self.servers:
403
280
  return []
281
+
404
282
  dat: Counter[Any] = Counter(
405
283
  client.clan
406
284
  for server in self.servers
407
285
  for client in server.info.clients
408
286
  if client != ''
409
287
  )
288
+
410
289
  for i in remove_list:
411
290
  del dat[i]
412
291
  return sorted(dat.items(), key=lambda x: x[1], reverse=True)
413
292
 
414
293
  @property
415
- def count_servers(self) -> list:
294
+ def count_servers(self) -> list[CountServers]:
416
295
  return sorted(
417
296
  (
418
- (i.count_client, i.addresses[0], i.info.game_type, i.info.name)
297
+ CountServers(
298
+ count_clients=i.count_client,
299
+ address=i.addresses[0],
300
+ game_type=i.info.game_type,
301
+ name=i.info.name
302
+ )
419
303
  if isinstance(i.addresses, list)
420
- else (i.count_client, i.addresses, i.info.game_type, i.info.name)
304
+ else CountServers(
305
+ count_clients=i.count_client,
306
+ address=i.addresses,
307
+ game_type=i.info.game_type,
308
+ name=i.info.name
309
+ )
421
310
  for i in self.servers
422
- ), key=lambda x: x[0], reverse=True)
311
+ ), key=lambda x: x.count_clients, reverse=True)
423
312
 
424
313
  @property
425
314
  def count_clients(self) -> int:
@@ -459,58 +348,3 @@ class DDStatusData(BaseModel):
459
348
 
460
349
  class DDStatus(BaseModel):
461
350
  servers: list[DDStatusData]
462
-
463
- # Status.tw
464
-
465
- class DataTw(BaseModel):
466
- name: str
467
- createdAt: datetime
468
-
469
- class VersionTw(BaseModel):
470
- version: str
471
- createdAt: datetime
472
-
473
-
474
- class CountryTw(BaseModel):
475
- identifier: str
476
- code: int
477
- iconUrl: str
478
-
479
-
480
- class ClientTw(BaseModel):
481
- name: str
482
- country: CountryTw
483
- score: int
484
- isPlayer: bool
485
- isAfk: bool
486
- team: int
487
- createdAt: datetime
488
- clan: DataTw | None
489
-
490
-
491
- class ServersTw(BaseModel):
492
- ip: str
493
- port: int
494
- name: str
495
- numClients: int
496
- numPlayers: int
497
- maxClients: int
498
- maxPlayers: int
499
- hasPassword: bool
500
- supports6: bool
501
- supports7: bool
502
- createdAt: datetime
503
- relevanceScore: int
504
- clientScoreKind: str | None
505
- logoUrl: str
506
- map: DataTw
507
- website: str | None = None
508
- discordInvite: str | None = None
509
- description: str | None = None
510
- gameType: DataTw
511
- version: VersionTw
512
- clients: list[ClientTw]
513
-
514
-
515
- class MasterTw(BaseModel):
516
- servers: list[ServersTw]
@@ -0,0 +1,84 @@
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
+
41
+
42
+ class ServerTw(BaseModel):
43
+ ip: str
44
+ port: int
45
+ name: str
46
+ numClients: int
47
+ numPlayers: int
48
+ maxClients: int
49
+ maxPlayers: int
50
+ hasPassword: bool
51
+ supports6: bool
52
+ supports7: bool
53
+ createdAt: datetime
54
+ relevanceScore: int
55
+ clientScoreKind: str | None
56
+ logoUrl: str
57
+ map: DataTw
58
+ website: Optional[str] = None
59
+ discordInvite: Optional[str] = None
60
+ description: Optional[str] = None
61
+ gameType: DataTw
62
+ version: VersionTw
63
+ clients: list[ClientTw]
64
+
65
+
66
+ class ServerTwOne(BaseModel):
67
+ ip: str
68
+ port: int
69
+ online: ServerTw
70
+
71
+
72
+ class Chart(BaseModel):
73
+ avgPlayers: float
74
+ maxPlayers: int
75
+ minPlayers: int
76
+ timestamp: datetime
77
+
78
+
79
+ class Charts(BaseModel):
80
+ charts: list[Chart]
81
+
82
+
83
+ class MasterTw(BaseModel):
84
+ servers: list[ServerTw]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ddapi
3
- Version: 0.7.2
3
+ Version: 0.8.1
4
4
  Summary: DDnet api
5
5
  Home-page: https://github.com/ByFox213/ddapi
6
6
  Author: ByFox
@@ -21,19 +21,23 @@ Dynamic: requires-python
21
21
  Dynamic: summary
22
22
 
23
23
  ddapi is an api from the ddnet and qwik website
24
+
24
25
  ## Installation
25
26
 
26
27
  ```bash
27
28
  pip install ddapi
28
29
  ```
30
+
29
31
  or
32
+
30
33
  ```bash
31
34
  pip install git+https://github.com/ByFox213/ddapi
32
35
  ```
33
-
36
+
34
37
  ## Usage/Examples
35
38
 
36
39
  DDnet
40
+
37
41
  ```python
38
42
  import asyncio
39
43
  from ddapi import DDnetApi, DDPlayer
@@ -45,6 +49,7 @@ async def main():
45
49
  user: DDPlayer = await obj.player(nickname)
46
50
  if user is None:
47
51
  return "Player not found"
52
+
48
53
  print(f"{user.player}: {user.points.points}")
49
54
  # Cor: 31473
50
55
  await obj.close() # Closing client Not necessary
@@ -53,10 +58,10 @@ async def main():
53
58
 
54
59
  asyncio.run(main())
55
60
  ```
61
+
56
62
  Tested on Python
57
63
  | 3.9 | 3.10 | 3.11 | 3.12 |
58
64
  |-----|------|------|------|
59
- | ? | ? | ✅ | ? |
60
-
65
+ | ? | ? | ✅ | ✅ |
61
66
 
62
67
  [other examples](https://github.com/ByFox213/ddapi/tree/main/example)
@@ -3,10 +3,13 @@ README.md
3
3
  setup.cfg
4
4
  setup.py
5
5
  ddapi/ddapi/__init__.py
6
- ddapi/ddapi/dataclass.py
7
- ddapi/ddapi/deflt.py
6
+ ddapi/ddapi/api.py
8
7
  ddapi/ddapi/main.py
9
8
  ddapi/ddapi.egg-info/PKG-INFO
10
9
  ddapi/ddapi.egg-info/SOURCES.txt
11
10
  ddapi/ddapi.egg-info/dependency_links.txt
12
- ddapi/ddapi.egg-info/top_level.txt
11
+ ddapi/ddapi.egg-info/top_level.txt
12
+ ddapi/ddapi/scheme/__init__.py
13
+ ddapi/ddapi/scheme/ddnet.py
14
+ ddapi/ddapi/scheme/ddstats_tw.py
15
+ ddapi/ddapi/scheme/status_tw.py
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = ddapi
3
- version = 0.7.2
3
+ version = 0.8.1
4
4
  author = ByFox
5
5
  description = DDnet api
6
6
  long_description = file: README.md
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="ddapi",
8
- version="0.7.2",
8
+ version="0.8.1",
9
9
  author="ByFox",
10
10
  description="DDnet api",
11
11
  long_description=long_description,
@@ -20,6 +20,6 @@ setuptools.setup(
20
20
  "Operating System :: OS Independent",
21
21
  ],
22
22
  package_dir={"": "ddapi"},
23
- packages=setuptools.find_packages(where="src"),
23
+ packages=setuptools.find_packages(where="ddapi"),
24
24
  python_requires=">=3.8"
25
25
  )
@@ -1,88 +0,0 @@
1
- from typing import Union
2
- from urllib.parse import quote
3
-
4
- from .dataclass import DDPlayer, Master, Query, DMap, DDStatus, Player, MasterTw
5
- from .deflt import API
6
-
7
- __all__ = (
8
- "DDnetApi",
9
- "DDstats",
10
- "Status"
11
- )
12
-
13
-
14
- class DDnetApi(API):
15
- def __init__(self):
16
- super().__init__()
17
- self.domain = "ddnet.org"
18
-
19
- @staticmethod
20
- def powered() -> str:
21
- return 'ddnet.org'
22
-
23
- async def player(self, player_name: str) -> DDPlayer:
24
- """Fetch player data from the API and return it as a dictionary."""
25
- return await self._generate_model_instance(
26
- f"https://{self.domain}/players/?json2={quote(player_name)}",
27
- DDPlayer
28
- )
29
-
30
- async def status(self) -> Union[DDStatus, None]:
31
- return await self._generate_model_instance(
32
- f"https://{self.domain}/status/json/stats.json",
33
- DDStatus
34
- )
35
-
36
- async def query(self, player: str) -> Union[Query, None]:
37
- return await self._generate_model_instance(
38
- f"https://{self.domain}/players/?query={quote(player)}",
39
- Query,
40
- "data"
41
- )
42
-
43
- async def master(self) -> Union[Master, None]:
44
- return await self._generate_model_instance(
45
- f"https://master1.{self.domain}/ddnet/15/servers.json",
46
- Master
47
- )
48
-
49
- async def map(self, map_name: str) -> Union[DMap, None]:
50
- return await self._generate_model_instance(
51
- f"https://{self.domain}/maps/?json={quote(map_name)}",
52
- DMap
53
- )
54
-
55
-
56
- class DDstats(API):
57
- def __init__(self):
58
- super().__init__()
59
- self.url = "https://ddstats.tw/player/json?player={0}"
60
-
61
- @staticmethod
62
- def powered() -> str:
63
- return 'ddstats.tw'
64
-
65
- async def player(self, player_name: str) -> Player:
66
- """Fetch player data from the API and return it as a dictionary."""
67
- return await self._generate_model_instance(
68
- self.url.format(quote(player_name)),
69
- Player
70
- )
71
-
72
-
73
- class Status(API): # TODO: Add other
74
- def __init__(self):
75
- super().__init__()
76
- self.url = "https://api.status.tw"
77
-
78
- @staticmethod
79
- def powered() -> str:
80
- return 'ddstats.tw'
81
-
82
- async def server_list(self) -> MasterTw:
83
- """Fetch player data from the API and return it as a dictionary."""
84
- return await self._generate_model_instance(
85
- self.url + "/server/list",
86
- MasterTw,
87
- "servers"
88
- )
File without changes