use std::pin::Pin; use futures::Future; use telexide::{ api::types::SendMessage, client::{self, ClientBuilder}, model::{self, MessageContent, MessageEntity, UpdateContent}, }; use typemap::Key; use crate::browser::BrowserHandle; pub struct BotDriver { client: client::Client, listen_user: String, } struct BotConfig { handle: BrowserHandle, listen_user: String, } struct BotConfigKey {} impl Key for BotConfigKey { type Value = BotConfig; } fn telegram_handler( ctx: client::Context, upd: model::Update, ) -> Pin + Send>> { let config = if let Some(cfg) = ctx.data.read().get::() { BotConfig { handle: cfg.handle.clone(), listen_user: cfg.listen_user.clone(), } } else { panic!("no config"); }; if let UpdateContent::Message(msg) = upd.content { if let Some(user) = msg.from { if let Some(name) = user.username { if name.to_lowercase() == config.listen_user.to_lowercase() { if let MessageContent::Text { content, entities } = msg.content { if let Some(url_text) = get_url(content, entities) { return Box::pin(async move { match config.handle.go_to(url_text.as_str()).await { Err(err) => { println!("failed opening url [{}]: {}", url_text, err) } _ => {} }; }); } } } else { return Box::pin(async move { match ctx .api .send_message(SendMessage::new( msg.chat.get_id(), "we don't listen to you", )) .await { Err(err) => println!("failed writing not listening: {}", err), _ => {} }; }); } } } } Box::pin(async {}) } impl BotDriver { pub fn new(token: String, listen_user: String) -> Self { Self { client: ClientBuilder::new() .set_token(&token) .add_handler_func(telegram_handler) .build(), listen_user: listen_user, } } pub async fn listen_and_paste(&mut self, handle: BrowserHandle) -> Result<(), anyhow::Error> { let listen_to = self.listen_user.clone(); let config = BotConfig { handle: handle, listen_user: listen_to, }; // Scope it so it instantly drops. Might instantly drop anyway cause I don't use the result. Ah well. { self.client.data.write().insert::(config); } self.client.start().await?; println!("telegram bot finished early"); Ok(()) } } fn get_url(text: String, entities: Vec) -> Option { match entities .into_iter() .filter(|ent| match ent { MessageEntity::Url(_) => true, _ => false, }) .take(1) .collect::>() .first() { Some(def) => match def { MessageEntity::Url(url_def) => Some( text.chars() .skip(url_def.offset as usize) .take(url_def.length as usize) .collect(), ), _ => None, }, _ => None, } }