tangled.org trending bluesky account

should be working hopefully

Changed files
+57 -71
bot
src
+57 -71
bot/src/main.rs
··· 111 111 let mut hbs = Handlebars::new(); 112 112 let _ = hbs.register_embed_templates::<Templates>(); 113 113 114 - // let cfg = BrowserConfig::builder() 115 - // .headless_mode(HeadlessMode::New) 116 - // .no_sandbox() 117 - // .build() 118 - // .map_err(|e| anyhow::anyhow!(e)) 119 - // .expect("build browser config"); 114 + // Initialize a long-running Chromium browser 115 + let cfg = BrowserConfig::builder() 116 + .headless_mode(HeadlessMode::New) 117 + .no_sandbox() 118 + .build() 119 + .map_err(|e| anyhow::anyhow!(e)) 120 + .expect("build browser config"); 120 121 121 - // let handle = tokio::spawn(async move { while let Some(_) = browser_handler.next().await {} }); 122 + let (browser, mut browser_handler) = Browser::launch(cfg).await.expect("launch browser"); 123 + // Drive the browser handler for the lifetime of the program 124 + tokio::spawn(async move { while let Some(_evt) = browser_handler.next().await {} }); 125 + let browser = Arc::new(browser); 122 126 123 127 // Ingestor for the star collection 124 128 let mut ingestors: HashMap<String, Box<dyn LexiconIngestor + Send + Sync>> = HashMap::new(); ··· 128 132 pool: pool.clone(), 129 133 bot: Arc::new(bot_api), 130 134 sling_shot: sling_shot.clone(), 131 - //Come back to this 132 - // browser: Arc::new(browser), 135 + browser: browser.clone(), 133 136 hbs: Arc::new(hbs), 134 137 timeframe_hours, 135 138 star_threshold, ··· 168 171 pool: SqlitePool, 169 172 bot: Arc<BotApi>, 170 173 sling_shot: Arc<Slingshot>, 171 - // browser: Arc<Browser>, 174 + browser: Arc<Browser>, 172 175 hbs: Arc<Handlebars<'static>>, 173 176 timeframe_hours: i64, 174 177 star_threshold: i64, ··· 284 287 285 288 let html = &self.hbs.render("repo_header.hbs", &ctx)?; 286 289 287 - // let bytes = render_with_chromiumoxide(&html, 336, 114).await?; 290 + let bytes = render_with_chromiumoxide( 291 + self.browser.as_ref(), 292 + &html, 293 + 336, 294 + 114, 295 + ) 296 + .await?; 288 297 289 - // let blob_upload = &self 290 - // .bot 291 - // .agent 292 - // .api 293 - // .com 294 - // .atproto 295 - // .repo 296 - // .upload_blob(bytes) 297 - // .await?; 298 + let blob_upload = &self 299 + .bot 300 + .agent 301 + .api 302 + .com 303 + .atproto 304 + .repo 305 + .upload_blob(bytes) 306 + .await?; 298 307 299 308 // let rt = RichText::new_with_detect_facets(format!( 300 309 // "{handle_and_repo}{description}\n⭐️ {stars} {tangled_sh_url}" ··· 303 312 let post_text = 304 313 format!("{handle_and_repo}{description}\n⭐️ {stars}"); 305 314 306 - // let image = atrium_api::app::bsky::embed::images::ImageData { 307 - // alt: format!( 308 - // "An image showing the same text inside of the post. {post_text}" 309 - // ), 310 - // aspect_ratio: Some( 311 - // atrium_api::app::bsky::embed::defs::AspectRatioData { 312 - // height: NonZeroU64::try_from(114_u64)?, 313 - // width: NonZeroU64::try_from(336_u64)?, 314 - // } 315 - // .into(), 316 - // ), 317 - // //Good lord how many clones is that 318 - // image: blob_upload.clone().blob.clone(), 319 - // }; 320 - // let embed = Some(atrium_api::types::Union::Refs( 321 - // RecordEmbedRefs::AppBskyEmbedImagesMain(Box::new( 322 - // atrium_api::app::bsky::embed::images::MainData { 323 - // images: vec![image.into()], 324 - // } 325 - // .into(), 326 - // )), 327 - // )); 315 + let image = atrium_api::app::bsky::embed::images::ImageData { 316 + alt: format!( 317 + "An image showing the same text inside of the post. {post_text}" 318 + ), 319 + aspect_ratio: Some( 320 + atrium_api::app::bsky::embed::defs::AspectRatioData { 321 + height: NonZeroU64::try_from(114_u64)?, 322 + width: NonZeroU64::try_from(336_u64)?, 323 + } 324 + .into(), 325 + ), 326 + //Good lord how many clones is that 327 + image: blob_upload.clone().blob.clone(), 328 + }; 329 + let embed = Some(atrium_api::types::Union::Refs( 330 + RecordEmbedRefs::AppBskyEmbedImagesMain(Box::new( 331 + atrium_api::app::bsky::embed::images::MainData { 332 + images: vec![image.into()], 333 + } 334 + .into(), 335 + )), 336 + )); 328 337 329 338 let post = atrium_api::app::bsky::feed::post::RecordData { 330 339 created_at: atrium_api::types::string::Datetime::now(), 331 - embed: None, 340 + embed, 332 341 entities: None, 333 342 facets: Some(vec![ 334 343 atrium_api::app::bsky::richtext::facet::MainData { ··· 394 403 } 395 404 396 405 async fn render_with_chromiumoxide( 406 + browser: &Browser, 397 407 html: &str, 398 408 width: u32, 399 409 height: u32, 400 410 ) -> Result<Vec<u8>, anyhow::Error> { 401 - // Configure and launch the browser 402 - 403 - let cfg = BrowserConfig::builder() 404 - .headless_mode(HeadlessMode::New) 405 - .no_sandbox() 406 - .build() 407 - .map_err(|e| anyhow::anyhow!(e)) 408 - .expect("build browser config"); 409 - 410 - let (mut browser, mut browser_handler) = Browser::launch(cfg).await.expect("launch browser"); 411 - 412 - let handle = tokio::task::spawn(async move { 413 - loop { 414 - match browser_handler.next().await { 415 - Some(h) => match h { 416 - Ok(_) => continue, 417 - Err(_) => break, 418 - }, 419 - None => break, 420 - } 421 - } 422 - }); 423 - 411 + // Use the long-running browser to render a page and capture a screenshot 424 412 let page = browser.new_page("about:blank").await?; 425 413 426 414 // Load the provided HTML as a data URL to avoid external fetches 427 415 let data_url = format!("data:text/html;charset=utf-8,{}", urlencoding::encode(html)); 428 416 page.goto(data_url).await?; 429 417 430 - // Wait for network idle-ish 418 + // Wait for navigation to settle 431 419 page.wait_for_navigation().await.ok(); 432 420 433 421 // Screenshot: clip to desired viewport size regardless of page layout ··· 444 432 .build(); 445 433 let png_bytes = page.screenshot(params).await?; 446 434 447 - // Close 435 + // Close just the page; keep the browser alive 448 436 page.close().await.ok(); 449 - browser.close().await.ok(); 450 - // stop handler task 451 - handle.await?; 437 + 452 438 Ok(png_bytes) 453 439 }