From 401938490520180d4427af5fb0091666583fbe7d Mon Sep 17 00:00:00 2001 From: d60 Date: Fri, 17 May 2024 23:48:48 +0900 Subject: [PATCH] Added get_latest_followers, get_latest_friends, get_followers_ids, get_friends_ids --- twikit/__init__.py | 2 +- twikit/client.py | 159 ++++++++++++++++++++++++++++++++++ twikit/twikit_async/client.py | 159 ++++++++++++++++++++++++++++++++++ twikit/utils.py | 5 ++ 4 files changed, 324 insertions(+), 1 deletion(-) diff --git a/twikit/__init__.py b/twikit/__init__.py index eb0c0955..29f11ec9 100644 --- a/twikit/__init__.py +++ b/twikit/__init__.py @@ -7,7 +7,7 @@ A Python library for interacting with the Twitter API. """ -__version__ = '1.6.2' +__version__ = '1.6.3' from .bookmark import BookmarkFolder from .client import Client diff --git a/twikit/client.py b/twikit/client.py index 8edd9388..25179af3 100644 --- a/twikit/client.py +++ b/twikit/client.py @@ -2822,6 +2822,38 @@ def _get_user_friendship( next_cursor ) + def _get_user_friendship_2( + self, user_id: str, count: int, endpoint: str, cursor: str + ) -> Result[User]: + params = {'count': count} + params['user_id'] = user_id + if cursor is not None: + params['cursor'] = cursor + + response = self.http.get( + endpoint, + params=params, + headers=self._base_headers + ).json() + + users = response['users'] + results = [] + for user in users: + results.append(User(self, build_user_data(user))) + + previous_cursor = response['previous_cursor'] + next_cursor = response['next_cursor'] + + return Result( + results, + partial(self._get_user_friendship_2, user_id, + count, endpoint, next_cursor), + next_cursor, + partial(self._get_user_friendship_2, user_id, + count, endpoint, previous_cursor), + previous_cursor + ) + def get_user_followers( self, user_id: str, count: int = 20, cursor: str | None = None ) -> Result[User]: @@ -2847,6 +2879,34 @@ def get_user_followers( cursor ) + def get_latest_followers( + self, user_id: str, count: int = 200, cursor: str | None = None + ) -> Result[User]: + """ + Retrieves the latest followers. + Max count : 200 + """ + return self._get_user_friendship_2( + user_id, + count, + Endpoint.FOLLOWERS2, + cursor + ) + + def get_latest_friends( + self, user_id: str, count: int = 200, cursor: str | None = None + ) -> Result[User]: + """ + Retrieves the latest friends (following users). + Max count : 200 + """ + return self._get_user_friendship_2( + user_id, + count, + Endpoint.FOLLOWING2, + cursor + ) + def get_user_verified_followers( self, user_id: str, count: int = 20, cursor: str | None = None ) -> Result[User]: @@ -2947,6 +3007,105 @@ def get_user_subscriptions( cursor ) + def _get_friendship_ids( + self, + user_id: str | None, + screen_name: str | None, + count: int, + endpoint: str, + cursor: str | None + ) -> Result[int]: + params = {'count': count} + if user_id is not None: + params['user_id'] = user_id + elif user_id is not None: + params['screen_name'] = screen_name + + if cursor is not None: + params['cursor'] = cursor + + response = self.http.get( + endpoint, + params=params, + headers=self._base_headers + ).json() + previous_cursor = response['previous_cursor'] + next_cursor = response['next_cursor'] + + return Result( + response['ids'], + partial(self._get_friendship_ids, user_id, + screen_name, count, endpoint, next_cursor), + next_cursor, + partial(self._get_friendship_ids, user_id, + screen_name, count, endpoint, previous_cursor), + previous_cursor + ) + + def get_followers_ids( + self, + user_id: str | None = None, + screen_name: str | None = None, + count: int = 5000, + cursor: str | None = None + ) -> Result[int]: + """ + Fetches the IDs of the followers of a specified user. + + Parameters + ---------- + user_id : :class:`str` | None, default=None + The ID of the user for whom to return results. + screen_name : :class:`str` | None, default=None + The screen name of the user for whom to return results. + count : :class:`int`, default=5000 + The maximum number of IDs to retrieve. + + Returns + ------- + :class:`Result`[:class:`int`] + A Result object containing the IDs of the followers. + """ + return self._get_friendship_ids( + user_id, + screen_name, + count, + Endpoint.FOLLOWERS_IDS, + cursor + ) + + def get_friends_ids( + self, + user_id: str | None = None, + screen_name: str | None = None, + count: int = 5000, + cursor: str | None = None + ) -> Result[int]: + """ + Fetches the IDs of the friends (following users) of a specified user. + + Parameters + ---------- + user_id : :class:`str` | None, default=None + The ID of the user for whom to return results. + screen_name : :class:`str` | None, default=None + The screen name of the user for whom to return results. + count : :class:`int`, default=5000 + The maximum number of IDs to retrieve. + + Returns + ------- + :class:`Result`[:class:`int`] + A Result object containing the IDs of the friends. + """ + return self._get_friendship_ids( + user_id, + screen_name, + count, + Endpoint.FRIENDS_IDS, + cursor + ) + def _send_dm( self, conversation_id: str, diff --git a/twikit/twikit_async/client.py b/twikit/twikit_async/client.py index 5f5fdd40..82eceadf 100644 --- a/twikit/twikit_async/client.py +++ b/twikit/twikit_async/client.py @@ -2842,6 +2842,38 @@ async def _get_user_friendship( next_cursor ) + async def _get_user_friendship_2( + self, user_id: str, count: int, endpoint: str, cursor: str + ) -> Result[User]: + params = {'count': count} + params['user_id'] = user_id + if cursor is not None: + params['cursor'] = cursor + + response = (await self.http.get( + endpoint, + params=params, + headers=self._base_headers + )).json() + + users = response['users'] + results = [] + for user in users: + results.append(User(self, build_user_data(user))) + + previous_cursor = response['previous_cursor'] + next_cursor = response['next_cursor'] + + return Result( + results, + partial(self._get_user_friendship_2, user_id, + count, endpoint, next_cursor), + next_cursor, + partial(self._get_user_friendship_2, user_id, + count, endpoint, previous_cursor), + previous_cursor + ) + async def get_user_followers( self, user_id: str, count: int = 20, cursor: str | None = None ) -> Result[User]: @@ -2867,6 +2899,34 @@ async def get_user_followers( cursor ) + async def get_latest_followers( + self, user_id: str, count: int = 200, cursor: str | None = None + ) -> Result[User]: + """ + Retrieves the latest followers. + Max count : 200 + """ + return await self._get_user_friendship_2( + user_id, + count, + Endpoint.FOLLOWERS2, + cursor + ) + + async def get_latest_friends( + self, user_id: str, count: int = 200, cursor: str | None = None + ) -> Result[User]: + """ + Retrieves the latest friends (following users). + Max count : 200 + """ + return await self._get_user_friendship_2( + user_id, + count, + Endpoint.FOLLOWING2, + cursor + ) + async def get_user_verified_followers( self, user_id: str, count: int = 20, cursor: str | None = None ) -> Result[User]: @@ -2967,6 +3027,105 @@ async def get_user_subscriptions( cursor ) + async def _get_friendship_ids( + self, + user_id: str | None, + screen_name: str | None, + count: int, + endpoint: str, + cursor: str | None + ) -> Result[int]: + params = {'count': count} + if user_id is not None: + params['user_id'] = user_id + elif user_id is not None: + params['screen_name'] = screen_name + + if cursor is not None: + params['cursor'] = cursor + + response = (await self.http.get( + endpoint, + params=params, + headers=self._base_headers + )).json() + previous_cursor = response['previous_cursor'] + next_cursor = response['next_cursor'] + + return Result( + response['ids'], + partial(self._get_friendship_ids, user_id, + screen_name, count, endpoint, next_cursor), + next_cursor, + partial(self._get_friendship_ids, user_id, + screen_name, count, endpoint, previous_cursor), + previous_cursor + ) + + async def get_followers_ids( + self, + user_id: str | None = None, + screen_name: str | None = None, + count: int = 5000, + cursor: str | None = None + ) -> Result[int]: + """ + Fetches the IDs of the followers of a specified user. + + Parameters + ---------- + user_id : :class:`str` | None, default=None + The ID of the user for whom to return results. + screen_name : :class:`str` | None, default=None + The screen name of the user for whom to return results. + count : :class:`int`, default=5000 + The maximum number of IDs to retrieve. + + Returns + ------- + :class:`Result`[:class:`int`] + A Result object containing the IDs of the followers. + """ + return await self._get_friendship_ids( + user_id, + screen_name, + count, + Endpoint.FOLLOWERS_IDS, + cursor + ) + + async def get_friends_ids( + self, + user_id: str | None = None, + screen_name: str | None = None, + count: int = 5000, + cursor: str | None = None + ) -> Result[int]: + """ + Fetches the IDs of the friends (following users) of a specified user. + + Parameters + ---------- + user_id : :class:`str` | None, default=None + The ID of the user for whom to return results. + screen_name : :class:`str` | None, default=None + The screen name of the user for whom to return results. + count : :class:`int`, default=5000 + The maximum number of IDs to retrieve. + + Returns + ------- + :class:`Result`[:class:`int`] + A Result object containing the IDs of the friends. + """ + return await self._get_friendship_ids( + user_id, + screen_name, + count, + Endpoint.FRIENDS_IDS, + cursor + ) + async def _send_dm( self, conversation_id: str, diff --git a/twikit/utils.py b/twikit/utils.py index 48f31543..c79b5d02 100644 --- a/twikit/utils.py +++ b/twikit/utils.py @@ -227,8 +227,13 @@ class Endpoint: HOME_TIMELINE = 'https://twitter.com/i/api/graphql/-X_hcgQzmHGl29-UXxz4sw/HomeTimeline' HOME_LATEST_TIMELINE = 'https://twitter.com/i/api/graphql/U0cdisy7QFIoTfu3-Okw0A/HomeLatestTimeline' FOLLOWERS = 'https://twitter.com/i/api/graphql/gC_lyAxZOptAMLCJX5UhWw/Followers' + FOLLOWERS2 = 'https://api.twitter.com/1.1/followers/list.json' + FOLLOWERS_IDS = 'https://api.twitter.com/1.1/followers/ids.json' + FRIENDS_IDS = 'https://api.twitter.com/1.1/friends/ids.json' + INCOMING_FRIENDSHIPS = 'https://api.twitter.com/1.1/friendships/incoming.json' BLUE_VERIFIED_FOLLOWERS = 'https://twitter.com/i/api/graphql/VmIlPJNEDVQ29HfzIhV4mw/BlueVerifiedFollowers' FOLLOWING = 'https://twitter.com/i/api/graphql/2vUj-_Ek-UmBVDNtd8OnQA/Following' + FOLLOWING2 = 'https://api.twitter.com/1.1/friends/list.json' FOLLOWERS_YOU_KNOW = 'https://twitter.com/i/api/graphql/f2tbuGNjfOE8mNUO5itMew/FollowersYouKnow' SUBSCRIPTIONS = 'https://twitter.com/i/api/graphql/Wsm5ZTCYtg2eH7mXAXPIgw/UserCreatorSubscriptions' SEND_DM = 'https://twitter.com/i/api/1.1/dm/new2.json'