html terminal
Diffstat (limited to 'src/bot/logic.rs')
| -rw-r--r-- | src/bot/logic.rs | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/src/bot/logic.rs b/src/bot/logic.rs new file mode 100644 index 0000000..0aba81b --- /dev/null +++ b/src/bot/logic.rs @@ -0,0 +1,102 @@ +use super::{Context, Result}; +use lemu::Executor; +use poise::{serenity_prelude::AttachmentType, KeyValueArgs}; +use regex::Regex; +use std::{borrow::Cow, sync::LazyLock}; + +#[poise::command(prefix_command, category = "Misc", track_edits, rename = "eval")] +pub async fn run( + ctx: Context<'_>, + #[description = "number of iterations"] kv: KeyValueArgs, + #[rest] + #[description = "Script"] + from: String, +) -> Result<()> { + let _ = ctx.channel_id().start_typing(&ctx.serenity_context().http); + + static RE: LazyLock<Regex> = + LazyLock::new(|| Regex::new(r#"```(arm|x86asm)?([^`]+)```"#).unwrap()); + + let lemu::Output { + output: Some(output), + displays, + .. + } = ({ + let mat = RE + .captures(&from) + .ok_or(anyhow::anyhow!(r#"no code found (use \`\`\`arm...\`\`\`."#))?; + let script = mat.get(2).unwrap().as_str(); + match Executor::with_output(vec![]) + .display() + .limit_iterations( + kv.get("iters") + .map_or(1, |v| v.parse::<usize>().unwrap_or(1).clamp(1, 50)), + ) + .limit_instructions(30000) + .program(&script) + { + Ok(mut v) => { + v.run(); + v.output() + } + Err(e) => { + let s = format!("{}", e.diagnose(script)).replace("`", "\u{200b}`"); + ctx.send(|c| { + c.allowed_mentions(|a| a.empty_parse()) + .content(format!("```ansi\n{s}\n```")) + }) + .await?; + return Ok(()); + } + } + }) + else { + unreachable!() + }; + let displays: Box<[_; 1]> = displays.try_into().unwrap(); + let [display] = *displays; + + ctx.send(|c| { + let mut empty = true; + if !output.is_empty() { + c.content(format!( + "```\n{}\n```", + String::from_utf8_lossy(&output).replace('`', "\u{200b}`") + )); + empty = false; + } + if display.buffer().iter().any(|&n| n != 0) { + let p = oxipng::RawImage::new( + display.width(), + display.height(), + oxipng::ColorType::RGBA, + oxipng::BitDepth::Eight, + display.take_buffer(), + ) + .unwrap(); + let p = p + .create_optimized_png(&oxipng::Options { + filter: oxipng::indexset! { oxipng::RowFilter::None }, + bit_depth_reduction: false, + color_type_reduction: false, + palette_reduction: false, + grayscale_reduction: false, + ..oxipng::Options::from_preset(0) + }) + .unwrap(); + c.attachment(AttachmentType::Bytes { + data: Cow::from(p), + filename: "display1.png".to_string(), + }); + c.embed(|e| e.attachment("display1.png")); + empty = false; + } + if empty { + c.content("no output"); + } + c.allowed_mentions(|a| a.empty_parse()) + }) + .await?; + + Ok(()) +} |