ddapi 0.10.0__tar.gz → 0.12.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,22 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddapi
3
- Version: 0.10.0
3
+ Version: 0.12.1
4
4
  Summary: DDnet api
5
5
  Home-page: https://github.com/ByFox213/ddapi
6
6
  Author: ByFox
7
- License: "MIT"
7
+ License: MIT
8
8
  Project-URL: Github, https://github.com/ByFox213/ddapi
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.8
9
+ Requires-Python: >=3.10
13
10
  Description-Content-Type: text/markdown
14
11
  License-File: LICENSE
15
12
  Dynamic: author
16
- Dynamic: classifier
17
13
  Dynamic: description
18
14
  Dynamic: description-content-type
19
15
  Dynamic: home-page
16
+ Dynamic: license
20
17
  Dynamic: license-file
21
18
  Dynamic: project-url
22
19
  Dynamic: requires-python
@@ -42,28 +39,28 @@ DDnet
42
39
 
43
40
  ```python
44
41
  import asyncio
42
+
45
43
  from ddapi import DDnetApi, DDPlayer
46
44
 
47
45
 
48
46
  async def main():
49
- obj = DDnetApi()
50
- nickname = "Cor"
51
- user: DDPlayer = await obj.player(nickname)
52
- if user is None:
53
- return "Player not found"
54
-
55
- print(f"{user.player}: {user.points.points}")
56
- # Cor: 31473
57
- await obj.close() # Closing client Not necessary
58
- 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")
59
56
 
60
57
 
61
58
  asyncio.run(main())
62
59
  ```
63
60
 
64
61
  Tested on Python
65
- | 3.9 | 3.10 | 3.11 | 3.12 |
66
- |-----|------|------|------|
67
- | ? | ? | ✅ | ✅ |
62
+ | 3.10 | 3.11 | 3.12 |
63
+ |------|------|------|
64
+ | ✅ | ✅ | ✅ |
68
65
 
69
66
  [other examples](https://github.com/ByFox213/ddapi/tree/main/example)
ddapi-0.12.1/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,8 @@
1
1
  """the module is made for the convenience of users to download and use"""
2
+
2
3
  from .main import DDnetApi, DDstats, Status
3
- from .scheme import *
4
+ from .scheme import * # noqa: F403
4
5
 
5
- __version__ = "0.10.0"
6
- __author__ = 'ByFox'
6
+ __version__ = "0.12.1"
7
+ __author__ = "ByFox"
7
8
  __LICENSE__ = "MIT"
@@ -1,43 +1,59 @@
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
- self,
14
- session: ClientSession = None,
15
- json_loads: Any = DEFAULT_JSON_DECODER
20
+ self,
21
+ session: ClientSession = None,
22
+ autoclose: bool = True,
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 await req.json(loads=self.json_loads) if req.status == 200 else None
34
53
 
35
54
  async def _generate_model_instance(
36
- self,
37
- url: str,
38
- model: Type[ModelType],
39
- k: Optional[str] = None
40
- ) -> Optional[ModelType]:
55
+ self, url: str, model: Type[T], k: Optional[str] = None
56
+ ) -> Optional[T]:
41
57
  """Generate a model instance from the given URL and optional keyword arguments.
42
58
 
43
59
  Args:
@@ -64,3 +80,11 @@ class API(ABC):
64
80
  if self.__session is not None:
65
81
  await self.__session.close()
66
82
  self.__session = None
83
+
84
+ @property
85
+ def domain(self) -> str:
86
+ return "ddnet.org"
87
+
88
+ @staticmethod
89
+ def powered() -> None:
90
+ return None
@@ -1,27 +1,47 @@
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
-
6
- __all__ = (
7
- "MasterEnum",
8
- "DDnetApi",
9
- "DDstats",
10
- "Status"
5
+ from .scheme import (
6
+ DDPlayer,
7
+ DDStatus,
8
+ ReleasesMaps,
9
+ Query,
10
+ QueryMap,
11
+ QueryMapper,
12
+ Master,
13
+ DMap,
14
+ DProfile,
15
+ SMap,
16
+ Maps,
17
+ Player,
18
+ Info,
19
+ BannedMaster,
20
+ List,
21
+ ListData,
22
+ ListPl,
23
+ ListPlData,
24
+ MasterTw,
25
+ ServerTwOne,
26
+ Charts,
27
+ ChartEnum,
28
+ Stats,
11
29
  )
12
30
 
31
+ __all__ = ("MasterEnum", "DDnetApi", "DDstats", "Status")
32
+
13
33
 
14
34
  class MasterEnum(StrEnum):
15
- master1 = "master1"
16
- master2 = "master2"
17
- master3 = "master3"
18
- master4 = "master4"
35
+ master1 = "1"
36
+ master2 = "2"
37
+ master3 = "3"
38
+ master4 = "4"
19
39
 
20
40
 
21
41
  class DDnetApi(API):
22
- def __init__(self):
23
- super().__init__()
24
- self.domain = "ddnet.org"
42
+ @property
43
+ def domain(self) -> str:
44
+ return "ddnet.org"
25
45
 
26
46
  @staticmethod
27
47
  def powered() -> str:
@@ -30,61 +50,53 @@ class DDnetApi(API):
30
50
  async def player(self, player: str) -> DDPlayer:
31
51
  """Fetch player data from the API and return it as a dictionary."""
32
52
  return await self._generate_model_instance(
33
- f"https://{self.domain}/players/?json2={quote(player)}",
34
- DDPlayer
53
+ f"https://{self.domain}/players/?json2={quote(player)}", DDPlayer
35
54
  )
36
55
 
37
56
  async def status(self) -> DDStatus:
38
57
  return await self._generate_model_instance(
39
- f"https://{self.domain}/status/json/stats.json",
40
- DDStatus
58
+ f"https://{self.domain}/status/json/stats.json", DDStatus
41
59
  )
42
60
 
43
61
  async def releases_map(self) -> ReleasesMaps:
44
62
  return await self._generate_model_instance(
45
- f"https://{self.domain}/releases/maps.json",
46
- ReleasesMaps,
47
- "maps"
63
+ f"https://{self.domain}/releases/maps.json", ReleasesMaps, "maps"
48
64
  )
49
65
 
50
66
  async def query(self, player: str) -> Query:
51
67
  return await self._generate_model_instance(
52
- f"https://{self.domain}/players/?query={quote(player)}",
53
- Query,
54
- "players"
68
+ f"https://{self.domain}/players/?query={quote(player)}", Query, "players"
55
69
  )
56
70
 
57
71
  async def query_map(self, map_name: str) -> QueryMap:
58
72
  return await self._generate_model_instance(
59
73
  f"https://{self.domain}/maps/?query={quote(map_name)}",
60
74
  QueryMap,
61
- 'maps',
75
+ "maps",
62
76
  )
63
77
 
64
78
  async def query_mapper(self, player: str) -> QueryMapper:
65
79
  return await self._generate_model_instance(
66
80
  f"https://{self.domain}/maps/?qmapper={quote(player)}",
67
81
  QueryMapper,
68
- 'players',
82
+ "players",
69
83
  )
70
84
 
71
- async def master(self, master: MasterEnum = MasterEnum.master1) -> Master:
85
+ async def master(self, number: MasterEnum = MasterEnum.master1) -> Master:
72
86
  return await self._generate_model_instance(
73
- f"https://{master}.{self.domain}/ddnet/15/servers.json",
74
- Master
87
+ f"https://master{number}.{self.domain}/ddnet/15/servers.json", Master
75
88
  )
76
89
 
77
90
  async def map(self, map_name: str) -> DMap:
78
91
  return await self._generate_model_instance(
79
- f"https://{self.domain}/maps/?json={quote(map_name)}",
80
- DMap
92
+ f"https://{self.domain}/maps/?json={quote(map_name)}", DMap
81
93
  )
82
94
 
83
95
 
84
96
  class DDstats(API):
85
- def __init__(self):
86
- super().__init__()
87
- self.domain = 'ddstats.tw'
97
+ @property
98
+ def domain(self) -> str:
99
+ return "ddstats.tw"
88
100
 
89
101
  @staticmethod
90
102
  def powered() -> str:
@@ -94,34 +106,30 @@ class DDstats(API):
94
106
  return await self._generate_model_instance(
95
107
  f"https://{self.domain}/profile/json?player={quote(player_name)}",
96
108
  DProfile,
97
- "profile"
109
+ "profile",
98
110
  )
99
111
 
100
112
  async def map(self, map_name: str) -> SMap:
101
113
  return await self._generate_model_instance(
102
- f"https://ddstats.tw/map/json?map={quote(map_name)}",
103
- SMap
114
+ f"https://{self.domain}/map/json?map={quote(map_name)}", SMap
104
115
  )
105
116
 
106
117
  async def maps(self) -> Maps:
107
118
  return await self._generate_model_instance(
108
- f"https://{self.domain}/maps/json",
109
- Maps,
110
- "maps"
119
+ f"https://{self.domain}/maps/json", Maps, "maps"
111
120
  )
112
121
 
113
122
  async def player(self, player_name: str) -> Player:
114
123
  """Fetch player data from the API and return it as a dictionary."""
115
124
  return await self._generate_model_instance(
116
- f"https://{self.domain}/player/json?player={quote(player_name)}",
117
- Player
125
+ f"https://{self.domain}/player/json?player={quote(player_name)}", Player
118
126
  )
119
127
 
120
128
 
121
129
  class Status(API):
122
- def __init__(self):
123
- super().__init__()
124
- self.domain = 'status.tw'
130
+ @property
131
+ def domain(self) -> str:
132
+ return "status.tw"
125
133
 
126
134
  @staticmethod
127
135
  def powered() -> str:
@@ -129,62 +137,48 @@ class Status(API):
129
137
 
130
138
  async def info(self) -> Info:
131
139
  return await self._generate_model_instance(
132
- f"https://api.{self.domain}/info",
133
- Info
140
+ f"https://api.{self.domain}/info", Info
134
141
  )
135
142
 
136
143
  async def master_bans(self) -> BannedMaster:
137
144
  return await self._generate_model_instance(
138
- f"https://api.{self.domain}/master/bans/list",
139
- BannedMaster,
140
- "servers"
145
+ f"https://api.{self.domain}/master/bans/list", BannedMaster, "servers"
141
146
  )
142
147
 
143
148
  async def map_list(self) -> List:
144
149
  return await self._generate_model_instance(
145
- f"https://api.{self.domain}/map/list",
146
- List,
147
- "data"
150
+ f"https://api.{self.domain}/map/list", List, "data"
148
151
  )
149
152
 
150
153
  async def map(self, name: str) -> ListData:
151
154
  return await self._generate_model_instance(
152
- f"https://api.{self.domain}/map/name/{quote(name)}",
153
- ListData
155
+ f"https://api.{self.domain}/map/name/{quote(name)}", ListData
154
156
  )
155
157
 
156
158
  async def gametype_list(self) -> List:
157
159
  return await self._generate_model_instance(
158
- f"https://api.{self.domain}/gametype/list",
159
- List,
160
- "data"
160
+ f"https://api.{self.domain}/gametype/list", List, "data"
161
161
  )
162
162
 
163
163
  async def gametype(self, name: str) -> ListData:
164
164
  return await self._generate_model_instance(
165
- f"https://api.{self.domain}/gametype/name/{quote(name)}",
166
- ListData
165
+ f"https://api.{self.domain}/gametype/name/{quote(name)}", ListData
167
166
  )
168
167
 
169
168
  async def version_list(self) -> List:
170
169
  return await self._generate_model_instance(
171
- f"https://api.{self.domain}/version/list",
172
- List,
173
- "data"
170
+ f"https://api.{self.domain}/version/list", List, "data"
174
171
  )
175
172
 
176
173
  async def version(self, name: str) -> ListData:
177
174
  return await self._generate_model_instance(
178
- f"https://api.{self.domain}/version/name/{quote(name)}",
179
- ListData
175
+ f"https://api.{self.domain}/version/name/{quote(name)}", ListData
180
176
  )
181
177
 
182
178
  async def server_list(self) -> MasterTw:
183
179
  """Fetch player data from the API and return it as a dictionary."""
184
180
  return await self._generate_model_instance(
185
- f"https://api.{self.domain}/server/list",
186
- MasterTw,
187
- "servers"
181
+ f"https://api.{self.domain}/server/list", MasterTw, "servers"
188
182
  )
189
183
 
190
184
  async def server(self, ip: str, port: int | str) -> ServerTwOne:
@@ -193,11 +187,13 @@ class Status(API):
193
187
  ServerTwOne,
194
188
  )
195
189
 
196
- async def server_charts(self, ip: str, port: int | str, chart: ChartEnum = ChartEnum.day) -> Charts:
190
+ async def server_charts(
191
+ self, ip: str, port: int | str, chart: ChartEnum = ChartEnum.day
192
+ ) -> Charts:
197
193
  return await self._generate_model_instance(
198
194
  f"https://api.{self.domain}/server/{quote(ip)}/{port}/chart/{chart}",
199
195
  Charts,
200
- "charts"
196
+ "charts",
201
197
  )
202
198
 
203
199
  async def server_history(self, ip: str, port: int | str) -> None:
@@ -210,28 +206,22 @@ class Status(API):
210
206
 
211
207
  async def clan_list(self) -> ListPl:
212
208
  return await self._generate_model_instance(
213
- f"https://api.{self.domain}/clan/list",
214
- ListPl,
215
- "data"
209
+ f"https://api.{self.domain}/clan/list", ListPl, "data"
216
210
  )
217
211
 
218
212
  async def clan(self, name: str) -> ListPlData:
219
213
  return await self._generate_model_instance(
220
- f"https://api.{self.domain}/clan/name/{quote(name)}",
221
- ListPlData
214
+ f"https://api.{self.domain}/clan/name/{quote(name)}", ListPlData
222
215
  )
223
216
 
224
217
  async def player_list(self) -> ListPl:
225
218
  return await self._generate_model_instance(
226
- f"https://api.{self.domain}/player/list",
227
- ListPl,
228
- "data"
219
+ f"https://api.{self.domain}/player/list", ListPl, "data"
229
220
  )
230
221
 
231
222
  async def player(self, name: str) -> ListPlData:
232
223
  return await self._generate_model_instance(
233
- f"https://api.{self.domain}/player/name/{quote(name)}",
234
- ListPlData
224
+ f"https://api.{self.domain}/player/name/{quote(name)}", ListPlData
235
225
  )
236
226
 
237
227
  async def player_history(self, name: str) -> None:
@@ -240,8 +230,7 @@ class Status(API):
240
230
 
241
231
  async def stats(self) -> Stats:
242
232
  return await self._generate_model_instance(
243
- f"https://api.{self.domain}/stats",
244
- Stats
233
+ f"https://api.{self.domain}/stats", Stats
245
234
  )
246
235
 
247
236
  async def stats_players(self, t: str) -> None:
@@ -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,37 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class DRank(BaseModel):
7
+ points: Optional[int] = None
8
+ rank: Optional[int] = None
9
+
10
+
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 DData(BaseModel):
18
+ points: Optional[DPoints] = Field(default=None)
19
+ team_rank: Optional[DRank] = Field(default=None)
20
+ rank: Optional[DRank] = Field(default=None)
21
+ maps: Optional[dict] = Field(default=None)
22
+
23
+
24
+ class DDType(BaseModel):
25
+ Novice: Optional[DData] = None
26
+ Moderate: Optional[DData] = None
27
+ Brutal: Optional[DData] = None
28
+ Insane: Optional[DData] = None
29
+ Dummy: Optional[DData] = None
30
+ DDmaX_Easy: Optional[DData] = Field(default=None, validation_alias="DDmaX.Easy")
31
+ DDmaX_Next: Optional[DData] = Field(default=None, validation_alias="DDmaX.Next")
32
+ DDmaX_Nut: Optional[DData] = Field(default=None, validation_alias="DDmaX.Nut")
33
+ DDmaX_Pro: Optional[DData] = Field(default=None, validation_alias="DDmaX.Pro")
34
+ Oldschool: Optional[DData] = Field(default=None)
35
+ Race: Optional[DData] = None
36
+ Solo: Optional[DData] = None
37
+ total: Optional[DData] = None
@@ -5,13 +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 DRank, DDType, DPoints
9
9
 
10
-
11
- class DPoints(BaseModel):
12
- total: int
13
- points: Optional[int] = Field(default=None)
14
- rank: Optional[int] = Field(default=None)
10
+ rm_list = ["DD-Persian", "/vDQMHSss8W", ""]
15
11
 
16
12
 
17
13
  class MaxFinishes(BaseModel):
@@ -63,11 +59,6 @@ class DMap(BaseModel):
63
59
  max_finishes: list[MaxFinishes]
64
60
 
65
61
 
66
- class DRank(BaseModel):
67
- points: Optional[int] = None
68
- rank: Optional[int] = None
69
-
70
-
71
62
  class FirstFinish(BaseModel):
72
63
  timestamp: float
73
64
  map: str
@@ -87,29 +78,6 @@ class FavoritePartner(BaseModel):
87
78
  finishes: int
88
79
 
89
80
 
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
81
  class Activity(BaseModel):
114
82
  date: str
115
83
  hours_played: int
@@ -220,7 +188,7 @@ class Master(BaseModel):
220
188
  client.clan
221
189
  for server in self.servers
222
190
  for client in server.info.clients
223
- if client != ''
191
+ if client != ""
224
192
  )
225
193
 
226
194
  for i in remove_list:
@@ -235,17 +203,20 @@ class Master(BaseModel):
235
203
  count_clients=i.count_client,
236
204
  address=i.addresses[0],
237
205
  game_type=i.info.game_type,
238
- name=i.info.name
206
+ name=i.info.name,
239
207
  )
240
208
  if isinstance(i.addresses, list)
241
209
  else CountServers(
242
210
  count_clients=i.count_client,
243
211
  address=i.addresses,
244
212
  game_type=i.info.game_type,
245
- name=i.info.name
213
+ name=i.info.name,
246
214
  )
247
215
  for i in self.servers
248
- ), key=lambda x: x.count_clients, reverse=True)
216
+ ),
217
+ key=lambda x: x.count_clients,
218
+ reverse=True,
219
+ )
249
220
 
250
221
  @property
251
222
  def count_clients(self) -> int:
@@ -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
@@ -117,9 +101,9 @@ class Points(BaseModel):
117
101
  weekly_points: Optional[PData] = None
118
102
  monthly_points: Optional[PData] = None
119
103
  yearly_points: Optional[PData] = None
120
- points: PPoints
121
- rank_points: PPoints
122
- team_points: PPoints
104
+ points: DDType
105
+ rank_points: DDType
106
+ team_points: DDType
123
107
 
124
108
 
125
109
  class RecentActivity(BaseModel):
@@ -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,22 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddapi
3
- Version: 0.10.0
3
+ Version: 0.12.1
4
4
  Summary: DDnet api
5
5
  Home-page: https://github.com/ByFox213/ddapi
6
6
  Author: ByFox
7
- License: "MIT"
7
+ License: MIT
8
8
  Project-URL: Github, https://github.com/ByFox213/ddapi
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.8
9
+ Requires-Python: >=3.10
13
10
  Description-Content-Type: text/markdown
14
11
  License-File: LICENSE
15
12
  Dynamic: author
16
- Dynamic: classifier
17
13
  Dynamic: description
18
14
  Dynamic: description-content-type
19
15
  Dynamic: home-page
16
+ Dynamic: license
20
17
  Dynamic: license-file
21
18
  Dynamic: project-url
22
19
  Dynamic: requires-python
@@ -42,28 +39,28 @@ DDnet
42
39
 
43
40
  ```python
44
41
  import asyncio
42
+
45
43
  from ddapi import DDnetApi, DDPlayer
46
44
 
47
45
 
48
46
  async def main():
49
- obj = DDnetApi()
50
- nickname = "Cor"
51
- user: DDPlayer = await obj.player(nickname)
52
- if user is None:
53
- return "Player not found"
54
-
55
- print(f"{user.player}: {user.points.points}")
56
- # Cor: 31473
57
- await obj.close() # Closing client Not necessary
58
- 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")
59
56
 
60
57
 
61
58
  asyncio.run(main())
62
59
  ```
63
60
 
64
61
  Tested on Python
65
- | 3.9 | 3.10 | 3.11 | 3.12 |
66
- |-----|------|------|------|
67
- | ? | ? | ✅ | ✅ |
62
+ | 3.10 | 3.11 | 3.12 |
63
+ |------|------|------|
64
+ | ✅ | ✅ | ✅ |
68
65
 
69
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.10.0
3
+ version = 0.12.1
4
4
  author = ByFox
5
5
  description = DDnet api
6
6
  long_description = file: README.md
@@ -8,13 +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
- license = "MIT"
11
+ license_files = LICENSE
12
12
 
13
13
  [options]
14
14
  package_dir =
15
15
  = ddapi
16
16
  packages = find:
17
- python_requires = >=3.8
17
+ python_requires = >=3.10
18
18
 
19
19
  [options.packages.find]
20
20
  where = ddapi
@@ -5,21 +5,15 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="ddapi",
8
- version="0.10.0",
8
+ version="0.12.1",
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
- project_urls={
15
- "Github": "https://github.com/ByFox213/ddapi"
16
- },
17
- classifiers=[
18
- "Programming Language :: Python :: 3",
19
- "License :: OSI Approved :: MIT License",
20
- "Operating System :: OS Independent",
21
- ],
14
+ license="MIT",
15
+ project_urls={"Github": "https://github.com/ByFox213/ddapi"},
22
16
  package_dir={"": "ddapi"},
23
17
  packages=setuptools.find_packages(where="ddapi"),
24
- python_requires=">=3.8"
18
+ python_requires=">=3.10",
25
19
  )
ddapi-0.10.0/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