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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.json
/*.py
/node_modules
/node_modules
__pycache__
36 changes: 18 additions & 18 deletions twikit/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from .._captcha import Capsolver
from ..bookmark import BookmarkFolder
from ..community import Community, CommunityMember
from ..constants import TOKEN, DOMAIN
from ..constants import TOKEN, DOMAIN, TIMELINE_IDS
from ..errors import (
AccountLocked,
AccountSuspended,
Expand Down Expand Up @@ -2594,30 +2594,30 @@ async def get_trends(
...
"""
category = category.lower()
if category in ['news', 'sports', 'entertainment']:
category += '_unified'
response, _ = await self.v11.guide(category, count, additional_request_params)

entry_id_prefix = 'trends' if category == 'trending' else 'Guide'
timeline_id = TIMELINE_IDS.get(category)
if timeline_id is None:
return []

response, _ = await self.gql.generic_timeline_by_id(timeline_id, count)
entry_id_prefix = "trend"
entries = [
i for i in find_dict(response, 'entries', find_one=True)[0]
if i['entryId'].startswith(entry_id_prefix)
]

if not entries:
if not retry:
return []
# Recall the method again, as the trend information
# may not be returned due to a Twitter error.
return await self.get_trends(category, count, retry, additional_request_params)

items = entries[-1]['content']['timelineModule']['items']

if not retry:
return []
# Recall the method again, as the trend information
# may not be returned due to a Twitter error.
return await self.get_trends(category, count, retry, additional_request_params)

results = []
for item in items:
trend_info = item['item']['content']['trend']
for entry in entries:
item_content = entry['content'].get('itemContent', {})
trend_info = item_content.get('trend')
if not trend_info:
continue
results.append(Trend(self, trend_info))

return results

async def get_available_locations(self) -> list[Location]:
Expand Down
17 changes: 16 additions & 1 deletion twikit/client/gql.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
TWEET_RESULT_BY_REST_ID_FEATURES,
TWEET_RESULTS_BY_REST_IDS_FEATURES,
USER_FEATURES,
USER_HIGHLIGHTS_TWEETS_FEATURES
USER_HIGHLIGHTS_TWEETS_FEATURES,
EXPLORE_PAGE_FEATURES,
GENERIC_TIMELINE_FEATURES
)
from ..utils import flatten_params, get_query_id

Expand Down Expand Up @@ -68,6 +70,8 @@ def url(path):
DELETE_BOOKMARK_FOLDER = url('2UTTsO-6zs93XqlEUZPsSg/DeleteBookmarkFolder')
CREATE_BOOKMARK_FOLDER = url('6Xxqpq8TM_CREYiuof_h5w/createBookmarkFolder')
FOLLOWERS = url('gC_lyAxZOptAMLCJX5UhWw/Followers')
EXPLORE_PAGE = url('Lr7rbLxwMLDrWFJrlCdRVw/ExplorePage')
GENERIC_TIMELINE_BY_ID = url('J5pGd3g_8gGG28OGzHci8g/GenericTimelineById')
BLUE_VERIFIED_FOLLOWERS = url('VmIlPJNEDVQ29HfzIhV4mw/BlueVerifiedFollowers')
FOLLOWERS_YOU_KNOW = url('f2tbuGNjfOE8mNUO5itMew/FollowersYouKnow')
FOLLOWING = url('2vUj-_Ek-UmBVDNtd8OnQA/Following')
Expand Down Expand Up @@ -301,6 +305,17 @@ async def retweeters(self, tweet_id, count, cursor):
async def favoriters(self, tweet_id, count, cursor):
return await self.tweet_engagements(tweet_id, count, cursor, Endpoint.FAVORITERS)

async def explore_page(self):
variables = {'cursor': ''}
return await self.gql_get(Endpoint.EXPLORE_PAGE, variables, EXPLORE_PAGE_FEATURES)

async def generic_timeline_by_id(self, timeline_id, count):
variables = {
'timelineId': timeline_id,
'count': count
}
return await self.gql_get(Endpoint.GENERIC_TIMELINE_BY_ID, variables, GENERIC_TIMELINE_FEATURES)

async def bird_watch_one_note(self, note_id):
variables = {'note_id': note_id}
return await self.gql_get(Endpoint.FETCH_COMMUNITY_NOTE, variables, COMMUNITY_NOTE_FEATURES)
Expand Down
83 changes: 83 additions & 0 deletions twikit/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,86 @@
'responsive_web_graphql_timeline_navigation_enabled': True,
'responsive_web_enhance_cards_enabled': False
}

EXPLORE_PAGE_FEATURES = {
"rweb_video_screen_enabled": False,
"payments_enabled": False,
"profile_label_improvements_pcf_label_in_post_enabled": True,
"rweb_tipjar_consumption_enabled": True,
"verified_phone_label_enabled": False,
"responsive_web_graphql_timeline_navigation_enabled": True,
"responsive_web_graphql_skip_user_profile_image_extensions_enabled": False,
"creator_subscriptions_tweet_preview_api_enabled": True,
"premium_content_api_read_enabled": False,
"communities_web_enable_tweet_community_results_fetch": True,
"c9s_tweet_anatomy_moderator_badge_enabled": True,
"responsive_web_grok_analyze_button_fetch_trends_enabled": False,
"responsive_web_grok_analyze_post_followups_enabled": True,
"responsive_web_jetfuel_frame": True,
"responsive_web_grok_share_attachment_enabled": True,
"articles_preview_enabled": True,
"responsive_web_edit_tweet_api_enabled": True,
"graphql_is_translatable_rweb_tweet_is_translatable_enabled": True,
"view_counts_everywhere_api_enabled": True,
"longform_notetweets_consumption_enabled": True,
"responsive_web_twitter_article_tweet_consumption_enabled": True,
"tweet_awards_web_tipping_enabled": False,
"responsive_web_grok_show_grok_translated_post": False,
"responsive_web_grok_analysis_button_from_backend": True,
"creator_subscriptions_quote_tweet_preview_enabled": False,
"freedom_of_speech_not_reach_fetch_enabled": True,
"standardized_nudges_misinfo": True,
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": True,
"longform_notetweets_rich_text_read_enabled": True,
"longform_notetweets_inline_media_enabled": True,
"responsive_web_grok_image_annotation_enabled": True,
"responsive_web_grok_imagine_annotation_enabled": True,
"responsive_web_grok_community_note_auto_translation_is_enabled": False,
"responsive_web_enhance_cards_enabled": False
}

GENERIC_TIMELINE_FEATURES = {
"rweb_video_screen_enabled": False,
"payments_enabled": False,
"profile_label_improvements_pcf_label_in_post_enabled": True,
"rweb_tipjar_consumption_enabled": True,
"verified_phone_label_enabled": False,
"creator_subscriptions_tweet_preview_api_enabled": True,
"responsive_web_graphql_timeline_navigation_enabled": True,
"responsive_web_graphql_skip_user_profile_image_extensions_enabled": False,
"premium_content_api_read_enabled": False,
"communities_web_enable_tweet_community_results_fetch": True,
"c9s_tweet_anatomy_moderator_badge_enabled": True,
"responsive_web_grok_analyze_button_fetch_trends_enabled": False,
"responsive_web_grok_analyze_post_followups_enabled": True,
"responsive_web_jetfuel_frame": True,
"responsive_web_grok_share_attachment_enabled": True,
"articles_preview_enabled": True,
"responsive_web_edit_tweet_api_enabled": True,
"graphql_is_translatable_rweb_tweet_is_translatable_enabled": True,
"view_counts_everywhere_api_enabled": True,
"longform_notetweets_consumption_enabled": True,
"responsive_web_twitter_article_tweet_consumption_enabled": True,
"tweet_awards_web_tipping_enabled": False,
"responsive_web_grok_show_grok_translated_post": False,
"responsive_web_grok_analysis_button_from_backend": True,
"creator_subscriptions_quote_tweet_preview_enabled": False,
"freedom_of_speech_not_reach_fetch_enabled": True,
"standardized_nudges_misinfo": True,
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": True,
"longform_notetweets_rich_text_read_enabled": True,
"longform_notetweets_inline_media_enabled": True,
"responsive_web_grok_image_annotation_enabled": True,
"responsive_web_grok_imagine_annotation_enabled": True,
"responsive_web_grok_community_note_auto_translation_is_enabled": False,
"responsive_web_enhance_cards_enabled": False
}

# NOTE: you can fetch these using explore_page
TIMELINE_IDS = {
'trending': 'VGltZWxpbmU6DAC2CwABAAAACHRyZW5kaW5nAAA=',
'for-you': 'VGltZWxpbmU6DAC2CwABAAAAB2Zvcl95b3UAAA==',
'news': 'VGltZWxpbmU6DAC2CwABAAAABG5ld3MAAA==',
'sports': 'VGltZWxpbmU6DAC2CwABAAAABnNwb3J0cwAA',
'entertainment': 'VGltZWxpbmU6DAC2CwABAAAADWVudGVydGFpbm1lbnQAAA==',
}
9 changes: 5 additions & 4 deletions twikit/trend.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ class Trend:
def __init__(self, client: Client, data: dict) -> None:
self._client = client

metadata: dict = data['trendMetadata']
metadata: dict = data['trend_metadata']
self.name: str = data['name']
self.tweets_count: int | None = metadata.get('metaDescription')
self.domain_context: str = metadata.get('domainContext')
# TODO: parse to int
self.tweets_count: str | None = metadata.get('meta_description')
self.domain_context: str = metadata.get('domain_context')
self.grouped_trends: list[str] = [
trend['name'] for trend in data.get('groupedTrends', [])
trend['name'] for trend in data.get('grouped_trends', [])
]

def __repr__(self) -> str:
Expand Down