smol bot
Diffstat (limited to 'src/bot/logic.rs')
-rw-r--r--src/bot/logic.rs120
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)
}