feat: add bookmark folders support (#30)
Add `twitter bookmarks folders` command to list bookmark folders and `twitter bookmarks folders <id>` to fetch tweets from a specific folder, with --since date filtering and pagination support. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,7 +47,7 @@ from .graphql import (
|
||||
_resolve_query_id,
|
||||
_update_features_from_html,
|
||||
)
|
||||
from .models import UserProfile
|
||||
from .models import BookmarkFolder, UserProfile
|
||||
from .parser import (
|
||||
_deep_get,
|
||||
_parse_int,
|
||||
@@ -183,6 +183,59 @@ class TwitterClient:
|
||||
|
||||
return self._fetch_timeline("Bookmarks", count, get_instructions)
|
||||
|
||||
def fetch_bookmark_folders(self):
|
||||
# type: () -> List[BookmarkFolder]
|
||||
"""Fetch all bookmark folders with pagination."""
|
||||
folders = [] # type: List[BookmarkFolder]
|
||||
cursor = None # type: Optional[str]
|
||||
max_pages = 10
|
||||
|
||||
for _ in range(max_pages):
|
||||
variables = {} # type: Dict[str, Any]
|
||||
if cursor:
|
||||
variables["cursor"] = cursor
|
||||
|
||||
data = self._graphql_get("BookmarkFoldersSlice", variables, FEATURES)
|
||||
slice_data = _deep_get(
|
||||
data, "data", "viewer", "user_results", "result",
|
||||
"bookmark_collections_slice",
|
||||
)
|
||||
if not isinstance(slice_data, dict):
|
||||
break
|
||||
|
||||
for item in slice_data.get("items", []):
|
||||
folder_id = item.get("id")
|
||||
folder_name = item.get("name", "")
|
||||
if folder_id:
|
||||
folders.append(BookmarkFolder(id=folder_id, name=folder_name))
|
||||
|
||||
next_cursor = _deep_get(slice_data, "slice_info", "next_cursor")
|
||||
if not next_cursor or next_cursor == cursor:
|
||||
break
|
||||
cursor = next_cursor
|
||||
|
||||
return folders
|
||||
|
||||
def fetch_bookmark_folder_timeline(self, folder_id, count=50):
|
||||
# type: (str, int) -> List[Tweet]
|
||||
"""Fetch tweets from a bookmark folder."""
|
||||
def get_instructions(data):
|
||||
# type: (Any) -> Any
|
||||
return _deep_get(
|
||||
data, "data", "bookmark_collection_timeline", "timeline", "instructions",
|
||||
)
|
||||
|
||||
return self._fetch_timeline(
|
||||
"BookmarkFolderTimeline",
|
||||
count,
|
||||
get_instructions,
|
||||
extra_variables={
|
||||
"bookmark_collection_id": folder_id,
|
||||
"includePromotedContent": False,
|
||||
},
|
||||
override_base_variables=True,
|
||||
)
|
||||
|
||||
def resolve_user_id(self, identifier):
|
||||
# type: (str) -> str
|
||||
"""Resolve a user identifier (screen_name or numeric user_id) to numeric user_id.
|
||||
|
||||
Reference in New Issue
Block a user