smol bot
Diffstat (limited to 'src/bot/map.rs')
| -rw-r--r-- | src/bot/map.rs | 136 |
1 files changed, 96 insertions, 40 deletions
diff --git a/src/bot/map.rs b/src/bot/map.rs index 76023e0..c790eae 100644 --- a/src/bot/map.rs +++ b/src/bot/map.rs @@ -1,12 +1,13 @@ use anyhow::Result; use mindus::{data::map::ReadError, *}; -use poise::{serenity_prelude::*, CreateReply}; +use poise::{CreateReply, serenity_prelude::*}; use std::{ ops::ControlFlow, time::{Duration, Instant}, }; - -use super::{strip_colors, SUCCESS}; +use tokio::task::JoinError; +const BENDN: ChannelId = ChannelId::new(1149866218057117747); +use super::{SUCCESS, strip_colors}; fn string((x, f): (ReadError, &str)) -> String { match x { @@ -30,18 +31,24 @@ fn string((x, f): (ReadError, &str)) -> String { } } -pub async fn download(a: &Attachment) -> Result<(Result<Map, (ReadError, &str)>, Duration)> { - let s = a.download().await?; +pub async fn download( + a: &Attachment, +) -> Result<(Result<(Map, Box<[u8]>), (ReadError, &str)>, Duration)> { + let s = a.download().await?.into_boxed_slice(); let then = Instant::now(); // could ignore, but i think if you have a msav, you dont want to ignore failures. Ok(( - Map::deserialize(&mut mindus::data::DataRead::new(&s)).map_err(|x| (x, &*a.filename)), + Map::deserialize(&mut mindus::data::DataRead::new(&s)) + .map_err(|x| (x, &*a.filename)) + .map(|x| (x, s)), then.elapsed(), )) } -pub async fn scour(m: &Message) -> Result<Option<(Result<Map, (ReadError, &str)>, Duration)>> { +pub async fn scour( + m: &Message, +) -> Result<Option<(Result<(Map, Box<[u8]>), (ReadError, &str)>, Duration)>> { for a in &m.attachments { if a.filename.ends_with("msav") { return Ok(Some(download(a).await?)); @@ -50,12 +57,27 @@ pub async fn scour(m: &Message) -> Result<Option<(Result<Map, (ReadError, &str)> Ok(None) } -pub async fn reply(a: &Attachment) -> Result<ControlFlow<CreateReply, String>> { - let (m, deser_took) = match download(a).await? { +pub async fn reply( + c: super::Context<'_>, + a: &Attachment, +) -> Result<ControlFlow<CreateReply, String>> { + let ((m, b), deser_took) = match download(a).await? { (Err(e), _) => return Ok(ControlFlow::Continue(string(e))), (Ok(m), deser_took) => (m, deser_took), }; - let (a, e) = embed(m, deser_took).await; + let (a, e) = match embed(m, deser_took).await { + Ok(x) => x, + Err(e) => { + BENDN.send_files( + &c, + [CreateAttachment::bytes(b, "map.msav")], + CreateMessage::new().content(format!("<@696196765564534825> failure: {e}")), + ); + return Ok(ControlFlow::Break(CreateReply::default().content( + "there was a problem. i have notified bendn about this issue.", + ))); + } + }; Ok(ControlFlow::Break( CreateReply::default().attachment(a).embed(e), )) @@ -67,33 +89,29 @@ struct Timings { compression_took: Duration, total: Duration, } -async fn render(m: Map, deser_took: Duration) -> (Timings, Vec<u8>) { - tokio::task::spawn_blocking(move || { - let render_took = Instant::now(); - let i = m.render(); - let render_took = render_took.elapsed(); - let compression_took = Instant::now(); - let png = super::png(i); - let compression_took = compression_took.elapsed(); - let total = deser_took + render_took + compression_took; - ( - Timings { - deser_took, - render_took, - compression_took, - total, - }, - png, - ) - }) - .await - .unwrap() +fn render(m: Map, deser_took: Duration) -> (Timings, Vec<u8>) { + let render_took = Instant::now(); + let i = m.render(); + let render_took = render_took.elapsed(); + let compression_took = Instant::now(); + let png = super::png(i); + let compression_took = compression_took.elapsed(); + let total = deser_took + render_took + compression_took; + ( + Timings { + deser_took, + render_took, + compression_took, + total, + }, + png, + ) } pub async fn find( msg: &Message, c: &serenity::client::Context, -) -> Result<Option<(String, Map, Duration)>> { +) -> Result<Option<(String, (Map, Box<[u8]>), Duration)>> { match scour(msg).await? { None => Ok(None), Some((Err(e), _)) => { @@ -108,12 +126,37 @@ pub async fn find( } } +#[implicit_fn::implicit_fn] pub async fn with(msg: &Message, c: &serenity::client::Context) -> Result<()> { - let Some((_auth, m, deser_took)) = find(msg, c).await? else { + let Some((_auth, (m, b), deser_took)) = find(msg, c).await? else { return Ok(()); }; let t = msg.channel_id.start_typing(&c.http); - let (png, embed) = embed(m, deser_took).await; + let (png, embed) = match embed(m, deser_took).await { + Ok(x) => x, + Err(e) => { + use crate::emoji::named::*; + + BENDN + .send_files( + &c, + [CreateAttachment::bytes(b, "file.msch")], + CreateMessage::new().content(format!( + "<@696196765564534825> panic `{e}` in {}\n", + msg.guild(&c.cache).map_or( + msg.guild_id + .map(_.get().to_string()) + .unwrap_or(format!("dms with {}", msg.author.name)), + _.name.clone(), + ) + )), + ) + .await?; + msg.reply(c, format!("{CANCEL} there was an error while rendering this map.\nthis issue has been reported to bendn, who will hopefully take a look eventually.")) + .await?; + return Ok(()); + } + }; t.stop(); super::data::push_j(serde_json::json! {{ "locale": msg.author.locale.as_deref().unwrap_or("no locale"), @@ -129,7 +172,7 @@ pub async fn with(msg: &Message, c: &serenity::client::Context) -> Result<()> { Ok(()) } -async fn embed(m: Map, deser_took: Duration) -> (CreateAttachment, CreateEmbed) { +async fn embed(m: Map, deser_took: Duration) -> Result<(CreateAttachment, CreateEmbed), JoinError> { let name = strip_colors(m.tags.get("name").or(m.tags.get("mapname")).unwrap()); let d = strip_colors(m.tags.get("description").map(|x| &**x).unwrap_or("?")); let f = if m.width == m.height { @@ -137,8 +180,8 @@ async fn embed(m: Map, deser_took: Duration) -> (CreateAttachment, CreateEmbed) } else { format!("{}×{}", m.height, m.width) }; - let (timings, png) = render(m, deser_took).await; - ( + let (timings, png) = tokio::task::spawn_blocking(move || render(m, deser_took)).await?; + Ok(( CreateAttachment::bytes(png, "map.png"), CreateEmbed::new() .title(&name) @@ -149,7 +192,7 @@ async fn embed(m: Map, deser_took: Duration) -> (CreateAttachment, CreateEmbed) ))) .attachment("map.png") .color(SUCCESS), - ) + )) } #[poise::command( @@ -160,11 +203,24 @@ async fn embed(m: Map, deser_took: Duration) -> (CreateAttachment, CreateEmbed) /// Renders map inside a message. pub async fn render_message(c: super::Context<'_>, m: Message) -> Result<()> { super::log(&c); - let Some((_auth, m, deser_took)) = find(&m, c.serenity_context()).await? else { + let Some((_auth, (m, b), deser_took)) = find(&m, c.serenity_context()).await? else { poise::say_reply(c, "no map").await?; return Ok(()); }; - let (png, embed) = embed(m, deser_took).await; + let (png, embed) = match embed(m, deser_took).await { + Ok(x) => x, + Err(e) => { + BENDN.send_files( + &c, + [CreateAttachment::bytes(b, "map.msav")], + CreateMessage::new().content(format!("<@696196765564534825> failure: {e}")), + ); + c.say("there was a problem. i have notified bendn about this issue.") + .await?; + return Ok(()); + } + }; + poise::send_reply(c, CreateReply::default().attachment(png).embed(embed)).await?; Ok(()) } |