globescraper 0.3.2

Scraper lib for Globe Explorer AI engine
Documentation
use futures::{Stream, TryStreamExt};

use chrono::offset::Utc;
use chrono::DateTime;
use eventsource_client::{self as es, Client, Error};
use rand::distributions::{Alphanumeric, DistString};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{
    collections::HashMap,
    marker::PhantomData,
    time::SystemTime,
};
use urlencoding::encode;
use uuid::Uuid;

pub mod contants;

pub struct GlobeScraperClient<'a, T> {
    client: Box<dyn Client>,
    phantom: PhantomData<&'a T>,
}

#[derive(Serialize, Deserialize, Debug)]
struct QueryData {
    index: i8,
    r#type: String,
    searchbox_query: String,
    clicked_category: Option<String>,
    search_id: String,
    staged_image: Option<String>,
}

impl<'a, T> GlobeScraperClient<'a, T> {
    pub fn new(query: String) -> Result<Self, Error> {
        let user_id = Uuid::new_v4().to_string();
        let search_id = Uuid::new_v4().to_string();
        let model = String::from("default");
        let quer_data_object = vec![GlobeScraperClient::<T>::buil_query_data(
            query,
            search_id.clone(),
        )];
        let temp = serde_json::to_string(&quer_data_object).unwrap();
        let query_data = encode(&temp);
        let userid_local: String = GlobeScraperClient::<T>::build_user_id();

        let url = String::from(contants::GLOBE_BASE_URL)
            + &format!(
                "?queryData={}&userid_auth={}&model={}&search_id={}&userid_local={}",
                query_data, user_id, model, search_id, userid_local
            );

        let client = es::ClientBuilder::for_url(&url)?.build();

        Ok(GlobeScraperClient {
            client: Box::new(client),
            phantom: PhantomData,
        })
    }

    fn buil_query_data(search_query: String, search_id: String) -> QueryData {
        QueryData {
            index: 0,
            r#type: String::from("top_searchbox"),
            searchbox_query: search_query,
            clicked_category: None,
            search_id,
            staged_image: None,
        }
    }

    fn build_user_id() -> String {
        let mut user_prf = String::from("user");
        let string = Alphanumeric.sample_string(&mut rand::thread_rng(), 9);
        let now = Into::<DateTime<Utc>>::into(SystemTime::now());

        user_prf.push_str(string.as_str());
        user_prf.push_str(now.timestamp().to_string().as_str());

        return String::from(user_prf);
    }

    pub fn get_page(
        &'a mut self,
        props: &'a mut Box<HashMap<String, String>>,
    ) -> impl Stream<Item = Result<(), ()>> + 'a {
        return self
            .client
            .stream()
            .map_ok(|event| match event {
                es::SSE::Event(ev) => {
                    let ev_data: Value = serde_json::from_str(&ev.data).unwrap();

                    match ev_data.get("type") {
                        Some(data) => {
                            let index = data.as_str().unwrap();

                            if index.to_owned() == String::from("top_answer_chunk") {
                                match ev_data.get("data") {
                                    Some(data) => {
                                        let formated = data
                                            .to_string()
                                            .as_str()
                                            .replace("\"", "")
                                            .replace("\n", "");

                                        if props.contains_key(contants::GLOBE_DESCRIPTION_KEY) {
                                            let mut desc = props
                                                .get(contants::GLOBE_DESCRIPTION_KEY)
                                                .unwrap()
                                                .clone();
                                            desc.push_str(formated.as_str());
                                            props.insert(
                                                contants::GLOBE_DESCRIPTION_KEY.to_owned(),
                                                desc,
                                            );
                                        } else {
                                            props.insert(
                                                contants::GLOBE_DESCRIPTION_KEY.to_owned(),
                                                formated,
                                            );
                                        }
                                    }
                                    None => todo!(),
                                }
                            }
                        }
                        None => todo!(),
                    }
                }
                es::SSE::Comment(_comment) => {}
            })
            .map_err(|err| {
                println!("error occurred : {:?}", err.to_string());
                ()
            });
    }
}