~misterio/sistemer-bot

22fa5456fd55a0354b5d982e5097fa89ecc0ddba — Gabriel Fontes 2 years ago 725bca6
novos comandos, e teclado pra disciplina
M Cargo.lock => Cargo.lock +1 -0
@@ 1138,6 1138,7 @@ name = "sistemer-bot"
version = "0.1.0"
dependencies = [
 "anyhow",
 "chrono",
 "dotenv",
 "log",
 "pretty_env_logger",

M Cargo.toml => Cargo.toml +1 -0
@@ 14,3 14,4 @@ dotenv = "0.15"
reqwest = "0.11"
anyhow = "1.0"
regex = "1.5"
chrono = "0.4"

A src/agora.rs => src/agora.rs +19 -0
@@ 0,0 1,19 @@
use crate::{disciplina, hoje};
use anyhow::Result;
use chrono::offset::Local;
use regex::Regex;

pub async fn get_agora() -> Result<String> {
    let horarios_texto = hoje::get_horarios_hoje().await?;
    let agora = Local::now().time();

    for linha in horarios_texto.lines() {
        let (inicio, fim) = crate::get_intervalo(linha)?;
        if inicio <= agora && agora <= fim {
            let nome_disciplina = Regex::new("^.*<a href=\"#(.*)\">.*$")?.replace(linha, "$1");
            log::info!("Disciplina: {:?}", nome_disciplina);
            return disciplina::get_disciplina(&nome_disciplina).await;
        }
    }
    Ok("Nenhuma aula agora!".into())
}

M src/disciplina.rs => src/disciplina.rs +2 -4
@@ 14,15 14,13 @@ pub async fn get_disciplina(disciplina: &str) -> Result<String> {
    let mut adding = false;
    for line in fulltext.lines() {
        if start_pattern.is_match(&line.to_lowercase()) {
            log::info!("Iniciando escrita");
            output.push_str(&crate::sanitize_line(line)?);
            output.push_str(&crate::sanitize_line(line, false)?);
            adding = true;
        } else if adding {
            if stop_pattern.is_match(line) {
                log::info!("Finalizando escrita:\n{}", output);
                return Ok(output);
            } else {
                output.push_str(&crate::sanitize_line(line)?);
                output.push_str(&crate::sanitize_line(line, false)?);
            }
        }
    }

A src/disciplinas.rs => src/disciplinas.rs +20 -0
@@ 0,0 1,20 @@
use anyhow::Result;
use regex::Regex;

pub async fn list_disciplinas() -> Result<Vec<String>> {
    let fulltext = reqwest::get("https://misterio.me/notes/bsi/disciplinas-2021-2.html")
        .await?
        .text()
        .await?;

    let disciplina_pattern = Regex::new("<h2 id=\"S")?;

    let mut output = Vec::new();
    for line in fulltext.lines() {
        if disciplina_pattern.is_match(line) {
            output.push(crate::sanitize_line(line, true)?);
        }
    }

    Ok(output)
}

A src/hoje.rs => src/hoje.rs +41 -0
@@ 0,0 1,41 @@
use crate::horarios;
use anyhow::Result;
use chrono::{offset::Local, Datelike, Duration, Weekday};

fn nome_dia(weekday: Weekday) -> String {
    match weekday {
        Weekday::Mon => "Segunda",
        Weekday::Tue => "Terça",
        Weekday::Wed => "Quarta",
        Weekday::Thu => "Quinta",
        Weekday::Fri => "Sexta",
        Weekday::Sat => "Sábado",
        Weekday::Sun => "Domingo",
    }
    .into()
}

pub async fn get_horarios_hoje() -> Result<String> {
    let texto_horarios = horarios::get_horarios().await?;
    let nome_hoje = nome_dia(Local::today().weekday());
    let nome_amanha = nome_dia((Local::today() + Duration::days(1)).weekday());

    let mut output: String = "".into();
    let mut adding = false;
    for line in texto_horarios.lines() {
        if line.contains(&nome_hoje) {
            adding = true;
        } else if line.contains(&nome_amanha) {
            adding = false;
        } else if adding {
            output.push_str(&crate::sanitize_line(line.trim(), false)?);
        }
    }

    let output = if output.is_empty() {
        "Sem aulas hoje!".into()
    } else {
        output
    };
    Ok(output)
}

M src/horarios.rs => src/horarios.rs +3 -5
@@ 14,17 14,15 @@ pub async fn get_horarios() -> Result<String> {
    let mut adding = false;
    for line in fulltext.lines() {
        if start_pattern.is_match(&line.to_lowercase()) {
            log::info!("Iniciando escrita");
            output.push_str(&crate::sanitize_line(line)?);
            output.push_str(&crate::sanitize_line(line, false)?);
            adding = true;
        } else if adding {
            if stop_pattern.is_match(line) {
                log::info!("Finalizando escrita:\n{}", output);
                return Ok(output);
            } else {
                output.push_str(&crate::sanitize_line(line)?);
                output.push_str(&crate::sanitize_line(line, false)?);
            }
        }
    }
    return Ok(output);
    Ok(output)
}

M src/lib.rs => src/lib.rs +19 -5
@@ 1,12 1,22 @@
pub mod agora;
pub mod disciplina;
pub mod disciplinas;
pub mod hoje;
pub mod horarios;
pub mod proxima;

use anyhow::Result;
use chrono::NaiveTime;
use regex::Regex;

fn sanitize_line(line: &str) -> Result<String> {
    log::info!("Transformando {:?}", line);
fn get_intervalo(input: &str) -> Result<(NaiveTime, NaiveTime)> {
    let inicio = Regex::new("^(.*) - .*:.*$")?.replace(input, "$1");
    let fim = Regex::new("^.* - (.*):.*$")?.replace(input, "$1");
    let parse = |x| NaiveTime::parse_from_str(x, "%H:%M");
    Ok((parse(&inicio)?, parse(&fim)?))
}

fn sanitize_line(line: &str, remove_bold: bool) -> Result<String> {
    let line = format!("{}\n", line);
    // Replace headers with <b>
    let line = Regex::new("h\\d")?.replace_all(&line, "b");


@@ 21,10 31,14 @@ fn sanitize_line(line: &str) -> Result<String> {
    // Remove email links
    let line = Regex::new("<a href=\"mailto:.*>(.*)</a>")?.replace(&line, "$1");
    // Fix calculadora links
    let line = Regex::new("/notes/bsi/calculadora")?.replace(&line, "https://misterio.me/notes/bsi/calculadora");
    let line = Regex::new("/notes/bsi/calculadora")?
        .replace(&line, "https://misterio.me/notes/bsi/calculadora");

    log::info!("Em: {:?}", line);
    let line = if remove_bold {
        Regex::new("<b.*>(.*)</b>")?.replace(&line, "$1")
    } else {
        line
    };

    Ok(line.into())
}


M src/main.rs => src/main.rs +65 -10
@@ 1,36 1,91 @@
use teloxide::prelude::*;
use teloxide::types::ParseMode::Html;
use teloxide::types::{KeyboardButton, KeyboardMarkup, ParseMode::Html, ReplyMarkup};
use teloxide::utils::command::BotCommand;

use anyhow::Result;
use dotenv::dotenv;
use regex::Regex;

use sistemer_bot::{horarios, disciplina};
use sistemer_bot::{agora, disciplina, disciplinas, hoje, horarios, proxima};

#[derive(BotCommand)]
#[command(rename = "lowercase", description = "These commands are supported:")]
#[command(rename = "lowercase", description = "Comandos que tenho no momento:")]
enum Command {
    #[command(description = "exibe essa ajuda.")]
    Help,
    #[command(description = "mostra info de uma disciplina.")]
    Disciplina(String),
    #[command(description = "mostra info de uma disciplina.")]
    D(String),
    #[command(description = "lista os horários.")]
    Horarios,
    #[command(description = "informações da aula atual.")]
    Agora,
    #[command(description = "informações da próxima aula.")]
    Proxima,
    #[command(description = "quais as aulas de hoje.")]
    Hoje,
}

fn disciplina_row(disciplina: String) -> Vec<KeyboardButton> {
    let disciplina = Regex::new(".* - ").unwrap().replace_all(&disciplina, "");
    vec![KeyboardButton {
        text: format!("/d {}", disciplina),
        request: None,
    }]
}

async fn answer(cx: UpdateWithCx<AutoSend<Bot>, Message>, command: Command) -> Result<()> {
async fn answer_command(cx: UpdateWithCx<AutoSend<Bot>, Message>, command: Command) -> Result<()> {
    match command {
        Command::Help => cx.answer(Command::descriptions()).await?,
        Command::Disciplina(disciplina) => {
            cx.answer(disciplina::get_disciplina(&disciplina).await?)
                .parse_mode(Html)
                .await?
        Command::Disciplina(disciplina) | Command::D(disciplina) => {
            if disciplina.is_empty() {
                let username = format!(
                    "@{}, ",
                    cx.update
                        .from()
                        .and_then(|u| u.clone().username)
                        .unwrap_or_default()
                );
                cx.answer(&format!("Certo, {}qual disciplina?", username))
                    .reply_markup(ReplyMarkup::Keyboard(KeyboardMarkup {
                        keyboard: disciplinas::list_disciplinas()
                            .await?
                            .into_iter()
                            .map(disciplina_row)
                            .collect(),
                        one_time_keyboard: Some(true),
                        selective: Some(true),
                        ..Default::default()
                    }))
                    .send()
                    .await?
            } else {
                cx.answer(disciplina::get_disciplina(&disciplina).await?)
                    .parse_mode(Html)
                    .await?
            }
        }
        Command::Horarios => {
            cx.answer(horarios::get_horarios().await?)
                .parse_mode(Html)
                .await?
        }
        Command::Agora => {
            cx.answer(agora::get_agora().await?)
                .parse_mode(Html)
                .await?
        }
        Command::Proxima => {
            cx.answer(proxima::get_proxima().await?)
                .parse_mode(Html)
                .await?
        }
        Command::Hoje => {
            cx.answer(hoje::get_horarios_hoje().await?)
                .parse_mode(Html)
                .await?
        }
    };

    Ok(())


@@ 40,9 95,9 @@ async fn run() {
    teloxide::enable_logging!();

    let bot = Bot::from_env().auto_send();
    let bot_name: String = "Sistemer Bot".into();
    let bot_name: String = "Sistemer_Bot".into();

    teloxide::commands_repl(bot, bot_name, answer).await;
    teloxide::commands_repl(bot, bot_name, answer_command).await;
}

#[tokio::main]

A src/proxima.rs => src/proxima.rs +19 -0
@@ 0,0 1,19 @@
use crate::{disciplina, hoje};
use anyhow::Result;
use chrono::offset::Local;
use regex::Regex;

pub async fn get_proxima() -> Result<String> {
    let horarios_texto = hoje::get_horarios_hoje().await?;
    let agora = Local::now().time();

    for linha in horarios_texto.lines() {
        let (inicio, _) = crate::get_intervalo(linha)?;
        if inicio > agora {
            let nome_disciplina = Regex::new("^.*<a href=\"#(.*)\">.*$")?.replace(linha, "$1");
            log::info!("Disciplina: {:?}", nome_disciplina);
            return disciplina::get_disciplina(&nome_disciplina).await;
        }
    }
    Ok("Nenhuma aula hoje mais tarde!".into())
}