twitter-internal-api 0.1.1

Twitter internal API
Documentation
use crate::utils::headers::RateLimitHeaders;
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct SearchTimelineParams {
    pub variables: Variables,
    pub features: Features,
    pub field_toggles: FieldToggles,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Features(Value);

impl Default for Features {
    fn default() -> Self {
        Self(serde_json::json!({
        "articles_preview_enabled": true,
        "c9s_tweet_anatomy_moderator_badge_enabled": true,
        "communities_web_enable_tweet_community_results_fetch": true,
        "creator_subscriptions_quote_tweet_preview_enabled": false,
        "creator_subscriptions_tweet_preview_api_enabled": true,
        "freedom_of_speech_not_reach_fetch_enabled": true,
        "graphql_is_translatable_rweb_tweet_is_translatable_enabled": true,
        "longform_notetweets_consumption_enabled": true,
        "longform_notetweets_inline_media_enabled": true,
        "longform_notetweets_rich_text_read_enabled": true,
        "payments_enabled": false,
        "premium_content_api_read_enabled": false,
        "profile_label_improvements_pcf_label_in_post_enabled": true,
        "responsive_web_edit_tweet_api_enabled": true,
        "responsive_web_enhance_cards_enabled": false,
        "responsive_web_graphql_exclude_directive_enabled": true,
        "responsive_web_graphql_skip_user_profile_image_extensions_enabled": false,
        "responsive_web_graphql_timeline_navigation_enabled": true,
        "responsive_web_grok_analysis_button_from_backend": true,
        "responsive_web_grok_analyze_button_fetch_trends_enabled": false,
        "responsive_web_grok_analyze_post_followups_enabled": true,
        "responsive_web_grok_image_annotation_enabled": true,
        "responsive_web_grok_share_attachment_enabled": true,
        "responsive_web_grok_show_grok_translated_post": false,
        "responsive_web_jetfuel_frame": false,
        "responsive_web_media_download_video_enabled": false,
        "responsive_web_twitter_article_tweet_consumption_enabled": false,
        "responsive_web_twitter_article_tweet_consumption_enabled": true,
        "rweb_lists_timeline_redesign_enabled": true,
        "rweb_tipjar_consumption_enabled": true,
        "rweb_video_screen_enabled": false,
        "standardized_nudges_misinfo": true,
        "tweet_awards_web_tipping_enabled": false,
        "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": true,
        "tweetypie_unmention_optimization_enabled": true,
        "verified_phone_label_enabled": false,
        "view_counts_everywhere_api_enabled": true,
        }))
    }
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[derive(Default)]
pub struct FieldToggles {
    pub with_article_rich_content_state: bool,
}

#[derive(Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct Variables {
    pub raw_query: String,
    pub count: u32,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cursor: Option<String>,
    pub query_source: QuerySource,
    pub product: Product,
}

#[derive(Clone, Serialize, Deserialize, Default)]
pub enum QuerySource {
    #[serde(rename = "typed_query")]
    #[default]
    TypedQuery,
}

#[derive(Clone, Serialize, Deserialize, Default, Debug, strum::EnumString, strum::Display)]
pub enum Product {
    #[default]
    #[serde(rename = "Top")]
    Top,
    #[serde(rename = "Latest")]
    Latest,
    #[serde(rename = "Photos")]
    Photos,
    #[serde(rename = "People")]
    People,
    #[serde(rename = "Videos")]
    Videos,
}

impl SearchTimelineParams {
    pub fn params(&self) -> anyhow::Result<Vec<(&str, String)>> {
        let params = &[
            ("variables", serde_json::to_string(&self.variables)?),
            ("features", serde_json::to_string(&self.features)?),
            ("fieldToggles", serde_json::to_string(&self.field_toggles)?),
        ];
        Ok(params.to_vec())
    }

    pub fn update_cursor(&mut self, cursor: Option<String>) {
        if let Some(cursor) = cursor {
            self.variables.cursor = Some(cursor);
        }
    }

    pub fn update_raw_query(&mut self, raw_query: &str) {
        self.variables.raw_query = raw_query.to_string();
    }

    pub fn update_count(&mut self, count: u32) {
        if count > 50 {
            self.variables.count = 50
        } else {
            self.variables.count = count;
        }
    }

    pub fn update_product(&mut self, product: Product) {
        self.variables.product = product;
    }
}

#[derive(Deserialize, Serialize, Debug)]
pub enum SearchTimeLineResponseType {
    Success,
    AuthError,
    RateLimit,
}

#[derive(Deserialize, Serialize, Debug)]
pub struct SearchTimeLineResponse {
    pub response_type: SearchTimeLineResponseType,
    pub response_content: Option<Value>,
    pub response_headers: RateLimitHeaders,
}