ddapi 0.5.1__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ddapi-0.5.1 → ddapi-0.6.0}/PKG-INFO +1 -1
- {ddapi-0.5.1 → ddapi-0.6.0}/ddapi/ddapi/__init__.py +2 -2
- {ddapi-0.5.1 → ddapi-0.6.0}/ddapi/ddapi/dataclass.py +16 -18
- ddapi-0.6.0/ddapi/ddapi/deflt.py +84 -0
- ddapi-0.6.0/ddapi/ddapi/main.py +97 -0
- {ddapi-0.5.1 → ddapi-0.6.0}/ddapi/ddapi.egg-info/PKG-INFO +1 -1
- {ddapi-0.5.1 → ddapi-0.6.0}/setup.cfg +1 -1
- {ddapi-0.5.1 → ddapi-0.6.0}/setup.py +1 -1
- ddapi-0.5.1/ddapi/ddapi/deflt.py +0 -63
- ddapi-0.5.1/ddapi/ddapi/main.py +0 -79
- {ddapi-0.5.1 → ddapi-0.6.0}/LICENSE +0 -0
- {ddapi-0.5.1 → ddapi-0.6.0}/README.md +0 -0
- {ddapi-0.5.1 → ddapi-0.6.0}/ddapi/ddapi.egg-info/SOURCES.txt +0 -0
- {ddapi-0.5.1 → ddapi-0.6.0}/ddapi/ddapi.egg-info/dependency_links.txt +0 -0
- {ddapi-0.5.1 → ddapi-0.6.0}/ddapi/ddapi.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""the module is made for the convenience of users to download and use"""
|
|
2
|
-
from .main import DDnetApi, DDstats,
|
|
2
|
+
from .main import DDnetApi, DDstats, DDStatsDB
|
|
3
3
|
from .dataclass import *
|
|
4
4
|
|
|
5
|
-
__version__ = "0.
|
|
5
|
+
__version__ = "0.6.0"
|
|
6
6
|
__author__ = 'ByFox'
|
|
7
7
|
__LICENSE__ = "MIT"
|
|
@@ -6,6 +6,7 @@ from pydantic import BaseModel, Field
|
|
|
6
6
|
|
|
7
7
|
rm_list = ["DD-Persian", "/vDQMHSss8W"]
|
|
8
8
|
|
|
9
|
+
|
|
9
10
|
# DDStats
|
|
10
11
|
|
|
11
12
|
|
|
@@ -407,29 +408,26 @@ class Master(BaseModel):
|
|
|
407
408
|
yield i.info.clients
|
|
408
409
|
|
|
409
410
|
def get_clans(self, limit: int = 50, rm: list[str] = None) -> list[tuple[Any, int]]:
|
|
410
|
-
if rm is None
|
|
411
|
-
|
|
412
|
-
rm.extend(rm_list)
|
|
411
|
+
remove_list = [] if rm is None else rm
|
|
412
|
+
remove_list.extend(["DD-Persian", ''])
|
|
413
413
|
if not self.servers:
|
|
414
414
|
return []
|
|
415
|
-
dat = Counter(client.clan
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
for i in
|
|
415
|
+
dat: Counter[Any] = Counter(client.clan
|
|
416
|
+
for server in self.servers
|
|
417
|
+
for client in server.info.clients
|
|
418
|
+
if server.info is not None
|
|
419
|
+
and server.info.clients is not None
|
|
420
|
+
and client != '')
|
|
421
|
+
for i in remove_list:
|
|
422
422
|
del dat[i]
|
|
423
423
|
return sorted(dat.items(), key=lambda x: x[1], reverse=True)[:limit]
|
|
424
424
|
|
|
425
|
-
def get_count_servers(self, limit: int = 10) -> list
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
dat.append((i.get_count_client(), add, i.info.game_type, i.info.name))
|
|
432
|
-
return sorted(dat, key=lambda x: x[0], reverse=True)[:limit]
|
|
425
|
+
def get_count_servers(self, limit: int = 10) -> list:
|
|
426
|
+
return sorted((
|
|
427
|
+
(i.get_count_client(), i.addresses[0], i.info.game_type, i.info.name)
|
|
428
|
+
if isinstance(i.addresses, list)
|
|
429
|
+
else (i.get_count_client(), i.addresses, i.info.game_type, i.info.name)
|
|
430
|
+
for i in self.servers), key=lambda x: x[0], reverse=True)[:limit]
|
|
433
431
|
|
|
434
432
|
|
|
435
433
|
class QueryData(BaseModel):
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from abc import ABC
|
|
3
|
+
from json import JSONDecoder
|
|
4
|
+
from typing import Union, Any
|
|
5
|
+
from urllib.parse import quote
|
|
6
|
+
|
|
7
|
+
import aiohttp
|
|
8
|
+
from aiohttp import ClientSession, ClientConnectorError
|
|
9
|
+
from aiohttp.typedefs import DEFAULT_JSON_DECODER
|
|
10
|
+
|
|
11
|
+
from .dataclass import Player
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class API(ABC):
|
|
15
|
+
def __init__(self,
|
|
16
|
+
session: ClientSession = None,
|
|
17
|
+
json_loads: JSONDecoder = DEFAULT_JSON_DECODER,
|
|
18
|
+
emojis: dict[str, str] = None):
|
|
19
|
+
self.emojis = emojis
|
|
20
|
+
if emojis is None:
|
|
21
|
+
self.emojis = {"fox": "🦊"}
|
|
22
|
+
self.session = session
|
|
23
|
+
self.json_loads = json_loads
|
|
24
|
+
self.url = "https://ddstats.qwik.space/player/json?player={0}"
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def powered() -> str:
|
|
28
|
+
"""Return an empty string."""
|
|
29
|
+
return ''
|
|
30
|
+
|
|
31
|
+
async def _get_emoji(self, player_name: str) -> str:
|
|
32
|
+
"""Return an emoji for the given player name."""
|
|
33
|
+
for name, emoji in self.emojis.items():
|
|
34
|
+
if name in player_name.lower():
|
|
35
|
+
return emoji
|
|
36
|
+
return ''
|
|
37
|
+
|
|
38
|
+
async def _send(self, url: str) -> Union[dict, None]:
|
|
39
|
+
"""Send a GET request to the given URL and return the response as JSON."""
|
|
40
|
+
if self.session is None:
|
|
41
|
+
self.session = aiohttp.ClientSession()
|
|
42
|
+
try:
|
|
43
|
+
async with self.session.get(url) as req:
|
|
44
|
+
if req.status == 200:
|
|
45
|
+
return await req.json(loads=self.json_loads)
|
|
46
|
+
return
|
|
47
|
+
except ClientConnectorError:
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
async def _generate(
|
|
51
|
+
self,
|
|
52
|
+
url: str,
|
|
53
|
+
model,
|
|
54
|
+
k: str = None,
|
|
55
|
+
emoji: str = None
|
|
56
|
+
) -> Union[Any, None]:
|
|
57
|
+
"""Generate a model instance from the given URL and optional keyword arguments."""
|
|
58
|
+
dat = await self._send(url)
|
|
59
|
+
if dat is None:
|
|
60
|
+
return
|
|
61
|
+
if k is not None:
|
|
62
|
+
dat = {k: dat}
|
|
63
|
+
if emoji is not None:
|
|
64
|
+
dat["emoji"] = emoji
|
|
65
|
+
return model(**dat)
|
|
66
|
+
|
|
67
|
+
async def close(self) -> None:
|
|
68
|
+
"""Close the aiohttp session."""
|
|
69
|
+
if self.session:
|
|
70
|
+
await self.session.close()
|
|
71
|
+
self.session = None
|
|
72
|
+
|
|
73
|
+
async def player(self, player_name: str) -> Player:
|
|
74
|
+
"""Fetch player data from the API and return it as a dictionary."""
|
|
75
|
+
return await self._generate(
|
|
76
|
+
self.url.format(quote(player_name)),
|
|
77
|
+
Player,
|
|
78
|
+
emoji=await self._get_emoji(player_name)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
async def players(self, player_names: list[str]) -> tuple[Player]:
|
|
82
|
+
"""Fetch data for multiple players from the API and return it as a list of dictionaries."""
|
|
83
|
+
tasks = [self.player(player_name) for player_name in player_names]
|
|
84
|
+
return await asyncio.gather(*tasks)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Union
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
from .dataclass import DDPlayer, Master, Query, DMap, DDStatsSql, DDStatus
|
|
6
|
+
from .deflt import API
|
|
7
|
+
|
|
8
|
+
__all__ = (
|
|
9
|
+
"DDnetApi",
|
|
10
|
+
"DDstats",
|
|
11
|
+
"DDStatsDB"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DDStatsDB(API):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super().__init__()
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def powered() -> str:
|
|
21
|
+
return 'ddstats.org'
|
|
22
|
+
|
|
23
|
+
async def _rt(self, url: str):
|
|
24
|
+
return await self._generate(url, DDStatsSql)
|
|
25
|
+
|
|
26
|
+
async def next(self, dd: DDStatsSql):
|
|
27
|
+
return await self._rt(dd.next_url)
|
|
28
|
+
|
|
29
|
+
async def sql(self, sql: str):
|
|
30
|
+
return await self._rt(f"https://db.ddstats.org/ddnet-7b306cf?sql={quote(sql)}")
|
|
31
|
+
|
|
32
|
+
async def mapinfo(self):
|
|
33
|
+
return await self._rt("https://db.ddstats.org/ddnet-7b306cf/mapinfo.json")
|
|
34
|
+
|
|
35
|
+
async def teamrace(self):
|
|
36
|
+
return await self._rt("https://db.ddstats.org/ddnet-7b306cf/teamrace.json")
|
|
37
|
+
|
|
38
|
+
async def race(self):
|
|
39
|
+
return await self._rt("https://db.ddstats.org/ddnet-7b306cf/race.json")
|
|
40
|
+
|
|
41
|
+
async def maps(self):
|
|
42
|
+
return await self._rt("https://db.ddstats.org/ddnet-7b306cf/maps.json")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class DDnetApi(API):
|
|
46
|
+
def __init__(self):
|
|
47
|
+
super().__init__()
|
|
48
|
+
self.url = "https://ddnet.org/"
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def powered() -> str:
|
|
52
|
+
return 'ddnet.org'
|
|
53
|
+
|
|
54
|
+
async def player(self, player_name: str) -> DDPlayer:
|
|
55
|
+
"""Fetch player data from the API and return it as a dictionary."""
|
|
56
|
+
return await self._generate(
|
|
57
|
+
f"{self.url}players/?json2={quote(player_name)}",
|
|
58
|
+
DDPlayer,
|
|
59
|
+
emoji=await self._get_emoji(player_name)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
async def players(self, player_names: list[str]) -> tuple[DDPlayer]:
|
|
63
|
+
"""Fetch data for multiple players from the API and return it as a list of dictionaries."""
|
|
64
|
+
tasks = [self.player(player_name) for player_name in player_names]
|
|
65
|
+
return await asyncio.gather(*tasks)
|
|
66
|
+
|
|
67
|
+
async def status(self) -> Union[DDStatus, None]:
|
|
68
|
+
return await self._generate(
|
|
69
|
+
f"{self.url}status/json/stats.json",
|
|
70
|
+
DDStatus
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
async def query(self, player: str) -> Union[Query, None]:
|
|
74
|
+
return await self._generate(
|
|
75
|
+
f"{self.url}players/?query={quote(player)}",
|
|
76
|
+
Query,
|
|
77
|
+
"data",
|
|
78
|
+
emoji=await self._get_emoji(player)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
async def master(self) -> Union[Master, None]:
|
|
82
|
+
return await self._generate(
|
|
83
|
+
"https://master1.ddnet.org/ddnet/15/servers.json",
|
|
84
|
+
Master
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
async def map(self, map_name: str) -> Union[DMap, None]:
|
|
88
|
+
return await self._generate(
|
|
89
|
+
f"{self.url}maps/?json={quote(map_name)}",
|
|
90
|
+
DMap
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class DDstats(API):
|
|
95
|
+
@staticmethod
|
|
96
|
+
def powered() -> str:
|
|
97
|
+
return 'ddstats.tw'
|
ddapi-0.5.1/ddapi/ddapi/deflt.py
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
from abc import ABC
|
|
2
|
-
from typing import Union, Any
|
|
3
|
-
|
|
4
|
-
import aiohttp
|
|
5
|
-
from aiohttp import ClientSession, ClientConnectorError
|
|
6
|
-
from aiohttp.typedefs import DEFAULT_JSON_DECODER, JSONDecoder
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class API(ABC):
|
|
10
|
-
def __init__(self,
|
|
11
|
-
session: ClientSession = None,
|
|
12
|
-
json_loads: JSONDecoder = DEFAULT_JSON_DECODER,
|
|
13
|
-
emojis: dict[str, str] = None):
|
|
14
|
-
self.emojis = emojis
|
|
15
|
-
if emojis is None:
|
|
16
|
-
self.emojis = {"fox": "🦊"}
|
|
17
|
-
self.session = session
|
|
18
|
-
self.json_loads = json_loads
|
|
19
|
-
self.url = "https://ddstats.qwik.space/player/json?player={0}"
|
|
20
|
-
|
|
21
|
-
def __del__(self):
|
|
22
|
-
self.session = None
|
|
23
|
-
|
|
24
|
-
@staticmethod
|
|
25
|
-
def powered() -> str:
|
|
26
|
-
return ''
|
|
27
|
-
|
|
28
|
-
def _get_emoji(self, player_name: str) -> str:
|
|
29
|
-
for name, emoji in self.emojis.items():
|
|
30
|
-
if name in player_name.lower():
|
|
31
|
-
return emoji
|
|
32
|
-
return ''
|
|
33
|
-
|
|
34
|
-
def closed(self):
|
|
35
|
-
return self.session.closed is None or self.session.closed
|
|
36
|
-
|
|
37
|
-
async def _send(self, url: str) -> Union[dict, None]:
|
|
38
|
-
if self.session is None:
|
|
39
|
-
self.session = aiohttp.ClientSession()
|
|
40
|
-
try:
|
|
41
|
-
async with self.session.get(url) as req:
|
|
42
|
-
if req.status == 200:
|
|
43
|
-
usr = await req.text()
|
|
44
|
-
if usr == '{}' or usr is None:
|
|
45
|
-
return
|
|
46
|
-
del usr
|
|
47
|
-
return await req.json(loads=self.json_loads)
|
|
48
|
-
return
|
|
49
|
-
except ClientConnectorError:
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
async def _generate(self, url: str, model, k: str = None, emoji: str = None) -> Any:
|
|
53
|
-
dat = await self._send(url)
|
|
54
|
-
if dat is None:
|
|
55
|
-
return
|
|
56
|
-
if k is not None:
|
|
57
|
-
dat = {k: dat}
|
|
58
|
-
if emoji is not None:
|
|
59
|
-
dat["emoji"] = emoji
|
|
60
|
-
return model(**dat)
|
|
61
|
-
|
|
62
|
-
async def close(self) -> None:
|
|
63
|
-
return await self.session.close()
|
ddapi-0.5.1/ddapi/ddapi/main.py
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
from datetime import datetime
|
|
3
|
-
from typing import Union
|
|
4
|
-
from urllib.parse import quote
|
|
5
|
-
|
|
6
|
-
from .dataclass import DDPlayer, Master, Player, Query, DMap, DDStatsSql, DDStatus
|
|
7
|
-
from .deflt import API
|
|
8
|
-
|
|
9
|
-
REG_SERVER = r"^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]).){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$"
|
|
10
|
-
|
|
11
|
-
__all__ = (
|
|
12
|
-
"utc_times",
|
|
13
|
-
"DDnetApi",
|
|
14
|
-
"DDstats",
|
|
15
|
-
"DDStatsDB"
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def utc_times(timestamp: int | float) -> datetime:
|
|
20
|
-
return datetime.utcfromtimestamp(timestamp)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class DDStatsDB(API):
|
|
24
|
-
@staticmethod
|
|
25
|
-
def powered() -> str:
|
|
26
|
-
return 'ddstats.org'
|
|
27
|
-
|
|
28
|
-
async def _rt(self, url: str):
|
|
29
|
-
return await self._generate(url, DDStatsSql)
|
|
30
|
-
|
|
31
|
-
async def next(self, dd: DDStatsSql):
|
|
32
|
-
return await self._rt(dd.next_url)
|
|
33
|
-
|
|
34
|
-
async def sql(self, sql: str):
|
|
35
|
-
return await self._rt(f"https://db.ddstats.org/ddnet-7b306cf?sql={quote(sql)}")
|
|
36
|
-
|
|
37
|
-
async def mapinfo(self):
|
|
38
|
-
return await self._rt("https://db.ddstats.org/ddnet-7b306cf/mapinfo.json")
|
|
39
|
-
|
|
40
|
-
async def teamrace(self):
|
|
41
|
-
return await self._rt("https://db.ddstats.org/ddnet-7b306cf/teamrace.json")
|
|
42
|
-
|
|
43
|
-
async def race(self):
|
|
44
|
-
return await self._rt("https://db.ddstats.org/ddnet-7b306cf/race.json")
|
|
45
|
-
|
|
46
|
-
async def maps(self):
|
|
47
|
-
return await self._rt("https://db.ddstats.org/ddnet-7b306cf/maps.json")
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class DDnetApi(API):
|
|
51
|
-
@staticmethod
|
|
52
|
-
def powered() -> str:
|
|
53
|
-
return 'ddnet.org'
|
|
54
|
-
|
|
55
|
-
async def player(self, player: str) -> Union[DDPlayer, None]:
|
|
56
|
-
return await self._generate(f"https://ddnet.org/players/?json2={quote(player)}", DDPlayer,
|
|
57
|
-
emoji=self._get_emoji(player))
|
|
58
|
-
|
|
59
|
-
async def status(self) -> Union[DDStatus, None]:
|
|
60
|
-
return await self._generate("https://ddnet.org/status/json/stats.json", DDStatus)
|
|
61
|
-
|
|
62
|
-
async def query(self, player: str) -> Union[Query, None]:
|
|
63
|
-
return await self._generate(f"https://ddnet.org/players/?query={quote(player)}", Query, "data",
|
|
64
|
-
emoji=self._get_emoji(player))
|
|
65
|
-
|
|
66
|
-
async def master(self) -> Union[Master, None]:
|
|
67
|
-
return await self._generate("https://master1.ddnet.org/ddnet/15/servers.json", Master)
|
|
68
|
-
|
|
69
|
-
async def map(self, map_name: str) -> Union[DMap, None]:
|
|
70
|
-
return await self._generate(f"https://ddnet.org/maps/?json={quote(map_name)}", DMap)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
class DDstats(API):
|
|
74
|
-
@staticmethod
|
|
75
|
-
def powered() -> str:
|
|
76
|
-
return 'ddstats.tw'
|
|
77
|
-
|
|
78
|
-
async def player(self, nickname) -> Union[Player, None]:
|
|
79
|
-
return await self._generate(f"https://ddstats.tw/player/json?player={quote(nickname)}", Player, emoji=self._get_emoji(nickname))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|