smol bot
Diffstat (limited to 'src/bot/logic.rs')
| -rw-r--r-- | src/bot/logic.rs | 120 |
1 files changed, 85 insertions, 35 deletions
diff --git a/src/bot/logic.rs b/src/bot/logic.rs index 10d5034..e299c08 100644 --- a/src/bot/logic.rs +++ b/src/bot/logic.rs @@ -2,13 +2,77 @@ use super::{Context, Result}; use lemu::Executor; use poise::{serenity_prelude::*, CodeBlock, KeyValueArgs}; +#[poise::command(slash_command, rename = "eval_file", install_context = "Guild|User")] +/// Execute MLOG from a file. +/// +/// Your file can run up to 52789849 instructions, and up to 50 iterations. +/// You get one large display to use. +pub async fn run_file( + ctx: Context<'_>, + #[description = "logic, txt"] mlog: Attachment, + #[description = "number of iterations (0–50)"] iterations: Option<u8>, +) -> Result<()> { + ctx.defer().await?; + let bytes = mlog.download().await?; + let Ok(code) = String::from_utf8(bytes) else { + ctx.say("this is not a mlog file!").await?; + return Ok(()); + }; + match exec(code, iterations.map_or(1, |x| x.clamp(0, 50)) as _).await { + Err(Err::Other(x)) => return Err(x), + Err(Err::Lemu(x)) => { + ctx.send( + poise::CreateReply::default() + .allowed_mentions(CreateAllowedMentions::default().empty_users().empty_roles()) + .content(format!("```ansi\n{x}\n```")), + ) + .await?; + } + Ok(x) => drop(ctx.send(x).await?), + } + ctx.say(format!("executed [{}]({})", mlog.filename, mlog.url)) + .await?; + Ok(()) +} + #[poise::command(prefix_command, track_edits, rename = "eval")] pub async fn run( ctx: Context<'_>, #[description = "number of iterations"] kv: KeyValueArgs, #[description = "Script"] block: CodeBlock, ) -> Result<()> { - let _ = ctx.channel_id().start_typing(&ctx.serenity_context().http); + match exec( + block.code, + kv.get("iters") + .map_or(1, |v| v.parse::<usize>().unwrap_or(1).clamp(1, 50)), + ) + .await + { + Err(Err::Other(x)) => return Err(x), + Err(Err::Lemu(x)) => { + ctx.send( + poise::CreateReply::default() + .allowed_mentions(CreateAllowedMentions::default().empty_users().empty_roles()) + .content(format!("```ansi\n{x}\n```")), + ) + .await?; + } + Ok(x) => drop(ctx.send(x).await?), + } + Ok(()) +} + +enum Err { + Lemu(String), + Other(anyhow::Error), +} +impl<T: Into<anyhow::Error>> From<T> for Err { + fn from(value: T) -> Self { + Self::Other(value.into()) + } +} + +async fn exec(code: String, iters: usize) -> Result<poise::CreateReply, Err> { let lemu::Output { output: Some(output), displays, @@ -16,29 +80,20 @@ pub async fn run( } = (match tokio::task::spawn_blocking(move || { Executor::with_output(vec![]) .large_display() - .limit_iterations( - kv.get("iters") - .map_or(1, |v| v.parse::<usize>().unwrap_or(1).clamp(1, 50)), - ) + .limit_iterations(iters) .limit_instructions(52789849) - .program(&block.code) + .program(&code) .map(|mut v| { v.run(); v.output() }) - .map_err(|e| format!("{}", e.diagnose(&block.code)).replace('`', "\u{200b}`")) + .map_err(|e| format!("{}", e.diagnose(&code)).replace('`', "\u{200b}`")) }) .await? { Ok(o) => o, Err(e) => { - ctx.send( - poise::CreateReply::default() - .allowed_mentions(CreateAllowedMentions::default().empty_users().empty_roles()) - .content(format!("```ansi\n{e}\n```")), - ) - .await?; - return Ok(()); + return Err(Err::Lemu(e)); } }) else { @@ -65,25 +120,20 @@ pub async fn run( None }; - ctx.send({ - let mut c = poise::CreateReply::default(); - if output.is_empty() && display.is_none() { - c = c.content("no output"); - } - if !output.is_empty() { - c = c.content(format!( - "```\n{}\n```", - String::from_utf8_lossy(&output).replace('`', "\u{200b}`") - )); - } - if let Some(display) = display { - c = c - .attachment(CreateAttachment::bytes(display, "display1.png")) - .embed(CreateEmbed::default().attachment("display1.png")); - } - c - }) - .await?; - - Ok(()) + let mut c = poise::CreateReply::default(); + if output.is_empty() && display.is_none() { + c = c.content("no output"); + } + if !output.is_empty() { + c = c.content(format!( + "```\n{}\n```", + String::from_utf8_lossy(&output).replace('`', "\u{200b}`") + )); + } + if let Some(display) = display { + c = c + .attachment(CreateAttachment::bytes(display, "display1.png")) + .embed(CreateEmbed::default().attachment("display1.png")); + } + Ok(c) } |