tweetapi 1.0.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.
tweetapi/__init__.py ADDED
@@ -0,0 +1,37 @@
1
+ """TweetAPI Python SDK — Official client for the Twitter/X Data API.
2
+
3
+ Usage::
4
+
5
+ from tweetapi import TweetAPI
6
+
7
+ client = TweetAPI(api_key="YOUR_API_KEY")
8
+
9
+ user = client.user.get_by_username(username="elonmusk")
10
+ print(user["data"]["followerCount"])
11
+ """
12
+
13
+ from .client import TweetAPI
14
+ from .errors import (
15
+ TweetAPIError,
16
+ AuthenticationError,
17
+ ForbiddenError,
18
+ NotFoundError,
19
+ ValidationError,
20
+ RateLimitError,
21
+ ServerError,
22
+ ConnectionError_ as NetworkError,
23
+ )
24
+
25
+ __all__ = [
26
+ "TweetAPI",
27
+ "TweetAPIError",
28
+ "AuthenticationError",
29
+ "ForbiddenError",
30
+ "NotFoundError",
31
+ "ValidationError",
32
+ "RateLimitError",
33
+ "ServerError",
34
+ "NetworkError",
35
+ ]
36
+
37
+ __version__ = "1.0.0"
tweetapi/client.py ADDED
@@ -0,0 +1,146 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional
4
+
5
+ import requests
6
+
7
+ from .errors import (
8
+ TweetAPIError,
9
+ AuthenticationError,
10
+ ForbiddenError,
11
+ NotFoundError,
12
+ ValidationError,
13
+ RateLimitError,
14
+ ServerError,
15
+ ConnectionError_,
16
+ )
17
+
18
+
19
+ class TweetAPI:
20
+ """TweetAPI Python SDK client.
21
+
22
+ Args:
23
+ api_key: Your TweetAPI API key.
24
+ base_url: Base URL for the API (default: https://api.tweetapi.com).
25
+ timeout: Request timeout in seconds (default: 30).
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ api_key: str,
31
+ base_url: str = "https://api.tweetapi.com",
32
+ timeout: int = 30,
33
+ ) -> None:
34
+ if not api_key:
35
+ raise ValueError("TweetAPI: api_key is required")
36
+
37
+ self._base_url = base_url.rstrip("/")
38
+ self._timeout = timeout
39
+ self._session = requests.Session()
40
+ self._session.headers.update({"X-API-Key": api_key})
41
+
42
+ # Lazy imports to avoid circular dependencies
43
+ from .resources.user import UserResource
44
+ from .resources.tweet import TweetResource
45
+ from .resources.post import PostResource
46
+ from .resources.interaction import InteractionResource
47
+ from .resources.list_ import ListResource
48
+ from .resources.community import CommunityResource
49
+ from .resources.space import SpaceResource
50
+ from .resources.explore import ExploreResource
51
+ from .resources.auth import AuthResource
52
+ from .resources.xchat import XChatResource
53
+ from .resources.unencrypted_dm import UnencryptedDMResource
54
+
55
+ self.user = UserResource(self)
56
+ self.tweet = TweetResource(self)
57
+ self.post = PostResource(self)
58
+ self.interaction = InteractionResource(self)
59
+ self.list = ListResource(self)
60
+ self.community = CommunityResource(self)
61
+ self.space = SpaceResource(self)
62
+ self.explore = ExploreResource(self)
63
+ self.auth = AuthResource(self)
64
+ self.xchat = XChatResource(self)
65
+ self.dm = UnencryptedDMResource(self)
66
+
67
+ def _get(self, path: str, params: Optional[dict[str, Any]] = None) -> Any:
68
+ """Send a GET request to the API."""
69
+ clean_params = None
70
+ if params:
71
+ clean_params = {
72
+ k: v for k, v in params.items() if v is not None
73
+ }
74
+ return self._request("GET", path, params=clean_params)
75
+
76
+ def _post(self, path: str, body: Optional[dict[str, Any]] = None) -> Any:
77
+ """Send a POST request to the API."""
78
+ clean_body = None
79
+ if body:
80
+ clean_body = {k: v for k, v in body.items() if v is not None}
81
+ return self._request("POST", path, json=clean_body)
82
+
83
+ def _request(
84
+ self,
85
+ method: str,
86
+ path: str,
87
+ params: Optional[dict[str, Any]] = None,
88
+ json: Optional[dict[str, Any]] = None,
89
+ ) -> Any:
90
+ url = f"{self._base_url}{path}"
91
+
92
+ try:
93
+ response = self._session.request(
94
+ method,
95
+ url,
96
+ params=params,
97
+ json=json,
98
+ timeout=self._timeout,
99
+ )
100
+ except requests.exceptions.Timeout as e:
101
+ raise ConnectionError_(
102
+ f"Request timed out after {self._timeout}s", e
103
+ ) from e
104
+ except requests.exceptions.ConnectionError as e:
105
+ raise ConnectionError_(f"Network error: {e}", e) from e
106
+ except requests.exceptions.RequestException as e:
107
+ raise ConnectionError_(f"Request failed: {e}", e) from e
108
+
109
+ if not response.ok:
110
+ self._handle_error_response(response)
111
+
112
+ return response.json()
113
+
114
+ def _handle_error_response(self, response: requests.Response) -> None:
115
+ """Parse error response and raise the appropriate exception."""
116
+ try:
117
+ body = response.json()
118
+ except (ValueError, requests.exceptions.JSONDecodeError):
119
+ body = None
120
+
121
+ code = "UNKNOWN_ERROR"
122
+ message = f"HTTP {response.status_code}"
123
+ details = None
124
+
125
+ if body and isinstance(body.get("error"), dict):
126
+ error = body["error"]
127
+ code = error.get("code", code)
128
+ message = error.get("message", message)
129
+ details = error.get("details")
130
+
131
+ status = response.status_code
132
+
133
+ if status == 400:
134
+ raise ValidationError(message, code, details)
135
+ elif status == 401:
136
+ raise AuthenticationError(message, code, details)
137
+ elif status == 403:
138
+ raise ForbiddenError(message, code, details)
139
+ elif status == 404:
140
+ raise NotFoundError(message, code, details)
141
+ elif status == 429:
142
+ raise RateLimitError(message, code, details)
143
+ elif status >= 500:
144
+ raise ServerError(message, code, status, details)
145
+ else:
146
+ raise TweetAPIError(message, code, status, details)
tweetapi/errors.py ADDED
@@ -0,0 +1,122 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional
4
+
5
+
6
+ class TweetAPIError(Exception):
7
+ """Base error for all TweetAPI errors.
8
+
9
+ Contains the API error code, HTTP status, and optional details.
10
+ """
11
+
12
+ def __init__(
13
+ self,
14
+ message: str,
15
+ code: str,
16
+ status_code: int,
17
+ details: Optional[dict[str, Any]] = None,
18
+ ) -> None:
19
+ super().__init__(message)
20
+ self.message = message
21
+ self.code = code
22
+ self.status_code = status_code
23
+ self.details = details
24
+
25
+ def __str__(self) -> str:
26
+ return f"[{self.code}] {self.message}"
27
+
28
+ def __repr__(self) -> str:
29
+ return f"{self.__class__.__name__}(code={self.code!r}, status_code={self.status_code}, message={self.message!r})"
30
+
31
+
32
+ class AuthenticationError(TweetAPIError):
33
+ """Thrown when the API key or auth token is invalid or missing (HTTP 401)."""
34
+
35
+ def __init__(
36
+ self,
37
+ message: str,
38
+ code: str,
39
+ details: Optional[dict[str, Any]] = None,
40
+ ) -> None:
41
+ super().__init__(message, code, 401, details)
42
+
43
+
44
+ class ForbiddenError(TweetAPIError):
45
+ """Thrown when the request is forbidden (HTTP 403)."""
46
+
47
+ def __init__(
48
+ self,
49
+ message: str,
50
+ code: str,
51
+ details: Optional[dict[str, Any]] = None,
52
+ ) -> None:
53
+ super().__init__(message, code, 403, details)
54
+
55
+
56
+ class NotFoundError(TweetAPIError):
57
+ """Thrown when the requested resource is not found (HTTP 404)."""
58
+
59
+ def __init__(
60
+ self,
61
+ message: str,
62
+ code: str,
63
+ details: Optional[dict[str, Any]] = None,
64
+ ) -> None:
65
+ super().__init__(message, code, 404, details)
66
+
67
+
68
+ class ValidationError(TweetAPIError):
69
+ """Thrown when the request parameters are invalid (HTTP 400)."""
70
+
71
+ def __init__(
72
+ self,
73
+ message: str,
74
+ code: str,
75
+ details: Optional[dict[str, Any]] = None,
76
+ ) -> None:
77
+ super().__init__(message, code, 400, details)
78
+
79
+
80
+ class RateLimitError(TweetAPIError):
81
+ """Thrown when you've exceeded rate limits (HTTP 429).
82
+
83
+ Check ``retry_after`` for seconds until you can retry.
84
+ """
85
+
86
+ def __init__(
87
+ self,
88
+ message: str,
89
+ code: str,
90
+ details: Optional[dict[str, Any]] = None,
91
+ ) -> None:
92
+ super().__init__(message, code, 429, details)
93
+ self.retry_after: int = (details or {}).get("retryAfter", 60)
94
+
95
+
96
+ class ServerError(TweetAPIError):
97
+ """Thrown when the API encounters a server error (HTTP 5xx)."""
98
+
99
+ def __init__(
100
+ self,
101
+ message: str,
102
+ code: str,
103
+ status_code: int,
104
+ details: Optional[dict[str, Any]] = None,
105
+ ) -> None:
106
+ super().__init__(message, code, status_code, details)
107
+
108
+
109
+ class ConnectionError_(TweetAPIError):
110
+ """Thrown when a network error occurs (DNS failure, timeout, connection refused).
111
+
112
+ Named ``ConnectionError_`` to avoid shadowing the built-in ``ConnectionError``.
113
+ Also importable as ``NetworkError``.
114
+ """
115
+
116
+ def __init__(self, message: str, cause: Optional[Exception] = None) -> None:
117
+ super().__init__(message, "CONNECTION_ERROR", 0, None)
118
+ self.__cause__ = cause
119
+
120
+
121
+ # Alias for cleaner imports
122
+ NetworkError = ConnectionError_
tweetapi/py.typed ADDED
File without changes
File without changes
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..client import TweetAPI
7
+
8
+
9
+ class AuthResource:
10
+ def __init__(self, client: TweetAPI) -> None:
11
+ self._client = client
12
+
13
+ def login(self, *, username: str, password: str, proxy: str, two_factor_secret: Optional[str] = None) -> dict[str, Any]:
14
+ """Log in to a Twitter account and get auth tokens."""
15
+ return self._client._post("/tw-v2/auth/login", {
16
+ "username": username, "password": password,
17
+ "proxy": proxy, "twoFactorSecret": two_factor_secret,
18
+ })
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..client import TweetAPI
7
+
8
+
9
+ class CommunityResource:
10
+ def __init__(self, client: TweetAPI) -> None:
11
+ self._client = client
12
+
13
+ def get_details(self, *, community_id: str) -> dict[str, Any]:
14
+ """Get community details."""
15
+ return self._client._get("/tw-v2/community/details", {"communityId": community_id})
16
+
17
+ def get_tweets(self, *, community_id: str, sort_by: str, cursor: Optional[str] = None) -> dict[str, Any]:
18
+ """Get tweets in a community."""
19
+ return self._client._get("/tw-v2/community/tweets", {"communityId": community_id, "sortBy": sort_by, "cursor": cursor})
20
+
21
+ def get_members(self, *, community_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
22
+ """Get community members."""
23
+ return self._client._get("/tw-v2/community/members", {"communityId": community_id, "cursor": cursor})
24
+
25
+ def search(self, *, query: str, cursor: Optional[str] = None) -> dict[str, Any]:
26
+ """Search communities."""
27
+ return self._client._get("/tw-v2/community/search", {"query": query, "cursor": cursor})
28
+
29
+ def create_post(self, *, auth_token: str, text: str, community_id: str, proxy: str, disable_link_preview: Optional[bool] = None) -> dict[str, Any]:
30
+ """Create a post in a community."""
31
+ return self._client._post("/tw-v2/interaction/create-community-post", {
32
+ "authToken": auth_token, "text": text, "communityId": community_id,
33
+ "proxy": proxy, "disableLinkPreview": disable_link_preview,
34
+ })
35
+
36
+ def create_post_with_media(self, *, auth_token: str, text: str, community_id: str, media: list[Any], proxy: str, disable_link_preview: Optional[bool] = None) -> dict[str, Any]:
37
+ """Create a post with media in a community."""
38
+ return self._client._post("/tw-v2/interaction/create-community-post-with-media", {
39
+ "authToken": auth_token, "text": text, "communityId": community_id,
40
+ "media": media, "proxy": proxy, "disableLinkPreview": disable_link_preview,
41
+ })
42
+
43
+ def reply_post(self, *, auth_token: str, text: str, tweet_id: str, community_id: str, proxy: str, disable_link_preview: Optional[bool] = None) -> dict[str, Any]:
44
+ """Reply to a community post."""
45
+ return self._client._post("/tw-v2/interaction/reply-community-post", {
46
+ "authToken": auth_token, "text": text, "tweetId": tweet_id,
47
+ "communityId": community_id, "proxy": proxy, "disableLinkPreview": disable_link_preview,
48
+ })
49
+
50
+ def reply_post_with_media(self, *, auth_token: str, text: str, tweet_id: str, community_id: str, media: list[Any], proxy: str, disable_link_preview: Optional[bool] = None) -> dict[str, Any]:
51
+ """Reply to a community post with media."""
52
+ return self._client._post("/tw-v2/interaction/reply-community-post-with-media", {
53
+ "authToken": auth_token, "text": text, "tweetId": tweet_id,
54
+ "communityId": community_id, "media": media, "proxy": proxy,
55
+ "disableLinkPreview": disable_link_preview,
56
+ })
57
+
58
+ def join(self, *, auth_token: str, community_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
59
+ """Join a community."""
60
+ return self._client._post("/tw-v2/interaction/join-community", {
61
+ "authToken": auth_token, "communityId": community_id, "proxy": proxy,
62
+ })
63
+
64
+ def leave(self, *, auth_token: str, community_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
65
+ """Leave a community."""
66
+ return self._client._post("/tw-v2/interaction/leave-community", {
67
+ "authToken": auth_token, "communityId": community_id, "proxy": proxy,
68
+ })
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..client import TweetAPI
7
+
8
+
9
+ class ExploreResource:
10
+ def __init__(self, client: TweetAPI) -> None:
11
+ self._client = client
12
+
13
+ def search(self, *, query: str, type: str, cursor: Optional[str] = None) -> dict[str, Any]:
14
+ """Search for tweets, users, photos, or videos.
15
+
16
+ Args:
17
+ query: Search query string. Supports Twitter search operators.
18
+ type: Type of search results — "Latest", "Top", "People", "Photos", or "Videos".
19
+ cursor: Pagination cursor for fetching next page.
20
+ """
21
+ return self._client._get("/tw-v2/search", {"query": query, "type": type, "cursor": cursor})
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..client import TweetAPI
7
+
8
+
9
+ class InteractionResource:
10
+ def __init__(self, client: TweetAPI) -> None:
11
+ self._client = client
12
+
13
+ def favorite_post(self, *, auth_token: str, tweet_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
14
+ """Like a tweet."""
15
+ return self._client._post("/tw-v2/interaction/like-post", {
16
+ "authToken": auth_token, "tweetId": tweet_id, "proxy": proxy,
17
+ })
18
+
19
+ def unfavorite_post(self, *, auth_token: str, tweet_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
20
+ """Unlike a tweet."""
21
+ return self._client._post("/tw-v2/interaction/unlike-post", {
22
+ "authToken": auth_token, "tweetId": tweet_id, "proxy": proxy,
23
+ })
24
+
25
+ def retweet(self, *, auth_token: str, tweet_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
26
+ """Retweet a tweet."""
27
+ return self._client._post("/tw-v2/interaction/retweet", {
28
+ "authToken": auth_token, "tweetId": tweet_id, "proxy": proxy,
29
+ })
30
+
31
+ def delete_retweet(self, *, auth_token: str, tweet_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
32
+ """Remove a retweet."""
33
+ return self._client._post("/tw-v2/interaction/delete-retweet", {
34
+ "authToken": auth_token, "tweetId": tweet_id, "proxy": proxy,
35
+ })
36
+
37
+ def bookmark(self, *, auth_token: str, tweet_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
38
+ """Bookmark a tweet."""
39
+ return self._client._post("/tw-v2/interaction/bookmark", {
40
+ "authToken": auth_token, "tweetId": tweet_id, "proxy": proxy,
41
+ })
42
+
43
+ def delete_bookmark(self, *, auth_token: str, tweet_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
44
+ """Remove a bookmark."""
45
+ return self._client._post("/tw-v2/interaction/delete-bookmark", {
46
+ "authToken": auth_token, "tweetId": tweet_id, "proxy": proxy,
47
+ })
48
+
49
+ def follow(self, *, auth_token: str, user_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
50
+ """Follow a user."""
51
+ return self._client._post("/tw-v2/interaction/follow", {
52
+ "authToken": auth_token, "userId": user_id, "proxy": proxy,
53
+ })
54
+
55
+ def unfollow(self, *, auth_token: str, user_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
56
+ """Unfollow a user."""
57
+ return self._client._post("/tw-v2/interaction/unfollow", {
58
+ "authToken": auth_token, "userId": user_id, "proxy": proxy,
59
+ })
60
+
61
+ def add_member_to_list(self, *, auth_token: str, list_id: str, user_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
62
+ """Add a user to a list."""
63
+ return self._client._post("/tw-v2/interaction/add-to-list", {
64
+ "authToken": auth_token, "listId": list_id, "userId": user_id, "proxy": proxy,
65
+ })
66
+
67
+ def remove_member_from_list(self, *, auth_token: str, list_id: str, user_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
68
+ """Remove a user from a list."""
69
+ return self._client._post("/tw-v2/interaction/remove-from-list", {
70
+ "authToken": auth_token, "listId": list_id, "userId": user_id, "proxy": proxy,
71
+ })
72
+
73
+ def get_notifications(self, *, auth_token: str, timeline_type: Optional[str] = None, count: Optional[int] = None, cursor: Optional[str] = None, proxy: Optional[str] = None) -> dict[str, Any]:
74
+ """Get your notifications."""
75
+ return self._client._get("/tw-v2/interaction/notifications", {
76
+ "authToken": auth_token, "timelineType": timeline_type,
77
+ "count": count, "cursor": cursor, "proxy": proxy,
78
+ })
79
+
80
+ def get_user_analytics(self, *, auth_token: str, to_time: Optional[str] = None, from_time: Optional[str] = None, granularity: Optional[str] = None, show_verified_followers: Optional[bool] = None, proxy: Optional[str] = None) -> dict[str, Any]:
81
+ """Get your account analytics."""
82
+ return self._client._get("/tw-v2/interaction/user-analytics", {
83
+ "authToken": auth_token, "toTime": to_time, "fromTime": from_time,
84
+ "granularity": granularity, "showVerifiedFollowers": show_verified_followers,
85
+ "proxy": proxy,
86
+ })
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..client import TweetAPI
7
+
8
+
9
+ class ListResource:
10
+ def __init__(self, client: TweetAPI) -> None:
11
+ self._client = client
12
+
13
+ def get_details(self, *, list_id: str) -> dict[str, Any]:
14
+ """Get list details."""
15
+ return self._client._get("/tw-v2/list/details", {"listId": list_id})
16
+
17
+ def get_tweets(self, *, list_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
18
+ """Get tweets in a list."""
19
+ return self._client._get("/tw-v2/list/tweets", {"listId": list_id, "cursor": cursor})
20
+
21
+ def get_members(self, *, list_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
22
+ """Get members of a list."""
23
+ return self._client._get("/tw-v2/list/members", {"listId": list_id, "cursor": cursor})
24
+
25
+ def get_followers(self, *, list_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
26
+ """Get followers of a list."""
27
+ return self._client._get("/tw-v2/list/followers", {"listId": list_id, "cursor": cursor})
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..client import TweetAPI
7
+
8
+
9
+ class PostResource:
10
+ def __init__(self, client: TweetAPI) -> None:
11
+ self._client = client
12
+
13
+ def create_post(self, *, auth_token: str, text: str, proxy: str, disable_link_preview: Optional[bool] = None) -> dict[str, Any]:
14
+ """Create a new tweet."""
15
+ return self._client._post("/tw-v2/interaction/create-post", {
16
+ "authToken": auth_token, "text": text, "proxy": proxy,
17
+ "disableLinkPreview": disable_link_preview,
18
+ })
19
+
20
+ def create_post_quote(self, *, auth_token: str, text: str, attachment_url: str, proxy: str, disable_link_preview: Optional[bool] = None) -> dict[str, Any]:
21
+ """Create a quote tweet."""
22
+ return self._client._post("/tw-v2/interaction/create-post-quote", {
23
+ "authToken": auth_token, "text": text, "attachmentUrl": attachment_url,
24
+ "proxy": proxy, "disableLinkPreview": disable_link_preview,
25
+ })
26
+
27
+ def create_post_with_media(self, *, auth_token: str, text: str, media: list[Any], proxy: str, disable_link_preview: Optional[bool] = None) -> dict[str, Any]:
28
+ """Create a tweet with media attachments."""
29
+ return self._client._post("/tw-v2/interaction/create-post-with-media", {
30
+ "authToken": auth_token, "text": text, "media": media,
31
+ "proxy": proxy, "disableLinkPreview": disable_link_preview,
32
+ })
33
+
34
+ def reply_post(self, *, auth_token: str, text: str, tweet_id: str, proxy: str, disable_link_preview: Optional[bool] = None) -> dict[str, Any]:
35
+ """Reply to a tweet."""
36
+ return self._client._post("/tw-v2/interaction/reply-post", {
37
+ "authToken": auth_token, "text": text, "tweetId": tweet_id,
38
+ "proxy": proxy, "disableLinkPreview": disable_link_preview,
39
+ })
40
+
41
+ def reply_post_with_media(self, *, auth_token: str, text: str, tweet_id: str, media: list[Any], proxy: str, disable_link_preview: Optional[bool] = None) -> dict[str, Any]:
42
+ """Reply to a tweet with media attachments."""
43
+ return self._client._post("/tw-v2/interaction/reply-post-with-media", {
44
+ "authToken": auth_token, "text": text, "tweetId": tweet_id,
45
+ "media": media, "proxy": proxy, "disableLinkPreview": disable_link_preview,
46
+ })
47
+
48
+ def delete_post(self, *, auth_token: str, tweet_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
49
+ """Delete a tweet."""
50
+ return self._client._post("/tw-v2/interaction/delete-post", {
51
+ "authToken": auth_token, "tweetId": tweet_id, "proxy": proxy,
52
+ })
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..client import TweetAPI
7
+
8
+
9
+ class SpaceResource:
10
+ def __init__(self, client: TweetAPI) -> None:
11
+ self._client = client
12
+
13
+ def get_by_id(self, *, space_id: str) -> dict[str, Any]:
14
+ """Get Space details by ID."""
15
+ return self._client._get("/tw-v2/space/by-id", {"spaceId": space_id})
16
+
17
+ def get_stream_url(self, *, media_key: str) -> dict[str, Any]:
18
+ """Get the HLS stream URL for a Space."""
19
+ return self._client._get("/tw-v2/space/stream-url", {"mediaKey": media_key})
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..client import TweetAPI
7
+
8
+
9
+ class TweetResource:
10
+ def __init__(self, client: TweetAPI) -> None:
11
+ self._client = client
12
+
13
+ def get_details_and_conversation(self, *, tweet_id: str, cursor: Optional[str] = None, sort_by: Optional[str] = None) -> dict[str, Any]:
14
+ """Get tweet details and conversation thread."""
15
+ return self._client._get("/tw-v2/tweet/details", {"tweetId": tweet_id, "cursor": cursor, "sortBy": sort_by})
16
+
17
+ def get_details_by_ids(self, *, ids: str) -> dict[str, Any]:
18
+ """Get details for multiple tweets by IDs (comma-separated, max 200)."""
19
+ return self._client._get("/tw-v2/tweet/details-by-ids", {"ids": ids})
20
+
21
+ def get_retweets(self, *, tweet_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
22
+ """Get users who retweeted a tweet."""
23
+ return self._client._get("/tw-v2/tweet/retweets", {"tweetId": tweet_id, "cursor": cursor})
24
+
25
+ def get_quotes(self, *, tweet_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
26
+ """Get quote tweets for a tweet."""
27
+ return self._client._get("/tw-v2/tweet/quotes", {"tweetId": tweet_id, "cursor": cursor})
28
+
29
+ def translate(self, *, tweet_id: str, dst_lang: str) -> dict[str, Any]:
30
+ """Translate a tweet to a different language."""
31
+ return self._client._post("/tw-v2/tweet/translate", {"tweetId": tweet_id, "dstLang": dst_lang})