diff --git a/twikit/client.py b/twikit/client.py index 6f0a9950..a4a63b30 100644 --- a/twikit/client.py +++ b/twikit/client.py @@ -460,6 +460,8 @@ def search_tweet( ... ... + + >>> latest_tweets = tweets.next() # Retrieve latest tweets """ product = product.capitalize() @@ -481,11 +483,14 @@ def search_tweet( items = items[0]['content']['items'] next_cursor = None + previous_cursor = None results = [] for item in items: if item['entryId'].startswith('cursor-bottom'): next_cursor = item['content']['value'] + if item['entryId'].startswith('cursor-top'): + previous_cursor = item['content']['value'] if not item['entryId'].startswith(('tweet', 'search-grid')): continue tweet_info = find_dict(item, 'result')[0] @@ -496,16 +501,21 @@ def search_tweet( if next_cursor is None: if product == 'Media': - next_cursor = find_dict( + entries = find_dict( instructions, 'entries' - )[0][-1]['content']['value'] + )[0] + next_cursor = entries[-1]['content']['value'] + previous_cursor = entries[-2]['content']['value'] else: next_cursor = instructions[-1]['entry']['content']['value'] + previous_cursor = instructions[-2]['entry']['content']['value'] return Result( results, lambda:self.search_tweet(query, product, count, next_cursor), - next_cursor + next_cursor, + lambda:self.search_tweet(query, product, count, previous_cursor), + previous_cursor ) def search_user( @@ -1362,19 +1372,26 @@ def _get_tweet_engagements( return Result([]) items = items_[0] next_cursor = items[-1]['content']['value'] + previous_cursor = items[-2]['content']['value'] results = [] for item in items: if not item['entryId'].startswith('user'): continue - user_info = find_dict(item, 'result')[0] + user_info_ = find_dict(item, 'result') + if not user_info_: + continue + user_info = user_info_[0] results.append(User(self, user_info)) return Result( results, lambda:self._get_tweet_engagements( tweet_id, count, next_cursor, endpoint), - next_cursor + next_cursor, + lambda:self._get_tweet_engagements( + tweet_id, count, previous_cursor, endpoint), + previous_cursor ) def get_retweeters( @@ -1541,6 +1558,7 @@ def get_user_tweets( items = instructions[-1]['entries'] next_cursor = items[-1]['content']['value'] + previous_cursor = items[-2]['content']['value'] if tweet_type == 'Media': if cursor is None: @@ -1587,7 +1605,10 @@ def get_user_tweets( results, lambda:self.get_user_tweets( user_id, tweet_type, count, next_cursor), - next_cursor + next_cursor, + lambda:self.get_user_tweets( + user_id, tweet_type, count, previous_cursor), + previous_cursor ) def get_timeline( @@ -2017,6 +2038,7 @@ def get_bookmarks( return Result([]) items = items_[0] next_cursor = items[-1]['content']['value'] + previous_cursor = items[-2]['content']['value'] results = [] for item in items: @@ -2029,7 +2051,9 @@ def get_bookmarks( return Result( results, lambda:self.get_bookmarks(count, next_cursor), - next_cursor + next_cursor, + lambda:self.get_bookmarks(count, previous_cursor), + previous_cursor ) def delete_all_bookmarks(self) -> Response: @@ -2786,7 +2810,8 @@ def get_dm_history( )) return Result( messages, - lambda:self.get_dm_history(user_id, messages[-1].id) + lambda:self.get_dm_history(user_id, messages[-1].id), + messages[-1].id ) def send_dm_to_group( @@ -2901,7 +2926,8 @@ def get_group_dm_history( )) return Result( messages, - lambda:self.get_group_dm_history(group_id, messages[-1].id) + lambda:self.get_group_dm_history(group_id, messages[-1].id), + messages[-1].id ) def get_group(self, group_id: str) -> Group: diff --git a/twikit/twikit_async/client.py b/twikit/twikit_async/client.py index 106cea6a..2eb13dec 100644 --- a/twikit/twikit_async/client.py +++ b/twikit/twikit_async/client.py @@ -463,6 +463,8 @@ async def search_tweet( ... ... + + >>> latest_tweets = await tweets.next() # Retrieve latest tweets """ product = product.capitalize() @@ -484,11 +486,14 @@ async def search_tweet( items = items[0]['content']['items'] next_cursor = None + previous_cursor = None results = [] for item in items: if item['entryId'].startswith('cursor-bottom'): next_cursor = item['content']['value'] + if item['entryId'].startswith('cursor-top'): + previous_cursor = item['content']['value'] if not item['entryId'].startswith(('tweet', 'search-grid')): continue tweet_info = find_dict(item, 'result')[0] @@ -499,19 +504,29 @@ async def search_tweet( if next_cursor is None: if product == 'Media': - next_cursor = find_dict( + entries = find_dict( instructions, 'entries' - )[0][-1]['content']['value'] + )[0] + next_cursor = entries[-1]['content']['value'] + previous_cursor = entries[-2]['content']['value'] else: next_cursor = instructions[-1]['entry']['content']['value'] + previous_cursor = instructions[-2]['entry']['content']['value'] async def _fetch_next_result(): return await self.search_tweet(query, product, count, next_cursor) + async def _fetch_previous_result(): + return await self.search_tweet( + query, product, count, previous_cursor + ) + return Result( results, _fetch_next_result, - next_cursor + next_cursor, + _fetch_previous_result, + previous_cursor ) async def search_user( @@ -1381,12 +1396,16 @@ async def _get_tweet_engagements( return Result([]) items = items_[0] next_cursor = items[-1]['content']['value'] + previous_cursor = items[-2]['content']['value'] results = [] for item in items: if not item['entryId'].startswith('user'): continue - user_info = find_dict(item, 'result')[0] + user_info_ = find_dict(item, 'result') + if not user_info_: + continue + user_info = user_info_[0] results.append(User(self, user_info)) async def _fetch_next_result(): @@ -1394,10 +1413,17 @@ async def _fetch_next_result(): tweet_id, count, next_cursor, endpoint ) + async def _fetch_previous_result(): + return await self._get_tweet_engagements( + tweet_id, count, previous_cursor, endpoint + ) + return Result( results, _fetch_next_result, - next_cursor + next_cursor, + _fetch_previous_result, + previous_cursor ) async def get_retweeters( @@ -1566,6 +1592,7 @@ async def get_user_tweets( items = instructions[-1]['entries'] next_cursor = items[-1]['content']['value'] + previous_cursor = items[-2]['content']['value'] if tweet_type == 'Media': if cursor is None: @@ -1613,10 +1640,17 @@ async def _fetch_next_result(): user_id, tweet_type, count, next_cursor ) + async def _fetch_previous_result(): + return await self.get_user_tweets( + user_id, tweet_type, count, previous_cursor + ) + return Result( results, _fetch_next_result, - next_cursor + next_cursor, + _fetch_previous_result, + previous_cursor ) async def get_timeline( @@ -2054,6 +2088,7 @@ async def get_bookmarks( return Result([]) items = items_[0] next_cursor = items[-1]['content']['value'] + previous_cursor = items[-2]['content']['value'] results = [] for item in items: @@ -2066,10 +2101,15 @@ async def get_bookmarks( async def _fetch_next_result(): return await self.get_bookmarks(count, next_cursor) + async def _fetch_previous_result(): + return await self.get_bookmarks(count, previous_cursor) + return Result( results, _fetch_next_result, - next_cursor + next_cursor, + _fetch_previous_result, + previous_cursor ) async def delete_all_bookmarks(self) -> Response: @@ -2836,7 +2876,8 @@ async def _fetch_next_result(): return Result( messages, - _fetch_next_result + _fetch_next_result, + messages[-1].id ) async def send_dm_to_group( @@ -2955,7 +2996,8 @@ async def _fetch_next_result(): return Result( messages, - _fetch_next_result + _fetch_next_result, + messages[-1].id ) async def get_group(self, group_id: str) -> Group: diff --git a/twikit/twikit_async/user.py b/twikit/twikit_async/user.py index 45d094f4..b036f1df 100644 --- a/twikit/twikit_async/user.py +++ b/twikit/twikit_async/user.py @@ -278,7 +278,7 @@ async def unmute(self) -> Response: """ return await self._client.unmute_user(self.id) - async def get_followers(self, count: int = 20) -> list[User]: + async def get_followers(self, count: int = 20) -> Result[User]: """ Retrieves a list of followers for the user. @@ -289,7 +289,7 @@ async def get_followers(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the followers. See Also @@ -298,7 +298,7 @@ async def get_followers(self, count: int = 20) -> list[User]: """ return await self._client.get_user_followers(self.id, count) - async def get_verified_followers(self, count: int = 20) -> list[User]: + async def get_verified_followers(self, count: int = 20) -> Result[User]: """ Retrieves a list of verified followers for the user. @@ -309,7 +309,7 @@ async def get_verified_followers(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the verified followers. See Also @@ -318,7 +318,7 @@ async def get_verified_followers(self, count: int = 20) -> list[User]: """ return await self._client.get_user_verified_followers(self.id, count) - async def get_followers_you_know(self, count: int = 20) -> list[User]: + async def get_followers_you_know(self, count: int = 20) -> Result[User]: """ Retrieves a list of followers whom the user might know. @@ -329,7 +329,7 @@ async def get_followers_you_know(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the followers you might know. See Also @@ -338,7 +338,7 @@ async def get_followers_you_know(self, count: int = 20) -> list[User]: """ return await self._client.get_user_followers_you_know(self.id, count) - async def get_following(self, count: int = 20) -> list[User]: + async def get_following(self, count: int = 20) -> Result[User]: """ Retrieves a list of users whom the user is following. @@ -349,7 +349,7 @@ async def get_following(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the users being followed. See Also @@ -358,7 +358,7 @@ async def get_following(self, count: int = 20) -> list[User]: """ return await self._client.get_user_following(self.id, count) - async def get_subscriptions(self, count: int = 20) -> list[User]: + async def get_subscriptions(self, count: int = 20) -> Result[User]: """ Retrieves a list of users whom the user is subscribed to. @@ -369,7 +369,7 @@ async def get_subscriptions(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the subscribed users. See Also diff --git a/twikit/twikit_async/utils.py b/twikit/twikit_async/utils.py index e177e6e8..eba06270 100644 --- a/twikit/twikit_async/utils.py +++ b/twikit/twikit_async/utils.py @@ -7,28 +7,36 @@ class Result(Generic[T]): """ - This class is designed to store multiple results. + This class is for storing multiple results. The `next` method can be used to retrieve further results. As with a regular list, you can access elements by specifying indexes and iterate over elements using a for loop. Attributes ---------- + next_cursor : str + Cursor used to obtain the next result. + previous_cursor : str + Cursor used to obtain the previous result. token : str - Token used to obtain the next result. + Alias of `next_cursor`. cursor : str - Alias of `token`. + Alias of `next_cursor`. """ def __init__( self, results: list[T], fetch_next_result: Awaitable | None = None, - token: str | None = None + next_cursor: str | None = None, + fetch_previous_result: Awaitable | None = None, + previous_cursor: str | None = None ) -> None: self.__results = results - self.token = token + self.next_cursor = next_cursor self.__fetch_next_result = fetch_next_result + self.previous_cursor = previous_cursor + self.__fetch_previous_result = fetch_previous_result async def next(self) -> Result[T]: """ @@ -38,11 +46,25 @@ async def next(self) -> Result[T]: return Result([]) return await self.__fetch_next_result() + async def previous(self) -> Result[T]: + """ + The previous result. + """ + if self.__fetch_previous_result is None: + return Result([]) + return await self.__fetch_previous_result() + @property def cursor(self) -> str: - """Alias of `token` + """Alias of `next_token` + """ + return self.next_cursor + + @property + def token(self) -> str: + """Alias of `next_token` """ - return self.token + return self.next_cursor def __iter__(self) -> Iterator[T]: yield from self.__results diff --git a/twikit/user.py b/twikit/user.py index c88aa0fa..84aaaf45 100644 --- a/twikit/user.py +++ b/twikit/user.py @@ -278,7 +278,7 @@ def unmute(self) -> Response: """ return self._client.unmute_user(self.id) - def get_followers(self, count: int = 20) -> list[User]: + def get_followers(self, count: int = 20) -> Result[User]: """ Retrieves a list of followers for the user. @@ -289,7 +289,7 @@ def get_followers(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the followers. See Also @@ -298,7 +298,7 @@ def get_followers(self, count: int = 20) -> list[User]: """ return self._client.get_user_followers(self.id, count) - def get_verified_followers(self, count: int = 20) -> list[User]: + def get_verified_followers(self, count: int = 20) -> Result[User]: """ Retrieves a list of verified followers for the user. @@ -309,7 +309,7 @@ def get_verified_followers(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the verified followers. See Also @@ -318,7 +318,7 @@ def get_verified_followers(self, count: int = 20) -> list[User]: """ return self._client.get_user_verified_followers(self.id, count) - def get_followers_you_know(self, count: int = 20) -> list[User]: + def get_followers_you_know(self, count: int = 20) -> Result[User]: """ Retrieves a list of followers whom the user might know. @@ -329,7 +329,7 @@ def get_followers_you_know(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the followers you might know. See Also @@ -338,7 +338,7 @@ def get_followers_you_know(self, count: int = 20) -> list[User]: """ return self._client.get_user_followers_you_know(self.id, count) - def get_following(self, count: int = 20) -> list[User]: + def get_following(self, count: int = 20) -> Result[User]: """ Retrieves a list of users whom the user is following. @@ -349,7 +349,7 @@ def get_following(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the users being followed. See Also @@ -358,7 +358,7 @@ def get_following(self, count: int = 20) -> list[User]: """ return self._client.get_user_following(self.id, count) - def get_subscriptions(self, count: int = 20) -> list[User]: + def get_subscriptions(self, count: int = 20) -> Result[User]: """ Retrieves a list of users whom the user is subscribed to. @@ -369,7 +369,7 @@ def get_subscriptions(self, count: int = 20) -> list[User]: Returns ------- - list[User] + Result[User] A list of User objects representing the subscribed users. See Also diff --git a/twikit/utils.py b/twikit/utils.py index 8fac734d..082fdc19 100644 --- a/twikit/utils.py +++ b/twikit/utils.py @@ -139,28 +139,36 @@ class Endpoint: class Result(Generic[T]): """ - This class is designed to store multiple results. + This class is for storing multiple results. The `next` method can be used to retrieve further results. As with a regular list, you can access elements by specifying indexes and iterate over elements using a for loop. Attributes ---------- + next_cursor : str + Cursor used to obtain the next result. + previous_cursor : str + Cursor used to obtain the previous result. token : str - Token used to obtain the next result. + Alias of `next_cursor`. cursor : str - Alias of `token`. + Alias of `next_cursor`. """ def __init__( self, results: list[T], fetch_next_result: Callable | None = None, - token: str | None = None + next_cursor: str | None = None, + fetch_previous_result: Callable | None = None, + previous_cursor: str | None = None ) -> None: self.__results = results - self.token = token + self.next_cursor = next_cursor self.__fetch_next_result = fetch_next_result + self.previous_cursor = previous_cursor + self.__fetch_previous_result = fetch_previous_result def next(self) -> Result[T]: """ @@ -170,11 +178,25 @@ def next(self) -> Result[T]: return Result([]) return self.__fetch_next_result() + def previous(self) -> Result[T]: + """ + The previous result. + """ + if self.__fetch_previous_result is None: + return Result([]) + return self.__fetch_previous_result() + @property def cursor(self) -> str: - """Alias of `token` + """Alias of `next_token` + """ + return self.next_cursor + + @property + def token(self) -> str: + """Alias of `next_token` """ - return self.token + return self.next_cursor def __iter__(self) -> Iterator[T]: yield from self.__results