Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion twikit/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
from ..trend import Location, PlaceTrend, PlaceTrends, Trend
from ..tweet import CommunityNote, Poll, ScheduledTweet, Tweet, tweet_from_data
from ..ui_metrics import solve_ui_metrics
from ..user import User
from ..user import User, ProfileSpotlights
from ..utils import (
Flow,
Result,
Expand Down Expand Up @@ -4322,3 +4322,8 @@ async def _update_subscriptions(
async def _get_user_state(self) -> Literal['normal', 'bounced', 'suspended']:
response, _ = await self.v11.user_state()
return response['userState']

async def get_profile_spotlights(self, display_name: str) -> ProfileSpotlights:
response, _ = await self.gql.profile_spotlights(display_name)
result = find_dict(response, 'result', find_one=True)[0]
return ProfileSpotlights(result)
Comment on lines +4326 to +4329
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Rename param to screen_name and handle empty/error responses.

“display_name” is misleading (API expects screen_name). Also avoid IndexError when no result is present and surface API errors consistently.

Apply:

-async def get_profile_spotlights(self, display_name: str) -> ProfileSpotlights:
-    response, _ = await self.gql.profile_spotlights(display_name)
-    result = find_dict(response, 'result', find_one=True)[0]
-    return ProfileSpotlights(result)
+async def get_profile_spotlights(self, screen_name: str) -> ProfileSpotlights:
+    response, _ = await self.gql.profile_spotlights(screen_name)
+    if isinstance(response, dict) and 'errors' in response:
+        raise_exceptions_from_response(response['errors'])
+    result_ = find_dict(response, 'result', find_one=True)
+    if not result_:
+        raise TwitterException(f'Profile spotlights not found for screen_name: {screen_name}')
+    return ProfileSpotlights(result_[0])

Run to ensure no callers rely on the old param name:


🏁 Script executed:

#!/bin/bash
rg -nC2 '\bget_profile_spotlights\s*\('

Length of output: 781


Rename parameter to screen_name and handle empty/error responses

Change the arg name and add explicit API-error / empty-result handling to avoid IndexError. Callers: twikit/user.py:528 calls self._client.get_profile_spotlights(self.screen_name) (positional) — safe to rename.

-async def get_profile_spotlights(self, display_name: str) -> ProfileSpotlights:
-    response, _ = await self.gql.profile_spotlights(display_name)
-    result = find_dict(response, 'result', find_one=True)[0]
-    return ProfileSpotlights(result)
+async def get_profile_spotlights(self, screen_name: str) -> ProfileSpotlights:
+    response, _ = await self.gql.profile_spotlights(screen_name)
+    if isinstance(response, dict) and 'errors' in response:
+        raise_exceptions_from_response(response['errors'])
+    result_ = find_dict(response, 'result', find_one=True)
+    if not result_:
+        raise TwitterException(f'Profile spotlights not found for screen_name: {screen_name}')
+    return ProfileSpotlights(result_[0])

Location: twikit/client/client.py:4326-4329; caller: twikit/user.py:528.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def get_profile_spotlights(self, display_name: str) -> ProfileSpotlights:
response, _ = await self.gql.profile_spotlights(display_name)
result = find_dict(response, 'result', find_one=True)[0]
return ProfileSpotlights(result)
async def get_profile_spotlights(self, screen_name: str) -> ProfileSpotlights:
response, _ = await self.gql.profile_spotlights(screen_name)
if isinstance(response, dict) and 'errors' in response:
raise_exceptions_from_response(response['errors'])
result_ = find_dict(response, 'result', find_one=True)
if not result_:
raise TwitterException(f'Profile spotlights not found for screen_name: {screen_name}')
return ProfileSpotlights(result_[0])
🤖 Prompt for AI Agents
In twikit/client/client.py around lines 4326-4329, rename the method parameter
from display_name to screen_name (callers use positional so this is safe) and
add explicit handling for API errors and empty results: call
self.gql.profile_spotlights(screen_name), check for an error condition in the
response and raise a descriptive exception (or return None) if present, then use
find_dict and verify the returned list is non-empty before indexing to avoid
IndexError; if the list is empty raise/return a clear API error indicating "no
profile spotlights found for {screen_name}" so callers get a deterministic
failure instead of an IndexError.

5 changes: 5 additions & 0 deletions twikit/client/gql.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def url(path):
MODERATORS_SLICE_TIMELINE_QUERY = url('9KI_r8e-tgp3--N5SZYVjg/moderatorsSliceTimeline_Query')
COMMUNITY_TWEET_SEARCH_MODULE_QUERY = url('5341rmzzvdjqfmPKfoHUBw/CommunityTweetSearchModuleQuery')
TWEET_RESULTS_BY_REST_IDS = url('PTN9HhBAlpoCTHfspDgqLA/TweetResultsByRestIds')
PROFILE_SPOTLIGHTS = url('1sAf0uU4-B2ZLJGUX5O7LQ/ProfileSpotlightsQuery')


class GQLClient:
Expand Down Expand Up @@ -682,6 +683,10 @@ async def tweet_results_by_rest_ids(self, tweet_ids):
}
return await self.gql_get(Endpoint.TWEET_RESULTS_BY_REST_IDS, variables, TWEET_RESULTS_BY_REST_IDS_FEATURES)

async def profile_spotlights(self, screen_name):
variables = {'screen_name': screen_name}
return await self.gql_get(Endpoint.PROFILE_SPOTLIGHTS, variables)

Comment on lines +686 to +689
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Pass USER_FEATURES and add a type hint for robustness.

Some GQL endpoints require feature gates; align with user queries and add typing.

Apply:

-async def profile_spotlights(self, screen_name):
-    variables = {'screen_name': screen_name}
-    return await self.gql_get(Endpoint.PROFILE_SPOTLIGHTS, variables)
+async def profile_spotlights(self, screen_name: str):
+    variables = {'screen_name': screen_name}
+    return await self.gql_get(Endpoint.PROFILE_SPOTLIGHTS, variables, USER_FEATURES)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def profile_spotlights(self, screen_name):
variables = {'screen_name': screen_name}
return await self.gql_get(Endpoint.PROFILE_SPOTLIGHTS, variables)
async def profile_spotlights(self, screen_name: str):
variables = {'screen_name': screen_name}
return await self.gql_get(Endpoint.PROFILE_SPOTLIGHTS, variables, USER_FEATURES)
🤖 Prompt for AI Agents
In twikit/client/gql.py around lines 686-689, update the profile_spotlights
method to accept and pass feature gates and add typing: change the signature to
include a user_features parameter (typed Optional[dict | Any] or appropriate
feature type) with a default of USER_FEATURES and add a return type hint (e.g.
-> Any or -> dict); then include user_features when calling self.gql_get (so
gql_get(Endpoint.PROFILE_SPOTLIGHTS, variables, user_features=user_features)).
Ensure any necessary imports for typing (Optional, Any) and the USER_FEATURES
symbol are present.

####################
# For guest client
####################
Expand Down
21 changes: 21 additions & 0 deletions twikit/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@
from .utils import Result


class ProfileSpotlights:
def __init__(self, data: dict):
self._data = data
relationship_perspectives = data.get('relationship_perspectives', {})
privacy = data.get('privacy', {})
core = data.get('core', {})

self.protected = privacy.get('protected', {})
self.blocking = relationship_perspectives.get('blocking', False)
self.blocked_by = relationship_perspectives.get('blocked_by', False)
self.following = relationship_perspectives.get('following', False)
self.followed_by = relationship_perspectives.get('followed_by', False)
self.is_verified_organisation = data.get('is_verified_organisation', False)
self.rest_id = data.get('rest_id', None)
self.name = core.get('name', None)
self.screen_name = core.get('screen_name', None)


class User:
"""
Attributes
Expand Down Expand Up @@ -507,6 +525,9 @@ async def get_highlights_tweets(self, count: int = 20, cursor: str | None = None
"""
return await self._client.get_user_highlights_tweets(self.id, count, cursor)

async def get_profile_spotlights(self) -> ProfileSpotlights:
return await self._client.get_profile_spotlights(self.screen_name)

async def update(self) -> None:
new = await self._client.get_user_by_id(self.id)
self.__dict__.update(new.__dict__)
Expand Down