bing_client 0.1.2

Async reverse client for Bing copilot
Documentation
use std::time::Duration;

use chrono::{DateTime, Utc};
use reqwest::{
    header::{HeaderMap, CONTENT_SECURITY_POLICY},
    redirect::Policy,
};
use serde_json::{json, Value};
use tokio::time::sleep;

use crate::{
    const_vars::{gen_draw_image_url, gen_get_images_url},
    types::bot_easy_resp_type::Image,
};

pub async fn gen_pool_image_url(
    prompt: &str,
    reqwest_header: HeaderMap,
    message_id: &str,
) -> Result<String, anyhow::Error> {
    #[cfg(not(feature = "allow-invalid-tls"))]
    let client = reqwest::Client::builder()
        .redirect(Policy::none())
        .build()?;

    #[cfg(feature = "allow-invalid-tls")]
    let client = reqwest::Client::builder()
        .redirect(Policy::none())
        .danger_accept_invalid_certs(true)
        .build()?;

    let response = client
        .get(gen_draw_image_url(prompt, &message_id))
        .headers(reqwest_header.clone())
        .send()
        .await?;
    let redirect_url = response
        .headers()
        .get("location")
        .ok_or(anyhow::anyhow!("Drawing Failed: Redirect failed"))?
        .to_str()
        .map_err(|_| anyhow::anyhow!("Drawing Failed: Invalid location header"))?;
    let redirect_url = format!("https://www.bing.com{}", redirect_url);

    let response = client
        .get(&redirect_url)
        .headers(reqwest_header.clone())
        .send()
        .await?;
    let mut request_id = response
        .headers()
        .get("location")
        .ok_or(anyhow::anyhow!("Drawing Failed: Redirect failed"))?
        .to_str()
        .map_err(|_| anyhow::anyhow!("Drawing Failed: Invalid location header"))?
        .split("id=")
        .last()
        .ok_or(anyhow::anyhow!("Drawing Failed: Invalid location header"))?;
    request_id = &request_id.split('&').collect::<Vec<&str>>()[0];
    Ok(gen_get_images_url(request_id))
}

pub async fn poll_images(
    polling_url: String,
    reqwest_header: HeaderMap,
    wait_long: bool,
) -> Result<Vec<Image>, anyhow::Error> {
    #[cfg(not(feature = "allow-invalid-tls"))]
    let client = reqwest::Client::builder()
        .default_headers(reqwest_header)
        .build()?;

    #[cfg(feature = "allow-invalid-tls")]
    let client = reqwest::Client::builder()
        .default_headers(reqwest_header)
        .danger_accept_invalid_certs(true)
        .build()?;

    let mut times = match wait_long {
        true => 100,
        _ => 50,
    };
    let content = loop {
        times -= 1;
        if times < 0 {
            return Err(anyhow::anyhow!("Drawing Failed: Timed out."));
        }
        let response = client
            .get(&polling_url)
            .header(CONTENT_SECURITY_POLICY, "script-src 'none'")
            .send()
            .await?;
        if response.status() != 200 {
            return Err(anyhow::anyhow!("Drawing Failed: Could not get results"));
        }
        let text = response.text().await?;
        if text.contains("th.bing.com/th") {
            break text;
        }
        sleep(Duration::from_micros(500)).await;
    };
    let links = content
        .split("src=\"")
        .filter_map(|s| {
            if let Some(end) = s.find("\"") {
                let link = s[..end].to_string();
                if let Some(link) = link.split("?w=").next() {
                    if !link.contains("r.bing.com")
                        && link.starts_with("https://")
                        && !link.starts_with("https://www.clarity.")
                    {
                        return Some(link.to_string());
                    }
                }
            }
            None
        })
        .collect::<Vec<String>>();
    if !links.is_empty() {
        let imgs: Vec<crate::types::bot_easy_resp_type::Image> = links
            .iter()
            .enumerate()
            .map(|(index, link)| crate::types::bot_easy_resp_type::Image {
                name: format!("bing_image_{}.jpg", index + 1),
                url: link.to_string(),
            })
            .collect();
        Ok(imgs)
    } else {
        return Err(anyhow::anyhow!(
            "Poll Draw Image Failed: No images are found."
        ));
    }
}

pub fn gen_update_draw_conversation(message_id: &str, prompt: &str, persistent_url: &str) -> Value {
    let time = {
        let dt: DateTime<Utc> = Utc::now();
        format!("{}", dt.to_rfc3339())
    };
    json!({
      "author": "bot",
      "contentOrigin": "DeepLeo",
      "contentType": "IMAGE",
      "createdAt": time,
      "feedback": { "tag": null, "updatedOn": null, "type": "None" },
      "invocation": format!("graphic_art(prompt=\"{prompt}\")",),
      "messageId": message_id,
      "messageType": "GenerateContentQuery",
      "offense": "None",
      "requestId": "97a9034e-2c6c-b97f-b71b-6b9e233966fa",
      "text": prompt,
      "timestamp": time,
      "responseType": 0,
      "genStream": false,
      "adaptiveCards": [
        {
          "type": "AdaptiveCard",
          "version": "1.0",
          "body": [
            {
              "type": "TextBlock",
              "persistentUrl": persistent_url,
              "id": "bic-persistent-url",
              "iframeid": message_id,
              "iframeWidth": 475,
              "iframeHeight": 520,
              "isVisible": false
            }
          ]
        }
      ]
    }
    )
}