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.
- {ddapi-0.10.0 → ddapi-0.12.1}/PKG-INFO +17 -20
- ddapi-0.12.1/README.md +45 -0
- {ddapi-0.10.0 → ddapi-0.12.1}/ddapi/ddapi/__init__.py +4 -3
- {ddapi-0.10.0 → ddapi-0.12.1}/ddapi/ddapi/api.py +46 -22
- {ddapi-0.10.0 → ddapi-0.12.1}/ddapi/ddapi/main.py +71 -82
- ddapi-0.12.1/ddapi/ddapi/scheme/__init__.py +4 -0
- ddapi-0.12.1/ddapi/ddapi/scheme/base.py +37 -0
- {ddapi-0.10.0 → ddapi-0.12.1}/ddapi/ddapi/scheme/ddnet.py +9 -38
- {ddapi-0.10.0 → ddapi-0.12.1}/ddapi/ddapi/scheme/ddstats_tw.py +4 -20
- ddapi-0.12.1/ddapi/ddapi/scheme/status_tw.py +131 -0
- ddapi-0.12.1/ddapi/ddapi/types.py +3 -0
- {ddapi-0.10.0 → ddapi-0.12.1}/ddapi/ddapi.egg-info/PKG-INFO +17 -20
- {ddapi-0.10.0 → ddapi-0.12.1}/ddapi/ddapi.egg-info/SOURCES.txt +2 -0
- {ddapi-0.10.0 → ddapi-0.12.1}/setup.cfg +3 -3
- {ddapi-0.10.0 → ddapi-0.12.1}/setup.py +4 -10
- ddapi-0.10.0/README.md +0 -45
- ddapi-0.10.0/ddapi/ddapi/scheme/__init__.py +0 -3
- ddapi-0.10.0/ddapi/ddapi/scheme/status_tw.py +0 -131
- {ddapi-0.10.0 → ddapi-0.12.1}/LICENSE +0 -0
- {ddapi-0.10.0 → ddapi-0.12.1}/ddapi/ddapi.egg-info/dependency_links.txt +0 -0
- {ddapi-0.10.0 → ddapi-0.12.1}/ddapi/ddapi.egg-info/top_level.txt +0 -0
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ddapi
|
|
3
|
-
Version: 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:
|
|
7
|
+
License: MIT
|
|
8
8
|
Project-URL: Github, https://github.com/ByFox213/ddapi
|
|
9
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
user
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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.
|
|
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.
|
|
6
|
-
__author__ =
|
|
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,
|
|
3
|
+
from typing import Optional, Type, Any
|
|
3
4
|
|
|
4
5
|
import aiohttp
|
|
5
|
-
from aiohttp import ClientSession
|
|
6
|
-
from aiohttp.typedefs import DEFAULT_JSON_DECODER
|
|
6
|
+
from aiohttp import ClientSession
|
|
7
7
|
|
|
8
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
return None
|
|
29
|
+
if self.__session is None:
|
|
30
|
+
self.__session = aiohttp.ClientSession()
|
|
23
31
|
|
|
24
|
-
async def
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 = "
|
|
16
|
-
master2 = "
|
|
17
|
-
master3 = "
|
|
18
|
-
master4 = "
|
|
35
|
+
master1 = "1"
|
|
36
|
+
master2 = "2"
|
|
37
|
+
master3 = "3"
|
|
38
|
+
master4 = "4"
|
|
19
39
|
|
|
20
40
|
|
|
21
41
|
class DDnetApi(API):
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
+
"players",
|
|
69
83
|
)
|
|
70
84
|
|
|
71
|
-
async def master(self,
|
|
85
|
+
async def master(self, number: MasterEnum = MasterEnum.master1) -> Master:
|
|
72
86
|
return await self._generate_model_instance(
|
|
73
|
-
f"https://{
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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://
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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(
|
|
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,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
|
-
|
|
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
|
-
),
|
|
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
|
-
|
|
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:
|
|
121
|
-
rank_points:
|
|
122
|
-
team_points:
|
|
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")
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ddapi
|
|
3
|
-
Version: 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:
|
|
7
|
+
License: MIT
|
|
8
8
|
Project-URL: Github, https://github.com/ByFox213/ddapi
|
|
9
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
user
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
11
|
+
license_files = LICENSE
|
|
12
12
|
|
|
13
13
|
[options]
|
|
14
14
|
package_dir =
|
|
15
15
|
= ddapi
|
|
16
16
|
packages = find:
|
|
17
|
-
python_requires = >=3.
|
|
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.
|
|
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
|
-
|
|
15
|
-
|
|
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.
|
|
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,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
|
|
File without changes
|
|
File without changes
|