1111from fake_useragent import UserAgent
1212from httpx import Response
1313
14+ from .bookmark import BookmarkFolder
1415from .community import Community , CommunityMember
1516from .errors import (
1617 CouldNotTweet ,
3031from .tweet import CommunityNote , Poll , ScheduledTweet , Tweet
3132from .user import User
3233from .utils import (
34+ BOOKMARK_FOLDER_TIMELINE_FEATURES ,
3335 COMMUNITY_TWEETS_FEATURES ,
3436 COMMUNITY_NOTE_FEATURES ,
3537 JOIN_COMMUNITY_FEATURES ,
@@ -1432,6 +1434,8 @@ def get_tweet_by_id(
14321434 show_replies = None
14331435 # Reply to reply
14341436 for reply in entry ['content' ]['items' ][1 :]:
1437+ if 'tweetcomposer' in reply ['entryId' ]:
1438+ continue
14351439 if 'tweet' in find_dict (reply , 'result' ):
14361440 reply = reply ['tweet' ]
14371441 if 'tweet' in reply .get ('entryId' ):
@@ -2134,14 +2138,18 @@ def delete_retweet(self, tweet_id: str) -> Response:
21342138 )
21352139 return response
21362140
2137- def bookmark_tweet (self , tweet_id : str ) -> Response :
2141+ def bookmark_tweet (
2142+ self , tweet_id : str , folder_id : str | None = None
2143+ ) -> Response :
21382144 """
21392145 Adds the tweet to bookmarks.
21402146
21412147 Parameters
21422148 ----------
21432149 tweet_id : :class:`str`
21442150 The ID of the tweet to be bookmarked.
2151+ folder_id : :class:`str` | None, default=None
2152+ The ID of the folder to add the bookmark to.
21452153
21462154 Returns
21472155 -------
@@ -2152,18 +2160,20 @@ def bookmark_tweet(self, tweet_id: str) -> Response:
21522160 --------
21532161 >>> tweet_id = '...'
21542162 >>> client.bookmark_tweet(tweet_id)
2155-
2156- See Also
2157- --------
2158- .bookmark_tweet
21592163 """
2164+ variables = {'tweet_id' : tweet_id }
2165+ if folder_id is None :
2166+ endpoint = Endpoint .CREATE_BOOKMARK
2167+ else :
2168+ endpoint = Endpoint .BOOKMARK_TO_FOLDER
2169+ variables ['bookmark_collection_id' ] = folder_id
21602170
21612171 data = {
2162- 'variables' : { 'tweet_id' : tweet_id } ,
2172+ 'variables' : variables ,
21632173 'queryId' : get_query_id (Endpoint .CREATE_BOOKMARK )
21642174 }
21652175 response = self .http .post (
2166- Endpoint . CREATE_BOOKMARK ,
2176+ endpoint ,
21672177 json = data ,
21682178 headers = self ._base_headers
21692179 )
@@ -2204,17 +2214,18 @@ def delete_bookmark(self, tweet_id: str) -> Response:
22042214 return response
22052215
22062216 def get_bookmarks (
2207- self , count : int = 20 , cursor : str | None = None
2217+ self , count : int = 20 ,
2218+ cursor : str | None = None , folder_id : str | None = None
22082219 ) -> Result [Tweet ]:
22092220 """
22102221 Retrieves bookmarks from the authenticated user's Twitter account.
22112222
22122223 Parameters
22132224 ----------
22142225 count : :class:`int`, default=20
2215- The number of bookmarks to retrieve (default is 20) .
2216- cursor : :class:`str`, default=None
2217- A cursor to paginate through the bookmarks (default is None) .
2226+ The number of bookmarks to retrieve.
2227+ folder_id : :class:`str` | None , default=None
2228+ Folder to retrieve bookmarks.
22182229
22192230 Returns
22202231 -------
@@ -2240,17 +2251,24 @@ def get_bookmarks(
22402251 'count' : count ,
22412252 'includePromotedContent' : True
22422253 }
2254+ if folder_id is None :
2255+ endpoint = Endpoint .BOOKMARKS
2256+ features = FEATURES | {
2257+ 'graphql_timeline_v2_bookmark_timeline' : True
2258+ }
2259+ else :
2260+ endpoint = Endpoint .BOOKMARK_FOLDER_TIMELINE
2261+ variables ['bookmark_collection_id' ] = folder_id
2262+ features = BOOKMARK_FOLDER_TIMELINE_FEATURES
2263+
22432264 if cursor is not None :
22442265 variables ['cursor' ] = cursor
2245- features = FEATURES | {
2246- 'graphql_timeline_v2_bookmark_timeline' : True
2247- }
22482266 params = flatten_params ({
22492267 'variables' : variables ,
22502268 'features' : features
22512269 })
22522270 response = self .http .get (
2253- Endpoint . BOOKMARKS ,
2271+ endpoint ,
22542272 params = params ,
22552273 headers = self ._base_headers
22562274 ).json ()
@@ -2260,7 +2278,13 @@ def get_bookmarks(
22602278 return Result ([])
22612279 items = items_ [0 ]
22622280 next_cursor = items [- 1 ]['content' ]['value' ]
2263- previous_cursor = items [- 2 ]['content' ]['value' ]
2281+ if folder_id is None :
2282+ previous_cursor = items [- 2 ]['content' ]['value' ]
2283+ fetch_previous_result = partial (self .get_bookmarks , count ,
2284+ previous_cursor , folder_id )
2285+ else :
2286+ previous_cursor = None
2287+ fetch_previous_result = None
22642288
22652289 results = []
22662290 for item in items :
@@ -2272,9 +2296,9 @@ def get_bookmarks(
22722296
22732297 return Result (
22742298 results ,
2275- partial (self .get_bookmarks , count , next_cursor ),
2299+ partial (self .get_bookmarks , count , next_cursor , folder_id ),
22762300 next_cursor ,
2277- partial ( self . get_bookmarks , count , previous_cursor ) ,
2301+ fetch_previous_result ,
22782302 previous_cursor
22792303 )
22802304
@@ -2302,6 +2326,148 @@ def delete_all_bookmarks(self) -> Response:
23022326 )
23032327 return response
23042328
2329+ def get_bookmark_folders (
2330+ self , cursor : str | None = None
2331+ ) -> Result [BookmarkFolder ]:
2332+ """
2333+ Retrieves bookmark folders.
2334+
2335+ Returns
2336+ -------
2337+ Result[:class:`BookmarkFolder`]
2338+ Result object containing a list of bookmark folders.
2339+
2340+ Examples
2341+ --------
2342+ >>> folders = client.get_bookmark_folders()
2343+ >>> print(folders)
2344+ [<BookmarkFolder id="...">, ..., <BookmarkFolder id="...">]
2345+ >>> more_folders = folders.next() # Retrieve more folders
2346+ """
2347+ variables = {}
2348+ if cursor is not None :
2349+ variables ['cursor' ] = cursor
2350+ params = flatten_params ({'variables' : variables })
2351+ response = self .http .get (
2352+ Endpoint .BOOKMARK_FOLDERS ,
2353+ params = params ,
2354+ headers = self ._base_headers
2355+ ).json ()
2356+
2357+ slice = find_dict (response , 'bookmark_collections_slice' )[0 ]
2358+ results = []
2359+ for item in slice ['items' ]:
2360+ results .append (BookmarkFolder (self , item ))
2361+
2362+ if 'next_cursor' in slice ['slice_info' ]:
2363+ next_cursor = slice ['slice_info' ]['next_cursor' ]
2364+ fetch_next_result = partial (self .get_bookmark_folders , next_cursor )
2365+ else :
2366+ next_cursor = None
2367+ fetch_next_result = None
2368+
2369+ return Result (
2370+ results ,
2371+ fetch_next_result ,
2372+ next_cursor
2373+ )
2374+
2375+ def edit_bookmark_folder (
2376+ self , folder_id : str , name : str
2377+ ) -> BookmarkFolder :
2378+ """
2379+ Edits a bookmark folder.
2380+
2381+ Parameters
2382+ ----------
2383+ folder_id : :class:`str`
2384+ ID of the folder to edit.
2385+ name : :class:`str`
2386+ New name for the folder.
2387+
2388+ Returns
2389+ -------
2390+ :class:`BookmarkFolder`
2391+ Updated bookmark folder.
2392+
2393+ Examples
2394+ --------
2395+ >>> client.edit_bookmark_folder('123456789', 'MyFolder')
2396+ """
2397+ variables = {
2398+ 'bookmark_collection_id' : folder_id ,
2399+ 'name' : name
2400+ }
2401+ data = {
2402+ 'variables' : variables ,
2403+ 'queryId' : get_query_id (Endpoint .EDIT_BOOKMARK_FOLDER )
2404+ }
2405+ response = self .http .post (
2406+ Endpoint .EDIT_BOOKMARK_FOLDER ,
2407+ json = data ,
2408+ headers = self ._base_headers
2409+ ).json ()
2410+ return BookmarkFolder (
2411+ self , response ['data' ]['bookmark_collection_update' ]
2412+ )
2413+
2414+ def delete_bookmark_folder (self , folder_id : str ) -> Response :
2415+ """
2416+ Deletes a bookmark folder.
2417+
2418+ Parameters
2419+ ----------
2420+ folder_id : :class:`str`
2421+ ID of the folder to delete.
2422+
2423+ Returns
2424+ -------
2425+ :class:`httpx.Response`
2426+ Response returned from twitter api.
2427+ """
2428+ variables = {
2429+ 'bookmark_collection_id' : folder_id
2430+ }
2431+ data = {
2432+ 'variables' : variables ,
2433+ 'queryId' : get_query_id (Endpoint .DELETE_BOOKMARK_FOLDER )
2434+ }
2435+ response = self .http .post (
2436+ Endpoint .DELETE_BOOKMARK_FOLDER ,
2437+ json = data ,
2438+ headers = self ._base_headers
2439+ )
2440+ return response
2441+
2442+ def create_bookmark_folder (self , name : str ) -> BookmarkFolder :
2443+ """Creates a bookmark folder.
2444+
2445+ Parameters
2446+ ----------
2447+ name : :class:`str`
2448+ Name of the folder.
2449+
2450+ Returns
2451+ -------
2452+ :class:`BookmarkFolder`
2453+ Newly created bookmark folder.
2454+ """
2455+ variables = {
2456+ 'name' : name
2457+ }
2458+ data = {
2459+ 'variables' : variables ,
2460+ 'queryId' : get_query_id (Endpoint .CREATE_BOOKMARK_FOLDER )
2461+ }
2462+ response = self .http .post (
2463+ Endpoint .CREATE_BOOKMARK_FOLDER ,
2464+ json = data ,
2465+ headers = self ._base_headers
2466+ ).json ()
2467+ return BookmarkFolder (
2468+ self , response ['data' ]['bookmark_collection_create' ]
2469+ )
2470+
23052471 def follow_user (self , user_id : str ) -> Response :
23062472 """
23072473 Follows a user.
0 commit comments