-
-
Notifications
You must be signed in to change notification settings - Fork 456
add profile spotlights query #386
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Reviewer's GuideThis PR adds support for retrieving profile spotlights by defining a new GraphQL query endpoint, extending both the low-level GQL client and the high-level client with fetch methods, introducing a ProfileSpotlights model to parse the API response, and exposing it through a User accessor. Sequence diagram for fetching profile spotlights via UsersequenceDiagram
participant U as User
participant C as Client
participant G as GQLClient
participant S as ProfileSpotlights
U->>C: get_profile_spotlights(screen_name)
C->>G: profile_spotlights(screen_name)
G-->>C: GraphQL response
C->>S: ProfileSpotlights(result)
C-->>U: ProfileSpotlights instance
Entity relationship diagram for ProfileSpotlights data structureerDiagram
PROFILE_SPOTLIGHTS {
string rest_id
string name
string screen_name
boolean is_verified_organisation
boolean blocking
boolean blocked_by
boolean following
boolean followed_by
object protected
}
PROFILE_SPOTLIGHTS ||--o| USER : "belongs to"
USER {
string screen_name
string id
}
Class diagram for new ProfileSpotlights model and related methodsclassDiagram
class ProfileSpotlights {
+__init__(data: dict)
+protected
+blocking
+blocked_by
+following
+followed_by
+is_verified_organisation
+rest_id
+name
+screen_name
}
class User {
+get_profile_spotlights() ProfileSpotlights
}
class Client {
+get_profile_spotlights(display_name: str) ProfileSpotlights
}
class GQLClient {
+profile_spotlights(screen_name: str)
}
User --> Client : uses
Client --> GQLClient : uses
Client --> ProfileSpotlights : returns
User --> ProfileSpotlights : returns
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
WalkthroughAdds a new Profile Spotlights feature: defines a ProfileSpotlights data model, exposes Client.get_profile_spotlights(display_name) to fetch data via a new GQL endpoint, and adds User.get_profile_spotlights() as a convenience wrapper. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev as Caller
participant User as User
participant Client as Client
participant GQL as GQLClient
participant API as GraphQL API
Dev->>User: await user.get_profile_spotlights()
User->>Client: get_profile_spotlights(screen_name)
Client->>GQL: profile_spotlights(screen_name)
GQL->>API: gql_get(PROFILE_SPOTLIGHTS, {screen_name})
API-->>GQL: JSON { data: { result: [...] } }
GQL-->>Client: data
Client->>Client: extract first result
Client-->>User: ProfileSpotlights(instance)
User-->>Dev: ProfileSpotlights
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Pre-merge checks (2 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. ✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
twikit/client/client.py(2 hunks)twikit/client/gql.py(2 hunks)twikit/user.py(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
twikit/user.py (1)
twikit/client/client.py (2)
get(209-211)get_profile_spotlights(4326-4329)
twikit/client/client.py (3)
twikit/user.py (2)
ProfileSpotlights(17-32)get_profile_spotlights(528-529)twikit/client/gql.py (1)
profile_spotlights(686-688)twikit/utils.py (1)
find_dict(111-127)
twikit/client/gql.py (1)
twikit/client/v11.py (1)
Endpoint(14-50)
🔇 Additional comments (4)
twikit/client/gql.py (1)
102-102: LGTM: endpoint added correctly.Constant name and path look consistent with existing endpoints.
twikit/client/client.py (1)
49-49: LGTM: type import is correct.Public return type wired up properly.
twikit/user.py (2)
528-530: LGTM, but depends on the client rename.Wrapper is fine; will align once
Client.get_profile_spotlights(screen_name)is renamed.After renaming in Client, ensure this call site still type-checks and runs.
17-33: Fix protected default type; add org spelling fallback.
protectedshould default to a bool, not{}.- Keep the
is_verified_organisationattribute but fall back to the US spelling key.Apply:
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.protected = bool(privacy.get('protected', False)) 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.is_verified_organisation = data.get( + 'is_verified_organisation', + data.get('is_verified_organization', False) + ) self.rest_id = data.get('rest_id', None) self.name = core.get('name', None) self.screen_name = core.get('screen_name', None)If you have a sample profile_spotlights payload, confirm whether
name/screen_namelive undercore(as used here) or underlegacyso mappings are correct.
| 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) |
There was a problem hiding this comment.
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.
| 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.
| async def profile_spotlights(self, screen_name): | ||
| variables = {'screen_name': screen_name} | ||
| return await self.gql_get(Endpoint.PROFILE_SPOTLIGHTS, variables) | ||
|
|
There was a problem hiding this comment.
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.
| 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.
Summary by Sourcery
Add support for fetching and modeling profile spotlights via a new GraphQL query.
New Features:
Summary by CodeRabbit