Ctrl K
ComunidadGuías…

src/server/commands.rs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
use std::process::Stdio;
use tokio::process::Command;
use tokio::sync::mpsc::{Sender, channel};
use tracing::{debug, error, info};

use crate::{CustomCommand, server::hx::Media};

pub struct CustomCommandTrigger {
    pub cmd: String,
    pub media: Media,
}

pub fn setup_custom_commands(commands: Vec<CustomCommand>) -> Sender<CustomCommandTrigger> {
    let (tx, mut rx) = channel::<CustomCommandTrigger>(100);
    let spis_path = std::env::current_exe()
        .map_or_else(|_| "spis".to_string(), |p| p.to_string_lossy().to_string());

    tokio::spawn(async move {
        while let Some(trigger) = rx.recv().await {
            if let Some(config) = commands.iter().find(|c| c.name == trigger.cmd) {
                if config.cmd.is_empty() {
                    error!("Command '{}' has no executable defined", config.name);
                    continue;
                }

                let processed_cmd: Vec<String> = config
                    .cmd
                    .iter()
                    .map(|arg| {
                        arg.replace("{spis}", &spis_path)
                            .replace("{name}", &trigger.media.name)
                            .replace("{path}", &trigger.media.path)
                            .replace("{taken_at}", &trigger.media.taken_at.to_string())
                            .replace("{uuid}", &trigger.media.uuid.to_string())
                            .replace("{url}", &trigger.media.url)
                            .replace("{thumbnail}", &trigger.media.thumbnail)
                    })
                    .collect();

                let program = &processed_cmd[0];
                let args = &processed_cmd[1..];

                info!(
                    "Executing custom command '{}': {} {:?}",
                    config.name, program, args
                );

                match Command::new(program)
                    .args(args)
                    .stdout(Stdio::piped())
                    .stderr(Stdio::piped())
                    .spawn()
                {
                    Ok(child) => match child.wait_with_output().await {
                        Ok(out) => {
                            if out.status.success() {
                                info!("Command '{}' finished successfully", config.name);
                                if !out.stdout.is_empty() {
                                    debug!("Stdout: {}", String::from_utf8_lossy(&out.stdout));
                                }
                            } else {
                                error!(
                                    "Command '{}' failed with exit code: {:?}",
                                    config.name,
                                    out.status.code()
                                );
                                if !out.stderr.is_empty() {
                                    error!("Stderr: {}", String::from_utf8_lossy(&out.stderr));
                                }
                            }
                        }
                        Err(e) => error!("Failed to wait for command '{}': {}", config.name, e),
                    },
                    Err(e) => error!("Failed to spawn command '{}': {}", config.name, e),
                }
            } else {
                error!("Custom command definition not found for: {}", trigger.cmd);
            }
        }
    });

    tx
}