aboutsummaryrefslogtreecommitdiffstats
path: root/src/commands/math.rs
blob: 43a7de87d2565d5efe651392f4df6ee08f01d1e9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use crate::lang;
use crate::Data;
use crate::error::{Error, Result};

type Context<'a> = poise::Context<'a, Data, Error>;

/// Evaluate a math expression via qalc
#[poise::command(slash_command)]
pub async fn math(
    ctx: Context<'_>,
    #[description = "Ekspresja matematyczn"] equation: String,
) -> Result<()> {
    if equation.len() > 1000 {
        ctx.say(lang::L_MATH_TOO_LONG).await?;
        return Ok(());
    }

    if !equation.is_ascii() {
        ctx.say(lang::L_MATH_NOT_ASCII).await?;
        return Ok(());
    }

    let mut depth = 0i32;
    for c in equation.chars() {
        match c {
            '(' | '[' | '{' => depth += 1,
            ')' | ']' | '}' => depth -= 1,
            _ => {}
        }
        if depth < 0 {
            ctx.say(lang::L_MATH_UNBALANCED).await?;
            return Ok(());
        }
    }
    if depth != 0 {
        ctx.say(lang::L_MATH_UNBALANCED).await?;
        return Ok(());
    }

    let dangerous = [
        "eval",
        "exec",
        "os.",
        "subprocess",
        "import",
        "__",
        "system",
        "shutil",
        "typecast",
        "shell",
        "fork",
        "execve",
    ];
    let lower = equation.to_lowercase();
    if dangerous.iter().any(|d| lower.contains(d)) {
        ctx.say(lang::L_MATH_DANGEROUS).await?;
        return Ok(());
    }

    let output = tokio::process::Command::new("qalc")
        .arg(&equation)
        .output()
        .await
        .map_err(|e| Error::Custom(lang::fmt_math_qalc_err(&e)))?;

    let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
    if result.is_empty() {
        ctx.say(lang::L_MATH_NO_RESULT).await?;
    } else {
        ctx.say(lang::fmt_math_result(&result)).await?;
    }
    Ok(())
}

pub fn commands() -> Vec<poise::Command<Data, Error>> {
    vec![math()]
}