diff --git a/src/bot/commands.rs b/src/bot/commands.rs index a82ff03..f451c1a 100644 --- a/src/bot/commands.rs +++ b/src/bot/commands.rs @@ -1,7 +1,12 @@ +use std::{borrow::Cow, collections::HashMap}; + +use anyhow::anyhow; use serenity::all::{ - CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption, - CreateInputText, CreateLabel, CreateModal, CreateModalComponent, CreateTextDisplay, - InputTextStyle, Permissions, + CommandInteraction, CommandOptionType, CommandType, Context, CreateCommand, + CreateCommandOption, CreateComponent, CreateContainer, CreateContainerComponent, + CreateInputText, CreateInteractionResponseMessage, CreateLabel, CreateModal, + CreateModalComponent, CreateTextDisplay, InputTextStyle, MessageFlags, Permissions, + ResolvedTarget, }; use crate::handler::{Handler, get_guild}; @@ -17,6 +22,13 @@ pub fn config_command() -> CreateCommand<'static> { .default_member_permissions(Permissions::ADMINISTRATOR) } +pub fn script_test_command() -> CreateCommand<'static> { + CreateCommand::new("test_script") + .name_localized("ko", "스크립트 테스트") + .kind(CommandType::Message) + .default_member_permissions(Permissions::ADMINISTRATOR) +} + impl Handler { pub async fn process_starboard_command( &self, @@ -27,6 +39,26 @@ impl Handler { return Ok(()); }; + if !interaction + .member + .as_ref() + .and_then(|x| x.permissions) + .map(|x| x.contains(Permissions::ADMINISTRATOR)) + .unwrap_or(false) + { + interaction + .create_response( + &ctx.http, + serenity::all::CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .content("관리자 권한이 필요합니다") + .ephemeral(true), + ), + ) + .await?; + return Ok(()); + } + // if let CommandType interaction.data.kind {} for option in interaction.data.options() { @@ -72,4 +104,102 @@ if reactions["⭐"] >= 3 { Ok(()) } + + pub async fn process_script_test_command( + &self, + ctx: &Context, + interaction: &CommandInteraction, + ) -> anyhow::Result<()> { + let Some(guild_id) = interaction.guild_id else { + return Ok(()); + }; + + if !interaction + .member + .as_ref() + .and_then(|x| x.permissions) + .map(|x| x.contains(Permissions::ADMINISTRATOR)) + .unwrap_or(false) + { + interaction + .create_response( + &ctx.http, + serenity::all::CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .content("관리자 권한이 필요합니다") + .ephemeral(true), + ), + ) + .await?; + return Ok(()); + } + + let target = interaction.data.target(); + + let target_message = target + .and_then(|x| match x { + ResolvedTarget::Message(msg) => Some(msg), + _ => None, + }) + .ok_or_else(|| anyhow!("no target message"))?; + + let script = get_guild(&self.db, guild_id) + .await? + .and_then(|x| x.script) + .unwrap_or("".to_string()); + + let channel = interaction + .channel + .as_ref() + .ok_or_else(|| anyhow!("no channel data"))?; + + let channel = ctx.http.get_channel(channel.id()).await?; + + debug!("selected message: {target_message:?}"); + + let reactions: HashMap = target_message + .reactions + .iter() + .map(|x| (x.reaction_type.to_string(), x.count as i64)) + .collect(); + + let (result, buffer) = paringboard::script::check( + &script, + reactions, + interaction.channel_id.to_string(), + channel + .guild() + .and_then(|x| x.parent_id) + .map(|x| x.to_string()), + ) + .unwrap_or_else(|err| (None, format!("run failed: {err:?}"))); + + interaction + .create_response( + &ctx.http, + serenity::all::CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .flags(MessageFlags::IS_COMPONENTS_V2) + .components(vec![ + CreateComponent::Container( + // result text + CreateContainer::new(vec![CreateContainerComponent::TextDisplay( + CreateTextDisplay::new(format!( + "## Result \n```rs\n{result:?}\n```" + )), + )]), + ), + CreateComponent::Container(CreateContainer::new(vec![ + CreateContainerComponent::TextDisplay(CreateTextDisplay::new( + format!("## Console Output \n```rs\n{}\n```", &buffer), + )), + ])), + ]) + .ephemeral(true), + ), + ) + .await?; + + Ok(()) + } } diff --git a/src/bot/handler.rs b/src/bot/handler.rs index 843d9ee..5c25aad 100644 --- a/src/bot/handler.rs +++ b/src/bot/handler.rs @@ -61,7 +61,10 @@ impl EventHandler for Handler { if let Err(e) = context .http - .create_global_commands(&vec![commands::config_command()]) + .create_global_commands(&vec![ + commands::config_command(), + commands::script_test_command(), + ]) .await { error!("Failed to register config command: {e:?}"); @@ -108,6 +111,11 @@ impl Handler { if command.data.name == "starboard" { self.process_starboard_command(ctx, command).await?; + return Ok(()); + } + if command.data.name == "test_script" { + self.process_script_test_command(ctx, command).await?; + return Ok(()); } } @@ -164,6 +172,7 @@ impl Handler { .and_then(|x| x.parent_id) .map(|x| x.to_string()), ) + .map(|x| x.0) .inspect_err(|res| { error!("check failed: {res:?}"); })? diff --git a/src/script.rs b/src/script.rs index 97b8bd2..bb1fc53 100644 --- a/src/script.rs +++ b/src/script.rs @@ -1,4 +1,7 @@ -use std::collections::HashMap; +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, +}; use anyhow::anyhow; use rhai::{Dynamic, Engine, Map, Scope}; @@ -17,9 +20,10 @@ pub fn check( reactions: HashMap, channel: String, category: Option, -) -> anyhow::Result> { +) -> anyhow::Result<(Option, String)> { debug!("script: {script}"); let mut engine = Engine::new(); + let buffer = Arc::new(RwLock::new(String::new())); engine.set_max_operations(1000); engine.register_type::(); engine.register_fn("result", |webhook_url: String, count: i64, icon: String| { @@ -30,6 +34,8 @@ pub fn check( } }); + let logger = buffer.clone(); + engine .disable_symbol("for") .disable_symbol("while") @@ -37,7 +43,10 @@ pub fn check( .set_max_expr_depths(50, 5) .set_max_string_size(60) .set_max_map_size(512) - .set_max_array_size(512); + .set_max_array_size(512) + .on_print(move |line| { + logger.write().unwrap().push_str(line); + }); let mut emotes_input = Map::new(); for (emoji, count) in reactions { @@ -57,6 +66,8 @@ pub fn check( }, ); + debug!("current scope: {scope:?}"); + let result: Dynamic = engine .eval_with_scope(&mut scope, script) .map_err(|e| anyhow!("{e:?}"))?; @@ -73,5 +84,5 @@ pub fn check( ) }; - return Ok(result); + return Ok((result, buffer.read().unwrap().to_string())); }