tail-fin-twitter 0.5.1

Twitter/X adapter for tail-fin: timeline, search, profile, bookmarks, likes, thread, post, like, follow, block, bookmark, reply, trending, lists, article, download, notifications
Documentation
pub mod actions;
pub mod auth;
pub mod graphql;
#[cfg(feature = "http")]
pub mod http;
pub mod parsing;
pub mod search;
pub mod site;
pub mod timeline;
pub mod types;
pub mod util;

use tail_fin_common::BrowserSession;
use tail_fin_common::TailFinError;

pub use site::TwitterSite;
pub use types::{
    AcceptResult, ActionResult, Article, DmResult, MediaItem, Notification, TimelineResponse,
    TimelineType, Trend, Tweet, TwitterList, TwitterUser, UserProfile,
};
pub use util::extract_tweet_id;

#[cfg(feature = "http")]
pub use http::TwitterHttpClient;

/// Shared trait for operations available on both browser and HTTP clients.
pub trait TwitterApi {
    /// Fetch a page of the user's home timeline.
    fn timeline(
        &self,
        kind: TimelineType,
        count: usize,
        cursor: Option<&str>,
    ) -> impl std::future::Future<Output = Result<TimelineResponse, TailFinError>> + Send;

    /// Search for tweets matching the given query.
    fn search(
        &self,
        query: &str,
        count: usize,
        cursor: Option<&str>,
    ) -> impl std::future::Future<Output = Result<TimelineResponse, TailFinError>> + Send;
}

/// High-level Twitter client backed by a NightFury browser session.
///
/// The browser must already be navigated to (or able to navigate to) x.com
/// with an active login session.
pub struct TwitterClient {
    session: BrowserSession,
}

impl TwitterClient {
    /// Create a new TwitterClient wrapping the given browser session.
    pub fn new(session: BrowserSession) -> Self {
        Self { session }
    }

    /// Get a reference to the underlying browser session.
    pub fn session(&self) -> &BrowserSession {
        &self.session
    }

    /// Post a new tweet.
    pub async fn post(&self, text: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_post(&self.session, text).await
    }

    /// Like a tweet by URL.
    pub async fn like(&self, tweet_url: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_like(&self.session, tweet_url).await
    }

    /// Follow a user by screen name.
    pub async fn follow(&self, username: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_follow(&self.session, username, true).await
    }

    /// Unfollow a user by screen name.
    pub async fn unfollow(&self, username: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_follow(&self.session, username, false).await
    }

    /// Delete a tweet by URL.
    pub async fn delete(&self, tweet_url: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_delete(&self.session, tweet_url).await
    }

    /// Block a user by screen name.
    pub async fn block(&self, username: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_block(&self.session, username, true).await
    }

    /// Unblock a user by screen name.
    pub async fn unblock(&self, username: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_block(&self.session, username, false).await
    }

    /// Reply to a tweet.
    pub async fn reply(&self, tweet_url: &str, text: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_reply(&self.session, tweet_url, text).await
    }

    /// Bookmark a tweet by URL.
    pub async fn bookmark(&self, tweet_url: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_bookmark(&self.session, tweet_url, true).await
    }

    /// Remove a bookmark from a tweet by URL.
    pub async fn unbookmark(&self, tweet_url: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_bookmark(&self.session, tweet_url, false).await
    }

    /// Get trending topics.
    pub async fn trending(&self, count: usize) -> Result<Vec<Trend>, TailFinError> {
        actions::browser_trending(&self.session, count).await
    }

    /// Hide a reply on your tweet.
    pub async fn hide_reply(&self, tweet_url: &str) -> Result<ActionResult, TailFinError> {
        actions::browser_hide_reply(&self.session, tweet_url).await
    }

    /// Download media URLs from a user's media tab.
    pub async fn download(
        &self,
        username: &str,
        count: usize,
    ) -> Result<Vec<MediaItem>, TailFinError> {
        actions::browser_download(&self.session, username, count).await
    }

    /// Get the authenticated user's lists.
    pub async fn lists(&self, count: usize) -> Result<Vec<TwitterList>, TailFinError> {
        actions::browser_lists(&self.session, count).await
    }

    /// Reply to DM conversations with a message.
    pub async fn reply_dm(&self, text: &str, count: usize) -> Result<Vec<DmResult>, TailFinError> {
        actions::browser_reply_dm(&self.session, text, count).await
    }

    /// Accept pending follow requests.
    pub async fn accept(
        &self,
        filter: Option<&str>,
        count: usize,
    ) -> Result<Vec<AcceptResult>, TailFinError> {
        actions::browser_accept(&self.session, filter, count).await
    }
}

impl TwitterApi for TwitterClient {
    async fn timeline(
        &self,
        kind: TimelineType,
        count: usize,
        cursor: Option<&str>,
    ) -> Result<TimelineResponse, TailFinError> {
        timeline::fetch_timeline(&self.session, kind, cursor, count).await
    }

    async fn search(
        &self,
        query: &str,
        count: usize,
        cursor: Option<&str>,
    ) -> Result<TimelineResponse, TailFinError> {
        search::search_tweets(&self.session, query, count, cursor).await
    }
}