ecapi-sdk 0.1.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.
- ecapi_sdk-0.1.0/PKG-INFO +120 -0
- ecapi_sdk-0.1.0/README.md +106 -0
- ecapi_sdk-0.1.0/pyproject.toml +31 -0
- ecapi_sdk-0.1.0/setup.cfg +4 -0
- ecapi_sdk-0.1.0/src/ecapi_sdk/__init__.py +4 -0
- ecapi_sdk-0.1.0/src/ecapi_sdk/client.py +794 -0
- ecapi_sdk-0.1.0/src/ecapi_sdk/py.typed +1 -0
- ecapi_sdk-0.1.0/src/ecapi_sdk.egg-info/PKG-INFO +120 -0
- ecapi_sdk-0.1.0/src/ecapi_sdk.egg-info/SOURCES.txt +9 -0
- ecapi_sdk-0.1.0/src/ecapi_sdk.egg-info/dependency_links.txt +1 -0
- ecapi_sdk-0.1.0/src/ecapi_sdk.egg-info/top_level.txt +1 -0
ecapi_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ecapi-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ECAPI SDK for Python
|
|
5
|
+
Author: EaseCation
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ecapi,easecation,sdk,python
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# ecapi-sdk (Python)
|
|
16
|
+
|
|
17
|
+
ECAPI 的 Python SDK,封装了常用接口,并内置两种鉴权方式:
|
|
18
|
+
|
|
19
|
+
- `X-API-Key`(IAM API Key)
|
|
20
|
+
- `X-App-Session-Token`(App Session Token)
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install ecapi-sdk
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 快速开始
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from ecapi_sdk import ECAPIClient
|
|
32
|
+
|
|
33
|
+
client = ECAPIClient(
|
|
34
|
+
base_url="https://your-ecapi-host",
|
|
35
|
+
auth={"type": "apiKey", "apiKey": "ec_xxx"},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
me = client.user.get_me()
|
|
39
|
+
player = client.player.get_info({"name": "player123"})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
切换为 App Session Token:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
client.set_app_session_token("your_app_session_token")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 客户端能力
|
|
49
|
+
|
|
50
|
+
- 统一请求入口:`client.request(method, path, ...)`
|
|
51
|
+
- 默认超时:15 秒(`timeout_seconds` 可覆盖)
|
|
52
|
+
- 支持 per-request 覆盖鉴权:`auth=...`
|
|
53
|
+
- 非 2xx 响应抛出 `ECAPIError`(包含 `status`、`payload`、`url`)
|
|
54
|
+
|
|
55
|
+
## API 覆盖
|
|
56
|
+
|
|
57
|
+
> SDK 已封装主要高频接口;其余接口可用 `client.request(...)` 直调。
|
|
58
|
+
|
|
59
|
+
### 用户
|
|
60
|
+
|
|
61
|
+
- `client.user.get_me()` → `GET /user/me`
|
|
62
|
+
- `client.user.login_by_password(payload)` → `POST /user/auth`
|
|
63
|
+
- `client.user.login_by_oauth2(payload)` → `POST /user/oauth2`
|
|
64
|
+
- `client.user.refresh_token(payload)` → `POST /user/refresh`
|
|
65
|
+
- `client.user.get_openid()` → `GET /user/openid`
|
|
66
|
+
- `client.user.list_all()` → `GET /user/all`
|
|
67
|
+
- `client.user.update_permissions(payload)` → `PUT /user/permissions`
|
|
68
|
+
- `client.user.get_by_id(id)` → `GET /user/:id`
|
|
69
|
+
|
|
70
|
+
### 玩家
|
|
71
|
+
|
|
72
|
+
- `client.player.get_info()` / `search_ecid()` / `get_user_data()` / `query_netease()`
|
|
73
|
+
- `client.player.set_rank_level(payload)` / `clear_respack_cache()`
|
|
74
|
+
- `client.player.get_wallet()` / `list_gaming_tags()` / `operate_gaming_tag(payload)`
|
|
75
|
+
- `client.player.get_last_played()` / `get_stage_record()`
|
|
76
|
+
- `client.player.get_headicon()` / `get_skin()`(返回 `bytes`)
|
|
77
|
+
- `client.player.batch_netease_nicknames(payload)`
|
|
78
|
+
- `client.player.get_binding()` / `reset_binding(payload)` / `update_binding(payload)`
|
|
79
|
+
- `client.player.update_user_data(nick, payload)`
|
|
80
|
+
- `client.player.update_password(ecid, payload)` / `get_password_hash(ecid)`
|
|
81
|
+
|
|
82
|
+
#### 玩家子模块
|
|
83
|
+
|
|
84
|
+
- `client.player.score.*` → `/player/score*`
|
|
85
|
+
- `client.player.tasks.*` → `/player/:ecid/tasks*`
|
|
86
|
+
- `client.player.merchandise.*` → `/player/:ecid/merchandise*`
|
|
87
|
+
- `client.player.year_summary.*` → `/player/year-summary/*`
|
|
88
|
+
- `client.player.vote.process_rewards()` → `GET /player/vote`
|
|
89
|
+
|
|
90
|
+
### 管理与处罚
|
|
91
|
+
|
|
92
|
+
- `client.admin.*` → `/admin/*`
|
|
93
|
+
- `client.punish.*` → `/punish/*`
|
|
94
|
+
- `client.ban.*` → `/ban/*`
|
|
95
|
+
- `client.permission.*` → `/permission`
|
|
96
|
+
|
|
97
|
+
### 日志与审计
|
|
98
|
+
|
|
99
|
+
- `client.log.*` → `/log/*`
|
|
100
|
+
- `client.audit.*` → `/audit/*`
|
|
101
|
+
|
|
102
|
+
### 配置与内容
|
|
103
|
+
|
|
104
|
+
- `client.stage.*`(含 `stage.logs.*`)→ `/stage/*`
|
|
105
|
+
- `client.item.get_commodity()` → `/item/commodity`
|
|
106
|
+
- `client.cfglang.*` → `/cfglang*`
|
|
107
|
+
- `client.globalkv.*` → `/globalkv*`
|
|
108
|
+
- `client.broadcast.*` → `/broadcast*`
|
|
109
|
+
- `client.pull_config.pull()` → `/pull-config`
|
|
110
|
+
|
|
111
|
+
### 运营与系统
|
|
112
|
+
|
|
113
|
+
- `client.order.*` → `/order/*`
|
|
114
|
+
- `client.count.*` → `/count/*`
|
|
115
|
+
- `client.servers.*` → `/servers*`
|
|
116
|
+
- `client.lobby.list()` → `/lobby/list`
|
|
117
|
+
- `client.easechat.*` → `/easechat/*`
|
|
118
|
+
- `client.monitor.spam_detector.*` → `/monitor/spam-detector/*`
|
|
119
|
+
- `client.system.get_health()` → `/health`
|
|
120
|
+
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# ecapi-sdk (Python)
|
|
2
|
+
|
|
3
|
+
ECAPI 的 Python SDK,封装了常用接口,并内置两种鉴权方式:
|
|
4
|
+
|
|
5
|
+
- `X-API-Key`(IAM API Key)
|
|
6
|
+
- `X-App-Session-Token`(App Session Token)
|
|
7
|
+
|
|
8
|
+
## 安装
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install ecapi-sdk
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 快速开始
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from ecapi_sdk import ECAPIClient
|
|
18
|
+
|
|
19
|
+
client = ECAPIClient(
|
|
20
|
+
base_url="https://your-ecapi-host",
|
|
21
|
+
auth={"type": "apiKey", "apiKey": "ec_xxx"},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
me = client.user.get_me()
|
|
25
|
+
player = client.player.get_info({"name": "player123"})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
切换为 App Session Token:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
client.set_app_session_token("your_app_session_token")
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 客户端能力
|
|
35
|
+
|
|
36
|
+
- 统一请求入口:`client.request(method, path, ...)`
|
|
37
|
+
- 默认超时:15 秒(`timeout_seconds` 可覆盖)
|
|
38
|
+
- 支持 per-request 覆盖鉴权:`auth=...`
|
|
39
|
+
- 非 2xx 响应抛出 `ECAPIError`(包含 `status`、`payload`、`url`)
|
|
40
|
+
|
|
41
|
+
## API 覆盖
|
|
42
|
+
|
|
43
|
+
> SDK 已封装主要高频接口;其余接口可用 `client.request(...)` 直调。
|
|
44
|
+
|
|
45
|
+
### 用户
|
|
46
|
+
|
|
47
|
+
- `client.user.get_me()` → `GET /user/me`
|
|
48
|
+
- `client.user.login_by_password(payload)` → `POST /user/auth`
|
|
49
|
+
- `client.user.login_by_oauth2(payload)` → `POST /user/oauth2`
|
|
50
|
+
- `client.user.refresh_token(payload)` → `POST /user/refresh`
|
|
51
|
+
- `client.user.get_openid()` → `GET /user/openid`
|
|
52
|
+
- `client.user.list_all()` → `GET /user/all`
|
|
53
|
+
- `client.user.update_permissions(payload)` → `PUT /user/permissions`
|
|
54
|
+
- `client.user.get_by_id(id)` → `GET /user/:id`
|
|
55
|
+
|
|
56
|
+
### 玩家
|
|
57
|
+
|
|
58
|
+
- `client.player.get_info()` / `search_ecid()` / `get_user_data()` / `query_netease()`
|
|
59
|
+
- `client.player.set_rank_level(payload)` / `clear_respack_cache()`
|
|
60
|
+
- `client.player.get_wallet()` / `list_gaming_tags()` / `operate_gaming_tag(payload)`
|
|
61
|
+
- `client.player.get_last_played()` / `get_stage_record()`
|
|
62
|
+
- `client.player.get_headicon()` / `get_skin()`(返回 `bytes`)
|
|
63
|
+
- `client.player.batch_netease_nicknames(payload)`
|
|
64
|
+
- `client.player.get_binding()` / `reset_binding(payload)` / `update_binding(payload)`
|
|
65
|
+
- `client.player.update_user_data(nick, payload)`
|
|
66
|
+
- `client.player.update_password(ecid, payload)` / `get_password_hash(ecid)`
|
|
67
|
+
|
|
68
|
+
#### 玩家子模块
|
|
69
|
+
|
|
70
|
+
- `client.player.score.*` → `/player/score*`
|
|
71
|
+
- `client.player.tasks.*` → `/player/:ecid/tasks*`
|
|
72
|
+
- `client.player.merchandise.*` → `/player/:ecid/merchandise*`
|
|
73
|
+
- `client.player.year_summary.*` → `/player/year-summary/*`
|
|
74
|
+
- `client.player.vote.process_rewards()` → `GET /player/vote`
|
|
75
|
+
|
|
76
|
+
### 管理与处罚
|
|
77
|
+
|
|
78
|
+
- `client.admin.*` → `/admin/*`
|
|
79
|
+
- `client.punish.*` → `/punish/*`
|
|
80
|
+
- `client.ban.*` → `/ban/*`
|
|
81
|
+
- `client.permission.*` → `/permission`
|
|
82
|
+
|
|
83
|
+
### 日志与审计
|
|
84
|
+
|
|
85
|
+
- `client.log.*` → `/log/*`
|
|
86
|
+
- `client.audit.*` → `/audit/*`
|
|
87
|
+
|
|
88
|
+
### 配置与内容
|
|
89
|
+
|
|
90
|
+
- `client.stage.*`(含 `stage.logs.*`)→ `/stage/*`
|
|
91
|
+
- `client.item.get_commodity()` → `/item/commodity`
|
|
92
|
+
- `client.cfglang.*` → `/cfglang*`
|
|
93
|
+
- `client.globalkv.*` → `/globalkv*`
|
|
94
|
+
- `client.broadcast.*` → `/broadcast*`
|
|
95
|
+
- `client.pull_config.pull()` → `/pull-config`
|
|
96
|
+
|
|
97
|
+
### 运营与系统
|
|
98
|
+
|
|
99
|
+
- `client.order.*` → `/order/*`
|
|
100
|
+
- `client.count.*` → `/count/*`
|
|
101
|
+
- `client.servers.*` → `/servers*`
|
|
102
|
+
- `client.lobby.list()` → `/lobby/list`
|
|
103
|
+
- `client.easechat.*` → `/easechat/*`
|
|
104
|
+
- `client.monitor.spam_detector.*` → `/monitor/spam-detector/*`
|
|
105
|
+
- `client.system.get_health()` → `/health`
|
|
106
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ecapi-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "ECAPI SDK for Python"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "EaseCation" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ecapi", "easecation", "sdk", "python"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[tool.setuptools]
|
|
24
|
+
package-dir = {"" = "src"}
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
where = ["src"]
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.package-data]
|
|
30
|
+
ecapi_sdk = ["py.typed"]
|
|
31
|
+
|
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any, Dict, Mapping, Optional, Sequence, Tuple, Union
|
|
6
|
+
from urllib.error import HTTPError, URLError
|
|
7
|
+
from urllib.parse import quote, urlencode
|
|
8
|
+
from urllib.request import Request, urlopen
|
|
9
|
+
|
|
10
|
+
Primitive = Union[str, int, float, bool, None]
|
|
11
|
+
QueryValue = Union[Primitive, Sequence[Primitive]]
|
|
12
|
+
QueryParams = Mapping[str, QueryValue]
|
|
13
|
+
Headers = Dict[str, str]
|
|
14
|
+
|
|
15
|
+
AuthCredentials = Dict[str, str]
|
|
16
|
+
UNSET = object()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ECAPIError(Exception):
|
|
20
|
+
def __init__(self, message: str, status: int, payload: Any, url: str) -> None:
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
self.status = status
|
|
23
|
+
self.payload = payload
|
|
24
|
+
self.url = url
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _normalize_base_url(base_url: str) -> str:
|
|
28
|
+
return base_url[:-1] if base_url.endswith("/") else base_url
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _to_query_pairs(query: Optional[QueryParams]) -> Sequence[Tuple[str, str]]:
|
|
32
|
+
if not query:
|
|
33
|
+
return []
|
|
34
|
+
|
|
35
|
+
pairs = []
|
|
36
|
+
for key, raw_value in query.items():
|
|
37
|
+
if raw_value is None:
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
if isinstance(raw_value, (list, tuple)):
|
|
41
|
+
for item in raw_value:
|
|
42
|
+
if item is not None:
|
|
43
|
+
pairs.append((key, str(item)))
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
pairs.append((key, str(raw_value)))
|
|
47
|
+
return pairs
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _parse_body(raw: bytes, content_type: str, response_type: str) -> Any:
|
|
51
|
+
if response_type == "bytes":
|
|
52
|
+
return raw
|
|
53
|
+
|
|
54
|
+
text = raw.decode("utf-8", errors="replace")
|
|
55
|
+
if response_type == "text":
|
|
56
|
+
return text
|
|
57
|
+
|
|
58
|
+
if not raw:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
if "application/json" in content_type:
|
|
62
|
+
try:
|
|
63
|
+
return json.loads(text)
|
|
64
|
+
except json.JSONDecodeError:
|
|
65
|
+
return text
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
return json.loads(text)
|
|
69
|
+
except json.JSONDecodeError:
|
|
70
|
+
return text
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class _HttpClient:
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
base_url: str,
|
|
77
|
+
auth: Optional[AuthCredentials] = None,
|
|
78
|
+
timeout_seconds: float = 15.0,
|
|
79
|
+
default_headers: Optional[Headers] = None,
|
|
80
|
+
) -> None:
|
|
81
|
+
self._base_url = _normalize_base_url(base_url)
|
|
82
|
+
self._auth = auth
|
|
83
|
+
self._timeout_seconds = timeout_seconds
|
|
84
|
+
self._default_headers = default_headers or {}
|
|
85
|
+
|
|
86
|
+
def set_auth(self, auth: Optional[AuthCredentials]) -> None:
|
|
87
|
+
self._auth = auth
|
|
88
|
+
|
|
89
|
+
def clear_auth(self) -> None:
|
|
90
|
+
self._auth = None
|
|
91
|
+
|
|
92
|
+
def set_api_key(self, api_key: str) -> None:
|
|
93
|
+
self._auth = {"type": "apiKey", "apiKey": api_key}
|
|
94
|
+
|
|
95
|
+
def set_app_session_token(self, app_session_token: str) -> None:
|
|
96
|
+
self._auth = {"type": "appSessionToken", "appSessionToken": app_session_token}
|
|
97
|
+
|
|
98
|
+
def request(
|
|
99
|
+
self,
|
|
100
|
+
method: str,
|
|
101
|
+
path: str,
|
|
102
|
+
*,
|
|
103
|
+
query: Optional[QueryParams] = None,
|
|
104
|
+
body: Any = None,
|
|
105
|
+
headers: Optional[Headers] = None,
|
|
106
|
+
response_type: str = "json",
|
|
107
|
+
auth: Any = UNSET,
|
|
108
|
+
) -> Any:
|
|
109
|
+
request_path = path if path.startswith("/") else f"/{path}"
|
|
110
|
+
query_pairs = _to_query_pairs(query)
|
|
111
|
+
query_string = f"?{urlencode(query_pairs, doseq=True)}" if query_pairs else ""
|
|
112
|
+
url = f"{self._base_url}{request_path}{query_string}"
|
|
113
|
+
|
|
114
|
+
merged_headers: Headers = {"Accept": "application/json"}
|
|
115
|
+
merged_headers.update(self._default_headers)
|
|
116
|
+
if headers:
|
|
117
|
+
merged_headers.update(headers)
|
|
118
|
+
|
|
119
|
+
resolved_auth = self._auth if auth is UNSET else auth
|
|
120
|
+
if resolved_auth:
|
|
121
|
+
auth_type = resolved_auth.get("type")
|
|
122
|
+
if auth_type == "apiKey":
|
|
123
|
+
merged_headers["X-API-Key"] = resolved_auth["apiKey"]
|
|
124
|
+
elif auth_type == "appSessionToken":
|
|
125
|
+
merged_headers["X-App-Session-Token"] = resolved_auth["appSessionToken"]
|
|
126
|
+
|
|
127
|
+
request_data: Optional[bytes] = None
|
|
128
|
+
if body is not None:
|
|
129
|
+
content_type = merged_headers.get("Content-Type", "")
|
|
130
|
+
if not content_type:
|
|
131
|
+
merged_headers["Content-Type"] = "application/json"
|
|
132
|
+
content_type = "application/json"
|
|
133
|
+
|
|
134
|
+
if "application/json" in content_type and not isinstance(body, (str, bytes, bytearray)):
|
|
135
|
+
request_data = json.dumps(body).encode("utf-8")
|
|
136
|
+
elif isinstance(body, bytes):
|
|
137
|
+
request_data = body
|
|
138
|
+
elif isinstance(body, bytearray):
|
|
139
|
+
request_data = bytes(body)
|
|
140
|
+
else:
|
|
141
|
+
request_data = str(body).encode("utf-8")
|
|
142
|
+
|
|
143
|
+
req = Request(url=url, data=request_data, method=method.upper(), headers=merged_headers)
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
with urlopen(req, timeout=self._timeout_seconds) as response:
|
|
147
|
+
raw = response.read()
|
|
148
|
+
content_type = response.headers.get("Content-Type", "")
|
|
149
|
+
if response.status == 204:
|
|
150
|
+
return None
|
|
151
|
+
return _parse_body(raw, content_type, response_type)
|
|
152
|
+
except HTTPError as error:
|
|
153
|
+
raw = error.read() if hasattr(error, "read") else b""
|
|
154
|
+
content_type = error.headers.get("Content-Type", "") if error.headers else ""
|
|
155
|
+
payload = _parse_body(raw, content_type, response_type) if raw else None
|
|
156
|
+
raise ECAPIError(
|
|
157
|
+
f"Request failed with status {error.code}",
|
|
158
|
+
error.code,
|
|
159
|
+
payload,
|
|
160
|
+
url,
|
|
161
|
+
) from error
|
|
162
|
+
except URLError as error:
|
|
163
|
+
raise ECAPIError("Network request failed", 0, str(error.reason), url) from error
|
|
164
|
+
except TimeoutError as error:
|
|
165
|
+
raise ECAPIError("Request timeout", 0, None, url) from error
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class _BaseApi:
|
|
169
|
+
def __init__(self, http: _HttpClient) -> None:
|
|
170
|
+
self._http = http
|
|
171
|
+
|
|
172
|
+
def _get(self, path: str, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
173
|
+
return self._http.request("GET", path, query=query, **kwargs)
|
|
174
|
+
|
|
175
|
+
def _post(self, path: str, body: Any = None, **kwargs: Any) -> Any:
|
|
176
|
+
return self._http.request("POST", path, body=body, **kwargs)
|
|
177
|
+
|
|
178
|
+
def _put(self, path: str, body: Any = None, **kwargs: Any) -> Any:
|
|
179
|
+
return self._http.request("PUT", path, body=body, **kwargs)
|
|
180
|
+
|
|
181
|
+
def _patch(self, path: str, body: Any = None, **kwargs: Any) -> Any:
|
|
182
|
+
return self._http.request("PATCH", path, body=body, **kwargs)
|
|
183
|
+
|
|
184
|
+
def _delete(
|
|
185
|
+
self,
|
|
186
|
+
path: str,
|
|
187
|
+
query: Optional[QueryParams] = None,
|
|
188
|
+
body: Any = None,
|
|
189
|
+
**kwargs: Any,
|
|
190
|
+
) -> Any:
|
|
191
|
+
return self._http.request("DELETE", path, query=query, body=body, **kwargs)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class UserAPI(_BaseApi):
|
|
195
|
+
def get_me(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
196
|
+
return self._get("/user/me", query, **kwargs)
|
|
197
|
+
|
|
198
|
+
def login_by_password(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
199
|
+
return self._post("/user/auth", payload, **kwargs)
|
|
200
|
+
|
|
201
|
+
def login_by_oauth2(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
202
|
+
return self._post("/user/oauth2", payload, **kwargs)
|
|
203
|
+
|
|
204
|
+
def refresh_token(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
205
|
+
return self._post("/user/refresh", payload, **kwargs)
|
|
206
|
+
|
|
207
|
+
def get_openid(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
208
|
+
return self._get("/user/openid", query, **kwargs)
|
|
209
|
+
|
|
210
|
+
def list_all(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
211
|
+
return self._get("/user/all", query, **kwargs)
|
|
212
|
+
|
|
213
|
+
def update_permissions(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
214
|
+
return self._put("/user/permissions", payload, **kwargs)
|
|
215
|
+
|
|
216
|
+
def get_by_id(self, user_id: str, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
217
|
+
return self._get(f"/user/{quote(user_id, safe='')}", query, **kwargs)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class PlayerScoreAPI(_BaseApi):
|
|
221
|
+
def get_score(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
222
|
+
return self._get("/player/score", query, **kwargs)
|
|
223
|
+
|
|
224
|
+
def get_stages(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
225
|
+
return self._get("/player/score/stages", query, **kwargs)
|
|
226
|
+
|
|
227
|
+
def get_top(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
228
|
+
return self._get("/player/score/top", query, **kwargs)
|
|
229
|
+
|
|
230
|
+
def delete_top(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
231
|
+
return self._delete("/player/score/top", query=query, **kwargs)
|
|
232
|
+
|
|
233
|
+
def get_config(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
234
|
+
return self._get("/player/score/config", query, **kwargs)
|
|
235
|
+
|
|
236
|
+
def get_leaderboard(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
237
|
+
return self._get("/player/score/leaderboard", query, **kwargs)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class PlayerTasksAPI(_BaseApi):
|
|
241
|
+
def list(self, ecid: str, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
242
|
+
return self._get(f"/player/{quote(ecid, safe='')}/tasks", query, **kwargs)
|
|
243
|
+
|
|
244
|
+
def create(self, ecid: str, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
245
|
+
return self._post(f"/player/{quote(ecid, safe='')}/tasks", payload, **kwargs)
|
|
246
|
+
|
|
247
|
+
def update(self, ecid: str, task_id: str, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
248
|
+
return self._put(
|
|
249
|
+
f"/player/{quote(ecid, safe='')}/tasks/{quote(task_id, safe='')}",
|
|
250
|
+
payload,
|
|
251
|
+
**kwargs,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
def delete_one(
|
|
255
|
+
self, ecid: str, task_id: str, query: Optional[QueryParams] = None, **kwargs: Any
|
|
256
|
+
) -> Any:
|
|
257
|
+
return self._delete(
|
|
258
|
+
f"/player/{quote(ecid, safe='')}/tasks/{quote(task_id, safe='')}",
|
|
259
|
+
query=query,
|
|
260
|
+
**kwargs,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class PlayerMerchandiseAPI(_BaseApi):
|
|
265
|
+
def list(self, ecid: str, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
266
|
+
return self._get(f"/player/{quote(ecid, safe='')}/merchandise", query, **kwargs)
|
|
267
|
+
|
|
268
|
+
def create(self, ecid: str, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
269
|
+
return self._post(f"/player/{quote(ecid, safe='')}/merchandise", payload, **kwargs)
|
|
270
|
+
|
|
271
|
+
def update(self, ecid: str, id_item: str, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
272
|
+
return self._put(
|
|
273
|
+
f"/player/{quote(ecid, safe='')}/merchandise/{quote(id_item, safe='')}",
|
|
274
|
+
payload,
|
|
275
|
+
**kwargs,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
def delete_one(
|
|
279
|
+
self, ecid: str, id_item: str, query: Optional[QueryParams] = None, **kwargs: Any
|
|
280
|
+
) -> Any:
|
|
281
|
+
return self._delete(
|
|
282
|
+
f"/player/{quote(ecid, safe='')}/merchandise/{quote(id_item, safe='')}",
|
|
283
|
+
query=query,
|
|
284
|
+
**kwargs,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class PlayerYearSummaryAPI(_BaseApi):
|
|
289
|
+
def get_basic_info(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
290
|
+
return self._get("/player/year-summary/basic-info", query, **kwargs)
|
|
291
|
+
|
|
292
|
+
def get_login_stats(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
293
|
+
return self._get("/player/year-summary/login-stats", query, **kwargs)
|
|
294
|
+
|
|
295
|
+
def get_game_stats(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
296
|
+
return self._get("/player/year-summary/game-stats", query, **kwargs)
|
|
297
|
+
|
|
298
|
+
def get_rank_data(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
299
|
+
return self._get("/player/year-summary/rank-data", query, **kwargs)
|
|
300
|
+
|
|
301
|
+
def get_currency_data(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
302
|
+
return self._get("/player/year-summary/currency-data", query, **kwargs)
|
|
303
|
+
|
|
304
|
+
def get_social_data(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
305
|
+
return self._get("/player/year-summary/social-data", query, **kwargs)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class PlayerVoteAPI(_BaseApi):
|
|
309
|
+
def process_rewards(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
310
|
+
return self._get("/player/vote", query, **kwargs)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class PlayerAPI(_BaseApi):
|
|
314
|
+
def __init__(self, http: _HttpClient) -> None:
|
|
315
|
+
super().__init__(http)
|
|
316
|
+
self.score = PlayerScoreAPI(http)
|
|
317
|
+
self.tasks = PlayerTasksAPI(http)
|
|
318
|
+
self.merchandise = PlayerMerchandiseAPI(http)
|
|
319
|
+
self.year_summary = PlayerYearSummaryAPI(http)
|
|
320
|
+
self.vote = PlayerVoteAPI(http)
|
|
321
|
+
|
|
322
|
+
def get_info(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
323
|
+
return self._get("/player/info", query, **kwargs)
|
|
324
|
+
|
|
325
|
+
def search_ecid(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
326
|
+
return self._get("/player/searchecid", query, **kwargs)
|
|
327
|
+
|
|
328
|
+
def get_user_data(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
329
|
+
return self._get("/player/userdata", query, **kwargs)
|
|
330
|
+
|
|
331
|
+
def query_netease(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
332
|
+
return self._get("/player/querynetease", query, **kwargs)
|
|
333
|
+
|
|
334
|
+
def set_rank_level(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
335
|
+
return self._post("/player/ranklevel", payload, **kwargs)
|
|
336
|
+
|
|
337
|
+
def clear_respack_cache(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
338
|
+
return self._delete("/player/respack/cache", query=query, **kwargs)
|
|
339
|
+
|
|
340
|
+
def get_wallet(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
341
|
+
return self._get("/player/wallet", query, **kwargs)
|
|
342
|
+
|
|
343
|
+
def list_gaming_tags(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
344
|
+
return self._get("/player/gaming-tag", query, **kwargs)
|
|
345
|
+
|
|
346
|
+
def operate_gaming_tag(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
347
|
+
return self._post("/player/gaming-tag", payload, **kwargs)
|
|
348
|
+
|
|
349
|
+
def get_last_played(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
350
|
+
return self._get("/player/last-played", query, **kwargs)
|
|
351
|
+
|
|
352
|
+
def get_stage_record(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
353
|
+
return self._get("/player/stage/record", query, **kwargs)
|
|
354
|
+
|
|
355
|
+
def get_headicon(self, query: Optional[QueryParams] = None, **kwargs: Any) -> bytes:
|
|
356
|
+
return self._http.request(
|
|
357
|
+
"GET",
|
|
358
|
+
"/player/headicon",
|
|
359
|
+
query=query,
|
|
360
|
+
response_type="bytes",
|
|
361
|
+
**kwargs,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
def get_skin(self, query: Optional[QueryParams] = None, **kwargs: Any) -> bytes:
|
|
365
|
+
return self._http.request(
|
|
366
|
+
"GET",
|
|
367
|
+
"/player/skin",
|
|
368
|
+
query=query,
|
|
369
|
+
response_type="bytes",
|
|
370
|
+
**kwargs,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
def batch_netease_nicknames(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
374
|
+
return self._post("/player/neteasenicknames", payload, **kwargs)
|
|
375
|
+
|
|
376
|
+
def get_binding(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
377
|
+
return self._get("/player/binding", query, **kwargs)
|
|
378
|
+
|
|
379
|
+
def reset_binding(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
380
|
+
return self._post("/player/reset-binding", payload, **kwargs)
|
|
381
|
+
|
|
382
|
+
def update_binding(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
383
|
+
return self._post("/player/update-binding", payload, **kwargs)
|
|
384
|
+
|
|
385
|
+
def update_user_data(self, nick: str, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
386
|
+
return self._post(f"/player/update/{quote(nick, safe='')}", payload, **kwargs)
|
|
387
|
+
|
|
388
|
+
def update_password(self, ecid: str, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
389
|
+
return self._post(f"/player/password/{quote(ecid, safe='')}", payload, **kwargs)
|
|
390
|
+
|
|
391
|
+
def get_password_hash(
|
|
392
|
+
self, ecid: str, query: Optional[QueryParams] = None, **kwargs: Any
|
|
393
|
+
) -> Any:
|
|
394
|
+
return self._get(f"/player/password/{quote(ecid, safe='')}", query, **kwargs)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class AdminAPI(_BaseApi):
|
|
398
|
+
def send_mail(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
399
|
+
return self._post("/admin/mail", payload, **kwargs)
|
|
400
|
+
|
|
401
|
+
def prism_device_ban(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
402
|
+
return self._post("/admin/prism-device-ban", payload, **kwargs)
|
|
403
|
+
|
|
404
|
+
def get_prism_device_ban_log(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
405
|
+
return self._get("/admin/prism-device-ban-log", query, **kwargs)
|
|
406
|
+
|
|
407
|
+
def upsert_overwatch_case(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
408
|
+
return self._post("/admin/overwatch", payload, **kwargs)
|
|
409
|
+
|
|
410
|
+
def get_overwatch_case(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
411
|
+
return self._get("/admin/overwatch", query, **kwargs)
|
|
412
|
+
|
|
413
|
+
def get_operation_logs(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
414
|
+
return self._get("/admin/operation", query, **kwargs)
|
|
415
|
+
|
|
416
|
+
def get_operation_statistics(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
417
|
+
return self._get("/admin/operation/statistic", query, **kwargs)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class PunishAPI(_BaseApi):
|
|
421
|
+
def hack(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
422
|
+
return self._post("/punish/hack", payload, **kwargs)
|
|
423
|
+
|
|
424
|
+
def ban(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
425
|
+
return self._post("/punish/ban", payload, **kwargs)
|
|
426
|
+
|
|
427
|
+
def unban(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
428
|
+
return self._post("/punish/unban", payload, **kwargs)
|
|
429
|
+
|
|
430
|
+
def mute(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
431
|
+
return self._post("/punish/mute", payload, **kwargs)
|
|
432
|
+
|
|
433
|
+
def unmute(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
434
|
+
return self._post("/punish/unmute", payload, **kwargs)
|
|
435
|
+
|
|
436
|
+
def warn(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
437
|
+
return self._post("/punish/warn", payload, **kwargs)
|
|
438
|
+
|
|
439
|
+
def kick(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
440
|
+
return self._post("/punish/kick", payload, **kwargs)
|
|
441
|
+
|
|
442
|
+
def clear_degree(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
443
|
+
return self._post("/punish/clear-degree", payload, **kwargs)
|
|
444
|
+
|
|
445
|
+
def overwatch(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
446
|
+
return self._post("/punish/overwatch", payload, **kwargs)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
class BanAPI(_BaseApi):
|
|
450
|
+
def ban(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
451
|
+
return self._post("/ban", payload, **kwargs)
|
|
452
|
+
|
|
453
|
+
def clear_degree(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
454
|
+
return self._post("/ban/clear-degree", payload, **kwargs)
|
|
455
|
+
|
|
456
|
+
def parkour_punish(
|
|
457
|
+
self,
|
|
458
|
+
query: Optional[QueryParams] = None,
|
|
459
|
+
payload: Optional[Mapping[str, Any]] = None,
|
|
460
|
+
**kwargs: Any,
|
|
461
|
+
) -> Any:
|
|
462
|
+
return self._http.request("POST", "/ban/parkour-punish", query=query, body=payload, **kwargs)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
class PermissionAPI(_BaseApi):
|
|
466
|
+
def get(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
467
|
+
return self._get("/permission", query, **kwargs)
|
|
468
|
+
|
|
469
|
+
def upsert(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
470
|
+
return self._post("/permission", payload, **kwargs)
|
|
471
|
+
|
|
472
|
+
def delete_one(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
473
|
+
return self._delete("/permission", query=query, **kwargs)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
class LogAPI(_BaseApi):
|
|
477
|
+
def get_action(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
478
|
+
return self._get("/log/action", query, **kwargs)
|
|
479
|
+
|
|
480
|
+
def get_chat(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
481
|
+
return self._get("/log/chat", query, **kwargs)
|
|
482
|
+
|
|
483
|
+
def get_auth(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
484
|
+
return self._get("/log/auth", query, **kwargs)
|
|
485
|
+
|
|
486
|
+
def get_teaming(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
487
|
+
return self._get("/log/teaming", query, **kwargs)
|
|
488
|
+
|
|
489
|
+
def get_latest_teaming(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
490
|
+
return self._get("/log/latestTeaming", query, **kwargs)
|
|
491
|
+
|
|
492
|
+
def get_command(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
493
|
+
return self._get("/log/command", query, **kwargs)
|
|
494
|
+
|
|
495
|
+
def get_mail(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
496
|
+
return self._get("/log/mail", query, **kwargs)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
class OrderAPI(_BaseApi):
|
|
500
|
+
def get_deliver(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
501
|
+
return self._get("/order/deliver", query, **kwargs)
|
|
502
|
+
|
|
503
|
+
def get_logs(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
504
|
+
return self._get("/order/logs", query, **kwargs)
|
|
505
|
+
|
|
506
|
+
def get_exchange_logs(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
507
|
+
return self._get("/order/exchange-logs", query, **kwargs)
|
|
508
|
+
|
|
509
|
+
def get_download_count(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
510
|
+
return self._get("/order/download/count", query, **kwargs)
|
|
511
|
+
|
|
512
|
+
def create_download_task(
|
|
513
|
+
self,
|
|
514
|
+
query: Optional[QueryParams] = None,
|
|
515
|
+
payload: Optional[Mapping[str, Any]] = None,
|
|
516
|
+
**kwargs: Any,
|
|
517
|
+
) -> Any:
|
|
518
|
+
return self._http.request("POST", "/order/download/create", query=query, body=payload, **kwargs)
|
|
519
|
+
|
|
520
|
+
def get_download_status(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
521
|
+
return self._get("/order/download/status", query, **kwargs)
|
|
522
|
+
|
|
523
|
+
def list_download_tasks(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
524
|
+
return self._get("/order/download/list", query, **kwargs)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
class StageLogAPI(_BaseApi):
|
|
528
|
+
def list(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
529
|
+
return self._get("/stage/log/list", query, **kwargs)
|
|
530
|
+
|
|
531
|
+
def query(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
532
|
+
return self._get("/stage/log/query", query, **kwargs)
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
class StageAPI(_BaseApi):
|
|
536
|
+
def __init__(self, http: _HttpClient) -> None:
|
|
537
|
+
super().__init__(http)
|
|
538
|
+
self.logs = StageLogAPI(http)
|
|
539
|
+
|
|
540
|
+
def get_types(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
541
|
+
return self._get("/stage/types", query, **kwargs)
|
|
542
|
+
|
|
543
|
+
def create(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
544
|
+
return self._post("/stage/create", payload, **kwargs)
|
|
545
|
+
|
|
546
|
+
def get_info(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
547
|
+
return self._get("/stage/info", query, **kwargs)
|
|
548
|
+
|
|
549
|
+
def get_db_id(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
550
|
+
return self._get("/stage/db-id", query, **kwargs)
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
class ItemAPI(_BaseApi):
|
|
554
|
+
def get_commodity(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
555
|
+
return self._get("/item/commodity", query, **kwargs)
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
class CountAPI(_BaseApi):
|
|
559
|
+
def get_history(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
560
|
+
return self._get("/count/history", query, **kwargs)
|
|
561
|
+
|
|
562
|
+
def get_latest(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
563
|
+
return self._get("/count/latest", query, **kwargs)
|
|
564
|
+
|
|
565
|
+
def get_types(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
566
|
+
return self._get("/count/types", query, **kwargs)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
class ServersAPI(_BaseApi):
|
|
570
|
+
def list(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
571
|
+
return self._get("/servers", query, **kwargs)
|
|
572
|
+
|
|
573
|
+
def get_statistics(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
574
|
+
return self._get("/servers/statistics", query, **kwargs)
|
|
575
|
+
|
|
576
|
+
def get_mainstack_event(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
577
|
+
return self._get("/servers/mainstack-event", query, **kwargs)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
class LobbyAPI(_BaseApi):
|
|
581
|
+
def list(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
582
|
+
return self._get("/lobby/list", query, **kwargs)
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
class CfgLangAPI(_BaseApi):
|
|
586
|
+
def list(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
587
|
+
return self._get("/cfglang", query, **kwargs)
|
|
588
|
+
|
|
589
|
+
def compare(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
590
|
+
return self._get("/cfglang/compare", query, **kwargs)
|
|
591
|
+
|
|
592
|
+
def deploy(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
593
|
+
return self._post("/cfglang/deploy", payload, **kwargs)
|
|
594
|
+
|
|
595
|
+
def create(
|
|
596
|
+
self, query: Optional[QueryParams], payload: Mapping[str, Any], **kwargs: Any
|
|
597
|
+
) -> Any:
|
|
598
|
+
return self._http.request("POST", "/cfglang", query=query, body=payload, **kwargs)
|
|
599
|
+
|
|
600
|
+
def update(
|
|
601
|
+
self, query: Optional[QueryParams], payload: Mapping[str, Any], **kwargs: Any
|
|
602
|
+
) -> Any:
|
|
603
|
+
return self._http.request("PATCH", "/cfglang", query=query, body=payload, **kwargs)
|
|
604
|
+
|
|
605
|
+
def delete_one(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
606
|
+
return self._delete("/cfglang", query=query, **kwargs)
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
class GlobalKVAPI(_BaseApi):
|
|
610
|
+
def list(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
611
|
+
return self._get("/globalkv", query, **kwargs)
|
|
612
|
+
|
|
613
|
+
def get_one(self, key: str, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
614
|
+
return self._get(f"/globalkv/{quote(key, safe='')}", query, **kwargs)
|
|
615
|
+
|
|
616
|
+
def create(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
617
|
+
return self._post("/globalkv", payload, **kwargs)
|
|
618
|
+
|
|
619
|
+
def update(self, key: str, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
620
|
+
return self._put(f"/globalkv/{quote(key, safe='')}", payload, **kwargs)
|
|
621
|
+
|
|
622
|
+
def delete_one(self, key: str, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
623
|
+
return self._delete(f"/globalkv/{quote(key, safe='')}", query=query, **kwargs)
|
|
624
|
+
|
|
625
|
+
def delete_batch(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
626
|
+
return self._delete("/globalkv", body=payload, **kwargs)
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
class BroadcastAPI(_BaseApi):
|
|
630
|
+
def list(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
631
|
+
return self._get("/broadcast", query, **kwargs)
|
|
632
|
+
|
|
633
|
+
def compare(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
634
|
+
return self._get("/broadcast/compare", query, **kwargs)
|
|
635
|
+
|
|
636
|
+
def deploy(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
637
|
+
return self._post("/broadcast/deploy", payload, **kwargs)
|
|
638
|
+
|
|
639
|
+
def get_one(self, msg: str, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
640
|
+
return self._get(f"/broadcast/{quote(msg, safe='')}", query, **kwargs)
|
|
641
|
+
|
|
642
|
+
def create(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
643
|
+
return self._post("/broadcast", payload, **kwargs)
|
|
644
|
+
|
|
645
|
+
def update(self, msg: str, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
646
|
+
return self._put(f"/broadcast/{quote(msg, safe='')}", payload, **kwargs)
|
|
647
|
+
|
|
648
|
+
def delete_one(self, msg: str, payload: Optional[Mapping[str, Any]] = None, **kwargs: Any) -> Any:
|
|
649
|
+
return self._delete(f"/broadcast/{quote(msg, safe='')}", body=payload, **kwargs)
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
class EaseChatAPI(_BaseApi):
|
|
653
|
+
def send_hlaba(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
654
|
+
return self._post("/easechat/hlaba", payload, **kwargs)
|
|
655
|
+
|
|
656
|
+
def list_subscriptions(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
657
|
+
return self._get("/easechat/subscriptions", query, **kwargs)
|
|
658
|
+
|
|
659
|
+
def create_subscription(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
660
|
+
return self._post("/easechat/subscriptions", payload, **kwargs)
|
|
661
|
+
|
|
662
|
+
def delete_subscription(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
663
|
+
return self._delete("/easechat/subscriptions", query=query, **kwargs)
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
class AuditAPI(_BaseApi):
|
|
667
|
+
def get_auth_logs(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
668
|
+
return self._get("/audit/auth", query, **kwargs)
|
|
669
|
+
|
|
670
|
+
def get_player_logs(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
671
|
+
return self._get("/audit/player", query, **kwargs)
|
|
672
|
+
|
|
673
|
+
def get_punishment_logs(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
674
|
+
return self._get("/audit/punishment", query, **kwargs)
|
|
675
|
+
|
|
676
|
+
def get_permission_logs(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
677
|
+
return self._get("/audit/permission", query, **kwargs)
|
|
678
|
+
|
|
679
|
+
def get_admin_logs(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
680
|
+
return self._get("/audit/admin", query, **kwargs)
|
|
681
|
+
|
|
682
|
+
def get_config_logs(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
683
|
+
return self._get("/audit/config", query, **kwargs)
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
class SpamDetectorAPI(_BaseApi):
|
|
687
|
+
def get_status(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
688
|
+
return self._get("/monitor/spam-detector/status", query, **kwargs)
|
|
689
|
+
|
|
690
|
+
def control(self, payload: Mapping[str, Any], **kwargs: Any) -> Any:
|
|
691
|
+
return self._post("/monitor/spam-detector/control", payload, **kwargs)
|
|
692
|
+
|
|
693
|
+
def get_stats(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
694
|
+
return self._get("/monitor/spam-detector/stats", query, **kwargs)
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
class MonitorAPI(_BaseApi):
|
|
698
|
+
def __init__(self, http: _HttpClient) -> None:
|
|
699
|
+
super().__init__(http)
|
|
700
|
+
self.spam_detector = SpamDetectorAPI(http)
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
class PullConfigAPI(_BaseApi):
|
|
704
|
+
def pull(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
705
|
+
return self._get("/pull-config", query, **kwargs)
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
class SystemAPI(_BaseApi):
|
|
709
|
+
def get_root(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
710
|
+
return self._get("/", query, response_type="text", **kwargs)
|
|
711
|
+
|
|
712
|
+
def get_health(self, query: Optional[QueryParams] = None, **kwargs: Any) -> Any:
|
|
713
|
+
return self._get("/health", query, **kwargs)
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
@dataclass
|
|
717
|
+
class ClientOptions:
|
|
718
|
+
base_url: str
|
|
719
|
+
auth: Optional[AuthCredentials] = None
|
|
720
|
+
timeout_seconds: float = 15.0
|
|
721
|
+
default_headers: Optional[Headers] = None
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
class ECAPIClient:
|
|
725
|
+
def __init__(
|
|
726
|
+
self,
|
|
727
|
+
*,
|
|
728
|
+
base_url: str,
|
|
729
|
+
auth: Optional[AuthCredentials] = None,
|
|
730
|
+
timeout_seconds: float = 15.0,
|
|
731
|
+
default_headers: Optional[Headers] = None,
|
|
732
|
+
) -> None:
|
|
733
|
+
self._http = _HttpClient(
|
|
734
|
+
base_url=base_url,
|
|
735
|
+
auth=auth,
|
|
736
|
+
timeout_seconds=timeout_seconds,
|
|
737
|
+
default_headers=default_headers,
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
self.user = UserAPI(self._http)
|
|
741
|
+
self.player = PlayerAPI(self._http)
|
|
742
|
+
self.admin = AdminAPI(self._http)
|
|
743
|
+
self.punish = PunishAPI(self._http)
|
|
744
|
+
self.ban = BanAPI(self._http)
|
|
745
|
+
self.permission = PermissionAPI(self._http)
|
|
746
|
+
self.log = LogAPI(self._http)
|
|
747
|
+
self.order = OrderAPI(self._http)
|
|
748
|
+
self.stage = StageAPI(self._http)
|
|
749
|
+
self.item = ItemAPI(self._http)
|
|
750
|
+
self.count = CountAPI(self._http)
|
|
751
|
+
self.servers = ServersAPI(self._http)
|
|
752
|
+
self.lobby = LobbyAPI(self._http)
|
|
753
|
+
self.cfglang = CfgLangAPI(self._http)
|
|
754
|
+
self.globalkv = GlobalKVAPI(self._http)
|
|
755
|
+
self.broadcast = BroadcastAPI(self._http)
|
|
756
|
+
self.easechat = EaseChatAPI(self._http)
|
|
757
|
+
self.audit = AuditAPI(self._http)
|
|
758
|
+
self.monitor = MonitorAPI(self._http)
|
|
759
|
+
self.pull_config = PullConfigAPI(self._http)
|
|
760
|
+
self.system = SystemAPI(self._http)
|
|
761
|
+
|
|
762
|
+
def set_auth(self, auth: Optional[AuthCredentials]) -> None:
|
|
763
|
+
self._http.set_auth(auth)
|
|
764
|
+
|
|
765
|
+
def set_api_key(self, api_key: str) -> None:
|
|
766
|
+
self._http.set_api_key(api_key)
|
|
767
|
+
|
|
768
|
+
def set_app_session_token(self, app_session_token: str) -> None:
|
|
769
|
+
self._http.set_app_session_token(app_session_token)
|
|
770
|
+
|
|
771
|
+
def clear_auth(self) -> None:
|
|
772
|
+
self._http.clear_auth()
|
|
773
|
+
|
|
774
|
+
def request(
|
|
775
|
+
self,
|
|
776
|
+
method: str,
|
|
777
|
+
path: str,
|
|
778
|
+
*,
|
|
779
|
+
query: Optional[QueryParams] = None,
|
|
780
|
+
body: Any = None,
|
|
781
|
+
headers: Optional[Headers] = None,
|
|
782
|
+
response_type: str = "json",
|
|
783
|
+
auth: Any = UNSET,
|
|
784
|
+
) -> Any:
|
|
785
|
+
return self._http.request(
|
|
786
|
+
method,
|
|
787
|
+
path,
|
|
788
|
+
query=query,
|
|
789
|
+
body=body,
|
|
790
|
+
headers=headers,
|
|
791
|
+
response_type=response_type,
|
|
792
|
+
auth=auth,
|
|
793
|
+
)
|
|
794
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ecapi-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ECAPI SDK for Python
|
|
5
|
+
Author: EaseCation
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ecapi,easecation,sdk,python
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# ecapi-sdk (Python)
|
|
16
|
+
|
|
17
|
+
ECAPI 的 Python SDK,封装了常用接口,并内置两种鉴权方式:
|
|
18
|
+
|
|
19
|
+
- `X-API-Key`(IAM API Key)
|
|
20
|
+
- `X-App-Session-Token`(App Session Token)
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install ecapi-sdk
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 快速开始
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from ecapi_sdk import ECAPIClient
|
|
32
|
+
|
|
33
|
+
client = ECAPIClient(
|
|
34
|
+
base_url="https://your-ecapi-host",
|
|
35
|
+
auth={"type": "apiKey", "apiKey": "ec_xxx"},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
me = client.user.get_me()
|
|
39
|
+
player = client.player.get_info({"name": "player123"})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
切换为 App Session Token:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
client.set_app_session_token("your_app_session_token")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 客户端能力
|
|
49
|
+
|
|
50
|
+
- 统一请求入口:`client.request(method, path, ...)`
|
|
51
|
+
- 默认超时:15 秒(`timeout_seconds` 可覆盖)
|
|
52
|
+
- 支持 per-request 覆盖鉴权:`auth=...`
|
|
53
|
+
- 非 2xx 响应抛出 `ECAPIError`(包含 `status`、`payload`、`url`)
|
|
54
|
+
|
|
55
|
+
## API 覆盖
|
|
56
|
+
|
|
57
|
+
> SDK 已封装主要高频接口;其余接口可用 `client.request(...)` 直调。
|
|
58
|
+
|
|
59
|
+
### 用户
|
|
60
|
+
|
|
61
|
+
- `client.user.get_me()` → `GET /user/me`
|
|
62
|
+
- `client.user.login_by_password(payload)` → `POST /user/auth`
|
|
63
|
+
- `client.user.login_by_oauth2(payload)` → `POST /user/oauth2`
|
|
64
|
+
- `client.user.refresh_token(payload)` → `POST /user/refresh`
|
|
65
|
+
- `client.user.get_openid()` → `GET /user/openid`
|
|
66
|
+
- `client.user.list_all()` → `GET /user/all`
|
|
67
|
+
- `client.user.update_permissions(payload)` → `PUT /user/permissions`
|
|
68
|
+
- `client.user.get_by_id(id)` → `GET /user/:id`
|
|
69
|
+
|
|
70
|
+
### 玩家
|
|
71
|
+
|
|
72
|
+
- `client.player.get_info()` / `search_ecid()` / `get_user_data()` / `query_netease()`
|
|
73
|
+
- `client.player.set_rank_level(payload)` / `clear_respack_cache()`
|
|
74
|
+
- `client.player.get_wallet()` / `list_gaming_tags()` / `operate_gaming_tag(payload)`
|
|
75
|
+
- `client.player.get_last_played()` / `get_stage_record()`
|
|
76
|
+
- `client.player.get_headicon()` / `get_skin()`(返回 `bytes`)
|
|
77
|
+
- `client.player.batch_netease_nicknames(payload)`
|
|
78
|
+
- `client.player.get_binding()` / `reset_binding(payload)` / `update_binding(payload)`
|
|
79
|
+
- `client.player.update_user_data(nick, payload)`
|
|
80
|
+
- `client.player.update_password(ecid, payload)` / `get_password_hash(ecid)`
|
|
81
|
+
|
|
82
|
+
#### 玩家子模块
|
|
83
|
+
|
|
84
|
+
- `client.player.score.*` → `/player/score*`
|
|
85
|
+
- `client.player.tasks.*` → `/player/:ecid/tasks*`
|
|
86
|
+
- `client.player.merchandise.*` → `/player/:ecid/merchandise*`
|
|
87
|
+
- `client.player.year_summary.*` → `/player/year-summary/*`
|
|
88
|
+
- `client.player.vote.process_rewards()` → `GET /player/vote`
|
|
89
|
+
|
|
90
|
+
### 管理与处罚
|
|
91
|
+
|
|
92
|
+
- `client.admin.*` → `/admin/*`
|
|
93
|
+
- `client.punish.*` → `/punish/*`
|
|
94
|
+
- `client.ban.*` → `/ban/*`
|
|
95
|
+
- `client.permission.*` → `/permission`
|
|
96
|
+
|
|
97
|
+
### 日志与审计
|
|
98
|
+
|
|
99
|
+
- `client.log.*` → `/log/*`
|
|
100
|
+
- `client.audit.*` → `/audit/*`
|
|
101
|
+
|
|
102
|
+
### 配置与内容
|
|
103
|
+
|
|
104
|
+
- `client.stage.*`(含 `stage.logs.*`)→ `/stage/*`
|
|
105
|
+
- `client.item.get_commodity()` → `/item/commodity`
|
|
106
|
+
- `client.cfglang.*` → `/cfglang*`
|
|
107
|
+
- `client.globalkv.*` → `/globalkv*`
|
|
108
|
+
- `client.broadcast.*` → `/broadcast*`
|
|
109
|
+
- `client.pull_config.pull()` → `/pull-config`
|
|
110
|
+
|
|
111
|
+
### 运营与系统
|
|
112
|
+
|
|
113
|
+
- `client.order.*` → `/order/*`
|
|
114
|
+
- `client.count.*` → `/count/*`
|
|
115
|
+
- `client.servers.*` → `/servers*`
|
|
116
|
+
- `client.lobby.list()` → `/lobby/list`
|
|
117
|
+
- `client.easechat.*` → `/easechat/*`
|
|
118
|
+
- `client.monitor.spam_detector.*` → `/monitor/spam-detector/*`
|
|
119
|
+
- `client.system.get_health()` → `/health`
|
|
120
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ecapi_sdk
|