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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from .client import ECAPIClient, ECAPIError
2
+
3
+ __all__ = ["ECAPIClient", "ECAPIError"]
4
+
@@ -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,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/ecapi_sdk/__init__.py
4
+ src/ecapi_sdk/client.py
5
+ src/ecapi_sdk/py.typed
6
+ src/ecapi_sdk.egg-info/PKG-INFO
7
+ src/ecapi_sdk.egg-info/SOURCES.txt
8
+ src/ecapi_sdk.egg-info/dependency_links.txt
9
+ src/ecapi_sdk.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ ecapi_sdk