about summary refs log blame commit diff
path: root/users/tazjin/tgsa/src/main.rs
blob: cfb1c7223f79ffaa87bf464edecc672dfbc31760 (plain) (tree)































                                                                               
                        







































                                                                                           










                                                                                  



                  
               


      
                                               
                                                                         
                                                           
 
                               
 







                                                                              
 
                                   

 
                                                                



                                                           






                                                                      
                              
                                                                          





                                         
                          
                                                          


                      


                                 





                       



                                         


                                            
                                               
 
                                                    
 
use anyhow::{anyhow, Context, Result};

#[derive(Debug)]
struct TgLink {
    username: String,
    message_id: usize,
}

impl TgLink {
    fn to_url(&self) -> String {
        format!("https://t.me/{}/{}?embed=1", self.username, self.message_id)
    }
}

fn fetch_embed(link: &TgLink) -> Result<String> {
    let response = crimp::Request::get(&link.to_url())
        .send()
        .context("failed to fetch embed data")?
        .as_string()
        .context("failed to decode embed data")?
        .error_for_status(|resp| {
            anyhow!("telegram request failed: {} ({})", resp.body, resp.status)
        })?;

    Ok(response.body)
}

#[derive(Debug)]
struct TgMessage {
    author: String,
    message: Option<String>,
    photos: Vec<String>,
    videos: Vec<String>,
}

fn extract_photo_url(style: &str) -> Option<&str> {
    let url_start = style.find("url('")? + 5;
    let url_end = style.find("')")?;

    Some(&style[url_start..url_end])
}

fn parse_tgmessage(embed: &str) -> Result<TgMessage> {
    use scraper::{Html, Selector};

    let doc = Html::parse_document(embed);

    let author_sel = Selector::parse("a.tgme_widget_message_owner_name").unwrap();
    let author = doc
        .select(&author_sel)
        .next()
        .ok_or_else(|| anyhow!("failed to find message author"))?
        .text()
        .collect::<Vec<&str>>()
        .concat();

    let msg_sel = Selector::parse("div.tgme_widget_message_text.js-message_text").unwrap();
    let message = doc
        .select(&msg_sel)
        .next()
        .map(|m| m.text().collect::<Vec<&str>>().concat());

    let photo_sel = Selector::parse("a.tgme_widget_message_photo_wrap").unwrap();
    let mut photos = vec![];

    for photo in doc.select(&photo_sel) {
        if let Some(style) = photo.value().attr("style") {
            if let Some(url) = extract_photo_url(style) {
                photos.push(url.to_string())
            }
        }
    }

    let video_sel = Selector::parse("i.tgme_widget_message_video_thumb").unwrap();
    let mut videos = vec![];

    for video in doc.select(&video_sel) {
        if let Some(style) = video.value().attr("style") {
            if let Some(url) = extract_photo_url(style) {
                videos.push(url.to_string())
            }
        }
    }

    Ok(TgMessage {
        author,
        message,
        photos,
        videos,
    })
}

fn shorten_link(link: &str) -> Result<String> {
    let mut url = url::Url::parse("https://tinyurl.com/api-create.php")?;
    url.query_pairs_mut().clear().append_pair("url", link);

    let request = url.as_str();

    let response = crimp::Request::get(request)
        .send()
        .context("failed to shorten URL")?
        .as_string()
        .context("failed to decode shortened URL")?
        .error_for_status(|resp| {
            anyhow!("tinyurl request failed: {} ({})", resp.body, resp.status)
        })?;

    Ok(response.body.trim().into())
}

fn to_bbcode(link: &TgLink, msg: &TgMessage) -> Result<String> {
    let mut out = String::new();

    out.push_str(&format!("[quote=\"{}\"]\n", msg.author));

    for video in &msg.videos {
        out.push_str(&format!("[url=\"{}\"]", link.to_url()));
        out.push_str(&format!("[img]{}[/img]", shorten_link(video)?));
        out.push_str("[/url]\n");
        out.push_str("[sub](Click thumbnail to open video)[/sub]\n")
    }

    for photo in &msg.photos {
        out.push_str(&format!("[timg]{}[/timg]\n", shorten_link(photo)?));
    }

    if let Some(message) = &msg.message {
        out.push_str(message);
    }

    out.push_str(&format!(
        "\n\n[sub](via [url=\"{}\"]Telegram[/url])[/sub]",
        link.to_url(),
    ));

    out.push_str("\n[/quote]\n");

    Ok(out)
}

fn main() {
    crimp::init();

    let link = TgLink {
        // username: "RWApodcast".into(),
        // message_id: 113,
        username: "intelslava".into(),
        message_id: 25483,
    };

    let embed = fetch_embed(&link).unwrap();
    let msg = parse_tgmessage(&embed).unwrap();

    println!("{}", to_bbcode(&link, &msg).unwrap());
}