xtree-sdk 0.1.0__py3-none-any.whl

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.
xtree/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """XTREE Python SDK — HTTP client for api.xtree.io."""
2
+
3
+ from xtree.client import XTreeClient
4
+ from xtree.exceptions import XTreeError
5
+
6
+ __all__ = ["XTreeClient", "XTreeError"]
7
+ __version__ = "0.1.0"
xtree/_http.py ADDED
@@ -0,0 +1,111 @@
1
+ """HTTP transport with envelope parsing and retries."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from typing import Any
7
+
8
+ import httpx
9
+
10
+ from xtree.exceptions import XTreeError, error_from_envelope
11
+ from xtree.types import APIResponse, ResponseMeta
12
+
13
+ DEFAULT_BASE_URL = "https://api.xtree.io/api"
14
+ API_KEY_HEADER = "X-XTREE-Key"
15
+ VERIFY_CODE_HEADER = "X-Verify-Code"
16
+ DEFAULT_TIMEOUT = 60.0
17
+ RETRY_STATUS = {429, 502, 503}
18
+ MAX_RETRIES = 3
19
+
20
+
21
+ class HTTPClient:
22
+ def __init__(
23
+ self,
24
+ *,
25
+ api_key: str,
26
+ base_url: str = DEFAULT_BASE_URL,
27
+ verify_code: str | None = None,
28
+ timeout: float = DEFAULT_TIMEOUT,
29
+ client: httpx.Client | None = None,
30
+ ) -> None:
31
+ self.base_url = base_url.rstrip("/")
32
+ self.verify_code = verify_code
33
+ self._owns_client = client is None
34
+ self._client = client or httpx.Client(timeout=timeout)
35
+ self._headers = {
36
+ API_KEY_HEADER: api_key,
37
+ "Accept": "application/json",
38
+ "User-Agent": "xtree-python/0.1.0",
39
+ }
40
+ if verify_code:
41
+ self._headers[VERIFY_CODE_HEADER] = verify_code
42
+ self.last_meta: ResponseMeta | None = None
43
+
44
+ def close(self) -> None:
45
+ if self._owns_client:
46
+ self._client.close()
47
+
48
+ def __enter__(self) -> HTTPClient:
49
+ return self
50
+
51
+ def __exit__(self, *args: object) -> None:
52
+ self.close()
53
+
54
+ def request(
55
+ self,
56
+ method: str,
57
+ path: str,
58
+ *,
59
+ json: dict[str, Any] | None = None,
60
+ params: dict[str, Any] | None = None,
61
+ ) -> APIResponse[Any]:
62
+ url = f"{self.base_url}{path}"
63
+ last_exc: Exception | None = None
64
+
65
+ for attempt in range(MAX_RETRIES + 1):
66
+ try:
67
+ response = self._client.request(
68
+ method,
69
+ url,
70
+ headers=self._headers,
71
+ json=json,
72
+ params=params,
73
+ )
74
+ except httpx.HTTPError as exc:
75
+ last_exc = exc
76
+ if attempt >= MAX_RETRIES:
77
+ raise XTreeError(str(exc), code="TRANSPORT_ERROR") from exc
78
+ time.sleep(2**attempt * 0.5)
79
+ continue
80
+
81
+ payload = _parse_json(response)
82
+ if response.status_code in RETRY_STATUS and attempt < MAX_RETRIES:
83
+ time.sleep(2**attempt * 0.5)
84
+ continue
85
+
86
+ return _unwrap_envelope(payload, status_code=response.status_code)
87
+
88
+ if last_exc:
89
+ raise XTreeError(str(last_exc), code="TRANSPORT_ERROR") from last_exc
90
+ raise XTreeError("Request failed after retries", code="TRANSPORT_ERROR")
91
+
92
+
93
+ def _parse_json(response: httpx.Response) -> dict[str, Any]:
94
+ try:
95
+ data = response.json()
96
+ if isinstance(data, dict):
97
+ return data
98
+ except ValueError:
99
+ pass
100
+ raise XTreeError(
101
+ f"Non-JSON response (HTTP {response.status_code})",
102
+ code="INVALID_RESPONSE",
103
+ status_code=response.status_code,
104
+ )
105
+
106
+
107
+ def _unwrap_envelope(payload: dict[str, Any], *, status_code: int) -> APIResponse[Any]:
108
+ meta = ResponseMeta.from_dict(payload.get("meta") or {})
109
+ if payload.get("ok") is True:
110
+ return APIResponse(data=payload.get("data"), meta=meta)
111
+ raise error_from_envelope(payload, status_code=status_code)
xtree/account.py ADDED
@@ -0,0 +1,19 @@
1
+ """Account endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from xtree._http import HTTPClient
8
+ from xtree.types import APIResponse
9
+
10
+
11
+ class AccountAPI:
12
+ def __init__(self, http: HTTPClient) -> None:
13
+ self._http = http
14
+
15
+ def balance(self) -> APIResponse[dict[str, Any]]:
16
+ """GET /account/balance — free, returns balance_cny."""
17
+ resp = self._http.request("GET", "/account/balance")
18
+ self._http.last_meta = resp.meta
19
+ return resp
@@ -0,0 +1,40 @@
1
+ """AUTO-GENERATED `bilibili` platform client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from xtree._http import HTTPClient
8
+ from xtree.types import APIResponse
9
+
10
+
11
+ class BilibiliAPI:
12
+ def __init__(self, http: HTTPClient) -> None:
13
+ self._http = http
14
+
15
+ def search_videos(self, **body: Any) -> APIResponse[Any]:
16
+ return self._http.request("POST", "/bilibili/search/videos", json=body)
17
+
18
+ def videos_detail(self, **body: Any) -> APIResponse[Any]:
19
+ return self._http.request("POST", "/bilibili/videos/detail", json=body)
20
+
21
+ def videos_comments(self, **body: Any) -> APIResponse[Any]:
22
+ return self._http.request("POST", "/bilibili/videos/comments", json=body)
23
+
24
+ def videos_download(self, **body: Any) -> APIResponse[Any]:
25
+ return self._http.request("POST", "/bilibili/videos/download", json=body)
26
+
27
+ def users_profile(self, **body: Any) -> APIResponse[Any]:
28
+ return self._http.request("POST", "/bilibili/users/profile", json=body)
29
+
30
+ def users_posts(self, **body: Any) -> APIResponse[Any]:
31
+ return self._http.request("POST", "/bilibili/users/posts", json=body)
32
+
33
+ def users_videos(self, **body: Any) -> APIResponse[Any]:
34
+ return self._http.request("POST", "/bilibili/users/videos", json=body)
35
+
36
+ def popular_list(self, **body: Any) -> APIResponse[Any]:
37
+ return self._http.request("POST", "/bilibili/popular/list", json=body)
38
+
39
+ def rank_list(self, **body: Any) -> APIResponse[Any]:
40
+ return self._http.request("POST", "/bilibili/rank/list", json=body)
xtree/cli.py ADDED
@@ -0,0 +1,138 @@
1
+ """xtree CLI — balance, wechat helpers, and raw call."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from xtree.client import XTreeClient
12
+ from xtree.exceptions import XTreeError
13
+
14
+
15
+ def main(argv: list[str] | None = None) -> int:
16
+ parser = argparse.ArgumentParser(prog="xtree", description="XTREE API CLI")
17
+ parser.add_argument("--json", action="store_true", help="Print data as JSON")
18
+ parser.add_argument(
19
+ "--verbose",
20
+ action="store_true",
21
+ help="Print meta to stderr (request_id, cost_cny, balance_cny)",
22
+ )
23
+ sub = parser.add_subparsers(dest="command", required=True)
24
+
25
+ sub.add_parser("balance", help="GET /account/balance")
26
+
27
+ call = sub.add_parser("call", help="Raw API call (any path)")
28
+ call.add_argument("method", choices=["GET", "POST"])
29
+ call.add_argument("path", help="API path, e.g. /wechat/search/articles")
30
+ call.add_argument("--body", default=None, help="JSON body for POST")
31
+ call.add_argument("--body-file", default=None, help="JSON file for POST body")
32
+
33
+ wechat = sub.add_parser("wechat", help="WeChat platform shortcuts")
34
+ wechat_sub = wechat.add_subparsers(dest="wechat_group", required=True)
35
+
36
+ search = wechat_sub.add_parser("search", help="搜一搜")
37
+ search_sub = search.add_subparsers(dest="search_action", required=True)
38
+ for name, help_text in [
39
+ ("articles", "POST /wechat/search/articles"),
40
+ ("videos", "POST /wechat/search/videos"),
41
+ ("mixed", "POST /wechat/search/mixed"),
42
+ ("accounts", "POST /wechat/search/accounts"),
43
+ ("suggestions", "POST /wechat/search/suggestions"),
44
+ ]:
45
+ p = search_sub.add_parser(name, help=help_text)
46
+ p.add_argument("--keyword", required=True)
47
+ p.add_argument("--page", type=int, default=1)
48
+ p.add_argument("--session", default=None)
49
+
50
+ library = wechat_sub.add_parser("library", help="库搜索 (Phase 2)")
51
+ library_sub = library.add_subparsers(dest="library_action", required=True)
52
+ lib_articles = library_sub.add_parser("articles", help="POST /wechat/library/articles/search")
53
+ lib_articles.add_argument("--keyword", required=True)
54
+ lib_articles.add_argument("--page", type=int, default=1)
55
+
56
+ args = parser.parse_args(argv)
57
+ resp = None
58
+
59
+ try:
60
+ with XTreeClient() as client:
61
+ resp = _dispatch(client, args, parser)
62
+ if args.verbose and client.last_meta:
63
+ _print_meta(client.last_meta)
64
+ except (XTreeError, ValueError, json.JSONDecodeError) as exc:
65
+ print(str(exc), file=sys.stderr)
66
+ return 1
67
+
68
+ if args.json or args.command != "balance":
69
+ print(json.dumps(resp.data, ensure_ascii=False, indent=2))
70
+ else:
71
+ balance = (resp.data or {}).get("balance_cny")
72
+ print(f"balance_cny: {balance}")
73
+
74
+ return 0
75
+
76
+
77
+ def _dispatch(client: XTreeClient, args: argparse.Namespace, parser: argparse.ArgumentParser) -> Any:
78
+ if args.command == "balance":
79
+ return client.account.balance()
80
+
81
+ if args.command == "call":
82
+ body = _load_body(args.body, args.body_file)
83
+ return client.request(args.method, args.path, json=body if args.method == "POST" else None)
84
+
85
+ if args.command == "wechat" and args.wechat_group == "search":
86
+ kwargs = {"keyword": args.keyword, "page": args.page, "session": args.session}
87
+ actions = {
88
+ "articles": client.wechat.search.articles,
89
+ "videos": client.wechat.search.videos,
90
+ "mixed": client.wechat.search.mixed,
91
+ "accounts": client.wechat.search.accounts,
92
+ "suggestions": client.wechat.search.suggestions,
93
+ }
94
+ fn = actions.get(args.search_action)
95
+ if fn is None:
96
+ parser.error("unknown wechat search action")
97
+ return fn(**kwargs)
98
+
99
+ if args.command == "wechat" and args.wechat_group == "library":
100
+ if args.library_action == "articles":
101
+ return client.wechat.library.search_articles(args.keyword, page=args.page)
102
+
103
+ parser.error("unknown command")
104
+ return None
105
+
106
+
107
+ def _load_body(body: str | None, body_file: str | None) -> dict[str, Any]:
108
+ if body_file:
109
+ return json.loads(Path(body_file).read_text(encoding="utf-8"))
110
+ if body:
111
+ return json.loads(body)
112
+ return {}
113
+
114
+
115
+ def _print_meta(meta: Any) -> None:
116
+ payload = {
117
+ "request_id": meta.request_id,
118
+ "cost_cny": meta.cost_cny,
119
+ "balance_cny": meta.balance_cny,
120
+ "rate_limit_remaining": meta.rate_limit_remaining,
121
+ }
122
+ for key in (
123
+ "observation_id",
124
+ "observed_at",
125
+ "basis",
126
+ "contract_rev",
127
+ "origin",
128
+ "cache_hit",
129
+ "cache_age_sec",
130
+ ):
131
+ value = getattr(meta, key, None)
132
+ if value is not None:
133
+ payload[key] = value
134
+ print(json.dumps(payload, ensure_ascii=False), file=sys.stderr)
135
+
136
+
137
+ if __name__ == "__main__":
138
+ raise SystemExit(main())
xtree/client.py ADDED
@@ -0,0 +1,89 @@
1
+ """XTreeClient — entry point for the Python SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import Any
7
+
8
+ from xtree._http import DEFAULT_BASE_URL, HTTPClient
9
+ from xtree.account import AccountAPI
10
+ from xtree.types import APIResponse, ResponseMeta
11
+ from xtree.wechat import WechatAPI
12
+ from xtree.douyin import DouyinAPI
13
+ from xtree.xhs import XhsAPI
14
+ from xtree.kuaishou import KuaishouAPI
15
+ from xtree.weibo import WeiboAPI
16
+ from xtree.bilibili import BilibiliAPI
17
+ from xtree.tiktok import TiktokAPI
18
+ from xtree.youtube import YoutubeAPI
19
+ from xtree.facebook import FacebookAPI
20
+ from xtree.taobao import TaobaoAPI
21
+ from xtree.jd import JdAPI
22
+ from xtree.pdd import PddAPI
23
+
24
+
25
+ class XTreeClient:
26
+ """HTTP client for https://api.xtree.io/api."""
27
+
28
+ def __init__(
29
+ self,
30
+ api_key: str | None = None,
31
+ *,
32
+ base_url: str | None = None,
33
+ verify_code: str | None = None,
34
+ timeout: float = 60.0,
35
+ http_client: Any | None = None,
36
+ ) -> None:
37
+ key = api_key or os.environ.get("XTREE_API_KEY")
38
+ if not key:
39
+ raise ValueError(
40
+ "api_key is required (pass api_key= or set XTREE_API_KEY)"
41
+ )
42
+ url = base_url or os.environ.get("XTREE_BASE_URL") or DEFAULT_BASE_URL
43
+ vcode = verify_code or os.environ.get("XTREE_VERIFY_CODE")
44
+ self._http = HTTPClient(
45
+ api_key=key,
46
+ base_url=url,
47
+ verify_code=vcode,
48
+ timeout=timeout,
49
+ client=http_client,
50
+ )
51
+ self.account = AccountAPI(self._http)
52
+ self.wechat = WechatAPI(self._http)
53
+ self.douyin = DouyinAPI(self._http)
54
+ self.xhs = XhsAPI(self._http)
55
+ self.kuaishou = KuaishouAPI(self._http)
56
+ self.weibo = WeiboAPI(self._http)
57
+ self.bilibili = BilibiliAPI(self._http)
58
+ self.tiktok = TiktokAPI(self._http)
59
+ self.youtube = YoutubeAPI(self._http)
60
+ self.facebook = FacebookAPI(self._http)
61
+ self.taobao = TaobaoAPI(self._http)
62
+ self.jd = JdAPI(self._http)
63
+ self.pdd = PddAPI(self._http)
64
+
65
+ @property
66
+ def last_meta(self) -> ResponseMeta | None:
67
+ return self._http.last_meta
68
+
69
+ def request(
70
+ self,
71
+ method: str,
72
+ path: str,
73
+ *,
74
+ json: dict[str, Any] | None = None,
75
+ params: dict[str, Any] | None = None,
76
+ ) -> APIResponse[Any]:
77
+ """Low-level API call with envelope parsing."""
78
+ resp = self._http.request(method, path, json=json, params=params)
79
+ self._http.last_meta = resp.meta
80
+ return resp
81
+
82
+ def close(self) -> None:
83
+ self._http.close()
84
+
85
+ def __enter__(self) -> XTreeClient:
86
+ return self
87
+
88
+ def __exit__(self, *args: object) -> None:
89
+ self.close()
@@ -0,0 +1,40 @@
1
+ """AUTO-GENERATED `douyin` platform client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from xtree._http import HTTPClient
8
+ from xtree.types import APIResponse
9
+
10
+
11
+ class DouyinAPI:
12
+ def __init__(self, http: HTTPClient) -> None:
13
+ self._http = http
14
+
15
+ def videos_detail(self, **body: Any) -> APIResponse[Any]:
16
+ return self._http.request("POST", "/douyin/videos/detail", json=body)
17
+
18
+ def users_profile(self, **body: Any) -> APIResponse[Any]:
19
+ return self._http.request("POST", "/douyin/users/profile", json=body)
20
+
21
+ def users_posts(self, **body: Any) -> APIResponse[Any]:
22
+ return self._http.request("POST", "/douyin/users/posts", json=body)
23
+
24
+ def videos_comments(self, **body: Any) -> APIResponse[Any]:
25
+ return self._http.request("POST", "/douyin/videos/comments", json=body)
26
+
27
+ def videos_sub_comments(self, **body: Any) -> APIResponse[Any]:
28
+ return self._http.request("POST", "/douyin/videos/sub-comments", json=body)
29
+
30
+ def search_videos(self, **body: Any) -> APIResponse[Any]:
31
+ return self._http.request("POST", "/douyin/search/videos", json=body)
32
+
33
+ def search_suggestions(self, **body: Any) -> APIResponse[Any]:
34
+ return self._http.request("POST", "/douyin/search/suggestions", json=body)
35
+
36
+ def users_creator_metrics(self, **body: Any) -> APIResponse[Any]:
37
+ return self._http.request("POST", "/douyin/users/creator-metrics", json=body)
38
+
39
+ def ids_resolve(self, **body: Any) -> APIResponse[Any]:
40
+ return self._http.request("POST", "/douyin/ids/resolve", json=body)
xtree/exceptions.py ADDED
@@ -0,0 +1,89 @@
1
+ """XTREE API errors mapped from envelope.error.code."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+
8
+ class XTreeError(Exception):
9
+ """Base error for failed API calls."""
10
+
11
+ def __init__(
12
+ self,
13
+ message: str,
14
+ *,
15
+ code: str = "UNKNOWN",
16
+ status_code: int | None = None,
17
+ request_id: str | None = None,
18
+ details: Any = None,
19
+ ) -> None:
20
+ super().__init__(message)
21
+ self.code = code
22
+ self.status_code = status_code
23
+ self.request_id = request_id
24
+ self.details = details
25
+
26
+ def __str__(self) -> str:
27
+ parts = [self.code, str(self.args[0])]
28
+ if self.request_id:
29
+ parts.append(f"request_id={self.request_id}")
30
+ return ": ".join(parts)
31
+
32
+
33
+ class XTreeAuthError(XTreeError):
34
+ """401 UNAUTHORIZED or 403 FORBIDDEN."""
35
+
36
+
37
+ class XTreeValidationError(XTreeError):
38
+ """400 VALIDATION_ERROR."""
39
+
40
+
41
+ class XTreeInsufficientBalanceError(XTreeError):
42
+ """402 INSUFFICIENT_BALANCE."""
43
+
44
+
45
+ class XTreeRateLimitError(XTreeError):
46
+ """429 RATE_LIMITED."""
47
+
48
+
49
+ class XTreeServiceError(XTreeError):
50
+ """502 INTERNAL_ERROR(这次没有完成交付)或 503 EDGE_UNAVAILABLE。"""
51
+
52
+
53
+ # 旧名保留,便于已有 except 继续接住 502。
54
+ XTreeUpstreamError = XTreeServiceError
55
+
56
+
57
+ _ERROR_MAP: dict[str, type[XTreeError]] = {
58
+ "UNAUTHORIZED": XTreeAuthError,
59
+ "FORBIDDEN": XTreeAuthError,
60
+ "VALIDATION_ERROR": XTreeValidationError,
61
+ "INSUFFICIENT_BALANCE": XTreeInsufficientBalanceError,
62
+ "RATE_LIMITED": XTreeRateLimitError,
63
+ "UPSTREAM_ERROR": XTreeServiceError,
64
+ "EDGE_UNAVAILABLE": XTreeServiceError,
65
+ "NOT_FOUND": XTreeError,
66
+ }
67
+
68
+
69
+ def error_from_envelope(
70
+ payload: dict[str, Any],
71
+ *,
72
+ status_code: int | None = None,
73
+ ) -> XTreeError:
74
+ err = payload.get("error") or {}
75
+ code = str(err.get("code") or "UNKNOWN")
76
+ message = str(err.get("message") or "API request failed")
77
+ meta = payload.get("meta") or {}
78
+ request_id = meta.get("request_id")
79
+ details = err.get("details")
80
+ cls = _ERROR_MAP.get(code, XTreeError)
81
+ if code == "INTERNAL_ERROR" and status_code == 502:
82
+ cls = XTreeServiceError
83
+ return cls(
84
+ message,
85
+ code=code,
86
+ status_code=status_code,
87
+ request_id=request_id,
88
+ details=details,
89
+ )
@@ -0,0 +1,19 @@
1
+ """AUTO-GENERATED `facebook` platform client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from xtree._http import HTTPClient
8
+ from xtree.types import APIResponse
9
+
10
+
11
+ class FacebookAPI:
12
+ def __init__(self, http: HTTPClient) -> None:
13
+ self._http = http
14
+
15
+ def search_general(self, **body: Any) -> APIResponse[Any]:
16
+ return self._http.request("POST", "/facebook/search/general", json=body)
17
+
18
+ def search_users(self, **body: Any) -> APIResponse[Any]:
19
+ return self._http.request("POST", "/facebook/search/users", json=body)
xtree/jd/__init__.py ADDED
@@ -0,0 +1,22 @@
1
+ """AUTO-GENERATED `jd` platform client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from xtree._http import HTTPClient
8
+ from xtree.types import APIResponse
9
+
10
+
11
+ class JdAPI:
12
+ def __init__(self, http: HTTPClient) -> None:
13
+ self._http = http
14
+
15
+ def items_detail(self, **body: Any) -> APIResponse[Any]:
16
+ return self._http.request("POST", "/jd/items/detail", json=body)
17
+
18
+ def items_comments(self, **body: Any) -> APIResponse[Any]:
19
+ return self._http.request("POST", "/jd/items/comments", json=body)
20
+
21
+ def items_comments_v2(self, **body: Any) -> APIResponse[Any]:
22
+ return self._http.request("POST", "/jd/items/comments/v2", json=body)
@@ -0,0 +1,37 @@
1
+ """AUTO-GENERATED `kuaishou` platform client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from xtree._http import HTTPClient
8
+ from xtree.types import APIResponse
9
+
10
+
11
+ class KuaishouAPI:
12
+ def __init__(self, http: HTTPClient) -> None:
13
+ self._http = http
14
+
15
+ def videos_detail(self, **body: Any) -> APIResponse[Any]:
16
+ return self._http.request("POST", "/kuaishou/videos/detail", json=body)
17
+
18
+ def videos_comments(self, **body: Any) -> APIResponse[Any]:
19
+ return self._http.request("POST", "/kuaishou/videos/comments", json=body)
20
+
21
+ def users_profile(self, **body: Any) -> APIResponse[Any]:
22
+ return self._http.request("POST", "/kuaishou/users/profile", json=body)
23
+
24
+ def users_posts(self, **body: Any) -> APIResponse[Any]:
25
+ return self._http.request("POST", "/kuaishou/users/posts", json=body)
26
+
27
+ def search_videos(self, **body: Any) -> APIResponse[Any]:
28
+ return self._http.request("POST", "/kuaishou/search/videos", json=body)
29
+
30
+ def videos_short_link(self, **body: Any) -> APIResponse[Any]:
31
+ return self._http.request("POST", "/kuaishou/videos/short-link", json=body)
32
+
33
+ def hot_board(self, **body: Any) -> APIResponse[Any]:
34
+ return self._http.request("POST", "/kuaishou/hot/board", json=body)
35
+
36
+ def ids_resolve(self, **body: Any) -> APIResponse[Any]:
37
+ return self._http.request("POST", "/kuaishou/ids/resolve", json=body)
xtree/pdd/__init__.py ADDED
@@ -0,0 +1,22 @@
1
+ """AUTO-GENERATED `pdd` platform client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from xtree._http import HTTPClient
8
+ from xtree.types import APIResponse
9
+
10
+
11
+ class PddAPI:
12
+ def __init__(self, http: HTTPClient) -> None:
13
+ self._http = http
14
+
15
+ def items_comments_submit(self, **body: Any) -> APIResponse[Any]:
16
+ return self._http.request("POST", "/pdd/items/comments/submit", json=body)
17
+
18
+ def items_comments_status(self, **body: Any) -> APIResponse[Any]:
19
+ return self._http.request("POST", "/pdd/items/comments/status", json=body)
20
+
21
+ def items_comments_fetch(self, **body: Any) -> APIResponse[Any]:
22
+ return self._http.request("POST", "/pdd/items/comments/fetch", json=body)