Add comprehensive news/trending feature that fetches AI-generated news headlines from Twitter's "For You" page. This feature specifically targets the AI-curated news items that Twitter displays under "Today's News" section, not generic trending hashtags. - Uses Twitter's ExplorePage GraphQL API endpoint to access the Explore page timeline data where AI headlines are located - AI headlines are identified by the explicit `is_ai_trend: true` flag in the API response - Fallback heuristic detection for full-sentence headlines (5+ words) with "News" or time indicators in social context - Headlines are found in "stories-*" modules within the initialTimeline structure under a "Today's News" header - Follows the existing mixin pattern (withNews) for composability - Supports both AI-only filtering and mixed trending/AI results - `bird news` - Fetch news and trending topics (mixed results) - `bird news --ai-only` - Fetch ONLY AI-curated headlines - `bird news -n <count>` - Limit number of results - `bird news --json` - Output as JSON - `bird news --json-full` - Include raw API response - `bird news --with-tweets` - Enrich with related tweets - `bird trending` - Alias for news command - AI headlines are clearly marked with "AI · " category prefix - Automatic deduplication of duplicate headlines - Rich formatting with category, time, post count, and URLs - src/commands/news.ts - CLI command implementation - src/lib/twitter-client-news.ts - Core news fetching functionality - tests/commands.news.test.ts - Command validation tests (7 tests) - tests/twitter-client-coverage.news.test.ts - API coverage tests (7 tests) - README.md - Added documentation for news command - src/cli/program.ts - Registered news command - src/lib/index.ts - Exported news-related types - src/lib/twitter-client.ts - Integrated withNews mixin - src/lib/query-ids.json - Added ExplorePage query ID - src/lib/twitter-client-constants.ts - Added ExplorePage constant - src/lib/twitter-client-features.ts - Added buildExploreFeatures() - scripts/update-query-ids.ts - Added ExplorePage to update script - All 268 existing tests continue to pass - Added 14 new tests covering command validation and API functionality - Tested with real Twitter data confirming AI headline detection - All TypeScript compilation and linting checks pass Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
360 lines
17 KiB
TypeScript
360 lines
17 KiB
TypeScript
import { applyFeatureOverrides } from './runtime-features.js';
|
|
|
|
export function buildArticleFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('article', {
|
|
rweb_video_screen_enabled: true,
|
|
profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
responsive_web_profile_redirect_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_exclude_directive_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: false,
|
|
responsive_web_grok_annotations_enabled: false,
|
|
responsive_web_jetfuel_frame: true,
|
|
post_ctas_fetch_enabled: 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,
|
|
});
|
|
}
|
|
|
|
export function buildTweetDetailFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('tweetDetail', {
|
|
...buildArticleFeatures(),
|
|
responsive_web_graphql_exclude_directive_enabled: true,
|
|
communities_web_enable_tweet_community_results_fetch: true,
|
|
responsive_web_twitter_article_plain_text_enabled: true,
|
|
responsive_web_twitter_article_seed_tweet_detail_enabled: true,
|
|
responsive_web_twitter_article_seed_tweet_summary_enabled: true,
|
|
longform_notetweets_rich_text_read_enabled: true,
|
|
longform_notetweets_inline_media_enabled: true,
|
|
responsive_web_edit_tweet_api_enabled: true,
|
|
tweet_awards_web_tipping_enabled: false,
|
|
creator_subscriptions_quote_tweet_preview_enabled: false,
|
|
verified_phone_label_enabled: false,
|
|
});
|
|
}
|
|
|
|
export function buildArticleFieldToggles(): Record<string, boolean> {
|
|
return {
|
|
withPayments: false,
|
|
withAuxiliaryUserLabels: false,
|
|
withArticleRichContentState: true,
|
|
withArticlePlainText: true,
|
|
withGrokAnalyze: false,
|
|
withDisallowedReplyControls: false,
|
|
};
|
|
}
|
|
|
|
export function buildSearchFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('search', {
|
|
rweb_video_screen_enabled: true,
|
|
profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
responsive_web_profile_redirect_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_exclude_directive_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: false,
|
|
responsive_web_grok_annotations_enabled: false,
|
|
responsive_web_jetfuel_frame: true,
|
|
post_ctas_fetch_enabled: true,
|
|
responsive_web_grok_share_attachment_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,
|
|
rweb_video_timestamps_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,
|
|
articles_preview_enabled: true,
|
|
responsive_web_enhance_cards_enabled: false,
|
|
});
|
|
}
|
|
|
|
export function buildTweetCreateFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('tweetCreate', {
|
|
rweb_video_screen_enabled: true,
|
|
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: false,
|
|
responsive_web_grok_annotations_enabled: false,
|
|
responsive_web_jetfuel_frame: true,
|
|
post_ctas_fetch_enabled: true,
|
|
responsive_web_grok_share_attachment_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,
|
|
longform_notetweets_rich_text_read_enabled: true,
|
|
longform_notetweets_inline_media_enabled: true,
|
|
profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
responsive_web_profile_redirect_enabled: false,
|
|
rweb_tipjar_consumption_enabled: true,
|
|
verified_phone_label_enabled: false,
|
|
articles_preview_enabled: true,
|
|
responsive_web_grok_community_note_auto_translation_is_enabled: false,
|
|
responsive_web_graphql_skip_user_profile_image_extensions_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,
|
|
responsive_web_grok_image_annotation_enabled: true,
|
|
responsive_web_grok_imagine_annotation_enabled: true,
|
|
responsive_web_graphql_timeline_navigation_enabled: true,
|
|
responsive_web_enhance_cards_enabled: false,
|
|
});
|
|
}
|
|
|
|
export function buildTimelineFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('timeline', {
|
|
...buildSearchFeatures(),
|
|
blue_business_profile_image_shape_enabled: true,
|
|
responsive_web_text_conversations_enabled: false,
|
|
tweetypie_unmention_optimization_enabled: true,
|
|
vibe_api_enabled: true,
|
|
responsive_web_twitter_blue_verified_badge_is_enabled: true,
|
|
interactive_text_enabled: true,
|
|
longform_notetweets_richtext_consumption_enabled: true,
|
|
responsive_web_media_download_video_enabled: false,
|
|
});
|
|
}
|
|
|
|
export function buildBookmarksFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('bookmarks', {
|
|
...buildTimelineFeatures(),
|
|
graphql_timeline_v2_bookmark_timeline: true,
|
|
});
|
|
}
|
|
|
|
export function buildLikesFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('likes', buildTimelineFeatures());
|
|
}
|
|
|
|
export function buildListsFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('lists', {
|
|
rweb_video_screen_enabled: true,
|
|
profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
responsive_web_profile_redirect_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_exclude_directive_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: false,
|
|
responsive_web_grok_annotations_enabled: false,
|
|
responsive_web_jetfuel_frame: true,
|
|
post_ctas_fetch_enabled: 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,
|
|
blue_business_profile_image_shape_enabled: false,
|
|
responsive_web_text_conversations_enabled: false,
|
|
tweetypie_unmention_optimization_enabled: true,
|
|
vibe_api_enabled: false,
|
|
interactive_text_enabled: false,
|
|
});
|
|
}
|
|
|
|
export function buildHomeTimelineFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('homeTimeline', {
|
|
...buildTimelineFeatures(),
|
|
});
|
|
}
|
|
|
|
export function buildUserTweetsFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('userTweets', {
|
|
rweb_video_screen_enabled: false,
|
|
profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
responsive_web_profile_redirect_enabled: false,
|
|
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,
|
|
post_ctas_fetch_enabled: true,
|
|
responsive_web_grok_share_attachment_enabled: true,
|
|
responsive_web_grok_annotations_enabled: false,
|
|
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: true,
|
|
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,
|
|
});
|
|
}
|
|
|
|
export function buildFollowingFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('following', {
|
|
rweb_video_screen_enabled: true,
|
|
profile_label_improvements_pcf_label_in_post_enabled: false,
|
|
responsive_web_profile_redirect_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: true,
|
|
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: false,
|
|
responsive_web_grok_annotations_enabled: false,
|
|
responsive_web_jetfuel_frame: false,
|
|
post_ctas_fetch_enabled: true,
|
|
responsive_web_grok_share_attachment_enabled: false,
|
|
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: true,
|
|
responsive_web_grok_show_grok_translated_post: false,
|
|
responsive_web_grok_analysis_button_from_backend: false,
|
|
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: false,
|
|
responsive_web_grok_imagine_annotation_enabled: false,
|
|
responsive_web_grok_community_note_auto_translation_is_enabled: false,
|
|
responsive_web_enhance_cards_enabled: false,
|
|
});
|
|
}
|
|
|
|
export function buildExploreFeatures(): Record<string, boolean> {
|
|
return applyFeatureOverrides('explore', {
|
|
rweb_video_screen_enabled: true,
|
|
profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
responsive_web_profile_redirect_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_exclude_directive_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: true,
|
|
responsive_web_grok_analyze_post_followups_enabled: true,
|
|
responsive_web_grok_annotations_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: true,
|
|
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: true,
|
|
responsive_web_enhance_cards_enabled: false,
|
|
// Additional features required for ExploreSidebar
|
|
post_ctas_fetch_enabled: true,
|
|
rweb_video_timestamps_enabled: true,
|
|
});
|
|
}
|