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())
+}